From 8bfbacef9cb973115c0cf0f4185c8f47812c37bc Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Thu, 23 Jul 2015 19:10:28 +0100 Subject: when building a CSR adding > 1 extension would trigger a bug We were checking sk_X509_EXTENSION_push for a value == 1, but in reality it returns the number of extensions on the stack. We now assert >= 1 and added a test. --- .../hazmat/backends/openssl/backend.py | 2 +- tests/test_x509.py | 25 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 7ccb39a4..9cfe73c1 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -976,7 +976,7 @@ class Backend(object): ) assert extension != self._ffi.NULL res = self._lib.sk_X509_EXTENSION_push(extensions, extension) - assert res == 1 + assert res >= 1 res = self._lib.X509_REQ_add_extensions(x509_req, extensions) assert res == 1 diff --git a/tests/test_x509.py b/tests/test_x509.py index 94eeab2b..b2262c71 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -978,6 +978,31 @@ class TestCertificateSigningRequestBuilder(object): with pytest.raises(NotImplementedError): builder.sign(private_key, hashes.SHA256(), backend) + def test_add_two_extensions(self, backend): + private_key = RSA_KEY_2048.private_key(backend) + builder = x509.CertificateSigningRequestBuilder() + request = builder.subject_name( + x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')]) + ).add_extension( + x509.SubjectAlternativeName([x509.DNSName(u"cryptography.io")]), + critical=False, + ).add_extension( + x509.BasicConstraints(ca=True, path_length=2), critical=True + ).sign(private_key, hashes.SHA1(), backend) + + assert isinstance(request.signature_hash_algorithm, hashes.SHA1) + public_key = request.public_key() + assert isinstance(public_key, rsa.RSAPublicKey) + basic_constraints = request.extensions.get_extension_for_oid( + x509.OID_BASIC_CONSTRAINTS + ) + assert basic_constraints.value.ca is True + assert basic_constraints.value.path_length == 2 + ext = request.extensions.get_extension_for_oid( + x509.OID_SUBJECT_ALTERNATIVE_NAME + ) + assert list(ext.value) == [x509.DNSName(u"cryptography.io")] + def test_set_subject_twice(self): builder = x509.CertificateSigningRequestBuilder() builder = builder.subject_name( -- cgit v1.2.3 From dce91f0b2923daf60a6fdfd811eb5b3d81ac7c88 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Thu, 23 Jul 2015 20:31:12 +0100 Subject: Support encoding KeyUsage into certificate signing requests --- src/_cffi_src/openssl/asn1.py | 2 + .../hazmat/backends/openssl/backend.py | 40 +++++++++++++ src/cryptography/hazmat/backends/openssl/x509.py | 1 + src/cryptography/x509.py | 2 + tests/test_x509.py | 66 +++++++++++++++++++++- 5 files changed, 109 insertions(+), 2 deletions(-) diff --git a/src/_cffi_src/openssl/asn1.py b/src/_cffi_src/openssl/asn1.py index 5f8ca697..96eff7d3 100644 --- a/src/_cffi_src/openssl/asn1.py +++ b/src/_cffi_src/openssl/asn1.py @@ -125,7 +125,9 @@ int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *, int, int); """ MACROS = """ +ASN1_BIT_STRING *ASN1_BIT_STRING_new(void); void ASN1_BIT_STRING_free(ASN1_BIT_STRING *); +int i2d_ASN1_BIT_STRING(ASN1_BIT_STRING *, unsigned char **); /* This is not a macro, but is const on some versions of OpenSSL */ int ASN1_BIT_STRING_get_bit(ASN1_BIT_STRING *, int); ASN1_TIME *M_ASN1_TIME_dup(void *); diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 9cfe73c1..046a1739 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -131,6 +131,44 @@ def _txt2obj(backend, name): return obj +def _encode_key_usage(backend, key_usage): + set_bit = backend._lib.ASN1_BIT_STRING_set_bit + ku = backend._lib.ASN1_BIT_STRING_new() + ku = backend._ffi.gc(ku, backend._lib.ASN1_BIT_STRING_free) + res = set_bit(ku, 0, key_usage.digital_signature) + assert res == 1 + res = set_bit(ku, 1, key_usage.content_commitment) + assert res == 1 + res = set_bit(ku, 2, key_usage.key_encipherment) + assert res == 1 + res = set_bit(ku, 3, key_usage.data_encipherment) + assert res == 1 + res = set_bit(ku, 4, key_usage.key_agreement) + assert res == 1 + res = set_bit(ku, 5, key_usage.key_cert_sign) + assert res == 1 + res = set_bit(ku, 6, key_usage.crl_sign) + assert res == 1 + if key_usage.key_agreement: + res = set_bit(ku, 7, key_usage.encipher_only) + assert res == 1 + res = set_bit(ku, 8, key_usage.decipher_only) + assert res == 1 + else: + res = set_bit(ku, 7, 0) + assert res == 1 + res = set_bit(ku, 8, 0) + assert res == 1 + + pp = backend._ffi.new('unsigned char **') + r = backend._lib.i2d_ASN1_BIT_STRING(ku, pp) + assert r > 0 + pp = backend._ffi.gc( + pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0]) + ) + return pp, r + + def _encode_basic_constraints(backend, basic_constraints): constraints = backend._lib.BASIC_CONSTRAINTS_new() constraints = backend._ffi.gc( @@ -964,6 +1002,8 @@ class Backend(object): pp, r = _encode_basic_constraints(self, extension.value) elif isinstance(extension.value, x509.SubjectAlternativeName): pp, r = _encode_subject_alt_name(self, extension.value) + elif isinstance(extension.value, x509.KeyUsage): + pp, r = _encode_key_usage(self, extension.value) else: raise NotImplementedError('Extension not yet supported.') diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 472d8a70..943cfd78 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -816,6 +816,7 @@ _CSR_EXTENSION_PARSER = _X509ExtensionParser( get_ext=lambda backend, x, i: backend._lib.sk_X509_EXTENSION_value(x, i), handlers={ x509.OID_BASIC_CONSTRAINTS: _decode_basic_constraints, + x509.OID_KEY_USAGE: _decode_key_usage, x509.OID_SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name, } ) diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 58e1a37c..5d108ee6 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1577,6 +1577,8 @@ class CertificateSigningRequestBuilder(object): ) elif isinstance(extension, KeyUsage): extension = Extension(OID_KEY_USAGE, critical, extension) + elif isinstance(extension, InhibitAnyPolicy): + extension = Extension(OID_INHIBIT_ANY_POLICY, critical, extension) else: raise NotImplementedError('Unsupported X.509 extension.') # TODO: This is quadratic in the number of extensions diff --git a/tests/test_x509.py b/tests/test_x509.py index b2262c71..af7d9421 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -961,6 +961,20 @@ class TestCertificateSigningRequestBuilder(object): ).add_extension( x509.SubjectAlternativeName([x509.DNSName(u"cryptography.io")]), critical=False, + ).add_extension( + x509.InhibitAnyPolicy(0), + critical=False + ) + with pytest.raises(NotImplementedError): + builder.sign(private_key, hashes.SHA256(), backend) + + def test_key_usage(self, backend): + private_key = RSA_KEY_2048.private_key(backend) + builder = x509.CertificateSigningRequestBuilder() + request = builder.subject_name( + x509.Name([ + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + ]) ).add_extension( x509.KeyUsage( digital_signature=True, @@ -974,9 +988,57 @@ class TestCertificateSigningRequestBuilder(object): decipher_only=False ), critical=False + ).sign(private_key, hashes.SHA256(), backend) + assert len(request.extensions) == 1 + ext = request.extensions.get_extension_for_oid(x509.OID_KEY_USAGE) + assert ext.critical is False + assert ext.value == x509.KeyUsage( + digital_signature=True, + content_commitment=True, + key_encipherment=False, + data_encipherment=False, + key_agreement=False, + key_cert_sign=True, + crl_sign=False, + encipher_only=False, + decipher_only=False + ) + + def test_key_usage_key_agreement_bit(self, backend): + private_key = RSA_KEY_2048.private_key(backend) + builder = x509.CertificateSigningRequestBuilder() + request = builder.subject_name( + x509.Name([ + x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US'), + ]) + ).add_extension( + x509.KeyUsage( + digital_signature=False, + content_commitment=False, + key_encipherment=False, + data_encipherment=False, + key_agreement=True, + key_cert_sign=True, + crl_sign=False, + encipher_only=False, + decipher_only=True + ), + critical=False + ).sign(private_key, hashes.SHA256(), backend) + assert len(request.extensions) == 1 + ext = request.extensions.get_extension_for_oid(x509.OID_KEY_USAGE) + assert ext.critical is False + assert ext.value == x509.KeyUsage( + digital_signature=False, + content_commitment=False, + key_encipherment=False, + data_encipherment=False, + key_agreement=True, + key_cert_sign=True, + crl_sign=False, + encipher_only=False, + decipher_only=True ) - with pytest.raises(NotImplementedError): - builder.sign(private_key, hashes.SHA256(), backend) def test_add_two_extensions(self, backend): private_key = RSA_KEY_2048.private_key(backend) -- cgit v1.2.3 From 0b8f327f59b5a890f2d2ad9101391a0b818e186a Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Thu, 23 Jul 2015 21:46:21 +0100 Subject: Support encoding ExtendedKeyUsage into certificate signing requests --- src/_cffi_src/openssl/x509.py | 2 ++ src/_cffi_src/openssl/x509v3.py | 3 +++ .../hazmat/backends/openssl/backend.py | 30 ++++++++++++++++++++-- src/cryptography/hazmat/backends/openssl/x509.py | 1 + src/cryptography/x509.py | 2 ++ tests/test_x509.py | 23 +++++++++++++++++ 6 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/_cffi_src/openssl/x509.py b/src/_cffi_src/openssl/x509.py index 6bd117b0..bdcc1719 100644 --- a/src/_cffi_src/openssl/x509.py +++ b/src/_cffi_src/openssl/x509.py @@ -325,6 +325,8 @@ int i2o_ECPublicKey(EC_KEY *, unsigned char **); int sk_ASN1_OBJECT_num(Cryptography_STACK_OF_ASN1_OBJECT *); ASN1_OBJECT *sk_ASN1_OBJECT_value(Cryptography_STACK_OF_ASN1_OBJECT *, int); void sk_ASN1_OBJECT_free(Cryptography_STACK_OF_ASN1_OBJECT *); +Cryptography_STACK_OF_ASN1_OBJECT *sk_ASN1_OBJECT_new_null(void); +int sk_ASN1_OBJECT_push(Cryptography_STACK_OF_ASN1_OBJECT *, ASN1_OBJECT *); """ CUSTOMIZATIONS = """ diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py index 8e42b65d..148cd64b 100644 --- a/src/_cffi_src/openssl/x509v3.py +++ b/src/_cffi_src/openssl/x509v3.py @@ -33,6 +33,7 @@ typedef ... Cryptography_STACK_OF_POLICYQUALINFO; typedef ... Cryptography_STACK_OF_POLICYINFO; typedef ... Cryptography_STACK_OF_ASN1_INTEGER; typedef ... Cryptography_STACK_OF_GENERAL_SUBTREE; +typedef ... EXTENDED_KEY_USAGE; typedef struct { X509 *issuer_cert; @@ -200,6 +201,8 @@ void *X509V3_set_ctx_nodb(X509V3_CTX *); int i2d_GENERAL_NAMES(GENERAL_NAMES *, unsigned char **); +int i2d_EXTENDED_KEY_USAGE(EXTENDED_KEY_USAGE *, unsigned char **); + int sk_GENERAL_NAME_num(struct stack_st_GENERAL_NAME *); int sk_GENERAL_NAME_push(struct stack_st_GENERAL_NAME *, GENERAL_NAME *); GENERAL_NAME *sk_GENERAL_NAME_value(struct stack_st_GENERAL_NAME *, int); diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 046a1739..64d518a1 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -101,7 +101,7 @@ def _encode_name(backend, attributes): subject = backend._lib.X509_NAME_new() for attribute in attributes: value = attribute.value.encode('utf8') - obj = _txt2obj(backend, attribute.oid.dotted_string) + obj = _txt2obj_gc(backend, attribute.oid.dotted_string) res = backend._lib.X509_NAME_add_entry_by_OBJ( subject, obj, @@ -127,6 +127,11 @@ def _txt2obj(backend, name): name = name.encode('ascii') obj = backend._lib.OBJ_txt2obj(name, 1) assert obj != backend._ffi.NULL + return obj + + +def _txt2obj_gc(backend, name): + obj = _txt2obj(backend, name) obj = backend._ffi.gc(obj, backend._lib.ASN1_OBJECT_free) return obj @@ -293,6 +298,25 @@ def _encode_subject_alt_name(backend, san): return pp, r +def _encode_extended_key_usage(backend, extended_key_usage): + eku = backend._lib.sk_ASN1_OBJECT_new_null() + eku = backend._ffi.gc(eku, backend._lib.sk_ASN1_OBJECT_free) + for oid in extended_key_usage: + obj = _txt2obj(backend, oid.dotted_string) + res = backend._lib.sk_ASN1_OBJECT_push(eku, obj) + assert res >= 1 + + pp = backend._ffi.new('unsigned char **') + r = backend._lib.i2d_EXTENDED_KEY_USAGE( + backend._ffi.cast("EXTENDED_KEY_USAGE *", eku), pp + ) + assert r > 0 + pp = backend._ffi.gc( + pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0]) + ) + return pp, r + + @utils.register_interface(CipherBackend) @utils.register_interface(CMACBackend) @utils.register_interface(DERSerializationBackend) @@ -1004,10 +1028,12 @@ class Backend(object): pp, r = _encode_subject_alt_name(self, extension.value) elif isinstance(extension.value, x509.KeyUsage): pp, r = _encode_key_usage(self, extension.value) + elif isinstance(extension.value, x509.ExtendedKeyUsage): + pp, r = _encode_extended_key_usage(self, extension.value) else: raise NotImplementedError('Extension not yet supported.') - obj = _txt2obj(self, extension.oid.dotted_string) + obj = _txt2obj_gc(self, extension.oid.dotted_string) extension = self._lib.X509_EXTENSION_create_by_OBJ( self._ffi.NULL, obj, diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 943cfd78..493abc83 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -818,5 +818,6 @@ _CSR_EXTENSION_PARSER = _X509ExtensionParser( x509.OID_BASIC_CONSTRAINTS: _decode_basic_constraints, x509.OID_KEY_USAGE: _decode_key_usage, x509.OID_SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name, + x509.OID_EXTENDED_KEY_USAGE: _decode_extended_key_usage, } ) diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 5d108ee6..75552fc1 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1571,6 +1571,8 @@ class CertificateSigningRequestBuilder(object): """ if isinstance(extension, BasicConstraints): extension = Extension(OID_BASIC_CONSTRAINTS, critical, extension) + elif isinstance(extension, ExtendedKeyUsage): + extension = Extension(OID_EXTENDED_KEY_USAGE, critical, extension) elif isinstance(extension, SubjectAlternativeName): extension = Extension( OID_SUBJECT_ALTERNATIVE_NAME, critical, extension diff --git a/tests/test_x509.py b/tests/test_x509.py index af7d9421..cacf3c88 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -1183,6 +1183,29 @@ class TestCertificateSigningRequestBuilder(object): with pytest.raises(ValueError): builder.sign(private_key, hashes.SHA256(), backend) + def test_extended_key_usage(self, backend): + private_key = RSA_KEY_2048.private_key(backend) + builder = x509.CertificateSigningRequestBuilder() + request = builder.subject_name( + x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')]) + ).add_extension( + x509.ExtendedKeyUsage([ + x509.OID_CLIENT_AUTH, + x509.OID_SERVER_AUTH, + x509.OID_CODE_SIGNING, + ]), critical=False + ).sign(private_key, hashes.SHA256(), backend) + + eku = request.extensions.get_extension_for_oid( + x509.OID_EXTENDED_KEY_USAGE + ) + assert eku.critical is False + assert eku.value == x509.ExtendedKeyUsage([ + x509.OID_CLIENT_AUTH, + x509.OID_SERVER_AUTH, + x509.OID_CODE_SIGNING, + ]) + @pytest.mark.requires_backend_interface(interface=DSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) -- cgit v1.2.3 From a5e8a75a0cd475877c3a8e8b07d00fd9f6dbb9e6 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 24 Jul 2015 16:05:23 +0100 Subject: refactor general name encoding to its own function --- .../hazmat/backends/openssl/backend.py | 171 +++++++++++---------- 1 file changed, 88 insertions(+), 83 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 64d518a1..2d2ecc81 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -203,89 +203,7 @@ def _encode_subject_alt_name(backend, san): ) for alt_name in san: - if isinstance(alt_name, x509.DNSName): - gn = backend._lib.GENERAL_NAME_new() - assert gn != backend._ffi.NULL - gn.type = backend._lib.GEN_DNS - - ia5 = backend._lib.ASN1_IA5STRING_new() - assert ia5 != backend._ffi.NULL - - if alt_name.value.startswith(u"*."): - value = b"*." + idna.encode(alt_name.value[2:]) - else: - value = idna.encode(alt_name.value) - - res = backend._lib.ASN1_STRING_set(ia5, value, len(value)) - assert res == 1 - gn.d.dNSName = ia5 - elif isinstance(alt_name, x509.RegisteredID): - gn = backend._lib.GENERAL_NAME_new() - assert gn != backend._ffi.NULL - gn.type = backend._lib.GEN_RID - obj = backend._lib.OBJ_txt2obj( - alt_name.value.dotted_string.encode('ascii'), 1 - ) - assert obj != backend._ffi.NULL - gn.d.registeredID = obj - elif isinstance(alt_name, x509.DirectoryName): - gn = backend._lib.GENERAL_NAME_new() - assert gn != backend._ffi.NULL - name = _encode_name(backend, alt_name.value) - gn.type = backend._lib.GEN_DIRNAME - gn.d.directoryName = name - elif isinstance(alt_name, x509.IPAddress): - gn = backend._lib.GENERAL_NAME_new() - assert gn != backend._ffi.NULL - ipaddr = _encode_asn1_str( - backend, alt_name.value.packed, len(alt_name.value.packed) - ) - gn.type = backend._lib.GEN_IPADD - gn.d.iPAddress = ipaddr - elif isinstance(alt_name, x509.OtherName): - gn = backend._lib.GENERAL_NAME_new() - assert gn != backend._ffi.NULL - other_name = backend._lib.OTHERNAME_new() - assert other_name != backend._ffi.NULL - - type_id = backend._lib.OBJ_txt2obj( - alt_name.type_id.dotted_string.encode('ascii'), 1 - ) - assert type_id != backend._ffi.NULL - data = backend._ffi.new("unsigned char[]", alt_name.value) - data_ptr_ptr = backend._ffi.new("unsigned char **") - data_ptr_ptr[0] = data - value = backend._lib.d2i_ASN1_TYPE( - backend._ffi.NULL, data_ptr_ptr, len(alt_name.value) - ) - if value == backend._ffi.NULL: - backend._consume_errors() - raise ValueError("Invalid ASN.1 data") - other_name.type_id = type_id - other_name.value = value - gn.type = backend._lib.GEN_OTHERNAME - gn.d.otherName = other_name - elif isinstance(alt_name, x509.RFC822Name): - gn = backend._lib.GENERAL_NAME_new() - assert gn != backend._ffi.NULL - asn1_str = _encode_asn1_str( - backend, alt_name._encoded, len(alt_name._encoded) - ) - gn.type = backend._lib.GEN_EMAIL - gn.d.rfc822Name = asn1_str - elif isinstance(alt_name, x509.UniformResourceIdentifier): - gn = backend._lib.GENERAL_NAME_new() - assert gn != backend._ffi.NULL - asn1_str = _encode_asn1_str( - backend, alt_name._encoded, len(alt_name._encoded) - ) - gn.type = backend._lib.GEN_URI - gn.d.uniformResourceIdentifier = asn1_str - else: - raise ValueError( - "{0} is an unknown GeneralName type".format(alt_name) - ) - + gn = _encode_general_name(backend, alt_name) res = backend._lib.sk_GENERAL_NAME_push(general_names, gn) assert res != 0 @@ -298,6 +216,93 @@ def _encode_subject_alt_name(backend, san): return pp, r +def _encode_general_name(backend, name): + if isinstance(name, x509.DNSName): + gn = backend._lib.GENERAL_NAME_new() + assert gn != backend._ffi.NULL + gn.type = backend._lib.GEN_DNS + + ia5 = backend._lib.ASN1_IA5STRING_new() + assert ia5 != backend._ffi.NULL + + if name.value.startswith(u"*."): + value = b"*." + idna.encode(name.value[2:]) + else: + value = idna.encode(name.value) + + res = backend._lib.ASN1_STRING_set(ia5, value, len(value)) + assert res == 1 + gn.d.dNSName = ia5 + elif isinstance(name, x509.RegisteredID): + gn = backend._lib.GENERAL_NAME_new() + assert gn != backend._ffi.NULL + gn.type = backend._lib.GEN_RID + obj = backend._lib.OBJ_txt2obj( + name.value.dotted_string.encode('ascii'), 1 + ) + assert obj != backend._ffi.NULL + gn.d.registeredID = obj + elif isinstance(name, x509.DirectoryName): + gn = backend._lib.GENERAL_NAME_new() + assert gn != backend._ffi.NULL + dir_name = _encode_name(backend, name.value) + gn.type = backend._lib.GEN_DIRNAME + gn.d.directoryName = dir_name + elif isinstance(name, x509.IPAddress): + gn = backend._lib.GENERAL_NAME_new() + assert gn != backend._ffi.NULL + ipaddr = _encode_asn1_str( + backend, name.value.packed, len(name.value.packed) + ) + gn.type = backend._lib.GEN_IPADD + gn.d.iPAddress = ipaddr + elif isinstance(name, x509.OtherName): + gn = backend._lib.GENERAL_NAME_new() + assert gn != backend._ffi.NULL + other_name = backend._lib.OTHERNAME_new() + assert other_name != backend._ffi.NULL + + type_id = backend._lib.OBJ_txt2obj( + name.type_id.dotted_string.encode('ascii'), 1 + ) + assert type_id != backend._ffi.NULL + data = backend._ffi.new("unsigned char[]", name.value) + data_ptr_ptr = backend._ffi.new("unsigned char **") + data_ptr_ptr[0] = data + value = backend._lib.d2i_ASN1_TYPE( + backend._ffi.NULL, data_ptr_ptr, len(name.value) + ) + if value == backend._ffi.NULL: + backend._consume_errors() + raise ValueError("Invalid ASN.1 data") + other_name.type_id = type_id + other_name.value = value + gn.type = backend._lib.GEN_OTHERNAME + gn.d.otherName = other_name + elif isinstance(name, x509.RFC822Name): + gn = backend._lib.GENERAL_NAME_new() + assert gn != backend._ffi.NULL + asn1_str = _encode_asn1_str( + backend, name._encoded, len(name._encoded) + ) + gn.type = backend._lib.GEN_EMAIL + gn.d.rfc822Name = asn1_str + elif isinstance(name, x509.UniformResourceIdentifier): + gn = backend._lib.GENERAL_NAME_new() + assert gn != backend._ffi.NULL + asn1_str = _encode_asn1_str( + backend, name._encoded, len(name._encoded) + ) + gn.type = backend._lib.GEN_URI + gn.d.uniformResourceIdentifier = asn1_str + else: + raise ValueError( + "{0} is an unknown GeneralName type".format(name) + ) + + return gn + + def _encode_extended_key_usage(backend, extended_key_usage): eku = backend._lib.sk_ASN1_OBJECT_new_null() eku = backend._ffi.gc(eku, backend._lib.sk_ASN1_OBJECT_free) -- cgit v1.2.3 From a34c0e858d1d5d0cb8bfc550841f476d926dce1c Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 24 Jul 2015 16:05:59 +0100 Subject: bindings for CRL encoding --- src/_cffi_src/openssl/x509v3.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py index 148cd64b..cb31ba1e 100644 --- a/src/_cffi_src/openssl/x509v3.py +++ b/src/_cffi_src/openssl/x509v3.py @@ -223,9 +223,11 @@ X509_EXTENSION *X509V3_EXT_conf_nid(Cryptography_LHASH_OF_CONF_VALUE *, const X509V3_EXT_METHOD *X509V3_EXT_get(X509_EXTENSION *); const X509V3_EXT_METHOD *X509V3_EXT_get_nid(int); +Cryptography_STACK_OF_DIST_POINT *sk_DIST_POINT_new_null(void); void sk_DIST_POINT_free(Cryptography_STACK_OF_DIST_POINT *); int sk_DIST_POINT_num(Cryptography_STACK_OF_DIST_POINT *); DIST_POINT *sk_DIST_POINT_value(Cryptography_STACK_OF_DIST_POINT *, int); +int sk_DIST_POINT_push(Cryptography_STACK_OF_DIST_POINT *, DIST_POINT *); void sk_POLICYINFO_free(Cryptography_STACK_OF_POLICYINFO *); int sk_POLICYINFO_num(Cryptography_STACK_OF_POLICYINFO *); @@ -249,6 +251,11 @@ int sk_ASN1_INTEGER_num(Cryptography_STACK_OF_ASN1_INTEGER *); ASN1_INTEGER *sk_ASN1_INTEGER_value(Cryptography_STACK_OF_ASN1_INTEGER *, int); X509_EXTENSION *X509V3_EXT_i2d(int, int, void *); + +DIST_POINT *DIST_POINT_new(void); +void DIST_POINT_free(DIST_POINT *); + +int i2d_CRL_DIST_POINTS(Cryptography_STACK_OF_DIST_POINT *, unsigned char **); """ CUSTOMIZATIONS = """ -- cgit v1.2.3 From eaac67b1bc25799102488f4a3338e9403ddde9d0 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 24 Jul 2015 20:15:33 +0100 Subject: let's also add the binding to make a new GENERAL_SUBTREE stack --- src/_cffi_src/openssl/x509v3.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py index cb31ba1e..d0a57349 100644 --- a/src/_cffi_src/openssl/x509v3.py +++ b/src/_cffi_src/openssl/x509v3.py @@ -238,6 +238,7 @@ int sk_POLICYQUALINFO_num(Cryptography_STACK_OF_POLICYQUALINFO *); POLICYQUALINFO *sk_POLICYQUALINFO_value(Cryptography_STACK_OF_POLICYQUALINFO *, int); +Cryptography_STACK_OF_GENERAL_SUBTREE *sk_GENERAL_SUBTREE_new_null(void); void sk_GENERAL_SUBTREE_free(Cryptography_STACK_OF_GENERAL_SUBTREE *); int sk_GENERAL_SUBTREE_num(Cryptography_STACK_OF_GENERAL_SUBTREE *); GENERAL_SUBTREE *sk_GENERAL_SUBTREE_value( -- cgit v1.2.3 From 6608b7e98e6669404c981ad981c277767998f129 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 24 Jul 2015 21:52:17 +0100 Subject: remove windows link type, update docs --- docs/installation.rst | 25 ++++++++----------------- src/_cffi_src/build_openssl.py | 15 +-------------- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 96e1e8de..becab6b0 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -45,28 +45,18 @@ dependencies are included. Just run $ pip install cryptography If you prefer to compile it yourself you'll need to have OpenSSL installed. -There are `pre-compiled binaries`_ available. If your installation is in an -unusual location set the ``LIB`` and ``INCLUDE`` environment variables to -include the corresponding locations.For example: +You can compile OpenSSL yourself as well or use the binaries we build for our +release infrastructure (`32-bit`_ and `64-bit`_). Wherever you place your copy +of OpenSSL you'll need to set the ``LIB`` and ``INCLUDE`` environment variables +to include the proper locations. For example: .. code-block:: console C:\> \path\to\vcvarsall.bat x86_amd64 - C:\> set LIB=C:\OpenSSL\lib\VC\static;C:\OpenSSL\lib;%LIB% - C:\> set INCLUDE=C:\OpenSSL\include;%INCLUDE% + C:\> set LIB=C:\OpenSSL-win64\lib;%LIB% + C:\> set INCLUDE=C:\OpenSSL-win64\include;%INCLUDE% C:\> pip install cryptography -You can also choose to build statically or dynamically using the -``PYCA_WINDOWS_LINK_TYPE`` variable. Allowed values are ``static`` (default) -and ``dynamic``. - -.. code-block:: console - - C:\> \path\to\vcvarsall.bat x86_amd64 - C:\> set LIB=C:\OpenSSL\lib\VC\static;C:\OpenSSL\lib;%LIB% - C:\> set INCLUDE=C:\OpenSSL\include;%INCLUDE% - C:\> set PYCA_WINDOWS_LINK_TYPE=dynamic - C:\> pip install cryptography Building cryptography on Linux ------------------------------ @@ -186,6 +176,7 @@ information, consult `Greg Wilson's blog post`_ on the subject. .. _`Homebrew`: http://brew.sh .. _`MacPorts`: http://www.macports.org -.. _`pre-compiled binaries`: https://www.openssl.org/related/binaries.html +.. _`32-bit`: https://jenkins.cryptography.io/job/openssl-win32-release/ +.. _`64-bit`: https://jenkins.cryptography.io/job/openssl-win64-release/ .. _`bug in conda`: https://github.com/conda/conda-recipes/issues/110 .. _`Greg Wilson's blog post`: http://software-carpentry.org/blog/2014/04/mr-biczo-was-right.html diff --git a/src/_cffi_src/build_openssl.py b/src/_cffi_src/build_openssl.py index dac3e4d8..6a5bf2da 100644 --- a/src/_cffi_src/build_openssl.py +++ b/src/_cffi_src/build_openssl.py @@ -4,7 +4,6 @@ from __future__ import absolute_import, division, print_function -import os import sys from _cffi_src.utils import build_ffi_for_binding, extra_link_args @@ -19,20 +18,8 @@ def _get_openssl_libraries(platform): # (http://marc.info/?l=openssl-users&m=135361825921871) return ["ssl", "crypto"] else: - link_type = os.environ.get("PYCA_WINDOWS_LINK_TYPE", "static") - return _get_openssl_windows_libraries(link_type) - - -def _get_openssl_windows_libraries(link_type): - if link_type == "dynamic": - return ["libeay32", "ssleay32", "advapi32"] - elif link_type == "static" or link_type == "": - return ["libeay32mt", "ssleay32mt", "advapi32", + return ["libeay32", "ssleay32", "advapi32", "crypt32", "gdi32", "user32", "ws2_32"] - else: - raise ValueError( - "PYCA_WINDOWS_LINK_TYPE must be 'static' or 'dynamic'" - ) _OSX_PRE_INCLUDE = """ -- cgit v1.2.3 From 4e4a9ba524efe4963961c62c6da915a834ca185c Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 25 Jul 2015 18:49:35 +0100 Subject: handle RSA key too small and consume errors on CSR signature failure --- src/_cffi_src/openssl/err.py | 1 + src/cryptography/hazmat/backends/openssl/backend.py | 11 ++++++++++- tests/test_x509.py | 13 +++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/_cffi_src/openssl/err.py b/src/_cffi_src/openssl/err.py index eebf19ba..73ce4e3c 100644 --- a/src/_cffi_src/openssl/err.py +++ b/src/_cffi_src/openssl/err.py @@ -230,6 +230,7 @@ static const int RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY; static const int RSA_R_BLOCK_TYPE_IS_NOT_01; static const int RSA_R_BLOCK_TYPE_IS_NOT_02; static const int RSA_R_PKCS_DECODING_ERROR; +static const int RSA_F_RSA_SIGN; """ FUNCTIONS = """ diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 2d2ecc81..dd89623e 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1055,7 +1055,16 @@ class Backend(object): res = self._lib.X509_REQ_sign( x509_req, private_key._evp_pkey, evp_md ) - assert res > 0 + if res == 0: + errors = self._consume_errors() + assert errors[0][1:] in ( + ( + self._lib.ERR_LIB_RSA, + self._lib.RSA_F_RSA_SIGN, + self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY + ), + ) + raise ValueError("Digest too big for RSA key") return _CertificateSigningRequest(self, x509_req) diff --git a/tests/test_x509.py b/tests/test_x509.py index cacf3c88..38432271 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -1206,6 +1206,19 @@ class TestCertificateSigningRequestBuilder(object): x509.OID_CODE_SIGNING, ]) + @pytest.mark.requires_backend_interface(interface=RSABackend) + def test_rsa_key_too_small(self, backend): + private_key = rsa.generate_private_key(65537, 512, backend) + builder = x509.CertificateSigningRequestBuilder() + builder = builder.subject_name( + x509.Name([x509.NameAttribute(x509.OID_COUNTRY_NAME, u'US')]) + ) + + with pytest.raises(ValueError) as exc: + builder.sign(private_key, hashes.SHA512(), backend) + + assert exc.value.message == "Digest too big for RSA key" + @pytest.mark.requires_backend_interface(interface=DSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) -- cgit v1.2.3 From 28fae4e437a8487e6d5414ce5e3f7904f412995d Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 25 Jul 2015 19:07:21 +0100 Subject: == instead of in --- src/cryptography/hazmat/backends/openssl/backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index dd89623e..8b332c53 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1057,12 +1057,12 @@ class Backend(object): ) if res == 0: errors = self._consume_errors() - assert errors[0][1:] in ( + assert errors[0][1:] == ( ( self._lib.ERR_LIB_RSA, self._lib.RSA_F_RSA_SIGN, self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY - ), + ) ) raise ValueError("Digest too big for RSA key") -- cgit v1.2.3 From 195a1ecb96d7ac7fc0d9ce39340e747531e5ad96 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 25 Jul 2015 19:12:19 +0100 Subject: extra parens --- src/cryptography/hazmat/backends/openssl/backend.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 8b332c53..76252404 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1058,11 +1058,9 @@ class Backend(object): if res == 0: errors = self._consume_errors() assert errors[0][1:] == ( - ( - self._lib.ERR_LIB_RSA, - self._lib.RSA_F_RSA_SIGN, - self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY - ) + self._lib.ERR_LIB_RSA, + self._lib.RSA_F_RSA_SIGN, + self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY ) raise ValueError("Digest too big for RSA key") -- cgit v1.2.3 From 6a71f8d9972032c5f034ba47bcf3439c9ffd3494 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 25 Jul 2015 19:15:59 +0100 Subject: py3 fixin' --- tests/test_x509.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_x509.py b/tests/test_x509.py index 38432271..98cf49be 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -1217,7 +1217,7 @@ class TestCertificateSigningRequestBuilder(object): with pytest.raises(ValueError) as exc: builder.sign(private_key, hashes.SHA512(), backend) - assert exc.value.message == "Digest too big for RSA key" + assert str(exc.value) == "Digest too big for RSA key" @pytest.mark.requires_backend_interface(interface=DSABackend) -- cgit v1.2.3 From af57f7d2ce0b77a3e58a40d85b772ddd949f8e89 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 25 Jul 2015 20:04:11 +0100 Subject: openssl error codes are clearly not considered part of the api contract --- src/cryptography/hazmat/backends/openssl/backend.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 76252404..db4f963a 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1057,11 +1057,8 @@ class Backend(object): ) if res == 0: errors = self._consume_errors() - assert errors[0][1:] == ( - self._lib.ERR_LIB_RSA, - self._lib.RSA_F_RSA_SIGN, - self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY - ) + assert errors[0][1] == self._lib.ERR_LIB_RSA + assert errors[0][3] == self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY raise ValueError("Digest too big for RSA key") return _CertificateSigningRequest(self, x509_req) -- cgit v1.2.3 From b314a2359f9a6eb06afd5125f1176de30f1ef0fc Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 26 Jul 2015 22:30:12 +0100 Subject: add many bindings we'll need to encode a certificate policies extension --- src/_cffi_src/openssl/x509v3.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py index d0a57349..a61ad321 100644 --- a/src/_cffi_src/openssl/x509v3.py +++ b/src/_cffi_src/openssl/x509v3.py @@ -232,11 +232,31 @@ int sk_DIST_POINT_push(Cryptography_STACK_OF_DIST_POINT *, DIST_POINT *); void sk_POLICYINFO_free(Cryptography_STACK_OF_POLICYINFO *); int sk_POLICYINFO_num(Cryptography_STACK_OF_POLICYINFO *); POLICYINFO *sk_POLICYINFO_value(Cryptography_STACK_OF_POLICYINFO *, int); +int sk_POLICYINFO_push(Cryptography_STACK_OF_POLICYINFO *, POLICYINFO *); +Cryptography_STACK_OF_POLICYINFO *sk_POLICYINFO_new_null(void); + +POLICYINFO *POLICYINFO_new(void); +void POLICYINFO_free(POLICYINFO *); + +POLICYQUALINFO *POLICYQUALINFO_new(void); +void POLICYQUALINFO_free(POLICYQUALINFO *); + +NOTICEREF *NOTICEREF_new(void); +void NOTICEREF_free(NOTICEREF *); + +USERNOTICE *USERNOTICE_new(void); +void USERNOTICE_free(USERNOTICE *); + +int i2d_CERTIFICATEPOLICIES(Cryptography_STACK_OF_POLICYINFO *, + unsigned char **); void sk_POLICYQUALINFO_free(Cryptography_STACK_OF_POLICYQUALINFO *); int sk_POLICYQUALINFO_num(Cryptography_STACK_OF_POLICYQUALINFO *); POLICYQUALINFO *sk_POLICYQUALINFO_value(Cryptography_STACK_OF_POLICYQUALINFO *, int); +int sk_POLICYQUALINFO_push(Cryptography_STACK_OF_POLICYQUALINFO *, + POLICYQUALINFO *); +Cryptography_STACK_OF_POLICYQUALINFO *sk_POLICYQUALINFO_new_null(void); Cryptography_STACK_OF_GENERAL_SUBTREE *sk_GENERAL_SUBTREE_new_null(void); void sk_GENERAL_SUBTREE_free(Cryptography_STACK_OF_GENERAL_SUBTREE *); @@ -250,6 +270,7 @@ int sk_GENERAL_SUBTREE_push(Cryptography_STACK_OF_GENERAL_SUBTREE *, void sk_ASN1_INTEGER_free(Cryptography_STACK_OF_ASN1_INTEGER *); int sk_ASN1_INTEGER_num(Cryptography_STACK_OF_ASN1_INTEGER *); ASN1_INTEGER *sk_ASN1_INTEGER_value(Cryptography_STACK_OF_ASN1_INTEGER *, int); +int sk_ASN1_INTEGER_push(Cryptography_STACK_OF_ASN1_INTEGER *, ASN1_INTEGER *); X509_EXTENSION *X509V3_EXT_i2d(int, int, void *); -- cgit v1.2.3 From ed07bbc76da5c582fa7449321759ad0598b6e20c Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 14:16:00 +0100 Subject: move urandom engine test This test was in the bindings dir, which is incorrect. We do not set the urandom engine to default unless the openssl backend is loaded. The reason the test wasn't failing (even in the random test case) is that the backends are loaded during pytest_generate_tests by a call to _available_backends. So no matter what order it occurred in the engine was already set to default. I discovered this when I tried to run the test_openssl.py bindings tests directly via pytest. Hooray global state. --- tests/hazmat/backends/test_openssl.py | 27 +++++++++++++++++++++++++++ tests/hazmat/bindings/test_openssl.py | 30 ------------------------------ 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index 6a2e8a77..a8401b30 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -34,6 +34,20 @@ from ..primitives.test_ec import _skip_curve_unsupported from ...utils import load_vectors_from_file, raises_unsupported_algorithm +def skip_if_libre_ssl(openssl_version): + if b'LibreSSL' in openssl_version: + pytest.skip("LibreSSL hard-codes RAND_bytes to use arc4random.") + + +class TestLibreSkip(object): + def test_skip_no(self): + assert skip_if_libre_ssl(b"OpenSSL 0.9.8zf 19 Mar 2015") is None + + def test_skip_yes(self): + with pytest.raises(pytest.skip.Exception): + skip_if_libre_ssl(b"LibreSSL 2.1.6") + + @utils.register_interface(Mode) class DummyMode(object): name = "dummy-mode" @@ -216,6 +230,19 @@ class TestOpenSSL(object): bn = backend._int_to_bn(0) assert backend._bn_to_int(bn) == 0 + def test_actual_osrandom_bytes(self, monkeypatch): + skip_if_libre_ssl(backend.openssl_version_text()) + sample_data = (b"\x01\x02\x03\x04" * 4) + length = len(sample_data) + + def notrandom(size): + assert size == length + return sample_data + monkeypatch.setattr(os, "urandom", notrandom) + buf = backend._ffi.new("char[]", length) + backend._lib.RAND_bytes(buf, length) + assert backend._ffi.buffer(buf)[0:length] == sample_data + class TestOpenSSLRandomEngine(object): def teardown_method(self, method): diff --git a/tests/hazmat/bindings/test_openssl.py b/tests/hazmat/bindings/test_openssl.py index f3f2eaf4..75a8e3f1 100644 --- a/tests/hazmat/bindings/test_openssl.py +++ b/tests/hazmat/bindings/test_openssl.py @@ -4,27 +4,11 @@ from __future__ import absolute_import, division, print_function -import os - import pytest from cryptography.hazmat.bindings.openssl.binding import Binding -def skip_if_libre_ssl(openssl_version): - if b'LibreSSL' in openssl_version: - pytest.skip("LibreSSL hard-codes RAND_bytes to use arc4random.") - - -class TestLibreSkip(object): - def test_skip_no(self): - assert skip_if_libre_ssl(b"OpenSSL 0.9.8zf 19 Mar 2015") is None - - def test_skip_yes(self): - with pytest.raises(pytest.skip.Exception): - skip_if_libre_ssl(b"LibreSSL 2.1.6") - - class TestOpenSSL(object): def test_binding_loads(self): binding = Binding() @@ -108,20 +92,6 @@ class TestOpenSSL(object): with pytest.raises(RuntimeError): b._register_osrandom_engine() - def test_actual_osrandom_bytes(self, monkeypatch): - b = Binding() - skip_if_libre_ssl(b.ffi.string(b.lib.OPENSSL_VERSION_TEXT)) - sample_data = (b"\x01\x02\x03\x04" * 4) - length = len(sample_data) - - def notrandom(size): - assert size == length - return sample_data - monkeypatch.setattr(os, "urandom", notrandom) - buf = b.ffi.new("char[]", length) - b.lib.RAND_bytes(buf, length) - assert b.ffi.buffer(buf)[0:length] == sample_data - def test_ssl_ctx_options(self): # Test that we're properly handling 32-bit unsigned on all platforms. b = Binding() -- cgit v1.2.3 From 301952b141eb346fea4c0308e9c54bd94370b145 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 16:30:57 +0100 Subject: this is gonna be unicode now --- tests/hazmat/backends/test_openssl.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index a8401b30..040e3677 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -35,17 +35,17 @@ from ...utils import load_vectors_from_file, raises_unsupported_algorithm def skip_if_libre_ssl(openssl_version): - if b'LibreSSL' in openssl_version: + if u'LibreSSL' in openssl_version: pytest.skip("LibreSSL hard-codes RAND_bytes to use arc4random.") class TestLibreSkip(object): def test_skip_no(self): - assert skip_if_libre_ssl(b"OpenSSL 0.9.8zf 19 Mar 2015") is None + assert skip_if_libre_ssl(u"OpenSSL 0.9.8zf 19 Mar 2015") is None def test_skip_yes(self): with pytest.raises(pytest.skip.Exception): - skip_if_libre_ssl(b"LibreSSL 2.1.6") + skip_if_libre_ssl(u"LibreSSL 2.1.6") @utils.register_interface(Mode) -- cgit v1.2.3 From bd16e4f7ed3f95cc0572c9ef48b7cae89c5c4707 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 18:52:33 +0100 Subject: Remove elifs and replace with else + assert This is kind of ugly, but resolves many partial branch coverage issues. --- src/cryptography/hazmat/backends/openssl/backend.py | 20 +++++++++++++------- src/cryptography/hazmat/backends/openssl/rsa.py | 5 +++-- src/cryptography/hazmat/backends/openssl/x509.py | 3 ++- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index db4f963a..5ab46d44 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -1577,13 +1577,15 @@ class Backend(object): if format is serialization.PrivateFormat.PKCS8: write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey key = evp_pkey - elif format is serialization.PrivateFormat.TraditionalOpenSSL: + else: + assert format is serialization.PrivateFormat.TraditionalOpenSSL if evp_pkey.type == self._lib.EVP_PKEY_RSA: write_bio = self._lib.PEM_write_bio_RSAPrivateKey elif evp_pkey.type == self._lib.EVP_PKEY_DSA: write_bio = self._lib.PEM_write_bio_DSAPrivateKey - elif (self._lib.Cryptography_HAS_EC == 1 and - evp_pkey.type == self._lib.EVP_PKEY_EC): + else: + assert self._lib.Cryptography_HAS_EC == 1 + assert evp_pkey.type == self._lib.EVP_PKEY_EC write_bio = self._lib.PEM_write_bio_ECPrivateKey key = cdata @@ -1600,7 +1602,8 @@ class Backend(object): return self._private_key_bytes_traditional_der( evp_pkey.type, cdata ) - elif format is serialization.PrivateFormat.PKCS8: + else: + assert format is serialization.PrivateFormat.PKCS8 write_bio = self._lib.i2d_PKCS8PrivateKey_bio key = evp_pkey else: @@ -1625,7 +1628,8 @@ class Backend(object): elif (self._lib.Cryptography_HAS_EC == 1 and key_type == self._lib.EVP_PKEY_EC): write_bio = self._lib.i2d_ECPrivateKey_bio - elif key_type == self._lib.EVP_PKEY_DSA: + else: + assert key_type == self._lib.EVP_PKEY_DSA write_bio = self._lib.i2d_DSAPrivateKey_bio bio = self._create_mem_bio() @@ -1640,7 +1644,8 @@ class Backend(object): if format is serialization.PublicFormat.SubjectPublicKeyInfo: if encoding is serialization.Encoding.PEM: write_bio = self._lib.PEM_write_bio_PUBKEY - elif encoding is serialization.Encoding.DER: + else: + assert encoding is serialization.Encoding.DER write_bio = self._lib.i2d_PUBKEY_bio key = evp_pkey @@ -1649,7 +1654,8 @@ class Backend(object): assert evp_pkey.type == self._lib.EVP_PKEY_RSA if encoding is serialization.Encoding.PEM: write_bio = self._lib.PEM_write_bio_RSAPublicKey - elif encoding is serialization.Encoding.DER: + else: + assert encoding is serialization.Encoding.DER write_bio = self._lib.i2d_RSAPublicKey_bio key = cdata diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py index 21414c05..822c7304 100644 --- a/src/cryptography/hazmat/backends/openssl/rsa.py +++ b/src/cryptography/hazmat/backends/openssl/rsa.py @@ -268,8 +268,9 @@ class _RSASignatureContext(object): self._backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE): reason = ("Salt length too long for key size. Try using " "MAX_LENGTH instead.") - elif (errors[0].reason == - self._backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY): + else: + assert (errors[0].reason == + self._backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY) reason = "Digest too large for key size. Use a larger key." assert reason is not None raise ValueError(reason) diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 493abc83..e7a9a792 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -386,7 +386,8 @@ def _decode_certificate_policies(backend, ext): pqi.d.cpsuri.data, pqi.d.cpsuri.length )[:].decode('ascii') qualifiers.append(cpsuri) - elif pqualid == x509.OID_CPS_USER_NOTICE: + else: + assert pqualid == x509.OID_CPS_USER_NOTICE user_notice = _decode_user_notice( backend, pqi.d.usernotice ) -- cgit v1.2.3 From fcd7d707feba6f02b89fa2d2c9eb3b8a0df5e7f4 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 19:41:25 +0100 Subject: add a line for the ecdh vector loader to cover a missed branch --- tests/test_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_utils.py b/tests/test_utils.py index f71264ea..416de795 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3047,6 +3047,7 @@ d518475576730ed528779366568e46b7dd4ed787cb72d0733c93 def test_load_kasvs_ecdh_vectors(): vector_data = textwrap.dedent(""" + # CAVS 11.0 # Parameter set(s) supported: EA EB EC ED EE # CAVSid: CAVSid (in hex: 434156536964) # IUTid: In hex: a1b2c3d4e5 -- cgit v1.2.3 From b60b8dd64513411f945da5af5f4f6eaa4330827b Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 19:47:22 +0100 Subject: remove some more branching in the vector loaders --- tests/utils.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/utils.py b/tests/utils.py index 5083d48c..7e7abdf1 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -539,8 +539,8 @@ def load_fips_ecdsa_key_pair_vectors(vector_data): elif line.startswith("Qy = "): key_data["y"] = int(line.split("=")[1], 16) - if key_data is not None: - vectors.append(key_data) + assert key_data is not None + vectors.append(key_data) return vectors @@ -559,9 +559,6 @@ def load_fips_ecdsa_signing_vectors(vector_data): for line in vector_data: line = line.strip() - if not line or line.startswith("#"): - continue - curve_match = curve_rx.match(line) if curve_match: curve_name = _ECDSA_CURVE_NAMES[curve_match.group("curve")] @@ -593,8 +590,8 @@ def load_fips_ecdsa_signing_vectors(vector_data): elif line.startswith("Result = "): data["fail"] = line.split("=")[1].strip()[0] == "F" - if data is not None: - vectors.append(data) + assert data is not None + vectors.append(data) return vectors -- cgit v1.2.3 From e88c4318eabcc2c252a184df9a672161078fe911 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 20:04:42 +0100 Subject: simplify x509 extension decoding --- src/cryptography/hazmat/backends/openssl/x509.py | 101 +++++++---------------- 1 file changed, 28 insertions(+), 73 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 493abc83..63e4a177 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -234,7 +234,9 @@ class _X509ExtensionParser(object): "{0} is not currently supported".format(oid), oid ) else: - value = handler(backend, ext) + d2i = backend._lib.X509V3_EXT_d2i(ext) + assert d2i != backend._ffi.NULL + value = handler(backend, d2i) extensions.append(x509.Extension(oid, critical, value)) seen_oids.add(oid) @@ -358,12 +360,8 @@ class _Certificate(object): return self._backend._read_mem_bio(bio) -def _decode_certificate_policies(backend, ext): - cp = backend._ffi.cast( - "Cryptography_STACK_OF_POLICYINFO *", - backend._lib.X509V3_EXT_d2i(ext) - ) - assert cp != backend._ffi.NULL +def _decode_certificate_policies(backend, cp): + cp = backend._ffi.cast("Cryptography_STACK_OF_POLICYINFO *", cp) cp = backend._ffi.gc(cp, backend._lib.sk_POLICYINFO_free) num = backend._lib.sk_POLICYINFO_num(cp) certificate_policies = [] @@ -431,12 +429,8 @@ def _decode_user_notice(backend, un): return x509.UserNotice(notice_reference, explicit_text) -def _decode_basic_constraints(backend, ext): - bc_st = backend._lib.X509V3_EXT_d2i(ext) - assert bc_st != backend._ffi.NULL - basic_constraints = backend._ffi.cast( - "BASIC_CONSTRAINTS *", bc_st - ) +def _decode_basic_constraints(backend, bc_st): + basic_constraints = backend._ffi.cast("BASIC_CONSTRAINTS *", bc_st) basic_constraints = backend._ffi.gc( basic_constraints, backend._lib.BASIC_CONSTRAINTS_free ) @@ -447,19 +441,13 @@ def _decode_basic_constraints(backend, ext): if basic_constraints.pathlen == backend._ffi.NULL: path_length = None else: - path_length = _asn1_integer_to_int( - backend, basic_constraints.pathlen - ) + path_length = _asn1_integer_to_int(backend, basic_constraints.pathlen) return x509.BasicConstraints(ca, path_length) -def _decode_subject_key_identifier(backend, ext): - asn1_string = backend._lib.X509V3_EXT_d2i(ext) - assert asn1_string != backend._ffi.NULL - asn1_string = backend._ffi.cast( - "ASN1_OCTET_STRING *", asn1_string - ) +def _decode_subject_key_identifier(backend, asn1_string): + asn1_string = backend._ffi.cast("ASN1_OCTET_STRING *", asn1_string) asn1_string = backend._ffi.gc( asn1_string, backend._lib.ASN1_OCTET_STRING_free ) @@ -468,13 +456,9 @@ def _decode_subject_key_identifier(backend, ext): ) -def _decode_authority_key_identifier(backend, ext): - akid = backend._lib.X509V3_EXT_d2i(ext) - assert akid != backend._ffi.NULL +def _decode_authority_key_identifier(backend, akid): akid = backend._ffi.cast("AUTHORITY_KEYID *", akid) - akid = backend._ffi.gc( - akid, backend._lib.AUTHORITY_KEYID_free - ) + akid = backend._ffi.gc(akid, backend._lib.AUTHORITY_KEYID_free) key_identifier = None authority_cert_issuer = None authority_cert_serial_number = None @@ -499,15 +483,9 @@ def _decode_authority_key_identifier(backend, ext): ) -def _decode_authority_information_access(backend, ext): - aia = backend._lib.X509V3_EXT_d2i(ext) - assert aia != backend._ffi.NULL - aia = backend._ffi.cast( - "Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia - ) - aia = backend._ffi.gc( - aia, backend._lib.sk_ACCESS_DESCRIPTION_free - ) +def _decode_authority_information_access(backend, aia): + aia = backend._ffi.cast("Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia) + aia = backend._ffi.gc(aia, backend._lib.sk_ACCESS_DESCRIPTION_free) num = backend._lib.sk_ACCESS_DESCRIPTION_num(aia) access_descriptions = [] for i in range(num): @@ -521,13 +499,9 @@ def _decode_authority_information_access(backend, ext): return x509.AuthorityInformationAccess(access_descriptions) -def _decode_key_usage(backend, ext): - bit_string = backend._lib.X509V3_EXT_d2i(ext) - assert bit_string != backend._ffi.NULL +def _decode_key_usage(backend, bit_string): bit_string = backend._ffi.cast("ASN1_BIT_STRING *", bit_string) - bit_string = backend._ffi.gc( - bit_string, backend._lib.ASN1_BIT_STRING_free - ) + bit_string = backend._ffi.gc(bit_string, backend._lib.ASN1_BIT_STRING_free) get_bit = backend._lib.ASN1_BIT_STRING_get_bit digital_signature = get_bit(bit_string, 0) == 1 content_commitment = get_bit(bit_string, 1) == 1 @@ -551,11 +525,8 @@ def _decode_key_usage(backend, ext): ) -def _decode_general_names_extension(backend, ext): - gns = backend._ffi.cast( - "GENERAL_NAMES *", backend._lib.X509V3_EXT_d2i(ext) - ) - assert gns != backend._ffi.NULL +def _decode_general_names_extension(backend, gns): + gns = backend._ffi.cast("GENERAL_NAMES *", gns) gns = backend._ffi.gc(gns, backend._lib.GENERAL_NAMES_free) general_names = _decode_general_names(backend, gns) return general_names @@ -573,11 +544,8 @@ def _decode_issuer_alt_name(backend, ext): ) -def _decode_name_constraints(backend, ext): - nc = backend._ffi.cast( - "NAME_CONSTRAINTS *", backend._lib.X509V3_EXT_d2i(ext) - ) - assert nc != backend._ffi.NULL +def _decode_name_constraints(backend, nc): + nc = backend._ffi.cast("NAME_CONSTRAINTS *", nc) nc = backend._ffi.gc(nc, backend._lib.NAME_CONSTRAINTS_free) permitted = _decode_general_subtrees(backend, nc.permittedSubtrees) excluded = _decode_general_subtrees(backend, nc.excludedSubtrees) @@ -602,12 +570,8 @@ def _decode_general_subtrees(backend, stack_subtrees): return subtrees -def _decode_extended_key_usage(backend, ext): - sk = backend._ffi.cast( - "Cryptography_STACK_OF_ASN1_OBJECT *", - backend._lib.X509V3_EXT_d2i(ext) - ) - assert sk != backend._ffi.NULL +def _decode_extended_key_usage(backend, sk): + sk = backend._ffi.cast("Cryptography_STACK_OF_ASN1_OBJECT *", sk) sk = backend._ffi.gc(sk, backend._lib.sk_ASN1_OBJECT_free) num = backend._lib.sk_ASN1_OBJECT_num(sk) ekus = [] @@ -621,14 +585,9 @@ def _decode_extended_key_usage(backend, ext): return x509.ExtendedKeyUsage(ekus) -def _decode_crl_distribution_points(backend, ext): - cdps = backend._ffi.cast( - "Cryptography_STACK_OF_DIST_POINT *", - backend._lib.X509V3_EXT_d2i(ext) - ) - assert cdps != backend._ffi.NULL - cdps = backend._ffi.gc( - cdps, backend._lib.sk_DIST_POINT_free) +def _decode_crl_distribution_points(backend, cdps): + cdps = backend._ffi.cast("Cryptography_STACK_OF_DIST_POINT *", cdps) + cdps = backend._ffi.gc(cdps, backend._lib.sk_DIST_POINT_free) num = backend._lib.sk_DIST_POINT_num(cdps) dist_points = [] @@ -716,12 +675,8 @@ def _decode_crl_distribution_points(backend, ext): return x509.CRLDistributionPoints(dist_points) -def _decode_inhibit_any_policy(backend, ext): - asn1_int = backend._ffi.cast( - "ASN1_INTEGER *", - backend._lib.X509V3_EXT_d2i(ext) - ) - assert asn1_int != backend._ffi.NULL +def _decode_inhibit_any_policy(backend, asn1_int): + asn1_int = backend._ffi.cast("ASN1_INTEGER *", asn1_int) asn1_int = backend._ffi.gc(asn1_int, backend._lib.ASN1_INTEGER_free) skip_certs = _asn1_integer_to_int(backend, asn1_int) return x509.InhibitAnyPolicy(skip_certs) -- cgit v1.2.3 From ac78c866d3fd94c3de0e8a33e18654a312083c3e Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 20:26:00 +0100 Subject: add test to cover partial branch in load_kasvs_ecdh_vectors The loop to find supported parameter sets is partially covered otherwise --- tests/test_utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_utils.py b/tests/test_utils.py index 416de795..210e9292 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3045,6 +3045,10 @@ d518475576730ed528779366568e46b7dd4ed787cb72d0733c93 assert expected == load_kasvs_dh_vectors(vector_data) +def test_load_kasvs_ecdh_vectors_empty_vector_data(): + assert [] == load_kasvs_ecdh_vectors([]) + + def test_load_kasvs_ecdh_vectors(): vector_data = textwrap.dedent(""" # CAVS 11.0 -- cgit v1.2.3 From 0f984369c0f58f0d5db5cb1a6927b550afc89027 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 20:45:08 +0100 Subject: add test vector from #2183 --- docs/development/test-vectors.rst | 2 ++ vectors/cryptography_vectors/x509/custom/cp_invalid.pem | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 vectors/cryptography_vectors/x509/custom/cp_invalid.pem diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index ea44a46c..d400e662 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -233,6 +233,8 @@ Custom X.509 Vectors * ``cp_user_notice_no_explicit_text.pem`` - An RSA 2048 bit self-signed certificate containing a certificate policies extension with a user notice with no explicit text. +* ``cp_invalid.pem`` - An RSA 2048 bit self-signed certificate containing a + certificate policies extension with invalid data. * ``ian_uri.pem`` - An RSA 2048 bit certificate containing an issuer alternative name extension with a ``URI`` general name. * ``ocsp_nocheck.pem`` - An RSA 2048 bit self-signed certificate containing diff --git a/vectors/cryptography_vectors/x509/custom/cp_invalid.pem b/vectors/cryptography_vectors/x509/custom/cp_invalid.pem new file mode 100644 index 00000000..b7bcc079 --- /dev/null +++ b/vectors/cryptography_vectors/x509/custom/cp_invalid.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIIC8TCCAdmgAwIBAgITBmsoYWX1PCELRmm8qB2WJ2QdDjANBgkqhkiG9w0BAQUFADASMRAwDgYD +VQQDDAdQeUNBIENBMB4XDTE1MDUxMTE4NTc0NVoXDTE2MDUxMDE4NTc0NVowEjEQMA4GA1UEAwwH +UHlDQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3FTRITEY4b/Y1Uv4CtH61Y +19TPxK2+H/XuqHwtYlPRyD35LLFES0wykf0V2m1DUmf9jQa9R63jBZxzCgJ/oIJzV28PgSg9P/Nn +417fNASDduY2GPvYuwwKXcLY2fBBFjBrz7z/5tyXCADjLDkzoUTzQlYPbhOrFU5QwaqlckXBgt/4 +8GRDujoHy4RSMEDNjLUDgwx7Z/JK2ujbGJDguLRuBsHirk2h6xXEmSWxquKDXw4NnakwBqp8kKhQ +2xTSWXxabNps8FCBM4sC78gKgONy3lbYdHFt/2BU4yAMyowJwtDEYHCqe1g4sVsB839Ol0SXb6vl +eXQ6dx+zbi8UzTsCAwEAAaNAMD4wPAYDVR0gBDUwMzAxBgtghkgB4DkBAgMEATAiMCAGCCsGAQUF +BwICFhRodHRwOi8vb3RoZXIuY29tL2NwczANBgkqhkiG9w0BAQUFAAOCAQEADpZIjHvu02euPNI8 +nzzDufRXEnjrF09xc9pudxTjWU2mSVApXPmTDyWzOD+2HmsNKHRE6sWjca5qPDeDbGq4JOw+TzYq +9eoqwK2Sh0QHUpg5ZaAmIJ1qe5/sNETH5RFlXrlzW9S0rwViLgUaJp6MreTdGZbxdpNsfdkuNd+S +Tz0MA/3ScbdUcj6uwQQ4JxQiTuPwD35pKwxfUzHjeTmqIEHDuCk17KqIRORdbeD3vFx0R5IQ3mQ6 +9zSGY2AGB0A9oS0qQ2/Mh59A6xyjbPH3Rr7g5MW58PPTWp2FSXkloy7Ze+doQ7wXE6PVmaeKz5qA +9OGaCHIiC2iG9UcqWxfeWw== +-----END CERTIFICATE----- -- cgit v1.2.3 From a08693f3a71a6537da9cfa7d9dda7781aef2bcdd Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 20:45:21 +0100 Subject: check if the extension decoded to internal openssl repr ...and if not, raise an error (plus consume the error stack) --- src/cryptography/hazmat/backends/openssl/x509.py | 7 ++++++- tests/test_x509_ext.py | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 63e4a177..1c0c3acf 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -235,7 +235,12 @@ class _X509ExtensionParser(object): ) else: d2i = backend._lib.X509V3_EXT_d2i(ext) - assert d2i != backend._ffi.NULL + if d2i == backend._ffi.NULL: + backend._consume_errors() + raise ValueError( + "The {0} extension appears to be corrupt".format(oid) + ) + value = handler(backend, d2i) extensions.append(x509.Extension(oid, critical, value)) diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py index 7b135828..2980808f 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -2853,3 +2853,18 @@ class TestInhibitAnyPolicyExtension(object): x509.OID_INHIBIT_ANY_POLICY ).value assert iap.skip_certs == 5 + + +@pytest.mark.requires_backend_interface(interface=RSABackend) +@pytest.mark.requires_backend_interface(interface=X509Backend) +class TestCorruptExtension(object): + def test_invalid_certificate_policies_data(self, backend): + cert = _load_cert( + os.path.join( + "x509", "custom", "cp_invalid.pem" + ), + x509.load_pem_x509_certificate, + backend + ) + with pytest.raises(ValueError): + cert.extensions -- cgit v1.2.3 From 60517a4466c153449f88063ef627291c53ea5ee1 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 20:54:45 +0100 Subject: improve changelog information around csrbuilder --- CHANGELOG.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 85f84477..0946b8bb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -25,7 +25,13 @@ Changelog * Raise a ``TypeError`` when passing objects that are not text as the value to :class:`~cryptography.x509.NameAttribute`. * Add support for creating certificate signing requests with - :class:`~cryptography.x509.CertificateSigningRequestBuilder`. + :class:`~cryptography.x509.CertificateSigningRequestBuilder`. This includes + support for the following extensions: + + * :class:`~cryptography.x509.BasicConstraints` + * :class:`~cryptography.x509.ExtendedKeyUsage` + * :class:`~cryptography.x509.KeyUsage` + * :class:`~cryptography.x509.SubjectAlternativeName` 0.9.3 - 2015-07-09 ~~~~~~~~~~~~~~~~~~ -- cgit v1.2.3 From 1b7500f5f91a9ad07f5f15caf17264753173f8d8 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 20:56:27 +0100 Subject: corrupt -> invalid --- src/cryptography/hazmat/backends/openssl/x509.py | 3 ++- tests/test_x509_ext.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 1c0c3acf..2572cc41 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -238,7 +238,8 @@ class _X509ExtensionParser(object): if d2i == backend._ffi.NULL: backend._consume_errors() raise ValueError( - "The {0} extension appears to be corrupt".format(oid) + "The {0} extension is invalid and can't be " + "parsed".format(oid) ) value = handler(backend, d2i) diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py index 2980808f..890709ae 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -2857,7 +2857,7 @@ class TestInhibitAnyPolicyExtension(object): @pytest.mark.requires_backend_interface(interface=RSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) -class TestCorruptExtension(object): +class TestInvalidExtension(object): def test_invalid_certificate_policies_data(self, backend): cert = _load_cert( os.path.join( -- cgit v1.2.3 From c4e6194f182dcf49280a140bedd0d88c7d94b4e3 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 21:21:13 +0100 Subject: add missing extensions documentation to CertificateSigningRequest --- docs/x509/reference.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index 9179468f..4d164dca 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -431,6 +431,25 @@ X.509 CSR (Certificate Signing Request) Object >>> isinstance(csr.signature_hash_algorithm, hashes.SHA1) True + .. attribute:: extensions + + :type: :class:`Extensions` + + The extensions encoded in the certificate signing request. + + :raises cryptography.x509.DuplicateExtension: If more than one + extension of the same type is found within the certificate siging request. + + :raises cryptography.x509.UnsupportedExtension: If the certificate siging request + contains an extension that is not supported. + + :raises cryptography.x509.UnsupportedGeneralNameType: If an extension + contains a general name that is not supported. + + :raises UnicodeError: If an extension contains IDNA encoding that is + invalid or not compliant with IDNA 2008. + + .. method:: public_bytes(encoding) .. versionadded:: 1.0 -- cgit v1.2.3 From 44171a29788ba7e8f95585bc9e97c4ca847a6267 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 21:21:26 +0100 Subject: improve the changelog for 1.0 --- CHANGELOG.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0946b8bb..4506a466 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -24,6 +24,18 @@ Changelog and :class:`~cryptography.hazmat.primitives.kdf.concatkdf.ConcatKDFHMAC`. * Raise a ``TypeError`` when passing objects that are not text as the value to :class:`~cryptography.x509.NameAttribute`. +* Add support for :class:`~cryptography.x509.OtherName` as a general name + type. +* Added new X.509 extension support in :class:`~cryptography.x509.Certificate` + The following new extensions are now supported: + + * :class:`~cryptography.x509.OCSPNoCheck` + * :class:`~cryptography.x509.InhibitAnyPolicy` + * :class:`~cryptography.x509.IssuerAlternativeName` + * :class:`~cryptography.x509.NameConstraints` + +* Extension support was added to + :class:`~cryptography.x509.CertificateSigningRequest`. * Add support for creating certificate signing requests with :class:`~cryptography.x509.CertificateSigningRequestBuilder`. This includes support for the following extensions: -- cgit v1.2.3 From 33fc7dc0afa71e66b2a9e2d85ff0c090ffedbdc6 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 21:23:22 +0100 Subject: allow certificate and CSR to both parse the same set of extensions --- src/cryptography/hazmat/backends/openssl/x509.py | 44 +++++++++++------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 63e4a177..eb0426e5 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -744,35 +744,33 @@ class _CertificateSigningRequest(object): return self._backend._read_mem_bio(bio) +_EXTENSION_HANDLERS = { + x509.OID_BASIC_CONSTRAINTS: _decode_basic_constraints, + x509.OID_SUBJECT_KEY_IDENTIFIER: _decode_subject_key_identifier, + x509.OID_KEY_USAGE: _decode_key_usage, + x509.OID_SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name, + x509.OID_EXTENDED_KEY_USAGE: _decode_extended_key_usage, + x509.OID_AUTHORITY_KEY_IDENTIFIER: _decode_authority_key_identifier, + x509.OID_AUTHORITY_INFORMATION_ACCESS: ( + _decode_authority_information_access + ), + x509.OID_CERTIFICATE_POLICIES: _decode_certificate_policies, + x509.OID_CRL_DISTRIBUTION_POINTS: _decode_crl_distribution_points, + x509.OID_OCSP_NO_CHECK: _decode_ocsp_no_check, + x509.OID_INHIBIT_ANY_POLICY: _decode_inhibit_any_policy, + x509.OID_ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name, + x509.OID_NAME_CONSTRAINTS: _decode_name_constraints, +} + + _CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser( ext_count=lambda backend, x: backend._lib.X509_get_ext_count(x), get_ext=lambda backend, x, i: backend._lib.X509_get_ext(x, i), - handlers={ - x509.OID_BASIC_CONSTRAINTS: _decode_basic_constraints, - x509.OID_SUBJECT_KEY_IDENTIFIER: _decode_subject_key_identifier, - x509.OID_KEY_USAGE: _decode_key_usage, - x509.OID_SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name, - x509.OID_EXTENDED_KEY_USAGE: _decode_extended_key_usage, - x509.OID_AUTHORITY_KEY_IDENTIFIER: _decode_authority_key_identifier, - x509.OID_AUTHORITY_INFORMATION_ACCESS: ( - _decode_authority_information_access - ), - x509.OID_CERTIFICATE_POLICIES: _decode_certificate_policies, - x509.OID_CRL_DISTRIBUTION_POINTS: _decode_crl_distribution_points, - x509.OID_OCSP_NO_CHECK: _decode_ocsp_no_check, - x509.OID_INHIBIT_ANY_POLICY: _decode_inhibit_any_policy, - x509.OID_ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name, - x509.OID_NAME_CONSTRAINTS: _decode_name_constraints, - } + handlers=_EXTENSION_HANDLERS ) _CSR_EXTENSION_PARSER = _X509ExtensionParser( ext_count=lambda backend, x: backend._lib.sk_X509_EXTENSION_num(x), get_ext=lambda backend, x, i: backend._lib.sk_X509_EXTENSION_value(x, i), - handlers={ - x509.OID_BASIC_CONSTRAINTS: _decode_basic_constraints, - x509.OID_KEY_USAGE: _decode_key_usage, - x509.OID_SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name, - x509.OID_EXTENDED_KEY_USAGE: _decode_extended_key_usage, - } + handlers=_EXTENSION_HANDLERS ) -- cgit v1.2.3 From 24283d22ef0e44e94aeef6cf170448240e83fcb2 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sat, 1 Aug 2015 21:31:19 +0100 Subject: typo fix --- docs/x509/reference.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index 4d164dca..c7d45c77 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -438,9 +438,9 @@ X.509 CSR (Certificate Signing Request) Object The extensions encoded in the certificate signing request. :raises cryptography.x509.DuplicateExtension: If more than one - extension of the same type is found within the certificate siging request. + extension of the same type is found within the certificate signing request. - :raises cryptography.x509.UnsupportedExtension: If the certificate siging request + :raises cryptography.x509.UnsupportedExtension: If the certificate signing request contains an extension that is not supported. :raises cryptography.x509.UnsupportedGeneralNameType: If an extension -- cgit v1.2.3