| # This file is dual licensed under the terms of the Apache License, Version |
| # 2.0, and the BSD License. See the LICENSE file in the root of this repository |
| # for complete details. |
| |
| from __future__ import absolute_import, division, print_function |
| |
| import binascii |
| import itertools |
| import os |
| |
| import pytest |
| |
| from cryptography.hazmat.backends.interfaces import ( |
| DERSerializationBackend, |
| DHBackend, |
| PEMSerializationBackend, |
| ) |
| from cryptography.hazmat.primitives import serialization |
| from cryptography.hazmat.primitives.asymmetric import dh |
| from cryptography.utils import int_from_bytes |
| |
| from .fixtures_dh import FFDH3072_P |
| from ...doubles import DummyKeySerializationEncryption |
| from ...utils import load_nist_vectors, load_vectors_from_file |
| |
| # RFC 3526 |
| P_1536 = int( |
| "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" |
| "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" |
| "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" |
| "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" |
| "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" |
| "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" |
| "83655D23DCA3AD961C62F356208552BB9ED529077096966D" |
| "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", |
| 16, |
| ) |
| |
| |
| def _skip_dhx_unsupported(backend, is_dhx): |
| if not is_dhx: |
| return |
| if not backend.dh_x942_serialization_supported(): |
| pytest.skip("DH x9.42 serialization is not supported") |
| |
| |
| def test_dh_parameternumbers(): |
| params = dh.DHParameterNumbers(P_1536, 2) |
| |
| assert params.p == P_1536 |
| assert params.g == 2 |
| |
| with pytest.raises(TypeError): |
| dh.DHParameterNumbers(None, 2) |
| |
| with pytest.raises(TypeError): |
| dh.DHParameterNumbers(P_1536, None) |
| |
| with pytest.raises(TypeError): |
| dh.DHParameterNumbers(None, None) |
| |
| with pytest.raises(ValueError): |
| dh.DHParameterNumbers(P_1536, 1) |
| |
| # p too small |
| with pytest.raises(ValueError): |
| dh.DHParameterNumbers(65537, 2) |
| |
| params = dh.DHParameterNumbers(P_1536, 7, 1245) |
| |
| assert params.p == P_1536 |
| assert params.g == 7 |
| assert params.q == 1245 |
| |
| with pytest.raises(TypeError): |
| dh.DHParameterNumbers(P_1536, 2, "hello") |
| |
| |
| def test_dh_numbers(): |
| params = dh.DHParameterNumbers(P_1536, 2) |
| |
| public = dh.DHPublicNumbers(1, params) |
| |
| assert public.parameter_numbers is params |
| assert public.y == 1 |
| |
| with pytest.raises(TypeError): |
| dh.DHPublicNumbers(1, None) |
| |
| with pytest.raises(TypeError): |
| dh.DHPublicNumbers(None, params) |
| |
| private = dh.DHPrivateNumbers(1, public) |
| |
| assert private.public_numbers is public |
| assert private.x == 1 |
| |
| with pytest.raises(TypeError): |
| dh.DHPrivateNumbers(1, None) |
| |
| with pytest.raises(TypeError): |
| dh.DHPrivateNumbers(None, public) |
| |
| |
| def test_dh_parameter_numbers_equality(): |
| assert dh.DHParameterNumbers(P_1536, 2) == dh.DHParameterNumbers(P_1536, 2) |
| assert dh.DHParameterNumbers(P_1536, 7, 12345) == dh.DHParameterNumbers( |
| P_1536, 7, 12345 |
| ) |
| assert dh.DHParameterNumbers(P_1536 + 2, 2) != dh.DHParameterNumbers( |
| P_1536, 2 |
| ) |
| assert dh.DHParameterNumbers(P_1536, 2, 123) != dh.DHParameterNumbers( |
| P_1536, 2, 456 |
| ) |
| assert dh.DHParameterNumbers(P_1536, 5) != dh.DHParameterNumbers(P_1536, 2) |
| assert dh.DHParameterNumbers(P_1536, 2) != object() |
| |
| |
| def test_dh_private_numbers_equality(): |
| params = dh.DHParameterNumbers(P_1536, 2) |
| public = dh.DHPublicNumbers(1, params) |
| private = dh.DHPrivateNumbers(2, public) |
| |
| assert private == dh.DHPrivateNumbers(2, public) |
| assert private != dh.DHPrivateNumbers(0, public) |
| assert private != dh.DHPrivateNumbers(2, dh.DHPublicNumbers(0, params)) |
| assert private != dh.DHPrivateNumbers( |
| 2, dh.DHPublicNumbers(1, dh.DHParameterNumbers(P_1536, 5)) |
| ) |
| assert private != object() |
| |
| |
| def test_dh_public_numbers_equality(): |
| params = dh.DHParameterNumbers(P_1536, 2) |
| public = dh.DHPublicNumbers(1, params) |
| |
| assert public == dh.DHPublicNumbers(1, params) |
| assert public != dh.DHPublicNumbers(0, params) |
| assert public != dh.DHPublicNumbers(1, dh.DHParameterNumbers(P_1536, 5)) |
| assert public != object() |
| |
| |
| @pytest.mark.requires_backend_interface(interface=DHBackend) |
| class TestDH(object): |
| def test_small_key_generate_dh(self, backend): |
| with pytest.raises(ValueError): |
| dh.generate_parameters(2, 511, backend) |
| |
| def test_unsupported_generator_generate_dh(self, backend): |
| with pytest.raises(ValueError): |
| dh.generate_parameters(7, 512, backend) |
| |
| @pytest.mark.skip_fips(reason="non-FIPS parameters") |
| def test_dh_parameters_supported(self, backend): |
| valid_p = int( |
| b"907c7211ae61aaaba1825ff53b6cb71ac6df9f1a424c033f4a0a41ac42fad3a9" |
| b"bcfc7f938a269710ed69e330523e4039029b7900977c740990d46efed79b9bbe" |
| b"73505ae878808944ce4d9c6c52daecc0a87dc889c53499be93db8551ee685f30" |
| b"349bf1b443d4ebaee0d5e8b441a40d4e8178f8f612f657a5eb91e0a8e" |
| b"107755f", |
| 16, |
| ) |
| assert backend.dh_parameters_supported(valid_p, 5) |
| assert not backend.dh_parameters_supported(23, 22) |
| |
| @pytest.mark.parametrize( |
| "vector", |
| load_vectors_from_file( |
| os.path.join("asymmetric", "DH", "rfc3526.txt"), load_nist_vectors |
| ), |
| ) |
| def test_dh_parameters_allows_rfc3526_groups(self, backend, vector): |
| p = int_from_bytes(binascii.unhexlify(vector["p"]), "big") |
| if ( |
| backend._fips_enabled |
| and p.bit_length() < backend._fips_dh_min_modulus |
| ): |
| pytest.skip("modulus too small for FIPS mode") |
| |
| params = dh.DHParameterNumbers(p, int(vector["g"])) |
| param = params.parameters(backend) |
| key = param.generate_private_key() |
| # This confirms that a key generated with this group |
| # will pass DH_check when we serialize and de-serialize it via |
| # the Numbers path. |
| roundtripped_key = key.private_numbers().private_key(backend) |
| assert key.private_numbers() == roundtripped_key.private_numbers() |
| |
| @pytest.mark.skip_fips(reason="non-FIPS parameters") |
| @pytest.mark.parametrize( |
| "vector", |
| load_vectors_from_file( |
| os.path.join("asymmetric", "DH", "RFC5114.txt"), load_nist_vectors |
| ), |
| ) |
| def test_dh_parameters_supported_with_q(self, backend, vector): |
| assert backend.dh_parameters_supported( |
| int(vector["p"], 16), int(vector["g"], 16), int(vector["q"], 16) |
| ) |
| |
| @pytest.mark.skip_fips(reason="modulus too small for FIPS") |
| @pytest.mark.parametrize("with_q", [False, True]) |
| def test_convert_to_numbers(self, backend, with_q): |
| if with_q: |
| vector = load_vectors_from_file( |
| os.path.join("asymmetric", "DH", "RFC5114.txt"), |
| load_nist_vectors, |
| )[0] |
| p = int(vector["p"], 16) |
| g = int(vector["g"], 16) |
| q = int(vector["q"], 16) |
| else: |
| parameters = backend.generate_dh_private_key_and_parameters(2, 512) |
| |
| private = parameters.private_numbers() |
| |
| p = private.public_numbers.parameter_numbers.p |
| g = private.public_numbers.parameter_numbers.g |
| q = None |
| |
| params = dh.DHParameterNumbers(p, g, q) |
| public = dh.DHPublicNumbers(1, params) |
| private = dh.DHPrivateNumbers(2, public) |
| |
| deserialized_params = params.parameters(backend) |
| deserialized_public = public.public_key(backend) |
| deserialized_private = private.private_key(backend) |
| |
| assert isinstance( |
| deserialized_params, dh.DHParametersWithSerialization |
| ) |
| assert isinstance(deserialized_public, dh.DHPublicKeyWithSerialization) |
| assert isinstance( |
| deserialized_private, dh.DHPrivateKeyWithSerialization |
| ) |
| |
| @pytest.mark.skip_fips(reason="FIPS requires specific parameters") |
| def test_numbers_unsupported_parameters(self, backend): |
| # p is set to P_1536 + 1 because when calling private_key we want it to |
| # fail the DH_check call OpenSSL does, but we specifically want it to |
| # fail such that we don't get a DH_NOT_SUITABLE_GENERATOR. We can cause |
| # this by making sure p is not prime. |
| params = dh.DHParameterNumbers(P_1536 + 1, 2) |
| public = dh.DHPublicNumbers(1, params) |
| private = dh.DHPrivateNumbers(2, public) |
| |
| with pytest.raises(ValueError): |
| private.private_key(backend) |
| |
| @pytest.mark.skip_fips(reason="FIPS requires key size >= 2048") |
| @pytest.mark.parametrize("with_q", [False, True]) |
| def test_generate_dh(self, backend, with_q): |
| if with_q: |
| vector = load_vectors_from_file( |
| os.path.join("asymmetric", "DH", "RFC5114.txt"), |
| load_nist_vectors, |
| )[0] |
| p = int(vector["p"], 16) |
| g = int(vector["g"], 16) |
| q = int(vector["q"], 16) |
| parameters = dh.DHParameterNumbers(p, g, q).parameters(backend) |
| key_size = 1024 |
| else: |
| generator = 2 |
| key_size = 512 |
| |
| parameters = dh.generate_parameters(generator, key_size, backend) |
| assert isinstance(parameters, dh.DHParameters) |
| |
| key = parameters.generate_private_key() |
| assert isinstance(key, dh.DHPrivateKey) |
| assert key.key_size == key_size |
| |
| public = key.public_key() |
| assert isinstance(public, dh.DHPublicKey) |
| assert public.key_size == key_size |
| |
| assert isinstance(parameters, dh.DHParametersWithSerialization) |
| parameter_numbers = parameters.parameter_numbers() |
| assert isinstance(parameter_numbers, dh.DHParameterNumbers) |
| assert parameter_numbers.p.bit_length() == key_size |
| |
| assert isinstance(public, dh.DHPublicKeyWithSerialization) |
| assert isinstance(public.public_numbers(), dh.DHPublicNumbers) |
| assert isinstance(public.parameters(), dh.DHParameters) |
| |
| assert isinstance(key, dh.DHPrivateKeyWithSerialization) |
| assert isinstance(key.private_numbers(), dh.DHPrivateNumbers) |
| assert isinstance(key.parameters(), dh.DHParameters) |
| |
| def test_exchange(self, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| assert isinstance(parameters, dh.DHParameters) |
| |
| key1 = parameters.generate_private_key() |
| key2 = parameters.generate_private_key() |
| |
| symkey1 = key1.exchange(key2.public_key()) |
| assert symkey1 |
| assert len(symkey1) == 3072 // 8 |
| |
| symkey2 = key2.exchange(key1.public_key()) |
| assert symkey1 == symkey2 |
| |
| def test_exchange_algorithm(self, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| key1 = parameters.generate_private_key() |
| key2 = parameters.generate_private_key() |
| |
| shared_key_bytes = key2.exchange(key1.public_key()) |
| symkey = int_from_bytes(shared_key_bytes, "big") |
| |
| symkey_manual = pow( |
| key1.public_key().public_numbers().y, |
| key2.private_numbers().x, |
| parameters.parameter_numbers().p, |
| ) |
| |
| assert symkey == symkey_manual |
| |
| @pytest.mark.skip_fips(reason="key_size too small for FIPS") |
| def test_symmetric_key_padding(self, backend): |
| """ |
| This test has specific parameters that produce a symmetric key |
| In length 63 bytes instead 64. We make sure here that we add |
| padding to the key. |
| """ |
| p = int( |
| "11859949538425015739337467917303613431031019140213666" |
| "129025407300654026585086345323066284800963463204246390" |
| "256567934582260424238844463330887962689642467123" |
| ) |
| g = 2 |
| y = int( |
| "32155788395534640648739966373159697798396966919821525" |
| "72238852825117261342483718574508213761865276905503199" |
| "969908098203345481366464874759377454476688391248" |
| ) |
| x = int( |
| "409364065449673443397833358558926598469347813468816037" |
| "268451847116982490733450463194921405069999008617231539" |
| "7147035896687401350877308899732826446337707128" |
| ) |
| parameters = dh.DHParameterNumbers(p, g) |
| public = dh.DHPublicNumbers(y, parameters) |
| private = dh.DHPrivateNumbers(x, public) |
| key = private.private_key(backend) |
| symkey = key.exchange(public.public_key(backend)) |
| assert len(symkey) == 512 // 8 |
| assert symkey[:1] == b"\x00" |
| |
| @pytest.mark.parametrize( |
| "vector", |
| load_vectors_from_file( |
| os.path.join("asymmetric", "DH", "bad_exchange.txt"), |
| load_nist_vectors, |
| ), |
| ) |
| def test_bad_exchange(self, backend, vector): |
| if ( |
| backend._fips_enabled |
| and int(vector["p1"]) < backend._fips_dh_min_modulus |
| ): |
| pytest.skip("modulus too small for FIPS mode") |
| parameters1 = dh.DHParameterNumbers( |
| int(vector["p1"]), int(vector["g"]) |
| ) |
| public1 = dh.DHPublicNumbers(int(vector["y1"]), parameters1) |
| private1 = dh.DHPrivateNumbers(int(vector["x1"]), public1) |
| key1 = private1.private_key(backend) |
| pub_key1 = key1.public_key() |
| |
| parameters2 = dh.DHParameterNumbers( |
| int(vector["p2"]), int(vector["g"]) |
| ) |
| public2 = dh.DHPublicNumbers(int(vector["y2"]), parameters2) |
| private2 = dh.DHPrivateNumbers(int(vector["x2"]), public2) |
| key2 = private2.private_key(backend) |
| pub_key2 = key2.public_key() |
| |
| if pub_key2.public_numbers().y >= parameters1.p: |
| with pytest.raises(ValueError): |
| key1.exchange(pub_key2) |
| else: |
| symkey1 = key1.exchange(pub_key2) |
| assert symkey1 |
| |
| symkey2 = key2.exchange(pub_key1) |
| |
| assert symkey1 != symkey2 |
| |
| @pytest.mark.skip_fips(reason="key_size too small for FIPS") |
| def test_load_256bit_key_from_pkcs8(self, backend): |
| data = load_vectors_from_file( |
| os.path.join("asymmetric", "DH", "dh_key_256.pem"), |
| lambda pemfile: pemfile.read(), |
| mode="rb", |
| ) |
| key = serialization.load_pem_private_key(data, None, backend) |
| assert key.key_size == 256 |
| |
| @pytest.mark.parametrize( |
| "vector", |
| load_vectors_from_file( |
| os.path.join("asymmetric", "DH", "vec.txt"), load_nist_vectors |
| ), |
| ) |
| def test_dh_vectors(self, backend, vector): |
| if ( |
| backend._fips_enabled |
| and int(vector["p"]) < backend._fips_dh_min_modulus |
| ): |
| pytest.skip("modulus too small for FIPS mode") |
| |
| if int(vector["p"]).bit_length() < 512: |
| pytest.skip("DH keys less than 512 bits are unsupported") |
| |
| parameters = dh.DHParameterNumbers(int(vector["p"]), int(vector["g"])) |
| public = dh.DHPublicNumbers(int(vector["y"]), parameters) |
| private = dh.DHPrivateNumbers(int(vector["x"]), public) |
| key = private.private_key(backend) |
| symkey = key.exchange(public.public_key(backend)) |
| |
| assert int_from_bytes(symkey, "big") == int(vector["k"], 16) |
| |
| @pytest.mark.skip_fips(reason="non-FIPS parameters") |
| @pytest.mark.parametrize( |
| "vector", |
| load_vectors_from_file( |
| os.path.join("asymmetric", "DH", "RFC5114.txt"), load_nist_vectors |
| ), |
| ) |
| def test_dh_vectors_with_q(self, backend, vector): |
| parameters = dh.DHParameterNumbers( |
| int(vector["p"], 16), int(vector["g"], 16), int(vector["q"], 16) |
| ) |
| public1 = dh.DHPublicNumbers(int(vector["ystatcavs"], 16), parameters) |
| private1 = dh.DHPrivateNumbers(int(vector["xstatcavs"], 16), public1) |
| public2 = dh.DHPublicNumbers(int(vector["ystatiut"], 16), parameters) |
| private2 = dh.DHPrivateNumbers(int(vector["xstatiut"], 16), public2) |
| key1 = private1.private_key(backend) |
| key2 = private2.private_key(backend) |
| symkey1 = key1.exchange(public2.public_key(backend)) |
| symkey2 = key2.exchange(public1.public_key(backend)) |
| |
| assert int_from_bytes(symkey1, "big") == int(vector["z"], 16) |
| assert int_from_bytes(symkey2, "big") == int(vector["z"], 16) |
| |
| |
| @pytest.mark.requires_backend_interface(interface=DHBackend) |
| @pytest.mark.requires_backend_interface(interface=PEMSerializationBackend) |
| @pytest.mark.requires_backend_interface(interface=DERSerializationBackend) |
| class TestDHPrivateKeySerialization(object): |
| @pytest.mark.parametrize( |
| ("encoding", "loader_func"), |
| [ |
| [serialization.Encoding.PEM, serialization.load_pem_private_key], |
| [serialization.Encoding.DER, serialization.load_der_private_key], |
| ], |
| ) |
| def test_private_bytes_unencrypted(self, backend, encoding, loader_func): |
| parameters = FFDH3072_P.parameters(backend) |
| key = parameters.generate_private_key() |
| serialized = key.private_bytes( |
| encoding, |
| serialization.PrivateFormat.PKCS8, |
| serialization.NoEncryption(), |
| ) |
| loaded_key = loader_func(serialized, None, backend) |
| loaded_priv_num = loaded_key.private_numbers() |
| priv_num = key.private_numbers() |
| assert loaded_priv_num == priv_num |
| |
| @pytest.mark.parametrize( |
| ("encoding", "fmt"), |
| [ |
| (serialization.Encoding.Raw, serialization.PrivateFormat.PKCS8), |
| (serialization.Encoding.DER, serialization.PrivateFormat.Raw), |
| (serialization.Encoding.Raw, serialization.PrivateFormat.Raw), |
| (serialization.Encoding.X962, serialization.PrivateFormat.PKCS8), |
| ], |
| ) |
| def test_private_bytes_rejects_invalid(self, encoding, fmt, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| key = parameters.generate_private_key() |
| with pytest.raises(ValueError): |
| key.private_bytes(encoding, fmt, serialization.NoEncryption()) |
| |
| @pytest.mark.skip_fips(reason="non-FIPS parameters") |
| @pytest.mark.parametrize( |
| ("key_path", "loader_func", "encoding", "is_dhx"), |
| [ |
| ( |
| os.path.join("asymmetric", "DH", "dhkey.pem"), |
| serialization.load_pem_private_key, |
| serialization.Encoding.PEM, |
| False, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhkey.der"), |
| serialization.load_der_private_key, |
| serialization.Encoding.DER, |
| False, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.pem"), |
| serialization.load_pem_private_key, |
| serialization.Encoding.PEM, |
| True, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.der"), |
| serialization.load_der_private_key, |
| serialization.Encoding.DER, |
| True, |
| ), |
| ], |
| ) |
| def test_private_bytes_match( |
| self, key_path, loader_func, encoding, is_dhx, backend |
| ): |
| _skip_dhx_unsupported(backend, is_dhx) |
| key_bytes = load_vectors_from_file( |
| key_path, lambda pemfile: pemfile.read(), mode="rb" |
| ) |
| key = loader_func(key_bytes, None, backend) |
| serialized = key.private_bytes( |
| encoding, |
| serialization.PrivateFormat.PKCS8, |
| serialization.NoEncryption(), |
| ) |
| assert serialized == key_bytes |
| |
| @pytest.mark.skip_fips(reason="non-FIPS parameters") |
| @pytest.mark.parametrize( |
| ("key_path", "loader_func", "vec_path", "is_dhx"), |
| [ |
| ( |
| os.path.join("asymmetric", "DH", "dhkey.pem"), |
| serialization.load_pem_private_key, |
| os.path.join("asymmetric", "DH", "dhkey.txt"), |
| False, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhkey.der"), |
| serialization.load_der_private_key, |
| os.path.join("asymmetric", "DH", "dhkey.txt"), |
| False, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.pem"), |
| serialization.load_pem_private_key, |
| os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"), |
| True, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.der"), |
| serialization.load_der_private_key, |
| os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"), |
| True, |
| ), |
| ], |
| ) |
| def test_private_bytes_values( |
| self, key_path, loader_func, vec_path, is_dhx, backend |
| ): |
| _skip_dhx_unsupported(backend, is_dhx) |
| key_bytes = load_vectors_from_file( |
| key_path, lambda pemfile: pemfile.read(), mode="rb" |
| ) |
| vec = load_vectors_from_file(vec_path, load_nist_vectors)[0] |
| key = loader_func(key_bytes, None, backend) |
| private_numbers = key.private_numbers() |
| assert private_numbers.x == int(vec["x"], 16) |
| assert private_numbers.public_numbers.y == int(vec["y"], 16) |
| assert private_numbers.public_numbers.parameter_numbers.g == int( |
| vec["g"], 16 |
| ) |
| assert private_numbers.public_numbers.parameter_numbers.p == int( |
| vec["p"], 16 |
| ) |
| if "q" in vec: |
| assert private_numbers.public_numbers.parameter_numbers.q == int( |
| vec["q"], 16 |
| ) |
| else: |
| assert private_numbers.public_numbers.parameter_numbers.q is None |
| |
| def test_private_bytes_traditional_openssl_invalid(self, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| key = parameters.generate_private_key() |
| with pytest.raises(ValueError): |
| key.private_bytes( |
| serialization.Encoding.PEM, |
| serialization.PrivateFormat.TraditionalOpenSSL, |
| serialization.NoEncryption(), |
| ) |
| |
| def test_private_bytes_invalid_encoding(self, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| key = parameters.generate_private_key() |
| with pytest.raises(TypeError): |
| key.private_bytes( |
| "notencoding", |
| serialization.PrivateFormat.PKCS8, |
| serialization.NoEncryption(), |
| ) |
| |
| def test_private_bytes_invalid_format(self, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| key = parameters.generate_private_key() |
| with pytest.raises(ValueError): |
| key.private_bytes( |
| serialization.Encoding.PEM, |
| "invalidformat", |
| serialization.NoEncryption(), |
| ) |
| |
| def test_private_bytes_invalid_encryption_algorithm(self, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| key = parameters.generate_private_key() |
| with pytest.raises(TypeError): |
| key.private_bytes( |
| serialization.Encoding.PEM, |
| serialization.PrivateFormat.PKCS8, |
| "notanencalg", |
| ) |
| |
| def test_private_bytes_unsupported_encryption_type(self, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| key = parameters.generate_private_key() |
| with pytest.raises(ValueError): |
| key.private_bytes( |
| serialization.Encoding.PEM, |
| serialization.PrivateFormat.PKCS8, |
| DummyKeySerializationEncryption(), |
| ) |
| |
| |
| @pytest.mark.requires_backend_interface(interface=DHBackend) |
| @pytest.mark.requires_backend_interface(interface=PEMSerializationBackend) |
| @pytest.mark.requires_backend_interface(interface=DERSerializationBackend) |
| class TestDHPublicKeySerialization(object): |
| @pytest.mark.parametrize( |
| ("encoding", "loader_func"), |
| [ |
| [serialization.Encoding.PEM, serialization.load_pem_public_key], |
| [serialization.Encoding.DER, serialization.load_der_public_key], |
| ], |
| ) |
| def test_public_bytes(self, backend, encoding, loader_func): |
| parameters = FFDH3072_P.parameters(backend) |
| key = parameters.generate_private_key().public_key() |
| serialized = key.public_bytes( |
| encoding, serialization.PublicFormat.SubjectPublicKeyInfo |
| ) |
| loaded_key = loader_func(serialized, backend) |
| loaded_pub_num = loaded_key.public_numbers() |
| pub_num = key.public_numbers() |
| assert loaded_pub_num == pub_num |
| |
| @pytest.mark.parametrize( |
| ("key_path", "loader_func", "encoding", "is_dhx"), |
| [ |
| ( |
| os.path.join("asymmetric", "DH", "dhpub.pem"), |
| serialization.load_pem_public_key, |
| serialization.Encoding.PEM, |
| False, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhpub.der"), |
| serialization.load_der_public_key, |
| serialization.Encoding.DER, |
| False, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.pem"), |
| serialization.load_pem_public_key, |
| serialization.Encoding.PEM, |
| True, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.der"), |
| serialization.load_der_public_key, |
| serialization.Encoding.DER, |
| True, |
| ), |
| ], |
| ) |
| def test_public_bytes_match( |
| self, key_path, loader_func, encoding, is_dhx, backend |
| ): |
| _skip_dhx_unsupported(backend, is_dhx) |
| key_bytes = load_vectors_from_file( |
| key_path, lambda pemfile: pemfile.read(), mode="rb" |
| ) |
| pub_key = loader_func(key_bytes, backend) |
| serialized = pub_key.public_bytes( |
| encoding, |
| serialization.PublicFormat.SubjectPublicKeyInfo, |
| ) |
| assert serialized == key_bytes |
| |
| @pytest.mark.parametrize( |
| ("key_path", "loader_func", "vec_path", "is_dhx"), |
| [ |
| ( |
| os.path.join("asymmetric", "DH", "dhpub.pem"), |
| serialization.load_pem_public_key, |
| os.path.join("asymmetric", "DH", "dhkey.txt"), |
| False, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhpub.der"), |
| serialization.load_der_public_key, |
| os.path.join("asymmetric", "DH", "dhkey.txt"), |
| False, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.pem"), |
| serialization.load_pem_public_key, |
| os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"), |
| True, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhpub_rfc5114_2.der"), |
| serialization.load_der_public_key, |
| os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"), |
| True, |
| ), |
| ], |
| ) |
| def test_public_bytes_values( |
| self, key_path, loader_func, vec_path, is_dhx, backend |
| ): |
| _skip_dhx_unsupported(backend, is_dhx) |
| key_bytes = load_vectors_from_file( |
| key_path, lambda pemfile: pemfile.read(), mode="rb" |
| ) |
| vec = load_vectors_from_file(vec_path, load_nist_vectors)[0] |
| pub_key = loader_func(key_bytes, backend) |
| public_numbers = pub_key.public_numbers() |
| assert public_numbers.y == int(vec["y"], 16) |
| assert public_numbers.parameter_numbers.g == int(vec["g"], 16) |
| assert public_numbers.parameter_numbers.p == int(vec["p"], 16) |
| if "q" in vec: |
| assert public_numbers.parameter_numbers.q == int(vec["q"], 16) |
| else: |
| assert public_numbers.parameter_numbers.q is None |
| |
| def test_public_bytes_invalid_encoding(self, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| key = parameters.generate_private_key().public_key() |
| with pytest.raises(TypeError): |
| key.public_bytes( |
| "notencoding", serialization.PublicFormat.SubjectPublicKeyInfo |
| ) |
| |
| def test_public_bytes_pkcs1_unsupported(self, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| key = parameters.generate_private_key().public_key() |
| with pytest.raises(ValueError): |
| key.public_bytes( |
| serialization.Encoding.PEM, serialization.PublicFormat.PKCS1 |
| ) |
| |
| |
| @pytest.mark.requires_backend_interface(interface=DHBackend) |
| @pytest.mark.requires_backend_interface(interface=PEMSerializationBackend) |
| @pytest.mark.requires_backend_interface(interface=DERSerializationBackend) |
| class TestDHParameterSerialization(object): |
| @pytest.mark.parametrize( |
| ("encoding", "loader_func"), |
| [ |
| [serialization.Encoding.PEM, serialization.load_pem_parameters], |
| [serialization.Encoding.DER, serialization.load_der_parameters], |
| ], |
| ) |
| def test_parameter_bytes(self, backend, encoding, loader_func): |
| parameters = FFDH3072_P.parameters(backend) |
| serialized = parameters.parameter_bytes( |
| encoding, serialization.ParameterFormat.PKCS3 |
| ) |
| loaded_key = loader_func(serialized, backend) |
| loaded_param_num = loaded_key.parameter_numbers() |
| assert loaded_param_num == parameters.parameter_numbers() |
| |
| @pytest.mark.parametrize( |
| ("param_path", "loader_func", "encoding", "is_dhx"), |
| [ |
| ( |
| os.path.join("asymmetric", "DH", "dhp.pem"), |
| serialization.load_pem_parameters, |
| serialization.Encoding.PEM, |
| False, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhp.der"), |
| serialization.load_der_parameters, |
| serialization.Encoding.DER, |
| False, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhp_rfc5114_2.pem"), |
| serialization.load_pem_parameters, |
| serialization.Encoding.PEM, |
| True, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhp_rfc5114_2.der"), |
| serialization.load_der_parameters, |
| serialization.Encoding.DER, |
| True, |
| ), |
| ], |
| ) |
| def test_parameter_bytes_match( |
| self, param_path, loader_func, encoding, backend, is_dhx |
| ): |
| _skip_dhx_unsupported(backend, is_dhx) |
| param_bytes = load_vectors_from_file( |
| param_path, lambda pemfile: pemfile.read(), mode="rb" |
| ) |
| parameters = loader_func(param_bytes, backend) |
| serialized = parameters.parameter_bytes( |
| encoding, |
| serialization.ParameterFormat.PKCS3, |
| ) |
| assert serialized == param_bytes |
| |
| @pytest.mark.parametrize( |
| ("param_path", "loader_func", "vec_path", "is_dhx"), |
| [ |
| ( |
| os.path.join("asymmetric", "DH", "dhp.pem"), |
| serialization.load_pem_parameters, |
| os.path.join("asymmetric", "DH", "dhkey.txt"), |
| False, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhp.der"), |
| serialization.load_der_parameters, |
| os.path.join("asymmetric", "DH", "dhkey.txt"), |
| False, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhp_rfc5114_2.pem"), |
| serialization.load_pem_parameters, |
| os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"), |
| True, |
| ), |
| ( |
| os.path.join("asymmetric", "DH", "dhp_rfc5114_2.der"), |
| serialization.load_der_parameters, |
| os.path.join("asymmetric", "DH", "dhkey_rfc5114_2.txt"), |
| True, |
| ), |
| ], |
| ) |
| def test_public_bytes_values( |
| self, param_path, loader_func, vec_path, backend, is_dhx |
| ): |
| _skip_dhx_unsupported(backend, is_dhx) |
| key_bytes = load_vectors_from_file( |
| param_path, lambda pemfile: pemfile.read(), mode="rb" |
| ) |
| vec = load_vectors_from_file(vec_path, load_nist_vectors)[0] |
| parameters = loader_func(key_bytes, backend) |
| parameter_numbers = parameters.parameter_numbers() |
| assert parameter_numbers.g == int(vec["g"], 16) |
| assert parameter_numbers.p == int(vec["p"], 16) |
| if "q" in vec: |
| assert parameter_numbers.q == int(vec["q"], 16) |
| else: |
| assert parameter_numbers.q is None |
| |
| @pytest.mark.parametrize( |
| ("encoding", "fmt"), |
| [ |
| ( |
| serialization.Encoding.Raw, |
| serialization.PublicFormat.SubjectPublicKeyInfo, |
| ), |
| (serialization.Encoding.Raw, serialization.PublicFormat.PKCS1), |
| ] |
| + list( |
| itertools.product( |
| [ |
| serialization.Encoding.Raw, |
| serialization.Encoding.X962, |
| serialization.Encoding.PEM, |
| serialization.Encoding.DER, |
| ], |
| [ |
| serialization.PublicFormat.Raw, |
| serialization.PublicFormat.UncompressedPoint, |
| serialization.PublicFormat.CompressedPoint, |
| ], |
| ) |
| ), |
| ) |
| def test_public_bytes_rejects_invalid(self, encoding, fmt, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| key = parameters.generate_private_key().public_key() |
| with pytest.raises(ValueError): |
| key.public_bytes(encoding, fmt) |
| |
| def test_parameter_bytes_invalid_encoding(self, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| with pytest.raises(TypeError): |
| parameters.parameter_bytes( |
| "notencoding", serialization.ParameterFormat.PKCS3 |
| ) |
| |
| def test_parameter_bytes_invalid_format(self, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| with pytest.raises(ValueError): |
| parameters.parameter_bytes(serialization.Encoding.PEM, "notformat") |
| |
| def test_parameter_bytes_openssh_unsupported(self, backend): |
| parameters = FFDH3072_P.parameters(backend) |
| with pytest.raises(TypeError): |
| parameters.parameter_bytes( |
| serialization.Encoding.OpenSSH, |
| serialization.ParameterFormat.PKCS3, |
| ) |