diff --git a/ml-dsa/src/lib.rs b/ml-dsa/src/lib.rs index d5567db5..3efe91cf 100644 --- a/ml-dsa/src/lib.rs +++ b/ml-dsa/src/lib.rs @@ -18,15 +18,15 @@ //! //! ``` //! use ml_dsa::{MlDsa65, KeyGen}; -//! use signature::{Signer, Verifier}; +//! use signature::{Keypair, Signer, Verifier}; //! //! let mut rng = rand::thread_rng(); //! let kp = MlDsa65::key_gen(&mut rng); //! //! let msg = b"Hello world"; -//! let sig = kp.signing_key.sign(msg); +//! let sig = kp.signing_key().sign(msg); //! -//! assert!(kp.verifying_key.verify(msg, &sig).is_ok()); +//! assert!(kp.verifying_key().verify(msg, &sig).is_ok()); //! ``` mod algebra; @@ -71,9 +71,9 @@ use { #[cfg(all(feature = "alloc", feature = "pkcs8"))] use pkcs8::{ - der::asn1::{BitString, BitStringRef}, + der::asn1::{BitString, BitStringRef, OctetStringRef}, spki::{SignatureBitStringEncoding, SubjectPublicKeyInfo}, - EncodePublicKey, + EncodePrivateKey, EncodePublicKey, }; use crate::algebra::{AlgebraExt, Elem, NttMatrix, NttVector, Truncate, Vector}; @@ -178,10 +178,20 @@ fn message_representative(tr: &[u8], Mp: &[&[u8]]) -> B64 { /// An ML-DSA key pair pub struct KeyPair { /// The signing key of the key pair - pub signing_key: SigningKey

, + signing_key: SigningKey

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

, + verifying_key: VerifyingKey

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

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

{ + &self.signing_key + } } impl AsRef> for KeyPair

{ @@ -234,6 +244,21 @@ where Signature::

::ALGORITHM_IDENTIFIER; } +#[cfg(all(feature = "alloc", feature = "pkcs8"))] +impl

EncodePrivateKey for KeyPair

+where + P: MlDsaParams, + P: AssociatedAlgorithmIdentifier>, +{ + fn to_pkcs8_der(&self) -> pkcs8::Result { + let pkcs8_key = pkcs8::PrivateKeyInfoRef::new( + P::ALGORITHM_IDENTIFIER, + OctetStringRef::new(&self.seed)?, + ); + Ok(der::SecretDocument::encode_msg(&pkcs8_key)?) + } +} + /// An ML-DSA signing key #[derive(Clone, PartialEq)] pub struct SigningKey { @@ -793,6 +818,7 @@ where KeyPair { signing_key, verifying_key, + seed: xi.clone(), } } } diff --git a/ml-dsa/tests/key-gen.rs b/ml-dsa/tests/key-gen.rs index f1832c5e..5ac5d600 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] @@ -32,8 +33,8 @@ fn verify(tc: &acvp::TestCase) { let sk_bytes = EncodedSigningKey::

::try_from(tc.sk.as_slice()).unwrap(); let kp = P::key_gen_internal(&seed); - let sk = kp.signing_key; - let vk = kp.verifying_key; + let sk = kp.signing_key().clone(); + let vk = kp.verifying_key().clone(); // Verify correctness via serialization assert_eq!(sk.encode(), sk_bytes); diff --git a/ml-dsa/tests/pkcs8.rs b/ml-dsa/tests/pkcs8.rs index 648b9bf4..ffc8474e 100644 --- a/ml-dsa/tests/pkcs8.rs +++ b/ml-dsa/tests/pkcs8.rs @@ -5,7 +5,7 @@ use ml_dsa::{KeyPair, MlDsa44, MlDsa65, MlDsa87, MlDsaParams, SigningKey, Verify use pkcs8::{ der::{pem::LineEnding, AnyRef}, spki::AssociatedAlgorithmIdentifier, - DecodePrivateKey, DecodePublicKey, EncodePublicKey, + DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey, }; use signature::Keypair; @@ -18,7 +18,13 @@ fn private_key_serialization() { { 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); + assert!(sk == *kp.signing_key()); + assert_eq!( + kp.to_pkcs8_pem(LineEnding::LF) + .expect("serialize private seed") + .deref(), + private_bytes + ); let pk = VerifyingKey::

::from_public_key_pem(public_bytes).expect("parse public key"); assert_eq!(