929 lines
34 KiB
Python
929 lines
34 KiB
Python
# 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,
|
|
)
|