1016 lines
38 KiB
Python
1016 lines
38 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 itertools
|
|
import os
|
|
|
|
import pytest
|
|
|
|
from cryptography.exceptions import AlreadyFinalized, InvalidSignature
|
|
from cryptography.hazmat.backends.interfaces import (
|
|
DSABackend,
|
|
PEMSerializationBackend,
|
|
)
|
|
from cryptography.hazmat.primitives import hashes, serialization
|
|
from cryptography.hazmat.primitives.asymmetric import dsa
|
|
from cryptography.hazmat.primitives.asymmetric.utils import (
|
|
Prehashed,
|
|
encode_dss_signature,
|
|
)
|
|
from cryptography.utils import CryptographyDeprecationWarning
|
|
|
|
from .fixtures_dsa import DSA_KEY_1024, DSA_KEY_2048, DSA_KEY_3072
|
|
from .utils import skip_fips_traditional_openssl
|
|
from ...doubles import DummyHashAlgorithm, DummyKeySerializationEncryption
|
|
from ...utils import (
|
|
load_fips_dsa_key_pair_vectors,
|
|
load_fips_dsa_sig_vectors,
|
|
load_vectors_from_file,
|
|
)
|
|
|
|
|
|
def _skip_if_dsa_not_supported(backend, algorithm, p, q, g):
|
|
if not backend.dsa_parameters_supported(
|
|
p, q, g
|
|
) or not backend.dsa_hash_supported(algorithm):
|
|
pytest.skip(
|
|
"{} does not support the provided parameters".format(backend)
|
|
)
|
|
|
|
|
|
@pytest.mark.requires_backend_interface(interface=DSABackend)
|
|
def test_skip_if_dsa_not_supported(backend):
|
|
with pytest.raises(pytest.skip.Exception):
|
|
_skip_if_dsa_not_supported(backend, DummyHashAlgorithm(), 1, 1, 1)
|
|
|
|
|
|
@pytest.mark.requires_backend_interface(interface=DSABackend)
|
|
class TestDSA(object):
|
|
def test_generate_dsa_parameters(self, backend):
|
|
parameters = dsa.generate_parameters(2048, backend)
|
|
assert isinstance(parameters, dsa.DSAParameters)
|
|
|
|
def test_generate_invalid_dsa_parameters(self, backend):
|
|
with pytest.raises(ValueError):
|
|
dsa.generate_parameters(1, backend)
|
|
|
|
@pytest.mark.parametrize(
|
|
"vector",
|
|
load_vectors_from_file(
|
|
os.path.join("asymmetric", "DSA", "FIPS_186-3", "KeyPair.rsp"),
|
|
load_fips_dsa_key_pair_vectors,
|
|
),
|
|
)
|
|
def test_generate_dsa_keys(self, vector, backend):
|
|
if (
|
|
backend._fips_enabled
|
|
and vector["p"] < backend._fips_dsa_min_modulus
|
|
):
|
|
pytest.skip("Small modulus blocked in FIPS mode")
|
|
parameters = dsa.DSAParameterNumbers(
|
|
p=vector["p"], q=vector["q"], g=vector["g"]
|
|
).parameters(backend)
|
|
skey = parameters.generate_private_key()
|
|
numbers = skey.private_numbers()
|
|
skey_parameters = numbers.public_numbers.parameter_numbers
|
|
pkey = skey.public_key()
|
|
parameters = pkey.parameters()
|
|
parameter_numbers = parameters.parameter_numbers()
|
|
assert parameter_numbers.p == skey_parameters.p
|
|
assert parameter_numbers.q == skey_parameters.q
|
|
assert parameter_numbers.g == skey_parameters.g
|
|
assert skey_parameters.p == vector["p"]
|
|
assert skey_parameters.q == vector["q"]
|
|
assert skey_parameters.g == vector["g"]
|
|
assert skey.key_size == vector["p"].bit_length()
|
|
assert pkey.key_size == skey.key_size
|
|
public_numbers = pkey.public_numbers()
|
|
assert numbers.public_numbers.y == public_numbers.y
|
|
assert numbers.public_numbers.y == pow(
|
|
skey_parameters.g, numbers.x, skey_parameters.p
|
|
)
|
|
|
|
def test_generate_dsa_private_key_and_parameters(self, backend):
|
|
skey = dsa.generate_private_key(2048, backend)
|
|
assert skey
|
|
numbers = skey.private_numbers()
|
|
skey_parameters = numbers.public_numbers.parameter_numbers
|
|
assert numbers.public_numbers.y == pow(
|
|
skey_parameters.g, numbers.x, skey_parameters.p
|
|
)
|
|
|
|
@pytest.mark.parametrize(
|
|
("p", "q", "g"),
|
|
[
|
|
(
|
|
2 ** 1000,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.g,
|
|
),
|
|
(
|
|
2 ** 2000,
|
|
DSA_KEY_2048.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_2048.public_numbers.parameter_numbers.g,
|
|
),
|
|
(
|
|
2 ** 3000,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.g,
|
|
),
|
|
(
|
|
2 ** 3100,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.g,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
2 ** 150,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.g,
|
|
),
|
|
(
|
|
DSA_KEY_2048.public_numbers.parameter_numbers.p,
|
|
2 ** 250,
|
|
DSA_KEY_2048.public_numbers.parameter_numbers.g,
|
|
),
|
|
(
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.p,
|
|
2 ** 260,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.g,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
0,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
1,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
2 ** 1200,
|
|
),
|
|
],
|
|
)
|
|
def test_invalid_parameters_values(self, p, q, g, backend):
|
|
with pytest.raises(ValueError):
|
|
dsa.DSAParameterNumbers(p, q, g).parameters(backend)
|
|
|
|
@pytest.mark.parametrize(
|
|
("p", "q", "g", "y", "x"),
|
|
[
|
|
(
|
|
2 ** 1000,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
DSA_KEY_1024.x,
|
|
),
|
|
(
|
|
2 ** 2000,
|
|
DSA_KEY_2048.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_2048.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_2048.public_numbers.y,
|
|
DSA_KEY_2048.x,
|
|
),
|
|
(
|
|
2 ** 3000,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_3072.public_numbers.y,
|
|
DSA_KEY_3072.x,
|
|
),
|
|
(
|
|
2 ** 3100,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_3072.public_numbers.y,
|
|
DSA_KEY_3072.x,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
2 ** 150,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
DSA_KEY_1024.x,
|
|
),
|
|
(
|
|
DSA_KEY_2048.public_numbers.parameter_numbers.p,
|
|
2 ** 250,
|
|
DSA_KEY_2048.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_2048.public_numbers.y,
|
|
DSA_KEY_2048.x,
|
|
),
|
|
(
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.p,
|
|
2 ** 260,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_3072.public_numbers.y,
|
|
DSA_KEY_3072.x,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
0,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
DSA_KEY_1024.x,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
1,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
DSA_KEY_1024.x,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
2 ** 1200,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
DSA_KEY_1024.x,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
0,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
-2,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
2 ** 159,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
2 ** 200,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.g,
|
|
2 ** 100,
|
|
DSA_KEY_1024.x,
|
|
),
|
|
],
|
|
)
|
|
def test_invalid_dsa_private_key_arguments(self, p, q, g, y, x, backend):
|
|
with pytest.raises(ValueError):
|
|
dsa.DSAPrivateNumbers(
|
|
public_numbers=dsa.DSAPublicNumbers(
|
|
parameter_numbers=dsa.DSAParameterNumbers(p=p, q=q, g=g),
|
|
y=y,
|
|
),
|
|
x=x,
|
|
).private_key(backend)
|
|
|
|
@pytest.mark.parametrize(
|
|
("p", "q", "g", "y"),
|
|
[
|
|
(
|
|
2 ** 1000,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
),
|
|
(
|
|
2 ** 2000,
|
|
DSA_KEY_2048.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_2048.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_2048.public_numbers.y,
|
|
),
|
|
(
|
|
2 ** 3000,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_3072.public_numbers.y,
|
|
),
|
|
(
|
|
2 ** 3100,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.q,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_3072.public_numbers.y,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
2 ** 150,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
),
|
|
(
|
|
DSA_KEY_2048.public_numbers.parameter_numbers.p,
|
|
2 ** 250,
|
|
DSA_KEY_2048.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_2048.public_numbers.y,
|
|
),
|
|
(
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.p,
|
|
2 ** 260,
|
|
DSA_KEY_3072.public_numbers.parameter_numbers.g,
|
|
DSA_KEY_3072.public_numbers.y,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
0,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
1,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
),
|
|
(
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.p,
|
|
DSA_KEY_1024.public_numbers.parameter_numbers.q,
|
|
2 ** 1200,
|
|
DSA_KEY_1024.public_numbers.y,
|
|
),
|
|
],
|
|
)
|
|
def test_invalid_dsa_public_key_arguments(self, p, q, g, y, backend):
|
|
with pytest.raises(ValueError):
|
|
dsa.DSAPublicNumbers(
|
|
parameter_numbers=dsa.DSAParameterNumbers(p=p, q=q, g=g), y=y
|
|
).public_key(backend)
|
|
|
|
def test_large_p(self, backend):
|
|
key = load_vectors_from_file(
|
|
os.path.join("asymmetric", "PEM_Serialization", "dsa_4096.pem"),
|
|
lambda pemfile: serialization.load_pem_private_key(
|
|
pemfile.read(), None, backend
|
|
),
|
|
mode="rb",
|
|
)
|
|
pn = key.private_numbers()
|
|
assert pn.public_numbers.parameter_numbers.p.bit_length() == 4096
|
|
# Turn it back into a key to confirm that values this large pass
|
|
# verification
|
|
dsa.DSAPrivateNumbers(
|
|
public_numbers=dsa.DSAPublicNumbers(
|
|
parameter_numbers=dsa.DSAParameterNumbers(
|
|
p=pn.public_numbers.parameter_numbers.p,
|
|
q=pn.public_numbers.parameter_numbers.q,
|
|
g=pn.public_numbers.parameter_numbers.g,
|
|
),
|
|
y=pn.public_numbers.y,
|
|
),
|
|
x=pn.x,
|
|
).private_key(backend)
|
|
|
|
|
|
@pytest.mark.requires_backend_interface(interface=DSABackend)
|
|
class TestDSAVerification(object):
|
|
_algorithms_dict = {
|
|
"SHA1": hashes.SHA1,
|
|
"SHA224": hashes.SHA224,
|
|
"SHA256": hashes.SHA256,
|
|
"SHA384": hashes.SHA384,
|
|
"SHA512": hashes.SHA512,
|
|
}
|
|
|
|
@pytest.mark.parametrize(
|
|
"vector",
|
|
load_vectors_from_file(
|
|
os.path.join("asymmetric", "DSA", "FIPS_186-3", "SigVer.rsp"),
|
|
load_fips_dsa_sig_vectors,
|
|
),
|
|
)
|
|
def test_dsa_verification(self, vector, backend):
|
|
digest_algorithm = vector["digest_algorithm"].replace("-", "")
|
|
algorithm = self._algorithms_dict[digest_algorithm]
|
|
|
|
_skip_if_dsa_not_supported(
|
|
backend, algorithm, vector["p"], vector["q"], vector["g"]
|
|
)
|
|
|
|
public_key = dsa.DSAPublicNumbers(
|
|
parameter_numbers=dsa.DSAParameterNumbers(
|
|
vector["p"], vector["q"], vector["g"]
|
|
),
|
|
y=vector["y"],
|
|
).public_key(backend)
|
|
sig = encode_dss_signature(vector["r"], vector["s"])
|
|
|
|
if vector["result"] == "F":
|
|
with pytest.raises(InvalidSignature):
|
|
public_key.verify(sig, vector["msg"], algorithm())
|
|
else:
|
|
public_key.verify(sig, vector["msg"], algorithm())
|
|
|
|
def test_dsa_verify_invalid_asn1(self, backend):
|
|
public_key = DSA_KEY_1024.public_numbers.public_key(backend)
|
|
with pytest.raises(InvalidSignature):
|
|
public_key.verify(b"fakesig", b"fakemsg", hashes.SHA1())
|
|
|
|
def test_signature_not_bytes(self, backend):
|
|
public_key = DSA_KEY_1024.public_numbers.public_key(backend)
|
|
with pytest.raises(TypeError), pytest.warns(
|
|
CryptographyDeprecationWarning
|
|
):
|
|
public_key.verifier(1234, hashes.SHA1())
|
|
|
|
def test_use_after_finalize(self, backend):
|
|
public_key = DSA_KEY_1024.public_numbers.public_key(backend)
|
|
with pytest.warns(CryptographyDeprecationWarning):
|
|
verifier = public_key.verifier(b"fakesig", hashes.SHA1())
|
|
verifier.update(b"irrelevant")
|
|
with pytest.raises(InvalidSignature):
|
|
verifier.verify()
|
|
with pytest.raises(AlreadyFinalized):
|
|
verifier.verify()
|
|
with pytest.raises(AlreadyFinalized):
|
|
verifier.update(b"more data")
|
|
|
|
def test_verify(self, backend):
|
|
message = b"one little message"
|
|
algorithm = hashes.SHA1()
|
|
private_key = DSA_KEY_1024.private_key(backend)
|
|
signature = private_key.sign(message, algorithm)
|
|
public_key = private_key.public_key()
|
|
public_key.verify(signature, message, algorithm)
|
|
|
|
def test_prehashed_verify(self, backend):
|
|
private_key = DSA_KEY_1024.private_key(backend)
|
|
message = b"one little message"
|
|
h = hashes.Hash(hashes.SHA1(), backend)
|
|
h.update(message)
|
|
digest = h.finalize()
|
|
prehashed_alg = Prehashed(hashes.SHA1())
|
|
signature = private_key.sign(message, hashes.SHA1())
|
|
public_key = private_key.public_key()
|
|
public_key.verify(signature, digest, prehashed_alg)
|
|
|
|
def test_prehashed_digest_mismatch(self, backend):
|
|
private_key = DSA_KEY_1024.private_key(backend)
|
|
public_key = private_key.public_key()
|
|
message = b"one little message"
|
|
h = hashes.Hash(hashes.SHA1(), backend)
|
|
h.update(message)
|
|
digest = h.finalize()
|
|
prehashed_alg = Prehashed(hashes.SHA224())
|
|
with pytest.raises(ValueError):
|
|
public_key.verify(b"\x00" * 128, digest, prehashed_alg)
|
|
|
|
def test_prehashed_unsupported_in_signer_ctx(self, backend):
|
|
private_key = DSA_KEY_1024.private_key(backend)
|
|
with pytest.raises(TypeError), pytest.warns(
|
|
CryptographyDeprecationWarning
|
|
):
|
|
private_key.signer(Prehashed(hashes.SHA1()))
|
|
|
|
def test_prehashed_unsupported_in_verifier_ctx(self, backend):
|
|
public_key = DSA_KEY_1024.private_key(backend).public_key()
|
|
with pytest.raises(TypeError), pytest.warns(
|
|
CryptographyDeprecationWarning
|
|
):
|
|
public_key.verifier(b"0" * 64, Prehashed(hashes.SHA1()))
|
|
|
|
|
|
@pytest.mark.requires_backend_interface(interface=DSABackend)
|
|
class TestDSASignature(object):
|
|
_algorithms_dict = {
|
|
"SHA1": hashes.SHA1,
|
|
"SHA224": hashes.SHA224,
|
|
"SHA256": hashes.SHA256,
|
|
"SHA384": hashes.SHA384,
|
|
"SHA512": hashes.SHA512,
|
|
}
|
|
|
|
@pytest.mark.parametrize(
|
|
"vector",
|
|
load_vectors_from_file(
|
|
os.path.join("asymmetric", "DSA", "FIPS_186-3", "SigGen.txt"),
|
|
load_fips_dsa_sig_vectors,
|
|
),
|
|
)
|
|
def test_dsa_signing(self, vector, backend):
|
|
digest_algorithm = vector["digest_algorithm"].replace("-", "")
|
|
algorithm = self._algorithms_dict[digest_algorithm]
|
|
|
|
_skip_if_dsa_not_supported(
|
|
backend, algorithm, vector["p"], vector["q"], vector["g"]
|
|
)
|
|
|
|
private_key = dsa.DSAPrivateNumbers(
|
|
public_numbers=dsa.DSAPublicNumbers(
|
|
parameter_numbers=dsa.DSAParameterNumbers(
|
|
vector["p"], vector["q"], vector["g"]
|
|
),
|
|
y=vector["y"],
|
|
),
|
|
x=vector["x"],
|
|
).private_key(backend)
|
|
signature = private_key.sign(vector["msg"], algorithm())
|
|
assert signature
|
|
|
|
private_key.public_key().verify(signature, vector["msg"], algorithm())
|
|
|
|
def test_use_after_finalize(self, backend):
|
|
private_key = DSA_KEY_1024.private_key(backend)
|
|
with pytest.warns(CryptographyDeprecationWarning):
|
|
signer = private_key.signer(hashes.SHA1())
|
|
signer.update(b"data")
|
|
signer.finalize()
|
|
with pytest.raises(AlreadyFinalized):
|
|
signer.finalize()
|
|
with pytest.raises(AlreadyFinalized):
|
|
signer.update(b"more data")
|
|
|
|
def test_sign(self, backend):
|
|
private_key = DSA_KEY_1024.private_key(backend)
|
|
message = b"one little message"
|
|
algorithm = hashes.SHA1()
|
|
signature = private_key.sign(message, algorithm)
|
|
public_key = private_key.public_key()
|
|
public_key.verify(signature, message, algorithm)
|
|
|
|
def test_prehashed_sign(self, backend):
|
|
private_key = DSA_KEY_1024.private_key(backend)
|
|
message = b"one little message"
|
|
h = hashes.Hash(hashes.SHA1(), backend)
|
|
h.update(message)
|
|
digest = h.finalize()
|
|
prehashed_alg = Prehashed(hashes.SHA1())
|
|
signature = private_key.sign(digest, prehashed_alg)
|
|
public_key = private_key.public_key()
|
|
public_key.verify(signature, message, hashes.SHA1())
|
|
|
|
def test_prehashed_digest_mismatch(self, backend):
|
|
private_key = DSA_KEY_1024.private_key(backend)
|
|
message = b"one little message"
|
|
h = hashes.Hash(hashes.SHA1(), backend)
|
|
h.update(message)
|
|
digest = h.finalize()
|
|
prehashed_alg = Prehashed(hashes.SHA224())
|
|
with pytest.raises(ValueError):
|
|
private_key.sign(digest, prehashed_alg)
|
|
|
|
|
|
class TestDSANumbers(object):
|
|
def test_dsa_parameter_numbers(self):
|
|
parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
|
|
assert parameter_numbers.p == 1
|
|
assert parameter_numbers.q == 2
|
|
assert parameter_numbers.g == 3
|
|
|
|
def test_dsa_parameter_numbers_invalid_types(self):
|
|
with pytest.raises(TypeError):
|
|
dsa.DSAParameterNumbers(p=None, q=2, g=3)
|
|
|
|
with pytest.raises(TypeError):
|
|
dsa.DSAParameterNumbers(p=1, q=None, g=3)
|
|
|
|
with pytest.raises(TypeError):
|
|
dsa.DSAParameterNumbers(p=1, q=2, g=None)
|
|
|
|
def test_dsa_public_numbers(self):
|
|
parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
|
|
public_numbers = dsa.DSAPublicNumbers(
|
|
y=4, parameter_numbers=parameter_numbers
|
|
)
|
|
assert public_numbers.y == 4
|
|
assert public_numbers.parameter_numbers == parameter_numbers
|
|
|
|
def test_dsa_public_numbers_invalid_types(self):
|
|
with pytest.raises(TypeError):
|
|
dsa.DSAPublicNumbers(y=4, parameter_numbers=None)
|
|
|
|
with pytest.raises(TypeError):
|
|
parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
|
|
dsa.DSAPublicNumbers(y=None, parameter_numbers=parameter_numbers)
|
|
|
|
def test_dsa_private_numbers(self):
|
|
parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
|
|
public_numbers = dsa.DSAPublicNumbers(
|
|
y=4, parameter_numbers=parameter_numbers
|
|
)
|
|
private_numbers = dsa.DSAPrivateNumbers(
|
|
x=5, public_numbers=public_numbers
|
|
)
|
|
assert private_numbers.x == 5
|
|
assert private_numbers.public_numbers == public_numbers
|
|
|
|
def test_dsa_private_numbers_invalid_types(self):
|
|
parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
|
|
public_numbers = dsa.DSAPublicNumbers(
|
|
y=4, parameter_numbers=parameter_numbers
|
|
)
|
|
with pytest.raises(TypeError):
|
|
dsa.DSAPrivateNumbers(x=4, public_numbers=None)
|
|
|
|
with pytest.raises(TypeError):
|
|
dsa.DSAPrivateNumbers(x=None, public_numbers=public_numbers)
|
|
|
|
def test_repr(self):
|
|
parameter_numbers = dsa.DSAParameterNumbers(p=1, q=2, g=3)
|
|
assert (
|
|
repr(parameter_numbers) == "<DSAParameterNumbers(p=1, q=2, g=3)>"
|
|
)
|
|
|
|
public_numbers = dsa.DSAPublicNumbers(
|
|
y=4, parameter_numbers=parameter_numbers
|
|
)
|
|
assert repr(public_numbers) == (
|
|
"<DSAPublicNumbers(y=4, parameter_numbers=<DSAParameterNumbers(p=1"
|
|
", q=2, g=3)>)>"
|
|
)
|
|
|
|
|
|
class TestDSANumberEquality(object):
|
|
def test_parameter_numbers_eq(self):
|
|
param = dsa.DSAParameterNumbers(1, 2, 3)
|
|
assert param == dsa.DSAParameterNumbers(1, 2, 3)
|
|
|
|
def test_parameter_numbers_ne(self):
|
|
param = dsa.DSAParameterNumbers(1, 2, 3)
|
|
assert param != dsa.DSAParameterNumbers(1, 2, 4)
|
|
assert param != dsa.DSAParameterNumbers(1, 1, 3)
|
|
assert param != dsa.DSAParameterNumbers(2, 2, 3)
|
|
assert param != object()
|
|
|
|
def test_public_numbers_eq(self):
|
|
pub = dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
|
|
assert pub == dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
|
|
|
|
def test_public_numbers_ne(self):
|
|
pub = dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
|
|
assert pub != dsa.DSAPublicNumbers(2, dsa.DSAParameterNumbers(1, 2, 3))
|
|
assert pub != dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(2, 2, 3))
|
|
assert pub != dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 3, 3))
|
|
assert pub != dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 4))
|
|
assert pub != object()
|
|
|
|
def test_private_numbers_eq(self):
|
|
pub = dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
|
|
priv = dsa.DSAPrivateNumbers(1, pub)
|
|
assert priv == dsa.DSAPrivateNumbers(
|
|
1, dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
|
|
)
|
|
|
|
def test_private_numbers_ne(self):
|
|
pub = dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
|
|
priv = dsa.DSAPrivateNumbers(1, pub)
|
|
assert priv != dsa.DSAPrivateNumbers(
|
|
2, dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 3))
|
|
)
|
|
assert priv != dsa.DSAPrivateNumbers(
|
|
1, dsa.DSAPublicNumbers(2, dsa.DSAParameterNumbers(1, 2, 3))
|
|
)
|
|
assert priv != dsa.DSAPrivateNumbers(
|
|
1, dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(2, 2, 3))
|
|
)
|
|
assert priv != dsa.DSAPrivateNumbers(
|
|
1, dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 3, 3))
|
|
)
|
|
assert priv != dsa.DSAPrivateNumbers(
|
|
1, dsa.DSAPublicNumbers(1, dsa.DSAParameterNumbers(1, 2, 4))
|
|
)
|
|
assert priv != object()
|
|
|
|
|
|
@pytest.mark.requires_backend_interface(interface=DSABackend)
|
|
@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
|
|
class TestDSASerialization(object):
|
|
@pytest.mark.parametrize(
|
|
("fmt", "password"),
|
|
itertools.product(
|
|
[
|
|
serialization.PrivateFormat.TraditionalOpenSSL,
|
|
serialization.PrivateFormat.PKCS8,
|
|
],
|
|
[
|
|
b"s",
|
|
b"longerpassword",
|
|
b"!*$&(@#$*&($T@%_somesymbols",
|
|
b"\x01" * 1000,
|
|
],
|
|
),
|
|
)
|
|
def test_private_bytes_encrypted_pem(self, backend, fmt, password):
|
|
skip_fips_traditional_openssl(backend, fmt)
|
|
key_bytes = load_vectors_from_file(
|
|
os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
|
|
lambda pemfile: pemfile.read().encode(),
|
|
)
|
|
key = serialization.load_pem_private_key(key_bytes, None, backend)
|
|
serialized = key.private_bytes(
|
|
serialization.Encoding.PEM,
|
|
fmt,
|
|
serialization.BestAvailableEncryption(password),
|
|
)
|
|
loaded_key = serialization.load_pem_private_key(
|
|
serialized, password, 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):
|
|
key = DSA_KEY_1024.private_key(backend)
|
|
with pytest.raises(ValueError):
|
|
key.private_bytes(encoding, fmt, serialization.NoEncryption())
|
|
|
|
@pytest.mark.parametrize(
|
|
("fmt", "password"),
|
|
[
|
|
[serialization.PrivateFormat.PKCS8, b"s"],
|
|
[serialization.PrivateFormat.PKCS8, b"longerpassword"],
|
|
[serialization.PrivateFormat.PKCS8, b"!*$&(@#$*&($T@%_somesymbol"],
|
|
[serialization.PrivateFormat.PKCS8, b"\x01" * 1000],
|
|
],
|
|
)
|
|
def test_private_bytes_encrypted_der(self, backend, fmt, password):
|
|
key_bytes = load_vectors_from_file(
|
|
os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
|
|
lambda pemfile: pemfile.read().encode(),
|
|
)
|
|
key = serialization.load_pem_private_key(key_bytes, None, backend)
|
|
serialized = key.private_bytes(
|
|
serialization.Encoding.DER,
|
|
fmt,
|
|
serialization.BestAvailableEncryption(password),
|
|
)
|
|
loaded_key = serialization.load_der_private_key(
|
|
serialized, password, backend
|
|
)
|
|
loaded_priv_num = loaded_key.private_numbers()
|
|
priv_num = key.private_numbers()
|
|
assert loaded_priv_num == priv_num
|
|
|
|
@pytest.mark.parametrize(
|
|
("encoding", "fmt", "loader_func"),
|
|
[
|
|
[
|
|
serialization.Encoding.PEM,
|
|
serialization.PrivateFormat.TraditionalOpenSSL,
|
|
serialization.load_pem_private_key,
|
|
],
|
|
[
|
|
serialization.Encoding.DER,
|
|
serialization.PrivateFormat.TraditionalOpenSSL,
|
|
serialization.load_der_private_key,
|
|
],
|
|
[
|
|
serialization.Encoding.PEM,
|
|
serialization.PrivateFormat.PKCS8,
|
|
serialization.load_pem_private_key,
|
|
],
|
|
[
|
|
serialization.Encoding.DER,
|
|
serialization.PrivateFormat.PKCS8,
|
|
serialization.load_der_private_key,
|
|
],
|
|
],
|
|
)
|
|
def test_private_bytes_unencrypted(
|
|
self, backend, encoding, fmt, loader_func
|
|
):
|
|
key = DSA_KEY_1024.private_key(backend)
|
|
serialized = key.private_bytes(
|
|
encoding, fmt, 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.skip_fips(
|
|
reason="Traditional OpenSSL key format is not supported in FIPS mode."
|
|
)
|
|
@pytest.mark.parametrize(
|
|
("key_path", "encoding", "loader_func"),
|
|
[
|
|
[
|
|
os.path.join(
|
|
"asymmetric",
|
|
"Traditional_OpenSSL_Serialization",
|
|
"dsa.1024.pem",
|
|
),
|
|
serialization.Encoding.PEM,
|
|
serialization.load_pem_private_key,
|
|
],
|
|
[
|
|
os.path.join(
|
|
"asymmetric", "DER_Serialization", "dsa.1024.der"
|
|
),
|
|
serialization.Encoding.DER,
|
|
serialization.load_der_private_key,
|
|
],
|
|
],
|
|
)
|
|
def test_private_bytes_traditional_openssl_unencrypted(
|
|
self, backend, key_path, encoding, loader_func
|
|
):
|
|
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.TraditionalOpenSSL,
|
|
serialization.NoEncryption(),
|
|
)
|
|
assert serialized == key_bytes
|
|
|
|
def test_private_bytes_traditional_der_encrypted_invalid(self, backend):
|
|
key = DSA_KEY_1024.private_key(backend)
|
|
with pytest.raises(ValueError):
|
|
key.private_bytes(
|
|
serialization.Encoding.DER,
|
|
serialization.PrivateFormat.TraditionalOpenSSL,
|
|
serialization.BestAvailableEncryption(b"password"),
|
|
)
|
|
|
|
def test_private_bytes_invalid_encoding(self, backend):
|
|
key = load_vectors_from_file(
|
|
os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
|
|
lambda pemfile: serialization.load_pem_private_key(
|
|
pemfile.read().encode(), None, backend
|
|
),
|
|
)
|
|
with pytest.raises(TypeError):
|
|
key.private_bytes(
|
|
"notencoding",
|
|
serialization.PrivateFormat.PKCS8,
|
|
serialization.NoEncryption(),
|
|
)
|
|
|
|
def test_private_bytes_invalid_format(self, backend):
|
|
key = load_vectors_from_file(
|
|
os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
|
|
lambda pemfile: serialization.load_pem_private_key(
|
|
pemfile.read().encode(), None, backend
|
|
),
|
|
)
|
|
with pytest.raises(TypeError):
|
|
key.private_bytes(
|
|
serialization.Encoding.PEM,
|
|
"invalidformat",
|
|
serialization.NoEncryption(),
|
|
)
|
|
|
|
def test_private_bytes_invalid_encryption_algorithm(self, backend):
|
|
key = load_vectors_from_file(
|
|
os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
|
|
lambda pemfile: serialization.load_pem_private_key(
|
|
pemfile.read().encode(), None, backend
|
|
),
|
|
)
|
|
with pytest.raises(TypeError):
|
|
key.private_bytes(
|
|
serialization.Encoding.PEM,
|
|
serialization.PrivateFormat.TraditionalOpenSSL,
|
|
"notanencalg",
|
|
)
|
|
|
|
def test_private_bytes_unsupported_encryption_type(self, backend):
|
|
key = load_vectors_from_file(
|
|
os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pem"),
|
|
lambda pemfile: serialization.load_pem_private_key(
|
|
pemfile.read().encode(), None, backend
|
|
),
|
|
)
|
|
with pytest.raises(ValueError):
|
|
key.private_bytes(
|
|
serialization.Encoding.PEM,
|
|
serialization.PrivateFormat.TraditionalOpenSSL,
|
|
DummyKeySerializationEncryption(),
|
|
)
|
|
|
|
|
|
@pytest.mark.requires_backend_interface(interface=DSABackend)
|
|
@pytest.mark.requires_backend_interface(interface=PEMSerializationBackend)
|
|
class TestDSAPEMPublicKeySerialization(object):
|
|
@pytest.mark.parametrize(
|
|
("key_path", "loader_func", "encoding"),
|
|
[
|
|
(
|
|
os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pub.pem"),
|
|
serialization.load_pem_public_key,
|
|
serialization.Encoding.PEM,
|
|
),
|
|
(
|
|
os.path.join(
|
|
"asymmetric",
|
|
"DER_Serialization",
|
|
"unenc-dsa-pkcs8.pub.der",
|
|
),
|
|
serialization.load_der_public_key,
|
|
serialization.Encoding.DER,
|
|
),
|
|
],
|
|
)
|
|
def test_public_bytes_match(
|
|
self, key_path, loader_func, encoding, backend
|
|
):
|
|
key_bytes = load_vectors_from_file(
|
|
key_path, lambda pemfile: pemfile.read(), mode="rb"
|
|
)
|
|
key = loader_func(key_bytes, backend)
|
|
serialized = key.public_bytes(
|
|
encoding,
|
|
serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
)
|
|
assert serialized == key_bytes
|
|
|
|
def test_public_bytes_openssh(self, backend):
|
|
key_bytes = load_vectors_from_file(
|
|
os.path.join("asymmetric", "PKCS8", "unenc-dsa-pkcs8.pub.pem"),
|
|
lambda pemfile: pemfile.read(),
|
|
mode="rb",
|
|
)
|
|
key = serialization.load_pem_public_key(key_bytes, backend)
|
|
|
|
ssh_bytes = key.public_bytes(
|
|
serialization.Encoding.OpenSSH, serialization.PublicFormat.OpenSSH
|
|
)
|
|
assert ssh_bytes == (
|
|
b"ssh-dss AAAAB3NzaC1kc3MAAACBAKoJMMwUWCUiHK/6KKwolBlqJ4M95ewhJweR"
|
|
b"aJQgd3Si57I4sNNvGySZosJYUIPrAUMpJEGNhn+qIS3RBx1NzrJ4J5StOTzAik1K"
|
|
b"2n9o1ug5pfzTS05ALYLLioy0D+wxkRv5vTYLA0yqy0xelHmSVzyekAmcGw8FlAyr"
|
|
b"5dLeSaFnAAAAFQCtwOhps28KwBOmgf301ImdaYIEUQAAAIEAjGtFia+lOk0QSL/D"
|
|
b"RtHzhsp1UhzPct2qJRKGiA7hMgH/SIkLv8M9ebrK7HHnp3hQe9XxpmQi45QVvgPn"
|
|
b"EUG6Mk9bkxMZKRgsiKn6QGKDYGbOvnS1xmkMfRARBsJAq369VOTjMB/Qhs5q2ski"
|
|
b"+ycTorCIfLoTubxozlz/8kHNMkYAAACAKyYOqX3GoSrpMsZA5989j/BKigWgMk+N"
|
|
b"Xxsj8V+hcP8/QgYRJO/yWGyxG0moLc3BuQ/GqE+xAQnLZ9tdLalxrq8Xvl43KEVj"
|
|
b"5MZNnl/ISAJYsxnw3inVTYNQcNnih5FNd9+BSR9EI7YtqYTrP0XrKin86l2uUlrG"
|
|
b"q2vM4Ev99bY="
|
|
)
|
|
|
|
def test_public_bytes_invalid_encoding(self, backend):
|
|
key = DSA_KEY_2048.private_key(backend).public_key()
|
|
with pytest.raises(TypeError):
|
|
key.public_bytes(
|
|
"notencoding", serialization.PublicFormat.SubjectPublicKeyInfo
|
|
)
|
|
|
|
def test_public_bytes_invalid_format(self, backend):
|
|
key = DSA_KEY_2048.private_key(backend).public_key()
|
|
with pytest.raises(TypeError):
|
|
key.public_bytes(serialization.Encoding.PEM, "invalidformat")
|
|
|
|
def test_public_bytes_pkcs1_unsupported(self, backend):
|
|
key = DSA_KEY_2048.private_key(backend).public_key()
|
|
with pytest.raises(ValueError):
|
|
key.public_bytes(
|
|
serialization.Encoding.PEM, serialization.PublicFormat.PKCS1
|
|
)
|
|
|
|
@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):
|
|
key = DSA_KEY_2048.private_key(backend).public_key()
|
|
with pytest.raises(ValueError):
|
|
key.public_bytes(encoding, fmt)
|