From 7e8fe9df4328f0d3134a502b5d3bc05435de7e6e Mon Sep 17 00:00:00 2001 From: Paul Kehrer Date: Mon, 18 May 2015 09:53:47 -0700 Subject: add policy constraints class --- docs/x509/reference.rst | 40 +++++++++++++++++++++++++++ src/cryptography/x509/extensions.py | 54 +++++++++++++++++++++++++++++++++++++ tests/test_x509_ext.py | 35 ++++++++++++++++++++++++ 3 files changed, 129 insertions(+) diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst index 8bb3f40d..14fc37c8 100644 --- a/docs/x509/reference.rst +++ b/docs/x509/reference.rst @@ -1860,6 +1860,40 @@ X.509 Extensions :type: int +.. class:: PolicyConstraints + + .. versionadded:: 1.3 + + The policy constraints extension can be used in certificates issued + to CAs. The policy constraints extension constrains path validation + in two ways. It can be used to prohibit policy mapping or require + that each certificate in a path contain an acceptable policy + identifier. For more information about the use of this extension see + :rfc:`5280`. + + .. attribute:: require_explicit_policy + + :type: int or None + + If this field is present, the value indicates the number of additional + certificates that may appear in the path before an explicit policy is + required for the entire path. When an explicit policy is required, it + is necessary for all certificates in the path to contain an acceptable + policy identifier in the certificate policies extension. An + acceptable policy identifier is the identifier of a policy required + by the user of the certification path or the identifier of a policy + that has been declared equivalent through policy mapping. + + .. attribute:: inhibit_policy_mapping + + :type: int or None + + If this field is present, the value indicates the number of additional + certificates that may appear in the path before policy mapping is no + longer permitted. For example, a value of one indicates that policy + mapping may be processed in certificates issued by the subject of this + certificate, but not in additional certificates in the path. + .. class:: CRLNumber(crl_number) .. versionadded:: 1.2 @@ -2392,6 +2426,12 @@ instances. The following common OIDs are available as constants. the ``CRLNumber`` extension type. This extension only has meaning for certificate revocation lists. + .. attribute:: POLICY_CONSTRAINTS + + Corresponds to the dotted string ``"2.5.29.36"``. The identifier for the + :class:`PolicyConstraints` extension type. + + .. class:: CRLEntryExtensionOID .. versionadded:: 1.2 diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py index db55789e..ba9cb373 100644 --- a/src/cryptography/x509/extensions.py +++ b/src/cryptography/x509/extensions.py @@ -489,6 +489,60 @@ class ReasonFlags(Enum): remove_from_crl = "removeFromCRL" +@utils.register_interface(ExtensionType) +class PolicyConstraints(object): + def __init__(self, require_explicit_policy, inhibit_policy_mapping): + if require_explicit_policy is not None and not isinstance( + require_explicit_policy, six.integer_types + ): + raise TypeError( + "require_explicit_policy must be a non-negative integer or " + "None" + ) + + if inhibit_policy_mapping is not None and not isinstance( + inhibit_policy_mapping, six.integer_types + ): + raise TypeError( + "inhibit_policy_mapping must be a non-negative integer or None" + ) + + if inhibit_policy_mapping is None and require_explicit_policy is None: + raise ValueError( + "At least one of require_explicit_policy and " + "inhibit_policy_mapping must not be None" + ) + + self._require_explicit_policy = require_explicit_policy + self._inhibit_policy_mapping = inhibit_policy_mapping + + def __repr__(self): + return ( + u"".format(self) + ) + + def __eq__(self, other): + if not isinstance(other, PolicyConstraints): + return NotImplemented + + return ( + self.require_explicit_policy == other.require_explicit_policy and + self.inhibit_policy_mapping == other.inhibit_policy_mapping + ) + + def __ne__(self, other): + return not self == other + + require_explicit_policy = utils.read_only_property( + "_require_explicit_policy" + ) + inhibit_policy_mapping = utils.read_only_property( + "_inhibit_policy_mapping" + ) + + @utils.register_interface(ExtensionType) class CertificatePolicies(object): oid = ExtensionOID.CERTIFICATE_POLICIES diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py index d8a5f9de..ceb11dfe 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -2245,6 +2245,41 @@ class TestAccessDescription(object): assert hash(ad) != hash(ad3) +class TestPolicyConstraints(object): + def test_invalid_explicit_policy(self): + with pytest.raises(TypeError): + x509.PolicyConstraints("invalid", None) + + def test_invalid_inhibit_policy(self): + with pytest.raises(TypeError): + x509.PolicyConstraints(None, "invalid") + + def test_both_none(self): + with pytest.raises(ValueError): + x509.PolicyConstraints(None, None) + + def test_repr(self): + pc = x509.PolicyConstraints(0, None) + + assert repr(pc) == ( + u"" + ) + + def test_eq(self): + pc = x509.PolicyConstraints(2, 1) + pc2 = x509.PolicyConstraints(2, 1) + assert pc == pc2 + + def test_ne(self): + pc = x509.PolicyConstraints(2, 1) + pc2 = x509.PolicyConstraints(2, 2) + pc3 = x509.PolicyConstraints(3, 1) + assert pc != pc2 + assert pc != pc3 + assert pc != object() + + class TestAuthorityInformationAccess(object): def test_invalid_descriptions(self): with pytest.raises(TypeError): -- cgit v1.2.3