aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2015-06-21 22:32:59 -0400
committerAlex Gaynor <alex.gaynor@gmail.com>2015-06-21 22:32:59 -0400
commit9fd9c0c791b5418a919456dd4183a7fd9cdbc919 (patch)
tree61e093911cc23253cb52b15f066f63c186b231d2
parentd845ea04b86568e544106207636aa3a47ab82170 (diff)
parente0017be396df1a506b92ec1b669086dd02ca25b8 (diff)
downloadcryptography-9fd9c0c791b5418a919456dd4183a7fd9cdbc919.tar.gz
cryptography-9fd9c0c791b5418a919456dd4183a7fd9cdbc919.tar.bz2
cryptography-9fd9c0c791b5418a919456dd4183a7fd9cdbc919.zip
Merge pull request #1974 from reaperhulk/name-constraints
add nameconstraints classes
-rw-r--r--docs/x509.rst31
-rw-r--r--src/cryptography/x509.py52
-rw-r--r--tests/test_x509_ext.py68
3 files changed, 151 insertions, 0 deletions
diff --git a/docs/x509.rst b/docs/x509.rst
index ed7b8716..1e4efb4c 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -814,6 +814,32 @@ X.509 Extensions
extension is only relevant when the certificate is an authorized OCSP
responder.
+.. class:: NameConstraints
+
+ .. versionadded:: 1.0
+
+ The name constraints extension, which only has meaning in a CA certificate,
+ defines a name space within which all subject names in certificates issued
+ beneath the CA certificate must (or must not) be in. For specific details
+ on the way this extension should be processed see :rfc:`5280`.
+
+ .. attribute:: permitted_subtrees
+
+ :type: list of :class:`GeneralName` objects or None
+
+ The set of permitted name patterns. If a name matches this and an
+ element in ``excluded_subtrees`` it is invalid. At least one of
+ ``permitted_subtrees`` and ``excluded_subtrees`` will be non-None.
+
+ .. attribute:: excluded_subtrees
+
+ :type: list of :class:`GeneralName` objects or None
+
+ Any name matching a restriction in the ``excluded_subtrees`` field is
+ invalid regardless of information appearing in the
+ ``permitted_subtrees``. At least one of ``permitted_subtrees`` and
+ ``excluded_subtrees`` will be non-None.
+
.. class:: AuthorityKeyIdentifier
.. versionadded:: 0.9
@@ -1369,6 +1395,11 @@ Extension OIDs
Corresponds to the dotted string ``"2.5.29.14"``. The identifier for the
:class:`SubjectKeyIdentifier` extension type.
+.. data:: OID_NAME_CONSTRAINTS
+
+ Corresponds to the dotted string ``"2.5.29.30"``. The identifier for the
+ :class:`NameConstraints` extension type.
+
.. data:: OID_CRL_DISTRIBUTION_POINTS
Corresponds to the dotted string ``"2.5.29.31"``. The identifier for the
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 2e2e8512..4dbe3da1 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -676,6 +676,58 @@ class SubjectKeyIdentifier(object):
return not self == other
+class NameConstraints(object):
+ 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 _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 CRLDistributionPoints(object):
def __init__(self, distribution_points):
if not all(
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 62d9f83d..a5747c37 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -1905,6 +1905,74 @@ class TestAuthorityKeyIdentifierExtension(object):
assert ext.value.authority_cert_serial_number == 3
+class TestNameConstraints(object):
+ def test_ipaddress_wrong_type(self):
+ with pytest.raises(TypeError):
+ x509.NameConstraints(
+ permitted_subtrees=[
+ x509.IPAddress(ipaddress.IPv4Address(u"127.0.0.1"))
+ ],
+ excluded_subtrees=None
+ )
+
+ with pytest.raises(TypeError):
+ x509.NameConstraints(
+ permitted_subtrees=None,
+ excluded_subtrees=[
+ x509.IPAddress(ipaddress.IPv4Address(u"127.0.0.1"))
+ ]
+ )
+
+ def test_ipaddress_allowed_type(self):
+ permitted = [x509.IPAddress(ipaddress.IPv4Network(u"192.168.0.0/29"))]
+ excluded = [x509.IPAddress(ipaddress.IPv4Network(u"10.10.0.0/24"))]
+ nc = x509.NameConstraints(
+ permitted_subtrees=permitted,
+ excluded_subtrees=excluded
+ )
+ assert nc.permitted_subtrees == permitted
+ assert nc.excluded_subtrees == excluded
+
+ def test_invalid_permitted_subtrees(self):
+ with pytest.raises(TypeError):
+ x509.NameConstraints("badpermitted", None)
+
+ def test_invalid_excluded_subtrees(self):
+ with pytest.raises(TypeError):
+ x509.NameConstraints(None, "badexcluded")
+
+ def test_no_subtrees(self):
+ with pytest.raises(ValueError):
+ x509.NameConstraints(None, None)
+
+ def test_permitted_none(self):
+ excluded = [x509.DNSName(u"name.local")]
+ nc = x509.NameConstraints(
+ permitted_subtrees=None, excluded_subtrees=excluded
+ )
+ assert nc.permitted_subtrees is None
+ assert nc.excluded_subtrees is not None
+
+ def test_excluded_none(self):
+ permitted = [x509.DNSName(u"name.local")]
+ nc = x509.NameConstraints(
+ permitted_subtrees=permitted, excluded_subtrees=None
+ )
+ assert nc.permitted_subtrees is not None
+ assert nc.excluded_subtrees is None
+
+ def test_repr(self):
+ permitted = [x509.DNSName(u"name.local"), x509.DNSName(u"name2.local")]
+ nc = x509.NameConstraints(
+ permitted_subtrees=permitted,
+ excluded_subtrees=None
+ )
+ assert repr(nc) == (
+ "<NameConstraints(permitted_subtrees=[<DNSName(value=name.local)>"
+ ", <DNSName(value=name2.local)>], excluded_subtrees=None)>"
+ )
+
+
class TestDistributionPoint(object):
def test_distribution_point_full_name_not_general_names(self):
with pytest.raises(TypeError):