From c60770699be48e9c3f3b4f542c8a4118be2936ab Mon Sep 17 00:00:00 2001 From: Alex Chan Date: Fri, 18 Nov 2016 13:53:39 +0000 Subject: Convert X509ExtTests to use pytest-style tests (#564) --- examples/simple/server.py | 1 + src/OpenSSL/SSL.py | 1 + src/OpenSSL/crypto.py | 3 + src/OpenSSL/rand.py | 2 + tests/test_crypto.py | 300 ++++++++++++++++++++++------------------------ tests/util.py | 24 +++- 6 files changed, 171 insertions(+), 160 deletions(-) diff --git a/examples/simple/server.py b/examples/simple/server.py index 19f6d21..9cce9e0 100644 --- a/examples/simple/server.py +++ b/examples/simple/server.py @@ -67,6 +67,7 @@ def dropClient(cli, errors=None): cli.shutdown() cli.close() + while 1: try: r, w, _ = select.select( diff --git a/src/OpenSSL/SSL.py b/src/OpenSSL/SSL.py index 7ebf7e0..f92c064 100644 --- a/src/OpenSSL/SSL.py +++ b/src/OpenSSL/SSL.py @@ -1065,6 +1065,7 @@ class Context(object): _lib.SSL_CTX_set_alpn_select_cb( self._context, self._alpn_select_callback, _ffi.NULL) + ContextType = Context diff --git a/src/OpenSSL/crypto.py b/src/OpenSSL/crypto.py index 52fcdaf..e1558ed 100644 --- a/src/OpenSSL/crypto.py +++ b/src/OpenSSL/crypto.py @@ -316,6 +316,8 @@ class PKey(object): :return: The number of bits of the key. """ return _lib.EVP_PKEY_bits(self._pkey) + + PKeyType = PKey @@ -2180,6 +2182,7 @@ class PKCS7(object): string_type = _lib.OBJ_nid2sn(nid) return _ffi.string(string_type) + PKCS7Type = PKCS7 diff --git a/src/OpenSSL/rand.py b/src/OpenSSL/rand.py index a22f9e5..8f51ad8 100644 --- a/src/OpenSSL/rand.py +++ b/src/OpenSSL/rand.py @@ -31,6 +31,7 @@ class Error(Exception): See :manpage:`err(3)` for more information. """ + _raise_current_error = partial(_exception_from_error_queue, Error) _unspecified = object() @@ -202,6 +203,7 @@ def screen(): """ _lib.RAND_screen() + if getattr(_lib, 'RAND_screen', None) is None: del screen diff --git a/tests/test_crypto.py b/tests/test_crypto.py index a44ac14..e75e3ad 100644 --- a/tests/test_crypto.py +++ b/tests/test_crypto.py @@ -46,7 +46,7 @@ from OpenSSL.crypto import ( from OpenSSL._util import native, lib from .util import ( - EqualityTestsMixin, TestCase, WARNING_TYPE_EXPECTED + EqualityTestsMixin, is_consistent_type, TestCase, WARNING_TYPE_EXPECTED ) @@ -524,235 +524,223 @@ zo0MUVPQgwJ3aJtNM1QMOQUayCrRwfklg+D/rFSUwEUqtZh7fJDiFqz3 """ -class X509ExtTests(TestCase): +@pytest.fixture +def x509_data(): """ - Tests for :py:class:`OpenSSL.crypto.X509Extension`. + Create a new private key and start a certificate request (for a test + to finish in one way or another). + """ + # Basic setup stuff to generate a certificate + pkey = PKey() + pkey.generate_key(TYPE_RSA, 384) + req = X509Req() + req.set_pubkey(pkey) + # Authority good you have. + req.get_subject().commonName = "Yoda root CA" + x509 = X509() + subject = x509.get_subject() + subject.commonName = req.get_subject().commonName + x509.set_issuer(subject) + x509.set_pubkey(pkey) + now = datetime.now() + expire = datetime.now() + timedelta(days=100) + x509.set_notBefore(now.strftime("%Y%m%d%H%M%SZ").encode()) + x509.set_notAfter(expire.strftime("%Y%m%d%H%M%SZ").encode()) + yield pkey, x509 + + +class TestX509Ext(object): + """ + Tests for `OpenSSL.crypto.X509Extension`. """ - - def setUp(self): - """ - Create a new private key and start a certificate request (for a test - method to finish in one way or another). - """ - super(X509ExtTests, self).setUp() - # Basic setup stuff to generate a certificate - self.pkey = PKey() - self.pkey.generate_key(TYPE_RSA, 384) - self.req = X509Req() - self.req.set_pubkey(self.pkey) - # Authority good you have. - self.req.get_subject().commonName = "Yoda root CA" - self.x509 = X509() - self.subject = self.x509.get_subject() - self.subject.commonName = self.req.get_subject().commonName - self.x509.set_issuer(self.subject) - self.x509.set_pubkey(self.pkey) - now = datetime.now() - expire = datetime.now() + timedelta(days=100) - self.x509.set_notBefore(now.strftime("%Y%m%d%H%M%SZ").encode()) - self.x509.set_notAfter(expire.strftime("%Y%m%d%H%M%SZ").encode()) - - def tearDown(self): - """ - Forget all of the pyOpenSSL objects so they can be garbage collected, - their memory released, and not interfere with the leak detection code. - """ - self.pkey = self.req = self.x509 = self.subject = None - super(X509ExtTests, self).tearDown() def test_str(self): """ - The string representation of :py:class:`X509Extension` instances as - returned by :py:data:`str` includes stuff. + The string representation of `X509Extension` instances as + returned by `str` includes stuff. """ # This isn't necessarily the best string representation. Perhaps it # will be changed/improved in the future. - self.assertEquals( - str(X509Extension(b'basicConstraints', True, b'CA:false')), - 'CA:FALSE') + assert ( + str(X509Extension(b'basicConstraints', True, b'CA:false')) == + 'CA:FALSE' + ) def test_type(self): """ - :py:class:`X509Extension` and :py:class:`X509ExtensionType` refer to - the same type object and can be used to create instances of that type. + `X509Extension` and `X509ExtensionType` refer to the same type object + and can be used to create instances of that type. """ - self.assertIdentical(X509Extension, X509ExtensionType) - self.assertConsistentType( + assert X509Extension is X509ExtensionType + assert is_consistent_type( X509Extension, 'X509Extension', b'basicConstraints', True, b'CA:true') def test_construction(self): """ - :py:class:`X509Extension` accepts an extension type name, a critical - flag, and an extension value and returns an - :py:class:`X509ExtensionType` instance. + `X509Extension` accepts an extension type name, a critical flag, + and an extension value and returns an `X509ExtensionType` instance. """ basic = X509Extension(b'basicConstraints', True, b'CA:true') - self.assertTrue( - isinstance(basic, X509ExtensionType), - "%r is of type %r, should be %r" % ( - basic, type(basic), X509ExtensionType)) + assert isinstance(basic, X509ExtensionType) - comment = X509Extension( - b'nsComment', False, b'pyOpenSSL unit test') - self.assertTrue( - isinstance(comment, X509ExtensionType), - "%r is of type %r, should be %r" % ( - comment, type(comment), X509ExtensionType)) + comment = X509Extension(b'nsComment', False, b'pyOpenSSL unit test') + assert isinstance(comment, X509ExtensionType) - def test_invalid_extension(self): - """ - :py:class:`X509Extension` raises something if it is passed a bad - extension name or value. - """ - self.assertRaises( - Error, X509Extension, b'thisIsMadeUp', False, b'hi') - self.assertRaises( - Error, X509Extension, b'basicConstraints', False, b'blah blah') + @pytest.mark.parametrize('type_name, critical, value', [ + (b'thisIsMadeUp', False, b'hi'), + (b'basicConstraints', False, b'blah blah'), # Exercise a weird one (an extension which uses the r2i method). This # exercises the codepath that requires a non-NULL ctx to be passed to # X509V3_EXT_nconf. It can't work now because we provide no # configuration database. It might be made to work in the future. - self.assertRaises( - Error, X509Extension, b'proxyCertInfo', True, - b'language:id-ppl-anyLanguage,pathlen:1,policy:text:AB') + (b'proxyCertInfo', True, + b'language:id-ppl-anyLanguage,pathlen:1,policy:text:AB') + ]) + def test_invalid_extension(self, type_name, critical, value): + """ + `X509Extension` raises something if it is passed a bad + extension name or value. + """ + with pytest.raises(Error): + X509Extension(type_name, critical, value) - def test_get_critical(self): + @pytest.mark.parametrize('critical_flag', [True, False]) + def test_get_critical(self, critical_flag): """ - :py:meth:`X509ExtensionType.get_critical` returns the value of the + `X509ExtensionType.get_critical` returns the value of the extension's critical flag. """ - ext = X509Extension(b'basicConstraints', True, b'CA:true') - self.assertTrue(ext.get_critical()) - ext = X509Extension(b'basicConstraints', False, b'CA:true') - self.assertFalse(ext.get_critical()) + ext = X509Extension(b'basicConstraints', critical_flag, b'CA:true') + assert ext.get_critical() == critical_flag - def test_get_short_name(self): + @pytest.mark.parametrize('short_name, value', [ + (b'basicConstraints', b'CA:true'), + (b'nsComment', b'foo bar'), + ]) + def test_get_short_name(self, short_name, value): """ - :py:meth:`X509ExtensionType.get_short_name` returns a string giving the + `X509ExtensionType.get_short_name` returns a string giving the short type name of the extension. """ - ext = X509Extension(b'basicConstraints', True, b'CA:true') - self.assertEqual(ext.get_short_name(), b'basicConstraints') - ext = X509Extension(b'nsComment', True, b'foo bar') - self.assertEqual(ext.get_short_name(), b'nsComment') + ext = X509Extension(short_name, True, value) + assert ext.get_short_name() == short_name def test_get_data(self): """ - :py:meth:`X509Extension.get_data` returns a string giving the data of + `X509Extension.get_data` returns a string giving the data of the extension. """ ext = X509Extension(b'basicConstraints', True, b'CA:true') # Expect to get back the DER encoded form of CA:true. - self.assertEqual(ext.get_data(), b'0\x03\x01\x01\xff') - - def test_get_data_wrong_args(self): - """ - :py:meth:`X509Extension.get_data` raises :py:exc:`TypeError` if passed - any arguments. - """ - ext = X509Extension(b'basicConstraints', True, b'CA:true') - self.assertRaises(TypeError, ext.get_data, None) - self.assertRaises(TypeError, ext.get_data, "foo") - self.assertRaises(TypeError, ext.get_data, 7) + assert ext.get_data() == b'0\x03\x01\x01\xff' - def test_unused_subject(self): + def test_unused_subject(self, x509_data): """ - The :py:data:`subject` parameter to :py:class:`X509Extension` may be - provided for an extension which does not use it and is ignored in this - case. + The `subject` parameter to `X509Extension` may be provided for an + extension which does not use it and is ignored in this case. """ + pkey, x509 = x509_data ext1 = X509Extension( - b'basicConstraints', False, b'CA:TRUE', subject=self.x509) - self.x509.add_extensions([ext1]) - self.x509.sign(self.pkey, 'sha1') + b'basicConstraints', False, b'CA:TRUE', subject=x509) + x509.add_extensions([ext1]) + x509.sign(pkey, 'sha1') # This is a little lame. Can we think of a better way? - text = dump_certificate(FILETYPE_TEXT, self.x509) - self.assertTrue(b'X509v3 Basic Constraints:' in text) - self.assertTrue(b'CA:TRUE' in text) + text = dump_certificate(FILETYPE_TEXT, x509) + assert b'X509v3 Basic Constraints:' in text + assert b'CA:TRUE' in text - def test_subject(self): + def test_subject(self, x509_data): """ - If an extension requires a subject, the :py:data:`subject` parameter to - :py:class:`X509Extension` provides its value. + If an extension requires a subject, the `subject` parameter to + `X509Extension` provides its value. """ + pkey, x509 = x509_data ext3 = X509Extension( - b'subjectKeyIdentifier', False, b'hash', subject=self.x509) - self.x509.add_extensions([ext3]) - self.x509.sign(self.pkey, 'sha1') - text = dump_certificate(FILETYPE_TEXT, self.x509) - self.assertTrue(b'X509v3 Subject Key Identifier:' in text) + b'subjectKeyIdentifier', False, b'hash', subject=x509) + x509.add_extensions([ext3]) + x509.sign(pkey, 'sha1') + text = dump_certificate(FILETYPE_TEXT, x509) + assert b'X509v3 Subject Key Identifier:' in text def test_missing_subject(self): """ - If an extension requires a subject and the :py:data:`subject` parameter + If an extension requires a subject and the `subject` parameter is given no value, something happens. """ - self.assertRaises( - Error, X509Extension, b'subjectKeyIdentifier', False, b'hash') + with pytest.raises(Error): + X509Extension(b'subjectKeyIdentifier', False, b'hash') - def test_invalid_subject(self): + @pytest.mark.parametrize('bad_obj', [ + True, + object(), + "hello", + [], + ]) + def test_invalid_subject(self, bad_obj): """ - If the :py:data:`subject` parameter is given a value which is not an - :py:class:`X509` instance, :py:exc:`TypeError` is raised. + If the `subject` parameter is given a value which is not an + `X509` instance, `TypeError` is raised. """ - for badObj in [True, object(), "hello", [], self]: - self.assertRaises( - TypeError, - X509Extension, - 'basicConstraints', False, 'CA:TRUE', subject=badObj) + with pytest.raises(TypeError): + X509Extension( + 'basicConstraints', False, 'CA:TRUE', subject=bad_obj) - def test_unused_issuer(self): + def test_unused_issuer(self, x509_data): """ - The :py:data:`issuer` parameter to :py:class:`X509Extension` may be - provided for an extension which does not use it and is ignored in this - case. + The `issuer` parameter to `X509Extension` may be provided for an + extension which does not use it and is ignored in this case. """ + pkey, x509 = x509_data ext1 = X509Extension( - b'basicConstraints', False, b'CA:TRUE', issuer=self.x509) - self.x509.add_extensions([ext1]) - self.x509.sign(self.pkey, 'sha1') - text = dump_certificate(FILETYPE_TEXT, self.x509) - self.assertTrue(b'X509v3 Basic Constraints:' in text) - self.assertTrue(b'CA:TRUE' in text) + b'basicConstraints', False, b'CA:TRUE', issuer=x509) + x509.add_extensions([ext1]) + x509.sign(pkey, 'sha1') + text = dump_certificate(FILETYPE_TEXT, x509) + assert b'X509v3 Basic Constraints:' in text + assert b'CA:TRUE' in text - def test_issuer(self): + def test_issuer(self, x509_data): """ - If an extension requires an issuer, the :py:data:`issuer` parameter to - :py:class:`X509Extension` provides its value. + If an extension requires an issuer, the `issuer` parameter to + `X509Extension` provides its value. """ + pkey, x509 = x509_data ext2 = X509Extension( b'authorityKeyIdentifier', False, b'issuer:always', - issuer=self.x509) - self.x509.add_extensions([ext2]) - self.x509.sign(self.pkey, 'sha1') - text = dump_certificate(FILETYPE_TEXT, self.x509) - self.assertTrue(b'X509v3 Authority Key Identifier:' in text) - self.assertTrue(b'DirName:/CN=Yoda root CA' in text) + issuer=x509) + x509.add_extensions([ext2]) + x509.sign(pkey, 'sha1') + text = dump_certificate(FILETYPE_TEXT, x509) + assert b'X509v3 Authority Key Identifier:' in text + assert b'DirName:/CN=Yoda root CA' in text def test_missing_issuer(self): """ - If an extension requires an issue and the :py:data:`issuer` parameter - is given no value, something happens. + If an extension requires an issue and the `issuer` parameter is + given no value, something happens. """ - self.assertRaises( - Error, - X509Extension, - b'authorityKeyIdentifier', False, - b'keyid:always,issuer:always') + with pytest.raises(Error): + X509Extension( + b'authorityKeyIdentifier', + False, b'keyid:always,issuer:always') - def test_invalid_issuer(self): + @pytest.mark.parametrize('bad_obj', [ + True, + object(), + "hello", + [], + ]) + def test_invalid_issuer(self, bad_obj): """ - If the :py:data:`issuer` parameter is given a value which is not an - :py:class:`X509` instance, :py:exc:`TypeError` is raised. + If the `issuer` parameter is given a value which is not an + `X509` instance, `TypeError` is raised. """ - for badObj in [True, object(), "hello", [], self]: - self.assertRaises( - TypeError, - X509Extension, - 'authorityKeyIdentifier', False, 'keyid:always,issuer:always', - issuer=badObj) + with pytest.raises(TypeError): + X509Extension( + 'basicConstraints', False, 'keyid:always,issuer:always', + issuer=bad_obj) class TestPKey(object): diff --git a/tests/util.py b/tests/util.py index 2cab91f..7520a28 100644 --- a/tests/util.py +++ b/tests/util.py @@ -305,10 +305,26 @@ class TestCase(TestCase): :param constructionArgs: Positional arguments to use with :py:data:`theType` to create an instance of it. """ - self.assertEqual(theType.__name__, name) - self.assertTrue(isinstance(theType, type)) - instance = theType(*constructionArgs) - self.assertIdentical(type(instance), theType) + assert is_consistent_type(theType, name, *constructionArgs) + + +def is_consistent_type(theType, name, *constructionArgs): + """ + Perform various assertions about *theType* to ensure that it is a + well-defined type. This is useful for extension types, where it's + pretty easy to do something wacky. If something about the type is + unusual, an exception will be raised. + + :param theType: The type object about which to make assertions. + :param name: A string giving the name of the type. + :param constructionArgs: Positional arguments to use with + *theType* to create an instance of it. + """ + assert theType.__name__ == name + assert isinstance(theType, type) + instance = theType(*constructionArgs) + assert type(instance) is theType + return True class EqualityTestsMixin(object): -- cgit v1.2.1