aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2015-07-01 19:16:36 -0500
committerPaul Kehrer <paul.l.kehrer@gmail.com>2015-07-10 11:11:19 -0500
commitcfb8aa2f39095d33d19b17123aed065dd5e9efd3 (patch)
treecc1c37f977d99be9dfc5dc588bd641409d3fd279
parent3fe1543c9e4c04604967a9524aa5b2e641bc9ede (diff)
downloadcryptography-cfb8aa2f39095d33d19b17123aed065dd5e9efd3.tar.gz
cryptography-cfb8aa2f39095d33d19b17123aed065dd5e9efd3.tar.bz2
cryptography-cfb8aa2f39095d33d19b17123aed065dd5e9efd3.zip
name constraints - support IP addresses with netmask
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py29
-rw-r--r--tests/test_x509_ext.py35
2 files changed, 59 insertions, 5 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index d78c60fa..c2a32b2a 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -141,11 +141,30 @@ def _decode_general_name(backend, gn):
oid = _obj2txt(backend, gn.d.registeredID)
return x509.RegisteredID(x509.ObjectIdentifier(oid))
elif gn.type == backend._lib.GEN_IPADD:
- return x509.IPAddress(
- ipaddress.ip_address(
- _asn1_string_to_bytes(backend, gn.d.iPAddress)
- )
- )
+ data = backend._ffi.buffer(
+ gn.d.iPAddress.data, gn.d.iPAddress.length
+ )[:]
+ data_len = len(data)
+ if data_len == 8 or data_len == 32:
+ # This is an IPv4 or IPv6 Network and not a single IP. This
+ # type of data appears in Name Constraints. Unfortunately,
+ # ipaddress doesn't support packed bytes + netmask. Additionally,
+ # IPv6Network can only handle CIDR rather than the full 16 byte
+ # netmask. To handle this we convert the netmask to integer, then
+ # find the first 0 bit, which will be the prefix. If another 1
+ # bit is present after that the netmask is invalid.
+ base = ipaddress.ip_address(data[:data_len // 2])
+ netmask = utils.int_from_bytes(data[data_len // 2:], 'big')
+ bits = bin(netmask)[2:]
+ prefix = bits.find('0')
+ if bits[prefix:].find('1') != -1:
+ raise ValueError("Invalid netmask")
+
+ ip = ipaddress.ip_network(base.exploded + u"/{0}".format(prefix))
+ else:
+ ip = ipaddress.ip_address(data)
+
+ return x509.IPAddress(ip)
elif gn.type == backend._lib.GEN_DIRNAME:
return x509.DirectoryName(
_decode_x509_name(backend, gn.d.directoryName)
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 993802b8..7a7e79e6 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -2184,6 +2184,41 @@ class TestNameConstraintsExtension(object):
]
)
+ def test_permitted_excluded_with_ips(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "nc_permitted_excluded.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ nc = cert.extensions.get_extension_for_oid(
+ x509.OID_NAME_CONSTRAINTS
+ ).value
+ assert nc == x509.NameConstraints(
+ permitted_subtrees=[
+ x509.IPAddress(ipaddress.IPv4Network(u"192.168.0.0/24")),
+ x509.IPAddress(ipaddress.IPv6Network(u"FF:0:0:0:0:0:0:0/96")),
+ ],
+ excluded_subtrees=[
+ x509.DNSName(u".domain.com"),
+ x509.UniformResourceIdentifier(u"http://test.local"),
+ ]
+ )
+
+ def test_invalid_netmask(self, backend):
+ cert = _load_cert(
+ os.path.join(
+ "x509", "custom", "nc_invalid_ip_netmask.pem"
+ ),
+ x509.load_pem_x509_certificate,
+ backend
+ )
+ with pytest.raises(ValueError):
+ cert.extensions.get_extension_for_oid(
+ x509.OID_NAME_CONSTRAINTS
+ )
+
class TestDistributionPoint(object):
def test_distribution_point_full_name_not_general_names(self):