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
34#[derive(Clone)]
38pub struct SigningKey<P: MlDsaParams> {
39 expanded_key: MaybeBox<ExpandedSigningKey<P>>,
41
42 seed: MaybeBox<Seed>,
44
45 #[cfg(feature = "alloc")]
47 verifying_key: VerifyingKey<P>,
48}
49
50impl<P: MlDsaParams> SigningKey<P> {
51 #[must_use]
55 pub fn from_seed(xi: &Seed) -> Self {
56 let mut h = H::default()
58 .absorb(xi)
59 .absorb(&[P::K::U8])
60 .absorb(&[P::L::U8]);
61
62 let rho: B32 = h.squeeze_new();
63 let rhop: B64 = h.squeeze_new();
64 let K: B32 = h.squeeze_new();
65
66 let A_hat = expand_a::<P::K, P::L>(&rho);
68 let s1 = expand_s::<P::L>(&rhop, P::Eta::ETA, 0);
69 let s2 = expand_s::<P::K>(&rhop, P::Eta::ETA, P::L::USIZE);
70
71 let As1_hat = &A_hat * &s1.ntt();
73 let t = &As1_hat.ntt_inverse() + &s2;
74
75 let (t1, t0) = t.power2round();
77
78 let enc = VerifyingKey::<P>::encode_internal(&rho, &t1);
79 let tr: B64 = H::default().absorb(&enc).squeeze_new();
80 let expanded_key = ExpandedSigningKey::new(rho, K, tr, s1, s2, t0, A_hat);
81
82 #[cfg(feature = "alloc")]
83 let verifying_key = expanded_key.verifying_key();
84
85 SigningKey {
86 expanded_key: MaybeBox::new(expanded_key),
87 seed: MaybeBox::new(xi.clone()),
88 #[cfg(feature = "alloc")]
89 verifying_key,
90 }
91 }
92
93 #[inline]
101 #[must_use]
102 pub fn as_seed(&self) -> &Seed {
103 &self.seed
104 }
105
106 #[inline]
114 #[must_use]
115 pub fn to_seed(&self) -> Seed {
116 *self.seed
117 }
118
119 #[doc(hidden)]
121 #[must_use]
122 pub fn expanded_key(&self) -> &ExpandedSigningKey<P> {
123 &self.expanded_key
124 }
125}
126
127impl<P: MlDsaParams> KeySizeUser for SigningKey<P> {
128 type KeySize = U32;
129}
130
131impl<P: MlDsaParams> KeyInit for SigningKey<P> {
132 fn new(seed: &Seed) -> Self {
133 Self::from_seed(seed)
134 }
135}
136
137impl<P: MlDsaParams> KeyExport for SigningKey<P> {
138 fn to_bytes(&self) -> Seed {
139 self.to_seed()
140 }
141}
142
143#[cfg(feature = "rand_core")]
145impl<P: MlDsaParams> Generate for SigningKey<P> {
146 fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
147 let seed = Seed::try_generate_from_rng(rng)?;
148 Ok(Self::from_seed(&seed))
149 }
150}
151
152impl<P: MlDsaParams> fmt::Debug for SigningKey<P> {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 f.debug_struct("SigningKey").finish_non_exhaustive()
155 }
156}
157
158#[cfg(not(feature = "alloc"))]
162impl<P: MlDsaParams> signature::Keypair for SigningKey<P> {
163 type VerifyingKey = VerifyingKey<P>;
164 fn verifying_key(&self) -> VerifyingKey<P> {
165 self.expanded_key.verifying_key()
166 }
167}
168
169#[cfg(feature = "alloc")]
170impl<P: MlDsaParams> AsRef<VerifyingKey<P>> for SigningKey<P> {
171 fn as_ref(&self) -> &VerifyingKey<P> {
172 &self.verifying_key
173 }
174}
175
176#[cfg(feature = "alloc")]
177impl<P: MlDsaParams> signature::KeypairRef for SigningKey<P> {
178 type VerifyingKey = VerifyingKey<P>;
179}
180
181impl<P: MlDsaParams> Signer<Signature<P>> for SigningKey<P> {
184 fn try_sign(&self, msg: &[u8]) -> Result<Signature<P>, Error> {
185 self.try_multipart_sign(&[msg])
186 }
187}
188
189impl<P: MlDsaParams> MultipartSigner<Signature<P>> for SigningKey<P> {
192 fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<Signature<P>, Error> {
193 self.expanded_key.raw_sign_deterministic(msg, &[])
194 }
195}
196
197impl<P: MlDsaParams> DigestSigner<Shake256, Signature<P>> for SigningKey<P> {
200 fn try_sign_digest<F: Fn(&mut Shake256) -> Result<(), Error>>(
201 &self,
202 f: F,
203 ) -> Result<Signature<P>, Error> {
204 self.expanded_key.try_sign_digest(&f)
205 }
206}
207
208impl<P: MlDsaParams> PartialEq for SigningKey<P> {
209 fn eq(&self, other: &Self) -> bool {
210 self.ct_eq(other).into()
211 }
212}
213
214impl<P: MlDsaParams> CtEq for SigningKey<P> {
215 fn ct_eq(&self, other: &Self) -> Choice {
216 self.expanded_key
217 .ct_eq(&other.expanded_key)
218 .and(self.seed.ct_eq(&other.seed))
219 }
220}
221
222impl<P: MlDsaParams> Drop for SigningKey<P> {
223 fn drop(&mut self) {
224 #[cfg(feature = "zeroize")]
226 self.seed.zeroize();
227 }
228}
229
230#[cfg(feature = "zeroize")]
231impl<P: MlDsaParams> ZeroizeOnDrop for SigningKey<P> {}
232
233#[derive(Clone)]
235pub struct ExpandedSigningKey<P: MlDsaParams> {
236 rho: B32,
237 K: B32,
238 pub(crate) tr: B64,
239 s1: Vector<P::L>,
240 s2: Vector<P::K>,
241 t0: Vector<P::K>,
242
243 s1_hat: NttVector<P::L>,
245 s2_hat: NttVector<P::K>,
246 t0_hat: NttVector<P::K>,
247 A_hat: NttMatrix<P::K, P::L>,
248}
249
250impl<P: MlDsaParams> ExpandedSigningKey<P> {
251 pub(crate) fn new(
252 rho: B32,
253 K: B32,
254 tr: B64,
255 s1: Vector<P::L>,
256 s2: Vector<P::K>,
257 t0: Vector<P::K>,
258 A_hat: NttMatrix<P::K, P::L>,
259 ) -> Self {
260 let s1_hat = s1.ntt();
261 let s2_hat = s2.ntt();
262 let t0_hat = t0.ntt();
263
264 Self {
265 rho,
266 K,
267 tr,
268 s1,
269 s2,
270 t0,
271
272 s1_hat,
273 s2_hat,
274 t0_hat,
275 A_hat,
276 }
277 }
278
279 #[inline]
280 fn new_expand_a(
281 rho: B32,
282 K: B32,
283 tr: B64,
284 s1: Vector<P::L>,
285 s2: Vector<P::K>,
286 t0: Vector<P::K>,
287 ) -> Self {
288 let A_hat = expand_a(&rho);
289 Self::new(rho, K, tr, s1, s2, t0, A_hat)
290 }
291
292 #[must_use]
297 #[inline]
298 pub fn from_seed(seed: &Seed) -> Self {
299 let kp = SigningKey::from_seed(seed);
300 (*kp.expanded_key).clone()
301 }
302
303 pub fn sign_internal(&self, Mp: &[&[u8]], rnd: &B32) -> Signature<P>
309 where
310 P: MlDsaParams,
311 {
312 let mu = MuBuilder::internal(&self.tr, Mp);
313 self.raw_sign_mu(&mu, rnd)
314 }
315
316 pub(crate) fn raw_sign_mu(&self, mu: &B64, rnd: &B32) -> Signature<P>
317 where
318 P: MlDsaParams,
319 {
320 let rhopp: B64 = H::default()
322 .absorb(&self.K)
323 .absorb(rnd)
324 .absorb(mu)
325 .squeeze_new();
326
327 for kappa in (0..u16::MAX).step_by(P::L::USIZE) {
329 let y = expand_mask::<P::L, P::Gamma1>(&rhopp, kappa);
330 let w = (&self.A_hat * &y.ntt()).ntt_inverse();
331 let w1 = w.high_bits::<P::TwoGamma2>();
332
333 let w1_tilde = P::encode_w1(&w1);
334 let c_tilde = H::default()
335 .absorb(mu)
336 .absorb(&w1_tilde)
337 .squeeze_new::<P::Lambda>();
338 let c = sample_in_ball(&c_tilde, P::TAU);
339 let c_hat = c.ntt();
340
341 let cs1 = (&c_hat * &self.s1_hat).ntt_inverse();
342 let cs2 = (&c_hat * &self.s2_hat).ntt_inverse();
343
344 let z = &y + &cs1;
345 let r0 = (&w - &cs2).low_bits::<P::TwoGamma2>();
346
347 if z.infinity_norm() >= P::GAMMA1_MINUS_BETA
348 || r0.infinity_norm() >= P::GAMMA2_MINUS_BETA
349 {
350 continue;
351 }
352
353 let ct0 = (&c_hat * &self.t0_hat).ntt_inverse();
354 let minus_ct0 = -&ct0;
355 let w_cs2_ct0 = &(&w - &cs2) + &ct0;
356 let h = Hint::<P>::new(&minus_ct0, &w_cs2_ct0);
357
358 if ct0.infinity_norm() >= P::Gamma2::U32 || h.hamming_weight() > P::Omega::USIZE {
359 continue;
360 }
361
362 let z = MaybeBox::new(z.mod_plus_minus::<SpecQ>());
363 return Signature { c_tilde, z, h };
364 }
365
366 unreachable!("Rejection sampling failed to find a valid signature");
367 }
368
369 #[cfg(feature = "rand_core")]
377 pub fn sign_randomized<R: TryCryptoRng + ?Sized>(
378 &self,
379 M: &[u8],
380 ctx: &[u8],
381 rng: &mut R,
382 ) -> Result<Signature<P>, Error> {
383 self.raw_sign_randomized(&[M], ctx, rng)
384 }
385
386 #[cfg(feature = "rand_core")]
387 fn raw_sign_randomized<R: TryCryptoRng + ?Sized>(
388 &self,
389 Mp: &[&[u8]],
390 ctx: &[u8],
391 rng: &mut R,
392 ) -> Result<Signature<P>, Error> {
393 if ctx.len() > 255 {
394 return Err(Error::new());
395 }
396
397 let mut rnd = B32::default();
398 rng.try_fill_bytes(&mut rnd).map_err(|_| Error::new())?;
399
400 let mu = MuBuilder::new(&self.tr, ctx).message(Mp);
401 Ok(self.raw_sign_mu(&mu, &rnd))
402 }
403
404 #[cfg(feature = "rand_core")]
411 pub fn sign_mu_randomized<R: TryCryptoRng + ?Sized>(
412 &self,
413 mu: &B64,
414 rng: &mut R,
415 ) -> Result<Signature<P>, Error> {
416 let mut rnd = B32::default();
417 rng.try_fill_bytes(&mut rnd).map_err(|_| Error::new())?;
418
419 Ok(self.raw_sign_mu(mu, &rnd))
420 }
421
422 pub fn sign_deterministic(&self, M: &[u8], ctx: &[u8]) -> Result<Signature<P>, Error> {
429 self.raw_sign_deterministic(&[M], ctx)
430 }
431
432 pub fn sign_mu_deterministic(&self, mu: &B64) -> Signature<P> {
436 let rnd = B32::default();
437 self.raw_sign_mu(mu, &rnd)
438 }
439
440 fn raw_sign_deterministic(&self, Mp: &[&[u8]], ctx: &[u8]) -> Result<Signature<P>, Error> {
441 if ctx.len() > 255 {
442 return Err(Error::new());
443 }
444
445 let mu = MuBuilder::new(&self.tr, ctx).message(Mp);
446 Ok(self.sign_mu_deterministic(&mu))
447 }
448
449 pub fn verifying_key(&self) -> VerifyingKey<P> {
461 let kp: &dyn signature::Keypair<VerifyingKey = VerifyingKey<P>> = self;
462 kp.verifying_key()
463 }
464
465 #[deprecated(since = "0.1.0", note = "use `ExpandedSigningKey::from_seed` instead")]
479 pub fn from_expanded(enc: &ExpandedSigningKeyBytes<P>) -> Self
480 where
481 P: MlDsaParams,
482 {
483 let (rho, K, tr, s1_enc, s2_enc, t0_enc) = P::split_sk(enc);
484 Self::new_expand_a(
485 rho.clone(),
486 K.clone(),
487 tr.clone(),
488 P::decode_s1(s1_enc),
489 P::decode_s2(s2_enc),
490 P::decode_t0(t0_enc),
491 )
492 }
493
494 #[deprecated(since = "0.1.0", note = "use `SigningKey::to_seed` instead")]
499 pub fn to_expanded(&self) -> ExpandedSigningKeyBytes<P>
500 where
501 P: MlDsaParams,
502 {
503 let s1_enc = P::encode_s1(&self.s1);
504 let s2_enc = P::encode_s2(&self.s2);
505 let t0_enc = P::encode_t0(&self.t0);
506 P::concat_sk(
507 self.rho.clone(),
508 self.K.clone(),
509 self.tr.clone(),
510 s1_enc,
511 s2_enc,
512 t0_enc,
513 )
514 }
515}
516
517impl<P: MlDsaParams> Signer<Signature<P>> for ExpandedSigningKey<P> {
521 fn try_sign(&self, msg: &[u8]) -> Result<Signature<P>, Error> {
522 self.try_multipart_sign(&[msg])
523 }
524}
525
526impl<P: MlDsaParams> MultipartSigner<Signature<P>> for ExpandedSigningKey<P> {
530 fn try_multipart_sign(&self, msg: &[&[u8]]) -> Result<Signature<P>, Error> {
531 self.raw_sign_deterministic(msg, &[])
532 }
533}
534
535impl<P: MlDsaParams> DigestSigner<Shake256, Signature<P>> for ExpandedSigningKey<P> {
539 fn try_sign_digest<F: Fn(&mut Shake256) -> Result<(), Error>>(
540 &self,
541 f: F,
542 ) -> Result<Signature<P>, Error> {
543 let mut mu = MuBuilder::new(&self.tr, &[]);
544 f(mu.as_mut())?;
545 let mu = mu.finish();
546
547 Ok(self.sign_mu_deterministic(&mu))
548 }
549}
550
551impl<P: MlDsaParams> signature::Keypair for ExpandedSigningKey<P> {
554 type VerifyingKey = VerifyingKey<P>;
555
556 fn verifying_key(&self) -> Self::VerifyingKey {
561 let As1 = &self.A_hat * &self.s1_hat;
562 let t = &As1.ntt_inverse() + &self.s2;
563
564 let (t1, _) = t.power2round();
566
567 VerifyingKey::new(self.rho.clone(), t1, self.A_hat.clone(), None)
568 }
569}
570
571#[cfg(feature = "rand_core")]
575impl<P: MlDsaParams> RandomizedSigner<Signature<P>> for ExpandedSigningKey<P> {
576 fn try_sign_with_rng<R: TryCryptoRng + ?Sized>(
577 &self,
578 rng: &mut R,
579 msg: &[u8],
580 ) -> Result<Signature<P>, Error> {
581 self.try_multipart_sign_with_rng(rng, &[msg])
582 }
583}
584
585#[cfg(feature = "rand_core")]
589impl<P: MlDsaParams> RandomizedMultipartSigner<Signature<P>> for ExpandedSigningKey<P> {
590 fn try_multipart_sign_with_rng<R: TryCryptoRng + ?Sized>(
591 &self,
592 rng: &mut R,
593 msg: &[&[u8]],
594 ) -> Result<Signature<P>, Error> {
595 self.raw_sign_randomized(msg, &[], rng)
596 }
597}
598
599#[cfg(feature = "rand_core")]
603impl<P: MlDsaParams> RandomizedDigestSigner<Shake256, Signature<P>> for ExpandedSigningKey<P> {
604 fn try_sign_digest_with_rng<
605 R: TryCryptoRng + ?Sized,
606 F: Fn(&mut Shake256) -> Result<(), Error>,
607 >(
608 &self,
609 rng: &mut R,
610 f: F,
611 ) -> Result<Signature<P>, Error> {
612 let mut mu = MuBuilder::new(&self.tr, &[]);
613 f(mu.as_mut())?;
614 let mu = mu.finish();
615
616 self.sign_mu_randomized(&mu, rng)
617 }
618}
619
620impl<P: MlDsaParams> PartialEq for ExpandedSigningKey<P> {
621 fn eq(&self, other: &Self) -> bool {
622 self.ct_eq(other).into()
623 }
624}
625
626impl<P: MlDsaParams> CtEq for ExpandedSigningKey<P> {
627 fn ct_eq(&self, other: &Self) -> Choice {
628 self.rho
629 .ct_eq(&other.rho)
630 .and(self.K.ct_eq(&other.K))
631 .and(self.tr.ct_eq(&other.tr))
632 .and(self.s1.ct_eq(&other.s1))
633 .and(self.s2.ct_eq(&other.s2))
634 .and(self.t0.ct_eq(&other.t0))
635 }
636}
637
638impl<P: MlDsaParams> fmt::Debug for ExpandedSigningKey<P> {
639 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
640 f.debug_struct("ExpandedSigningKey").finish_non_exhaustive()
641 }
642}
643
644impl<P: MlDsaParams> Drop for ExpandedSigningKey<P> {
645 fn drop(&mut self) {
646 #[cfg(feature = "zeroize")]
647 {
648 self.rho.zeroize();
649 self.K.zeroize();
650 self.tr.zeroize();
651 self.s1.zeroize();
652 self.s2.zeroize();
653 self.t0.zeroize();
654 self.s1_hat.zeroize();
655 self.s2_hat.zeroize();
656 self.t0_hat.zeroize();
657 }
658 }
659}
660
661#[cfg(feature = "zeroize")]
662impl<P: MlDsaParams> ZeroizeOnDrop for ExpandedSigningKey<P> {}