From dc57040a59ce4211a6ab6db843c497a87a825509 Mon Sep 17 00:00:00 2001 From: Erik Trauschke Date: Thu, 24 Sep 2015 20:24:28 -0700 Subject: OpenSSL backend code for CRLs --- src/cryptography/hazmat/backends/multibackend.py | 18 ++ .../hazmat/backends/openssl/backend.py | 26 ++- src/cryptography/hazmat/backends/openssl/x509.py | 258 +++++++++++++++++++++ src/cryptography/x509/__init__.py | 6 +- src/cryptography/x509/base.py | 8 + 5 files changed, 312 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py index 9db32aa5..cda33145 100644 --- a/src/cryptography/hazmat/backends/multibackend.py +++ b/src/cryptography/hazmat/backends/multibackend.py @@ -325,6 +325,24 @@ class MultiBackend(object): _Reasons.UNSUPPORTED_X509 ) + def load_pem_x509_crl(self, data): + for b in self._filtered_backends(X509Backend): + return b.load_pem_x509_crl(data) + + raise UnsupportedAlgorithm( + "This backend does not support X.509.", + _Reasons.UNSUPPORTED_X509 + ) + + def load_der_x509_crl(self, data): + for b in self._filtered_backends(X509Backend): + return b.load_der_x509_crl(data) + + raise UnsupportedAlgorithm( + "This backend does not support X.509.", + _Reasons.UNSUPPORTED_X509 + ) + def load_der_x509_csr(self, data): for b in self._filtered_backends(X509Backend): return b.load_der_x509_csr(data) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index c74d90d4..8503b9b9 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -39,8 +39,8 @@ from cryptography.hazmat.backends.openssl.rsa import ( _RSAPrivateKey, _RSAPublicKey ) from cryptography.hazmat.backends.openssl.x509 import ( - _Certificate, _CertificateSigningRequest, _DISTPOINT_TYPE_FULLNAME, - _DISTPOINT_TYPE_RELATIVENAME + _Certificate, _CertificateRevocationList, _CertificateSigningRequest, + _DISTPOINT_TYPE_FULLNAME, _DISTPOINT_TYPE_RELATIVENAME ) from cryptography.hazmat.bindings.openssl.binding import Binding from cryptography.hazmat.primitives import hashes, serialization @@ -1474,6 +1474,28 @@ class Backend(object): x509 = self._ffi.gc(x509, self._lib.X509_free) return _Certificate(self, x509) + def load_pem_x509_crl(self, data): + mem_bio = self._bytes_to_bio(data) + x509_crl = self._lib.PEM_read_bio_X509_CRL( + mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL + ) + if x509_crl == self._ffi.NULL: + self._consume_errors() + raise ValueError("Unable to load CRL") + + x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free) + return _CertificateRevocationList(self, x509_crl) + + def load_der_x509_crl(self, data): + mem_bio = self._bytes_to_bio(data) + x509_crl = self._lib.d2i_X509_CRL_bio(mem_bio.bio, self._ffi.NULL) + if x509_crl == self._ffi.NULL: + self._consume_errors() + raise ValueError("Unable to load CRL") + + x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free) + return _CertificateRevocationList(self, x509_crl) + def load_pem_x509_csr(self, data): mem_bio = self._bytes_to_bio(data) x509_req = self._lib.PEM_read_bio_X509_REQ( diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 70cec163..79b516cc 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -4,7 +4,9 @@ from __future__ import absolute_import, division, print_function +import datetime import ipaddress + from email.utils import parseaddr import idna @@ -637,6 +639,262 @@ def _decode_inhibit_any_policy(backend, asn1_int): return x509.InhibitAnyPolicy(skip_certs) +@utils.register_interface(x509.RevokedCertificate) +class _RevokedCertificate(object): + def __init__(self, backend, x509_revoked): + self._backend = backend + self._x509_revoked = x509_revoked + + self.__serial_number = None + self.__revocation_date = None + self.__extensions = None + + @property + def serial_number(self): + if self.__serial_number: + return self.__serial_number + + bn = self._backend._lib.ASN1_INTEGER_to_BN( + self._x509_revoked.serialNumber, self._backend._ffi.NULL + ) + assert bn != self._backend._ffi.NULL + bn = self._backend._ffi.gc(bn, self._backend._lib.BN_free) + self.__serial_number = self._backend._bn_to_int(bn) + return self.__serial_number + + @property + def revocation_date(self): + if self.__revocation_date: + return self.__revocation_date + + self.__revocation_date = self._backend._parse_asn1_time( + self._x509_revoked.revocationDate) + return self.__revocation_date + + @property + def extensions(self): + if self.__extensions: + return self.__extensions + + extensions = [] + seen_oids = set() + extcount = self._backend._lib.X509_REVOKED_get_ext_count( + self._x509_revoked) + for i in range(0, extcount): + ext = self._backend._lib.X509_REVOKED_get_ext( + self._x509_revoked, 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 + ) + + if oid == x509.OID_CRL_REASON: + value = self._build_crl_reason(ext) + elif oid == x509.OID_INVALIDITY_DATE: + value = self._build_invalidity_date(ext) + elif oid == x509.OID_CERTIFICATE_ISSUER and \ + self._backend._lib.OPENSSL_VERSION_NUMBER >= 0x10000000: + value = self._build_cert_issuer(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)) + + self.__extensions = x509.Extensions(extensions) + return self.__extensions + + def get_reason(self): + """ + Returns the CRLReason extension if it exists. + """ + try: + return self.extensions.get_extension_for_oid( + x509.OID_CRL_REASON).value + except x509.ExtensionNotFound: + return None + + def get_invalidity_date(self): + """ + Returns the InvalidityDate extension if it exists. + """ + try: + return self.extensions.get_extension_for_oid( + x509.OID_INVALIDITY_DATE).value + except x509.ExtensionNotFound: + return None + + def get_certificate_issuer(self): + """ + Returns the CertificateIssuer extension if it exists. + """ + try: + return self.extensions.get_extension_for_oid( + x509.OID_CERTIFICATE_ISSUER).value + except x509.ExtensionNotFound: + return None + + def _build_crl_reason(self, ext): + enum = self._backend._lib.X509V3_EXT_d2i(ext) + assert enum != self._backend._ffi.NULL + enum = self._backend._ffi.cast("ASN1_ENUMERATED *", enum) + enum = self._backend._ffi.gc( + enum, self._backend._lib.ASN1_ENUMERATED_free + ) + code = self._backend._lib.ASN1_ENUMERATED_get(enum) + if code == 0: + return x509.ReasonFlags.unspecified + elif code == 1: + return x509.ReasonFlags.key_compromise + elif code == 2: + return x509.ReasonFlags.ca_compromise + elif code == 3: + return x509.ReasonFlags.affiliation_changed + elif code == 4: + return x509.ReasonFlags.superseded + elif code == 5: + return x509.ReasonFlags.cessation_of_operation + elif code == 6: + return x509.ReasonFlags.certificate_hold + elif code == 8: + return x509.ReasonFlags.remove_from_crl + elif code == 9: + return x509.ReasonFlags.privilege_withdrawn + elif code == 10: + return x509.ReasonFlags.aa_compromise + else: + raise ValueError("Unsupported reason code: {0}".format(code)) + + def _build_invalidity_date(self, ext): + generalized_time = self._backend._ffi.cast( + "ASN1_GENERALIZEDTIME *", + self._backend._lib.X509V3_EXT_d2i(ext) + ) + assert generalized_time != self._backend._ffi.NULL + generalized_time = self._backend._ffi.gc( + generalized_time, self._backend._lib.ASN1_GENERALIZEDTIME_free + ) + time = self._backend._ffi.string( + self._backend._lib.ASN1_STRING_data( + self._backend._ffi.cast("ASN1_STRING *", generalized_time) + ) + ).decode("ascii") + return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ") + + def _build_cert_issuer(self, ext): + gns = self._backend._ffi.cast( + "GENERAL_NAMES *", self._backend._lib.X509V3_EXT_d2i(ext) + ) + assert gns != self._backend._ffi.NULL + gns = self._backend._ffi.gc(gns, self._backend._lib.GENERAL_NAMES_free) + return x509.GeneralNames(_decode_general_names(self._backend, gns)) + + +@utils.register_interface(x509.CertificateRevocationList) +class _CertificateRevocationList(object): + def __init__(self, backend, x509_crl): + self._backend = backend + self._x509_crl = x509_crl + + self.__revoked = None + self.__issuer = None + self.__next_update = None + self.__last_update = None + + def __eq__(self, other): + if not isinstance(other, x509.CertificateRevocationList): + return NotImplemented + + res = self._backend._lib.X509_CRL_cmp(self._x509_crl, other._x509_crl) + return res == 0 + + def __ne__(self, other): + return not self == other + + def fingerprint(self, algorithm): + h = hashes.Hash(algorithm, self._backend) + bio = self._backend._create_mem_bio() + res = self._backend._lib.i2d_X509_CRL_bio( + bio, self._x509_crl + ) + assert res == 1 + der = self._backend._read_mem_bio(bio) + h.update(der) + return h.finalize() + + @property + def signature_hash_algorithm(self): + oid = _obj2txt(self._backend, self._x509_crl.sig_alg.algorithm) + try: + return x509._SIG_OIDS_TO_HASH[oid] + except KeyError: + raise UnsupportedAlgorithm( + "Signature algorithm OID:{0} not recognized".format(oid) + ) + + @property + def issuer(self): + if self.__issuer: + return self.__issuer + + issuer = self._backend._lib.X509_CRL_get_issuer(self._x509_crl) + assert issuer != self._backend._ffi.NULL + self.__issuer = _decode_x509_name(self._backend, issuer) + return self.__issuer + + @property + def next_update(self): + if self.__next_update: + return self.__next_update + + nu = self._backend._lib.X509_CRL_get_nextUpdate(self._x509_crl) + assert nu != self._backend._ffi.NULL + self.__next_update = self._backend._parse_asn1_time(nu) + return self.__next_update + + @property + def last_update(self): + if self.__last_update: + return self.__last_update + + lu = self._backend._lib.X509_CRL_get_lastUpdate(self._x509_crl) + assert lu != self._backend._ffi.NULL + self.__last_update = self._backend._parse_asn1_time(lu) + return self.__last_update + + @property + def revoked_certificates(self): + if self.__revoked: + return self.__revoked + + revoked = self._backend._lib.X509_CRL_get_REVOKED(self._x509_crl) + assert revoked != self._backend._ffi.NULL + + num = self._backend._lib.sk_X509_REVOKED_num(revoked) + revoked_list = [] + for i in range(num): + r = self._backend._lib.sk_X509_REVOKED_value(revoked, i) + assert r != self._backend._ffi.NULL + revoked_list.append(_RevokedCertificate(self._backend, r)) + + self.__revoked = revoked_list + return self.__revoked + + @property + def extensions(self): + raise NotImplementedError() + + @utils.register_interface(x509.CertificateSigningRequest) class _CertificateSigningRequest(object): def __init__(self, backend, x509_req): diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py index 1aa2598b..70e1d3da 100644 --- a/src/cryptography/x509/__init__.py +++ b/src/cryptography/x509/__init__.py @@ -8,8 +8,8 @@ from cryptography.x509.base import ( Certificate, CertificateBuilder, CertificateRevocationList, CertificateSigningRequest, CertificateSigningRequestBuilder, InvalidVersion, RevokedCertificate, - Version, load_der_x509_certificate, load_der_x509_csr, - load_pem_x509_certificate, load_pem_x509_csr, + Version, load_der_x509_certificate, load_der_x509_crl, load_der_x509_csr, + load_pem_x509_certificate, load_pem_x509_crl, load_pem_x509_csr, ) from cryptography.x509.extensions import ( AccessDescription, AuthorityInformationAccess, @@ -108,6 +108,8 @@ __all__ = [ "load_der_x509_certificate", "load_pem_x509_csr", "load_der_x509_csr", + "load_pem_x509_crl", + "load_der_x509_crl", "InvalidVersion", "DuplicateExtension", "UnsupportedExtension", diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py index 27eafac6..9dc49a60 100644 --- a/src/cryptography/x509/base.py +++ b/src/cryptography/x509/base.py @@ -40,6 +40,14 @@ def load_der_x509_csr(data, backend): return backend.load_der_x509_csr(data) +def load_pem_x509_crl(data, backend): + return backend.load_pem_x509_crl(data) + + +def load_der_x509_crl(data, backend): + return backend.load_der_x509_crl(data) + + class InvalidVersion(Exception): def __init__(self, msg, parsed_version): super(InvalidVersion, self).__init__(msg) -- cgit v1.2.3 From 396a282fd5febabe382dcdacf4e8a6c2395f0087 Mon Sep 17 00:00:00 2001 From: Erik Trauschke Date: Mon, 28 Sep 2015 08:56:18 -0700 Subject: use openssl assert change _build* to _decode* make CRLs into iterators various fixes --- src/cryptography/hazmat/backends/openssl/x509.py | 49 +++++++++++++----------- 1 file changed, 27 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 8a13aa5d..a0ad87ec 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -654,12 +654,9 @@ class _RevokedCertificate(object): if self.__serial_number: return self.__serial_number - bn = self._backend._lib.ASN1_INTEGER_to_BN( - self._x509_revoked.serialNumber, self._backend._ffi.NULL - ) - assert bn != self._backend._ffi.NULL - bn = self._backend._ffi.gc(bn, self._backend._lib.BN_free) - self.__serial_number = self._backend._bn_to_int(bn) + asn1_int = self._x509_revoked.serialNumber + self._backend.openssl_assert(asn1_int != self._backend._ffi.NULL) + self.__serial_number = self._backend._asn1_integer_to_int(asn1_int) return self.__serial_number @property @@ -683,7 +680,7 @@ class _RevokedCertificate(object): for i in range(0, extcount): ext = self._backend._lib.X509_REVOKED_get_ext( self._x509_revoked, i) - assert ext != self._backend._ffi.NULL + self._backend.openssl_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)) @@ -693,12 +690,12 @@ class _RevokedCertificate(object): ) if oid == x509.OID_CRL_REASON: - value = self._build_crl_reason(ext) + value = self._decode_crl_reason(ext) elif oid == x509.OID_INVALIDITY_DATE: - value = self._build_invalidity_date(ext) + value = self._decode_invalidity_date(ext) elif oid == x509.OID_CERTIFICATE_ISSUER and \ self._backend._lib.OPENSSL_VERSION_NUMBER >= 0x10000000: - value = self._build_cert_issuer(ext) + value = self._decode_cert_issuer(ext) elif critical: raise x509.UnsupportedExtension( "{0} is not currently supported".format(oid), oid @@ -744,9 +741,9 @@ class _RevokedCertificate(object): except x509.ExtensionNotFound: return None - def _build_crl_reason(self, ext): + def _decode_crl_reason(self, ext): enum = self._backend._lib.X509V3_EXT_d2i(ext) - assert enum != self._backend._ffi.NULL + self._backend.openssl_assert(enum != self._backend._ffi.NULL) enum = self._backend._ffi.cast("ASN1_ENUMERATED *", enum) enum = self._backend._ffi.gc( enum, self._backend._lib.ASN1_ENUMERATED_free @@ -775,12 +772,14 @@ class _RevokedCertificate(object): else: raise ValueError("Unsupported reason code: {0}".format(code)) - def _build_invalidity_date(self, ext): + def _decode_invalidity_date(self, ext): generalized_time = self._backend._ffi.cast( "ASN1_GENERALIZEDTIME *", self._backend._lib.X509V3_EXT_d2i(ext) ) - assert generalized_time != self._backend._ffi.NULL + self._backend.openssl_assert( + generalized_time != self._backend._ffi.NULL + ) generalized_time = self._backend._ffi.gc( generalized_time, self._backend._lib.ASN1_GENERALIZEDTIME_free ) @@ -791,11 +790,11 @@ class _RevokedCertificate(object): ).decode("ascii") return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ") - def _build_cert_issuer(self, ext): + def _decode_cert_issuer(self, ext): gns = self._backend._ffi.cast( "GENERAL_NAMES *", self._backend._lib.X509V3_EXT_d2i(ext) ) - assert gns != self._backend._ffi.NULL + self._backend.openssl_assert(gns != self._backend._ffi.NULL) gns = self._backend._ffi.gc(gns, self._backend._lib.GENERAL_NAMES_free) return x509.GeneralNames(_decode_general_names(self._backend, gns)) @@ -827,7 +826,7 @@ class _CertificateRevocationList(object): res = self._backend._lib.i2d_X509_CRL_bio( bio, self._x509_crl ) - assert res == 1 + self._backend.openssl_assert(res == 1) der = self._backend._read_mem_bio(bio) h.update(der) return h.finalize() @@ -848,7 +847,7 @@ class _CertificateRevocationList(object): return self.__issuer issuer = self._backend._lib.X509_CRL_get_issuer(self._x509_crl) - assert issuer != self._backend._ffi.NULL + self._backend.openssl_assert(issuer != self._backend._ffi.NULL) self.__issuer = _decode_x509_name(self._backend, issuer) return self.__issuer @@ -858,7 +857,7 @@ class _CertificateRevocationList(object): return self.__next_update nu = self._backend._lib.X509_CRL_get_nextUpdate(self._x509_crl) - assert nu != self._backend._ffi.NULL + self._backend.openssl_assert(nu != self._backend._ffi.NULL) self.__next_update = self._backend._parse_asn1_time(nu) return self.__next_update @@ -868,7 +867,7 @@ class _CertificateRevocationList(object): return self.__last_update lu = self._backend._lib.X509_CRL_get_lastUpdate(self._x509_crl) - assert lu != self._backend._ffi.NULL + self._backend.openssl_assert(lu != self._backend._ffi.NULL) self.__last_update = self._backend._parse_asn1_time(lu) return self.__last_update @@ -878,18 +877,24 @@ class _CertificateRevocationList(object): return self.__revoked revoked = self._backend._lib.X509_CRL_get_REVOKED(self._x509_crl) - assert revoked != self._backend._ffi.NULL + self._backend.openssl_assert(revoked != self._backend._ffi.NULL) num = self._backend._lib.sk_X509_REVOKED_num(revoked) revoked_list = [] for i in range(num): r = self._backend._lib.sk_X509_REVOKED_value(revoked, i) - assert r != self._backend._ffi.NULL + self._backend.openssl_assert(r != self._backend._ffi.NULL) revoked_list.append(_RevokedCertificate(self._backend, r)) self.__revoked = revoked_list return self.__revoked + def __iter__(self): + return iter(self.revoked_certificates) + + def __len__(self): + return len(self.revoked_certificates) + @property def extensions(self): raise NotImplementedError() -- cgit v1.2.3 From 3cdabaf2544b98c6c915ca6704f9e03fdd58e1e6 Mon Sep 17 00:00:00 2001 From: Erik Trauschke Date: Tue, 13 Oct 2015 09:42:53 -0700 Subject: fix indentations change docs to indicate CRL objects are iterable fix docs for revoked certs make _decode_crl_reason more readable add __getitem__ method to CRL object remove double underscores --- src/cryptography/hazmat/backends/openssl/x509.py | 133 +++++++++++------------ 1 file changed, 65 insertions(+), 68 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index a0ad87ec..32678939 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -645,39 +645,39 @@ class _RevokedCertificate(object): self._backend = backend self._x509_revoked = x509_revoked - self.__serial_number = None - self.__revocation_date = None - self.__extensions = None + self._serial_number = None + self._revocation_date = None + self._extensions = None @property def serial_number(self): - if self.__serial_number: - return self.__serial_number + if self._serial_number: + return self._serial_number asn1_int = self._x509_revoked.serialNumber self._backend.openssl_assert(asn1_int != self._backend._ffi.NULL) - self.__serial_number = self._backend._asn1_integer_to_int(asn1_int) - return self.__serial_number + self._serial_number = self._backend._asn1_integer_to_int(asn1_int) + return self._serial_number @property def revocation_date(self): - if self.__revocation_date: - return self.__revocation_date + if self._revocation_date: + return self._revocation_date - self.__revocation_date = self._backend._parse_asn1_time( + self._revocation_date = self._backend._parse_asn1_time( self._x509_revoked.revocationDate) - return self.__revocation_date + return self._revocation_date @property def extensions(self): - if self.__extensions: - return self.__extensions + if self._extensions: + return self._extensions extensions = [] seen_oids = set() extcount = self._backend._lib.X509_REVOKED_get_ext_count( self._x509_revoked) - for i in range(0, extcount): + for i in range(extcount): ext = self._backend._lib.X509_REVOKED_get_ext( self._x509_revoked, i) self._backend.openssl_assert(ext != self._backend._ffi.NULL) @@ -693,8 +693,8 @@ class _RevokedCertificate(object): value = self._decode_crl_reason(ext) elif oid == x509.OID_INVALIDITY_DATE: value = self._decode_invalidity_date(ext) - elif oid == x509.OID_CERTIFICATE_ISSUER and \ - self._backend._lib.OPENSSL_VERSION_NUMBER >= 0x10000000: + elif (oid == x509.OID_CERTIFICATE_ISSUER and + self._backend._lib.OPENSSL_VERSION_NUMBER >= 0x10000000): value = self._decode_cert_issuer(ext) elif critical: raise x509.UnsupportedExtension( @@ -708,38 +708,38 @@ class _RevokedCertificate(object): seen_oids.add(oid) extensions.append(x509.Extension(oid, critical, value)) - self.__extensions = x509.Extensions(extensions) - return self.__extensions + self._extensions = x509.Extensions(extensions) + return self._extensions def get_reason(self): """ Returns the CRLReason extension if it exists. """ try: - return self.extensions.get_extension_for_oid( - x509.OID_CRL_REASON).value + return self.extensions.get_extension_for_oid( + x509.OID_CRL_REASON).value except x509.ExtensionNotFound: - return None + return None def get_invalidity_date(self): """ Returns the InvalidityDate extension if it exists. """ try: - return self.extensions.get_extension_for_oid( - x509.OID_INVALIDITY_DATE).value + return self.extensions.get_extension_for_oid( + x509.OID_INVALIDITY_DATE).value except x509.ExtensionNotFound: - return None + return None def get_certificate_issuer(self): """ Returns the CertificateIssuer extension if it exists. """ try: - return self.extensions.get_extension_for_oid( - x509.OID_CERTIFICATE_ISSUER).value + return self.extensions.get_extension_for_oid( + x509.OID_CERTIFICATE_ISSUER).value except x509.ExtensionNotFound: - return None + return None def _decode_crl_reason(self, ext): enum = self._backend._lib.X509V3_EXT_d2i(ext) @@ -749,27 +749,21 @@ class _RevokedCertificate(object): enum, self._backend._lib.ASN1_ENUMERATED_free ) code = self._backend._lib.ASN1_ENUMERATED_get(enum) - if code == 0: - return x509.ReasonFlags.unspecified - elif code == 1: - return x509.ReasonFlags.key_compromise - elif code == 2: - return x509.ReasonFlags.ca_compromise - elif code == 3: - return x509.ReasonFlags.affiliation_changed - elif code == 4: - return x509.ReasonFlags.superseded - elif code == 5: - return x509.ReasonFlags.cessation_of_operation - elif code == 6: - return x509.ReasonFlags.certificate_hold - elif code == 8: - return x509.ReasonFlags.remove_from_crl - elif code == 9: - return x509.ReasonFlags.privilege_withdrawn - elif code == 10: - return x509.ReasonFlags.aa_compromise - else: + + try: + return { + 0: x509.ReasonFlags.unspecified, + 1: x509.ReasonFlags.key_compromise, + 2: x509.ReasonFlags.ca_compromise, + 3: x509.ReasonFlags.affiliation_changed, + 4: x509.ReasonFlags.superseded, + 5: x509.ReasonFlags.cessation_of_operation, + 6: x509.ReasonFlags.certificate_hold, + 8: x509.ReasonFlags.remove_from_crl, + 9: x509.ReasonFlags.privilege_withdrawn, + 10: x509.ReasonFlags.aa_compromise, + }[code] + except KeyError: raise ValueError("Unsupported reason code: {0}".format(code)) def _decode_invalidity_date(self, ext): @@ -805,10 +799,10 @@ class _CertificateRevocationList(object): self._backend = backend self._x509_crl = x509_crl - self.__revoked = None - self.__issuer = None - self.__next_update = None - self.__last_update = None + self._revoked = None + self._issuer = None + self._next_update = None + self._last_update = None def __eq__(self, other): if not isinstance(other, x509.CertificateRevocationList): @@ -843,38 +837,38 @@ class _CertificateRevocationList(object): @property def issuer(self): - if self.__issuer: - return self.__issuer + if self._issuer: + return self._issuer issuer = self._backend._lib.X509_CRL_get_issuer(self._x509_crl) self._backend.openssl_assert(issuer != self._backend._ffi.NULL) - self.__issuer = _decode_x509_name(self._backend, issuer) - return self.__issuer + self._issuer = _decode_x509_name(self._backend, issuer) + return self._issuer @property def next_update(self): - if self.__next_update: - return self.__next_update + if self._next_update: + return self._next_update nu = self._backend._lib.X509_CRL_get_nextUpdate(self._x509_crl) self._backend.openssl_assert(nu != self._backend._ffi.NULL) - self.__next_update = self._backend._parse_asn1_time(nu) - return self.__next_update + self._next_update = self._backend._parse_asn1_time(nu) + return self._next_update @property def last_update(self): - if self.__last_update: - return self.__last_update + if self._last_update: + return self._last_update lu = self._backend._lib.X509_CRL_get_lastUpdate(self._x509_crl) self._backend.openssl_assert(lu != self._backend._ffi.NULL) - self.__last_update = self._backend._parse_asn1_time(lu) - return self.__last_update + self._last_update = self._backend._parse_asn1_time(lu) + return self._last_update @property def revoked_certificates(self): - if self.__revoked: - return self.__revoked + if self._revoked: + return self._revoked revoked = self._backend._lib.X509_CRL_get_REVOKED(self._x509_crl) self._backend.openssl_assert(revoked != self._backend._ffi.NULL) @@ -886,12 +880,15 @@ class _CertificateRevocationList(object): self._backend.openssl_assert(r != self._backend._ffi.NULL) revoked_list.append(_RevokedCertificate(self._backend, r)) - self.__revoked = revoked_list - return self.__revoked + self._revoked = revoked_list + return self._revoked def __iter__(self): return iter(self.revoked_certificates) + def __getitem__(self, idx): + return self.revoked_certificates[idx] + def __len__(self): return len(self.revoked_certificates) -- cgit v1.2.3 From 77f5a2560a2c637364467a5f74b60a0e70e177f9 Mon Sep 17 00:00:00 2001 From: Erik Trauschke Date: Wed, 14 Oct 2015 08:06:38 -0700 Subject: use X509ExtensionParser for Revoked extensions remove revoked_certificates property from RevokedCertificate class CRLExtensions should actually be RevokedExtensions doctest cleanup for RevokedCertificate --- src/cryptography/hazmat/backends/openssl/x509.py | 179 ++++++++++------------- src/cryptography/x509/__init__.py | 8 +- src/cryptography/x509/base.py | 6 - src/cryptography/x509/oid.py | 8 +- 4 files changed, 88 insertions(+), 113 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 32678939..7f7be545 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -18,7 +18,9 @@ from six.moves import urllib_parse from cryptography import utils, x509 from cryptography.exceptions import UnsupportedAlgorithm from cryptography.hazmat.primitives import hashes, serialization -from cryptography.x509.oid import CertificatePoliciesOID, ExtensionOID +from cryptography.x509.oid import ( + CertificatePoliciesOID, ExtensionOID, RevokedExtensionOID +) def _obj2txt(backend, obj): @@ -173,10 +175,11 @@ def _decode_ocsp_no_check(backend, ext): class _X509ExtensionParser(object): - def __init__(self, ext_count, get_ext, handlers): + def __init__(self, ext_count, get_ext, handlers, supported_versions=None): self.ext_count = ext_count self.get_ext = get_ext self.handlers = handlers + self.supported_versions = supported_versions def parse(self, backend, x509_obj): extensions = [] @@ -187,6 +190,13 @@ class _X509ExtensionParser(object): crit = backend._lib.X509_EXTENSION_get_critical(ext) critical = crit == 1 oid = x509.ObjectIdentifier(_obj2txt(backend, ext.object)) + + # Filter out extensions we know are not supported by the backend + if (self.supported_versions and oid in self.supported_versions and + self.supported_versions[oid] > + backend._lib.OPENSSL_VERSION_NUMBER): + self.handlers.pop(oid, None) + if oid in seen_oids: raise x509.DuplicateExtension( "Duplicate {0} extension found".format(oid), oid @@ -196,7 +206,8 @@ class _X509ExtensionParser(object): except KeyError: if critical: raise x509.UnsupportedExtension( - "{0} is not currently supported".format(oid), oid + "Critical extension {0} is not currently supported" + .format(oid), oid ) else: d2i = backend._lib.X509V3_EXT_d2i(ext) @@ -639,6 +650,49 @@ def _decode_inhibit_any_policy(backend, asn1_int): return x509.InhibitAnyPolicy(skip_certs) +def _decode_crl_reason(backend, enum): + enum = backend._ffi.cast("ASN1_ENUMERATED *", enum) + enum = backend._ffi.gc(enum, backend._lib.ASN1_ENUMERATED_free) + code = backend._lib.ASN1_ENUMERATED_get(enum) + + try: + return { + 0: x509.ReasonFlags.unspecified, + 1: x509.ReasonFlags.key_compromise, + 2: x509.ReasonFlags.ca_compromise, + 3: x509.ReasonFlags.affiliation_changed, + 4: x509.ReasonFlags.superseded, + 5: x509.ReasonFlags.cessation_of_operation, + 6: x509.ReasonFlags.certificate_hold, + 8: x509.ReasonFlags.remove_from_crl, + 9: x509.ReasonFlags.privilege_withdrawn, + 10: x509.ReasonFlags.aa_compromise, + }[code] + except KeyError: + raise ValueError("Unsupported reason code: {0}".format(code)) + + +def _decode_invalidity_date(backend, inv_date): + generalized_time = backend._ffi.cast( + "ASN1_GENERALIZEDTIME *", inv_date + ) + generalized_time = backend._ffi.gc( + generalized_time, backend._lib.ASN1_GENERALIZEDTIME_free + ) + time = backend._ffi.string( + backend._lib.ASN1_STRING_data( + backend._ffi.cast("ASN1_STRING *", generalized_time) + ) + ).decode("ascii") + return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ") + + +def _decode_cert_issuer(backend, issuer): + gns = backend._ffi.cast("GENERAL_NAMES *", issuer) + gns = backend._ffi.gc(gns, backend._lib.GENERAL_NAMES_free) + return x509.GeneralNames(_decode_general_names(backend, gns)) + + @utils.register_interface(x509.RevokedCertificate) class _RevokedCertificate(object): def __init__(self, backend, x509_revoked): @@ -670,46 +724,9 @@ class _RevokedCertificate(object): @property def extensions(self): - if self._extensions: - return self._extensions - - extensions = [] - seen_oids = set() - extcount = self._backend._lib.X509_REVOKED_get_ext_count( - self._x509_revoked) - for i in range(extcount): - ext = self._backend._lib.X509_REVOKED_get_ext( - self._x509_revoked, i) - self._backend.openssl_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 - ) - - if oid == x509.OID_CRL_REASON: - value = self._decode_crl_reason(ext) - elif oid == x509.OID_INVALIDITY_DATE: - value = self._decode_invalidity_date(ext) - elif (oid == x509.OID_CERTIFICATE_ISSUER and - self._backend._lib.OPENSSL_VERSION_NUMBER >= 0x10000000): - value = self._decode_cert_issuer(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)) - - self._extensions = x509.Extensions(extensions) - return self._extensions + return _REVOKED_CERTIFICATE_EXTENSION_PARSER.parse( + self._backend, self._x509_revoked + ) def get_reason(self): """ @@ -741,57 +758,6 @@ class _RevokedCertificate(object): except x509.ExtensionNotFound: return None - def _decode_crl_reason(self, ext): - enum = self._backend._lib.X509V3_EXT_d2i(ext) - self._backend.openssl_assert(enum != self._backend._ffi.NULL) - enum = self._backend._ffi.cast("ASN1_ENUMERATED *", enum) - enum = self._backend._ffi.gc( - enum, self._backend._lib.ASN1_ENUMERATED_free - ) - code = self._backend._lib.ASN1_ENUMERATED_get(enum) - - try: - return { - 0: x509.ReasonFlags.unspecified, - 1: x509.ReasonFlags.key_compromise, - 2: x509.ReasonFlags.ca_compromise, - 3: x509.ReasonFlags.affiliation_changed, - 4: x509.ReasonFlags.superseded, - 5: x509.ReasonFlags.cessation_of_operation, - 6: x509.ReasonFlags.certificate_hold, - 8: x509.ReasonFlags.remove_from_crl, - 9: x509.ReasonFlags.privilege_withdrawn, - 10: x509.ReasonFlags.aa_compromise, - }[code] - except KeyError: - raise ValueError("Unsupported reason code: {0}".format(code)) - - def _decode_invalidity_date(self, ext): - generalized_time = self._backend._ffi.cast( - "ASN1_GENERALIZEDTIME *", - self._backend._lib.X509V3_EXT_d2i(ext) - ) - self._backend.openssl_assert( - generalized_time != self._backend._ffi.NULL - ) - generalized_time = self._backend._ffi.gc( - generalized_time, self._backend._lib.ASN1_GENERALIZEDTIME_free - ) - time = self._backend._ffi.string( - self._backend._lib.ASN1_STRING_data( - self._backend._ffi.cast("ASN1_STRING *", generalized_time) - ) - ).decode("ascii") - return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ") - - def _decode_cert_issuer(self, ext): - gns = self._backend._ffi.cast( - "GENERAL_NAMES *", self._backend._lib.X509V3_EXT_d2i(ext) - ) - self._backend.openssl_assert(gns != self._backend._ffi.NULL) - gns = self._backend._ffi.gc(gns, self._backend._lib.GENERAL_NAMES_free) - return x509.GeneralNames(_decode_general_names(self._backend, gns)) - @utils.register_interface(x509.CertificateRevocationList) class _CertificateRevocationList(object): @@ -865,8 +831,7 @@ class _CertificateRevocationList(object): self._last_update = self._backend._parse_asn1_time(lu) return self._last_update - @property - def revoked_certificates(self): + def _revoked_certificates(self): if self._revoked: return self._revoked @@ -884,13 +849,13 @@ class _CertificateRevocationList(object): return self._revoked def __iter__(self): - return iter(self.revoked_certificates) + return iter(self._revoked_certificates()) def __getitem__(self, idx): - return self.revoked_certificates[idx] + return self._revoked_certificates()[idx] def __len__(self): - return len(self.revoked_certificates) + return len(self._revoked_certificates()) @property def extensions(self): @@ -977,6 +942,15 @@ _EXTENSION_HANDLERS = { ExtensionOID.NAME_CONSTRAINTS: _decode_name_constraints, } +_REVOKED_EXTENSION_HANDLERS = { + RevokedExtensionOID.CRL_REASON: _decode_crl_reason, + RevokedExtensionOID.INVALIDITY_DATE: _decode_invalidity_date, + RevokedExtensionOID.CERTIFICATE_ISSUER: _decode_cert_issuer, +} + +_REVOKED_SUPPORTED_VERSIONS = { + RevokedExtensionOID.CERTIFICATE_ISSUER: 0x10000000, +} _CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser( ext_count=lambda backend, x: backend._lib.X509_get_ext_count(x), @@ -989,3 +963,10 @@ _CSR_EXTENSION_PARSER = _X509ExtensionParser( get_ext=lambda backend, x, i: backend._lib.sk_X509_EXTENSION_value(x, i), handlers=_EXTENSION_HANDLERS ) + +_REVOKED_CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser( + ext_count=lambda backend, x: backend._lib.X509_REVOKED_get_ext_count(x), + get_ext=lambda backend, x, i: backend._lib.X509_REVOKED_get_ext(x, i), + handlers=_REVOKED_EXTENSION_HANDLERS, + supported_versions=_REVOKED_SUPPORTED_VERSIONS +) diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py index 70e1d3da..697df6f2 100644 --- a/src/cryptography/x509/__init__.py +++ b/src/cryptography/x509/__init__.py @@ -28,7 +28,7 @@ from cryptography.x509.general_name import ( ) from cryptography.x509.name import Name, NameAttribute from cryptography.x509.oid import ( - AuthorityInformationAccessOID, CRLExtensionOID, CertificatePoliciesOID, + AuthorityInformationAccessOID, RevokedExtensionOID, CertificatePoliciesOID, ExtendedKeyUsageOID, ExtensionOID, NameOID, ObjectIdentifier, SignatureAlgorithmOID, _SIG_OIDS_TO_HASH ) @@ -95,9 +95,9 @@ OID_ANY_POLICY = CertificatePoliciesOID.ANY_POLICY OID_CPS_QUALIFIER = CertificatePoliciesOID.CPS_QUALIFIER OID_CPS_USER_NOTICE = CertificatePoliciesOID.CPS_USER_NOTICE -OID_CERTIFICATE_ISSUER = CRLExtensionOID.CERTIFICATE_ISSUER -OID_CRL_REASON = CRLExtensionOID.CRL_REASON -OID_INVALIDITY_DATE = CRLExtensionOID.INVALIDITY_DATE +OID_CERTIFICATE_ISSUER = RevokedExtensionOID.CERTIFICATE_ISSUER +OID_CRL_REASON = RevokedExtensionOID.CRL_REASON +OID_INVALIDITY_DATE = RevokedExtensionOID.INVALIDITY_DATE OID_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS OID_OCSP = AuthorityInformationAccessOID.OCSP diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py index 9dc49a60..01eadfcb 100644 --- a/src/cryptography/x509/base.py +++ b/src/cryptography/x509/base.py @@ -176,12 +176,6 @@ class CertificateRevocationList(object): Returns the date of last update for this CRL. """ - @abc.abstractproperty - def revoked_certificates(self): - """ - Returns a list of RevokedCertificate objects for this CRL. - """ - @abc.abstractproperty def extensions(self): """ diff --git a/src/cryptography/x509/oid.py b/src/cryptography/x509/oid.py index ead40169..667045af 100644 --- a/src/cryptography/x509/oid.py +++ b/src/cryptography/x509/oid.py @@ -58,7 +58,7 @@ class ExtensionOID(object): OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5") -class CRLExtensionOID(object): +class RevokedExtensionOID(object): CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29") CRL_REASON = ObjectIdentifier("2.5.29.21") INVALIDITY_DATE = ObjectIdentifier("2.5.29.24") @@ -177,9 +177,9 @@ _OID_NAMES = { ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName", ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName", ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints", - CRLExtensionOID.CRL_REASON: "cRLReason", - CRLExtensionOID.INVALIDITY_DATE: "invalidityDate", - CRLExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer", + RevokedExtensionOID.CRL_REASON: "cRLReason", + RevokedExtensionOID.INVALIDITY_DATE: "invalidityDate", + RevokedExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer", ExtensionOID.NAME_CONSTRAINTS: "nameConstraints", ExtensionOID.CRL_DISTRIBUTION_POINTS: "cRLDistributionPoints", ExtensionOID.CERTIFICATE_POLICIES: "certificatePolicies", -- cgit v1.2.3 From 164bae538cfe5fcb320ebe5ee7e080598ad7ec5f Mon Sep 17 00:00:00 2001 From: Erik Trauschke Date: Wed, 14 Oct 2015 09:23:41 -0700 Subject: import fix --- src/cryptography/x509/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py index 697df6f2..6438da9c 100644 --- a/src/cryptography/x509/__init__.py +++ b/src/cryptography/x509/__init__.py @@ -28,8 +28,8 @@ from cryptography.x509.general_name import ( ) from cryptography.x509.name import Name, NameAttribute from cryptography.x509.oid import ( - AuthorityInformationAccessOID, RevokedExtensionOID, CertificatePoliciesOID, - ExtendedKeyUsageOID, ExtensionOID, NameOID, ObjectIdentifier, + AuthorityInformationAccessOID, CertificatePoliciesOID, ExtendedKeyUsageOID, + ExtensionOID, NameOID, ObjectIdentifier, RevokedExtensionOID, SignatureAlgorithmOID, _SIG_OIDS_TO_HASH ) -- cgit v1.2.3 From d4e7d43416077f18a37008298abdc566bd3f069d Mon Sep 17 00:00:00 2001 From: Erik Trauschke Date: Thu, 15 Oct 2015 14:45:38 -0700 Subject: removing caching mechanism for x509 properties undo name change of CRLExtensionOID use custom parsing mechanism for certIssuer entry extension add new crl to vectors for testing invalid certIssuer entry ext --- src/_cffi_src/openssl/x509v3.py | 2 + src/cryptography/hazmat/backends/openssl/x509.py | 107 +++++++++-------------- src/cryptography/x509/__init__.py | 10 +-- src/cryptography/x509/oid.py | 8 +- 4 files changed, 51 insertions(+), 76 deletions(-) (limited to 'src') diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py index 84e49640..51cac62b 100644 --- a/src/_cffi_src/openssl/x509v3.py +++ b/src/_cffi_src/openssl/x509v3.py @@ -290,6 +290,8 @@ DIST_POINT_NAME *DIST_POINT_NAME_new(void); void DIST_POINT_NAME_free(DIST_POINT_NAME *); int i2d_CRL_DIST_POINTS(Cryptography_STACK_OF_DIST_POINT *, unsigned char **); +GENERAL_NAMES *d2i_GENERAL_NAMES(GENERAL_NAMES **, const unsigned char **, + long); """ CUSTOMIZATIONS = """ diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 7f7be545..073dfb1e 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -19,7 +19,7 @@ from cryptography import utils, x509 from cryptography.exceptions import UnsupportedAlgorithm from cryptography.hazmat.primitives import hashes, serialization from cryptography.x509.oid import ( - CertificatePoliciesOID, ExtensionOID, RevokedExtensionOID + CRLExtensionOID, CertificatePoliciesOID, ExtensionOID ) @@ -175,11 +175,11 @@ def _decode_ocsp_no_check(backend, ext): class _X509ExtensionParser(object): - def __init__(self, ext_count, get_ext, handlers, supported_versions=None): + def __init__(self, ext_count, get_ext, handlers, unsupported_exts=None): self.ext_count = ext_count self.get_ext = get_ext self.handlers = handlers - self.supported_versions = supported_versions + self.unsupported_exts = unsupported_exts def parse(self, backend, x509_obj): extensions = [] @@ -190,13 +190,6 @@ class _X509ExtensionParser(object): crit = backend._lib.X509_EXTENSION_get_critical(ext) critical = crit == 1 oid = x509.ObjectIdentifier(_obj2txt(backend, ext.object)) - - # Filter out extensions we know are not supported by the backend - if (self.supported_versions and oid in self.supported_versions and - self.supported_versions[oid] > - backend._lib.OPENSSL_VERSION_NUMBER): - self.handlers.pop(oid, None) - if oid in seen_oids: raise x509.DuplicateExtension( "Duplicate {0} extension found".format(oid), oid @@ -210,15 +203,18 @@ class _X509ExtensionParser(object): .format(oid), oid ) else: - d2i = backend._lib.X509V3_EXT_d2i(ext) - if d2i == backend._ffi.NULL: - backend._consume_errors() - raise ValueError( - "The {0} extension is invalid and can't be " - "parsed".format(oid) - ) - - value = handler(backend, d2i) + if self.unsupported_exts and oid in self.unsupported_exts: + ext_data = ext + else: + ext_data = backend._lib.X509V3_EXT_d2i(ext) + if ext_data == backend._ffi.NULL: + backend._consume_errors() + raise ValueError( + "The {0} extension is invalid and can't be " + "parsed".format(oid) + ) + + value = handler(backend, ext_data) extensions.append(x509.Extension(oid, critical, value)) seen_oids.add(oid) @@ -687,8 +683,18 @@ def _decode_invalidity_date(backend, inv_date): return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ") -def _decode_cert_issuer(backend, issuer): - gns = backend._ffi.cast("GENERAL_NAMES *", issuer) +def _decode_cert_issuer(backend, ext): + data_ptr_ptr = backend._ffi.new("const unsigned char **") + data_ptr_ptr[0] = ext.value.data + gns = backend._lib.d2i_GENERAL_NAMES( + backend._ffi.NULL, data_ptr_ptr, ext.value.length + ) + if gns == backend._ffi.NULL: + backend._consume_errors() + raise ValueError( + "The {0} extension is corrupted and can't be parsed".format( + CRLExtensionOID.CERTIFICATE_ISSUER)) + gns = backend._ffi.gc(gns, backend._lib.GENERAL_NAMES_free) return x509.GeneralNames(_decode_general_names(backend, gns)) @@ -699,28 +705,16 @@ class _RevokedCertificate(object): self._backend = backend self._x509_revoked = x509_revoked - self._serial_number = None - self._revocation_date = None - self._extensions = None - @property def serial_number(self): - if self._serial_number: - return self._serial_number - asn1_int = self._x509_revoked.serialNumber self._backend.openssl_assert(asn1_int != self._backend._ffi.NULL) - self._serial_number = self._backend._asn1_integer_to_int(asn1_int) - return self._serial_number + return self._backend._asn1_integer_to_int(asn1_int) @property def revocation_date(self): - if self._revocation_date: - return self._revocation_date - - self._revocation_date = self._backend._parse_asn1_time( + return self._backend._parse_asn1_time( self._x509_revoked.revocationDate) - return self._revocation_date @property def extensions(self): @@ -765,11 +759,6 @@ class _CertificateRevocationList(object): self._backend = backend self._x509_crl = x509_crl - self._revoked = None - self._issuer = None - self._next_update = None - self._last_update = None - def __eq__(self, other): if not isinstance(other, x509.CertificateRevocationList): return NotImplemented @@ -803,38 +792,23 @@ class _CertificateRevocationList(object): @property def issuer(self): - if self._issuer: - return self._issuer - issuer = self._backend._lib.X509_CRL_get_issuer(self._x509_crl) self._backend.openssl_assert(issuer != self._backend._ffi.NULL) - self._issuer = _decode_x509_name(self._backend, issuer) - return self._issuer + return _decode_x509_name(self._backend, issuer) @property def next_update(self): - if self._next_update: - return self._next_update - nu = self._backend._lib.X509_CRL_get_nextUpdate(self._x509_crl) self._backend.openssl_assert(nu != self._backend._ffi.NULL) - self._next_update = self._backend._parse_asn1_time(nu) - return self._next_update + return self._backend._parse_asn1_time(nu) @property def last_update(self): - if self._last_update: - return self._last_update - lu = self._backend._lib.X509_CRL_get_lastUpdate(self._x509_crl) self._backend.openssl_assert(lu != self._backend._ffi.NULL) - self._last_update = self._backend._parse_asn1_time(lu) - return self._last_update + return self._backend._parse_asn1_time(lu) def _revoked_certificates(self): - if self._revoked: - return self._revoked - revoked = self._backend._lib.X509_CRL_get_REVOKED(self._x509_crl) self._backend.openssl_assert(revoked != self._backend._ffi.NULL) @@ -845,8 +819,7 @@ class _CertificateRevocationList(object): self._backend.openssl_assert(r != self._backend._ffi.NULL) revoked_list.append(_RevokedCertificate(self._backend, r)) - self._revoked = revoked_list - return self._revoked + return revoked_list def __iter__(self): return iter(self._revoked_certificates()) @@ -943,14 +916,14 @@ _EXTENSION_HANDLERS = { } _REVOKED_EXTENSION_HANDLERS = { - RevokedExtensionOID.CRL_REASON: _decode_crl_reason, - RevokedExtensionOID.INVALIDITY_DATE: _decode_invalidity_date, - RevokedExtensionOID.CERTIFICATE_ISSUER: _decode_cert_issuer, + CRLExtensionOID.CRL_REASON: _decode_crl_reason, + CRLExtensionOID.INVALIDITY_DATE: _decode_invalidity_date, + CRLExtensionOID.CERTIFICATE_ISSUER: _decode_cert_issuer, } -_REVOKED_SUPPORTED_VERSIONS = { - RevokedExtensionOID.CERTIFICATE_ISSUER: 0x10000000, -} +_REVOKED_UNSUPPORTED_EXTENSIONS = set([ + CRLExtensionOID.CERTIFICATE_ISSUER, +]) _CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser( ext_count=lambda backend, x: backend._lib.X509_get_ext_count(x), @@ -968,5 +941,5 @@ _REVOKED_CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser( ext_count=lambda backend, x: backend._lib.X509_REVOKED_get_ext_count(x), get_ext=lambda backend, x, i: backend._lib.X509_REVOKED_get_ext(x, i), handlers=_REVOKED_EXTENSION_HANDLERS, - supported_versions=_REVOKED_SUPPORTED_VERSIONS + unsupported_exts=_REVOKED_UNSUPPORTED_EXTENSIONS ) diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py index 6438da9c..70e1d3da 100644 --- a/src/cryptography/x509/__init__.py +++ b/src/cryptography/x509/__init__.py @@ -28,8 +28,8 @@ from cryptography.x509.general_name import ( ) from cryptography.x509.name import Name, NameAttribute from cryptography.x509.oid import ( - AuthorityInformationAccessOID, CertificatePoliciesOID, ExtendedKeyUsageOID, - ExtensionOID, NameOID, ObjectIdentifier, RevokedExtensionOID, + AuthorityInformationAccessOID, CRLExtensionOID, CertificatePoliciesOID, + ExtendedKeyUsageOID, ExtensionOID, NameOID, ObjectIdentifier, SignatureAlgorithmOID, _SIG_OIDS_TO_HASH ) @@ -95,9 +95,9 @@ OID_ANY_POLICY = CertificatePoliciesOID.ANY_POLICY OID_CPS_QUALIFIER = CertificatePoliciesOID.CPS_QUALIFIER OID_CPS_USER_NOTICE = CertificatePoliciesOID.CPS_USER_NOTICE -OID_CERTIFICATE_ISSUER = RevokedExtensionOID.CERTIFICATE_ISSUER -OID_CRL_REASON = RevokedExtensionOID.CRL_REASON -OID_INVALIDITY_DATE = RevokedExtensionOID.INVALIDITY_DATE +OID_CERTIFICATE_ISSUER = CRLExtensionOID.CERTIFICATE_ISSUER +OID_CRL_REASON = CRLExtensionOID.CRL_REASON +OID_INVALIDITY_DATE = CRLExtensionOID.INVALIDITY_DATE OID_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS OID_OCSP = AuthorityInformationAccessOID.OCSP diff --git a/src/cryptography/x509/oid.py b/src/cryptography/x509/oid.py index 667045af..ead40169 100644 --- a/src/cryptography/x509/oid.py +++ b/src/cryptography/x509/oid.py @@ -58,7 +58,7 @@ class ExtensionOID(object): OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5") -class RevokedExtensionOID(object): +class CRLExtensionOID(object): CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29") CRL_REASON = ObjectIdentifier("2.5.29.21") INVALIDITY_DATE = ObjectIdentifier("2.5.29.24") @@ -177,9 +177,9 @@ _OID_NAMES = { ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName", ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName", ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints", - RevokedExtensionOID.CRL_REASON: "cRLReason", - RevokedExtensionOID.INVALIDITY_DATE: "invalidityDate", - RevokedExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer", + CRLExtensionOID.CRL_REASON: "cRLReason", + CRLExtensionOID.INVALIDITY_DATE: "invalidityDate", + CRLExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer", ExtensionOID.NAME_CONSTRAINTS: "nameConstraints", ExtensionOID.CRL_DISTRIBUTION_POINTS: "cRLDistributionPoints", ExtensionOID.CERTIFICATE_POLICIES: "certificatePolicies", -- cgit v1.2.3 From 12121fc2fdf647cf205f0eec81f9fd0aee89b32b Mon Sep 17 00:00:00 2001 From: Erik Trauschke Date: Tue, 20 Oct 2015 08:24:33 -0700 Subject: add comments --- src/_cffi_src/openssl/x509v3.py | 2 - src/cryptography/hazmat/backends/openssl/x509.py | 61 ++++++++++++++---------- 2 files changed, 37 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py index 22406c40..8e163dc2 100644 --- a/src/_cffi_src/openssl/x509v3.py +++ b/src/_cffi_src/openssl/x509v3.py @@ -292,8 +292,6 @@ DIST_POINT_NAME *DIST_POINT_NAME_new(void); void DIST_POINT_NAME_free(DIST_POINT_NAME *); int i2d_CRL_DIST_POINTS(Cryptography_STACK_OF_DIST_POINT *, unsigned char **); -GENERAL_NAMES *d2i_GENERAL_NAMES(GENERAL_NAMES **, const unsigned char **, - long); """ CUSTOMIZATIONS = """ diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 7ca4850d..1c0f87fd 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -208,6 +208,9 @@ class _X509ExtensionParser(object): .format(oid), oid ) else: + # For extensions which are not supported by OpenSSL we pass the + # extension object directly to the parsing routine so it can + # be decoded manually. if self.unsupported_exts and oid in self.unsupported_exts: ext_data = ext else: @@ -678,34 +681,44 @@ def _decode_crl_reason(backend, enum): def _decode_invalidity_date(backend, inv_date): - generalized_time = backend._ffi.cast( - "ASN1_GENERALIZEDTIME *", inv_date - ) - generalized_time = backend._ffi.gc( - generalized_time, backend._lib.ASN1_GENERALIZEDTIME_free + generalized_time = backend._ffi.cast( + "ASN1_GENERALIZEDTIME *", inv_date + ) + generalized_time = backend._ffi.gc( + generalized_time, backend._lib.ASN1_GENERALIZEDTIME_free + ) + time = backend._ffi.string( + backend._lib.ASN1_STRING_data( + backend._ffi.cast("ASN1_STRING *", generalized_time) ) - time = backend._ffi.string( - backend._lib.ASN1_STRING_data( - backend._ffi.cast("ASN1_STRING *", generalized_time) - ) - ).decode("ascii") - return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ") + ).decode("ascii") + return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ") def _decode_cert_issuer(backend, ext): - data_ptr_ptr = backend._ffi.new("const unsigned char **") - data_ptr_ptr[0] = ext.value.data - gns = backend._lib.d2i_GENERAL_NAMES( - backend._ffi.NULL, data_ptr_ptr, ext.value.length - ) - if gns == backend._ffi.NULL: - backend._consume_errors() - raise ValueError( - "The {0} extension is corrupted and can't be parsed".format( - CRLExtensionOID.CERTIFICATE_ISSUER)) - - gns = backend._ffi.gc(gns, backend._lib.GENERAL_NAMES_free) - return x509.GeneralNames(_decode_general_names(backend, gns)) + """ + This handler decodes the CertificateIssuer entry extension directly + from the X509_EXTENSION object. This is necessary because this entry + extension is not directly supported by OpenSSL 0.9.8. + """ + + data_ptr_ptr = backend._ffi.new("const unsigned char **") + data_ptr_ptr[0] = ext.value.data + gns = backend._lib.d2i_GENERAL_NAMES( + backend._ffi.NULL, data_ptr_ptr, ext.value.length + ) + + # Check the result of d2i_GENERAL_NAMES() is valid. Usually this is covered + # in _X509ExtensionParser but since we are responsible for decoding this + # entry extension ourselves, we have to this here. + if gns == backend._ffi.NULL: + backend._consume_errors() + raise ValueError( + "The {0} extension is corrupted and can't be parsed".format( + CRLExtensionOID.CERTIFICATE_ISSUER)) + + gns = backend._ffi.gc(gns, backend._lib.GENERAL_NAMES_free) + return x509.GeneralNames(_decode_general_names(backend, gns)) @utils.register_interface(x509.RevokedCertificate) -- cgit v1.2.3 From 32bbfe0f27c284d6c268f2998e64f62083465faf Mon Sep 17 00:00:00 2001 From: Erik Trauschke Date: Wed, 21 Oct 2015 08:04:55 -0700 Subject: remove convenience functions for revoked extensions fix docs regarding CRL PEM format --- src/cryptography/hazmat/backends/openssl/x509.py | 30 ------------------------ 1 file changed, 30 deletions(-) (limited to 'src') diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 1c0f87fd..2790ec7d 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -744,36 +744,6 @@ class _RevokedCertificate(object): self._backend, self._x509_revoked ) - def get_reason(self): - """ - Returns the CRLReason extension if it exists. - """ - try: - return self.extensions.get_extension_for_oid( - x509.OID_CRL_REASON).value - except x509.ExtensionNotFound: - return None - - def get_invalidity_date(self): - """ - Returns the InvalidityDate extension if it exists. - """ - try: - return self.extensions.get_extension_for_oid( - x509.OID_INVALIDITY_DATE).value - except x509.ExtensionNotFound: - return None - - def get_certificate_issuer(self): - """ - Returns the CertificateIssuer extension if it exists. - """ - try: - return self.extensions.get_extension_for_oid( - x509.OID_CERTIFICATE_ISSUER).value - except x509.ExtensionNotFound: - return None - @utils.register_interface(x509.CertificateRevocationList) class _CertificateRevocationList(object): -- cgit v1.2.3