blob: 877607fb1f7b635b70e6ae59ce06e027a39c4cee [file]
# 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.
import binascii
import pytest
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives.asymmetric.mldsa import (
MlDsa44PrivateKey,
MlDsa44PublicKey,
MlDsa65PrivateKey,
MlDsa65PublicKey,
)
from .utils import wycheproof_tests
@pytest.mark.supported(
only_if=lambda backend: backend.mldsa_supported(),
skip_message="Requires a backend with ML-DSA support",
)
@wycheproof_tests("mldsa_44_verify_test.json")
def test_mldsa44_verify(backend, wycheproof):
try:
pub = MlDsa44PublicKey.from_public_bytes(
binascii.unhexlify(wycheproof.testgroup["publicKey"])
)
except ValueError:
assert wycheproof.invalid
assert wycheproof.has_flag("IncorrectPublicKeyLength")
return
msg = binascii.unhexlify(wycheproof.testcase["msg"])
sig = binascii.unhexlify(wycheproof.testcase["sig"])
has_ctx = "ctx" in wycheproof.testcase
ctx = binascii.unhexlify(wycheproof.testcase["ctx"]) if has_ctx else None
if wycheproof.valid:
pub.verify(sig, msg, ctx)
else:
with pytest.raises(
(
ValueError,
InvalidSignature,
)
):
pub.verify(sig, msg, ctx)
@pytest.mark.supported(
only_if=lambda backend: backend.mldsa_supported(),
skip_message="Requires a backend with ML-DSA support",
)
@wycheproof_tests("mldsa_44_sign_seed_test.json")
def test_mldsa44_sign_seed(backend, wycheproof):
# Skip "Internal" tests, they use the inner method `Sign_internal`
# instead of `Sign` which we do not expose.
if wycheproof.has_flag("Internal"):
return
seed = binascii.unhexlify(wycheproof.testgroup["privateSeed"])
try:
key = MlDsa44PrivateKey.from_seed_bytes(seed)
except ValueError:
assert wycheproof.invalid
assert wycheproof.has_flag("IncorrectPrivateKeyLength")
return
pub = MlDsa44PublicKey.from_public_bytes(
binascii.unhexlify(wycheproof.testgroup["publicKey"])
)
assert key.public_key() == pub
msg = binascii.unhexlify(wycheproof.testcase["msg"])
has_ctx = "ctx" in wycheproof.testcase
ctx = binascii.unhexlify(wycheproof.testcase["ctx"]) if has_ctx else None
if wycheproof.valid or wycheproof.acceptable:
# Sign and verify round-trip. We don't compare exact signature
# bytes because some backends use hedged (randomized) signing.
sig = key.sign(msg, ctx)
pub.verify(sig, msg, ctx)
else:
with pytest.raises(ValueError):
assert has_ctx
key.sign(msg, ctx)
@pytest.mark.supported(
only_if=lambda backend: backend.mldsa_supported(),
skip_message="Requires a backend with ML-DSA support",
)
@wycheproof_tests("mldsa_65_verify_test.json")
def test_mldsa65_verify(backend, wycheproof):
try:
pub = MlDsa65PublicKey.from_public_bytes(
binascii.unhexlify(wycheproof.testgroup["publicKey"])
)
except ValueError:
assert wycheproof.invalid
assert wycheproof.has_flag("IncorrectPublicKeyLength")
return
msg = binascii.unhexlify(wycheproof.testcase["msg"])
sig = binascii.unhexlify(wycheproof.testcase["sig"])
has_ctx = "ctx" in wycheproof.testcase
ctx = binascii.unhexlify(wycheproof.testcase["ctx"]) if has_ctx else None
if wycheproof.valid:
pub.verify(sig, msg, ctx)
else:
with pytest.raises(
(
ValueError,
InvalidSignature,
)
):
pub.verify(sig, msg, ctx)
@pytest.mark.supported(
only_if=lambda backend: backend.mldsa_supported(),
skip_message="Requires a backend with ML-DSA support",
)
@wycheproof_tests("mldsa_65_sign_seed_test.json")
def test_mldsa65_sign_seed(backend, wycheproof):
# Skip "Internal" tests, they use the inner method `Sign_internal`
# instead of `Sign` which we do not expose.
if wycheproof.has_flag("Internal"):
return
seed = binascii.unhexlify(wycheproof.testgroup["privateSeed"])
try:
key = MlDsa65PrivateKey.from_seed_bytes(seed)
except ValueError:
assert wycheproof.invalid
assert wycheproof.has_flag("IncorrectPrivateKeyLength")
return
pub = MlDsa65PublicKey.from_public_bytes(
binascii.unhexlify(wycheproof.testgroup["publicKey"])
)
assert key.public_key() == pub
msg = binascii.unhexlify(wycheproof.testcase["msg"])
has_ctx = "ctx" in wycheproof.testcase
ctx = binascii.unhexlify(wycheproof.testcase["ctx"]) if has_ctx else None
if wycheproof.valid or wycheproof.acceptable:
# Sign and verify round-trip. We don't compare exact signature
# bytes because some backends use hedged (randomized) signing.
sig = key.sign(msg, ctx)
pub.verify(sig, msg, ctx)
else:
with pytest.raises(ValueError):
assert has_ctx
key.sign(msg, ctx)