From 8c234d10b712de3b72df168c19cd06f246415091 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Fri, 15 May 2015 09:27:22 -0700 Subject: add extensions to the interface for Certificate --- src/cryptography/x509.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 71ba9042..ec2092bb 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1125,6 +1125,12 @@ class Certificate(object): in the certificate. """ + @abc.abstractproperty + def extensions(self): + """ + Returns an Extensions object. + """ + @abc.abstractmethod def __eq__(self, other): """ -- cgit v1.2.3 From 4a684ed2fc9a42d5e12827aeadf68e8ac82132df Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Fri, 15 May 2015 17:45:57 -0400 Subject: Prepares X509 decoding functions for more CA functionality. Most of the existing `_build_*()` methods will need to be reused for CSR extensions, so it is more convenient to have them as free functinos. Since we will need to add the reverse functions, it is convenient to rename the `_build_*()` methods to `_decode_*()`. The reverse functions will be named `_encode_*()`. --- src/cryptography/hazmat/backends/openssl/x509.py | 604 ++++++++++++----------- 1 file changed, 309 insertions(+), 295 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 07e54baa..b36694dd 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -56,7 +56,7 @@ def _asn1_string_to_utf8(backend, asn1_string): return backend._ffi.buffer(buf[0], res)[:].decode('utf8') -def _build_x509_name_entry(backend, x509_name_entry): +def _decode_x509_name_entry(backend, x509_name_entry): obj = backend._lib.X509_NAME_ENTRY_get_object(x509_name_entry) assert obj != backend._ffi.NULL data = backend._lib.X509_NAME_ENTRY_get_data(x509_name_entry) @@ -67,28 +67,28 @@ def _build_x509_name_entry(backend, x509_name_entry): return x509.NameAttribute(x509.ObjectIdentifier(oid), value) -def _build_x509_name(backend, x509_name): +def _decode_x509_name(backend, x509_name): count = backend._lib.X509_NAME_entry_count(x509_name) attributes = [] for x in range(count): entry = backend._lib.X509_NAME_get_entry(x509_name, x) - attributes.append(_build_x509_name_entry(backend, entry)) + attributes.append(_decode_x509_name_entry(backend, entry)) return x509.Name(attributes) -def _build_general_names(backend, gns): +def _decode_general_names(backend, gns): num = backend._lib.sk_GENERAL_NAME_num(gns) names = [] for i in range(num): gn = backend._lib.sk_GENERAL_NAME_value(gns, i) assert gn != backend._ffi.NULL - names.append(_build_general_name(backend, gn)) + names.append(_decode_general_name(backend, gn)) return names -def _build_general_name(backend, gn): +def _decode_general_name(backend, gn): if gn.type == backend._lib.GEN_DNS: data = backend._ffi.buffer(gn.d.dNSName.data, gn.d.dNSName.length)[:] return x509.DNSName(idna.decode(data)) @@ -129,7 +129,7 @@ def _build_general_name(backend, gn): ) elif gn.type == backend._lib.GEN_DIRNAME: return x509.DirectoryName( - _build_x509_name(backend, gn.d.directoryName) + _decode_x509_name(backend, gn.d.directoryName) ) elif gn.type == backend._lib.GEN_EMAIL: data = backend._ffi.buffer( @@ -244,13 +244,13 @@ class _Certificate(object): def issuer(self): issuer = self._backend._lib.X509_get_issuer_name(self._x509) assert issuer != self._backend._ffi.NULL - return _build_x509_name(self._backend, issuer) + return _decode_x509_name(self._backend, issuer) @property def subject(self): subject = self._backend._lib.X509_get_subject_name(self._x509) assert subject != self._backend._ffi.NULL - return _build_x509_name(self._backend, subject) + return _decode_x509_name(self._backend, subject) @property def signature_hash_algorithm(self): @@ -278,23 +278,25 @@ class _Certificate(object): "Duplicate {0} extension found".format(oid), oid ) elif oid == x509.OID_BASIC_CONSTRAINTS: - value = self._build_basic_constraints(ext) + value = _decode_basic_constraints(self._backend, ext) elif oid == x509.OID_SUBJECT_KEY_IDENTIFIER: - value = self._build_subject_key_identifier(ext) + value = _decode_subject_key_identifier(self._backend, ext) elif oid == x509.OID_KEY_USAGE: - value = self._build_key_usage(ext) + value = _decode_key_usage(self._backend, ext) elif oid == x509.OID_SUBJECT_ALTERNATIVE_NAME: - value = self._build_subject_alt_name(ext) + value = _decode_subject_alt_name(self._backend, ext) elif oid == x509.OID_EXTENDED_KEY_USAGE: - value = self._build_extended_key_usage(ext) + value = _decode_extended_key_usage(self._backend, ext) elif oid == x509.OID_AUTHORITY_KEY_IDENTIFIER: - value = self._build_authority_key_identifier(ext) + value = _decode_authority_key_identifier(self._backend, ext) elif oid == x509.OID_AUTHORITY_INFORMATION_ACCESS: - value = self._build_authority_information_access(ext) + value = _decode_authority_information_access( + self._backend, ext + ) elif oid == x509.OID_CERTIFICATE_POLICIES: - value = self._build_certificate_policies(ext) + value = _decode_certificate_policies(self._backend, ext) elif oid == x509.OID_CRL_DISTRIBUTION_POINTS: - value = self._build_crl_distribution_points(ext) + value = _decode_crl_distribution_points(self._backend, ext) elif critical: raise x509.UnsupportedExtension( "{0} is not currently supported".format(oid), oid @@ -309,311 +311,323 @@ class _Certificate(object): return x509.Extensions(extensions) - def _build_certificate_policies(self, ext): - cp = self._backend._ffi.cast( - "Cryptography_STACK_OF_POLICYINFO *", - self._backend._lib.X509V3_EXT_d2i(ext) - ) - assert cp != self._backend._ffi.NULL - cp = self._backend._ffi.gc(cp, self._backend._lib.sk_POLICYINFO_free) - num = self._backend._lib.sk_POLICYINFO_num(cp) - certificate_policies = [] - for i in range(num): - qualifiers = None - pi = self._backend._lib.sk_POLICYINFO_value(cp, i) - oid = x509.ObjectIdentifier(_obj2txt(self._backend, pi.policyid)) - if pi.qualifiers != self._backend._ffi.NULL: - qnum = self._backend._lib.sk_POLICYQUALINFO_num(pi.qualifiers) - qualifiers = [] - for j in range(qnum): - pqi = self._backend._lib.sk_POLICYQUALINFO_value( - pi.qualifiers, j - ) - pqualid = x509.ObjectIdentifier( - _obj2txt(self._backend, pqi.pqualid) - ) - if pqualid == x509.OID_CPS_QUALIFIER: - cpsuri = self._backend._ffi.buffer( - pqi.d.cpsuri.data, pqi.d.cpsuri.length - )[:].decode('ascii') - qualifiers.append(cpsuri) - elif pqualid == x509.OID_CPS_USER_NOTICE: - user_notice = self._build_user_notice(pqi.d.usernotice) - qualifiers.append(user_notice) - - certificate_policies.append( - x509.PolicyInformation(oid, qualifiers) - ) - - return x509.CertificatePolicies(certificate_policies) - - def _build_user_notice(self, un): - explicit_text = None - notice_reference = None - - if un.exptext != self._backend._ffi.NULL: - explicit_text = _asn1_string_to_utf8(self._backend, un.exptext) - if un.noticeref != self._backend._ffi.NULL: - organization = _asn1_string_to_utf8( - self._backend, un.noticeref.organization - ) - - num = self._backend._lib.sk_ASN1_INTEGER_num( - un.noticeref.noticenos - ) - notice_numbers = [] - for i in range(num): - asn1_int = self._backend._lib.sk_ASN1_INTEGER_value( - un.noticeref.noticenos, i +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 + cp = backend._ffi.gc(cp, backend._lib.sk_POLICYINFO_free) + num = backend._lib.sk_POLICYINFO_num(cp) + certificate_policies = [] + for i in range(num): + qualifiers = None + pi = backend._lib.sk_POLICYINFO_value(cp, i) + oid = x509.ObjectIdentifier(_obj2txt(backend, pi.policyid)) + if pi.qualifiers != backend._ffi.NULL: + qnum = backend._lib.sk_POLICYQUALINFO_num(pi.qualifiers) + qualifiers = [] + for j in range(qnum): + pqi = backend._lib.sk_POLICYQUALINFO_value( + pi.qualifiers, j ) - notice_num = _asn1_integer_to_int( - self._backend, asn1_int + pqualid = x509.ObjectIdentifier( + _obj2txt(backend, pqi.pqualid) ) - notice_numbers.append(notice_num) + if pqualid == x509.OID_CPS_QUALIFIER: + cpsuri = backend._ffi.buffer( + pqi.d.cpsuri.data, pqi.d.cpsuri.length + )[:].decode('ascii') + qualifiers.append(cpsuri) + elif pqualid == x509.OID_CPS_USER_NOTICE: + user_notice = _decode_user_notice( + backend, pqi.d.usernotice + ) + qualifiers.append(user_notice) - notice_reference = x509.NoticeReference( - organization, notice_numbers - ) + certificate_policies.append( + x509.PolicyInformation(oid, qualifiers) + ) - return x509.UserNotice(notice_reference, explicit_text) + return x509.CertificatePolicies(certificate_policies) - def _build_basic_constraints(self, ext): - bc_st = self._backend._lib.X509V3_EXT_d2i(ext) - assert bc_st != self._backend._ffi.NULL - basic_constraints = self._backend._ffi.cast( - "BASIC_CONSTRAINTS *", bc_st - ) - basic_constraints = self._backend._ffi.gc( - basic_constraints, self._backend._lib.BASIC_CONSTRAINTS_free - ) - # The byte representation of an ASN.1 boolean true is \xff. OpenSSL - # chooses to just map this to its ordinal value, so true is 255 and - # false is 0. - ca = basic_constraints.ca == 255 - if basic_constraints.pathlen == self._backend._ffi.NULL: - path_length = None - else: - path_length = _asn1_integer_to_int( - self._backend, basic_constraints.pathlen - ) - return x509.BasicConstraints(ca, path_length) +def _decode_user_notice(backend, un): + explicit_text = None + notice_reference = None - def _build_subject_key_identifier(self, ext): - asn1_string = self._backend._lib.X509V3_EXT_d2i(ext) - assert asn1_string != self._backend._ffi.NULL - asn1_string = self._backend._ffi.cast( - "ASN1_OCTET_STRING *", asn1_string - ) - asn1_string = self._backend._ffi.gc( - asn1_string, self._backend._lib.ASN1_OCTET_STRING_free - ) - return x509.SubjectKeyIdentifier( - self._backend._ffi.buffer(asn1_string.data, asn1_string.length)[:] + if un.exptext != backend._ffi.NULL: + explicit_text = _asn1_string_to_utf8(backend, un.exptext) + + if un.noticeref != backend._ffi.NULL: + organization = _asn1_string_to_utf8( + backend, un.noticeref.organization ) - def _build_authority_key_identifier(self, ext): - akid = self._backend._lib.X509V3_EXT_d2i(ext) - assert akid != self._backend._ffi.NULL - akid = self._backend._ffi.cast("AUTHORITY_KEYID *", akid) - akid = self._backend._ffi.gc( - akid, self._backend._lib.AUTHORITY_KEYID_free + num = backend._lib.sk_ASN1_INTEGER_num( + un.noticeref.noticenos ) - key_identifier = None - authority_cert_issuer = None - authority_cert_serial_number = None - - if akid.keyid != self._backend._ffi.NULL: - key_identifier = self._backend._ffi.buffer( - akid.keyid.data, akid.keyid.length - )[:] - - if akid.issuer != self._backend._ffi.NULL: - authority_cert_issuer = _build_general_names( - self._backend, akid.issuer + notice_numbers = [] + for i in range(num): + asn1_int = backend._lib.sk_ASN1_INTEGER_value( + un.noticeref.noticenos, i ) - - if akid.serial != self._backend._ffi.NULL: - authority_cert_serial_number = _asn1_integer_to_int( - self._backend, akid.serial + notice_num = _asn1_integer_to_int( + backend, asn1_int ) + notice_numbers.append(notice_num) - return x509.AuthorityKeyIdentifier( - key_identifier, authority_cert_issuer, authority_cert_serial_number + notice_reference = x509.NoticeReference( + organization, notice_numbers ) - def _build_authority_information_access(self, ext): - aia = self._backend._lib.X509V3_EXT_d2i(ext) - assert aia != self._backend._ffi.NULL - aia = self._backend._ffi.cast( - "Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia - ) - aia = self._backend._ffi.gc( - aia, self._backend._lib.sk_ACCESS_DESCRIPTION_free - ) - num = self._backend._lib.sk_ACCESS_DESCRIPTION_num(aia) - access_descriptions = [] - for i in range(num): - ad = self._backend._lib.sk_ACCESS_DESCRIPTION_value(aia, i) - assert ad.method != self._backend._ffi.NULL - oid = x509.ObjectIdentifier(_obj2txt(self._backend, ad.method)) - assert ad.location != self._backend._ffi.NULL - gn = _build_general_name(self._backend, ad.location) - access_descriptions.append(x509.AccessDescription(oid, gn)) - - return x509.AuthorityInformationAccess(access_descriptions) - - def _build_key_usage(self, ext): - bit_string = self._backend._lib.X509V3_EXT_d2i(ext) - assert bit_string != self._backend._ffi.NULL - bit_string = self._backend._ffi.cast("ASN1_BIT_STRING *", bit_string) - bit_string = self._backend._ffi.gc( - bit_string, self._backend._lib.ASN1_BIT_STRING_free + 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 + ) + basic_constraints = backend._ffi.gc( + basic_constraints, backend._lib.BASIC_CONSTRAINTS_free + ) + # The byte representation of an ASN.1 boolean true is \xff. OpenSSL + # chooses to just map this to its ordinal value, so true is 255 and + # false is 0. + ca = basic_constraints.ca == 255 + if basic_constraints.pathlen == backend._ffi.NULL: + path_length = None + else: + path_length = _asn1_integer_to_int( + backend, basic_constraints.pathlen ) - get_bit = self._backend._lib.ASN1_BIT_STRING_get_bit - digital_signature = get_bit(bit_string, 0) == 1 - content_commitment = get_bit(bit_string, 1) == 1 - key_encipherment = get_bit(bit_string, 2) == 1 - data_encipherment = get_bit(bit_string, 3) == 1 - key_agreement = get_bit(bit_string, 4) == 1 - key_cert_sign = get_bit(bit_string, 5) == 1 - crl_sign = get_bit(bit_string, 6) == 1 - encipher_only = get_bit(bit_string, 7) == 1 - decipher_only = get_bit(bit_string, 8) == 1 - return x509.KeyUsage( - digital_signature, - content_commitment, - key_encipherment, - data_encipherment, - key_agreement, - key_cert_sign, - crl_sign, - encipher_only, - decipher_only + + 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 + ) + asn1_string = backend._ffi.gc( + asn1_string, backend._lib.ASN1_OCTET_STRING_free + ) + return x509.SubjectKeyIdentifier( + backend._ffi.buffer(asn1_string.data, asn1_string.length)[:] + ) + + +def _decode_authority_key_identifier(backend, ext): + akid = backend._lib.X509V3_EXT_d2i(ext) + assert akid != backend._ffi.NULL + akid = backend._ffi.cast("AUTHORITY_KEYID *", akid) + akid = backend._ffi.gc( + akid, backend._lib.AUTHORITY_KEYID_free + ) + key_identifier = None + authority_cert_issuer = None + authority_cert_serial_number = None + + if akid.keyid != backend._ffi.NULL: + key_identifier = backend._ffi.buffer( + akid.keyid.data, akid.keyid.length + )[:] + + if akid.issuer != backend._ffi.NULL: + authority_cert_issuer = _decode_general_names( + backend, akid.issuer ) - def _build_subject_alt_name(self, ext): - gns = self._backend._ffi.cast( - "GENERAL_NAMES *", self._backend._lib.X509V3_EXT_d2i(ext) + if akid.serial != backend._ffi.NULL: + authority_cert_serial_number = _asn1_integer_to_int( + backend, akid.serial ) - assert gns != self._backend._ffi.NULL - gns = self._backend._ffi.gc(gns, self._backend._lib.GENERAL_NAMES_free) - general_names = _build_general_names(self._backend, gns) - return x509.SubjectAlternativeName(general_names) + return x509.AuthorityKeyIdentifier( + key_identifier, authority_cert_issuer, authority_cert_serial_number + ) - def _build_extended_key_usage(self, ext): - sk = self._backend._ffi.cast( - "Cryptography_STACK_OF_ASN1_OBJECT *", - self._backend._lib.X509V3_EXT_d2i(ext) - ) - assert sk != self._backend._ffi.NULL - sk = self._backend._ffi.gc(sk, self._backend._lib.sk_ASN1_OBJECT_free) - num = self._backend._lib.sk_ASN1_OBJECT_num(sk) - ekus = [] - for i in range(num): - obj = self._backend._lib.sk_ASN1_OBJECT_value(sk, i) - assert obj != self._backend._ffi.NULL - oid = x509.ObjectIdentifier(_obj2txt(self._backend, obj)) - ekus.append(oid) +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 + ) + num = backend._lib.sk_ACCESS_DESCRIPTION_num(aia) + access_descriptions = [] + for i in range(num): + ad = backend._lib.sk_ACCESS_DESCRIPTION_value(aia, i) + assert ad.method != backend._ffi.NULL + oid = x509.ObjectIdentifier(_obj2txt(backend, ad.method)) + assert ad.location != backend._ffi.NULL + gn = _decode_general_name(backend, ad.location) + access_descriptions.append(x509.AccessDescription(oid, gn)) + + 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 + bit_string = backend._ffi.cast("ASN1_BIT_STRING *", bit_string) + 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 + key_encipherment = get_bit(bit_string, 2) == 1 + data_encipherment = get_bit(bit_string, 3) == 1 + key_agreement = get_bit(bit_string, 4) == 1 + key_cert_sign = get_bit(bit_string, 5) == 1 + crl_sign = get_bit(bit_string, 6) == 1 + encipher_only = get_bit(bit_string, 7) == 1 + decipher_only = get_bit(bit_string, 8) == 1 + return x509.KeyUsage( + digital_signature, + content_commitment, + key_encipherment, + data_encipherment, + key_agreement, + key_cert_sign, + crl_sign, + encipher_only, + decipher_only + ) - return x509.ExtendedKeyUsage(ekus) - def _build_crl_distribution_points(self, ext): - cdps = self._backend._ffi.cast( - "Cryptography_STACK_OF_DIST_POINT *", - self._backend._lib.X509V3_EXT_d2i(ext) - ) - assert cdps != self._backend._ffi.NULL - cdps = self._backend._ffi.gc( - cdps, self._backend._lib.sk_DIST_POINT_free) - num = self._backend._lib.sk_DIST_POINT_num(cdps) +def _decode_subject_alt_name(backend, ext): + gns = backend._ffi.cast( + "GENERAL_NAMES *", backend._lib.X509V3_EXT_d2i(ext) + ) + assert gns != backend._ffi.NULL + gns = backend._ffi.gc(gns, backend._lib.GENERAL_NAMES_free) + general_names = _decode_general_names(backend, gns) - dist_points = [] - for i in range(num): - full_name = None - relative_name = None - crl_issuer = None - reasons = None - cdp = self._backend._lib.sk_DIST_POINT_value(cdps, i) - if cdp.reasons != self._backend._ffi.NULL: - # We will check each bit from RFC 5280 - # ReasonFlags ::= BIT STRING { - # unused (0), - # keyCompromise (1), - # cACompromise (2), - # affiliationChanged (3), - # superseded (4), - # cessationOfOperation (5), - # certificateHold (6), - # privilegeWithdrawn (7), - # aACompromise (8) } - reasons = [] - get_bit = self._backend._lib.ASN1_BIT_STRING_get_bit - if get_bit(cdp.reasons, 1): - reasons.append(x509.ReasonFlags.key_compromise) - - if get_bit(cdp.reasons, 2): - reasons.append(x509.ReasonFlags.ca_compromise) - - if get_bit(cdp.reasons, 3): - reasons.append(x509.ReasonFlags.affiliation_changed) - - if get_bit(cdp.reasons, 4): - reasons.append(x509.ReasonFlags.superseded) - - if get_bit(cdp.reasons, 5): - reasons.append(x509.ReasonFlags.cessation_of_operation) - - if get_bit(cdp.reasons, 6): - reasons.append(x509.ReasonFlags.certificate_hold) - - if get_bit(cdp.reasons, 7): - reasons.append(x509.ReasonFlags.privilege_withdrawn) - - if get_bit(cdp.reasons, 8): - reasons.append(x509.ReasonFlags.aa_compromise) - - reasons = frozenset(reasons) - - if cdp.CRLissuer != self._backend._ffi.NULL: - crl_issuer = _build_general_names(self._backend, cdp.CRLissuer) - - # Certificates may have a crl_issuer/reasons and no distribution - # point so make sure it's not null. - if cdp.distpoint != self._backend._ffi.NULL: - # Type 0 is fullName, there is no #define for it in the code. - if cdp.distpoint.type == 0: - full_name = _build_general_names( - self._backend, cdp.distpoint.name.fullname - ) - # OpenSSL code doesn't test for a specific type for - # relativename, everything that isn't fullname is considered - # relativename. - else: - rns = cdp.distpoint.name.relativename - rnum = self._backend._lib.sk_X509_NAME_ENTRY_num(rns) - attributes = [] - for i in range(rnum): - rn = self._backend._lib.sk_X509_NAME_ENTRY_value( - rns, i - ) - assert rn != self._backend._ffi.NULL - attributes.append( - _build_x509_name_entry(self._backend, rn) - ) - - relative_name = x509.Name(attributes) - - dist_points.append( - x509.DistributionPoint( - full_name, relative_name, reasons, crl_issuer + return x509.SubjectAlternativeName(general_names) + + +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 + sk = backend._ffi.gc(sk, backend._lib.sk_ASN1_OBJECT_free) + num = backend._lib.sk_ASN1_OBJECT_num(sk) + ekus = [] + + for i in range(num): + obj = backend._lib.sk_ASN1_OBJECT_value(sk, i) + assert obj != backend._ffi.NULL + oid = x509.ObjectIdentifier(_obj2txt(backend, obj)) + ekus.append(oid) + + 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) + num = backend._lib.sk_DIST_POINT_num(cdps) + + dist_points = [] + for i in range(num): + full_name = None + relative_name = None + crl_issuer = None + reasons = None + cdp = backend._lib.sk_DIST_POINT_value(cdps, i) + if cdp.reasons != backend._ffi.NULL: + # We will check each bit from RFC 5280 + # ReasonFlags ::= BIT STRING { + # unused (0), + # keyCompromise (1), + # cACompromise (2), + # affiliationChanged (3), + # superseded (4), + # cessationOfOperation (5), + # certificateHold (6), + # privilegeWithdrawn (7), + # aACompromise (8) } + reasons = [] + get_bit = backend._lib.ASN1_BIT_STRING_get_bit + if get_bit(cdp.reasons, 1): + reasons.append(x509.ReasonFlags.key_compromise) + + if get_bit(cdp.reasons, 2): + reasons.append(x509.ReasonFlags.ca_compromise) + + if get_bit(cdp.reasons, 3): + reasons.append(x509.ReasonFlags.affiliation_changed) + + if get_bit(cdp.reasons, 4): + reasons.append(x509.ReasonFlags.superseded) + + if get_bit(cdp.reasons, 5): + reasons.append(x509.ReasonFlags.cessation_of_operation) + + if get_bit(cdp.reasons, 6): + reasons.append(x509.ReasonFlags.certificate_hold) + + if get_bit(cdp.reasons, 7): + reasons.append(x509.ReasonFlags.privilege_withdrawn) + + if get_bit(cdp.reasons, 8): + reasons.append(x509.ReasonFlags.aa_compromise) + + reasons = frozenset(reasons) + + if cdp.CRLissuer != backend._ffi.NULL: + crl_issuer = _decode_general_names(backend, cdp.CRLissuer) + + # Certificates may have a crl_issuer/reasons and no distribution + # point so make sure it's not null. + if cdp.distpoint != backend._ffi.NULL: + # Type 0 is fullName, there is no #define for it in the code. + if cdp.distpoint.type == 0: + full_name = _decode_general_names( + backend, cdp.distpoint.name.fullname ) + # OpenSSL code doesn't test for a specific type for + # relativename, everything that isn't fullname is considered + # relativename. + else: + rns = cdp.distpoint.name.relativename + rnum = backend._lib.sk_X509_NAME_ENTRY_num(rns) + attributes = [] + for i in range(rnum): + rn = backend._lib.sk_X509_NAME_ENTRY_value( + rns, i + ) + assert rn != backend._ffi.NULL + attributes.append( + _decode_x509_name_entry(backend, rn) + ) + + relative_name = x509.Name(attributes) + + dist_points.append( + x509.DistributionPoint( + full_name, relative_name, reasons, crl_issuer ) + ) - return x509.CRLDistributionPoints(dist_points) + return x509.CRLDistributionPoints(dist_points) @utils.register_interface(x509.CertificateSigningRequest) @@ -632,7 +646,7 @@ class _CertificateSigningRequest(object): def subject(self): subject = self._backend._lib.X509_REQ_get_subject_name(self._x509_req) assert subject != self._backend._ffi.NULL - return _build_x509_name(self._backend, subject) + return _decode_x509_name(self._backend, subject) @property def signature_hash_algorithm(self): -- cgit v1.2.3 From 58e870ca8ac61e4099fb9d5d9e10820d338f8671 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 17 May 2015 09:15:30 -0700 Subject: x509 extension equality --- src/cryptography/x509.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'src') diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index ec2092bb..325d6d62 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -278,6 +278,19 @@ class Extension(object): return ("").format(self) + def __eq__(self, other): + if not isinstance(other, Extension): + return NotImplemented + + return ( + self.oid == other.oid and + self.critical == other.critical and + self.value == other.value + ) + + def __ne__(self, other): + return not self == other + class ExtendedKeyUsage(object): def __init__(self, usages): -- cgit v1.2.3 From 6e721a939f6fff70940ff4272e288f17b193e138 Mon Sep 17 00:00:00 2001 From: Andre Caron Date: Sun, 17 May 2015 15:08:48 -0400 Subject: Adds support for CSR extensions. --- src/cryptography/hazmat/backends/openssl/x509.py | 32 ++++++++++++++++++++++++ src/cryptography/x509.py | 6 +++++ 2 files changed, 38 insertions(+) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index b36694dd..6db6fc9c 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -657,3 +657,35 @@ class _CertificateSigningRequest(object): raise UnsupportedAlgorithm( "Signature algorithm OID:{0} not recognized".format(oid) ) + + @property + def extensions(self): + extensions = [] + seen_oids = set() + x509_exts = self._backend._lib.X509_REQ_get_extensions(self._x509_req) + extcount = self._backend._lib.sk_X509_EXTENSION_num(x509_exts) + for i in range(0, extcount): + ext = self._backend._lib.sk_X509_EXTENSION_value(x509_exts, i) + assert ext != self._backend._ffi.NULL + crit = self._backend._lib.X509_EXTENSION_get_critical(ext) + critical = crit == 1 + oid = x509.ObjectIdentifier(_obj2txt(self._backend, ext.object)) + if oid in seen_oids: + raise x509.DuplicateExtension( + "Duplicate {0} extension found".format(oid), oid + ) + elif oid == x509.OID_BASIC_CONSTRAINTS: + value = _decode_basic_constraints(self._backend, ext) + elif critical: + raise x509.UnsupportedExtension( + "{0} is not currently supported".format(oid), oid + ) + else: + # Unsupported non-critical extension, silently skipping for now + seen_oids.add(oid) + continue + + seen_oids.add(oid) + extensions.append(x509.Extension(oid, critical, value)) + + return x509.Extensions(extensions) diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 325d6d62..49cc8493 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1177,3 +1177,9 @@ class CertificateSigningRequest(object): Returns a HashAlgorithm corresponding to the type of the digest signed in the certificate. """ + + @abc.abstractproperty + def extensions(self): + """ + Returns the extensions in the signing request. + """ -- cgit v1.2.3 From eb177931f825308f9b4df9c789f76b7ce04751f6 Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Sun, 17 May 2015 18:33:33 -0700 Subject: IPAddress needs to support networks for nameconstraints --- src/cryptography/x509.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 325d6d62..17c2a7c4 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -909,11 +909,18 @@ class RegisteredID(object): class IPAddress(object): def __init__(self, value): if not isinstance( - value, (ipaddress.IPv4Address, ipaddress.IPv6Address) + value, + ( + ipaddress.IPv4Address, + ipaddress.IPv6Address, + ipaddress.IPv4Network, + ipaddress.IPv6Network + ) ): raise TypeError( - "value must be an instance of ipaddress.IPv4Address or " - "ipaddress.IPv6Address" + "value must be an instance of ipaddress.IPv4Address, " + "ipaddress.IPv6Address, ipaddress.IPv4Network, or " + "ipaddress.IPv6Network" ) self._value = value -- cgit v1.2.3 From 4a1038e0742c720a6046f9bb20f1156ce8624c6b Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 18 May 2015 10:28:31 -0700 Subject: add ocsp no check --- src/cryptography/x509.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index ccb9f6de..7ac06622 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -320,6 +320,10 @@ class ExtendedKeyUsage(object): return not self == other +class OCSPNoCheck(object): + pass + + class BasicConstraints(object): def __init__(self, ca, path_length): if not isinstance(ca, bool): -- cgit v1.2.3