diff --git a/ml-dsa/benches/ml_dsa.rs b/ml-dsa/benches/ml_dsa.rs index 0a362e1b..9cd7f592 100644 --- a/ml-dsa/benches/ml_dsa.rs +++ b/ml-dsa/benches/ml_dsa.rs @@ -1,7 +1,7 @@ use criterion::{Criterion, criterion_group, criterion_main}; use getrandom::SysRng; use hybrid_array::{Array, ArraySize}; -use ml_dsa::{B32, KeyGen, MlDsa65, Signature, SigningKey, VerifyingKey}; +use ml_dsa::{B32, KeyGen, MlDsa65, Signature, SigningKey, VerifyingKey, signature::Keypair}; use rand_core::{CryptoRng, UnwrapErr}; pub fn rand(rng: &mut R) -> Array { diff --git a/ml-dsa/src/lib.rs b/ml-dsa/src/lib.rs index 394a4490..dd5bb11b 100644 --- a/ml-dsa/src/lib.rs +++ b/ml-dsa/src/lib.rs @@ -54,7 +54,7 @@ use crate::hint::Hint; 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 core::convert::{AsRef, TryFrom, TryInto}; +use core::convert::{TryFrom, TryInto}; use core::fmt; use hybrid_array::{ Array, @@ -184,31 +184,23 @@ impl AsMut for MuBuilder { } } -/// An ML-DSA key pair -pub struct KeyPair { +/// An ML-DSA signing key initialized through a seed +pub struct SeededSigningKey { /// The signing key of the key pair signing_key: SigningKey

, - /// The verifying key of the key pair - verifying_key: VerifyingKey

, - /// The seed this signing key was derived from seed: B32, } -impl KeyPair

{ +impl SeededSigningKey

{ /// The signing key of the key pair pub fn signing_key(&self) -> &SigningKey

{ &self.signing_key } - /// The verifying key of the key pair - pub fn verifying_key(&self) -> &VerifyingKey

{ - &self.verifying_key - } - /// Serialize the [`Seed`] value: 32-bytes which can be used to reconstruct the - /// [`KeyPair`]. + /// [`SeededSigningKey`]. /// /// # ⚠️ Warning! /// @@ -219,43 +211,38 @@ impl KeyPair

{ } } -impl AsRef> for KeyPair

{ - fn as_ref(&self) -> &VerifyingKey

{ - &self.verifying_key - } -} - -impl fmt::Debug for KeyPair

{ +impl fmt::Debug for SeededSigningKey

{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("KeyPair") - .field("verifying_key", &self.verifying_key) - .finish_non_exhaustive() + f.debug_struct("SeededSigningKey").finish_non_exhaustive() } } -impl signature::KeypairRef for KeyPair

{ +impl signature::Keypair for SeededSigningKey

{ type VerifyingKey = VerifyingKey

; + fn verifying_key(&self) -> VerifyingKey

{ + self.signing_key.verifying_key() + } } -/// The `Signer` implementation for `KeyPair` uses the optional deterministic variant of ML-DSA, and +/// The `Signer` implementation for `SeededSigningKey` uses the optional deterministic variant of ML-DSA, and /// only supports signing with an empty context string. -impl Signer> for KeyPair

{ +impl Signer> for SeededSigningKey

{ fn try_sign(&self, msg: &[u8]) -> Result, Error> { self.try_multipart_sign(&[msg]) } } -/// The `Signer` implementation for `KeyPair` uses the optional deterministic variant of ML-DSA, and +/// The `Signer` implementation for `SeededSigningKey` uses the optional deterministic variant of ML-DSA, and /// only supports signing with an empty context string. -impl MultipartSigner> for KeyPair

{ +impl MultipartSigner> for SeededSigningKey

{ fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result, Error> { self.signing_key.raw_sign_deterministic(msg, &[]) } } -/// The `DigestSigner` implementation for `KeyPair` uses the optional deterministic variant of ML-DSA +/// The `DigestSigner` implementation for `SeededSigningKey` uses the optional deterministic variant of ML-DSA /// with a pre-computed μ, and only supports signing with an empty context string. -impl DigestSigner> for KeyPair

{ +impl DigestSigner> for SeededSigningKey

{ fn try_sign_digest Result<(), Error>>( &self, f: F, @@ -547,9 +534,9 @@ impl SigningKey

{ /// DEPRECATED: encode the key in a fixed-size byte array. /// - /// Note that this form is deprecated in practice; prefer to use [`KeyPair::to_seed`]. + /// Note that this form is deprecated in practice; prefer to use [`SeededSigningKey:to_seed`]. // Algorithm 24 skEncode - #[deprecated(since = "0.1.0", note = "use `KeyPair::to_seed` instead")] + #[deprecated(since = "0.1.0", note = "use `SeededSigningKey::to_seed` instead")] pub fn to_expanded(&self) -> ExpandedSigningKey

where P: MlDsaParams, @@ -602,7 +589,7 @@ impl DigestSigner> for SigningKey

{ } } -/// The `KeyPair` implementation for `SigningKey` allows to derive a `VerifyingKey` from +/// The [`signature::KeyPair`] implementation for `SigningKey` allows to derive a `VerifyingKey` from /// a bare `SigningKey` (even in the absence of the original seed). impl signature::Keypair for SigningKey

{ type VerifyingKey = VerifyingKey

; @@ -902,12 +889,12 @@ impl

KeyGen for P where P: MlDsaParams, { - type KeyPair = KeyPair

; + type KeyPair = SeededSigningKey

; /// Generate a signing key pair from the specified RNG // Algorithm 1 ML-DSA.KeyGen() #[cfg(feature = "rand_core")] - fn key_gen(rng: &mut R) -> KeyPair

{ + fn key_gen(rng: &mut R) -> SeededSigningKey

{ let mut xi = B32::default(); rng.fill_bytes(&mut xi); Self::from_seed(&xi) @@ -917,7 +904,7 @@ where /// /// This method reflects the ML-DSA.KeyGen_internal algorithm from FIPS 204. // Algorithm 6 ML-DSA.KeyGen_internal - fn from_seed(xi: &Seed) -> KeyPair

+ fn from_seed(xi: &Seed) -> SeededSigningKey

where P: MlDsaParams, { @@ -943,12 +930,12 @@ where // Compress and encode let (t1, t0) = t.power2round(); - let verifying_key = VerifyingKey::new(rho, t1, A_hat.clone(), None); - let signing_key = SigningKey::new(rho, K, verifying_key.tr.clone(), s1, s2, t0, A_hat); + let enc = VerifyingKey::

::encode_internal(&rho, &t1); + let tr: B64 = H::default().absorb(&enc).squeeze_new(); + let signing_key = SigningKey::new(rho, K, tr, s1, s2, t0, A_hat); - KeyPair { + SeededSigningKey { signing_key, - verifying_key, seed: xi.clone(), } } @@ -958,6 +945,7 @@ where mod test { use super::*; use crate::param::*; + use signature::Keypair; #[test] fn output_sizes() { @@ -983,11 +971,11 @@ mod test { P: MlDsaParams + PartialEq, { let seed = Array::default(); - let kp = P::from_seed(&seed); - assert_eq!(kp.to_seed(), seed); + let ssk = P::from_seed(&seed); + assert_eq!(ssk.to_seed(), seed); - let sk = kp.signing_key; - let vk = kp.verifying_key; + let sk = &ssk.signing_key; + let vk = ssk.verifying_key(); let vk_bytes = vk.encode(); let vk2 = VerifyingKey::

::decode(&vk_bytes); @@ -997,7 +985,7 @@ mod test { { let sk_bytes = sk.to_expanded(); let sk2 = SigningKey::

::from_expanded(&sk_bytes); - assert!(sk == sk2); + assert!(sk == &sk2); let M = b"Hello world"; let rnd = Array([0u8; 32]); @@ -1019,9 +1007,9 @@ mod test { where P: MlDsaParams + PartialEq, { - let kp = P::from_seed(&Array::default()); - let sk = kp.signing_key; - let vk = kp.verifying_key; + let ssk = P::from_seed(&Array::default()); + let sk = &ssk.signing_key; + let vk = ssk.verifying_key(); let vk_derived = sk.verifying_key(); assert!(vk == vk_derived); @@ -1038,9 +1026,9 @@ mod test { where P: MlDsaParams, { - let kp = P::from_seed(&Array::default()); - let sk = kp.signing_key; - let vk = kp.verifying_key; + let ssk = P::from_seed(&Array::default()); + let sk = &ssk.signing_key; + let vk = ssk.verifying_key(); let M = b"Hello world"; let rnd = Array([0u8; 32]); @@ -1062,9 +1050,9 @@ mod test { where P: MlDsaParams, { - let kp = P::from_seed(&Array::default()); - let sk = kp.signing_key; - let vk = kp.verifying_key; + let ssk = P::from_seed(&Array::default()); + let sk = &ssk.signing_key; + let vk = ssk.verifying_key(); let M = b"Hello world"; let rnd = Array([0u8; 32]); @@ -1084,9 +1072,9 @@ mod test { where P: MlDsaParams, { - let kp = P::from_seed(&Array::default()); - let sk = kp.signing_key; - let vk = kp.verifying_key; + let ssk = P::from_seed(&Array::default()); + let sk = &ssk.signing_key; + let vk = ssk.verifying_key(); let M = b"Hello world"; let rnd = Array([0u8; 32]); @@ -1106,9 +1094,9 @@ mod test { where P: MlDsaParams, { - let kp = P::from_seed(&Array::default()); - let sk = kp.signing_key; - let vk = kp.verifying_key; + let ssk = P::from_seed(&Array::default()); + let sk = &ssk.signing_key; + let vk = ssk.verifying_key(); let M = b"Hello world"; let rnd = Array([0u8; 32]); @@ -1129,11 +1117,9 @@ mod test { P: MlDsaParams, { let seed = Seed::default(); - let kp1 = P::from_seed(&seed); + let ssk = P::from_seed(&seed); let sk1 = SigningKey::

::from_seed(&seed); - let vk1 = sk1.verifying_key(); - assert_eq!(kp1.signing_key, sk1); - assert_eq!(kp1.verifying_key, vk1); + assert_eq!(ssk.signing_key, sk1); } assert_from_seed_equality::(); assert_from_seed_equality::(); @@ -1194,9 +1180,9 @@ mod test { #[test] fn context_length_validation() { fn test_ctx_length() { - let kp = P::from_seed(&Array::default()); - let sk = kp.signing_key(); - let vk = kp.verifying_key(); + let ssk = P::from_seed(&Array::default()); + let sk = ssk.signing_key(); + let vk = ssk.verifying_key(); let msg = b"Hello world"; let long_ctx = [0u8; 256]; @@ -1217,8 +1203,8 @@ mod test { fn derived_verifying_key_validates_signatures() { fn test_derived_vk() { let seed = Array([42u8; 32]); - let kp = P::from_seed(&seed); - let sk = kp.signing_key(); + let ssk = P::from_seed(&seed); + let sk = ssk.signing_key(); let derived_vk = sk.verifying_key(); let msg = b"Test message for derived key"; @@ -1226,7 +1212,7 @@ mod test { let sig = sk.sign_internal(&[msg], &rnd); assert!(derived_vk.verify_internal(msg, &sig)); - assert_eq!(derived_vk.encode(), kp.verifying_key().encode()); + assert_eq!(derived_vk.encode(), ssk.verifying_key().encode()); } test_derived_vk::(); test_derived_vk::(); @@ -1244,7 +1230,7 @@ mod test { let mut kp_debug = alloc::string::String::new(); write!(&mut kp_debug, "{:?}", kp).unwrap(); - assert!(kp_debug.contains("KeyPair")); + assert!(kp_debug.contains("SeededSigningKey")); let mut sk_debug = alloc::string::String::new(); write!(&mut sk_debug, "{:?}", kp.signing_key()).unwrap(); diff --git a/ml-dsa/src/pkcs8.rs b/ml-dsa/src/pkcs8.rs index e423d911..ecc9a967 100644 --- a/ml-dsa/src/pkcs8.rs +++ b/ml-dsa/src/pkcs8.rs @@ -3,8 +3,8 @@ #![cfg(feature = "pkcs8")] use crate::{ - EncodedVerifyingKey, KeyGen, KeyPair, MlDsa44, MlDsa65, MlDsa87, MlDsaParams, Signature, - SigningKey, VerifyingKey, + EncodedVerifyingKey, KeyGen, MlDsa44, MlDsa65, MlDsa87, MlDsaParams, SeededSigningKey, + Signature, SigningKey, VerifyingKey, }; use ::pkcs8::{ AlgorithmIdentifierRef, PrivateKeyInfoRef, @@ -79,7 +79,7 @@ impl SignatureBitStringEncoding for Signature

{ } } -impl

SignatureAlgorithmIdentifier for KeyPair

+impl

SignatureAlgorithmIdentifier for SeededSigningKey

where P: MlDsaParams, P: AssociatedAlgorithmIdentifier>, @@ -90,7 +90,7 @@ where Signature::

::ALGORITHM_IDENTIFIER; } -impl

TryFrom> for KeyPair

+impl

TryFrom> for SeededSigningKey

where P: MlDsaParams, P: AssociatedAlgorithmIdentifier>, @@ -117,7 +117,7 @@ where } #[cfg(feature = "alloc")] -impl

EncodePrivateKey for KeyPair

+impl

EncodePrivateKey for SeededSigningKey

where P: MlDsaParams, P: AssociatedAlgorithmIdentifier>, @@ -155,7 +155,7 @@ where type Error = ::pkcs8::Error; fn try_from(private_key_info: ::pkcs8::PrivateKeyInfoRef<'_>) -> ::pkcs8::Result { - let keypair = KeyPair::try_from(private_key_info)?; + let keypair = SeededSigningKey::try_from(private_key_info)?; Ok(keypair.signing_key) } diff --git a/ml-dsa/tests/key-gen.rs b/ml-dsa/tests/key-gen.rs index 4b26f748..80a7e376 100644 --- a/ml-dsa/tests/key-gen.rs +++ b/ml-dsa/tests/key-gen.rs @@ -1,6 +1,7 @@ use ml_dsa::*; use hybrid_array::Array; +use signature::Keypair; use std::{fs::read_to_string, path::PathBuf}; #[test] @@ -31,9 +32,9 @@ fn verify(tc: &acvp::TestCase) { let vk_bytes = EncodedVerifyingKey::

::try_from(tc.pk.as_slice()).unwrap(); let sk_bytes = ExpandedSigningKey::

::try_from(tc.sk.as_slice()).unwrap(); - let kp = P::from_seed(&seed); - let sk = kp.signing_key().clone(); - let vk = kp.verifying_key().clone(); + let ssk = P::from_seed(&seed); + let sk = ssk.signing_key().clone(); + let vk = ssk.verifying_key().clone(); assert_eq!(vk.encode(), vk_bytes); assert!(vk == VerifyingKey::

::decode(&vk_bytes)); diff --git a/ml-dsa/tests/pkcs8.rs b/ml-dsa/tests/pkcs8.rs index e5073906..0888cf6c 100644 --- a/ml-dsa/tests/pkcs8.rs +++ b/ml-dsa/tests/pkcs8.rs @@ -1,7 +1,10 @@ #![cfg(all(feature = "pkcs8", feature = "alloc"))] use core::ops::Deref; -use ml_dsa::{KeyPair, MlDsa44, MlDsa65, MlDsa87, MlDsaParams, SigningKey, VerifyingKey}; +use ml_dsa::{ + MlDsa44, MlDsa65, MlDsa87, MlDsaParams, SeededSigningKey, SigningKey, VerifyingKey, + signature::Keypair, +}; use pkcs8::{ DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey, der::{AnyRef, pem::LineEnding}, @@ -16,10 +19,10 @@ fn private_key_serialization() { P: AssociatedAlgorithmIdentifier>, { let sk = SigningKey::

::from_pkcs8_pem(private_bytes).expect("parse private key"); - let kp = KeyPair::

::from_pkcs8_pem(private_bytes).expect("parse private key"); - assert!(sk == *kp.signing_key()); + let ssk = SeededSigningKey::

::from_pkcs8_pem(private_bytes).expect("parse private key"); + assert!(sk == *ssk.signing_key()); assert_eq!( - kp.to_pkcs8_pem(LineEnding::LF) + ssk.to_pkcs8_pem(LineEnding::LF) .expect("serialize private seed") .deref(), private_bytes @@ -33,7 +36,8 @@ fn private_key_serialization() { public_bytes ); - assert_eq!(kp.verifying_key(), &pk); + assert_eq!(sk.verifying_key(), pk); + assert_eq!(ssk.verifying_key(), pk); } test_roundtrip::( diff --git a/ml-dsa/tests/proptests.rs b/ml-dsa/tests/proptests.rs index 2c9f5663..bee84a9c 100644 --- a/ml-dsa/tests/proptests.rs +++ b/ml-dsa/tests/proptests.rs @@ -14,7 +14,7 @@ macro_rules! mldsa_proptests { mod $name { use ml_dsa::{ KeyGen, Signature, - signature::{DigestSigner, DigestVerifier, digest::Update}, + signature::{DigestSigner, DigestVerifier, Keypair, digest::Update}, $alg, }; use proptest::{collection, prelude::*};