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
81 changes: 23 additions & 58 deletions ml-dsa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ pub use signature::{self, Error};
use crate::algebra::{AlgebraExt, Vector};
use crate::crypto::H;
use crate::hint::Hint;
use crate::ntt::{Ntt, NttInverse};
use crate::param::{ParameterSet, QMinus1, SamplingSize};
use crate::sampling::{expand_a, expand_s};
use crate::param::{ParameterSet, QMinus1};
use core::{
convert::{TryFrom, TryInto},
ops::{Deref, DerefMut},
Expand All @@ -68,7 +66,7 @@ use hybrid_array::{
Array,
typenum::{
Diff, Length, Prod, Quot, Shleft, U1, U2, U4, U5, U6, U7, U8, U17, U19, U32, U48, U55, U64,
U75, U80, U88, Unsigned,
U75, U80, U88,
},
};
use module_lattice::Truncate;
Expand Down Expand Up @@ -273,43 +271,9 @@ where
}

/// Deterministically generate a signing key pair from the specified seed
///
/// This method reflects the ML-DSA.KeyGen_internal algorithm from FIPS 204.
// Algorithm 6 ML-DSA.KeyGen_internal
fn from_seed(xi: &Seed) -> SigningKey<P>
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::<P::K, P::L>(&rho);
let s1 = expand_s::<P::L>(&rhop, P::Eta::ETA, 0);
let s2 = expand_s::<P::K>(&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::<P>::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 {
expanded_key: signing_key,
seed: xi.clone(),
}
fn from_seed(seed: &Seed) -> SigningKey<P> {
SigningKey::from_seed(seed)
}
}

Expand Down Expand Up @@ -356,6 +320,7 @@ impl<T> DerefMut for MaybeBox<T> {
mod test {
use super::*;
use crate::param::*;
use hybrid_array::typenum::Unsigned;
use signature::Keypair;

#[test]
Expand Down Expand Up @@ -385,7 +350,7 @@ mod test {
let ssk = P::from_seed(&seed);
assert_eq!(ssk.to_seed(), seed);

let sk = &ssk.expanded_key;
let esk = ssk.expanded_key();
let vk = ssk.verifying_key();

let vk_bytes = vk.encode();
Expand All @@ -394,13 +359,13 @@ mod test {

#[allow(deprecated)]
{
let sk_bytes = sk.to_expanded();
let sk_bytes = esk.to_expanded();
let sk2 = ExpandedSigningKey::<P>::from_expanded(&sk_bytes);
assert!(sk == &sk2);
assert!(esk == &sk2);

let M = b"Hello world";
let rnd = Array([0u8; 32]);
let sig = sk.sign_internal(&[M], &rnd);
let sig = esk.sign_internal(&[M], &rnd);
let sig_bytes = sig.encode();
let sig2 = Signature::<P>::decode(&sig_bytes).unwrap();
assert!(sig == sig2);
Expand All @@ -419,9 +384,9 @@ mod test {
P: MlDsaParams + PartialEq,
{
let ssk = P::from_seed(&Array::default());
let sk = &ssk.expanded_key;
let esk = ssk.expanded_key();
let vk = ssk.verifying_key();
let vk_derived = sk.verifying_key();
let vk_derived = esk.verifying_key();

assert!(vk == vk_derived);
}
Expand All @@ -438,12 +403,12 @@ mod test {
P: MlDsaParams,
{
let ssk = P::from_seed(&Array::default());
let sk = &ssk.expanded_key;
let esk = ssk.expanded_key();
let vk = ssk.verifying_key();

let M = b"Hello world";
let rnd = Array([0u8; 32]);
let sig = sk.sign_internal(&[M], &rnd);
let sig = esk.sign_internal(&[M], &rnd);

assert!(vk.verify_internal(M, &sig));
}
Expand All @@ -462,13 +427,13 @@ mod test {
P: MlDsaParams,
{
let ssk = P::from_seed(&Array::default());
let sk = &ssk.expanded_key;
let esk = ssk.expanded_key();
let vk = ssk.verifying_key();

let M = b"Hello world";
let rnd = Array([0u8; 32]);
let mu = MuBuilder::internal(&sk.tr, &[M]);
let sig = sk.raw_sign_mu(&mu, &rnd);
let mu = MuBuilder::internal(&esk.tr, &[M]);
let sig = esk.raw_sign_mu(&mu, &rnd);

assert!(vk.raw_verify_mu(&mu, &sig));
}
Expand All @@ -484,13 +449,13 @@ mod test {
P: MlDsaParams,
{
let ssk = P::from_seed(&Array::default());
let sk = &ssk.expanded_key;
let esk = ssk.expanded_key();
let vk = ssk.verifying_key();

let M = b"Hello world";
let rnd = Array([0u8; 32]);
let mu = MuBuilder::internal(&sk.tr, &[M]);
let sig = sk.raw_sign_mu(&mu, &rnd);
let mu = MuBuilder::internal(&esk.tr, &[M]);
let sig = esk.raw_sign_mu(&mu, &rnd);

assert!(vk.verify_internal(M, &sig));
}
Expand All @@ -506,13 +471,13 @@ mod test {
P: MlDsaParams,
{
let ssk = P::from_seed(&Array::default());
let sk = &ssk.expanded_key;
let esk = ssk.expanded_key();
let vk = ssk.verifying_key();

let M = b"Hello world";
let rnd = Array([0u8; 32]);
let mu = MuBuilder::internal(&sk.tr, &[M]);
let sig = sk.sign_internal(&[M], &rnd);
let mu = MuBuilder::internal(&esk.tr, &[M]);
let sig = esk.sign_internal(&[M], &rnd);

assert!(vk.raw_verify_mu(&mu, &sig));
}
Expand All @@ -530,7 +495,7 @@ mod test {
let seed = Seed::default();
let ssk = P::from_seed(&seed);
let sk1 = ExpandedSigningKey::<P>::from_seed(&seed);
assert_eq!(ssk.expanded_key, sk1);
assert_eq!(ssk.expanded_key(), &sk1);
}
assert_from_seed_equality::<MlDsa44>();
assert_from_seed_equality::<MlDsa65>();
Expand Down
8 changes: 3 additions & 5 deletions ml-dsa/src/pkcs8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ where
let seed_der = SeedString {
tag_mode: TagMode::Implicit,
tag_number: SEED_TAG_NUMBER,
value: OctetStringRef::new(&self.seed)?,
value: OctetStringRef::new(&self.to_seed())?,
}
.to_der()?;

Expand Down Expand Up @@ -148,10 +148,8 @@ where
{
type Error = ::pkcs8::Error;

fn try_from(private_key_info: ::pkcs8::PrivateKeyInfoRef<'_>) -> ::pkcs8::Result<Self> {
let keypair = SigningKey::try_from(private_key_info)?;

Ok(keypair.expanded_key)
fn try_from(private_key_info: PrivateKeyInfoRef<'_>) -> Result<Self> {
SigningKey::try_from(private_key_info).map(|sk| sk.expanded_key().clone())
}
}

Expand Down
49 changes: 43 additions & 6 deletions ml-dsa/src/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
crypto::H,
hint::Hint,
ntt::{Ntt, NttInverse},
param::SpecQ,
param::{SamplingSize, SpecQ},
sampling::{expand_a, expand_mask, sample_in_ball},
};
use core::fmt;
Expand All @@ -24,23 +24,60 @@ use {
signature::{RandomizedDigestSigner, RandomizedMultipartSigner, RandomizedSigner},
};

use crate::sampling::expand_s;
#[cfg(feature = "zeroize")]
use zeroize::{Zeroize, ZeroizeOnDrop};

/// An ML-DSA signing key.
/// ML-DSA signing key (i.e. private/secret key).
///
/// This type is initialized through a [`Seed`].
// TODO(tarcieri): reduce field-level visibility.
/// This type is initialized through a [`Seed`], and can be used to generate ML-DSA signatures.
#[derive(Clone)]
pub struct SigningKey<P: MlDsaParams> {
/// The expanded form of the signing key.
pub(crate) expanded_key: ExpandedSigningKey<P>,
expanded_key: ExpandedSigningKey<P>,

/// The seed this signing key was derived from
pub(crate) seed: B32,
seed: Seed,
}

impl<P: MlDsaParams> SigningKey<P> {
/// Deterministically generate a signing key pair from the specified seed
///
/// This method reflects the `ML-DSA.KeyGen_internal` algorithm from FIPS 204 (Algorithm 6).
#[must_use]
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::<P::K, P::L>(&rho);
let s1 = expand_s::<P::L>(&rhop, P::Eta::ETA, 0);
let s2 = expand_s::<P::K>(&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::<P>::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 {
expanded_key: signing_key,
seed: xi.clone(),
}
}

/// Serialize the [`Seed`] value: 32-bytes which can be used to reconstruct the
/// [`SigningKey`].
///
Expand Down