diff options
-rw-r--r-- | src/cryptography/x509/__init__.py | 22 | ||||
-rw-r--r-- | src/cryptography/x509/base.py | 239 | ||||
-rw-r--r-- | src/cryptography/x509/general_name.py | 265 |
3 files changed, 278 insertions, 248 deletions
diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py index 45923b31..04a94a37 100644 --- a/src/cryptography/x509/__init__.py +++ b/src/cryptography/x509/__init__.py @@ -8,19 +8,21 @@ from cryptography.x509.base import ( AccessDescription, AuthorityInformationAccess, AuthorityKeyIdentifier, BasicConstraints, CRLDistributionPoints, Certificate, CertificateBuilder, CertificatePolicies, CertificateRevocationList, CertificateSigningRequest, - CertificateSigningRequestBuilder, DNSName, DirectoryName, - DistributionPoint, DuplicateExtension, ExtendedKeyUsage, - Extension, ExtensionNotFound, ExtensionType, Extensions, GeneralName, - GeneralNames, IPAddress, InhibitAnyPolicy, InvalidVersion, - IssuerAlternativeName, KeyUsage, NameConstraints, - NoticeReference, OCSPNoCheck, ObjectIdentifier, OtherName, - PolicyInformation, RFC822Name, ReasonFlags, RegisteredID, + CertificateSigningRequestBuilder, DistributionPoint, + DuplicateExtension, ExtendedKeyUsage, Extension, ExtensionNotFound, + ExtensionType, Extensions, GeneralNames, InhibitAnyPolicy, + InvalidVersion, IssuerAlternativeName, KeyUsage, NameConstraints, + NoticeReference, OCSPNoCheck, ObjectIdentifier, + PolicyInformation, ReasonFlags, RevokedCertificate, SubjectAlternativeName, SubjectKeyIdentifier, - UniformResourceIdentifier, UnsupportedExtension, - UnsupportedGeneralNameType, UserNotice, Version, _GENERAL_NAMES, - load_der_x509_certificate, + UnsupportedExtension, UserNotice, Version, load_der_x509_certificate, load_der_x509_csr, load_pem_x509_certificate, load_pem_x509_csr, ) +from cryptography.x509.general_name import ( + DNSName, DirectoryName, GeneralName, IPAddress, OtherName, RFC822Name, + RegisteredID, UniformResourceIdentifier, UnsupportedGeneralNameType, + _GENERAL_NAMES +) from cryptography.x509.name import Name, NameAttribute from cryptography.x509.oid import ( OID_ANY_POLICY, OID_AUTHORITY_INFORMATION_ACCESS, diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py index 29e6e878..c10b338e 100644 --- a/src/cryptography/x509/base.py +++ b/src/cryptography/x509/base.py @@ -8,21 +8,17 @@ import abc import datetime import hashlib import ipaddress -from email.utils import parseaddr from enum import Enum -import idna - from pyasn1.codec.der import decoder from pyasn1.type import namedtype, univ import six -from six.moves import urllib_parse - 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.name import Name from cryptography.x509.oid import ( OID_AUTHORITY_INFORMATION_ACCESS, @@ -123,12 +119,6 @@ class ExtensionNotFound(Exception): self.oid = oid -class UnsupportedGeneralNameType(Exception): - def __init__(self, msg, type): - super(UnsupportedGeneralNameType, self).__init__(msg) - self.type = type - - class Extensions(object): def __init__(self, extensions): self._extensions = extensions @@ -807,233 +797,6 @@ class InhibitAnyPolicy(object): skip_certs = utils.read_only_property("_skip_certs") -@six.add_metaclass(abc.ABCMeta) -class GeneralName(object): - @abc.abstractproperty - def value(self): - """ - Return the value of the object - """ - - -@utils.register_interface(GeneralName) -class RFC822Name(object): - def __init__(self, value): - if not isinstance(value, six.text_type): - raise TypeError("value must be a unicode string") - - name, address = parseaddr(value) - parts = address.split(u"@") - if name or not address: - # parseaddr has found a name (e.g. Name <email>) 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 encoding needed since there is no domain component. - encoded = address.encode("ascii") - else: - # A normal email of the form user@domain.com. Let's attempt to - # encode the domain component and reconstruct the address. - encoded = parts[0].encode("ascii") + b"@" + idna.encode(parts[1]) - - self._value = value - self._encoded = encoded - - value = utils.read_only_property("_value") - - def __repr__(self): - return "<RFC822Name(value={0})>".format(self.value) - - def __eq__(self, other): - if not isinstance(other, RFC822Name): - return NotImplemented - - return self.value == other.value - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(GeneralName) -class DNSName(object): - def __init__(self, value): - if not isinstance(value, six.text_type): - raise TypeError("value must be a unicode string") - - self._value = value - - value = utils.read_only_property("_value") - - def __repr__(self): - return "<DNSName(value={0})>".format(self.value) - - def __eq__(self, other): - if not isinstance(other, DNSName): - return NotImplemented - - return self.value == other.value - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(GeneralName) -class UniformResourceIdentifier(object): - def __init__(self, value): - if not isinstance(value, six.text_type): - raise TypeError("value must be a unicode string") - - parsed = urllib_parse.urlparse(value) - if not parsed.hostname: - netloc = "" - elif parsed.port: - netloc = ( - idna.encode(parsed.hostname) + - ":{0}".format(parsed.port).encode("ascii") - ).decode("ascii") - else: - netloc = idna.encode(parsed.hostname).decode("ascii") - - # Note that building a URL in this fashion means it should be - # semantically indistinguishable from the original but is not - # guaranteed to be exactly the same. - uri = urllib_parse.urlunparse(( - parsed.scheme, - netloc, - parsed.path, - parsed.params, - parsed.query, - parsed.fragment - )).encode("ascii") - - self._value = value - self._encoded = uri - - value = utils.read_only_property("_value") - - def __repr__(self): - return "<UniformResourceIdentifier(value={0})>".format(self.value) - - def __eq__(self, other): - if not isinstance(other, UniformResourceIdentifier): - return NotImplemented - - return self.value == other.value - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(GeneralName) -class DirectoryName(object): - def __init__(self, value): - if not isinstance(value, Name): - raise TypeError("value must be a Name") - - self._value = value - - value = utils.read_only_property("_value") - - def __repr__(self): - return "<DirectoryName(value={0})>".format(self.value) - - def __eq__(self, other): - if not isinstance(other, DirectoryName): - return NotImplemented - - return self.value == other.value - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(GeneralName) -class RegisteredID(object): - def __init__(self, value): - if not isinstance(value, ObjectIdentifier): - raise TypeError("value must be an ObjectIdentifier") - - self._value = value - - value = utils.read_only_property("_value") - - def __repr__(self): - return "<RegisteredID(value={0})>".format(self.value) - - def __eq__(self, other): - if not isinstance(other, RegisteredID): - return NotImplemented - - return self.value == other.value - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(GeneralName) -class IPAddress(object): - def __init__(self, value): - if not isinstance( - value, - ( - ipaddress.IPv4Address, - ipaddress.IPv6Address, - ipaddress.IPv4Network, - ipaddress.IPv6Network - ) - ): - raise TypeError( - "value must be an instance of ipaddress.IPv4Address, " - "ipaddress.IPv6Address, ipaddress.IPv4Network, or " - "ipaddress.IPv6Network" - ) - - self._value = value - - value = utils.read_only_property("_value") - - def __repr__(self): - return "<IPAddress(value={0})>".format(self.value) - - def __eq__(self, other): - if not isinstance(other, IPAddress): - return NotImplemented - - return self.value == other.value - - def __ne__(self, other): - return not self == other - - -@utils.register_interface(GeneralName) -class OtherName(object): - def __init__(self, type_id, value): - if not isinstance(type_id, ObjectIdentifier): - raise TypeError("type_id must be an ObjectIdentifier") - if not isinstance(value, bytes): - raise TypeError("value must be a binary string") - - self._type_id = type_id - self._value = value - - type_id = utils.read_only_property("_type_id") - value = utils.read_only_property("_value") - - def __repr__(self): - return "<OtherName(type_id={0}, value={1!r})>".format( - self.type_id, self.value) - - def __eq__(self, other): - if not isinstance(other, OtherName): - return NotImplemented - - return self.type_id == other.type_id 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): diff --git a/src/cryptography/x509/general_name.py b/src/cryptography/x509/general_name.py new file mode 100644 index 00000000..f5bd30fb --- /dev/null +++ b/src/cryptography/x509/general_name.py @@ -0,0 +1,265 @@ +# 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 ipaddress +from email.utils import parseaddr + +import idna + +import six + +from six.moves import urllib_parse + +from cryptography import utils +from cryptography.x509.name import Name +from cryptography.x509.oid import ObjectIdentifier + + +_GENERAL_NAMES = { + 0: "otherName", + 1: "rfc822Name", + 2: "dNSName", + 3: "x400Address", + 4: "directoryName", + 5: "ediPartyName", + 6: "uniformResourceIdentifier", + 7: "iPAddress", + 8: "registeredID", +} + + +class UnsupportedGeneralNameType(Exception): + def __init__(self, msg, type): + super(UnsupportedGeneralNameType, self).__init__(msg) + self.type = type + + +@six.add_metaclass(abc.ABCMeta) +class GeneralName(object): + @abc.abstractproperty + def value(self): + """ + Return the value of the object + """ + + +@utils.register_interface(GeneralName) +class RFC822Name(object): + def __init__(self, value): + if not isinstance(value, six.text_type): + raise TypeError("value must be a unicode string") + + name, address = parseaddr(value) + parts = address.split(u"@") + if name or not address: + # parseaddr has found a name (e.g. Name <email>) 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 encoding needed since there is no domain component. + encoded = address.encode("ascii") + else: + # A normal email of the form user@domain.com. Let's attempt to + # encode the domain component and reconstruct the address. + encoded = parts[0].encode("ascii") + b"@" + idna.encode(parts[1]) + + self._value = value + self._encoded = encoded + + value = utils.read_only_property("_value") + + def __repr__(self): + return "<RFC822Name(value={0})>".format(self.value) + + def __eq__(self, other): + if not isinstance(other, RFC822Name): + return NotImplemented + + return self.value == other.value + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(GeneralName) +class DNSName(object): + def __init__(self, value): + if not isinstance(value, six.text_type): + raise TypeError("value must be a unicode string") + + self._value = value + + value = utils.read_only_property("_value") + + def __repr__(self): + return "<DNSName(value={0})>".format(self.value) + + def __eq__(self, other): + if not isinstance(other, DNSName): + return NotImplemented + + return self.value == other.value + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(GeneralName) +class UniformResourceIdentifier(object): + def __init__(self, value): + if not isinstance(value, six.text_type): + raise TypeError("value must be a unicode string") + + parsed = urllib_parse.urlparse(value) + if not parsed.hostname: + netloc = "" + elif parsed.port: + netloc = ( + idna.encode(parsed.hostname) + + ":{0}".format(parsed.port).encode("ascii") + ).decode("ascii") + else: + netloc = idna.encode(parsed.hostname).decode("ascii") + + # Note that building a URL in this fashion means it should be + # semantically indistinguishable from the original but is not + # guaranteed to be exactly the same. + uri = urllib_parse.urlunparse(( + parsed.scheme, + netloc, + parsed.path, + parsed.params, + parsed.query, + parsed.fragment + )).encode("ascii") + + self._value = value + self._encoded = uri + + value = utils.read_only_property("_value") + + def __repr__(self): + return "<UniformResourceIdentifier(value={0})>".format(self.value) + + def __eq__(self, other): + if not isinstance(other, UniformResourceIdentifier): + return NotImplemented + + return self.value == other.value + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(GeneralName) +class DirectoryName(object): + def __init__(self, value): + if not isinstance(value, Name): + raise TypeError("value must be a Name") + + self._value = value + + value = utils.read_only_property("_value") + + def __repr__(self): + return "<DirectoryName(value={0})>".format(self.value) + + def __eq__(self, other): + if not isinstance(other, DirectoryName): + return NotImplemented + + return self.value == other.value + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(GeneralName) +class RegisteredID(object): + def __init__(self, value): + if not isinstance(value, ObjectIdentifier): + raise TypeError("value must be an ObjectIdentifier") + + self._value = value + + value = utils.read_only_property("_value") + + def __repr__(self): + return "<RegisteredID(value={0})>".format(self.value) + + def __eq__(self, other): + if not isinstance(other, RegisteredID): + return NotImplemented + + return self.value == other.value + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(GeneralName) +class IPAddress(object): + def __init__(self, value): + if not isinstance( + value, + ( + ipaddress.IPv4Address, + ipaddress.IPv6Address, + ipaddress.IPv4Network, + ipaddress.IPv6Network + ) + ): + raise TypeError( + "value must be an instance of ipaddress.IPv4Address, " + "ipaddress.IPv6Address, ipaddress.IPv4Network, or " + "ipaddress.IPv6Network" + ) + + self._value = value + + value = utils.read_only_property("_value") + + def __repr__(self): + return "<IPAddress(value={0})>".format(self.value) + + def __eq__(self, other): + if not isinstance(other, IPAddress): + return NotImplemented + + return self.value == other.value + + def __ne__(self, other): + return not self == other + + +@utils.register_interface(GeneralName) +class OtherName(object): + def __init__(self, type_id, value): + if not isinstance(type_id, ObjectIdentifier): + raise TypeError("type_id must be an ObjectIdentifier") + if not isinstance(value, bytes): + raise TypeError("value must be a binary string") + + self._type_id = type_id + self._value = value + + type_id = utils.read_only_property("_type_id") + value = utils.read_only_property("_value") + + def __repr__(self): + return "<OtherName(type_id={0}, value={1!r})>".format( + self.type_id, self.value) + + def __eq__(self, other): + if not isinstance(other, OtherName): + return NotImplemented + + return self.type_id == other.type_id and self.value == other.value + + def __ne__(self, other): + return not self == other |