Repository: pyca/cryptography

Issue:
Add support for policyConstraints x.509 extension in parser
```
cryptography.x509.extensions.UnsupportedExtension: Critical extension <ObjectIdentifier(oid=2.5.29.36, name=policyConstraints)> is not currently supported
```



Discussion:

Resolving patch:
diff --git a/src/cryptography/hazmat/backends/openssl/decode_asn1.py b/src/cryptography/hazmat/backends/openssl/decode_asn1.py
--- a/src/cryptography/hazmat/backends/openssl/decode_asn1.py
+++ b/src/cryptography/hazmat/backends/openssl/decode_asn1.py
@@ -452,6 +452,29 @@ def _decode_general_subtrees(backend, stack_subtrees):
     return subtrees
 
 
+def _decode_policy_constraints(backend, pc):
+    pc = backend._ffi.cast("POLICY_CONSTRAINTS *", pc)
+    pc = backend._ffi.gc(pc, backend._lib.POLICY_CONSTRAINTS_free)
+
+    if pc.requireExplicitPolicy == backend._ffi.NULL:
+        require_explicit_policy = None
+    else:
+        require_explicit_policy = _asn1_integer_to_int(
+            backend, pc.requireExplicitPolicy
+        )
+
+    if pc.inhibitPolicyMapping == backend._ffi.NULL:
+        inhibit_policy_mapping = None
+    else:
+        inhibit_policy_mapping = _asn1_integer_to_int(
+            backend, pc.inhibitPolicyMapping
+        )
+
+    return x509.PolicyConstraints(
+        require_explicit_policy, inhibit_policy_mapping
+    )
+
+
 def _decode_extended_key_usage(backend, sk):
     sk = backend._ffi.cast("Cryptography_STACK_OF_ASN1_OBJECT *", sk)
     sk = backend._ffi.gc(sk, backend._lib.sk_ASN1_OBJECT_free)
@@ -729,6 +752,7 @@ def _parse_asn1_generalized_time(backend, generalized_time):
     ExtensionOID.INHIBIT_ANY_POLICY: _decode_inhibit_any_policy,
     ExtensionOID.ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name,
     ExtensionOID.NAME_CONSTRAINTS: _decode_name_constraints,
+    ExtensionOID.POLICY_CONSTRAINTS: _decode_policy_constraints,
 }
 
 _REVOKED_EXTENSION_HANDLERS = {


Test patch:
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -2280,6 +2280,39 @@ def test_ne(self):
         assert pc != object()
 
 
+@pytest.mark.requires_backend_interface(interface=RSABackend)
+@pytest.mark.requires_backend_interface(interface=X509Backend)
+class TestPolicyConstraintsExtension(object):
+    def test_inhibit_policy_mapping(self, backend):
+        cert = _load_cert(
+            os.path.join("x509", "department-of-state-root.pem"),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        ext = cert.extensions.get_extension_for_oid(
+            ExtensionOID.POLICY_CONSTRAINTS,
+        )
+        assert ext.critical is True
+
+        assert ext.value == x509.PolicyConstraints(
+            require_explicit_policy=None, inhibit_policy_mapping=0,
+        )
+
+    def test_require_explicit_policy(self, backend):
+        cert = _load_cert(
+            os.path.join("x509", "custom", "policy_constraints_explicit.pem"),
+            x509.load_pem_x509_certificate,
+            backend
+        )
+        ext = cert.extensions.get_extension_for_oid(
+            ExtensionOID.POLICY_CONSTRAINTS
+        )
+        assert ext.critical is True
+        assert ext.value == x509.PolicyConstraints(
+            require_explicit_policy=1, inhibit_policy_mapping=None,
+        )
+
+
 class TestAuthorityInformationAccess(object):
     def test_invalid_descriptions(self):
         with pytest.raises(TypeError):