Merge pull request #2264 from reaperhulk/split-extensions-4

move extension exceptions, NameConstraints, and KeyUsage
diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py
index 8e345ae..a6d376b 100644
--- a/src/cryptography/x509/__init__.py
+++ b/src/cryptography/x509/__init__.py
@@ -7,19 +7,19 @@
 from cryptography.x509.base import (
     Certificate, CertificateBuilder, CertificateRevocationList,
     CertificateSigningRequest, CertificateSigningRequestBuilder,
-    DuplicateExtension, Extension, ExtensionNotFound,
-    ExtensionType, Extensions, GeneralNames,
-    InvalidVersion, IssuerAlternativeName, KeyUsage, NameConstraints,
+    Extension, ExtensionType, GeneralNames,
+    InvalidVersion, IssuerAlternativeName,
     ObjectIdentifier, RevokedCertificate, SubjectAlternativeName,
-    UnsupportedExtension, Version, load_der_x509_certificate,
+    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, ExtendedKeyUsage,
-    InhibitAnyPolicy, NoticeReference, OCSPNoCheck, PolicyInformation,
-    ReasonFlags, SubjectKeyIdentifier, UserNotice
+    CertificatePolicies, DistributionPoint, DuplicateExtension,
+    ExtendedKeyUsage, ExtensionNotFound, Extensions, InhibitAnyPolicy,
+    KeyUsage, NameConstraints, NoticeReference, OCSPNoCheck, PolicyInformation,
+    ReasonFlags, SubjectKeyIdentifier, UnsupportedExtension, UserNotice
 )
 from cryptography.x509.general_name import (
     DNSName, DirectoryName, GeneralName, IPAddress, OtherName, RFC822Name,
diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py
index 2b4eeb5..312eea0 100644
--- a/src/cryptography/x509/base.py
+++ b/src/cryptography/x509/base.py
@@ -6,14 +6,13 @@
 
 import abc
 import datetime
-import ipaddress
 from enum import Enum
 
 import six
 
 from cryptography import utils
 from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
-from cryptography.x509.general_name import GeneralName, IPAddress, OtherName
+from cryptography.x509.general_name import GeneralName, OtherName
 from cryptography.x509.name import Name
 from cryptography.x509.oid import (
     ExtensionOID, ObjectIdentifier
@@ -50,42 +49,6 @@
         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):
@@ -131,159 +94,6 @@
         """
 
 
-@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 GeneralNames(object):
     def __init__(self, general_names):
         if not all(isinstance(x, GeneralName) for x in general_names):
diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py
index f227dfe..92a3735 100644
--- a/src/cryptography/x509/extensions.py
+++ b/src/cryptography/x509/extensions.py
@@ -5,6 +5,7 @@
 from __future__ import absolute_import, division, print_function
 
 import hashlib
+import ipaddress
 from enum import Enum
 
 from pyasn1.codec.der import decoder
@@ -15,7 +16,7 @@
 from cryptography import utils
 from cryptography.hazmat.primitives import serialization
 from cryptography.x509.base import ExtensionType
-from cryptography.x509.general_name import GeneralName
+from cryptography.x509.general_name import GeneralName, IPAddress
 from cryptography.x509.name import Name
 from cryptography.x509.oid import (
     AuthorityInformationAccessOID, ExtensionOID, ObjectIdentifier
@@ -50,6 +51,42 @@
     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
+
+
+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
@@ -579,3 +616,156 @@
         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")