mirror of
https://github.com/RustCrypto/signatures
synced 2026-06-21 13:45:42 +00:00
ml-dsa: add Wycheproof verification tests (#1176)
Adds Wycheproof verification tests for ML-DSA-44/65/87. On ML-DSA-44, this reproduced an issue previously reported as GHSA-5x2r-hc65-25f9 by @orenyomtov: > Test #18: signature with a repeated hint (Invalid) Likewise, on ML-DSA-44 only we are also failing this case: > Test #68: public key with t1 component set to zero (Valid) I have added an exceptions for those two for now, and all the other tests pass. I opened #1183 as a tracking issue for these verification failures.
This commit is contained in:
+87
-16
@@ -2,9 +2,9 @@
|
||||
|
||||
// Implementation is based in part on `rsa` which is in turn based on Graviola.
|
||||
|
||||
use ml_dsa::{KeyGen, MlDsa44, MlDsa65, MlDsa87};
|
||||
use ml_dsa::{KeyGen, MlDsa44, MlDsa65, MlDsa87, Signature, VerifyingKey};
|
||||
use serde::Deserialize;
|
||||
use signature::{SignatureEncoding, Signer};
|
||||
use signature::{SignatureEncoding, Signer, Verifier};
|
||||
use std::fs::File;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
@@ -53,18 +53,25 @@ enum ExpectedResult {
|
||||
Acceptable,
|
||||
}
|
||||
|
||||
macro_rules! mldsa_sign_from_seed_test {
|
||||
macro_rules! load_json_file {
|
||||
($json_file:expr) => {{
|
||||
let path = format!("../thirdparty/wycheproof/testvectors_v1/{}", $json_file);
|
||||
let data_file = File::open(&path)
|
||||
.expect("failed to open data file (try running `git submodule update --init`)");
|
||||
|
||||
println!("Loading file: {path}");
|
||||
|
||||
let tests: TestFile = serde_json::from_reader(data_file).expect("invalid test JSON");
|
||||
println!("{}:\n{}\n", tests.algorithm, tests.header.join(""));
|
||||
tests
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! mldsa_sign_seed_test {
|
||||
($name:ident, $json_file:expr, $keypair:ident) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
let path = format!("../thirdparty/wycheproof/testvectors_v1/{}", $json_file);
|
||||
let data_file = File::open(&path)
|
||||
.expect("failed to open data file (try running `git submodule update --init`)");
|
||||
|
||||
println!("Loading file: {path}");
|
||||
|
||||
let tests: TestFile = serde_json::from_reader(data_file).expect("invalid test JSON");
|
||||
println!("{}:\n{}\n", tests.algorithm, tests.header.join(""));
|
||||
let tests = load_json_file!($json_file);
|
||||
|
||||
for group in tests.groups {
|
||||
let sk = $keypair::from_seed(&group.private_seed.as_slice().try_into().unwrap());
|
||||
@@ -80,10 +87,10 @@ macro_rules! mldsa_sign_from_seed_test {
|
||||
|
||||
match test.result {
|
||||
ExpectedResult::Valid => {
|
||||
assert_eq!(&*result.unwrap().to_bytes(), test.sig.as_slice());
|
||||
assert_eq!(&*result.unwrap().to_bytes(), test.sig.as_slice())
|
||||
}
|
||||
ExpectedResult::Invalid => {
|
||||
assert!(result.is_err());
|
||||
assert!(result.is_err())
|
||||
}
|
||||
other => todo!("{:?}", other),
|
||||
}
|
||||
@@ -94,18 +101,82 @@ macro_rules! mldsa_sign_from_seed_test {
|
||||
};
|
||||
}
|
||||
|
||||
mldsa_sign_from_seed_test!(
|
||||
macro_rules! mldsa_verify_test {
|
||||
($name:ident, $json_file:expr, $keypair:ident) => {
|
||||
mldsa_verify_test!($name, $json_file, $keypair, []);
|
||||
};
|
||||
($name:ident, $json_file:expr, $keypair:ident, $skip:expr) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
let tests = load_json_file!($json_file);
|
||||
|
||||
for group in &tests.groups {
|
||||
if let Ok(encoded_vk) = group.public_key.as_slice().try_into() {
|
||||
let vk = VerifyingKey::<MlDsa44>::decode(&encoded_vk);
|
||||
for test in &group.tests {
|
||||
println!("Test #{}: {} ({:?})", test.id, &test.comment, &test.result);
|
||||
|
||||
if $skip.contains(&test.id) {
|
||||
println!("Test #{} is in skip list, skipping!", test.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(sig) = test
|
||||
.sig
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.ok()
|
||||
.and_then(|sig| Signature::<MlDsa44>::decode(&sig))
|
||||
{
|
||||
if test.ctx.is_empty() {
|
||||
let result = vk.verify(&test.msg, &sig);
|
||||
|
||||
match test.result {
|
||||
ExpectedResult::Valid => assert!(result.is_ok()),
|
||||
ExpectedResult::Invalid => assert!(result.is_err()),
|
||||
other => todo!("{:?}", other),
|
||||
}
|
||||
} else {
|
||||
// TODO(test) contexts
|
||||
}
|
||||
} else {
|
||||
println!("error decoding signature (length: {})", test.sig.len(),);
|
||||
assert_eq!(test.result, ExpectedResult::Invalid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO(tarcieri): debug these test failures
|
||||
const SIGNATURE_WITH_A_REPEATED_HINT_TEST_ID: usize = 18;
|
||||
const PUBLIC_KEY_WITH_T1_COMPONENT_SET_TO_ZERO_TEST_ID: usize = 68;
|
||||
|
||||
mldsa_sign_seed_test!(
|
||||
mldsa_44_sign_seed_test,
|
||||
"mldsa_44_sign_seed_test.json",
|
||||
MlDsa44
|
||||
);
|
||||
mldsa_sign_from_seed_test!(
|
||||
mldsa_sign_seed_test!(
|
||||
mldsa_65_sign_seed_test,
|
||||
"mldsa_65_sign_seed_test.json",
|
||||
MlDsa65
|
||||
);
|
||||
mldsa_sign_from_seed_test!(
|
||||
mldsa_sign_seed_test!(
|
||||
mldsa_87_sign_seed_test,
|
||||
"mldsa_87_sign_seed_test.json",
|
||||
MlDsa87
|
||||
);
|
||||
mldsa_verify_test!(
|
||||
mldsa_44_verify_test,
|
||||
"mldsa_44_verify_test.json",
|
||||
MlDsa44,
|
||||
[
|
||||
SIGNATURE_WITH_A_REPEATED_HINT_TEST_ID,
|
||||
PUBLIC_KEY_WITH_T1_COMPONENT_SET_TO_ZERO_TEST_ID
|
||||
]
|
||||
);
|
||||
mldsa_verify_test!(mldsa_65_verify_test, "mldsa_65_verify_test.json", MlDsa65);
|
||||
mldsa_verify_test!(mldsa_87_verify_test, "mldsa_87_verify_test.json", MlDsa87);
|
||||
|
||||
Reference in New Issue
Block a user