diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 106 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/openssl/ssl.py | 6 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/openssl/x509.py | 5 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/openssl/x509v3.py | 17 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/kdf/hkdf.py | 4 | ||||
-rw-r--r-- | src/cryptography/x509.py | 96 |
6 files changed, 225 insertions, 9 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 7f633c76..42ca138d 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -15,6 +15,7 @@ from __future__ import absolute_import, division, print_function import datetime import ipaddress +from email.utils import parseaddr import idna @@ -107,6 +108,27 @@ def _build_general_name(backend, gn): return x509.DirectoryName( _build_x509_name(backend, gn.d.directoryName) ) + elif gn.type == backend._lib.GEN_EMAIL: + data = backend._ffi.buffer( + gn.d.rfc822Name.data, gn.d.rfc822Name.length + )[:].decode("ascii") + name, address = parseaddr(data) + parts = address.split(u"@") + if name or len(parts) > 2 or not address: + # parseaddr has found a name (e.g. Name <email>) or the split + # has found more than 2 parts (which means more than one @ sign) + # or the entire value is an empty string. + raise ValueError("Invalid rfc822name value") + elif len(parts) == 1: + # Single label email name. This is valid for local delivery. No + # IDNA decoding can be done since there is no domain component. + return x509.RFC822Name(address) + else: + # A normal email of the form user@domain.com. Let's attempt to + # decode the domain component and return the entire address. + return x509.RFC822Name( + parts[0] + u"@" + idna.decode(parts[1]) + ) else: # otherName, x400Address or ediPartyName raise x509.UnsupportedGeneralNameType( @@ -245,6 +267,12 @@ class _Certificate(object): value = self._build_key_usage(ext) elif oid == x509.OID_SUBJECT_ALTERNATIVE_NAME: value = self._build_subject_alt_name(ext) + elif oid == x509.OID_EXTENDED_KEY_USAGE: + value = self._build_extended_key_usage(ext) + elif oid == x509.OID_AUTHORITY_KEY_IDENTIFIER: + value = self._build_authority_key_identifier(ext) + elif oid == x509.OID_AUTHORITY_INFORMATION_ACCESS: + value = self._build_authority_information_access(ext) elif critical: raise x509.UnsupportedExtension( "{0} is not currently supported".format(oid), oid @@ -297,6 +325,66 @@ class _Certificate(object): self._backend._ffi.buffer(asn1_string.data, asn1_string.length)[:] ) + 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 + ) + 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 = [] + + num = self._backend._lib.sk_GENERAL_NAME_num(akid.issuer) + for i in range(num): + gn = self._backend._lib.sk_GENERAL_NAME_value(akid.issuer, i) + assert gn != self._backend._ffi.NULL + value = _build_general_name(self._backend, gn) + + authority_cert_issuer.append(value) + + if akid.serial != self._backend._ffi.NULL: + bn = self._backend._lib.ASN1_INTEGER_to_BN( + akid.serial, self._backend._ffi.NULL + ) + assert bn != self._backend._ffi.NULL + bn = self._backend._ffi.gc(bn, self._backend._lib.BN_free) + authority_cert_serial_number = self._backend._bn_to_int(bn) + + return x509.AuthorityKeyIdentifier( + key_identifier, authority_cert_issuer, authority_cert_serial_number + ) + + 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 @@ -344,6 +432,24 @@ class _Certificate(object): return x509.SubjectAlternativeName(general_names) + 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) + + return x509.ExtendedKeyUsage(ekus) + @utils.register_interface(x509.CertificateSigningRequest) class _CertificateSigningRequest(object): diff --git a/src/cryptography/hazmat/bindings/openssl/ssl.py b/src/cryptography/hazmat/bindings/openssl/ssl.py index b182180f..6493e867 100644 --- a/src/cryptography/hazmat/bindings/openssl/ssl.py +++ b/src/cryptography/hazmat/bindings/openssl/ssl.py @@ -211,6 +211,9 @@ int SSL_CTX_use_certificate_chain_file(SSL_CTX *, const char *); int SSL_CTX_use_PrivateKey(SSL_CTX *, EVP_PKEY *); int SSL_CTX_use_PrivateKey_file(SSL_CTX *, const char *, int); int SSL_CTX_check_private_key(const SSL_CTX *); +void SSL_CTX_set_cert_verify_callback(SSL_CTX *, + int (*)(X509_STORE_CTX *,void *), + void *); void SSL_CTX_set_cert_store(SSL_CTX *, X509_STORE *); X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *); @@ -322,6 +325,7 @@ void SSL_CTX_set_tlsext_servername_callback( is fraught with peril thanks to OS distributions we check some constants to determine if they are supported or not */ long SSL_set_tlsext_status_ocsp_resp(SSL *, unsigned char *, int); +long SSL_get_tlsext_status_ocsp_resp(SSL *, const unsigned char **); long SSL_CTX_set_tlsext_status_cb(SSL_CTX *, int(*)(SSL *, void *)); long SSL_session_reused(SSL *); @@ -431,6 +435,7 @@ static const long Cryptography_HAS_STATUS_REQ_OCSP_RESP = 1; #else static const long Cryptography_HAS_STATUS_REQ_OCSP_RESP = 0; long (*SSL_set_tlsext_status_ocsp_resp)(SSL *, unsigned char *, int) = NULL; +long (*SSL_get_tlsext_status_ocsp_resp)(SSL *, const unsigned char **) = NULL; #endif #ifdef SSL_MODE_RELEASE_BUFFERS @@ -617,6 +622,7 @@ CONDITIONAL_NAMES = { "Cryptography_HAS_STATUS_REQ_OCSP_RESP": [ "SSL_set_tlsext_status_ocsp_resp", + "SSL_get_tlsext_status_ocsp_resp", ], "Cryptography_HAS_RELEASE_BUFFERS": [ diff --git a/src/cryptography/hazmat/bindings/openssl/x509.py b/src/cryptography/hazmat/bindings/openssl/x509.py index a1fb7ffb..fa6a16b3 100644 --- a/src/cryptography/hazmat/bindings/openssl/x509.py +++ b/src/cryptography/hazmat/bindings/openssl/x509.py @@ -303,6 +303,11 @@ EC_KEY *d2i_EC_PUBKEY_bio(BIO *, EC_KEY **); int i2d_EC_PUBKEY_bio(BIO *, EC_KEY *); EC_KEY *d2i_ECPrivateKey_bio(BIO *, EC_KEY **); int i2d_ECPrivateKey_bio(BIO *, EC_KEY *); + +// declared in safestack +int sk_ASN1_OBJECT_num(Cryptography_STACK_OF_ASN1_OBJECT *); +ASN1_OBJECT *sk_ASN1_OBJECT_value(Cryptography_STACK_OF_ASN1_OBJECT *, int); +void sk_ASN1_OBJECT_free(Cryptography_STACK_OF_ASN1_OBJECT *); """ CUSTOMIZATIONS = """ diff --git a/src/cryptography/hazmat/bindings/openssl/x509v3.py b/src/cryptography/hazmat/bindings/openssl/x509v3.py index 28dd7f32..c2b6860f 100644 --- a/src/cryptography/hazmat/bindings/openssl/x509v3.py +++ b/src/cryptography/hazmat/bindings/openssl/x509v3.py @@ -19,9 +19,12 @@ typedef LHASH_OF(CONF_VALUE) Cryptography_LHASH_OF_CONF_VALUE; #else typedef LHASH Cryptography_LHASH_OF_CONF_VALUE; #endif +typedef STACK_OF(ACCESS_DESCRIPTION) Cryptography_STACK_OF_ACCESS_DESCRIPTION; """ TYPES = """ +typedef ... Cryptography_STACK_OF_ACCESS_DESCRIPTION; + typedef struct { X509 *issuer_cert; X509 *subject_cert; @@ -92,6 +95,11 @@ typedef struct { ASN1_INTEGER *serial; } AUTHORITY_KEYID; +typedef struct { + ASN1_OBJECT *method; + GENERAL_NAME *location; +} ACCESS_DESCRIPTION; + typedef ... Cryptography_LHASH_OF_CONF_VALUE; """ @@ -109,11 +117,20 @@ MACROS = """ /* This is a macro defined by a call to DECLARE_ASN1_FUNCTIONS in the x509v3.h header. */ void BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *); +/* This is a macro defined by a call to DECLARE_ASN1_FUNCTIONS in the + x509v3.h header. */ +void AUTHORITY_KEYID_free(AUTHORITY_KEYID *); void *X509V3_set_ctx_nodb(X509V3_CTX *); int sk_GENERAL_NAME_num(struct stack_st_GENERAL_NAME *); int sk_GENERAL_NAME_push(struct stack_st_GENERAL_NAME *, GENERAL_NAME *); GENERAL_NAME *sk_GENERAL_NAME_value(struct stack_st_GENERAL_NAME *, int); +int sk_ACCESS_DESCRIPTION_num(Cryptography_STACK_OF_ACCESS_DESCRIPTION *); +ACCESS_DESCRIPTION *sk_ACCESS_DESCRIPTION_value( + Cryptography_STACK_OF_ACCESS_DESCRIPTION *, int +); +void sk_ACCESS_DESCRIPTION_free(Cryptography_STACK_OF_ACCESS_DESCRIPTION *); + X509_EXTENSION *X509V3_EXT_conf_nid(Cryptography_LHASH_OF_CONF_VALUE *, X509V3_CTX *, int, char *); diff --git a/src/cryptography/hazmat/primitives/kdf/hkdf.py b/src/cryptography/hazmat/primitives/kdf/hkdf.py index 65b7091a..f738bbdc 100644 --- a/src/cryptography/hazmat/primitives/kdf/hkdf.py +++ b/src/cryptography/hazmat/primitives/kdf/hkdf.py @@ -26,7 +26,7 @@ class HKDF(object): self._algorithm = algorithm - if not isinstance(salt, bytes) and salt is not None: + if not (salt is None or isinstance(salt, bytes)): raise TypeError("salt must be bytes.") if salt is None: @@ -77,7 +77,7 @@ class HKDFExpand(object): self._length = length - if not isinstance(info, bytes) and info is not None: + if not (info is None or isinstance(info, bytes)): raise TypeError("info must be bytes.") if info is None: diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index b22ac8be..0d87cd51 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -67,6 +67,8 @@ _OID_NAMES = { "1.3.6.1.5.5.7.1.1": "authorityInfoAccess", "1.3.6.1.5.5.7.1.11": "subjectInfoAccess", "1.3.6.1.5.5.7.48.1.5": "OCSPNoCheck", + "1.3.6.1.5.5.7.48.1": "OCSP", + "1.3.6.1.5.5.7.48.2": "caIssuers", } @@ -277,11 +279,10 @@ class Extension(object): class ExtendedKeyUsage(object): def __init__(self, usages): - for oid in usages: - if not isinstance(oid, ObjectIdentifier): - raise TypeError( - "Every item in the usages list must be an ObjectIdentifier" - ) + if not all(isinstance(x, ObjectIdentifier) for x in usages): + raise TypeError( + "Every item in the usages list must be an ObjectIdentifier" + ) self._usages = usages @@ -294,6 +295,15 @@ class ExtendedKeyUsage(object): def __repr__(self): return "<ExtendedKeyUsage({0})>".format(self._usages) + def __eq__(self, other): + if not isinstance(other, ExtendedKeyUsage): + return NotImplemented + + return self._usages == other._usages + + def __ne__(self, other): + return not self == other + class BasicConstraints(object): def __init__(self, ca, path_length): @@ -386,6 +396,70 @@ class KeyUsage(object): self, encipher_only, decipher_only) +class AuthorityInformationAccess(object): + def __init__(self, descriptions): + if not all(isinstance(x, AccessDescription) for x in descriptions): + raise TypeError( + "Every item in the descriptions list must be an " + "AccessDescription" + ) + + self._descriptions = descriptions + + def __iter__(self): + return iter(self._descriptions) + + def __len__(self): + return len(self._descriptions) + + def __repr__(self): + return "<AuthorityInformationAccess({0})>".format(self._descriptions) + + def __eq__(self, other): + if not isinstance(other, AuthorityInformationAccess): + return NotImplemented + + return self._descriptions == other._descriptions + + def __ne__(self, other): + return not self == other + + +class AccessDescription(object): + def __init__(self, access_method, access_location): + if not (access_method == OID_OCSP or access_method == OID_CA_ISSUERS): + raise ValueError( + "access_method must be OID_OCSP or OID_CA_ISSUERS" + ) + + if not isinstance(access_location, GeneralName): + raise TypeError("access_location must be a GeneralName") + + self._access_method = access_method + self._access_location = access_location + + def __repr__(self): + return ( + "<AccessDescription(access_method={0.access_method}, access_locati" + "on={0.access_location})>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, AccessDescription): + return NotImplemented + + return ( + self.access_method == other.access_method and + self.access_location == other.access_location + ) + + def __ne__(self, other): + return not self == other + + access_method = utils.read_only_property("_access_method") + access_location = utils.read_only_property("_access_location") + + class SubjectKeyIdentifier(object): def __init__(self, digest): self._digest = digest @@ -592,8 +666,13 @@ class AuthorityKeyIdentifier(object): "must both be present or both None" ) - if not isinstance(authority_cert_issuer, Name): - raise TypeError("authority_cert_issuer must be a Name") + if not all( + isinstance(x, GeneralName) for x in authority_cert_issuer + ): + raise TypeError( + "authority_cert_issuer must be a list of GeneralName " + "objects" + ) if not isinstance(authority_cert_serial_number, six.integer_types): raise TypeError( @@ -672,6 +751,9 @@ OID_EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4") OID_TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8") OID_OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9") +OID_CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2") +OID_OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1") + @six.add_metaclass(abc.ABCMeta) class Certificate(object): |