pyopenssl icon indicating copy to clipboard operation
pyopenssl copied to clipboard

TestX509StoreContext::test_verify_with_time FAILED

Open PPed72 opened this issue 8 years ago • 10 comments

Whenever I try to build pyopenssl-17.2.0 on my gentoo system I get the following test failure:

tests/test_crypto.py::TestX509StoreContext::test_verify_with_time FAILED
[...]
================================================================== FAILURES ==================================================================
_________________________________________________ TestX509StoreContext.test_verify_with_time _________________________________________________

self = <tests.test_crypto.TestX509StoreContext object at 0x00007f71d67b9360>

    def test_verify_with_time(self):
        """
            `verify_certificate` raises error when the verification time is
            set at notAfter.
            """
        store = X509Store()
        store.add_cert(self.root_cert)
        store.add_cert(self.intermediate_cert)
    
        expire_time = self.intermediate_server_cert.get_notAfter()
        expire_datetime = datetime.strptime(
            expire_time.decode('utf-8'), '%Y%m%d%H%M%SZ'
        )
        store.set_time(expire_datetime)
    
        store_ctx = X509StoreContext(store, self.intermediate_server_cert)
        with pytest.raises(X509StoreContextError) as exc:
>           store_ctx.verify_certificate()
E           Failed: DID NOT RAISE <class 'OpenSSL.crypto.X509StoreContextError'>

tests/test_crypto.py:3639: Failed
==================================================== 1 failed, 504 passed in 9.74 seconds ====================================================
 * ERROR: dev-python/pyopenssl-17.2.0::gentoo failed (test phase):

openssl version is 1.0.2l

It happens with any python implementation on my system (I have python-2.7, python-3.5, pypy and pypy3)

PPed72 avatar Aug 17 '17 10:08 PPed72

Could we just…you know give expire_datetime 13h of leeway? 😇

hynek avatar Nov 26 '17 14:11 hynek

c)

Run the whole test suite in UTC local, e.g. "TZ=UTC py.test -v"

This seems the most reasonable approach.

PPed72 avatar Nov 27 '17 16:11 PPed72

c) Run the whole test suite in UTC local, e.g. "TZ=UTC py.test -v"

This seems the most reasonable approach.

TZ=UTC worked for me on OpenIndiana 2019.04.

Mno-hime avatar May 12 '19 12:05 Mno-hime

This appears to be a bug in X509Store.set_time(), caused by vfy_time.strftime("%s") (vfy_time is a datetime object) disregarding the timezone, therefore passing an incorrect timestamp to X509_VERIFY_PARAM_set_time().

The "%s" problem is a known python issue, see Issue 12750, but it's not very straightforward to fix here. On python >= 3.3, int(vfy_time.timestamp()) could be used instead of int(vfy_time.strftime("%s")), but that's not available on python 2.7.

On python 3, the issue can be verified easily:

>>> from datetime import datetime, timezone
>>> tn = datetime.strptime("19700101000000Z", "%Y%m%d%H%M%SZ")  # tn will be naive (timezone-unaware)
>>> tn.timestamp()
-3600.0  # I'm in CET, which is 1 hour after UTC on 1st January
>>> tn.strftime("%s")
'-3600'
>>> ta = tn.replace(tzinfo=timezone.utc)  # ta will be timezone-aware
>>> ta.timestamp()
0.0  # since ta is in UTC, the timestamp of 1970-01-01 00:00 UTC is zero
>>> ta.strftime("%s")
'-3600'  # but strftime("%s") disregards the timezone in ta, and always uses the local timezone

orosam avatar Oct 08 '20 14:10 orosam

... and apparently I'm not the first to figure this out. I just bumped into #798, which is basically the same issue, and #907 fixing them.

orosam avatar Oct 12 '20 10:10 orosam

@orosam thats another PR that needs rebasing sadly. 😬

reaperhulk avatar Oct 12 '20 13:10 reaperhulk

I was playing with that a little :smile:, strftime("%s") is evil... More about that at #798...

orosam avatar Oct 12 '20 14:10 orosam

This issue is also fixed by #907, and documented in #952.

orosam avatar Oct 26 '20 15:10 orosam

Perhaps this issue has been fixed, but I have two more failures with this test.

This one is on 32bit i586 (OpenSUSE/Tumbleweed):

[   48s] __________________ TestX509StoreContext.test_verify_with_time __________________
[   48s] 
[   48s] self = <tests.test_crypto.TestX509StoreContext object at 0xf61d3f2c>
[   48s] 
[   48s]     def test_verify_with_time(self):
[   48s]         """
[   48s]         `verify_certificate` raises error when the verification time is
[   48s]         set at notAfter.
[   48s]         """
[   48s]         store = X509Store()
[   48s]         store.add_cert(self.root_cert)
[   48s]         store.add_cert(self.intermediate_cert)
[   48s]     
[   48s]         expire_time = self.intermediate_server_cert.get_notAfter()
[   48s]         expire_datetime = datetime.strptime(
[   48s]             expire_time.decode("utf-8"), "%Y%m%d%H%M%SZ"
[   48s]         )
[   48s] >       store.set_time(expire_datetime)
[   48s] 
[   48s] tests/test_crypto.py:4111: 
[   48s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
[   48s] 
[   48s] self = <OpenSSL.crypto.X509Store object at 0xf61d3fec>
[   48s] vfy_time = datetime.datetime(2047, 12, 20, 17, 11, 20)
[   48s] 
[   48s]     def set_time(self, vfy_time):
[   48s]         """
[   48s]         Set the time against which the certificates are verified.
[   48s]     
[   48s]         Normally the current time is used.
[   48s]     
[   48s]         .. note::
[   48s]     
[   48s]           For example, you can determine if a certificate was valid at a given
[   48s]           time.
[   48s]     
[   48s]         .. versionadded:: 17.0.0
[   48s]     
[   48s]         :param datetime vfy_time: The verification time to set on this store.
[   48s]         :return: ``None`` if the verification time was successfully set.
[   48s]         """
[   48s]         param = _lib.X509_VERIFY_PARAM_new()
[   48s]         param = _ffi.gc(param, _lib.X509_VERIFY_PARAM_free)
[   48s]     
[   48s]         _lib.X509_VERIFY_PARAM_set_time(
[   48s] >           param, calendar.timegm(vfy_time.timetuple())
[   48s]         )
[   48s] E       OverflowError: integer 2460474680 does not fit '32-bit int'
[   48s] 
[   48s] ../../BUILDROOT/python-pyOpenSSL-20.0.0-71.1.i386/usr/lib/python3.6/site-packages/OpenSSL/crypto.py:1680: OverflowError

mcepl avatar Dec 11 '20 14:12 mcepl

On x86 (not x86_64) time_t is defined as a 32-bit value. Unfortunately this means verification past int32 max won't work. OpenSSL may have other APIs for this, but someone will need to do the research.

reaperhulk avatar Dec 11 '20 16:12 reaperhulk