aboutsummaryrefslogtreecommitdiffstats
path: root/tests/hazmat/primitives/test_dh.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/hazmat/primitives/test_dh.py')
-rw-r--r--tests/hazmat/primitives/test_dh.py802
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
+ )