diff options
-rw-r--r-- | docs/x509.rst | 43 | ||||
-rw-r--r-- | src/cryptography/x509.py | 46 | ||||
-rw-r--r-- | tests/test_x509_ext.py | 40 |
3 files changed, 129 insertions, 0 deletions
diff --git a/docs/x509.rst b/docs/x509.rst index 27f1d544..89265df2 100644 --- a/docs/x509.rst +++ b/docs/x509.rst @@ -273,6 +273,49 @@ X.509 Certificate Object The dotted string value of the OID (e.g. ``"2.5.4.3"``) +X.509 Extensions +~~~~~~~~~~~~~~~~ + +.. class:: Extension + + .. versionadded:: 0.9 + + All X.509 extensions are registered against this interface. + + .. attribute:: critical + + :type: bool + + Determines whether a given extension is critical or not. + +.. class:: BasicConstraints + + .. versionadded:: 0.9 + + Basic constraints is an X.509 extension that defines whether a given + certificate is allowed to sign additional certificates and what path + length restrictions may exist. + + .. attribute:: ca + + :type: bool + + Whether the certificate can sign certificates. + + .. attribute:: path_length + + :type: int, None + + The maximum path length for certificates subordinate to this + certificate. This attribute only has meaning if ``ca`` is true. + If ``ca`` is true then a path length of None means there's no + restriction on the number of subordinate CAs in the certificate chain. + If it is zero or greater then that number defines the maximum length. + For example, a ``path_length`` of 1 means the certificate can sign a + subordinate CA, but the subordinate CA is not allowed to create + ``ca`` true certificates. + + Object Identifiers ~~~~~~~~~~~~~~~~~~ diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index ad7ebbe0..c053dd61 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -141,6 +141,52 @@ class Name(object): return len(self._attributes) +OID_BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19") + + +@six.add_metaclass(abc.ABCMeta) +class Extension(object): + @abc.abstractproperty + def critical(self): + """ + Returns the boolean value of the critical extension field. + """ + + +@utils.register_interface(Extension) +class BasicConstraints(object): + oid = OID_BASIC_CONSTRAINTS + + def __init__(self, ca, path_length, critical): + if not isinstance(ca, bool): + raise TypeError("ca must be a boolean value") + + if not isinstance(critical, bool): + raise TypeError("critical must be a boolean value") + + if path_length is not None and ca is False: + raise ValueError("path_length must be None when ca is False") + + if path_length is not None and (not isinstance(path_length, int) + or path_length < 0): + raise TypeError( + "path_length must be a non-negative integer or None" + ) + + self._ca = ca + self._path_length = path_length + self._critical = critical + + ca = utils.read_only_property("_ca") + path_length = utils.read_only_property("_path_length") + critical = utils.read_only_property("_critical") + + def __repr__(self): + return "<BasicConstraints(ca={}, path_length={}, critical={})>".format( + self.ca, self.path_length, self.critical + ) + + OID_COMMON_NAME = ObjectIdentifier("2.5.4.3") OID_COUNTRY_NAME = ObjectIdentifier("2.5.4.6") OID_LOCALITY_NAME = ObjectIdentifier("2.5.4.7") diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py new file mode 100644 index 00000000..9fde1be1 --- /dev/null +++ b/tests/test_x509_ext.py @@ -0,0 +1,40 @@ +# 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 pytest + +from cryptography import x509 + + +class TestBasicConstraints(object): + def test_ca_not_boolean(self): + with pytest.raises(TypeError): + x509.BasicConstraints("notbool", None, False) + + def test_critical_not_boolean(self): + with pytest.raises(TypeError): + x509.BasicConstraints(False, None, "notbool") + + def test_path_length_not_ca(self): + with pytest.raises(ValueError): + x509.BasicConstraints(False, 0, True) + + def test_path_length_not_int(self): + with pytest.raises(TypeError): + x509.BasicConstraints(True, 1.1, True) + + with pytest.raises(TypeError): + x509.BasicConstraints(True, "notint", True) + + def test_path_length_negative(self): + with pytest.raises(TypeError): + x509.BasicConstraints(True, -1, True) + + def test_repr(self): + na = x509.BasicConstraints(True, None, True) + assert repr(na) == ( + "<BasicConstraints(ca=True, path_length=None, critical=True)>" + ) |