|
1 | | -use hybrid_array::AsArrayRef; |
2 | | -use ml_dsa::{ |
3 | | - KeyGen, KeyPair, MlDsa44, MlDsa65, MlDsa87, Signature, |
4 | | - signature::{Signer, Verifier}, |
5 | | -}; |
6 | | -use proptest::prelude::*; |
7 | | - |
8 | | -/// Example message |
9 | | -const MSG: &[u8] = b"Hello world"; |
10 | | - |
11 | | -// Keypairs |
12 | | -prop_compose! { |
13 | | - fn mldsa44_keypair()(seed_bytes in any::<[u8; 32]>()) -> KeyPair<MlDsa44> { |
14 | | - MlDsa44::from_seed(seed_bytes.as_array_ref()) |
15 | | - } |
16 | | -} |
17 | | -prop_compose! { |
18 | | - fn mldsa65_keypair()(seed_bytes in any::<[u8; 32]>()) -> KeyPair<MlDsa65> { |
19 | | - MlDsa65::from_seed(seed_bytes.as_array_ref()) |
20 | | - } |
21 | | -} |
22 | | -prop_compose! { |
23 | | - fn mldsa87_keypair()(seed_bytes in any::<[u8; 32]>()) -> KeyPair<MlDsa87> { |
24 | | - MlDsa87::from_seed(seed_bytes.as_array_ref()) |
25 | | - } |
| 1 | +//! Property-based tests for the `ml-dsa` crate. |
| 2 | +
|
| 3 | +macro_rules! signature_round_trip_encode { |
| 4 | + ($alg:ident, $sig:expr) => {{ |
| 5 | + let sig_enc = $sig.encode(); |
| 6 | + let sig_dec_result = Signature::<$alg>::decode(&sig_enc); |
| 7 | + prop_assert_eq!(&sig_dec_result, &Some($sig)); |
| 8 | + sig_dec_result.unwrap() |
| 9 | + }}; |
26 | 10 | } |
27 | 11 |
|
28 | | -macro_rules! round_trip_test { |
29 | | - ($params:path, $keypair:expr) => { |
30 | | - let sig = $keypair.signing_key().sign(MSG); |
| 12 | +macro_rules! mldsa_proptests { |
| 13 | + ($name:ident, $alg:ident) => { |
| 14 | + mod $name { |
| 15 | + use ml_dsa::{ |
| 16 | + KeyGen, Signature, |
| 17 | + signature::{DigestSigner, DigestVerifier, digest::Update}, |
| 18 | + $alg, |
| 19 | + }; |
| 20 | + use proptest::{collection, prelude::*}; |
| 21 | + |
| 22 | + #[cfg(feature = "rand_core")] |
| 23 | + use ml_dsa::signature::RandomizedDigestSigner; |
| 24 | + |
| 25 | + proptest! { |
| 26 | + #[test] |
| 27 | + fn round_trip_test( |
| 28 | + seed in any::<[u8; 32]>(), |
| 29 | + msg in collection::vec(0u8..u8::MAX, 0..65536), |
| 30 | + rnd in any::<[u8; 32]>() |
| 31 | + ) { |
| 32 | + let kp = $alg::from_seed(&seed.into()); |
| 33 | + let sk = kp.signing_key(); |
| 34 | + let vk = kp.verifying_key(); |
31 | 35 |
|
32 | | - // Check signature verification |
33 | | - let verify_result = $keypair.verifying_key().verify(MSG, &sig); |
34 | | - prop_assert!(verify_result.is_ok()); |
| 36 | + let sig = sk.sign_internal(&[&msg], &rnd.into()); |
| 37 | + let sig_dec = signature_round_trip_encode!($alg, sig); |
| 38 | + assert!(vk.verify_internal(&msg, &sig_dec)); |
| 39 | + } |
35 | 40 |
|
36 | | - // Check signature encoding round trip |
37 | | - let sig_decoded = Signature::<$params>::decode(&sig.encode()); |
38 | | - prop_assert_eq!(Some(sig), sig_decoded); |
| 41 | + #[test] |
| 42 | + fn round_trip_digest_test( |
| 43 | + seed in any::<[u8; 32]>(), |
| 44 | + msg in collection::vec(0u8..u8::MAX, 0..65536), |
| 45 | + ) { |
| 46 | + let kp = $alg::from_seed(&seed.into()); |
| 47 | + let sk = kp.signing_key(); |
| 48 | + let vk = kp.verifying_key(); |
| 49 | + |
| 50 | + let sig = sk.sign_digest(|digest| digest.update(&msg)); |
| 51 | + let sig_dec = signature_round_trip_encode!($alg, sig); |
| 52 | + let verify_result = vk.verify_digest(|digest| Ok(digest.update(&msg)), &sig_dec); |
| 53 | + assert!(verify_result.is_ok()); |
| 54 | + } |
| 55 | + |
| 56 | + #[cfg(feature = "rand_core")] |
| 57 | + #[test] |
| 58 | + fn round_trip_randomized_digest_test( |
| 59 | + seed in any::<[u8; 32]>(), |
| 60 | + msg in collection::vec(0u8..u8::MAX, 0..65536), |
| 61 | + ) { |
| 62 | + let kp = $alg::from_seed(&seed.into()); |
| 63 | + let sk = kp.signing_key(); |
| 64 | + let vk = kp.verifying_key(); |
| 65 | + |
| 66 | + let mut rng = rand_core::UnwrapErr(getrandom::SysRng); |
| 67 | + let sig = sk.sign_digest_with_rng(&mut rng, |digest| digest.update(&msg)); |
| 68 | + let sig_dec = signature_round_trip_encode!($alg, sig); |
| 69 | + let verify_result = vk.verify_digest(|digest| Ok(digest.update(&msg)), &sig_dec); |
| 70 | + assert!(verify_result.is_ok()); |
| 71 | + } |
| 72 | + } |
| 73 | + } |
39 | 74 | }; |
40 | 75 | } |
41 | 76 |
|
42 | | -proptest! { |
43 | | - #[test] |
44 | | - fn mldsa44_round_trip(keypair in mldsa44_keypair()) { |
45 | | - round_trip_test!(MlDsa44, keypair); |
46 | | - } |
47 | | - |
48 | | - #[test] |
49 | | - fn mldsa65_round_trip(keypair in mldsa65_keypair()) { |
50 | | - round_trip_test!(MlDsa65, keypair); |
51 | | - } |
52 | | - |
53 | | - #[test] |
54 | | - fn mldsa87_round_trip(keypair in mldsa87_keypair()) { |
55 | | - round_trip_test!(MlDsa87, keypair); |
56 | | - } |
57 | | -} |
| 77 | +mldsa_proptests!(mldsa44, MlDsa44); |
| 78 | +mldsa_proptests!(mldsa65, MlDsa65); |
| 79 | +mldsa_proptests!(mldsa87, MlDsa87); |
0 commit comments