Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ml-dsa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
25 changes: 22 additions & 3 deletions ml-dsa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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> {
Expand All @@ -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>;
}
Expand Down Expand Up @@ -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) {
Expand Down
1 change: 0 additions & 1 deletion ml-dsa/tests/key-gen.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use ml_dsa::*;

use hybrid_array::Array;
use signature::Keypair;
use std::{fs::read_to_string, path::PathBuf};

#[test]
Expand Down
3 changes: 1 addition & 2 deletions ml-dsa/tests/pkcs8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use pkcs8::{
spki::AssociatedAlgorithmIdentifier,
DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey,
};
use signature::Keypair;

#[test]
fn private_key_serialization() {
Expand All @@ -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>(
Expand Down
8 changes: 8 additions & 0 deletions ml-dsa/tests/proptests.proptest-regressions

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions ml-dsa/tests/proptests.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}