diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/_cffi_src/openssl/x509name.py | 10 | ||||
-rw-r--r-- | src/_cffi_src/openssl/x509v3.py | 5 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/interfaces.py | 4 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/multibackend.py | 4 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 92 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 6 | ||||
-rw-r--r-- | src/cryptography/x509.py | 75 |
7 files changed, 176 insertions, 20 deletions
diff --git a/src/_cffi_src/openssl/x509name.py b/src/_cffi_src/openssl/x509name.py index 2d87db4c..7b833d61 100644 --- a/src/_cffi_src/openssl/x509name.py +++ b/src/_cffi_src/openssl/x509name.py @@ -15,10 +15,13 @@ typedef STACK_OF(X509_NAME_ENTRY) Cryptography_STACK_OF_X509_NAME_ENTRY; """ TYPES = """ -typedef ... X509_NAME; +typedef ... Cryptography_STACK_OF_X509_NAME_ENTRY; +typedef struct { + Cryptography_STACK_OF_X509_NAME_ENTRY *entries; + ...; +} X509_NAME; typedef ... X509_NAME_ENTRY; typedef ... Cryptography_STACK_OF_X509_NAME; -typedef ... Cryptography_STACK_OF_X509_NAME_ENTRY; """ FUNCTIONS = """ @@ -55,6 +58,9 @@ void sk_X509_NAME_free(Cryptography_STACK_OF_X509_NAME *); int sk_X509_NAME_ENTRY_num(Cryptography_STACK_OF_X509_NAME_ENTRY *); X509_NAME_ENTRY *sk_X509_NAME_ENTRY_value( Cryptography_STACK_OF_X509_NAME_ENTRY *, int); +Cryptography_STACK_OF_X509_NAME_ENTRY *sk_X509_NAME_ENTRY_dup( + Cryptography_STACK_OF_X509_NAME_ENTRY * +); """ CUSTOMIZATIONS = """ diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py index f6a18903..6e35dacc 100644 --- a/src/_cffi_src/openssl/x509v3.py +++ b/src/_cffi_src/openssl/x509v3.py @@ -189,7 +189,9 @@ BASIC_CONSTRAINTS *BASIC_CONSTRAINTS_new(void); void BASIC_CONSTRAINTS_free(BASIC_CONSTRAINTS *); /* This is a macro defined by a call to DECLARE_ASN1_FUNCTIONS in the x509v3.h header. */ +AUTHORITY_KEYID *AUTHORITY_KEYID_new(void); void AUTHORITY_KEYID_free(AUTHORITY_KEYID *); +int i2d_AUTHORITY_KEYID(AUTHORITY_KEYID *, unsigned char **); NAME_CONSTRAINTS *NAME_CONSTRAINTS_new(void); void NAME_CONSTRAINTS_free(NAME_CONSTRAINTS *); @@ -283,6 +285,9 @@ X509_EXTENSION *X509V3_EXT_i2d(int, int, void *); DIST_POINT *DIST_POINT_new(void); void DIST_POINT_free(DIST_POINT *); +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 **); """ diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py index 49ccda18..a43621a7 100644 --- a/src/cryptography/hazmat/backends/interfaces.py +++ b/src/cryptography/hazmat/backends/interfaces.py @@ -281,9 +281,9 @@ class X509Backend(object): """ @abc.abstractmethod - def sign_x509_certificate(self, builder, private_key, algorithm): + def create_x509_certificate(self, builder, private_key, algorithm): """ - Sign an X.509 Certificate from a CertificateBuilder object. + Create and sign an X.509 certificate from a CertificateBuilder object. """ diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py index 8008989e..9db32aa5 100644 --- a/src/cryptography/hazmat/backends/multibackend.py +++ b/src/cryptography/hazmat/backends/multibackend.py @@ -352,9 +352,9 @@ class MultiBackend(object): _Reasons.UNSUPPORTED_X509 ) - def sign_x509_certificate(self, builder, private_key, algorithm): + def create_x509_certificate(self, builder, private_key, algorithm): for b in self._filtered_backends(X509Backend): - return b.sign_x509_certificate(builder, private_key, algorithm) + return b.create_x509_certificate(builder, private_key, algorithm) raise UnsupportedAlgorithm( "This backend does not support X.509.", diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index f9da9ea7..2752d98d 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -38,7 +38,8 @@ from cryptography.hazmat.backends.openssl.rsa import ( _RSAPrivateKey, _RSAPublicKey ) from cryptography.hazmat.backends.openssl.x509 import ( - _Certificate, _CertificateSigningRequest + _Certificate, _CertificateSigningRequest, _DISTPOINT_TYPE_FULLNAME, + _DISTPOINT_TYPE_RELATIVENAME ) from cryptography.hazmat.bindings.openssl.binding import Binding from cryptography.hazmat.primitives import hashes, serialization @@ -228,18 +229,22 @@ def _encode_authority_information_access(backend, authority_info_access): return pp, r -def _encode_subject_alt_name(backend, san): +def _encode_general_names(backend, names): 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: - gn = _encode_general_name(backend, alt_name) + for name in names: + gn = _encode_general_name(backend, name) res = backend._lib.sk_GENERAL_NAME_push(general_names, gn) assert res != 0 + return general_names + + +def _encode_subject_alt_name(backend, san): + general_names = _encode_general_names(backend, san) + general_names = backend._ffi.gc( + general_names, backend._lib.GENERAL_NAMES_free + ) pp = backend._ffi.new("unsigned char **") r = backend._lib.i2d_GENERAL_NAMES(general_names, pp) assert r > 0 @@ -355,6 +360,67 @@ def _encode_extended_key_usage(backend, extended_key_usage): return pp, r +_CRLREASONFLAGS = { + x509.ReasonFlags.key_compromise: 1, + x509.ReasonFlags.ca_compromise: 2, + x509.ReasonFlags.affiliation_changed: 3, + x509.ReasonFlags.superseded: 4, + x509.ReasonFlags.cessation_of_operation: 5, + x509.ReasonFlags.certificate_hold: 6, + x509.ReasonFlags.privilege_withdrawn: 7, + x509.ReasonFlags.aa_compromise: 8, +} + + +def _encode_crl_distribution_points(backend, crl_distribution_points): + cdp = backend._lib.sk_DIST_POINT_new_null() + cdp = backend._ffi.gc(cdp, backend._lib.sk_DIST_POINT_free) + for point in crl_distribution_points: + dp = backend._lib.DIST_POINT_new() + assert dp != backend._ffi.NULL + + if point.reasons: + bitmask = backend._lib.ASN1_BIT_STRING_new() + assert bitmask != backend._ffi.NULL + dp.reasons = bitmask + for reason in point.reasons: + res = backend._lib.ASN1_BIT_STRING_set_bit( + bitmask, _CRLREASONFLAGS[reason], 1 + ) + assert res == 1 + + if point.full_name: + dpn = backend._lib.DIST_POINT_NAME_new() + assert dpn != backend._ffi.NULL + dpn.type = _DISTPOINT_TYPE_FULLNAME + dpn.name.fullname = _encode_general_names(backend, point.full_name) + dp.distpoint = dpn + + if point.relative_name: + dpn = backend._lib.DIST_POINT_NAME_new() + assert dpn != backend._ffi.NULL + dpn.type = _DISTPOINT_TYPE_RELATIVENAME + name = _encode_name_gc(backend, point.relative_name) + relativename = backend._lib.sk_X509_NAME_ENTRY_dup(name.entries) + assert relativename != backend._ffi.NULL + dpn.name.relativename = relativename + dp.distpoint = dpn + + if point.crl_issuer: + dp.CRLissuer = _encode_general_names(backend, point.crl_issuer) + + res = backend._lib.sk_DIST_POINT_push(cdp, dp) + assert res >= 1 + + pp = backend._ffi.new('unsigned char **') + r = backend._lib.i2d_CRL_DIST_POINTS(cdp, 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) @@ -1096,7 +1162,7 @@ class Backend(object): return _CertificateSigningRequest(self, x509_req) - def sign_x509_certificate(self, builder, private_key, algorithm): + def create_x509_certificate(self, builder, private_key, algorithm): if not isinstance(builder, x509.CertificateBuilder): raise TypeError('Builder type mismatch.') if not isinstance(algorithm, hashes.HashAlgorithm): @@ -1173,16 +1239,22 @@ class Backend(object): pp, r = _encode_authority_information_access( self, extension.value ) + elif isinstance(extension.value, x509.CRLDistributionPoints): + pp, r = _encode_crl_distribution_points( + self, extension.value + ) else: raise NotImplementedError('Extension not yet supported.') - obj = _txt2obj(self, extension.oid.dotted_string) + obj = _txt2obj_gc(self, extension.oid.dotted_string) extension = self._lib.X509_EXTENSION_create_by_OBJ( self._ffi.NULL, obj, 1 if extension.critical else 0, _encode_asn1_str_gc(self, pp[0], r) ) + assert extension != self._ffi.NULL + extension = self._ffi.gc(extension, self._lib.X509_EXTENSION_free) res = self._lib.X509_add_ext(x509_cert, extension, i) assert res == 1 diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index ee9a3bbf..564b2680 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -592,6 +592,10 @@ def _decode_extended_key_usage(backend, sk): return x509.ExtendedKeyUsage(ekus) +_DISTPOINT_TYPE_FULLNAME = 0 +_DISTPOINT_TYPE_RELATIVENAME = 1 + + def _decode_crl_distribution_points(backend, cdps): cdps = backend._ffi.cast("Cryptography_STACK_OF_DIST_POINT *", cdps) cdps = backend._ffi.gc(cdps, backend._lib.sk_DIST_POINT_free) @@ -651,7 +655,7 @@ def _decode_crl_distribution_points(backend, cdps): # 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: + if cdp.distpoint.type == _DISTPOINT_TYPE_FULLNAME: full_name = _decode_general_names( backend, cdp.distpoint.name.fullname ) diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 82b8bd36..5ed3c094 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -324,7 +324,19 @@ class Extension(object): return not self == other +@six.add_metaclass(abc.ABCMeta) +class ExtensionType(object): + @abc.abstractproperty + def oid(self): + """ + Returns the oid associated with the given extension type. + """ + + +@utils.register_interface(ExtensionType) class ExtendedKeyUsage(object): + oid = OID_EXTENDED_KEY_USAGE + def __init__(self, usages): if not all(isinstance(x, ObjectIdentifier) for x in usages): raise TypeError( @@ -352,11 +364,15 @@ class ExtendedKeyUsage(object): return not self == other +@utils.register_interface(ExtensionType) class OCSPNoCheck(object): - pass + oid = OID_OCSP_NO_CHECK +@utils.register_interface(ExtensionType) class BasicConstraints(object): + oid = OID_BASIC_CONSTRAINTS + def __init__(self, ca, path_length): if not isinstance(ca, bool): raise TypeError("ca must be a boolean value") @@ -392,7 +408,10 @@ class BasicConstraints(object): return not self == other +@utils.register_interface(ExtensionType) class KeyUsage(object): + oid = OID_KEY_USAGE + def __init__(self, digital_signature, content_commitment, key_encipherment, data_encipherment, key_agreement, key_cert_sign, crl_sign, encipher_only, decipher_only): @@ -475,7 +494,10 @@ class KeyUsage(object): return not self == other +@utils.register_interface(ExtensionType) class AuthorityInformationAccess(object): + oid = OID_AUTHORITY_INFORMATION_ACCESS + def __init__(self, descriptions): if not all(isinstance(x, AccessDescription) for x in descriptions): raise TypeError( @@ -539,7 +561,10 @@ class AccessDescription(object): access_location = utils.read_only_property("_access_location") +@utils.register_interface(ExtensionType) class CertificatePolicies(object): + oid = OID_CERTIFICATE_POLICIES + def __init__(self, policies): if not all(isinstance(x, PolicyInformation) for x in policies): raise TypeError( @@ -676,7 +701,10 @@ class NoticeReference(object): notice_numbers = utils.read_only_property("_notice_numbers") +@utils.register_interface(ExtensionType) class SubjectKeyIdentifier(object): + oid = OID_SUBJECT_KEY_IDENTIFIER + def __init__(self, digest): self._digest = digest @@ -718,7 +746,10 @@ class SubjectKeyIdentifier(object): return not self == other +@utils.register_interface(ExtensionType) class NameConstraints(object): + oid = OID_NAME_CONSTRAINTS + def __init__(self, permitted_subtrees, excluded_subtrees): if permitted_subtrees is not None: if not all( @@ -782,7 +813,10 @@ class NameConstraints(object): excluded_subtrees = utils.read_only_property("_excluded_subtrees") +@utils.register_interface(ExtensionType) class CRLDistributionPoints(object): + oid = OID_CRL_DISTRIBUTION_POINTS + def __init__(self, distribution_points): if not all( isinstance(x, DistributionPoint) for x in distribution_points @@ -817,7 +851,8 @@ class DistributionPoint(object): def __init__(self, full_name, relative_name, reasons, crl_issuer): if full_name and relative_name: raise ValueError( - "At least one of full_name and relative_name must be None" + "You cannot provide both full_name and relative_name, at " + "least one must be None." ) if full_name and not all( @@ -902,7 +937,10 @@ class ReasonFlags(Enum): remove_from_crl = "removeFromCRL" +@utils.register_interface(ExtensionType) class InhibitAnyPolicy(object): + oid = OID_INHIBIT_ANY_POLICY + def __init__(self, skip_certs): if not isinstance(skip_certs, six.integer_types): raise TypeError("skip_certs must be an integer") @@ -1192,7 +1230,10 @@ class GeneralNames(object): return not self == other +@utils.register_interface(ExtensionType) class SubjectAlternativeName(object): + oid = OID_SUBJECT_ALTERNATIVE_NAME + def __init__(self, general_names): self._general_names = GeneralNames(general_names) @@ -1218,7 +1259,10 @@ class SubjectAlternativeName(object): return not self == other +@utils.register_interface(ExtensionType) class IssuerAlternativeName(object): + oid = OID_ISSUER_ALTERNATIVE_NAME + def __init__(self, general_names): self._general_names = GeneralNames(general_names) @@ -1244,7 +1288,10 @@ class IssuerAlternativeName(object): return not self == other +@utils.register_interface(ExtensionType) class AuthorityKeyIdentifier(object): + oid = OID_AUTHORITY_KEY_IDENTIFIER + def __init__(self, key_identifier, authority_cert_issuer, authority_cert_serial_number): if authority_cert_issuer or authority_cert_serial_number: @@ -1766,6 +1813,10 @@ class CertificateBuilder(object): ) elif isinstance(extension, InhibitAnyPolicy): extension = Extension(OID_INHIBIT_ANY_POLICY, critical, extension) + elif isinstance(extension, CRLDistributionPoints): + extension = Extension( + OID_CRL_DISTRIBUTION_POINTS, critical, extension + ) else: raise NotImplementedError('Unsupported X.509 extension.') @@ -1784,4 +1835,22 @@ class CertificateBuilder(object): """ Signs the certificate using the CA's private key. """ - return backend.sign_x509_certificate(self, private_key, algorithm) + if self._subject_name is None: + raise ValueError("A certificate must have a subject name") + + if self._issuer_name is None: + raise ValueError("A certificate must have an issuer name") + + if self._serial_number is None: + raise ValueError("A certificate must have a serial number") + + if self._not_valid_before is None: + raise ValueError("A certificate must have a not valid before time") + + if self._not_valid_after is None: + raise ValueError("A certificate must have a not valid after time") + + if self._public_key is None: + raise ValueError("A certificate must have a public key") + + return backend.create_x509_certificate(self, private_key, algorithm) |