diff options
Diffstat (limited to 'tests/hazmat/primitives/test_dh.py')
| -rw-r--r-- | tests/hazmat/primitives/test_dh.py | 802 |
1 files changed, 790 insertions, 12 deletions
diff --git a/tests/hazmat/primitives/test_dh.py b/tests/hazmat/primitives/test_dh.py index d8869de9..43f2ce5c 100644 --- a/tests/hazmat/primitives/test_dh.py +++ b/tests/hazmat/primitives/test_dh.py @@ -4,22 +4,42 @@ 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 ...doubles import DummyKeySerializationEncryption +from ...utils import load_nist_vectors, load_vectors_from_file + + +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( - 65537, 3 + 65537, 2 ) assert params.p == 65537 - assert params.g == 3 + assert params.g == 2 with pytest.raises(TypeError): dh.DHParameterNumbers( - None, 3 + None, 2 ) with pytest.raises(TypeError): @@ -32,10 +52,28 @@ def test_dh_parameternumbers(): None, None ) + with pytest.raises(ValueError): + dh.DHParameterNumbers( + 65537, 1 + ) + + params = dh.DHParameterNumbers( + 65537, 7, 1245 + ) + + assert params.p == 65537 + assert params.g == 7 + assert params.q == 1245 + + with pytest.raises(TypeError): + dh.DHParameterNumbers( + 65537, 2, "hello" + ) + def test_dh_numbers(): params = dh.DHParameterNumbers( - 65537, 3 + 65537, 2 ) public = dh.DHPublicNumbers( @@ -74,14 +112,18 @@ def test_dh_numbers(): def test_dh_parameter_numbers_equality(): - assert dh.DHParameterNumbers(65537, 3) == dh.DHParameterNumbers(65537, 3) - assert dh.DHParameterNumbers(6, 3) != dh.DHParameterNumbers(65537, 3) - assert dh.DHParameterNumbers(65537, 0) != dh.DHParameterNumbers(65537, 3) - assert dh.DHParameterNumbers(65537, 0) != object() + assert dh.DHParameterNumbers(65537, 2) == dh.DHParameterNumbers(65537, 2) + assert dh.DHParameterNumbers(65537, 7, 12345) == dh.DHParameterNumbers( + 65537, 7, 12345) + assert dh.DHParameterNumbers(6, 2) != dh.DHParameterNumbers(65537, 2) + assert dh.DHParameterNumbers(65537, 2, 123) != dh.DHParameterNumbers( + 65537, 2, 456) + assert dh.DHParameterNumbers(65537, 5) != dh.DHParameterNumbers(65537, 2) + assert dh.DHParameterNumbers(65537, 2) != object() def test_dh_private_numbers_equality(): - params = dh.DHParameterNumbers(65537, 3) + params = dh.DHParameterNumbers(65537, 2) public = dh.DHPublicNumbers(1, params) private = dh.DHPrivateNumbers(2, public) @@ -89,16 +131,752 @@ def test_dh_private_numbers_equality(): 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(65537, 0)) + 2, dh.DHPublicNumbers(1, dh.DHParameterNumbers(65537, 5)) ) assert private != object() def test_dh_public_numbers_equality(): - params = dh.DHParameterNumbers(65537, 3) + params = dh.DHParameterNumbers(65537, 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(65537, 0)) + assert public != dh.DHPublicNumbers(1, dh.DHParameterNumbers(65537, 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) + + 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') + 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.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.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) + + def test_numbers_unsupported_parameters(self, backend): + # p is set to 21 because when calling private_key we want it to + # fail the DH_check call OpenSSL does. Originally this was 23, but + # we are allowing p % 24 to == 23 with this PR (see #3768 for more) + # By setting it to 21 it fails later in DH_check in a primality check + # which triggers the code path we want to test + params = dh.DHParameterNumbers(21, 2) + public = dh.DHPublicNumbers(1, params) + private = dh.DHPrivateNumbers(2, public) + + with pytest.raises(ValueError): + private.private_key(backend) + + @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 = dh.generate_parameters(2, 512, 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) == 512 // 8 + + symkey2 = key2.exchange(key1.public_key()) + assert symkey1 == symkey2 + + def test_exchange_algorithm(self, backend): + parameters = dh.generate_parameters(2, 512, 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 + + 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): + 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.parametrize( + "vector", + load_vectors_from_file( + os.path.join("asymmetric", "DH", "vec.txt"), + load_nist_vectors)) + def test_dh_vectors(self, backend, vector): + 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.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 = dh.generate_parameters(2, 512, 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 = dh.generate_parameters(2, 512, backend) + key = parameters.generate_private_key() + with pytest.raises(ValueError): + key.private_bytes(encoding, fmt, serialization.NoEncryption()) + + @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.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 = dh.generate_parameters(2, 512, 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 = dh.generate_parameters(2, 512, 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 = dh.generate_parameters(2, 512, 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 = dh.generate_parameters(2, 512, 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 = dh.generate_parameters(2, 512, 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 = dh.generate_parameters(2, 512, 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 = dh.generate_parameters(2, 512, 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 = dh.generate_parameters(2, 512, 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 = dh.generate_parameters(2, 512, 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 = dh.generate_parameters(2, 512, 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 = dh.generate_parameters(2, 512, backend) + with pytest.raises(TypeError): + parameters.parameter_bytes( + "notencoding", + serialization.ParameterFormat.PKCS3 + ) + + def test_parameter_bytes_invalid_format(self, backend): + parameters = dh.generate_parameters(2, 512, backend) + with pytest.raises(ValueError): + parameters.parameter_bytes( + serialization.Encoding.PEM, + "notformat" + ) + + def test_parameter_bytes_openssh_unsupported(self, backend): + parameters = dh.generate_parameters(2, 512, backend) + with pytest.raises(TypeError): + parameters.parameter_bytes( + serialization.Encoding.OpenSSH, + serialization.ParameterFormat.PKCS3 + ) |
