diff options
author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2017-09-11 09:16:34 +0800 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2017-09-10 21:16:34 -0400 |
commit | 5d66966032a1efbcbf093804a19951f399c2a6eb (patch) | |
tree | d769399c9c7f3e3a81fb199e89af8167bd28339f /src | |
parent | d4bde9ce6668bb019f9c9db4cd26280e6cf7fa21 (diff) | |
download | cryptography-5d66966032a1efbcbf093804a19951f399c2a6eb.tar.gz cryptography-5d66966032a1efbcbf093804a19951f399c2a6eb.tar.bz2 cryptography-5d66966032a1efbcbf093804a19951f399c2a6eb.zip |
[WIP] add support for the TLSFeature extension in x509 (#3899)
* add support for the TLSFeature extension in x509
This extension is used for OCSP Must-Staple.
* fix changelog link
* pep8
* refactor to support the sequence properly and add status_request_v2
* update some language
* add test vector, implement eq/ne/hash on TLSFeature
* address review comments
Diffstat (limited to 'src')
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 19 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/decode_asn1.py | 21 | ||||
-rw-r--r-- | src/cryptography/x509/__init__.py | 6 | ||||
-rw-r--r-- | src/cryptography/x509/extensions.py | 56 | ||||
-rw-r--r-- | src/cryptography/x509/oid.py | 2 |
5 files changed, 95 insertions, 9 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index d9a5bdf2..3a889344 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -23,6 +23,7 @@ from cryptography.hazmat.backends.interfaces import ( from cryptography.hazmat.backends.openssl import aead from cryptography.hazmat.backends.openssl.ciphers import _CipherContext from cryptography.hazmat.backends.openssl.cmac import _CMACContext +from cryptography.hazmat.backends.openssl.decode_asn1 import _Integers from cryptography.hazmat.backends.openssl.dh import ( _DHParameters, _DHPrivateKey, _DHPublicKey, _dh_params_dup @@ -943,18 +944,22 @@ class Backend(object): res = add_func(x509_obj, x509_extension, i) self.openssl_assert(res >= 1) + def _create_raw_x509_extension(self, extension, value): + obj = _txt2obj_gc(self, extension.oid.dotted_string) + return self._lib.X509_EXTENSION_create_by_OBJ( + self._ffi.NULL, obj, 1 if extension.critical else 0, value + ) + def _create_x509_extension(self, handlers, extension): if isinstance(extension.value, x509.UnrecognizedExtension): - obj = _txt2obj_gc(self, extension.oid.dotted_string) value = _encode_asn1_str_gc( self, extension.value.value, len(extension.value.value) ) - return self._lib.X509_EXTENSION_create_by_OBJ( - self._ffi.NULL, - obj, - 1 if extension.critical else 0, - value - ) + return self._create_raw_x509_extension(extension, value) + elif isinstance(extension.value, x509.TLSFeature): + asn1 = _Integers([x.value for x in extension.value]).dump() + value = _encode_asn1_str_gc(self, asn1, len(asn1)) + return self._create_raw_x509_extension(extension, value) else: try: encode = handlers[extension.oid] diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py index a66f65f6..9c2d763e 100644 --- a/src/cryptography/hazmat/backends/openssl/decode_asn1.py +++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py @@ -9,6 +9,8 @@ import ipaddress from email.utils import parseaddr +from asn1crypto.core import Integer, SequenceOf + import idna import six @@ -16,11 +18,16 @@ import six from six.moves import urllib_parse from cryptography import x509 +from cryptography.x509.extensions import _TLS_FEATURE_TYPE_TO_ENUM from cryptography.x509.oid import ( CRLEntryExtensionOID, CertificatePoliciesOID, ExtensionOID ) +class _Integers(SequenceOf): + _child_spec = Integer + + def _obj2txt(backend, obj): # Set to 80 on the recommendation of # https://www.openssl.org/docs/crypto/OBJ_nid2ln.html#return_values @@ -210,6 +217,20 @@ class _X509ExtensionParser(object): raise x509.DuplicateExtension( "Duplicate {0} extension found".format(oid), oid ) + + # This OID is only supported in OpenSSL 1.1.0+ but we want + # to support it in all versions of OpenSSL so we decode it + # ourselves. + if oid == ExtensionOID.TLS_FEATURE: + data = backend._lib.X509_EXTENSION_get_data(ext) + parsed = _Integers.load(_asn1_string_to_bytes(backend, data)) + value = x509.TLSFeature( + [_TLS_FEATURE_TYPE_TO_ENUM[x.native] for x in parsed] + ) + extensions.append(x509.Extension(oid, critical, value)) + seen_oids.add(oid) + continue + try: handler = self.handlers[oid] except KeyError: diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py index 3b747302..176ed8ca 100644 --- a/src/cryptography/x509/__init__.py +++ b/src/cryptography/x509/__init__.py @@ -23,8 +23,8 @@ from cryptography.x509.extensions import ( InhibitAnyPolicy, InvalidityDate, IssuerAlternativeName, KeyUsage, NameConstraints, NoticeReference, OCSPNoCheck, PolicyConstraints, PolicyInformation, PrecertificateSignedCertificateTimestamps, ReasonFlags, - SubjectAlternativeName, SubjectKeyIdentifier, UnrecognizedExtension, - UserNotice + SubjectAlternativeName, SubjectKeyIdentifier, TLSFeature, TLSFeatureType, + UnrecognizedExtension, UserNotice ) from cryptography.x509.general_name import ( DNSName, DirectoryName, GeneralName, IPAddress, OtherName, RFC822Name, @@ -130,6 +130,8 @@ __all__ = [ "Extensions", "Extension", "ExtendedKeyUsage", + "TLSFeature", + "TLSFeatureType", "OCSPNoCheck", "BasicConstraints", "CRLNumber", diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py index d90465b2..9eff3431 100644 --- a/src/cryptography/x509/extensions.py +++ b/src/cryptography/x509/extensions.py @@ -733,6 +733,62 @@ class OCSPNoCheck(object): @utils.register_interface(ExtensionType) +class TLSFeature(object): + oid = ExtensionOID.TLS_FEATURE + + def __init__(self, features): + features = list(features) + if ( + not all(isinstance(x, TLSFeatureType) for x in features) or + len(features) == 0 + ): + raise TypeError( + "features must be a list of elements from the TLSFeatureType " + "enum" + ) + + self._features = features + + def __iter__(self): + return iter(self._features) + + def __len__(self): + return len(self._features) + + def __repr__(self): + return "<TLSFeature(features={0._features})>".format(self) + + def __eq__(self, other): + if not isinstance(other, TLSFeature): + return NotImplemented + + return self._features == other._features + + def __getitem__(self, idx): + return self._features[idx] + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash(tuple(self._features)) + + +class TLSFeatureType(Enum): + # status_request is defined in RFC 6066 and is used for what is commonly + # called OCSP Must-Staple when present in the TLS Feature extension in an + # X.509 certificate. + status_request = 5 + # status_request_v2 is defined in RFC 6961 and allows multiple OCSP + # responses to be provided. It is not currently in use by clients or + # servers. + status_request_v2 = 17 + + +_TLS_FEATURE_TYPE_TO_ENUM = dict((x.value, x) for x in TLSFeatureType) + + +@utils.register_interface(ExtensionType) class InhibitAnyPolicy(object): oid = ExtensionOID.INHIBIT_ANY_POLICY diff --git a/src/cryptography/x509/oid.py b/src/cryptography/x509/oid.py index 4a6fa3c1..7f8c9031 100644 --- a/src/cryptography/x509/oid.py +++ b/src/cryptography/x509/oid.py @@ -85,6 +85,7 @@ class ExtensionOID(object): AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1") SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11") OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5") + TLS_FEATURE = ObjectIdentifier("1.3.6.1.5.5.7.1.24") CRL_NUMBER = ObjectIdentifier("2.5.29.20") PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS = ( ObjectIdentifier("1.3.6.1.4.1.11129.2.4.2") @@ -255,6 +256,7 @@ _OID_NAMES = { ExtensionOID.SUBJECT_INFORMATION_ACCESS: "subjectInfoAccess", ExtensionOID.OCSP_NO_CHECK: "OCSPNoCheck", ExtensionOID.CRL_NUMBER: "cRLNumber", + ExtensionOID.TLS_FEATURE: "TLSFeature", AuthorityInformationAccessOID.OCSP: "OCSP", AuthorityInformationAccessOID.CA_ISSUERS: "caIssuers", CertificatePoliciesOID.CPS_QUALIFIER: "id-qt-cps", |