diff options
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 93 | ||||
-rw-r--r-- | src/cryptography/x509.py | 4 | ||||
-rw-r--r-- | tests/test_x509.py | 118 |
3 files changed, 215 insertions, 0 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 0038ddb0..c2f4ba6d 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -359,6 +359,95 @@ def _encode_extended_key_usage(backend, extended_key_usage): return pp, r +def _encode_crl_distribution_points(backend, crl_distribution_points): + cdp = backend._lib.sk_DIST_POINT_new_null() + cdp = backend._ffi.gc(cdp, backend._lib.sk_DIST_POINT_free) + for point in crl_distribution_points: + dp = backend._lib.DIST_POINT_new() + assert dp != backend._ffi.NULL + + if point.reasons: + # TODO: determining reason flag is quadratic + bitmask = backend._lib.ASN1_BIT_STRING_new() + res = backend._lib.ASN1_BIT_STRING_set_bit( + bitmask, 1, x509.ReasonFlags.key_compromise in point.reasons + ) + assert res == 1 + res = backend._lib.ASN1_BIT_STRING_set_bit( + bitmask, 2, x509.ReasonFlags.ca_compromise in point.reasons + ) + assert res == 1 + res = backend._lib.ASN1_BIT_STRING_set_bit( + bitmask, + 3, + x509.ReasonFlags.affiliation_changed in point.reasons + ) + assert res == 1 + res = backend._lib.ASN1_BIT_STRING_set_bit( + bitmask, 4, x509.ReasonFlags.superseded in point.reasons + ) + assert res == 1 + res = backend._lib.ASN1_BIT_STRING_set_bit( + bitmask, + 5, + x509.ReasonFlags.cessation_of_operation in point.reasons + ) + assert res == 1 + res = backend._lib.ASN1_BIT_STRING_set_bit( + bitmask, 6, x509.ReasonFlags.certificate_hold in point.reasons + ) + assert res == 1 + res = backend._lib.ASN1_BIT_STRING_set_bit( + bitmask, + 7, + x509.ReasonFlags.privilege_withdrawn in point.reasons + ) + assert res == 1 + res = backend._lib.ASN1_BIT_STRING_set_bit( + bitmask, 8, x509.ReasonFlags.aa_compromise in point.reasons + ) + assert res == 1 + + dp.reasons = bitmask + + if point.full_name: + # Type 0 is fullName, there is no #define for it in the code. + dpn = backend._lib.DIST_POINT_NAME_new() + assert dpn != backend._ffi.NULL + dpn.type = 0 + for name in point.full_name: + gns = backend._lib.GENERAL_NAMES_new() + assert gns != backend._ffi.NULL + for name in point.full_name: + gn = _encode_general_name(backend, name) + res = backend._lib.sk_GENERAL_NAME_push(gns, gn) + assert res >= 1 + + dpn.name.fullname = gns + dp.distpoint = dpn + + if point.relative_name: + # TODO: don't duplicate this with fullname above + dpn = backend._lib.DIST_POINT_NAME_new() + assert dpn != backend._ffi.NULL + dpn.name.relativename = _encode_name(backend, point.relative_name) + dp.distpoint = dpn + + if point.crl_issuer: + dp.CRLissuer = _encode_general_names(backend, point.crl_issuer) + + res = backend._lib.sk_DIST_POINT_push(cdp, dp) + assert res >= 1 + + pp = backend._ffi.new('unsigned char **') + r = backend._lib.i2d_CRL_DIST_POINTS(cdp, 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) @@ -1177,6 +1266,10 @@ class Backend(object): pp, r = _encode_authority_information_access( self, extension.value ) + elif isinstance(extension.value, x509.CRLDistributionPoints): + pp, r = _encode_crl_distribution_points( + self, extension.value + ) else: raise NotImplementedError('Extension not yet supported.') diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 08a0c7c9..0245fad4 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1780,6 +1780,10 @@ class CertificateBuilder(object): ) elif isinstance(extension, InhibitAnyPolicy): extension = Extension(OID_INHIBIT_ANY_POLICY, critical, extension) + elif isinstance(extension, CRLDistributionPoints): + extension = Extension( + OID_CRL_DISTRIBUTION_POINTS, critical, extension + ) else: raise NotImplementedError('Unsupported X.509 extension.') diff --git a/tests/test_x509.py b/tests/test_x509.py index 5e0342cb..71c29f75 100644 --- a/tests/test_x509.py +++ b/tests/test_x509.py @@ -1143,6 +1143,124 @@ class TestCertificateBuilder(object): with pytest.raises(NotImplementedError): builder.sign(private_key, hashes.SHA512(), backend) + @pytest.mark.parametrize( + "cdp", + [ + x509.CRLDistributionPoints([ + x509.DistributionPoint( + full_name=[x509.DirectoryName( + x509.Name([ + x509.NameAttribute(x509.OID_COUNTRY_NAME, u"US"), + ]) + )], + relative_name=None, + reasons=None, + crl_issuer=[x509.DirectoryName( + x509.Name([ + x509.NameAttribute( + x509.OID_ORGANIZATION_NAME, + u"cryptography Testing" + ), + ]) + )], + ) + ]), + x509.CRLDistributionPoints([ + x509.DistributionPoint( + full_name=[x509.UniformResourceIdentifier( + u"http://myhost.com/myca.crl" + )], + relative_name=None, + reasons=frozenset([ + x509.ReasonFlags.key_compromise, + x509.ReasonFlags.ca_compromise + ]), + crl_issuer=[x509.DirectoryName( + x509.Name([ + x509.NameAttribute(x509.OID_COUNTRY_NAME, u"US"), + x509.NameAttribute( + x509.OID_COMMON_NAME, u"cryptography CA" + ), + ]) + )], + ) + ]), + x509.CRLDistributionPoints([ + x509.DistributionPoint( + full_name=[x509.UniformResourceIdentifier( + u"http://domain.com/some.crl" + )], + relative_name=None, + reasons=frozenset([ + x509.ReasonFlags.key_compromise, + x509.ReasonFlags.ca_compromise, + x509.ReasonFlags.affiliation_changed, + x509.ReasonFlags.superseded, + x509.ReasonFlags.privilege_withdrawn, + x509.ReasonFlags.cessation_of_operation, + x509.ReasonFlags.aa_compromise, + x509.ReasonFlags.certificate_hold, + ]), + crl_issuer=None + ) + ]), + x509.CRLDistributionPoints([ + x509.DistributionPoint( + full_name=None, + relative_name=None, + reasons=None, + crl_issuer=[x509.DirectoryName( + x509.Name([ + x509.NameAttribute( + x509.OID_COMMON_NAME, u"cryptography CA" + ), + ]) + )], + ) + ]), + x509.CRLDistributionPoints([ + x509.DistributionPoint( + full_name=[x509.UniformResourceIdentifier( + u"http://domain.com/some.crl" + )], + relative_name=None, + reasons=frozenset([x509.ReasonFlags.aa_compromise]), + crl_issuer=None + ) + ]) + ] + ) + @pytest.mark.requires_backend_interface(interface=RSABackend) + @pytest.mark.requires_backend_interface(interface=X509Backend) + def test_crl_distribution_points(self, backend, cdp): + issuer_private_key = RSA_KEY_2048.private_key(backend) + subject_private_key = RSA_KEY_2048.private_key(backend) + + builder = x509.CertificateBuilder().serial_number( + 4444444 + ).issuer_name(x509.Name([ + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + ])).subject_name(x509.Name([ + x509.NameAttribute(x509.OID_LOCALITY_NAME, u'Austin'), + ])).public_key( + subject_private_key.public_key() + ).add_extension( + cdp, + critical=False, + ).not_valid_before( + datetime.datetime(2002, 1, 1, 12, 1) + ).not_valid_after( + datetime.datetime(2030, 12, 31, 8, 30) + ) + + cert = builder.sign(issuer_private_key, hashes.SHA1(), backend) + + ext = cert.extensions.get_extension_for_oid( + x509.OID_CRL_DISTRIBUTION_POINTS + ) + assert ext.critical is False + assert ext.value == cdp + @pytest.mark.requires_backend_interface(interface=DSABackend) @pytest.mark.requires_backend_interface(interface=X509Backend) def test_build_cert_with_dsa_private_key(self, backend): |