aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/development/test-vectors.rst3
-rw-r--r--docs/x509/reference.rst44
-rw-r--r--src/cryptography/x509/__init__.py8
-rw-r--r--src/cryptography/x509/extensions.py56
-rw-r--r--tests/test_x509_ext.py35
-rw-r--r--vectors/cryptography_vectors/x509/department-of-state-root.pem40
6 files changed, 183 insertions, 3 deletions
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index ad945f2f..d62fb93a 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -110,6 +110,9 @@ X.509
containing a SAN extension with an ``ediPartyName`` general name.
* ``san_x400address.der`` - A DSA certificate from a `Mozilla bug`_ containing
a SAN extension with an ``x400Address`` general name.
+* ``department-of-state-root.pem`` - The intermediary CA for the Department of
+ State, issued by the United States Federal Government's Common Policy CA.
+ Notably has a ``critical`` policy constraints extensions.
Custom X.509 Vectors
~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/x509/reference.rst b/docs/x509/reference.rst
index 8bb3f40d..529578ba 100644
--- a/docs/x509/reference.rst
+++ b/docs/x509/reference.rst
@@ -1860,6 +1860,44 @@ X.509 Extensions
:type: int
+.. class:: PolicyConstraints
+
+ .. versionadded:: 1.3
+
+ The policy constraints extension is used to inhibit policy mapping or
+ require that each certificate in a chain contain an acceptable policy
+ identifier. For more information about the use of this extension see
+ :rfc:`5280`.
+
+ .. attribute:: oid
+
+ :type: :class:`ObjectIdentifier`
+
+ Returns :attr:`~cryptography.x509.oid.ExtensionOID.POLICY_CONSTRAINTS`.
+
+ .. attribute:: require_explicit_policy
+
+ :type: int or None
+
+ If this field is not None, the value indicates the number of additional
+ certificates that may appear in the chain before an explicit policy is
+ required for the entire path. When an explicit policy is required, it
+ is necessary for all certificates in the chain 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 not None, the value indicates the number of additional
+ certificates that may appear in the chain 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 chain.
+
.. class:: CRLNumber(crl_number)
.. versionadded:: 1.2
@@ -2392,6 +2430,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:`~cryptography.x509.PolicyConstraints` extension type.
+
+
.. class:: CRLEntryExtensionOID
.. versionadded:: 1.2
diff --git a/src/cryptography/x509/__init__.py b/src/cryptography/x509/__init__.py
index 787f1a60..8d7bad27 100644
--- a/src/cryptography/x509/__init__.py
+++ b/src/cryptography/x509/__init__.py
@@ -20,9 +20,10 @@ from cryptography.x509.extensions import (
DistributionPoint, DuplicateExtension, ExtendedKeyUsage, Extension,
ExtensionNotFound, ExtensionType, Extensions, GeneralNames,
InhibitAnyPolicy, InvalidityDate, IssuerAlternativeName, KeyUsage,
- NameConstraints, NoticeReference, OCSPNoCheck, PolicyInformation,
- ReasonFlags, SubjectAlternativeName, SubjectKeyIdentifier,
- UnrecognizedExtension, UnsupportedExtension, UserNotice
+ NameConstraints, NoticeReference, OCSPNoCheck, PolicyConstraints,
+ PolicyInformation, ReasonFlags, SubjectAlternativeName,
+ SubjectKeyIdentifier, UnrecognizedExtension, UnsupportedExtension,
+ UserNotice
)
from cryptography.x509.general_name import (
DNSName, DirectoryName, GeneralName, IPAddress, OtherName, RFC822Name,
@@ -178,4 +179,5 @@ __all__ = [
"CRLReason",
"InvalidityDate",
"UnrecognizedExtension",
+ "PolicyConstraints",
]
diff --git a/src/cryptography/x509/extensions.py b/src/cryptography/x509/extensions.py
index db55789e..0aa67212 100644
--- a/src/cryptography/x509/extensions.py
+++ b/src/cryptography/x509/extensions.py
@@ -490,6 +490,62 @@ class ReasonFlags(Enum):
@utils.register_interface(ExtensionType)
+class PolicyConstraints(object):
+ oid = ExtensionOID.POLICY_CONSTRAINTS
+
+ 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"<PolicyConstraints(require_explicit_policy={0.require_explicit"
+ u"_policy}, inhibit_policy_mapping={0.inhibit_policy_"
+ u"mapping})>".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"<PolicyConstraints(require_explicit_policy=0, inhibit_policy_ma"
+ u"pping=None)>"
+ )
+
+ 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):
diff --git a/vectors/cryptography_vectors/x509/department-of-state-root.pem b/vectors/cryptography_vectors/x509/department-of-state-root.pem
new file mode 100644
index 00000000..6b6885c1
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/department-of-state-root.pem
@@ -0,0 +1,40 @@
+-----BEGIN CERTIFICATE-----
+MIIG+DCCBeCgAwIBAgICITgwDQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCVVMx
+GDAWBgNVBAoTD1UuUy4gR292ZXJubWVudDENMAsGA1UECxMERlBLSTEhMB8GA1UE
+AxMYRmVkZXJhbCBDb21tb24gUG9saWN5IENBMB4XDTE0MDgxMTE0MjUwMloXDTE3
+MDgxMTE0MjM1OVowgbExEzARBgoJkiaJk/IsZAEZFgNzYnUxFTATBgoJkiaJk/Is
+ZAEZFgVzdGF0ZTEWMBQGA1UEAxMNQ29uZmlndXJhdGlvbjERMA8GA1UEAxMIU2Vy
+dmljZXMxHDAaBgNVBAMTE1B1YmxpYyBLZXkgU2VydmljZXMxDDAKBgNVBAMTA0FJ
+QTEsMCoGA1UEAxMjVS5TLiBEZXBhcnRtZW50IG9mIFN0YXRlIEFEIFJvb3QgQ0Ew
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5Nl+zsQXaSuJrw5d/SlwQ
+2Qopr9lvmlWvpRlutPSl7X5Dg5WSyU2V0u++JE0uprOhs+ZI9WS27MK+a32OmMzT
+g6HVzpO3curRiM/h5fJrAEBYFeXour0oUHYFwWcChi1k0mZkMrb4WO9+WppTFlIv
+9b7MSgaOoH5UTUNE5HAMMDgPVQGnIsxylftF7ikCyld45N764HDLdlfzw2j0RHn1
+ntEw2q0pp6vGbBN9/RPh97rOqJrKIr3ieE4Bzw3b+gAF4ymjcKOigl2lih8PJG5Z
+puGmPwOmWXh+rmwnzAM6pQmHp/xXDIPqJx5hvTfoOKsJhBiePgwQOc0blYgsXM7n
+AgMBAAGjggNvMIIDazAPBgNVHRMBAf8EBTADAQH/MGsGA1UdIARkMGIwDAYKYIZI
+AWUDAgEDAjAMBgpghkgBZQMCAQMNMAwGCmCGSAFlAwIBAwEwDAYKYIZIAWUDAgED
+BjAMBgpghkgBZQMCAQMHMAwGCmCGSAFlAwIBAwgwDAYKYIZIAWUDAgEDEDBPBggr
+BgEFBQcBAQRDMEEwPwYIKwYBBQUHMAKGM2h0dHA6Ly9odHRwLmZwa2kuZ292L2Zj
+cGNhL2NhQ2VydHNJc3N1ZWRUb2ZjcGNhLnA3YzCBjQYDVR0hBIGFMIGCMBgGCmCG
+SAFlAwIBAwEGCmCGSAFlAwIBBgEwGAYKYIZIAWUDAgEDAgYKYIZIAWUDAgEGAjAY
+BgpghkgBZQMCAQMGBgpghkgBZQMCAQYDMBgGCmCGSAFlAwIBAwcGCmCGSAFlAwIB
+BgwwGAYKYIZIAWUDAgEDEAYKYIZIAWUDAgEGBDCCATYGCCsGAQUFBwELBIIBKDCC
+ASQwQwYIKwYBBQUHMAWGN2h0dHA6Ly9jcmxzLnBraS5zdGF0ZS5nb3YvU0lBL0Nl
+cnRzSXNzdWVkQnlBRFJvb3RDQS5wN2MwgdwGCCsGAQUFBzAFhoHPbGRhcDovL2Nl
+cnRyZXAucGtpLnN0YXRlLmdvdi9jbj1VLlMuJTIwRGVwYXJ0bWVudCUyMG9mJTIw
+U3RhdGUlMjBBRCUyMFJvb3QlMjBDQSxjbj1BSUEsY249UHVibGljJTIwS2V5JTIw
+U2VydmljZXMsY249U2VydmljZXMsY249Q29uZmlndXJhdGlvbixkYz1zdGF0ZSxk
+Yz1zYnU/Y0FDZXJ0aWZpY2F0ZTtiaW5hcnksY3Jvc3NDZXJ0aWZpY2F0ZVBhaXI7
+YmluYXJ5MCkGA1UdHgEB/wQfMB2hGzAZpBcwFTETMBEGCgmSJomT8ixkARkWA21p
+bDAPBgNVHSQBAf8EBTADgQEAMA0GA1UdNgEB/wQDAgEAMA4GA1UdDwEB/wQEAwIB
+BjAfBgNVHSMEGDAWgBStDHp1XOXzmMR5mA6sKP2X9OcC/DA1BgNVHR8ELjAsMCqg
+KKAmhiRodHRwOi8vaHR0cC5mcGtpLmdvdi9mY3BjYS9mY3BjYS5jcmwwHQYDVR0O
+BBYEFG+D/oJQZGV3Pv3fA5rOKdEvMMzsMA0GCSqGSIb3DQEBCwUAA4IBAQBYnF0+
+cCv5Kbqafkn8hdljuUnhCDHKVVL7gysmZUCsIerzzklPWaCrWgTO+yRzIA7oHX4n
+o9sLVpru8evQL5IQVYUCnHIoCWPW9LIvt7eKJHi0KdTlbK5JTN6SNo1MVn1z1L+D
+15nUhuqs3b5FWxCl9AbO0V5tsiAIq6dNrhdfhUJIOLzDffM24HOC3wIDERAfZiPC
+LsK0nOHixW8dNQ8XsT6ZVJ4xhxJ9zW2O2wp/sWJhnzGUYd43IYH0AriHlByYJ3Ef
+vWtu74ypiI9C5xgnzW+rqze4DxG38+QC0V4kIB5adGghRD/qdPElw4hMCzbbZGGs
++OJGofrihzt0GyNM
+-----END CERTIFICATE----- \ No newline at end of file