aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.rst2
-rw-r--r--src/_cffi_src/openssl/x509v3.py12
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py37
-rw-r--r--tests/test_x509.py35
4 files changed, 86 insertions, 0 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index ace04677..4573a474 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -9,6 +9,8 @@ Changelog
* The :class:`~cryptography.x509.Certificate` class now has
:attr:`~cryptography.x509.Certificate.signature` and
:attr:`~cryptography.x509.Certificate.tbs_certificate_bytes` attributes.
+* :class:`~cryptography.x509.NameConstraints` are now supported in the
+ :class:`~cryptography.x509.CertificateBuilder`.
1.1.1 - 2015-11-19
~~~~~~~~~~~~~~~~~~
diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py
index 8e163dc2..51c8410a 100644
--- a/src/_cffi_src/openssl/x509v3.py
+++ b/src/_cffi_src/openssl/x509v3.py
@@ -195,6 +195,7 @@ int i2d_AUTHORITY_KEYID(AUTHORITY_KEYID *, unsigned char **);
NAME_CONSTRAINTS *NAME_CONSTRAINTS_new(void);
void NAME_CONSTRAINTS_free(NAME_CONSTRAINTS *);
+int Cryptography_i2d_NAME_CONSTRAINTS(NAME_CONSTRAINTS *, unsigned char **);
OTHERNAME *OTHERNAME_new(void);
void OTHERNAME_free(OTHERNAME *);
@@ -277,6 +278,8 @@ GENERAL_SUBTREE *sk_GENERAL_SUBTREE_value(
int sk_GENERAL_SUBTREE_push(Cryptography_STACK_OF_GENERAL_SUBTREE *,
GENERAL_SUBTREE *);
+GENERAL_SUBTREE *GENERAL_SUBTREE_new(void);
+
void sk_ASN1_INTEGER_free(Cryptography_STACK_OF_ASN1_INTEGER *);
int sk_ASN1_INTEGER_num(Cryptography_STACK_OF_ASN1_INTEGER *);
ASN1_INTEGER *sk_ASN1_INTEGER_value(Cryptography_STACK_OF_ASN1_INTEGER *, int);
@@ -295,4 +298,13 @@ int i2d_CRL_DIST_POINTS(Cryptography_STACK_OF_DIST_POINT *, unsigned char **);
"""
CUSTOMIZATIONS = """
+/* i2d_NAME_CONSTRAINTS doesn't exist, but this is the way the macros in
+ asn1t.h would implement it. We're not using those macros in case
+ OpenSSL exposes this function in the future. */
+int Cryptography_i2d_NAME_CONSTRAINTS(NAME_CONSTRAINTS *nc,
+ unsigned char **out) {
+ return ASN1_item_i2d((ASN1_VALUE *)nc, out,
+ ASN1_ITEM_rptr(NAME_CONSTRAINTS));
+}
+
"""
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 8cf67551..9811e3ba 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -58,6 +58,42 @@ from cryptography.x509.oid import ExtensionOID, NameOID
_MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"])
+def _encode_name_constraints(backend, name_constraints):
+ nc = backend._lib.NAME_CONSTRAINTS_new()
+ assert nc != backend._ffi.NULL
+ nc = backend._ffi.gc(nc, backend._lib.NAME_CONSTRAINTS_free)
+ permitted = _encode_general_subtree(
+ backend, name_constraints.permitted_subtrees
+ )
+ nc.permittedSubtrees = permitted
+ excluded = _encode_general_subtree(
+ backend, name_constraints.excluded_subtrees
+ )
+ nc.excludedSubtrees = excluded
+
+ pp = backend._ffi.new('unsigned char **')
+ r = backend._lib.Cryptography_i2d_NAME_CONSTRAINTS(nc, pp)
+ assert r > 0
+ pp = backend._ffi.gc(
+ pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ )
+ return pp, r
+
+
+def _encode_general_subtree(backend, subtrees):
+ if subtrees is None:
+ return backend._ffi.NULL
+ else:
+ general_subtrees = backend._lib.sk_GENERAL_SUBTREE_new_null()
+ for name in subtrees:
+ gs = backend._lib.GENERAL_SUBTREE_new()
+ gs.base = _encode_general_name(backend, name)
+ res = backend._lib.sk_GENERAL_SUBTREE_push(general_subtrees, gs)
+ assert res >= 1
+
+ return general_subtrees
+
+
def _encode_asn1_int(backend, x):
"""
Converts a python integer to an ASN1_INTEGER. The returned ASN1_INTEGER
@@ -585,6 +621,7 @@ _EXTENSION_ENCODE_HANDLERS = {
ExtensionOID.CRL_DISTRIBUTION_POINTS: _encode_crl_distribution_points,
ExtensionOID.INHIBIT_ANY_POLICY: _encode_inhibit_any_policy,
ExtensionOID.OCSP_NO_CHECK: _encode_ocsp_nocheck,
+ ExtensionOID.NAME_CONSTRAINTS: _encode_name_constraints,
}
diff --git a/tests/test_x509.py b/tests/test_x509.py
index 9b5dda69..5a1c4c54 100644
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -2030,6 +2030,41 @@ class TestCertificateBuilder(object):
@pytest.mark.requires_backend_interface(interface=RSABackend)
@pytest.mark.requires_backend_interface(interface=X509Backend)
+ def test_name_constraints(self, backend):
+ issuer_private_key = RSA_KEY_2048.private_key(backend)
+ subject_private_key = RSA_KEY_2048.private_key(backend)
+
+ not_valid_before = datetime.datetime(2002, 1, 1, 12, 1)
+ not_valid_after = datetime.datetime(2030, 12, 31, 8, 30)
+
+ excluded = [x509.DNSName(u"name.local")]
+ nc = x509.NameConstraints(
+ permitted_subtrees=None, excluded_subtrees=excluded
+ )
+
+ cert = x509.CertificateBuilder().subject_name(
+ x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
+ ).issuer_name(
+ x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u'US')])
+ ).not_valid_before(
+ not_valid_before
+ ).not_valid_after(
+ not_valid_after
+ ).public_key(
+ subject_private_key.public_key()
+ ).serial_number(
+ 123
+ ).add_extension(
+ nc, critical=False
+ ).sign(issuer_private_key, hashes.SHA256(), backend)
+
+ ext = cert.extensions.get_extension_for_oid(
+ ExtensionOID.NAME_CONSTRAINTS
+ )
+ assert ext.value == nc
+
+ @pytest.mark.requires_backend_interface(interface=RSABackend)
+ @pytest.mark.requires_backend_interface(interface=X509Backend)
def test_key_usage(self, backend):
issuer_private_key = RSA_KEY_2048.private_key(backend)
subject_private_key = RSA_KEY_2048.private_key(backend)