diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/_cffi_src/openssl/ssl.py | 1 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 56 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 54 | ||||
-rw-r--r-- | src/cryptography/x509.py | 18 |
4 files changed, 105 insertions, 24 deletions
diff --git a/src/_cffi_src/openssl/ssl.py b/src/_cffi_src/openssl/ssl.py index fa0aefc8..5841ee2f 100644 --- a/src/_cffi_src/openssl/ssl.py +++ b/src/_cffi_src/openssl/ssl.py @@ -182,6 +182,7 @@ int SSL_get_shutdown(const SSL *); int SSL_pending(const SSL *); int SSL_write(SSL *, const void *, int); int SSL_read(SSL *, void *, int); +int SSL_peek(SSL *, void *, int); X509 *SSL_get_peer_certificate(const SSL *); int SSL_get_ex_data_X509_STORE_CTX_idx(void); diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 73a58637..d6493778 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -8,6 +8,8 @@ import collections import itertools from contextlib import contextmanager +import idna + import six from cryptography import utils, x509 @@ -136,6 +138,56 @@ def _encode_basic_constraints(backend, basic_constraints): return pp, r +def _encode_subject_alt_name(backend, san): + general_names = backend._lib.GENERAL_NAMES_new() + assert general_names != backend._ffi.NULL + general_names = backend._ffi.gc( + general_names, backend._lib.GENERAL_NAMES_free + ) + + for alt_name in san: + if isinstance(alt_name, x509.DNSName): + gn = backend._lib.GENERAL_NAME_new() + assert gn != backend._ffi.NULL + gn.type = backend._lib.GEN_DNS + + ia5 = backend._lib.ASN1_IA5STRING_new() + assert ia5 != backend._ffi.NULL + + if alt_name.value.startswith(u"*."): + value = b"*." + idna.encode(alt_name.value[2:]) + else: + value = idna.encode(alt_name.value) + + res = backend._lib.ASN1_STRING_set(ia5, value, len(value)) + assert res == 1 + gn.d.dNSName = ia5 + elif isinstance(alt_name, x509.RegisteredID): + gn = backend._lib.GENERAL_NAME_new() + assert gn != backend._ffi.NULL + gn.type = backend._lib.GEN_RID + obj = backend._lib.OBJ_txt2obj( + alt_name.value.dotted_string.encode('ascii'), 1 + ) + assert obj != backend._ffi.NULL + gn.d.registeredID = obj + else: + raise NotImplementedError( + "Only DNSName and RegisteredID supported right now" + ) + + res = backend._lib.sk_GENERAL_NAME_push(general_names, gn) + assert res != 0 + + pp = backend._ffi.new("unsigned char **") + r = backend._lib.i2d_GENERAL_NAMES(general_names, pp) + assert r > 0 + pp = backend._ffi.gc( + pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0]) + ) + return pp, r + + @utils.register_interface(CipherBackend) @utils.register_interface(CMACBackend) @utils.register_interface(DERSerializationBackend) @@ -841,12 +893,14 @@ class Backend(object): self._lib.sk_X509_EXTENSION_free, ) for extension in builder._extensions: - obj = _txt2obj(self, extension.oid.dotted_string) if isinstance(extension.value, x509.BasicConstraints): pp, r = _encode_basic_constraints(self, extension.value) + elif isinstance(extension.value, x509.SubjectAlternativeName): + pp, r = _encode_subject_alt_name(self, extension.value) else: raise NotImplementedError('Extension not yet supported.') + obj = _txt2obj(self, extension.oid.dotted_string) extension = self._lib.X509_EXTENSION_create_by_OBJ( self._ffi.NULL, obj, diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index e720bfdb..399e6a6e 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -36,6 +36,14 @@ def _asn1_integer_to_int(backend, asn1_int): return backend._bn_to_int(bn) +def _asn1_string_to_bytes(backend, asn1_string): + return backend._ffi.buffer(asn1_string.data, asn1_string.length)[:] + + +def _asn1_string_to_ascii(backend, asn1_string): + return _asn1_string_to_bytes(backend, asn1_string).decode("ascii") + + def _asn1_string_to_utf8(backend, asn1_string): buf = backend._ffi.new("unsigned char **") res = backend._lib.ASN1_STRING_to_UTF8(buf, asn1_string) @@ -92,7 +100,7 @@ def _decode_general_names(backend, gns): 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)[:] + data = _asn1_string_to_bytes(backend, gn.d.dNSName) if data.startswith(b"*."): # This is a wildcard name. We need to remove the leading wildcard, # IDNA decode, then re-add the wildcard. Wildcard characters should @@ -109,10 +117,7 @@ def _decode_general_name(backend, gn): return x509.DNSName(decoded) elif gn.type == backend._lib.GEN_URI: - data = backend._ffi.buffer( - gn.d.uniformResourceIdentifier.data, - gn.d.uniformResourceIdentifier.length - )[:].decode("ascii") + data = _asn1_string_to_ascii(backend, gn.d.uniformResourceIdentifier) parsed = urllib_parse.urlparse(data) hostname = idna.decode(parsed.hostname) if parsed.port: @@ -138,9 +143,7 @@ def _decode_general_name(backend, gn): elif gn.type == backend._lib.GEN_IPADD: return x509.IPAddress( ipaddress.ip_address( - backend._ffi.buffer( - gn.d.iPAddress.data, gn.d.iPAddress.length - )[:] + _asn1_string_to_bytes(backend, gn.d.iPAddress) ) ) elif gn.type == backend._lib.GEN_DIRNAME: @@ -148,9 +151,7 @@ def _decode_general_name(backend, gn): _decode_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") + data = _asn1_string_to_ascii(backend, gn.d.rfc822Name) name, address = parseaddr(data) parts = address.split(u"@") if name or len(parts) > 2 or not address: @@ -240,15 +241,12 @@ class _Certificate(object): def __ne__(self, other): return not self == other + def __hash__(self): + return hash(self.public_bytes(serialization.Encoding.DER)) + def fingerprint(self, algorithm): h = hashes.Hash(algorithm, self._backend) - bio = self._backend._create_mem_bio() - res = self._backend._lib.i2d_X509_bio( - bio, self._x509 - ) - assert res == 1 - der = self._backend._read_mem_bio(bio) - h.update(der) + h.update(self.public_bytes(serialization.Encoding.DER)) return h.finalize() @property @@ -295,11 +293,10 @@ class _Certificate(object): 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") + time = _asn1_string_to_ascii( + self._backend, + self._backend._ffi.cast("ASN1_STRING *", generalized_time) + ) return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ") @property @@ -716,6 +713,17 @@ class _CertificateSigningRequest(object): self._backend = backend self._x509_req = x509_req + def __eq__(self, other): + if not isinstance(other, _CertificateSigningRequest): + return NotImplemented + + self_bytes = self.public_bytes(serialization.Encoding.DER) + other_bytes = other.public_bytes(serialization.Encoding.DER) + return self_bytes == other_bytes + + def __ne__(self, other): + return not self == other + def public_key(self): pkey = self._backend._lib.X509_REQ_get_pubkey(self._x509_req) assert pkey != self._backend._ffi.NULL diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index c9d0c260..d9d6db4a 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -1358,6 +1358,12 @@ class Certificate(object): """ @abc.abstractmethod + def __hash__(self): + """ + Computes a hash. + """ + + @abc.abstractmethod def public_bytes(self, encoding): """ Serializes the certificate to PEM or DER format. @@ -1426,6 +1432,18 @@ class CertificateRevocationList(object): @six.add_metaclass(abc.ABCMeta) class CertificateSigningRequest(object): @abc.abstractmethod + def __eq__(self, other): + """ + Checks equality. + """ + + @abc.abstractmethod + def __ne__(self, other): + """ + Checks not equal. + """ + + @abc.abstractmethod def public_key(self): """ Returns the public key |