diff options
-rw-r--r-- | CHANGELOG.rst | 6 | ||||
-rw-r--r-- | docs/hazmat/primitives/asymmetric/serialization.rst | 4 | ||||
-rw-r--r-- | docs/index.rst | 2 | ||||
-rw-r--r-- | docs/x509/index.rst | 14 | ||||
-rw-r--r-- | docs/x509/reference.rst (renamed from docs/x509.rst) | 10 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 31 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/asymmetric/rsa.py | 14 | ||||
-rw-r--r-- | tests/conftest.py | 6 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_rsa.py | 16 | ||||
-rw-r--r-- | tests/test_x509_ext.py | 54 |
10 files changed, 135 insertions, 22 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 475a2a35..85f84477 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -93,7 +93,7 @@ Changelog Note that unsupported extensions with the critical flag raise :class:`~cryptography.x509.UnsupportedExtension` while unsupported extensions set to non-critical are silently ignored. Read the - :doc:`X.509 documentation</x509>` for more information. + :doc:`X.509 documentation</x509/index>` for more information. 0.8.2 - 2015-04-10 ~~~~~~~~~~~~~~~~~~ @@ -120,7 +120,7 @@ Changelog from :mod:`~cryptography.hazmat.primitives.interfaces` to :mod:`~cryptography.hazmat.primitives.kdf`. * Added support for parsing X.509 names. See the - :doc:`X.509 documentation</x509>` for more information. + :doc:`X.509 documentation</x509/index>` for more information. * Added :func:`~cryptography.hazmat.primitives.serialization.load_der_private_key` to support loading of DER encoded private keys and @@ -256,7 +256,7 @@ Changelog support the loading of OpenSSH public keys (:rfc:`4253`). Only RSA and DSA keys are currently supported. * Added initial support for X.509 certificate parsing. See the - :doc:`X.509 documentation</x509>` for more information. + :doc:`X.509 documentation</x509/index>` for more information. 0.6.1 - 2014-10-15 ~~~~~~~~~~~~~~~~~~ diff --git a/docs/hazmat/primitives/asymmetric/serialization.rst b/docs/hazmat/primitives/asymmetric/serialization.rst index 7839f346..8d51f0d7 100644 --- a/docs/hazmat/primitives/asymmetric/serialization.rst +++ b/docs/hazmat/primitives/asymmetric/serialization.rst @@ -97,8 +97,8 @@ all begin with ``-----BEGIN {format}-----`` and end with ``-----END .. note:: A PEM block which starts with ``-----BEGIN CERTIFICATE-----`` is not a - public or private key, it's an :doc:`X.509 Certificate </x509>`. You can - load it using :func:`~cryptography.x509.load_pem_x509_certificate` and + public or private key, it's an :doc:`X.509 Certificate </x509/index>`. You + can load it using :func:`~cryptography.x509.load_pem_x509_certificate` and extract the public key with :meth:`Certificate.public_key <cryptography.x509.Certificate.public_key>`. diff --git a/docs/index.rst b/docs/index.rst index 35f80a2d..5c26a754 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -63,7 +63,7 @@ The recipes layer :maxdepth: 2 fernet - x509 + x509/index random-numbers exceptions faq diff --git a/docs/x509/index.rst b/docs/x509/index.rst new file mode 100644 index 00000000..c3fa1ed2 --- /dev/null +++ b/docs/x509/index.rst @@ -0,0 +1,14 @@ +X.509 +===== + +X.509 is an ITU-T standard for a `public key infrastructure`_. X.509v3 is +defined in :rfc:`5280` (which obsoletes :rfc:`2459` and :rfc:`3280`). X.509 +certificates are commonly used in protocols like `TLS`_. + +.. toctree:: + :maxdepth: 2 + + reference + +.. _`public key infrastructure`: https://en.wikipedia.org/wiki/Public_key_infrastructure +.. _`TLS`: https://en.wikipedia.org/wiki/Transport_Layer_Security diff --git a/docs/x509.rst b/docs/x509/reference.rst index bcb6ee66..9179468f 100644 --- a/docs/x509.rst +++ b/docs/x509/reference.rst @@ -1,5 +1,5 @@ -X.509 -===== +X.509 Reference +=============== .. currentmodule:: cryptography.x509 @@ -86,10 +86,6 @@ X.509 -----END CERTIFICATE----- """.strip() -X.509 is an ITU-T standard for a `public key infrastructure`_. X.509v3 is -defined in :rfc:`5280` (which obsoletes :rfc:`2459` and :rfc:`3280`). X.509 -certificates are commonly used in protocols like `TLS`_. - Loading Certificates ~~~~~~~~~~~~~~~~~~~~ @@ -1582,7 +1578,5 @@ Exceptions types can be found in `RFC 5280 section 4.2.1.6`_. -.. _`public key infrastructure`: https://en.wikipedia.org/wiki/Public_key_infrastructure -.. _`TLS`: https://en.wikipedia.org/wiki/Transport_Layer_Security .. _`RFC 5280 section 4.2.1.1`: https://tools.ietf.org/html/rfc5280#section-4.2.1.1 .. _`RFC 5280 section 4.2.1.6`: https://tools.ietf.org/html/rfc5280#section-4.2.1.6 diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index d78c60fa..096cbc9e 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -141,11 +141,32 @@ 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 = _asn1_string_to_bytes(backend, gn.d.iPAddress) + 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 = ipaddress.ip_address(data[data_len // 2:]) + bits = bin(int(netmask))[2:] + prefix = bits.find('0') + # If no 0 bits are found it is a /32 or /128 + if prefix == -1: + prefix = len(bits) + + if "1" in bits[prefix:]: + 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/src/cryptography/hazmat/primitives/asymmetric/rsa.py b/src/cryptography/hazmat/primitives/asymmetric/rsa.py index 89eac4d4..41b0089e 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/rsa.py +++ b/src/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -307,6 +307,17 @@ class RSAPrivateNumbers(object): def __ne__(self, other): return not self == other + def __hash__(self): + return hash(( + self.p, + self.q, + self.d, + self.dmp1, + self.dmq1, + self.iqmp, + self.public_numbers, + )) + class RSAPublicNumbers(object): def __init__(self, e, n): @@ -336,3 +347,6 @@ class RSAPublicNumbers(object): def __ne__(self, other): return not self == other + + def __hash__(self): + return hash((self.e, self.n)) diff --git a/tests/conftest.py b/tests/conftest.py index 6599a643..bdd17fb7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,10 +12,10 @@ from .utils import check_backend_support, select_backends, skip_if_empty def pytest_generate_tests(metafunc): - names = metafunc.config.getoption("--backend") - selected_backends = select_backends(names, _available_backends()) - if "backend" in metafunc.fixturenames: + names = metafunc.config.getoption("--backend") + selected_backends = select_backends(names, _available_backends()) + filtered_backends = [] required = metafunc.function.requires_backend_interface required_interfaces = [ diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py index bfeab8dd..0c5f7042 100644 --- a/tests/hazmat/primitives/test_rsa.py +++ b/tests/hazmat/primitives/test_rsa.py @@ -1705,6 +1705,22 @@ class TestRSANumbersEquality(object): ) assert num != object() + def test_public_numbers_hash(self): + pub1 = RSAPublicNumbers(3, 17) + pub2 = RSAPublicNumbers(3, 17) + pub3 = RSAPublicNumbers(7, 21) + + assert hash(pub1) == hash(pub2) + assert hash(pub1) != hash(pub3) + + def test_private_numbers_hash(self): + priv1 = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 2)) + priv2 = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 2)) + priv3 = RSAPrivateNumbers(1, 2, 3, 4, 5, 6, RSAPublicNumbers(1, 3)) + + assert hash(priv1) == hash(priv2) + assert hash(priv1) != hash(priv3) + class TestRSAPrimeFactorRecovery(object): @pytest.mark.parametrize( diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py index e1569865..84a40995 100644 --- a/tests/test_x509_ext.py +++ b/tests/test_x509_ext.py @@ -2202,6 +2202,60 @@ 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_single_ip_netmask(self, backend): + cert = _load_cert( + os.path.join( + "x509", "custom", "nc_single_ip_netmask.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.IPv6Network(u"FF:0:0:0:0:0:0:0/128")), + x509.IPAddress(ipaddress.IPv4Network(u"192.168.0.1/32")), + ], + excluded_subtrees=None + ) + + 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): |