diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 23 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 31 | ||||
-rw-r--r-- | src/cryptography/x509/__init__.py | 64 | ||||
-rw-r--r-- | src/cryptography/x509/base.py | 899 | ||||
-rw-r--r-- | src/cryptography/x509/extensions.py | 912 | ||||
-rw-r--r-- | src/cryptography/x509/oid.py | 61 |
6 files changed, 1010 insertions, 980 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 3866c0d4..9eae69c7 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -53,6 +53,7 @@ from cryptography.hazmat.primitives.ciphers.algorithms import ( from cryptography.hazmat.primitives.ciphers.modes import ( CBC, CFB, CFB8, CTR, ECB, GCM, OFB ) +from cryptography.x509.oid import ExtensionOID _MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"]) @@ -482,19 +483,19 @@ def _encode_crl_distribution_points(backend, crl_distribution_points): _EXTENSION_ENCODE_HANDLERS = { - x509.OID_BASIC_CONSTRAINTS: _encode_basic_constraints, - x509.OID_SUBJECT_KEY_IDENTIFIER: _encode_subject_key_identifier, - x509.OID_KEY_USAGE: _encode_key_usage, - x509.OID_SUBJECT_ALTERNATIVE_NAME: _encode_alt_name, - x509.OID_ISSUER_ALTERNATIVE_NAME: _encode_alt_name, - x509.OID_EXTENDED_KEY_USAGE: _encode_extended_key_usage, - x509.OID_AUTHORITY_KEY_IDENTIFIER: _encode_authority_key_identifier, - x509.OID_AUTHORITY_INFORMATION_ACCESS: ( + ExtensionOID.BASIC_CONSTRAINTS: _encode_basic_constraints, + ExtensionOID.SUBJECT_KEY_IDENTIFIER: _encode_subject_key_identifier, + ExtensionOID.KEY_USAGE: _encode_key_usage, + ExtensionOID.SUBJECT_ALTERNATIVE_NAME: _encode_alt_name, + ExtensionOID.ISSUER_ALTERNATIVE_NAME: _encode_alt_name, + ExtensionOID.EXTENDED_KEY_USAGE: _encode_extended_key_usage, + ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _encode_authority_key_identifier, + ExtensionOID.AUTHORITY_INFORMATION_ACCESS: ( _encode_authority_information_access ), - x509.OID_CRL_DISTRIBUTION_POINTS: _encode_crl_distribution_points, - x509.OID_INHIBIT_ANY_POLICY: _encode_inhibit_any_policy, - x509.OID_OCSP_NO_CHECK: _encode_ocsp_nocheck, + ExtensionOID.CRL_DISTRIBUTION_POINTS: _encode_crl_distribution_points, + ExtensionOID.INHIBIT_ANY_POLICY: _encode_inhibit_any_policy, + ExtensionOID.OCSP_NO_CHECK: _encode_ocsp_nocheck, } diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 564b2680..e9af97f3 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -17,6 +17,7 @@ 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 def _obj2txt(backend, obj): @@ -385,13 +386,13 @@ def _decode_certificate_policies(backend, cp): pqualid = x509.ObjectIdentifier( _obj2txt(backend, pqi.pqualid) ) - if pqualid == x509.OID_CPS_QUALIFIER: + if pqualid == CertificatePoliciesOID.CPS_QUALIFIER: cpsuri = backend._ffi.buffer( pqi.d.cpsuri.data, pqi.d.cpsuri.length )[:].decode('ascii') qualifiers.append(cpsuri) else: - assert pqualid == x509.OID_CPS_USER_NOTICE + assert pqualid == CertificatePoliciesOID.CPS_USER_NOTICE user_notice = _decode_user_notice( backend, pqi.d.usernotice ) @@ -756,21 +757,21 @@ class _CertificateSigningRequest(object): _EXTENSION_HANDLERS = { - x509.OID_BASIC_CONSTRAINTS: _decode_basic_constraints, - x509.OID_SUBJECT_KEY_IDENTIFIER: _decode_subject_key_identifier, - x509.OID_KEY_USAGE: _decode_key_usage, - x509.OID_SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name, - x509.OID_EXTENDED_KEY_USAGE: _decode_extended_key_usage, - x509.OID_AUTHORITY_KEY_IDENTIFIER: _decode_authority_key_identifier, - x509.OID_AUTHORITY_INFORMATION_ACCESS: ( + ExtensionOID.BASIC_CONSTRAINTS: _decode_basic_constraints, + ExtensionOID.SUBJECT_KEY_IDENTIFIER: _decode_subject_key_identifier, + ExtensionOID.KEY_USAGE: _decode_key_usage, + ExtensionOID.SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name, + ExtensionOID.EXTENDED_KEY_USAGE: _decode_extended_key_usage, + ExtensionOID.AUTHORITY_KEY_IDENTIFIER: _decode_authority_key_identifier, + ExtensionOID.AUTHORITY_INFORMATION_ACCESS: ( _decode_authority_information_access ), - x509.OID_CERTIFICATE_POLICIES: _decode_certificate_policies, - x509.OID_CRL_DISTRIBUTION_POINTS: _decode_crl_distribution_points, - x509.OID_OCSP_NO_CHECK: _decode_ocsp_no_check, - x509.OID_INHIBIT_ANY_POLICY: _decode_inhibit_any_policy, - x509.OID_ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name, - x509.OID_NAME_CONSTRAINTS: _decode_name_constraints, + ExtensionOID.CERTIFICATE_POLICIES: _decode_certificate_policies, + ExtensionOID.CRL_DISTRIBUTION_POINTS: _decode_crl_distribution_points, + ExtensionOID.OCSP_NO_CHECK: _decode_ocsp_no_check, + ExtensionOID.INHIBIT_ANY_POLICY: _decode_inhibit_any_policy, + ExtensionOID.ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name, + ExtensionOID.NAME_CONSTRAINTS: _decode_name_constraints, } diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py index 82e83616..1aa2598b 100644 --- a/src/cryptography/x509/__init__.py +++ b/src/cryptography/x509/__init__.py @@ -5,18 +5,21 @@ from __future__ import absolute_import, division, print_function from cryptography.x509.base import ( - AccessDescription, AuthorityInformationAccess, AuthorityKeyIdentifier, - BasicConstraints, CRLDistributionPoints, Certificate, CertificateBuilder, - CertificatePolicies, CertificateRevocationList, CertificateSigningRequest, - CertificateSigningRequestBuilder, DistributionPoint, - DuplicateExtension, ExtendedKeyUsage, Extension, ExtensionNotFound, - ExtensionType, Extensions, GeneralNames, InhibitAnyPolicy, - InvalidVersion, IssuerAlternativeName, KeyUsage, NameConstraints, - NoticeReference, OCSPNoCheck, ObjectIdentifier, - PolicyInformation, ReasonFlags, - RevokedCertificate, SubjectAlternativeName, SubjectKeyIdentifier, - UnsupportedExtension, UserNotice, Version, load_der_x509_certificate, - load_der_x509_csr, load_pem_x509_certificate, load_pem_x509_csr, + Certificate, CertificateBuilder, CertificateRevocationList, + CertificateSigningRequest, CertificateSigningRequestBuilder, + InvalidVersion, RevokedCertificate, + Version, load_der_x509_certificate, load_der_x509_csr, + load_pem_x509_certificate, load_pem_x509_csr, +) +from cryptography.x509.extensions import ( + AccessDescription, AuthorityInformationAccess, + AuthorityKeyIdentifier, BasicConstraints, CRLDistributionPoints, + CertificatePolicies, DistributionPoint, DuplicateExtension, + ExtendedKeyUsage, Extension, ExtensionNotFound, ExtensionType, Extensions, + GeneralNames, InhibitAnyPolicy, IssuerAlternativeName, KeyUsage, + NameConstraints, NoticeReference, OCSPNoCheck, PolicyInformation, + ReasonFlags, SubjectAlternativeName, SubjectKeyIdentifier, + UnsupportedExtension, UserNotice ) from cryptography.x509.general_name import ( DNSName, DirectoryName, GeneralName, IPAddress, OtherName, RFC822Name, @@ -25,11 +28,8 @@ from cryptography.x509.general_name import ( ) from cryptography.x509.name import Name, NameAttribute from cryptography.x509.oid import ( - ExtensionOID, NameOID, OID_ANY_POLICY, - OID_CA_ISSUERS, OID_CERTIFICATE_ISSUER, OID_CLIENT_AUTH, - OID_CODE_SIGNING, OID_CPS_QUALIFIER, OID_CPS_USER_NOTICE, OID_CRL_REASON, - OID_EMAIL_PROTECTION, OID_INVALIDITY_DATE, OID_OCSP, OID_OCSP_SIGNING, - OID_SERVER_AUTH, OID_TIME_STAMPING, + AuthorityInformationAccessOID, CRLExtensionOID, CertificatePoliciesOID, + ExtendedKeyUsageOID, ExtensionOID, NameOID, ObjectIdentifier, SignatureAlgorithmOID, _SIG_OIDS_TO_HASH ) @@ -84,6 +84,24 @@ OID_STATE_OR_PROVINCE_NAME = NameOID.STATE_OR_PROVINCE_NAME OID_SURNAME = NameOID.SURNAME OID_TITLE = NameOID.TITLE +OID_CLIENT_AUTH = ExtendedKeyUsageOID.CLIENT_AUTH +OID_CODE_SIGNING = ExtendedKeyUsageOID.CODE_SIGNING +OID_EMAIL_PROTECTION = ExtendedKeyUsageOID.EMAIL_PROTECTION +OID_OCSP_SIGNING = ExtendedKeyUsageOID.OCSP_SIGNING +OID_SERVER_AUTH = ExtendedKeyUsageOID.SERVER_AUTH +OID_TIME_STAMPING = ExtendedKeyUsageOID.TIME_STAMPING + +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_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS +OID_OCSP = AuthorityInformationAccessOID.OCSP + __all__ = [ "load_pem_x509_certificate", @@ -136,20 +154,8 @@ __all__ = [ "CertificateSigningRequestBuilder", "CertificateBuilder", "Version", - "OID_CRL_REASON", - "OID_INVALIDITY_DATE", - "OID_CERTIFICATE_ISSUER", "_SIG_OIDS_TO_HASH", - "OID_CPS_QUALIFIER", - "OID_CPS_USER_NOTICE", - "OID_ANY_POLICY", "OID_CA_ISSUERS", "OID_OCSP", - "OID_SERVER_AUTH", - "OID_CLIENT_AUTH", - "OID_CODE_SIGNING", - "OID_EMAIL_PROTECTION", - "OID_TIME_STAMPING", - "OID_OCSP_SIGNING", "_GENERAL_NAMES", ] diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py index 8eabee88..27eafac6 100644 --- a/src/cryptography/x509/base.py +++ b/src/cryptography/x509/base.py @@ -6,51 +6,14 @@ from __future__ import absolute_import, division, print_function import abc import datetime -import hashlib -import ipaddress from enum import Enum -from pyasn1.codec.der import decoder -from pyasn1.type import namedtype, univ - import six from cryptography import utils -from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa -from cryptography.x509.general_name import GeneralName, IPAddress, OtherName +from cryptography.x509.extensions import Extension, ExtensionType from cryptography.x509.name import Name -from cryptography.x509.oid import ( - ExtensionOID, OID_CA_ISSUERS, OID_OCSP, ObjectIdentifier -) - - -class _SubjectPublicKeyInfo(univ.Sequence): - componentType = namedtype.NamedTypes( - namedtype.NamedType('algorithm', univ.Sequence()), - namedtype.NamedType('subjectPublicKey', univ.BitString()) - ) - - -def _key_identifier_from_public_key(public_key): - # This is a very slow way to do this. - serialized = public_key.public_bytes( - serialization.Encoding.DER, - serialization.PublicFormat.SubjectPublicKeyInfo - ) - spki, remaining = decoder.decode( - serialized, asn1Spec=_SubjectPublicKeyInfo() - ) - assert not remaining - # the univ.BitString object is a tuple of bits. We need bytes and - # pyasn1 really doesn't want to give them to us. To get it we'll - # build an integer and convert that to bytes. - bits = 0 - for bit in spki.getComponentByName("subjectPublicKey"): - bits = bits << 1 | bit - - data = utils.int_to_bytes(bits) - return hashlib.sha1(data).digest() _UNIX_EPOCH = datetime.datetime(1970, 1, 1) @@ -83,866 +46,6 @@ class InvalidVersion(Exception): self.parsed_version = parsed_version -class DuplicateExtension(Exception): - def __init__(self, msg, oid): - super(DuplicateExtension, self).__init__(msg) - self.oid = oid - - -class UnsupportedExtension(Exception): - def __init__(self, msg, oid): - super(UnsupportedExtension, self).__init__(msg) - self.oid = oid - - -class ExtensionNotFound(Exception): - def __init__(self, msg, oid): - super(ExtensionNotFound, self).__init__(msg) - self.oid = oid - - -class Extensions(object): - def __init__(self, extensions): - self._extensions = extensions - - def get_extension_for_oid(self, oid): - for ext in self: - if ext.oid == oid: - return ext - - raise ExtensionNotFound("No {0} extension was found".format(oid), oid) - - def __iter__(self): - return iter(self._extensions) - - def __len__(self): - return len(self._extensions) - - -class Extension(object): - def __init__(self, oid, critical, value): - if not isinstance(oid, ObjectIdentifier): - raise TypeError( - "oid argument must be an ObjectIdentifier instance." - ) - - if not isinstance(critical, bool): - raise TypeError("critical must be a boolean value") - - self._oid = oid - self._critical = critical - self._value = value - - oid = utils.read_only_property("_oid") - critical = utils.read_only_property("_critical") - value = utils.read_only_property("_value") - - def __repr__(self): - return ("<Extension(oid={0.oid}, critical={0.critical}, " - "value={0.value})>").format(self) - - def __eq__(self, other): - if not isinstance(other, Extension): - return NotImplemented - - return ( - self.oid == other.oid and - self.critical == other.critical and - self.value == other.value - ) - - def __ne__(self, other): - 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 = ExtensionOID.EXTENDED_KEY_USAGE - - def __init__(self, usages): - 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 - - def __iter__(self): - return iter(self._usages) - - def __len__(self): - return len(self._usages) - - 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 - - -@utils.register_interface(ExtensionType) -class OCSPNoCheck(object): - oid = ExtensionOID.OCSP_NO_CHECK - - -@utils.register_interface(ExtensionType) -class BasicConstraints(object): - oid = ExtensionOID.BASIC_CONSTRAINTS - - def __init__(self, ca, path_length): - if not isinstance(ca, bool): - raise TypeError("ca must be a boolean value") - - if path_length is not None and not ca: - raise ValueError("path_length must be None when ca is False") - - if ( - path_length is not None and - (not isinstance(path_length, six.integer_types) or path_length < 0) - ): - raise TypeError( - "path_length must be a non-negative integer or None" - ) - - self._ca = ca - self._path_length = path_length - - ca = utils.read_only_property("_ca") - path_length = utils.read_only_property("_path_length") - - def __repr__(self): - return ("<BasicConstraints(ca={0.ca}, " - "path_length={0.path_length})>").format(self) - - def __eq__(self, other): - if not isinstance(other, BasicConstraints): - return NotImplemented - - return self.ca == other.ca and self.path_length == other.path_length - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(ExtensionType) -class KeyUsage(object): - oid = ExtensionOID.KEY_USAGE - - def __init__(self, digital_signature, content_commitment, key_encipherment, - data_encipherment, key_agreement, key_cert_sign, crl_sign, - encipher_only, decipher_only): - if not key_agreement and (encipher_only or decipher_only): - raise ValueError( - "encipher_only and decipher_only can only be true when " - "key_agreement is true" - ) - - self._digital_signature = digital_signature - self._content_commitment = content_commitment - self._key_encipherment = key_encipherment - self._data_encipherment = data_encipherment - self._key_agreement = key_agreement - self._key_cert_sign = key_cert_sign - self._crl_sign = crl_sign - self._encipher_only = encipher_only - self._decipher_only = decipher_only - - digital_signature = utils.read_only_property("_digital_signature") - content_commitment = utils.read_only_property("_content_commitment") - key_encipherment = utils.read_only_property("_key_encipherment") - data_encipherment = utils.read_only_property("_data_encipherment") - key_agreement = utils.read_only_property("_key_agreement") - key_cert_sign = utils.read_only_property("_key_cert_sign") - crl_sign = utils.read_only_property("_crl_sign") - - @property - def encipher_only(self): - if not self.key_agreement: - raise ValueError( - "encipher_only is undefined unless key_agreement is true" - ) - else: - return self._encipher_only - - @property - def decipher_only(self): - if not self.key_agreement: - raise ValueError( - "decipher_only is undefined unless key_agreement is true" - ) - else: - return self._decipher_only - - def __repr__(self): - try: - encipher_only = self.encipher_only - decipher_only = self.decipher_only - except ValueError: - encipher_only = None - decipher_only = None - - return ("<KeyUsage(digital_signature={0.digital_signature}, " - "content_commitment={0.content_commitment}, " - "key_encipherment={0.key_encipherment}, " - "data_encipherment={0.data_encipherment}, " - "key_agreement={0.key_agreement}, " - "key_cert_sign={0.key_cert_sign}, crl_sign={0.crl_sign}, " - "encipher_only={1}, decipher_only={2})>").format( - self, encipher_only, decipher_only) - - def __eq__(self, other): - if not isinstance(other, KeyUsage): - return NotImplemented - - return ( - self.digital_signature == other.digital_signature and - self.content_commitment == other.content_commitment and - self.key_encipherment == other.key_encipherment and - self.data_encipherment == other.data_encipherment and - self.key_agreement == other.key_agreement and - self.key_cert_sign == other.key_cert_sign and - self.crl_sign == other.crl_sign and - self._encipher_only == other._encipher_only and - self._decipher_only == other._decipher_only - ) - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(ExtensionType) -class AuthorityInformationAccess(object): - oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS - - 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") - - -@utils.register_interface(ExtensionType) -class CertificatePolicies(object): - oid = ExtensionOID.CERTIFICATE_POLICIES - - def __init__(self, policies): - if not all(isinstance(x, PolicyInformation) for x in policies): - raise TypeError( - "Every item in the policies list must be a " - "PolicyInformation" - ) - - self._policies = policies - - def __iter__(self): - return iter(self._policies) - - def __len__(self): - return len(self._policies) - - def __repr__(self): - return "<CertificatePolicies({0})>".format(self._policies) - - def __eq__(self, other): - if not isinstance(other, CertificatePolicies): - return NotImplemented - - return self._policies == other._policies - - def __ne__(self, other): - return not self == other - - -class PolicyInformation(object): - def __init__(self, policy_identifier, policy_qualifiers): - if not isinstance(policy_identifier, ObjectIdentifier): - raise TypeError("policy_identifier must be an ObjectIdentifier") - - self._policy_identifier = policy_identifier - if policy_qualifiers and not all( - isinstance( - x, (six.text_type, UserNotice) - ) for x in policy_qualifiers - ): - raise TypeError( - "policy_qualifiers must be a list of strings and/or UserNotice" - " objects or None" - ) - - self._policy_qualifiers = policy_qualifiers - - def __repr__(self): - return ( - "<PolicyInformation(policy_identifier={0.policy_identifier}, polic" - "y_qualifiers={0.policy_qualifiers})>".format(self) - ) - - def __eq__(self, other): - if not isinstance(other, PolicyInformation): - return NotImplemented - - return ( - self.policy_identifier == other.policy_identifier and - self.policy_qualifiers == other.policy_qualifiers - ) - - def __ne__(self, other): - return not self == other - - policy_identifier = utils.read_only_property("_policy_identifier") - policy_qualifiers = utils.read_only_property("_policy_qualifiers") - - -class UserNotice(object): - def __init__(self, notice_reference, explicit_text): - if notice_reference and not isinstance( - notice_reference, NoticeReference - ): - raise TypeError( - "notice_reference must be None or a NoticeReference" - ) - - self._notice_reference = notice_reference - self._explicit_text = explicit_text - - def __repr__(self): - return ( - "<UserNotice(notice_reference={0.notice_reference}, explicit_text=" - "{0.explicit_text!r})>".format(self) - ) - - def __eq__(self, other): - if not isinstance(other, UserNotice): - return NotImplemented - - return ( - self.notice_reference == other.notice_reference and - self.explicit_text == other.explicit_text - ) - - def __ne__(self, other): - return not self == other - - notice_reference = utils.read_only_property("_notice_reference") - explicit_text = utils.read_only_property("_explicit_text") - - -class NoticeReference(object): - def __init__(self, organization, notice_numbers): - self._organization = organization - if not isinstance(notice_numbers, list) or not all( - isinstance(x, int) for x in notice_numbers - ): - raise TypeError( - "notice_numbers must be a list of integers" - ) - - self._notice_numbers = notice_numbers - - def __repr__(self): - return ( - "<NoticeReference(organization={0.organization!r}, notice_numbers=" - "{0.notice_numbers})>".format(self) - ) - - def __eq__(self, other): - if not isinstance(other, NoticeReference): - return NotImplemented - - return ( - self.organization == other.organization and - self.notice_numbers == other.notice_numbers - ) - - def __ne__(self, other): - return not self == other - - organization = utils.read_only_property("_organization") - notice_numbers = utils.read_only_property("_notice_numbers") - - -@utils.register_interface(ExtensionType) -class SubjectKeyIdentifier(object): - oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER - - def __init__(self, digest): - self._digest = digest - - @classmethod - def from_public_key(cls, public_key): - return cls(_key_identifier_from_public_key(public_key)) - - digest = utils.read_only_property("_digest") - - def __repr__(self): - return "<SubjectKeyIdentifier(digest={0!r})>".format(self.digest) - - def __eq__(self, other): - if not isinstance(other, SubjectKeyIdentifier): - return NotImplemented - - return ( - self.digest == other.digest - ) - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(ExtensionType) -class NameConstraints(object): - oid = ExtensionOID.NAME_CONSTRAINTS - - def __init__(self, permitted_subtrees, excluded_subtrees): - if permitted_subtrees is not None: - if not all( - isinstance(x, GeneralName) for x in permitted_subtrees - ): - raise TypeError( - "permitted_subtrees must be a list of GeneralName objects " - "or None" - ) - - self._validate_ip_name(permitted_subtrees) - - if excluded_subtrees is not None: - if not all( - isinstance(x, GeneralName) for x in excluded_subtrees - ): - raise TypeError( - "excluded_subtrees must be a list of GeneralName objects " - "or None" - ) - - self._validate_ip_name(excluded_subtrees) - - if permitted_subtrees is None and excluded_subtrees is None: - raise ValueError( - "At least one of permitted_subtrees and excluded_subtrees " - "must not be None" - ) - - self._permitted_subtrees = permitted_subtrees - self._excluded_subtrees = excluded_subtrees - - def __eq__(self, other): - if not isinstance(other, NameConstraints): - return NotImplemented - - return ( - self.excluded_subtrees == other.excluded_subtrees and - self.permitted_subtrees == other.permitted_subtrees - ) - - def __ne__(self, other): - return not self == other - - def _validate_ip_name(self, tree): - if any(isinstance(name, IPAddress) and not isinstance( - name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network) - ) for name in tree): - raise TypeError( - "IPAddress name constraints must be an IPv4Network or" - " IPv6Network object" - ) - - def __repr__(self): - return ( - u"<NameConstraints(permitted_subtrees={0.permitted_subtrees}, " - u"excluded_subtrees={0.excluded_subtrees})>".format(self) - ) - - permitted_subtrees = utils.read_only_property("_permitted_subtrees") - excluded_subtrees = utils.read_only_property("_excluded_subtrees") - - -@utils.register_interface(ExtensionType) -class CRLDistributionPoints(object): - oid = ExtensionOID.CRL_DISTRIBUTION_POINTS - - def __init__(self, distribution_points): - if not all( - isinstance(x, DistributionPoint) for x in distribution_points - ): - raise TypeError( - "distribution_points must be a list of DistributionPoint " - "objects" - ) - - self._distribution_points = distribution_points - - def __iter__(self): - return iter(self._distribution_points) - - def __len__(self): - return len(self._distribution_points) - - def __repr__(self): - return "<CRLDistributionPoints({0})>".format(self._distribution_points) - - def __eq__(self, other): - if not isinstance(other, CRLDistributionPoints): - return NotImplemented - - return self._distribution_points == other._distribution_points - - def __ne__(self, other): - return not self == other - - -class DistributionPoint(object): - def __init__(self, full_name, relative_name, reasons, crl_issuer): - if full_name and relative_name: - raise ValueError( - "You cannot provide both full_name and relative_name, at " - "least one must be None." - ) - - if full_name and not all( - isinstance(x, GeneralName) for x in full_name - ): - raise TypeError( - "full_name must be a list of GeneralName objects" - ) - - if relative_name and not isinstance(relative_name, Name): - raise TypeError("relative_name must be a Name") - - if crl_issuer and not all( - isinstance(x, GeneralName) for x in crl_issuer - ): - raise TypeError( - "crl_issuer must be None or a list of general names" - ) - - if reasons and (not isinstance(reasons, frozenset) or not all( - isinstance(x, ReasonFlags) for x in reasons - )): - raise TypeError("reasons must be None or frozenset of ReasonFlags") - - if reasons and ( - ReasonFlags.unspecified in reasons or - ReasonFlags.remove_from_crl in reasons - ): - raise ValueError( - "unspecified and remove_from_crl are not valid reasons in a " - "DistributionPoint" - ) - - if reasons and not crl_issuer and not (full_name or relative_name): - raise ValueError( - "You must supply crl_issuer, full_name, or relative_name when " - "reasons is not None" - ) - - self._full_name = full_name - self._relative_name = relative_name - self._reasons = reasons - self._crl_issuer = crl_issuer - - def __repr__(self): - return ( - "<DistributionPoint(full_name={0.full_name}, relative_name={0.rela" - "tive_name}, reasons={0.reasons}, crl_issuer={0.crl_is" - "suer})>".format(self) - ) - - def __eq__(self, other): - if not isinstance(other, DistributionPoint): - return NotImplemented - - return ( - self.full_name == other.full_name and - self.relative_name == other.relative_name and - self.reasons == other.reasons and - self.crl_issuer == other.crl_issuer - ) - - def __ne__(self, other): - return not self == other - - full_name = utils.read_only_property("_full_name") - relative_name = utils.read_only_property("_relative_name") - reasons = utils.read_only_property("_reasons") - crl_issuer = utils.read_only_property("_crl_issuer") - - -class ReasonFlags(Enum): - unspecified = "unspecified" - key_compromise = "keyCompromise" - ca_compromise = "cACompromise" - affiliation_changed = "affiliationChanged" - superseded = "superseded" - cessation_of_operation = "cessationOfOperation" - certificate_hold = "certificateHold" - privilege_withdrawn = "privilegeWithdrawn" - aa_compromise = "aACompromise" - remove_from_crl = "removeFromCRL" - - -@utils.register_interface(ExtensionType) -class InhibitAnyPolicy(object): - oid = ExtensionOID.INHIBIT_ANY_POLICY - - def __init__(self, skip_certs): - if not isinstance(skip_certs, six.integer_types): - raise TypeError("skip_certs must be an integer") - - if skip_certs < 0: - raise ValueError("skip_certs must be a non-negative integer") - - self._skip_certs = skip_certs - - def __repr__(self): - return "<InhibitAnyPolicy(skip_certs={0.skip_certs})>".format(self) - - def __eq__(self, other): - if not isinstance(other, InhibitAnyPolicy): - return NotImplemented - - return self.skip_certs == other.skip_certs - - def __ne__(self, other): - return not self == other - - skip_certs = utils.read_only_property("_skip_certs") - - -class GeneralNames(object): - def __init__(self, general_names): - if not all(isinstance(x, GeneralName) for x in general_names): - raise TypeError( - "Every item in the general_names list must be an " - "object conforming to the GeneralName interface" - ) - - self._general_names = general_names - - def __iter__(self): - return iter(self._general_names) - - def __len__(self): - return len(self._general_names) - - def get_values_for_type(self, type): - # Return the value of each GeneralName, except for OtherName instances - # which we return directly because it has two important properties not - # just one value. - objs = (i for i in self if isinstance(i, type)) - if type != OtherName: - objs = (i.value for i in objs) - return list(objs) - - def __repr__(self): - return "<GeneralNames({0})>".format(self._general_names) - - def __eq__(self, other): - if not isinstance(other, GeneralNames): - return NotImplemented - - return self._general_names == other._general_names - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(ExtensionType) -class SubjectAlternativeName(object): - oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME - - def __init__(self, general_names): - self._general_names = GeneralNames(general_names) - - def __iter__(self): - return iter(self._general_names) - - def __len__(self): - return len(self._general_names) - - def get_values_for_type(self, type): - return self._general_names.get_values_for_type(type) - - def __repr__(self): - return "<SubjectAlternativeName({0})>".format(self._general_names) - - def __eq__(self, other): - if not isinstance(other, SubjectAlternativeName): - return NotImplemented - - return self._general_names == other._general_names - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(ExtensionType) -class IssuerAlternativeName(object): - oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME - - def __init__(self, general_names): - self._general_names = GeneralNames(general_names) - - def __iter__(self): - return iter(self._general_names) - - def __len__(self): - return len(self._general_names) - - def get_values_for_type(self, type): - return self._general_names.get_values_for_type(type) - - def __repr__(self): - return "<IssuerAlternativeName({0})>".format(self._general_names) - - def __eq__(self, other): - if not isinstance(other, IssuerAlternativeName): - return NotImplemented - - return self._general_names == other._general_names - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(ExtensionType) -class AuthorityKeyIdentifier(object): - oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER - - def __init__(self, key_identifier, authority_cert_issuer, - authority_cert_serial_number): - if authority_cert_issuer or authority_cert_serial_number: - if not authority_cert_issuer or not authority_cert_serial_number: - raise ValueError( - "authority_cert_issuer and authority_cert_serial_number " - "must both be present or both None" - ) - - 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( - "authority_cert_serial_number must be an integer" - ) - - self._key_identifier = key_identifier - self._authority_cert_issuer = authority_cert_issuer - self._authority_cert_serial_number = authority_cert_serial_number - - @classmethod - def from_issuer_public_key(cls, public_key): - digest = _key_identifier_from_public_key(public_key) - return cls( - key_identifier=digest, - authority_cert_issuer=None, - authority_cert_serial_number=None - ) - - def __repr__(self): - return ( - "<AuthorityKeyIdentifier(key_identifier={0.key_identifier!r}, " - "authority_cert_issuer={0.authority_cert_issuer}, " - "authority_cert_serial_number={0.authority_cert_serial_number}" - ")>".format(self) - ) - - def __eq__(self, other): - if not isinstance(other, AuthorityKeyIdentifier): - return NotImplemented - - return ( - self.key_identifier == other.key_identifier and - self.authority_cert_issuer == other.authority_cert_issuer and - self.authority_cert_serial_number == - other.authority_cert_serial_number - ) - - def __ne__(self, other): - return not self == other - - key_identifier = utils.read_only_property("_key_identifier") - authority_cert_issuer = utils.read_only_property("_authority_cert_issuer") - authority_cert_serial_number = utils.read_only_property( - "_authority_cert_serial_number" - ) - - @six.add_metaclass(abc.ABCMeta) class Certificate(object): @abc.abstractmethod diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py new file mode 100644 index 00000000..798a0e3a --- /dev/null +++ b/src/cryptography/x509/extensions.py @@ -0,0 +1,912 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +import abc +import hashlib +import ipaddress +from enum import Enum + +from pyasn1.codec.der import decoder +from pyasn1.type import namedtype, univ + +import six + +from cryptography import utils +from cryptography.hazmat.primitives import serialization +from cryptography.x509.general_name import GeneralName, IPAddress, OtherName +from cryptography.x509.name import Name +from cryptography.x509.oid import ( + AuthorityInformationAccessOID, ExtensionOID, ObjectIdentifier +) + + +class _SubjectPublicKeyInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('algorithm', univ.Sequence()), + namedtype.NamedType('subjectPublicKey', univ.BitString()) + ) + + +def _key_identifier_from_public_key(public_key): + # This is a very slow way to do this. + serialized = public_key.public_bytes( + serialization.Encoding.DER, + serialization.PublicFormat.SubjectPublicKeyInfo + ) + spki, remaining = decoder.decode( + serialized, asn1Spec=_SubjectPublicKeyInfo() + ) + assert not remaining + # the univ.BitString object is a tuple of bits. We need bytes and + # pyasn1 really doesn't want to give them to us. To get it we'll + # build an integer and convert that to bytes. + bits = 0 + for bit in spki.getComponentByName("subjectPublicKey"): + bits = bits << 1 | bit + + data = utils.int_to_bytes(bits) + return hashlib.sha1(data).digest() + + +class DuplicateExtension(Exception): + def __init__(self, msg, oid): + super(DuplicateExtension, self).__init__(msg) + self.oid = oid + + +class UnsupportedExtension(Exception): + def __init__(self, msg, oid): + super(UnsupportedExtension, self).__init__(msg) + self.oid = oid + + +class ExtensionNotFound(Exception): + def __init__(self, msg, oid): + super(ExtensionNotFound, self).__init__(msg) + self.oid = oid + + +@six.add_metaclass(abc.ABCMeta) +class ExtensionType(object): + @abc.abstractproperty + def oid(self): + """ + Returns the oid associated with the given extension type. + """ + + +class Extensions(object): + def __init__(self, extensions): + self._extensions = extensions + + def get_extension_for_oid(self, oid): + for ext in self: + if ext.oid == oid: + return ext + + raise ExtensionNotFound("No {0} extension was found".format(oid), oid) + + def __iter__(self): + return iter(self._extensions) + + def __len__(self): + return len(self._extensions) + + +@utils.register_interface(ExtensionType) +class AuthorityKeyIdentifier(object): + oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER + + def __init__(self, key_identifier, authority_cert_issuer, + authority_cert_serial_number): + if authority_cert_issuer or authority_cert_serial_number: + if not authority_cert_issuer or not authority_cert_serial_number: + raise ValueError( + "authority_cert_issuer and authority_cert_serial_number " + "must both be present or both None" + ) + + 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( + "authority_cert_serial_number must be an integer" + ) + + self._key_identifier = key_identifier + self._authority_cert_issuer = authority_cert_issuer + self._authority_cert_serial_number = authority_cert_serial_number + + @classmethod + def from_issuer_public_key(cls, public_key): + digest = _key_identifier_from_public_key(public_key) + return cls( + key_identifier=digest, + authority_cert_issuer=None, + authority_cert_serial_number=None + ) + + def __repr__(self): + return ( + "<AuthorityKeyIdentifier(key_identifier={0.key_identifier!r}, " + "authority_cert_issuer={0.authority_cert_issuer}, " + "authority_cert_serial_number={0.authority_cert_serial_number}" + ")>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, AuthorityKeyIdentifier): + return NotImplemented + + return ( + self.key_identifier == other.key_identifier and + self.authority_cert_issuer == other.authority_cert_issuer and + self.authority_cert_serial_number == + other.authority_cert_serial_number + ) + + def __ne__(self, other): + return not self == other + + key_identifier = utils.read_only_property("_key_identifier") + authority_cert_issuer = utils.read_only_property("_authority_cert_issuer") + authority_cert_serial_number = utils.read_only_property( + "_authority_cert_serial_number" + ) + + +@utils.register_interface(ExtensionType) +class SubjectKeyIdentifier(object): + oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER + + def __init__(self, digest): + self._digest = digest + + @classmethod + def from_public_key(cls, public_key): + return cls(_key_identifier_from_public_key(public_key)) + + digest = utils.read_only_property("_digest") + + def __repr__(self): + return "<SubjectKeyIdentifier(digest={0!r})>".format(self.digest) + + def __eq__(self, other): + if not isinstance(other, SubjectKeyIdentifier): + return NotImplemented + + return ( + self.digest == other.digest + ) + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(ExtensionType) +class AuthorityInformationAccess(object): + oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS + + 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 == AuthorityInformationAccessOID.OCSP or + access_method == AuthorityInformationAccessOID.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") + + +@utils.register_interface(ExtensionType) +class BasicConstraints(object): + oid = ExtensionOID.BASIC_CONSTRAINTS + + def __init__(self, ca, path_length): + if not isinstance(ca, bool): + raise TypeError("ca must be a boolean value") + + if path_length is not None and not ca: + raise ValueError("path_length must be None when ca is False") + + if ( + path_length is not None and + (not isinstance(path_length, six.integer_types) or path_length < 0) + ): + raise TypeError( + "path_length must be a non-negative integer or None" + ) + + self._ca = ca + self._path_length = path_length + + ca = utils.read_only_property("_ca") + path_length = utils.read_only_property("_path_length") + + def __repr__(self): + return ("<BasicConstraints(ca={0.ca}, " + "path_length={0.path_length})>").format(self) + + def __eq__(self, other): + if not isinstance(other, BasicConstraints): + return NotImplemented + + return self.ca == other.ca and self.path_length == other.path_length + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(ExtensionType) +class CRLDistributionPoints(object): + oid = ExtensionOID.CRL_DISTRIBUTION_POINTS + + def __init__(self, distribution_points): + if not all( + isinstance(x, DistributionPoint) for x in distribution_points + ): + raise TypeError( + "distribution_points must be a list of DistributionPoint " + "objects" + ) + + self._distribution_points = distribution_points + + def __iter__(self): + return iter(self._distribution_points) + + def __len__(self): + return len(self._distribution_points) + + def __repr__(self): + return "<CRLDistributionPoints({0})>".format(self._distribution_points) + + def __eq__(self, other): + if not isinstance(other, CRLDistributionPoints): + return NotImplemented + + return self._distribution_points == other._distribution_points + + def __ne__(self, other): + return not self == other + + +class DistributionPoint(object): + def __init__(self, full_name, relative_name, reasons, crl_issuer): + if full_name and relative_name: + raise ValueError( + "You cannot provide both full_name and relative_name, at " + "least one must be None." + ) + + if full_name and not all( + isinstance(x, GeneralName) for x in full_name + ): + raise TypeError( + "full_name must be a list of GeneralName objects" + ) + + if relative_name and not isinstance(relative_name, Name): + raise TypeError("relative_name must be a Name") + + if crl_issuer and not all( + isinstance(x, GeneralName) for x in crl_issuer + ): + raise TypeError( + "crl_issuer must be None or a list of general names" + ) + + if reasons and (not isinstance(reasons, frozenset) or not all( + isinstance(x, ReasonFlags) for x in reasons + )): + raise TypeError("reasons must be None or frozenset of ReasonFlags") + + if reasons and ( + ReasonFlags.unspecified in reasons or + ReasonFlags.remove_from_crl in reasons + ): + raise ValueError( + "unspecified and remove_from_crl are not valid reasons in a " + "DistributionPoint" + ) + + if reasons and not crl_issuer and not (full_name or relative_name): + raise ValueError( + "You must supply crl_issuer, full_name, or relative_name when " + "reasons is not None" + ) + + self._full_name = full_name + self._relative_name = relative_name + self._reasons = reasons + self._crl_issuer = crl_issuer + + def __repr__(self): + return ( + "<DistributionPoint(full_name={0.full_name}, relative_name={0.rela" + "tive_name}, reasons={0.reasons}, crl_issuer={0.crl_is" + "suer})>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, DistributionPoint): + return NotImplemented + + return ( + self.full_name == other.full_name and + self.relative_name == other.relative_name and + self.reasons == other.reasons and + self.crl_issuer == other.crl_issuer + ) + + def __ne__(self, other): + return not self == other + + full_name = utils.read_only_property("_full_name") + relative_name = utils.read_only_property("_relative_name") + reasons = utils.read_only_property("_reasons") + crl_issuer = utils.read_only_property("_crl_issuer") + + +class ReasonFlags(Enum): + unspecified = "unspecified" + key_compromise = "keyCompromise" + ca_compromise = "cACompromise" + affiliation_changed = "affiliationChanged" + superseded = "superseded" + cessation_of_operation = "cessationOfOperation" + certificate_hold = "certificateHold" + privilege_withdrawn = "privilegeWithdrawn" + aa_compromise = "aACompromise" + remove_from_crl = "removeFromCRL" + + +@utils.register_interface(ExtensionType) +class CertificatePolicies(object): + oid = ExtensionOID.CERTIFICATE_POLICIES + + def __init__(self, policies): + if not all(isinstance(x, PolicyInformation) for x in policies): + raise TypeError( + "Every item in the policies list must be a " + "PolicyInformation" + ) + + self._policies = policies + + def __iter__(self): + return iter(self._policies) + + def __len__(self): + return len(self._policies) + + def __repr__(self): + return "<CertificatePolicies({0})>".format(self._policies) + + def __eq__(self, other): + if not isinstance(other, CertificatePolicies): + return NotImplemented + + return self._policies == other._policies + + def __ne__(self, other): + return not self == other + + +class PolicyInformation(object): + def __init__(self, policy_identifier, policy_qualifiers): + if not isinstance(policy_identifier, ObjectIdentifier): + raise TypeError("policy_identifier must be an ObjectIdentifier") + + self._policy_identifier = policy_identifier + if policy_qualifiers and not all( + isinstance( + x, (six.text_type, UserNotice) + ) for x in policy_qualifiers + ): + raise TypeError( + "policy_qualifiers must be a list of strings and/or UserNotice" + " objects or None" + ) + + self._policy_qualifiers = policy_qualifiers + + def __repr__(self): + return ( + "<PolicyInformation(policy_identifier={0.policy_identifier}, polic" + "y_qualifiers={0.policy_qualifiers})>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, PolicyInformation): + return NotImplemented + + return ( + self.policy_identifier == other.policy_identifier and + self.policy_qualifiers == other.policy_qualifiers + ) + + def __ne__(self, other): + return not self == other + + policy_identifier = utils.read_only_property("_policy_identifier") + policy_qualifiers = utils.read_only_property("_policy_qualifiers") + + +class UserNotice(object): + def __init__(self, notice_reference, explicit_text): + if notice_reference and not isinstance( + notice_reference, NoticeReference + ): + raise TypeError( + "notice_reference must be None or a NoticeReference" + ) + + self._notice_reference = notice_reference + self._explicit_text = explicit_text + + def __repr__(self): + return ( + "<UserNotice(notice_reference={0.notice_reference}, explicit_text=" + "{0.explicit_text!r})>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, UserNotice): + return NotImplemented + + return ( + self.notice_reference == other.notice_reference and + self.explicit_text == other.explicit_text + ) + + def __ne__(self, other): + return not self == other + + notice_reference = utils.read_only_property("_notice_reference") + explicit_text = utils.read_only_property("_explicit_text") + + +class NoticeReference(object): + def __init__(self, organization, notice_numbers): + self._organization = organization + if not isinstance(notice_numbers, list) or not all( + isinstance(x, int) for x in notice_numbers + ): + raise TypeError( + "notice_numbers must be a list of integers" + ) + + self._notice_numbers = notice_numbers + + def __repr__(self): + return ( + "<NoticeReference(organization={0.organization!r}, notice_numbers=" + "{0.notice_numbers})>".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, NoticeReference): + return NotImplemented + + return ( + self.organization == other.organization and + self.notice_numbers == other.notice_numbers + ) + + def __ne__(self, other): + return not self == other + + organization = utils.read_only_property("_organization") + notice_numbers = utils.read_only_property("_notice_numbers") + + +@utils.register_interface(ExtensionType) +class ExtendedKeyUsage(object): + oid = ExtensionOID.EXTENDED_KEY_USAGE + + def __init__(self, usages): + 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 + + def __iter__(self): + return iter(self._usages) + + def __len__(self): + return len(self._usages) + + 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 + + +@utils.register_interface(ExtensionType) +class OCSPNoCheck(object): + oid = ExtensionOID.OCSP_NO_CHECK + + +@utils.register_interface(ExtensionType) +class InhibitAnyPolicy(object): + oid = ExtensionOID.INHIBIT_ANY_POLICY + + def __init__(self, skip_certs): + if not isinstance(skip_certs, six.integer_types): + raise TypeError("skip_certs must be an integer") + + if skip_certs < 0: + raise ValueError("skip_certs must be a non-negative integer") + + self._skip_certs = skip_certs + + def __repr__(self): + return "<InhibitAnyPolicy(skip_certs={0.skip_certs})>".format(self) + + def __eq__(self, other): + if not isinstance(other, InhibitAnyPolicy): + return NotImplemented + + return self.skip_certs == other.skip_certs + + def __ne__(self, other): + return not self == other + + skip_certs = utils.read_only_property("_skip_certs") + + +@utils.register_interface(ExtensionType) +class KeyUsage(object): + oid = ExtensionOID.KEY_USAGE + + def __init__(self, digital_signature, content_commitment, key_encipherment, + data_encipherment, key_agreement, key_cert_sign, crl_sign, + encipher_only, decipher_only): + if not key_agreement and (encipher_only or decipher_only): + raise ValueError( + "encipher_only and decipher_only can only be true when " + "key_agreement is true" + ) + + self._digital_signature = digital_signature + self._content_commitment = content_commitment + self._key_encipherment = key_encipherment + self._data_encipherment = data_encipherment + self._key_agreement = key_agreement + self._key_cert_sign = key_cert_sign + self._crl_sign = crl_sign + self._encipher_only = encipher_only + self._decipher_only = decipher_only + + digital_signature = utils.read_only_property("_digital_signature") + content_commitment = utils.read_only_property("_content_commitment") + key_encipherment = utils.read_only_property("_key_encipherment") + data_encipherment = utils.read_only_property("_data_encipherment") + key_agreement = utils.read_only_property("_key_agreement") + key_cert_sign = utils.read_only_property("_key_cert_sign") + crl_sign = utils.read_only_property("_crl_sign") + + @property + def encipher_only(self): + if not self.key_agreement: + raise ValueError( + "encipher_only is undefined unless key_agreement is true" + ) + else: + return self._encipher_only + + @property + def decipher_only(self): + if not self.key_agreement: + raise ValueError( + "decipher_only is undefined unless key_agreement is true" + ) + else: + return self._decipher_only + + def __repr__(self): + try: + encipher_only = self.encipher_only + decipher_only = self.decipher_only + except ValueError: + encipher_only = None + decipher_only = None + + return ("<KeyUsage(digital_signature={0.digital_signature}, " + "content_commitment={0.content_commitment}, " + "key_encipherment={0.key_encipherment}, " + "data_encipherment={0.data_encipherment}, " + "key_agreement={0.key_agreement}, " + "key_cert_sign={0.key_cert_sign}, crl_sign={0.crl_sign}, " + "encipher_only={1}, decipher_only={2})>").format( + self, encipher_only, decipher_only) + + def __eq__(self, other): + if not isinstance(other, KeyUsage): + return NotImplemented + + return ( + self.digital_signature == other.digital_signature and + self.content_commitment == other.content_commitment and + self.key_encipherment == other.key_encipherment and + self.data_encipherment == other.data_encipherment and + self.key_agreement == other.key_agreement and + self.key_cert_sign == other.key_cert_sign and + self.crl_sign == other.crl_sign and + self._encipher_only == other._encipher_only and + self._decipher_only == other._decipher_only + ) + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(ExtensionType) +class NameConstraints(object): + oid = ExtensionOID.NAME_CONSTRAINTS + + def __init__(self, permitted_subtrees, excluded_subtrees): + if permitted_subtrees is not None: + if not all( + isinstance(x, GeneralName) for x in permitted_subtrees + ): + raise TypeError( + "permitted_subtrees must be a list of GeneralName objects " + "or None" + ) + + self._validate_ip_name(permitted_subtrees) + + if excluded_subtrees is not None: + if not all( + isinstance(x, GeneralName) for x in excluded_subtrees + ): + raise TypeError( + "excluded_subtrees must be a list of GeneralName objects " + "or None" + ) + + self._validate_ip_name(excluded_subtrees) + + if permitted_subtrees is None and excluded_subtrees is None: + raise ValueError( + "At least one of permitted_subtrees and excluded_subtrees " + "must not be None" + ) + + self._permitted_subtrees = permitted_subtrees + self._excluded_subtrees = excluded_subtrees + + def __eq__(self, other): + if not isinstance(other, NameConstraints): + return NotImplemented + + return ( + self.excluded_subtrees == other.excluded_subtrees and + self.permitted_subtrees == other.permitted_subtrees + ) + + def __ne__(self, other): + return not self == other + + def _validate_ip_name(self, tree): + if any(isinstance(name, IPAddress) and not isinstance( + name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network) + ) for name in tree): + raise TypeError( + "IPAddress name constraints must be an IPv4Network or" + " IPv6Network object" + ) + + def __repr__(self): + return ( + u"<NameConstraints(permitted_subtrees={0.permitted_subtrees}, " + u"excluded_subtrees={0.excluded_subtrees})>".format(self) + ) + + permitted_subtrees = utils.read_only_property("_permitted_subtrees") + excluded_subtrees = utils.read_only_property("_excluded_subtrees") + + +class Extension(object): + def __init__(self, oid, critical, value): + if not isinstance(oid, ObjectIdentifier): + raise TypeError( + "oid argument must be an ObjectIdentifier instance." + ) + + if not isinstance(critical, bool): + raise TypeError("critical must be a boolean value") + + self._oid = oid + self._critical = critical + self._value = value + + oid = utils.read_only_property("_oid") + critical = utils.read_only_property("_critical") + value = utils.read_only_property("_value") + + def __repr__(self): + return ("<Extension(oid={0.oid}, critical={0.critical}, " + "value={0.value})>").format(self) + + def __eq__(self, other): + if not isinstance(other, Extension): + return NotImplemented + + return ( + self.oid == other.oid and + self.critical == other.critical and + self.value == other.value + ) + + def __ne__(self, other): + return not self == other + + +class GeneralNames(object): + def __init__(self, general_names): + if not all(isinstance(x, GeneralName) for x in general_names): + raise TypeError( + "Every item in the general_names list must be an " + "object conforming to the GeneralName interface" + ) + + self._general_names = general_names + + def __iter__(self): + return iter(self._general_names) + + def __len__(self): + return len(self._general_names) + + def get_values_for_type(self, type): + # Return the value of each GeneralName, except for OtherName instances + # which we return directly because it has two important properties not + # just one value. + objs = (i for i in self if isinstance(i, type)) + if type != OtherName: + objs = (i.value for i in objs) + return list(objs) + + def __repr__(self): + return "<GeneralNames({0})>".format(self._general_names) + + def __eq__(self, other): + if not isinstance(other, GeneralNames): + return NotImplemented + + return self._general_names == other._general_names + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(ExtensionType) +class SubjectAlternativeName(object): + oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME + + def __init__(self, general_names): + self._general_names = GeneralNames(general_names) + + def __iter__(self): + return iter(self._general_names) + + def __len__(self): + return len(self._general_names) + + def get_values_for_type(self, type): + return self._general_names.get_values_for_type(type) + + def __repr__(self): + return "<SubjectAlternativeName({0})>".format(self._general_names) + + def __eq__(self, other): + if not isinstance(other, SubjectAlternativeName): + return NotImplemented + + return self._general_names == other._general_names + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(ExtensionType) +class IssuerAlternativeName(object): + oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME + + def __init__(self, general_names): + self._general_names = GeneralNames(general_names) + + def __iter__(self): + return iter(self._general_names) + + def __len__(self): + return len(self._general_names) + + def get_values_for_type(self, type): + return self._general_names.get_values_for_type(type) + + def __repr__(self): + return "<IssuerAlternativeName({0})>".format(self._general_names) + + def __eq__(self, other): + if not isinstance(other, IssuerAlternativeName): + return NotImplemented + + return self._general_names == other._general_names + + def __ne__(self, other): + return not self == other diff --git a/src/cryptography/x509/oid.py b/src/cryptography/x509/oid.py index 911343e3..9fabab72 100644 --- a/src/cryptography/x509/oid.py +++ b/src/cryptography/x509/oid.py @@ -54,9 +54,10 @@ class ExtensionOID(object): OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5") -OID_CRL_REASON = ObjectIdentifier("2.5.29.21") -OID_INVALIDITY_DATE = ObjectIdentifier("2.5.29.24") -OID_CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29") +class CRLExtensionOID(object): + CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29") + CRL_REASON = ObjectIdentifier("2.5.29.21") + INVALIDITY_DATE = ObjectIdentifier("2.5.29.24") class NameOID(object): @@ -110,19 +111,25 @@ _SIG_OIDS_TO_HASH = { SignatureAlgorithmOID.DSA_WITH_SHA256.dotted_string: hashes.SHA256() } -OID_SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1") -OID_CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2") -OID_CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3") -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") +class ExtendedKeyUsageOID(object): + SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1") + CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2") + CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3") + EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4") + TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8") + OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9") -OID_CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1") -OID_CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2") -OID_ANY_POLICY = ObjectIdentifier("2.5.29.32.0") + +class AuthorityInformationAccessOID(object): + CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2") + OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1") + + +class CertificatePoliciesOID(object): + CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1") + CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2") + ANY_POLICY = ObjectIdentifier("2.5.29.32.0") _OID_NAMES = { NameOID.COMMON_NAME: "commonName", @@ -154,21 +161,21 @@ _OID_NAMES = { SignatureAlgorithmOID.DSA_WITH_SHA1: "dsa-with-sha1", SignatureAlgorithmOID.DSA_WITH_SHA224: "dsa-with-sha224", SignatureAlgorithmOID.DSA_WITH_SHA256: "dsa-with-sha256", - OID_SERVER_AUTH: "serverAuth", - OID_CLIENT_AUTH: "clientAuth", - OID_CODE_SIGNING: "codeSigning", - OID_EMAIL_PROTECTION: "emailProtection", - OID_TIME_STAMPING: "timeStamping", - OID_OCSP_SIGNING: "OCSPSigning", + ExtendedKeyUsageOID.SERVER_AUTH: "serverAuth", + ExtendedKeyUsageOID.CLIENT_AUTH: "clientAuth", + ExtendedKeyUsageOID.CODE_SIGNING: "codeSigning", + ExtendedKeyUsageOID.EMAIL_PROTECTION: "emailProtection", + ExtendedKeyUsageOID.TIME_STAMPING: "timeStamping", + ExtendedKeyUsageOID.OCSP_SIGNING: "OCSPSigning", ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES: "subjectDirectoryAttributes", ExtensionOID.SUBJECT_KEY_IDENTIFIER: "subjectKeyIdentifier", ExtensionOID.KEY_USAGE: "keyUsage", ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName", ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName", ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints", - OID_CRL_REASON: "cRLReason", - OID_INVALIDITY_DATE: "invalidityDate", - OID_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", @@ -181,8 +188,8 @@ _OID_NAMES = { ExtensionOID.AUTHORITY_INFORMATION_ACCESS: "authorityInfoAccess", ExtensionOID.SUBJECT_INFORMATION_ACCESS: "subjectInfoAccess", ExtensionOID.OCSP_NO_CHECK: "OCSPNoCheck", - OID_OCSP: "OCSP", - OID_CA_ISSUERS: "caIssuers", - OID_CPS_QUALIFIER: "id-qt-cps", - OID_CPS_USER_NOTICE: "id-qt-unotice", + AuthorityInformationAccessOID.OCSP: "OCSP", + AuthorityInformationAccessOID.CA_ISSUERS: "caIssuers", + CertificatePoliciesOID.CPS_QUALIFIER: "id-qt-cps", + CertificatePoliciesOID.CPS_USER_NOTICE: "id-qt-unotice", } |