pyopenssl icon indicating copy to clipboard operation
pyopenssl copied to clipboard

21.0.0: pytest is failing and `DeprecationWarning` warnings

Open kloczek opened this issue 2 years ago • 8 comments

I'm trying to package your module as an rpm package. So I'm using the typical PEP517 based build, install and test cycle used on building packages from non-root account.

  • python3 -sBm build -w
  • install .whl file in </install/prefix>
  • run pytest with PYTHONPATH pointing to sitearch and sitelib inside </install/prefix>
  • I'm using openssl 3.0.0

Here is pytest output:

+ SETUPTOOLS_SCM_PRETEND_VERSION=21.0.0
+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-pyOpenSSL-21.0.0-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-pyOpenSSL-21.0.0-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.12, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
OpenSSL: b'OpenSSL 3.0.0 7 sep 2021'
cryptography: 36.0.1
rootdir: /home/tkloczko/rpmbuild/BUILD/pyopenssl-21.0.0, configfile: setup.cfg, testpaths: tests
plugins: flaky-3.7.0
collected 526 items

tests/test_crypto.py ...............................................................................................F............................................... [ 27%]
..............................................................................................................F.................................                     [ 54%]
tests/test_debug.py .                                                                                                                                                [ 54%]
tests/test_rand.py ....                                                                                                                                              [ 55%]
tests/test_ssl.py .....F....F..................................F.................................................................................................... [ 83%]
.............................................................F.........................                                                                              [ 99%]
tests/test_util.py .                                                                                                                                                 [100%]

================================================================================= FAILURES =================================================================================
__________________________________________________________________ TestX509.test_nullbyte_subjectAltName ___________________________________________________________________

self = <tests.test_crypto.TestX509 object at 0x7f62e0419910>

    def test_nullbyte_subjectAltName(self):
        """
        The fields of a `subjectAltName` extension on an X509 may contain NUL
        bytes and this value is reflected in the string representation of the
        extension object.
        """
        cert = load_certificate(FILETYPE_PEM, nulbyteSubjectAltNamePEM)

        ext = cert.get_extension(3)
        assert ext.get_short_name() == b"subjectAltName"
>       assert (
            b"DNS:altnull.python.org\x00example.com, "
            b"email:[email protected]\[email protected], "
            b"URI:http://null.python.org\x00http://example.org, "
            b"IP Address:192.0.2.1, IP Address:2001:DB8:0:0:0:0:0:1\n"
            == str(ext).encode("ascii")
        )
E       AssertionError: assert b'DNS:altnull...0:0:0:0:0:1\n' == b'DNS:altnull...8:0:0:0:0:0:1'
E         Use -v to get the full diff

tests/test_crypto.py:2035: AssertionError
_____________________________________________________________ TestX509StoreContext.test_untrusted_self_signed ______________________________________________________________

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

    def test_untrusted_self_signed(self):
        """
        `verify_certificate` raises error when a self-signed certificate is
        verified without itself in the chain.
        """
        store = X509Store()
        store_ctx = X509StoreContext(store, self.root_cert)
        with pytest.raises(X509StoreContextError) as exc:
            store_ctx.verify_certificate()

>       assert exc.value.args[0][2] == "self signed certificate"
E       AssertionError: assert 'self-signed certificate' == 'self signed certificate'
E         - self signed certificate
E         ?     ^
E         + self-signed certificate
E         ?     ^

tests/test_crypto.py:4054: AssertionError
_____________________________________________________________ TestContext.test_set_cipher_list_no_cipher_match _____________________________________________________________

self = <tests.test_ssl.TestContext object at 0x7f62e0c487c0>, context = <OpenSSL.SSL.Context object at 0x7f62e0c48df0>

    @flaky.flaky
    def test_set_cipher_list_no_cipher_match(self, context):
        """
        `Context.set_cipher_list` raises `OpenSSL.SSL.Error` with a
        `"no cipher match"` reason string regardless of the TLS
        version.
        """
        with pytest.raises(Error) as excinfo:
            context.set_cipher_list(b"imaginary-cipher")
>       assert excinfo.value.args == (
            [
                (
                    "SSL routines",
                    "SSL_CTX_set_cipher_list",
                    "no cipher match",
                )
            ],
        )
E       AssertionError: assert ([('SSL routi...her match')],) == ([('SSL routi...her match')],)
E         At index 0 diff: [('SSL routines', '', 'no cipher match')] != [('SSL routines', 'SSL_CTX_set_cipher_list', 'no cipher match')]
E         Use -v to get the full diff

tests/test_ssl.py:531: AssertionError
___________________________________________________________________ TestContext.test_set_session_id_fail ___________________________________________________________________

self = <tests.test_ssl.TestContext object at 0x7f62e094af10>, context = <OpenSSL.SSL.Context object at 0x7f62e0c74460>

    def test_set_session_id_fail(self, context):
        """
        `Context.set_session_id` errors are propagated.
        """
        with pytest.raises(Error) as e:
            context.set_session_id(b"abc" * 1000)

>       assert [
            (
                "SSL routines",
                "SSL_CTX_set_session_id_context",
                "ssl session id context too long",
            )
        ] == e.value.args[0]
E       AssertionError: assert [('SSL routin...xt too long')] == [('SSL routin...xt too long')]
E         At index 0 diff: ('SSL routines', 'SSL_CTX_set_session_id_context', 'ssl session id context too long') != ('SSL routines', '', 'ssl session id context too long')
E         Use -v to get the full diff

tests/test_ssl.py:578: AssertionError
________________________________________________________________ TestContext.test_passwd_callback_too_long _________________________________________________________________

self = <tests.test_ssl.TestContext object at 0x7f62e063c730>, tmpfile = b'/tmp/pytest-of-tkloczko/pytest-444/tmp_ips1zbx'

    def test_passwd_callback_too_long(self, tmpfile):
        """
        If the passphrase returned by the passphrase callback returns a string
        longer than the indicated maximum length, it is truncated.
        """
        # A priori knowledge!
        passphrase = b"x" * 1024
        pemFile = self._write_encrypted_pem(passphrase, tmpfile)

        def passphraseCallback(maxlen, verify, extra):
            assert maxlen == 1024
            return passphrase + b"y"

        context = Context(SSLv23_METHOD)
        context.set_passwd_cb(passphraseCallback)
        # This shall succeed because the truncated result is the correct
        # passphrase.
>       context.use_privatekey_file(pemFile)

tests/test_ssl.py:986:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../BUILDROOT/python-pyOpenSSL-21.0.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/OpenSSL/SSL.py:1012: in use_privatekey_file
    self._raise_passphrase_exception()
../../BUILDROOT/python-pyOpenSSL-21.0.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/OpenSSL/SSL.py:986: in _raise_passphrase_exception
    self._passphrase_helper.raise_if_problem(Error)
../../BUILDROOT/python-pyOpenSSL-21.0.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/OpenSSL/crypto.py:2836: in raise_if_problem
    raise self._problems.pop(0)
../../BUILDROOT/python-pyOpenSSL-21.0.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/OpenSSL/crypto.py:2842: in _read_passphrase
    result = self._passphrase(size, rwflag, userdata)
../../BUILDROOT/python-pyOpenSSL-21.0.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/OpenSSL/SSL.py:800: in wrapper
    return callback(size, verify, self._passphrase_userdata)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

maxlen = 1022, verify = 0, extra = None

    def passphraseCallback(maxlen, verify, extra):
>       assert maxlen == 1024
E       assert 1022 == 1024

tests/test_ssl.py:979: AssertionError
____________________________________________________________________ TestMemoryBIO.test_unexpected_EOF _____________________________________________________________________

self = <tests.test_ssl.TestMemoryBIO object at 0x7f62e0405be0>

    def test_unexpected_EOF(self):
        """
        If the connection is lost before an orderly SSL shutdown occurs,
        `OpenSSL.SSL.SysCallError` is raised with a message of
        "Unexpected EOF".
        """
        server_conn, client_conn = loopback()
        client_conn.sock_shutdown(SHUT_RDWR)
        with pytest.raises(SysCallError) as err:
>           server_conn.recv(1024)

tests/test_ssl.py:3682:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../BUILDROOT/python-pyOpenSSL-21.0.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/OpenSSL/SSL.py:1865: in recv
    self._raise_ssl_error(self._ssl, result)
../../BUILDROOT/python-pyOpenSSL-21.0.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/OpenSSL/SSL.py:1700: in _raise_ssl_error
    _raise_current_error()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

exception_type = <class 'OpenSSL.SSL.Error'>

    def exception_from_error_queue(exception_type):
        """
        Convert an OpenSSL library failure into a Python exception.

        When a call to the native OpenSSL library fails, this is usually signalled
        by the return value, and an error code is stored in an error queue
        associated with the current thread. The err library provides functions to
        obtain these error codes and textual error messages.
        """
        errors = []

        while True:
            error = lib.ERR_get_error()
            if error == 0:
                break
            errors.append(
                (
                    text(lib.ERR_lib_error_string(error)),
                    text(lib.ERR_func_error_string(error)),
                    text(lib.ERR_reason_error_string(error)),
                )
            )

>       raise exception_type(errors)
E       OpenSSL.SSL.Error: [('SSL routines', '', 'unexpected eof while reading')]

../../BUILDROOT/python-pyOpenSSL-21.0.0-2.fc35.x86_64/usr/lib/python3.8/site-packages/OpenSSL/_util.py:55: Error
============================================================================= warnings summary =============================================================================
../../../../../usr/lib/python3.8/site-packages/_pytest/config/__init__.py:1233
  /usr/lib/python3.8/site-packages/_pytest/config/__init__.py:1233: PytestConfigWarning: Unknown config option: strict

    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")

tests/test_crypto.py:39
  /home/tkloczko/rpmbuild/BUILD/pyopenssl-21.0.0/tests/test_crypto.py:39: DeprecationWarning: PKCS#7 support in pyOpenSSL is deprecated. You should use the APIs in cryptography.
    from OpenSSL.crypto import PKCS7, load_pkcs7_data

tests/test_crypto.py:40
  /home/tkloczko/rpmbuild/BUILD/pyopenssl-21.0.0/tests/test_crypto.py:40: DeprecationWarning: PKCS#12 support in pyOpenSSL is deprecated. You should use the APIs in cryptography.
    from OpenSSL.crypto import PKCS12, load_pkcs12

tests/test_ssl.py::TestContext::test_set_cipher_list[hello world:AES128-SHA1]
  /home/tkloczko/rpmbuild/BUILD/pyopenssl-21.0.0/tests/test_ssl.py:509: DeprecationWarning: str for cipher_list is no longer accepted, use bytes
    context.set_cipher_list(cipher_string)

tests/test_ssl.py::TestConnection::test_client_set_session
  /home/tkloczko/rpmbuild/BUILD/pyopenssl-21.0.0/tests/test_ssl.py:2686: DeprecationWarning: str for buf is no longer accepted, use bytes
    ctx.set_session_id("unity-test")

-- Docs: https://docs.pytest.org/en/stable/warnings.html
===Flaky Test Report===

test_gmtime_adj_notBefore passed 1 out of the required 1 times. Success!
test_gmtime_adj_notAfter passed 1 out of the required 1 times. Success!
test_set_cipher_list_no_cipher_match failed (1 runs remaining out of 2).
        <class 'AssertionError'>
        assert ([('SSL routi...her match')],) == ([('SSL routi...her match')],)
  At index 0 diff: [('SSL routines', '', 'no cipher match')] != [('SSL routines', 'SSL_CTX_set_cipher_list', 'no cipher match')]
  Use -v to get the full diff
        [<TracebackEntry /home/tkloczko/rpmbuild/BUILD/pyopenssl-21.0.0/tests/test_ssl.py:531>]
test_set_cipher_list_no_cipher_match failed; it passed 0 out of the required 1 times.
        <class 'AssertionError'>
        assert ([('SSL routi...her match')],) == ([('SSL routi...her match')],)
  At index 0 diff: [('SSL routines', '', 'no cipher match')] != [('SSL routines', 'SSL_CTX_set_cipher_list', 'no cipher match')]
  Use -v to get the full diff
        [<TracebackEntry /home/tkloczko/rpmbuild/BUILD/pyopenssl-21.0.0/tests/test_ssl.py:531>]

===End Flaky Test Report===
========================================================================= short test summary info ==========================================================================
FAILED tests/test_crypto.py::TestX509::test_nullbyte_subjectAltName - AssertionError: assert b'DNS:altnull...0:0:0:0:0:1\n' == b'DNS:altnull...8:0:0:0:0:0:1'
FAILED tests/test_crypto.py::TestX509StoreContext::test_untrusted_self_signed - AssertionError: assert 'self-signed certificate' == 'self signed certificate'
FAILED tests/test_ssl.py::TestContext::test_set_cipher_list_no_cipher_match - AssertionError: assert ([('SSL routi...her match')],) == ([('SSL routi...her match')],)
FAILED tests/test_ssl.py::TestContext::test_set_session_id_fail - AssertionError: assert [('SSL routin...xt too long')] == [('SSL routin...xt too long')]
FAILED tests/test_ssl.py::TestContext::test_passwd_callback_too_long - assert 1022 == 1024
FAILED tests/test_ssl.py::TestMemoryBIO::test_unexpected_EOF - OpenSSL.SSL.Error: [('SSL routines', '', 'unexpected eof while reading')]
================================================================ 6 failed, 520 passed, 5 warnings in 15.10s ================================================================

kloczek avatar Jan 01 '22 21:01 kloczek

I just tested as well 21.0.0 + all commits from master and result is the same.

kloczek avatar Jan 01 '22 21:01 kloczek

All the test failures (with the exception of the last one) appear to be textual output from OpenSSL, which is unfortunately not stable between all versions (and pyOpenSSL, even more unfortunately, depends on its test suite). What version of OpenSSL are you packaging against?

reaperhulk avatar Jan 01 '22 21:01 reaperhulk

Oops, sorry I saw you said 3.0.0 in your initial report. That's likely the culprit for the failures. I believe they also changed EOF behavior in 3.0.0. pyOpenSSL needs to add a 3.0.0 test builder and improve the test suite to understand 3.0.0's output.

reaperhulk avatar Jan 01 '22 21:01 reaperhulk

I understand .. Neverthe less more and more distribuions is usimg mopw openssl 3.0. I've manage to switch 100% of my distro packages to openssl 3.x. There are only some minor issues with such change (on scale of the whole duistribition not more than it was with oleder openssl).

kloczek avatar Jan 01 '22 23:01 kloczek

KInd of question: despite pyopenssl test suite do you know about any problem with using pyopenssl with openssl 3.x?

kloczek avatar Jan 01 '22 23:01 kloczek

I believe it will work fine in almost all cases, although test_unexpected_EOF suggests there is an edge case where it will not raise the appropriate error type due to behavioral change between 1.1.x and 3.0.0.

reaperhulk avatar Jan 01 '22 23:01 reaperhulk

Any update? (just asking 😄 ) Just tested everything up to fb26edde and looks like pytest is failing in the same units.

Kind of question: despite tose fails pyopenssl should be ok with openssl 3.x or still some work needs to be done to make it ready for that version of openssl?

Thx.

kloczek avatar Jan 14 '22 08:01 kloczek

I think I already answered the question in my previous reply, but no updates on getting this fixed. Someone has to step forward to do the work 😄

reaperhulk avatar Jan 15 '22 16:01 reaperhulk

Just test ed 23.1.1 and none of the unis are failing (which is good) however I see some warnings Here is pytest output:

+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-pyOpenSSL-23.1.1-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-pyOpenSSL-23.1.1-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra -m 'not network'
==================================================================================== test session starts ====================================================================================
platform linux -- Python 3.8.16, pytest-7.3.1, pluggy-1.0.0
OpenSSL: b'OpenSSL 3.0.5 5 Jul 2022'
cryptography: 40.0.2
rootdir: /home/tkloczko/rpmbuild/BUILD/pyopenssl-23.1.1
configfile: setup.cfg
testpaths: tests
plugins: flaky-3.7.0
collected 542 items

tests/test_crypto.py ................................................................................................................................................................ [ 29%]
....................................................................................................................................                                                  [ 53%]
tests/test_debug.py .                                                                                                                                                                 [ 54%]
tests/test_rand.py ....                                                                                                                                                               [ 54%]
tests/test_ssl.py ................................................................................................................................................................... [ 84%]
.................................................................................                                                                                                     [ 99%]
tests/test_util.py .                                                                                                                                                                  [100%]

===================================================================================== warnings summary ======================================================================================
../../../../../usr/lib/python3.8/site-packages/_pytest/config/__init__.py:1302
  /usr/lib/python3.8/site-packages/_pytest/config/__init__.py:1302: PytestConfigWarning: Unknown config option: strict

    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")

tests/test_crypto.py:22
  /home/tkloczko/rpmbuild/BUILD/pyopenssl-23.1.1/tests/test_crypto.py:22: DeprecationWarning: PKCS#12 support in pyOpenSSL is deprecated. You should use the APIs in cryptography.
    from OpenSSL.crypto import (

tests/test_crypto.py:22
  /home/tkloczko/rpmbuild/BUILD/pyopenssl-23.1.1/tests/test_crypto.py:22: DeprecationWarning: PKCS#7 support in pyOpenSSL is deprecated. You should use the APIs in cryptography.
    from OpenSSL.crypto import (

tests/test_crypto.py::TestCRL::test_export_md5_digest
  /usr/lib/python3.8/site-packages/_pytest/python.py:199: PytestRemovedIn8Warning: Passing None has been deprecated.
  See https://docs.pytest.org/en/latest/how-to/capture-warnings.html#additional-use-cases-of-warnings-in-tests for alternatives in common use cases.
    result = testfunction(**testargs)

tests/test_ssl.py::TestContext::test_set_cipher_list[hello world:AES128-SHA1]
  /home/tkloczko/rpmbuild/BUILD/pyopenssl-23.1.1/tests/test_ssl.py:502: DeprecationWarning: str for cipher_list is no longer accepted, use bytes
    context.set_cipher_list(cipher_string)

tests/test_ssl.py::TestConnection::test_client_set_session
  /home/tkloczko/rpmbuild/BUILD/pyopenssl-23.1.1/tests/test_ssl.py:2772: DeprecationWarning: str for buf is no longer accepted, use bytes
    ctx.set_session_id("unity-test")

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
===Flaky Test Report===

test_gmtime_adj_notBefore passed 1 out of the required 1 times. Success!
test_gmtime_adj_notAfter passed 1 out of the required 1 times. Success!
test_set_cipher_list_no_cipher_match passed 1 out of the required 1 times. Success!

===End Flaky Test Report===
============================================================================= 542 passed, 6 warnings in 11.28s ==============================================================================

Thank you for your time :

kloczek avatar Apr 28 '23 16:04 kloczek

Warnigs are covered by https://github.com/pyca/pyopenssl/issues/1024 so look slike this ticked can be closed.

Closing and one more time than you.

kloczek avatar Apr 28 '23 16:04 kloczek