diff --git a/ml-dsa/src/lib.rs b/ml-dsa/src/lib.rs index b15bd09d..93fd737f 100644 --- a/ml-dsa/src/lib.rs +++ b/ml-dsa/src/lib.rs @@ -269,6 +269,18 @@ impl CtEq for SigningKey

{ } } +#[cfg(feature = "zeroize")] +impl Drop for SigningKey

{ + fn drop(&mut self) { + // `signing_key` is zeroized by its own `Drop` impl. Zeroize the seed, + // which is private key material (it regenerates the signing key). + self.seed.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for SigningKey

{} + /// An ML-DSA signing key #[derive(Clone)] pub struct ExpandedSigningKey { @@ -357,9 +369,32 @@ impl ExpandedSigningKey

{ /// This method reflects the ML-DSA.KeyGen_internal algorithm from FIPS 204, but only returns a /// signing key. #[must_use] - pub fn from_seed(seed: &Seed) -> Self { - let kp = P::from_seed(seed); - kp.signing_key + pub fn from_seed(xi: &Seed) -> Self { + // Derive seeds + let mut h = H::default() + .absorb(xi) + .absorb(&[P::K::U8]) + .absorb(&[P::L::U8]); + + let rho: B32 = h.squeeze_new(); + let rhop: B64 = h.squeeze_new(); + let K: B32 = h.squeeze_new(); + + // Sample private key components + let A_hat = expand_a::(&rho); + let s1 = expand_s::(&rhop, P::Eta::ETA, 0); + let s2 = expand_s::(&rhop, P::Eta::ETA, P::L::USIZE); + + // Compute derived values + let As1_hat = &A_hat * &s1.ntt(); + let t = &As1_hat.ntt_inverse() + &s2; + + // Compress and encode + let (t1, t0) = t.power2round(); + + let enc = VerifyingKey::

::encode_internal(&rho, &t1); + let tr: B64 = H::default().absorb(&enc).squeeze_new(); + ExpandedSigningKey::new(rho, K, tr, s1, s2, t0, A_hat) } /// This method reflects the ML-DSA.Sign_internal algorithm from FIPS 204. It does not @@ -947,34 +982,8 @@ where where P: MlDsaParams, { - // Derive seeds - let mut h = H::default() - .absorb(xi) - .absorb(&[P::K::U8]) - .absorb(&[P::L::U8]); - - let rho: B32 = h.squeeze_new(); - let rhop: B64 = h.squeeze_new(); - let K: B32 = h.squeeze_new(); - - // Sample private key components - let A_hat = expand_a::(&rho); - let s1 = expand_s::(&rhop, P::Eta::ETA, 0); - let s2 = expand_s::(&rhop, P::Eta::ETA, P::L::USIZE); - - // Compute derived values - let As1_hat = &A_hat * &s1.ntt(); - let t = &As1_hat.ntt_inverse() + &s2; - - // Compress and encode - let (t1, t0) = t.power2round(); - - let enc = VerifyingKey::

::encode_internal(&rho, &t1); - let tr: B64 = H::default().absorb(&enc).squeeze_new(); - let signing_key = ExpandedSigningKey::new(rho, K, tr, s1, s2, t0, A_hat); - SigningKey { - signing_key, + signing_key: ExpandedSigningKey::

::from_seed(xi), seed: xi.clone(), } } @@ -1258,6 +1267,18 @@ mod test { test_derived_vk::(); } + #[test] + #[cfg(feature = "zeroize")] + fn zeroize_on_drop_impls() { + fn assert_zeroize_on_drop() {} + assert_zeroize_on_drop::>(); + assert_zeroize_on_drop::>(); + assert_zeroize_on_drop::>(); + assert_zeroize_on_drop::>(); + assert_zeroize_on_drop::>(); + assert_zeroize_on_drop::>(); + } + #[test] #[cfg(feature = "alloc")] fn debug_implementations() { diff --git a/ml-dsa/src/pkcs8.rs b/ml-dsa/src/pkcs8.rs index 604f22e2..fcd66011 100644 --- a/ml-dsa/src/pkcs8.rs +++ b/ml-dsa/src/pkcs8.rs @@ -151,9 +151,21 @@ where type Error = ::pkcs8::Error; fn try_from(private_key_info: ::pkcs8::PrivateKeyInfoRef<'_>) -> ::pkcs8::Result { - let keypair = SigningKey::try_from(private_key_info)?; + private_key_info + .algorithm + .assert_algorithm_oid(P::ALGORITHM_IDENTIFIER.oid)?; + + let mut reader = der::SliceReader::new(private_key_info.private_key.as_bytes())?; + let seed_string = SeedString::decode_implicit(&mut reader, SEED_TAG_NUMBER)? + .ok_or(pkcs8::Error::KeyMalformed)?; + let seed = seed_string + .value + .as_bytes() + .try_into() + .map_err(|_| pkcs8::Error::KeyMalformed)?; + reader.finish()?; - Ok(keypair.signing_key) + Ok(ExpandedSigningKey::

::from_seed(&seed)) } }