Add proptests with round trip check (#897)

- Ensures we can verify signatures we've produced
- Ensures we can decode signatures we've encoded
This commit is contained in:
Tony Arcieri
2025-02-12 15:15:28 -07:00
committed by GitHub
parent b01c3b73dd
commit b4c90c53d5
7 changed files with 89 additions and 6 deletions
Generated
+1
View File
@@ -690,6 +690,7 @@ dependencies = [
"hybrid-array",
"num-traits",
"pkcs8",
"proptest",
"rand",
"rand_core",
"serde",
+1
View File
@@ -37,6 +37,7 @@ criterion = "0.5.1"
hex = { version = "0.4.3", features = ["serde"] }
hex-literal = "0.4.1"
pkcs8 = { version = "=0.11.0-rc.1", features = ["pem"] }
proptest = "1"
rand = "0.8.5"
serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.132"
+22 -3
View File
@@ -17,8 +17,7 @@
//! # Quickstart
//!
//! ```
//! use ml_dsa::{MlDsa65, KeyGen};
//! use signature::{Keypair, Signer, Verifier};
//! use ml_dsa::{MlDsa65, KeyGen, signature::{Keypair, Signer, Verifier}};
//!
//! let mut rng = rand::thread_rng();
//! let kp = MlDsa65::key_gen(&mut rng);
@@ -83,10 +82,11 @@ use crate::ntt::{Ntt, NttInverse};
use crate::param::{ParameterSet, QMinus1, SamplingSize, SpecQ};
use crate::sampling::{expand_a, expand_mask, expand_s, sample_in_ball};
use crate::util::B64;
use core::fmt;
pub use crate::param::{EncodedSignature, EncodedSigningKey, EncodedVerifyingKey, MlDsaParams};
pub use crate::util::B32;
pub use signature::Error;
pub use signature::{self, Error};
/// An ML-DSA signature
#[derive(Clone, PartialEq, Debug)]
@@ -192,6 +192,11 @@ impl<P: MlDsaParams> KeyPair<P> {
pub fn signing_key(&self) -> &SigningKey<P> {
&self.signing_key
}
/// The verifying key of the key pair
pub fn verifying_key(&self) -> &VerifyingKey<P> {
&self.verifying_key
}
}
impl<P: MlDsaParams> AsRef<VerifyingKey<P>> for KeyPair<P> {
@@ -200,6 +205,14 @@ impl<P: MlDsaParams> AsRef<VerifyingKey<P>> for KeyPair<P> {
}
}
impl<P: MlDsaParams> fmt::Debug for KeyPair<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("KeyPair")
.field("verifying_key", &self.verifying_key)
.finish_non_exhaustive()
}
}
impl<P: MlDsaParams> signature::KeypairRef for KeyPair<P> {
type VerifyingKey = VerifyingKey<P>;
}
@@ -276,6 +289,12 @@ pub struct SigningKey<P: MlDsaParams> {
A_hat: NttMatrix<P::K, P::L>,
}
impl<P: MlDsaParams> fmt::Debug for SigningKey<P> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SigningKey").finish_non_exhaustive()
}
}
#[cfg(feature = "zeroize")]
impl<P: MlDsaParams> Drop for SigningKey<P> {
fn drop(&mut self) {
-1
View File
@@ -1,7 +1,6 @@
use ml_dsa::*;
use hybrid_array::Array;
use signature::Keypair;
use std::{fs::read_to_string, path::PathBuf};
#[test]
+1 -2
View File
@@ -7,7 +7,6 @@ use pkcs8::{
spki::AssociatedAlgorithmIdentifier,
DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey,
};
use signature::Keypair;
#[test]
fn private_key_serialization() {
@@ -34,7 +33,7 @@ fn private_key_serialization() {
public_bytes
);
assert_eq!(kp.verifying_key(), pk);
assert_eq!(kp.verifying_key(), &pk);
}
test_roundtrip::<MlDsa44>(
File diff suppressed because one or more lines are too long
+56
View File
@@ -0,0 +1,56 @@
use ml_dsa::{
signature::{Signer, Verifier},
KeyGen, KeyPair, MlDsa44, MlDsa65, MlDsa87, Signature,
};
use proptest::prelude::*;
/// Example message
const MSG: &[u8] = b"Hello world";
// Keypairs
prop_compose! {
fn mldsa44_keypair()(seed_bytes in any::<[u8; 32]>()) -> KeyPair<MlDsa44> {
MlDsa44::key_gen_internal(seed_bytes.as_ref())
}
}
prop_compose! {
fn mldsa65_keypair()(seed_bytes in any::<[u8; 32]>()) -> KeyPair<MlDsa65> {
MlDsa65::key_gen_internal(seed_bytes.as_ref())
}
}
prop_compose! {
fn mldsa87_keypair()(seed_bytes in any::<[u8; 32]>()) -> KeyPair<MlDsa87> {
MlDsa87::key_gen_internal(seed_bytes.as_ref())
}
}
macro_rules! round_trip_test {
($params:path, $keypair:expr) => {
let sig = $keypair.signing_key().sign(MSG);
// Check signature verification
let verify_result = $keypair.verifying_key().verify(MSG, &sig);
prop_assert!(verify_result.is_ok());
// Check signature encoding round trip
let sig_decoded = Signature::<$params>::decode(&sig.encode());
prop_assert_eq!(Some(sig), sig_decoded);
};
}
proptest! {
#[test]
fn mldsa44_round_trip(keypair in mldsa44_keypair()) {
round_trip_test!(MlDsa44, keypair);
}
#[test]
fn mldsa65_round_trip(keypair in mldsa65_keypair()) {
round_trip_test!(MlDsa65, keypair);
}
#[test]
fn mldsa87_round_trip(keypair in mldsa87_keypair()) {
round_trip_test!(MlDsa87, keypair);
}
}