1use crate::{
6 B32, B64, ExpandedSigningKeyBytes, MlDsaParams, MuBuilder, Seed, Signature, VerifyingKey,
7 algebra::{AlgebraExt, NttMatrix, NttVector, Vector},
8 crypto::H,
9 hint::Hint,
10 ntt::{Ntt, NttInverse},
11 param::{SamplingSize, SpecQ},
12 sampling::{expand_a, expand_mask, expand_s, sample_in_ball},
13};
14use common::{KeyExport, KeyInit, KeySizeUser, typenum::U32};
15use core::fmt;
16use ctutils::{Choice, CtEq};
17use hybrid_array::typenum::Unsigned;
18use module_lattice::MaybeBox;
19use shake::Shake256;
20use signature::{DigestSigner, Error, MultipartSigner, Signer};
21
22#[cfg(feature = "rand_core")]
23use {
24 common::Generate,
25 signature::{
26 RandomizedDigestSigner, RandomizedMultipartSigner, RandomizedSigner,
27 rand_core::TryCryptoRng,
28 },
29};
30
31#[cfg(feature = "zeroize")]
32use zeroize::{Zeroize, ZeroizeOnDrop};
33
34mod steps;
35
36#[derive(Clone)]
40pub struct SigningKey<P: MlDsaParams> {
41 expanded_key: MaybeBox<ExpandedSigningKey<P>>,
43
44 seed: MaybeBox<Seed>,
46
47 #[cfg(feature = "alloc")]
49 verifying_key: VerifyingKey<P>,
50}
51
52impl<P: MlDsaParams> SigningKey<P> {
53 #[must_use]
57 pub fn from_seed(xi: &Seed) -> Self {
58 let mut h = H::default()
60 .absorb(xi)
61 .absorb(&[P::K::U8])
62 .absorb(&[P::L::U8]);
63
64 let rho: B32 = h.squeeze_new();
65 let rhop: B64 = h.squeeze_new();
66 let K: B32 = h.squeeze_new();
67
68 let A_hat = expand_a::<P::K, P::L>(&rho);
70 let s1 = expand_s::<P::L>(&rhop, P::Eta::ETA, 0);
71 let s2 = expand_s::<P::K>(&rhop, P::Eta::ETA, P::L::USIZE);
72
73 let As1_hat = &A_hat * &s1.ntt();
75 let t = &As1_hat.ntt_inverse() + &s2;
76
77 let (t1, t0) = t.power2round();
79
80 let enc = VerifyingKey::<P>::encode_internal(&rho, &t1);
81 let tr: B64 = H::default().absorb(&enc).squeeze_new();
82
83 #[cfg(feature = "alloc")]
84 let verifying_key = VerifyingKey::new(rho.clone(), t1, A_hat.clone(), Some(enc));
85
86 let expanded_key = ExpandedSigningKey::new(rho, K, tr, s1, s2, t0, A_hat);
87
88 SigningKey {
89 expanded_key: MaybeBox::new(expanded_key),
90 seed: MaybeBox::new(xi.clone()),
91 #[cfg(feature = "alloc")]
92 verifying_key,
93 }
94 }
95
96 #[inline]
104 #[must_use]
105 pub fn as_seed(&self) -> &Seed {
106 &self.seed
107 }
108
109 #[inline]
117 #[must_use]
118 pub fn to_seed(&self) -> Seed {
119 *self.seed
120 }
121
122 #[doc(hidden)]
124 #[must_use]
125 pub fn expanded_key(&self) -> &ExpandedSigningKey<P> {
126 &self.expanded_key
127 }
128}
129
130impl<P: MlDsaParams> KeySizeUser for SigningKey<P> {
131 type KeySize = U32;
132}
133
134impl<P: MlDsaParams> KeyInit for SigningKey<P> {
135 fn new(seed: &Seed) -> Self {
136 Self::from_seed(seed)
137 }
138}
139
140impl<P: MlDsaParams> KeyExport for SigningKey<P> {
141 fn to_bytes(&self) -> Seed {
142 self.to_seed()
143 }
144}
145
146#[cfg(feature = "rand_core")]
148impl<P: MlDsaParams> Generate for SigningKey<P> {
149 fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
150 let seed = Seed::try_generate_from_rng(rng)?;
151 Ok(Self::from_seed(&seed))
152 }
153}
154
155impl<P: MlDsaParams> fmt::Debug for SigningKey<P> {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 f.debug_struct("SigningKey").finish_non_exhaustive()
158 }
159}
160
161#[cfg(not(feature = "alloc"))]
165impl<P: MlDsaParams> signature::Keypair for SigningKey<P> {
166 type VerifyingKey = VerifyingKey<P>;
167 fn verifying_key(&self) -> VerifyingKey<P> {
168 self.expanded_key.verifying_key()
169 }
170}
171
172#[cfg(feature = "alloc")]
173impl<P: MlDsaParams> AsRef<VerifyingKey<P>> for SigningKey<P> {
174 fn as_ref(&self) -> &VerifyingKey<P> {
175 &self.verifying_key
176 }
177}
178
179#[cfg(feature = "alloc")]
180impl<P: MlDsaParams> signature::KeypairRef for SigningKey<P> {
181 type VerifyingKey = VerifyingKey<P>;
182}
183
184impl<P: MlDsaParams> Signer<Signature<P>> for SigningKey<P> {
187 fn try_sign(&self, msg: &[u8]) -> Result<Signature<P>, Error> {
188 self.try_multipart_sign(&[msg])
189 }
190}
191
192impl<P: MlDsaParams> MultipartSigner<Signature<P>> for SigningKey<P> {
195 fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<Signature<P>, Error> {
196 self.expanded_key.raw_sign_deterministic(msg, &[])
197 }
198}
199
200impl<P: MlDsaParams> DigestSigner<Shake256, Signature<P>> for SigningKey<P> {
203 fn try_sign_digest<F: Fn(&mut Shake256) -> Result<(), Error>>(
204 &self,
205 f: F,
206 ) -> Result<Signature<P>, Error> {
207 self.expanded_key.try_sign_digest(&f)
208 }
209}
210
211impl<P: MlDsaParams> PartialEq for SigningKey<P> {
212 fn eq(&self, other: &Self) -> bool {
213 self.ct_eq(other).into()
214 }
215}
216
217impl<P: MlDsaParams> CtEq for SigningKey<P> {
218 fn ct_eq(&self, other: &Self) -> Choice {
219 self.expanded_key
220 .ct_eq(&other.expanded_key)
221 .and(self.seed.ct_eq(&other.seed))
222 }
223}
224
225impl<P: MlDsaParams> Drop for SigningKey<P> {
226 fn drop(&mut self) {
227 #[cfg(feature = "zeroize")]
229 self.seed.zeroize();
230 }
231}
232
233#[cfg(feature = "zeroize")]
234impl<P: MlDsaParams> ZeroizeOnDrop for SigningKey<P> {}
235
236#[derive(Clone)]
238pub struct ExpandedSigningKey<P: MlDsaParams> {
239 rho: B32,
240 K: B32,
241 pub(crate) tr: B64,
242 s1: Vector<P::L>,
243 s2: Vector<P::K>,
244 t0: Vector<P::K>,
245
246 s1_hat: NttVector<P::L>,
248 s2_hat: NttVector<P::K>,
249 t0_hat: NttVector<P::K>,
250 A_hat: NttMatrix<P::K, P::L>,
251}
252
253impl<P: MlDsaParams> ExpandedSigningKey<P> {
254 pub(crate) fn new(
255 rho: B32,
256 K: B32,
257 tr: B64,
258 s1: Vector<P::L>,
259 s2: Vector<P::K>,
260 t0: Vector<P::K>,
261 A_hat: NttMatrix<P::K, P::L>,
262 ) -> Self {
263 let s1_hat = s1.ntt();
264 let s2_hat = s2.ntt();
265 let t0_hat = t0.ntt();
266
267 Self {
268 rho,
269 K,
270 tr,
271 s1,
272 s2,
273 t0,
274
275 s1_hat,
276 s2_hat,
277 t0_hat,
278 A_hat,
279 }
280 }
281
282 #[inline]
283 fn new_expand_a(
284 rho: B32,
285 K: B32,
286 tr: B64,
287 s1: Vector<P::L>,
288 s2: Vector<P::K>,
289 t0: Vector<P::K>,
290 ) -> Self {
291 let A_hat = expand_a(&rho);
292 Self::new(rho, K, tr, s1, s2, t0, A_hat)
293 }
294
295 #[must_use]
300 #[inline]
301 pub fn from_seed(seed: &Seed) -> Self {
302 let kp = SigningKey::from_seed(seed);
303 (*kp.expanded_key).clone()
304 }
305
306 pub fn sign_internal(&self, Mp: &[&[u8]], rnd: &B32) -> Signature<P>
316 where
317 P: MlDsaParams,
318 {
319 let mu = MuBuilder::internal(&self.tr, Mp);
320 self.raw_sign_mu(&mu, rnd)
321 }
322
323 pub(crate) fn raw_sign_mu(&self, mu: &B64, rnd: &B32) -> Signature<P>
324 where
325 P: MlDsaParams,
326 {
327 self.try_raw_sign_mu(mu, rnd)
328 .expect("Rejection sampling failed to find a valid signature")
329 }
330
331 fn try_raw_sign_mu(&self, mu: &B64, rnd: &B32) -> Result<Signature<P>, Error> {
332 let rhopp: B64 = H::default()
334 .absorb(&self.K)
335 .absorb(rnd)
336 .absorb(mu)
337 .squeeze_new();
338
339 for kappa in signing_nonces::<P>() {
341 let y = expand_mask::<P::L, P::Gamma1>(&rhopp, kappa);
342 let w = (&self.A_hat * &y.ntt()).ntt_inverse();
343 let w1 = w.high_bits::<P::TwoGamma2>();
344
345 let w1_tilde = P::encode_w1(&w1);
346 let c_tilde = H::default()
347 .absorb(mu)
348 .absorb(&w1_tilde)
349 .squeeze_new::<P::Lambda>();
350 let c = sample_in_ball(&c_tilde, P::TAU);
351 let c_hat = c.ntt();
352
353 let steps::Response { z, cs2, r0 } =
354 steps::response::<P>(&y, &w, &c_hat, &self.s1_hat, &self.s2_hat);
355
356 if z.infinity_norm() >= P::GAMMA1_MINUS_BETA
357 || r0.infinity_norm() >= P::GAMMA2_MINUS_BETA
358 {
359 continue;
360 }
361
362 let (ct0, w_cs2_ct0) = steps::hint_polynomials::<P>(&w, &cs2, &c_hat, &self.t0_hat);
363 let minus_ct0 = -&ct0;
364 let h = Hint::<P>::new(&minus_ct0, &w_cs2_ct0);
365
366 if ct0.infinity_norm() >= P::Gamma2::U32 || h.hamming_weight() > P::Omega::USIZE {
367 continue;
368 }
369
370 let z = MaybeBox::new(z.mod_plus_minus::<SpecQ>());
371 return Ok(Signature { c_tilde, z, h });
372 }
373
374 Err(Error::new())
375 }
376
377 #[cfg(feature = "rand_core")]
386 pub fn sign_randomized<R: TryCryptoRng + ?Sized>(
387 &self,
388 M: &[u8],
389 ctx: &[u8],
390 rng: &mut R,
391 ) -> Result<Signature<P>, Error> {
392 self.raw_sign_randomized(&[M], ctx, rng)
393 }
394
395 #[cfg(feature = "rand_core")]
396 fn raw_sign_randomized<R: TryCryptoRng + ?Sized>(
397 &self,
398 Mp: &[&[u8]],
399 ctx: &[u8],
400 rng: &mut R,
401 ) -> Result<Signature<P>, Error> {
402 if ctx.len() > 255 {
403 return Err(Error::new());
404 }
405
406 let mut rnd = B32::default();
407 rng.try_fill_bytes(&mut rnd).map_err(|_| Error::new())?;
408
409 let mu = MuBuilder::new(&self.tr, ctx).message(Mp);
410 self.try_raw_sign_mu(&mu, &rnd)
411 }
412
413 #[cfg(feature = "rand_core")]
421 pub fn sign_mu_randomized<R: TryCryptoRng + ?Sized>(
422 &self,
423 mu: &B64,
424 rng: &mut R,
425 ) -> Result<Signature<P>, Error> {
426 let mut rnd = B32::default();
427 rng.try_fill_bytes(&mut rnd).map_err(|_| Error::new())?;
428
429 self.try_raw_sign_mu(mu, &rnd)
430 }
431
432 pub fn sign_deterministic(&self, M: &[u8], ctx: &[u8]) -> Result<Signature<P>, Error> {
441 self.raw_sign_deterministic(&[M], ctx)
442 }
443
444 pub fn sign_mu_deterministic(&self, mu: &B64) -> Signature<P> {
452 let rnd = B32::default();
453 self.raw_sign_mu(mu, &rnd)
454 }
455
456 fn raw_sign_deterministic(&self, Mp: &[&[u8]], ctx: &[u8]) -> Result<Signature<P>, Error> {
457 if ctx.len() > 255 {
458 return Err(Error::new());
459 }
460
461 let mu = MuBuilder::new(&self.tr, ctx).message(Mp);
462 self.try_raw_sign_mu(&mu, &B32::default())
463 }
464
465 pub fn verifying_key(&self) -> VerifyingKey<P> {
477 let kp: &dyn signature::Keypair<VerifyingKey = VerifyingKey<P>> = self;
478 kp.verifying_key()
479 }
480
481 #[deprecated(since = "0.1.0", note = "use `ExpandedSigningKey::from_seed` instead")]
495 pub fn from_expanded(enc: &ExpandedSigningKeyBytes<P>) -> Self
496 where
497 P: MlDsaParams,
498 {
499 let (rho, K, tr, s1_enc, s2_enc, t0_enc) = P::split_sk(enc);
500 Self::new_expand_a(
501 rho.clone(),
502 K.clone(),
503 tr.clone(),
504 P::decode_s1(s1_enc),
505 P::decode_s2(s2_enc),
506 P::decode_t0(t0_enc),
507 )
508 }
509
510 #[deprecated(since = "0.1.0", note = "use `SigningKey::to_seed` instead")]
515 pub fn to_expanded(&self) -> ExpandedSigningKeyBytes<P>
516 where
517 P: MlDsaParams,
518 {
519 let s1_enc = P::encode_s1(&self.s1);
520 let s2_enc = P::encode_s2(&self.s2);
521 let t0_enc = P::encode_t0(&self.t0);
522 P::concat_sk(
523 self.rho.clone(),
524 self.K.clone(),
525 self.tr.clone(),
526 s1_enc,
527 s2_enc,
528 t0_enc,
529 )
530 }
531}
532
533impl<P: MlDsaParams> Signer<Signature<P>> for ExpandedSigningKey<P> {
537 fn try_sign(&self, msg: &[u8]) -> Result<Signature<P>, Error> {
538 self.try_multipart_sign(&[msg])
539 }
540}
541
542impl<P: MlDsaParams> MultipartSigner<Signature<P>> for ExpandedSigningKey<P> {
546 fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<Signature<P>, Error> {
547 self.raw_sign_deterministic(msg, &[])
548 }
549}
550
551impl<P: MlDsaParams> DigestSigner<Shake256, Signature<P>> for ExpandedSigningKey<P> {
555 fn try_sign_digest<F: Fn(&mut Shake256) -> Result<(), Error>>(
556 &self,
557 f: F,
558 ) -> Result<Signature<P>, Error> {
559 let mut mu = MuBuilder::new(&self.tr, &[]);
560 f(mu.as_mut())?;
561 let mu = mu.finish();
562
563 self.try_raw_sign_mu(&mu, &B32::default())
564 }
565}
566
567impl<P: MlDsaParams> signature::Keypair for ExpandedSigningKey<P> {
570 type VerifyingKey = VerifyingKey<P>;
571
572 fn verifying_key(&self) -> Self::VerifyingKey {
577 let As1 = &self.A_hat * &self.s1_hat;
578 let t = &As1.ntt_inverse() + &self.s2;
579
580 let (t1, _) = t.power2round();
582
583 VerifyingKey::new(self.rho.clone(), t1, self.A_hat.clone(), None)
584 }
585}
586
587#[cfg(feature = "rand_core")]
591impl<P: MlDsaParams> RandomizedSigner<Signature<P>> for ExpandedSigningKey<P> {
592 fn try_sign_with_rng<R: TryCryptoRng + ?Sized>(
593 &self,
594 rng: &mut R,
595 msg: &[u8],
596 ) -> Result<Signature<P>, Error> {
597 self.try_multipart_sign_with_rng(rng, &[msg])
598 }
599}
600
601#[cfg(feature = "rand_core")]
605impl<P: MlDsaParams> RandomizedMultipartSigner<Signature<P>> for ExpandedSigningKey<P> {
606 fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
607 &self,
608 rng: &mut R,
609 msg: &[&[u8]],
610 ) -> Result<Signature<P>, Error> {
611 self.raw_sign_randomized(msg, &[], rng)
612 }
613}
614
615#[cfg(feature = "rand_core")]
619impl<P: MlDsaParams> RandomizedDigestSigner<Shake256, Signature<P>> for ExpandedSigningKey<P> {
620 fn try_sign_digest_with_rng<
621 R: TryCryptoRng + ?Sized,
622 F: Fn(&mut Shake256) -> Result<(), Error>,
623 >(
624 &self,
625 rng: &mut R,
626 f: F,
627 ) -> Result<Signature<P>, Error> {
628 let mut mu = MuBuilder::new(&self.tr, &[]);
629 f(mu.as_mut())?;
630 let mu = mu.finish();
631
632 self.sign_mu_randomized(&mu, rng)
633 }
634}
635
636impl<P: MlDsaParams> PartialEq for ExpandedSigningKey<P> {
637 fn eq(&self, other: &Self) -> bool {
638 self.ct_eq(other).into()
639 }
640}
641
642impl<P: MlDsaParams> CtEq for ExpandedSigningKey<P> {
643 fn ct_eq(&self, other: &Self) -> Choice {
644 self.rho
645 .ct_eq(&other.rho)
646 .and(self.K.ct_eq(&other.K))
647 .and(self.tr.ct_eq(&other.tr))
648 .and(self.s1.ct_eq(&other.s1))
649 .and(self.s2.ct_eq(&other.s2))
650 .and(self.t0.ct_eq(&other.t0))
651 }
652}
653
654impl<P: MlDsaParams> fmt::Debug for ExpandedSigningKey<P> {
655 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
656 f.debug_struct("ExpandedSigningKey").finish_non_exhaustive()
657 }
658}
659
660impl<P: MlDsaParams> Drop for ExpandedSigningKey<P> {
661 fn drop(&mut self) {
662 #[cfg(feature = "zeroize")]
663 {
664 self.rho.zeroize();
665 self.K.zeroize();
666 self.tr.zeroize();
667 self.s1.zeroize();
668 self.s2.zeroize();
669 self.t0.zeroize();
670 self.s1_hat.zeroize();
671 self.s2_hat.zeroize();
672 self.t0_hat.zeroize();
673 }
674 }
675}
676
677#[cfg(feature = "zeroize")]
678impl<P: MlDsaParams> ZeroizeOnDrop for ExpandedSigningKey<P> {}
679
680fn signing_nonces<P: MlDsaParams>() -> impl Iterator<Item = u16> {
682 (0..=u16::MAX - (P::L::U16 - 1)).step_by(P::L::USIZE)
683}
684
685#[cfg(test)]
686mod tests {
687 use super::*;
688 use crate::{MlDsa44, MlDsa65, MlDsa87};
689
690 fn check_signing_nonces<P: MlDsaParams>(expected_last: u16, expected_count: usize) {
691 let mut count = 0;
692 let mut last = None;
693 for kappa in signing_nonces::<P>() {
694 assert_eq!(usize::from(kappa), count * P::L::USIZE);
695 assert!(kappa.checked_add(P::L::U16 - 1).is_some());
696 last = Some(kappa);
697 count += 1;
698 }
699 assert_eq!(last, Some(expected_last));
700 assert_eq!(count, expected_count);
701
702 assert!(u32::from(expected_last) + 2 * P::L::U32 - 1 > u32::from(u16::MAX));
704
705 let _ = expand_mask::<P::L, P::Gamma1>(&B64::default(), expected_last);
707 }
708
709 #[test]
710 fn signing_nonce_bounds() {
711 check_signing_nonces::<MlDsa44>(65532, 16384);
712 check_signing_nonces::<MlDsa65>(65530, 13107);
713 check_signing_nonces::<MlDsa87>(65527, 9362);
714 }
715}