Skip to main content

bouncycastle_mldsa/
mldsa.rs

1//! This page documents advanced features of the Module Lattice Digital Signature Algorithm (ML-DSA)
2//! available in this crate.
3//!
4//!
5//! # Streaming APIs
6//!
7//! Sometimes the message you need to sign or verify is too big to fit in device memory all at once.
8//! No worries, we got you covered!
9//!
10//! ```rust
11//! use bouncycastle_core::errors::SignatureError;
12//! use bouncycastle_core::traits::Signature;
13//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
14//!
15//! let (pk, sk) = MLDSA65::keygen().unwrap();
16//!
17//! // Let's pretend this message was so long that you couldn't possibly
18//! // stream the whole thing over a network, and you need it pre-hashed.
19//! let msg_chunk1 = b"The quick brown fox ";
20//! let msg_chunk2 = b"jumped over the lazy dog";
21//!
22//! let mut signer = MLDSA65::sign_init(&sk, None).unwrap();
23//! signer.sign_update(msg_chunk1);
24//! signer.sign_update(msg_chunk2);
25//! let sig = signer.sign_final().unwrap();
26//! // This is the signature value that you can save to a file or whatever you need.
27//!
28//! // This is compatible with a verifies that takes the whole message as one chunk:
29//! let msg = b"The quick brown fox jumped over the lazy dog";
30//! match MLDSA65::verify(&pk, msg, None, &sig) {
31//!     Ok(()) => println!("Signature is valid!"),
32//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
33//!     Err(e) => panic!("Something else went wrong: {:?}", e),
34//! }
35//!
36//! // But of course there's also a streaming API for the verifier!
37//! let mut verifier = MLDSA65::verify_init(&pk, None).unwrap();
38//! verifier.verify_update(msg_chunk1);
39//! verifier.verify_update(msg_chunk2);
40//!
41//! match verifier.verify_final(&sig.as_slice()) {
42//!     Ok(()) => println!("Signature is valid!"),
43//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
44//!     Err(e) => panic!("Something else went wrong: {:?}", e),
45//! }
46//! ```
47//!
48//!
49//! Note that the streaming API also supports setting the signing context `ctx` and signing nonce `rnd`,
50//! which are explained in more detail below.
51//!
52//! ```rust
53//! use bouncycastle_core::errors::SignatureError;
54//! use bouncycastle_core::traits::Signature;
55//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
56//!
57//! let (pk, sk) = MLDSA65::keygen().unwrap();
58//!
59//! // Let's pretend this message was so long that you couldn't possibly
60//! // stream the whole thing over a network, and you need it pre-hashed.
61//! let msg_chunk1 = b"The quick brown fox ";
62//! let msg_chunk2 = b"jumped over the lazy dog";
63//!
64//! let mut signer = MLDSA65::sign_init(&sk, Some(b"signing ctx value")).unwrap();
65//! signer.set_signer_rnd([0u8; 32]); // an all-zero rnd is the "deterministic" mode of ML-DSA
66//! signer.sign_update(msg_chunk1);
67//! signer.sign_update(msg_chunk2);
68//! let sig = signer.sign_final().unwrap();
69//! ```
70//!
71//! # External Mu mode
72//!
73//! Here, `mu` refers to the message digest which is computed internally to the ML-DSA algorithm:
74//!
75//! > πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀′, 64)
76//! >   β–· message representative that may optionally be computed in a different cryptographic module
77//!
78//! The External Mu mode of ML-DSA fulfills a similar function to [hash_mldsa] in that it allows large
79//! messages to be pre-digested outside of the cryptographic module that holds the private key,
80//! but it does it in a way that is compatible with the ML-DSA verification function.
81//! In other works, whereas [hash_mldsa] represents a different signature algorithm, the external mu
82//! mode of ML-DSA is simply internal implementation detail of how the signature was computed and
83//! produces signatures that are indistinguishable from "direct" ML-DSA mode.
84//!
85//! The one potential complication with external mu mode -- that [hash_mldsa] does not have --
86//! is that it requires you to know the public key that you are about to sign the message with.
87//! Or, more specifically, the hash of the public key `tr`.
88//! `tr` is a public value (derivable from the public key), so there is no harm in, for example,
89//! sending it down to a client device so that it can pre-hash a large message and only send the
90//! 64-byte `mu` value up to the server to be signed.
91//! But in some contexts, the message has to be pre-hashed for performance reasons but
92//! the public key that will be used for signing cannot be known in advance.
93//! For those use cases, your only choice is to use [hash_mldsa].
94//!
95//! This library exposes [MuBuilder] which can be used to pre-hash a large to-be-signed message
96//! along with the public key hash `tr`:
97//!
98//! ```rust
99//! use bouncycastle_core::errors::SignatureError;
100//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
101//! use bouncycastle_core::traits::Signature;
102//!
103//! let (pk, _) = MLDSA65::keygen().unwrap();
104//!
105//! // Let's pretend this message was so long that you couldn't possibly
106//! // stream the whole thing over a network, and you need it pre-hashed.
107//! let msg = b"The quick brown fox jumped over the lazy dog";
108//!
109//! let mu: [u8; 64] = MuBuilder::compute_mu(&pk.compute_tr(), msg, None).unwrap();
110//! ```
111//!
112//! Note: if you are going to bind a `ctx` value (explained below), then you need to do in in [MuBuilder::compute_mu].
113//!
114//! If the message really is so huge that you can't hold it all in memory at once, then you might prefer a streaming API for
115//! computing mu:
116//!
117//! ```rust
118//! use bouncycastle_core::errors::SignatureError;
119//! use bouncycastle_core::traits::Signature;
120//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
121//!
122//! let (pk, _) = MLDSA65::keygen().unwrap();
123//!
124//! // Let's pretend this message was so long that you couldn't possibly
125//! // stream the whole thing over a network, and you need it pre-hashed.
126//! let msg_chunk1 = b"The quick brown fox ";
127//! let msg_chunk2 = b"jumped over the lazy dog";
128//!
129//! let mut mb = MuBuilder::do_init(&pk.compute_tr(), None).unwrap();
130//! mb.do_update(msg_chunk1);
131//! mb.do_update(msg_chunk2);
132//! let mu = mb.do_final();
133//! ```
134//!
135//! Given a mu value, you can compute a signature that verifies as normal (no mu's required!):
136//!
137//! ```rust
138//! use bouncycastle_core::errors::SignatureError;
139//! use bouncycastle_core::traits::Signature;
140//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
141//!
142//! let msg = b"The quick brown fox jumped over the lazy dog";
143//!
144//! let (pk, sk) = MLDSA65::keygen().unwrap();
145//!
146//! // Assume this was computed somewhere else and sent to you.
147//! // They would have had to know pk!
148//! let mu: [u8; 64] = MuBuilder::compute_mu(&pk.compute_tr(), msg, None).unwrap();
149//!
150//! let sig = MLDSA65::sign_mu(&sk, None, &mu).unwrap();
151//! // This is the signature value that you can save to a file or whatever you need.
152//!
153//! match MLDSA65::verify(&pk, msg, None, &sig) {
154//!     Ok(()) => println!("Signature is valid!"),
155//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
156//!     Err(e) => panic!("Something else went wrong: {:?}", e),
157//! }
158//!
159//! ```
160//!
161//! # Ctx and Rnd params
162//! Various functions in this crate let you set the signing context value (`ctx`) and the signing nonce (`rnd`).
163//! Let's talk about them both:
164//!
165//! ## ctx
166//! The `ctx` value allows the signer to bind the signature value to an extra piece of information
167//! (up to 255 bytes long) that must also be known to the verifier in order to successfully verify the signature.
168//! This optional parameter allows cryptographic protocol designers to get additional binding properties
169//! from the ML-DSA signature.
170//! The `ctx` value should be something that is known to both the signer and verifier,
171//! does not necessarily need to be a secret, but should not go over the wire as part of the not-yet-verified message.
172//! Examples of uses of the `ctx` could include binding the application data type (ex: `FooEmailData`) in order
173//! to disambiguate other data types that share an encoding (ex: `FooTextDocumentData`) and might otherwise be possible for an
174//! attacker to trick a verifier into accepting one in place of the other.
175//! In a network protocol, `ctx` could be used to bind a transaction ID or protocol nonce in order to strongly
176//! protect against replay attacks.
177//! Generally, `ctx` is one of those things that if you don't know what it does, then you're probably
178//! fine to ignore it.
179//!
180//! Example of signing and verifying with a `ctx` value:
181//!
182//! ```rust
183//! use bouncycastle_core::errors::SignatureError;
184//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait};
185//! use bouncycastle_core::traits::Signature;
186//!
187//! let msg = b"The quick brown fox";
188//! let ctx = b"FooTextDocumentFormat";
189//!
190//! let (pk, sk) = MLDSA65::keygen().unwrap();
191//!
192//! let sig = MLDSA65::sign(&sk, msg, Some(ctx)).unwrap();
193//! // This is the signature value that you can save to a file or whatever you need.
194//!
195//! match MLDSA65::verify(&pk, msg, Some(ctx), &sig) {
196//!     Ok(()) => println!("Signature is valid!"),
197//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
198//!     Err(e) => panic!("Something else went wrong: {:?}", e),
199//! }
200//! ```
201//!
202//! ## rnd
203//!
204//! This is the signature nonce, whose purpose is to ensure that you get different signature values
205//! if you sign the same message with the same public key multiple times.
206//!
207//! In general, the "deterministic" mode of ML-DSA (which usually uses an all-zero `rnd`) is considered
208//! secure and safe to use but you may lose certain privacy properties, because, for example,
209//! it becomes obvious that multiple identical signatures means that the same message was signed multiple times
210//! by the same private key.
211//!
212//! The default mode of ML-DSA uses a `rnd` generated by the library's OS-backed RNG, but you can set the `rnd`
213//! if you need to; for example if you are running on an embedded device that does not have access to an RNG.
214//!
215//! Note that in order to avoid combinatorial explosion of API functions, setting the `rnd` value is only
216//! available in conjunction with external mu or streaming modes. The example of setting `rnd` on the streaming
217//! API was shown above.
218//!
219//! Here is an example of using the [MLDSA::sign_mu_deterministic] function:
220//!
221//! ```rust
222//! use bouncycastle_core::errors::SignatureError;
223//! use bouncycastle_core::traits::Signature;
224//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
225//!
226//! let msg = b"The quick brown fox jumped over the lazy dog";
227//!
228//! let (pk, sk) = MLDSA65::keygen().unwrap();
229//!
230//! // Assume this was computed somewhere else and sent to you.
231//! // They would have had to know pk!
232//! let mu: [u8; 64] = MuBuilder::compute_mu(&pk.compute_tr(), msg, None).unwrap();
233//!
234//! // Typically, "deterministic" mode of ML-DSA will use an all-zero rnd,
235//! // but we've exposed it so you can set any value you need to.
236//! let sig = MLDSA65::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap();
237//! // This is the signature value that you can save to a file or whatever you need.
238//!
239//! match MLDSA65::verify(&pk, msg, None, &sig) {
240//!     Ok(()) => println!("Signature is valid!"),
241//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
242//!     Err(e) => panic!("Something else went wrong: {:?}", e),
243//! }
244//! ```
245//!
246//! # Pre-expanding the public key for repeated use
247//!
248//! Within the usual ML-DSA public key representation, the public matrix A is stored as a seed rho, which
249//! means that both the ML-DSA.sign() and ML-DSA.verify() operations need to expand it into a full matrix
250//! before performing the matrix multiplication.
251//! We offer a version of the public and private key structs that pre-expand the public matrix for repeated use.
252//!
253//! The runtime of ML-DSA.sign() is dominated by the rejection sampling look, making the A-expansion
254//! a negligible part of the function -- accounting for only about 2% of the computation.
255//! However, for ML-DSA.verify(), pre-expansion of the public matrix A gives speedups of 38% / 45% / 63%
256//! speedup for ML-DSA 44 / 65 / 87, especially if verifying multiple signatures against the same public key.
257//!
258//! ```rust
259//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait};
260//! use bouncycastle_mldsa::{MLDSA65PublicKeyExpanded, MLDSA65PrivateKeyExpanded};
261//! use bouncycastle_core::traits::Signature;
262//! use bouncycastle_core::errors::SignatureError;
263//!
264//! let msg = b"The quick brown fox";
265//!
266//! let (pk, sk) = MLDSA65::keygen().unwrap();
267//!
268//! // The pre-expanded private key uses more memory, but has performance
269//! // improvements if doing multiple decapsulations with the same key
270//! // although the performance improvements on signing are slight -- only around 2%.
271//! let sk_expanded = MLDSA65PrivateKeyExpanded::from(&sk);
272//! let sig = MLDSA65::sign_with_expanded_key(&sk_expanded, msg, None).unwrap();
273//!
274//! // The pre-expand the public key uses more memory, but has performance
275//! // improvements if doing multiple encapsulations for the same key.
276//! let pk_expanded = MLDSA65PublicKeyExpanded::from(&pk);
277//! match MLDSA65::verify_with_expanded_key(&pk_expanded, msg, None, &sig) {
278//!     Ok(()) => println!("Signature is valid!"),
279//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
280//!     Err(e) => panic!("Something else went wrong: {:?}", e),
281//! }
282//! ```
283//!
284//! # sign_from_seed
285//!
286//! This mode is intended for users with extreme performance or resource-limitation requirements.
287//!
288//! A very careful analysis of the ML-DSA signing algorithm will show that you don't actually need
289//! the entire ML-DSA private key to be in memory at the same time. In fact, it is possible to merge
290//! the keygen() and sign() functions
291//!
292//! We provide [MLDSA::sign_mu_deterministic_from_seed] which implements such an algorithm.
293//! It has a significantly lower peak-memory-footprint than the regular signing API (although there's
294//! always room for more optimization), and according to our benchmarks it is only around 25% slower
295//! than signing with a fully-expanded private key -- which is still faster than performing a full
296//! keygen followed by a regular sign since there are intermediate values common to keygen and sign
297//! that the merged function is able to only compute once.
298//!
299//! Since this is intended for hard-core embedded systems people, we have not wrapped this in all
300//! the beginner-friendly APIs. If you need this, then we assume you know what you're doing!
301//!
302//! Example usage:
303//!
304//! ```rust
305//! use bouncycastle_core::errors::SignatureError;
306//! use bouncycastle_core::traits::Signature;
307//! use bouncycastle_core::key_material::{KeyMaterial256, KeyType, KeyMaterialTrait};
308//! use bouncycastle_hex as hex;
309//! use bouncycastle_mldsa::{MLDSA44, MLDSA44_SIG_LEN, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
310//!
311//! let msg = b"The quick brown fox jumped over the lazy dog";
312//!
313//! let seed = KeyMaterial256::from_bytes_as_type(
314//!     &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(),
315//!     KeyType::Seed,
316//! ).unwrap();
317//!
318//! // At some point, you'll need to compute the public key, both to get `tr`, and so other
319//! // people can verify your signature.
320//! // There's no possible short-cut to efficiently computing the public key or `tr` from the seed;
321//! // you have to run the full keygen to get the full private key, at least momentarily, then
322//! // you can discard it in only keep `tr` and `seed`.
323//! let (pk, _) = MLDSA44::keygen_from_seed(&seed).unwrap();
324//! let tr: [u8; 64] = pk.compute_tr();
325//!
326//! // Assume this was computed somewhere else and sent to you.
327//! // They would have had to know pk!
328//! let mu: [u8; 64] = MuBuilder::compute_mu(&tr, msg, None).unwrap();
329//! let rnd: [u8; 32] = [0u8; 32]; // with this API, you're responsible for your own nonce
330//!                                // because in the cases where this level of memory optimization
331//!                                // is needed, our RNG probably won't work anyway.
332//!
333//! let mut sig = [0u8; MLDSA44_SIG_LEN];
334//! let bytes_written = MLDSA44::sign_mu_deterministic_from_seed_out(&seed, &mu, rnd, &mut sig)
335//!                                                                                 .unwrap();
336//!
337//! // it can be verified normally
338//! match MLDSA44::verify(&pk, msg, None, &sig) {
339//!     Ok(()) => println!("Signature is valid!"),
340//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
341//!     Err(e) => panic!("Something else went wrong: {:?}", e),
342//! }
343//! ```
344//!
345//! While this is currently only supported when operating from a seed-based private key, something analogous
346//! could be done that merges the sk_decode() and sign() routines when working with the standardized
347//! private key encoding (which is often called the "semi-expanded format" since the in-memory representation
348//! is still larger).
349//! Contact us if you need such a thing implemented.
350//!
351//!
352//! # Pre-expanding the public key for repeated use
353//!
354//! One of the computationally expensive parts of both ML-DSA.sign() and ML-DSA.verify() is expansion
355//! of the public matrix A from the public seed that is stored in with the compressed representation.
356//! When done as part of the keygen, expansion of the public matrix accounts for 20% - 30% of the keygen
357//! and verification time (depending on parameter set), and around 5% of the signing time since the
358//! rejection sampling loop dominates the runtime of the sign operation.
359//!
360//! If the key is loaded and used once for a single signature or a single verification,
361//! then there is no performance difference to whether the
362//! public matrix A is expanded as part of keygen or as part of sign / verify, but it does make both
363//! the public and private key take up more space in memory, so the default ML-DSA public and private key
364//! objects defer expansion until it is needed.
365//!
366//! However, in uses where many sign or verify operations are performed against the same
367//! key pair in quick succession, there can be substantial performance improvements to pre-computing
368//! this and holding on to a larger key object.
369//! This is accomplished via constructing a [MLDSAPublicKeyExpanded] or [MLDSAPrivateKeyExpanded] object
370//! of the appropriate parameter set from the original key, and then using this with [MLDSA::sign_with_expanded_key]
371//! or [MLDSA::verify_with_expanded_key].
372//! Both [MLDSAPublicKeyExpanded] and [MLDSAPrivateKeyExpanded] implement the same traits
373//! and therefore behave the same as their non-expanded counterparts in most regards.
374//!
375//! ```rust
376//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait};
377//! use bouncycastle_mldsa::{MLDSA65PublicKeyExpanded, MLDSA65PrivateKeyExpanded};
378//! use bouncycastle_core::traits::Signature;
379//! use bouncycastle_core::errors::SignatureError;
380//!
381//! let msg = b"The quick brown fox jumped over the lazy dog";
382//!
383//! let (pk, sk) = MLDSA65::keygen().unwrap();
384//!
385//! // pre-expand the private key, which uses more memory, but has performance
386//! // improvements if doing multiple decapsulations with the same key
387//! let sk_expanded = MLDSA65PrivateKeyExpanded::from(&sk);
388//!
389//! let sig = MLDSA65::sign_with_expanded_key(&sk_expanded, msg, None).unwrap();
390//!
391//! // pre-expand the public key, which uses more memory, but has performance
392//! // improvements if doing multiple verifications with the same key
393//! let pk_expanded = MLDSA65PublicKeyExpanded::from(&pk);
394//!
395//! match MLDSA65::verify_with_expanded_key(&pk_expanded, msg, None, &sig) {
396//!     Ok(()) => println!("Signature is valid!"),
397//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
398//!     Err(e) => panic!("Something else went wrong: {:?}", e),
399//! }
400//! ```
401
402use crate::aux_functions::{
403    expand_mask, expandA, expandS, make_hint_vecs, power_2_round_vec, sample_in_ball, sig_decode,
404    sig_encode, use_hint_vecs,
405};
406use crate::matrix::{Matrix, Vector};
407use crate::mldsa_keys::{MLDSAPrivateKeyInternalTrait, MLDSAPrivateKeyTrait};
408use crate::mldsa_keys::{MLDSAPublicKeyInternalTrait, MLDSAPublicKeyTrait};
409use crate::{
410    MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65PrivateKey, MLDSA65PublicKey, MLDSA87PrivateKey,
411    MLDSA87PublicKey, MLDSAPrivateKeyExpanded, MLDSAPublicKeyExpanded,
412};
413use bouncycastle_core::errors::SignatureError;
414use bouncycastle_core::key_material::{KeyMaterial, KeyMaterial256, KeyMaterialTrait, KeyType};
415use bouncycastle_core::traits::{Algorithm, RNG, SecurityStrength, Signature, XOF};
416use bouncycastle_rng::HashDRBG_SHA512;
417use bouncycastle_sha3::{SHAKE128, SHAKE256};
418use core::marker::PhantomData;
419
420// imports needed just for docs
421#[allow(unused_imports)]
422use crate::hash_mldsa;
423#[allow(unused_imports)]
424use bouncycastle_core::traits::PHSignature;
425
426/*** Constants ***/
427
428///
429pub const ML_DSA_44_NAME: &str = "ML-DSA-44";
430///
431pub const ML_DSA_65_NAME: &str = "ML-DSA-65";
432///
433pub const ML_DSA_87_NAME: &str = "ML-DSA-87";
434
435// From FIPS 204 Table 1 and Table 2
436
437// Constants that are the same for all parameter sets
438pub(crate) const N: usize = 256;
439pub(crate) const q: i32 = 8380417;
440pub(crate) const q_inv: i32 = 58728449; // q ^ (-1) mod 2 ^32
441pub(crate) const d: i32 = 13;
442/// Length of the \[u8] holding an ML-DSA seed value.
443pub const MLDSA_SEED_LEN: usize = 32;
444/// Length of the \[u8] holding an ML-DSA signing random value.
445pub const MLDSA_RND_LEN: usize = 32;
446/// Length of the \[u8] holding an ML-DSA tr value (which is the SHAKE256 hash of the public key).
447pub const MLDSA_TR_LEN: usize = 64;
448/// Length of the \[u8] holding an ML-DSA mu value.
449pub const MLDSA_MU_LEN: usize = 64;
450pub(crate) const POLY_T0PACKED_LEN: usize = 416;
451pub(crate) const POLY_T1PACKED_LEN: usize = 320;
452
453/* ML-DSA-44 params */
454
455/// Length of the \[u8] holding a ML-DSA-44 public key.
456pub const MLDSA44_PK_LEN: usize = 1312;
457/// Length of the \[u8] holding a ML-DSA-44 private key.
458pub const MLDSA44_SK_LEN: usize = 2560;
459/// Length of the \[u8] holding a ML-DSA-44 signature value.
460pub const MLDSA44_SIG_LEN: usize = 2420;
461pub(crate) const MLDSA44_TAU: i32 = 39;
462pub(crate) const MLDSA44_LAMBDA: i32 = 128;
463pub(crate) const MLDSA44_GAMMA1: i32 = 1 << 17;
464pub(crate) const MLDSA44_GAMMA2: i32 = (q - 1) / 88; // mutants note: because of the bitshifting, the "- 1" ends up not mattering
465pub(crate) const MLDSA44_k: usize = 4;
466pub(crate) const MLDSA44_l: usize = 4;
467pub(crate) const MLDSA44_ETA: usize = 2;
468pub(crate) const MLDSA44_BETA: i32 = 78;
469pub(crate) const MLDSA44_OMEGA: i32 = 80;
470
471// Useful derived values
472pub(crate) const MLDSA44_C_TILDE: usize = 32;
473pub(crate) const MLDSA44_POLY_Z_PACKED_LEN: usize = 576;
474pub(crate) const MLDSA44_POLY_W1_PACKED_LEN: usize = 192;
475pub(crate) const MLDSA44_LAMBDA_over_4: usize = 128 / 4;
476pub(crate) const MLDSA44_GAMMA1_MINUS_BETA: i32 = MLDSA44_GAMMA1 - MLDSA44_BETA;
477pub(crate) const MLDSA44_GAMMA2_MINUS_BETA: i32 = MLDSA44_GAMMA2 - MLDSA44_BETA;
478
479// Alg 32
480// 1: 𝑐 ← 1 + bitlen (𝛾1 βˆ’ 1)
481pub(crate) const MLDSA44_GAMMA1_MASK_LEN: usize = 576; // 32*(1 + bitlen (𝛾1 βˆ’ 1) )
482
483/* ML-DSA-65 params */
484
485/// Length of the \[u8] holding a ML-DSA-65 public key.
486pub const MLDSA65_PK_LEN: usize = 1952;
487/// Length of the \[u8] holding a ML-DSA-65 private key.
488pub const MLDSA65_SK_LEN: usize = 4032;
489/// Length of the \[u8] holding a ML-DSA-65 signature value.
490pub const MLDSA65_SIG_LEN: usize = 3309;
491pub(crate) const MLDSA65_TAU: i32 = 49;
492pub(crate) const MLDSA65_LAMBDA: i32 = 192;
493pub(crate) const MLDSA65_GAMMA1: i32 = 1 << 19;
494pub(crate) const MLDSA65_GAMMA2: i32 = (q - 1) / 32; // mutants note: because of the bitshifting, the "- 1" ends up not mattering
495pub(crate) const MLDSA65_k: usize = 6;
496pub(crate) const MLDSA65_l: usize = 5;
497pub(crate) const MLDSA65_ETA: usize = 4;
498pub(crate) const MLDSA65_BETA: i32 = 196;
499pub(crate) const MLDSA65_OMEGA: i32 = 55;
500
501// Useful derived values
502pub(crate) const MLDSA65_C_TILDE: usize = 48;
503pub(crate) const MLDSA65_POLY_Z_PACKED_LEN: usize = 640;
504pub(crate) const MLDSA65_POLY_W1_PACKED_LEN: usize = 128;
505pub(crate) const MLDSA65_LAMBDA_over_4: usize = 192 / 4;
506pub(crate) const MLDSA65_GAMMA1_MINUS_BETA: i32 = MLDSA65_GAMMA1 - MLDSA65_BETA;
507pub(crate) const MLDSA65_GAMMA2_MINUS_BETA: i32 = MLDSA65_GAMMA2 - MLDSA65_BETA;
508
509// Alg 32
510// 1: 𝑐 ← 1 + bitlen (𝛾1 βˆ’ 1)
511pub(crate) const MLDSA65_GAMMA1_MASK_LEN: usize = 640;
512
513/* ML-DSA-87 params */
514
515/// Length of the \[u8] holding a ML-DSA-87 public key.
516pub const MLDSA87_PK_LEN: usize = 2592;
517/// Length of the \[u8] holding a ML-DSA-87 private key.
518pub const MLDSA87_SK_LEN: usize = 4896;
519/// Length of the \[u8] holding a ML-DSA-87 signature value.
520pub const MLDSA87_SIG_LEN: usize = 4627;
521pub(crate) const MLDSA87_TAU: i32 = 60;
522pub(crate) const MLDSA87_LAMBDA: i32 = 256;
523pub(crate) const MLDSA87_GAMMA1: i32 = 1 << 19;
524pub(crate) const MLDSA87_GAMMA2: i32 = (q - 1) / 32; // mutants note: because of the bitshifting, the "- 1" ends up not mattering
525pub(crate) const MLDSA87_k: usize = 8;
526pub(crate) const MLDSA87_l: usize = 7;
527pub(crate) const MLDSA87_ETA: usize = 2;
528pub(crate) const MLDSA87_BETA: i32 = 120;
529pub(crate) const MLDSA87_OMEGA: i32 = 75;
530
531// Useful derived values
532pub(crate) const MLDSA87_C_TILDE: usize = 64;
533pub(crate) const MLDSA87_POLY_Z_PACKED_LEN: usize = 640;
534pub(crate) const MLDSA87_POLY_W1_PACKED_LEN: usize = 128;
535pub(crate) const MLDSA87_LAMBDA_over_4: usize = 256 / 4;
536pub(crate) const MLDSA87_GAMMA1_MINUS_BETA: i32 = MLDSA87_GAMMA1 - MLDSA87_BETA;
537pub(crate) const MLDSA87_GAMMA2_MINUS_BETA: i32 = MLDSA87_GAMMA2 - MLDSA87_BETA;
538
539// Alg 32
540// 1: 𝑐 ← 1 + bitlen (𝛾1 βˆ’ 1)
541pub(crate) const MLDSA87_GAMMA1_MASK_LEN: usize = 640;
542
543// Typedefs just to make the algorithms look more like the FIPS 204 sample code.
544pub(crate) type H = SHAKE256;
545pub(crate) type G = SHAKE128;
546
547/*** Pub Types ***/
548
549/// The ML-DSA-44 algorithm.
550pub type MLDSA44 = MLDSA<
551    MLDSA44_PK_LEN,
552    MLDSA44_SK_LEN,
553    MLDSA44_SIG_LEN,
554    MLDSA44PublicKey,
555    MLDSA44PrivateKey,
556    MLDSA44_TAU,
557    MLDSA44_LAMBDA,
558    MLDSA44_GAMMA1,
559    MLDSA44_GAMMA2,
560    MLDSA44_k,
561    MLDSA44_l,
562    MLDSA44_ETA,
563    MLDSA44_BETA,
564    MLDSA44_OMEGA,
565    MLDSA44_C_TILDE,
566    MLDSA44_POLY_Z_PACKED_LEN,
567    MLDSA44_POLY_W1_PACKED_LEN,
568    MLDSA44_LAMBDA_over_4,
569    MLDSA44_GAMMA1_MASK_LEN,
570    MLDSA44_GAMMA1_MINUS_BETA,
571    MLDSA44_GAMMA2_MINUS_BETA,
572>;
573
574impl Algorithm for MLDSA44 {
575    const ALG_NAME: &'static str = ML_DSA_44_NAME;
576    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit;
577}
578
579/// The ML-DSA-65 algorithm.
580pub type MLDSA65 = MLDSA<
581    MLDSA65_PK_LEN,
582    MLDSA65_SK_LEN,
583    MLDSA65_SIG_LEN,
584    MLDSA65PublicKey,
585    MLDSA65PrivateKey,
586    MLDSA65_TAU,
587    MLDSA65_LAMBDA,
588    MLDSA65_GAMMA1,
589    MLDSA65_GAMMA2,
590    MLDSA65_k,
591    MLDSA65_l,
592    MLDSA65_ETA,
593    MLDSA65_BETA,
594    MLDSA65_OMEGA,
595    MLDSA65_C_TILDE,
596    MLDSA65_POLY_Z_PACKED_LEN,
597    MLDSA65_POLY_W1_PACKED_LEN,
598    MLDSA65_LAMBDA_over_4,
599    MLDSA65_GAMMA1_MASK_LEN,
600    MLDSA65_GAMMA1_MINUS_BETA,
601    MLDSA65_GAMMA2_MINUS_BETA,
602>;
603
604impl Algorithm for MLDSA65 {
605    const ALG_NAME: &'static str = ML_DSA_65_NAME;
606    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit;
607}
608
609/// The ML-DSA-87 algorithm.
610pub type MLDSA87 = MLDSA<
611    MLDSA87_PK_LEN,
612    MLDSA87_SK_LEN,
613    MLDSA87_SIG_LEN,
614    MLDSA87PublicKey,
615    MLDSA87PrivateKey,
616    MLDSA87_TAU,
617    MLDSA87_LAMBDA,
618    MLDSA87_GAMMA1,
619    MLDSA87_GAMMA2,
620    MLDSA87_k,
621    MLDSA87_l,
622    MLDSA87_ETA,
623    MLDSA87_BETA,
624    MLDSA87_OMEGA,
625    MLDSA87_C_TILDE,
626    MLDSA87_POLY_Z_PACKED_LEN,
627    MLDSA87_POLY_W1_PACKED_LEN,
628    MLDSA87_LAMBDA_over_4,
629    MLDSA87_GAMMA1_MASK_LEN,
630    MLDSA87_GAMMA1_MINUS_BETA,
631    MLDSA87_GAMMA2_MINUS_BETA,
632>;
633
634impl Algorithm for MLDSA87 {
635    const ALG_NAME: &'static str = ML_DSA_87_NAME;
636    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit;
637}
638
639/// The core internal implementation of the ML-DSA algorithm.
640/// This needs to be public for the compiler to be able to find it, but you shouldn't ever
641/// need to use this directly. Please use the named public types.
642pub struct MLDSA<
643    const PK_LEN: usize,
644    const SK_LEN: usize,
645    const SIG_LEN: usize,
646    PK: MLDSAPublicKeyTrait<k, l, PK_LEN> + MLDSAPublicKeyInternalTrait<k, PK_LEN>,
647    SK: MLDSAPrivateKeyTrait<k, l, ETA, SK_LEN, PK_LEN>
648        + MLDSAPrivateKeyInternalTrait<k, l, ETA, SK_LEN, PK_LEN>,
649    const TAU: i32,
650    const LAMBDA: i32,
651    const GAMMA1: i32,
652    const GAMMA2: i32,
653    const k: usize,
654    const l: usize,
655    const ETA: usize,
656    const BETA: i32,
657    const OMEGA: i32,
658    const C_TILDE: usize,
659    const POLY_VEC_H_PACKED_LEN: usize,
660    const POLY_W1_PACKED_LEN: usize,
661    const LAMBDA_over_4: usize,
662    const GAMMA1_MASK_LEN: usize,
663    const GAMMA1_MINUS_BETA: i32,
664    const GAMMA2_MINUS_BETA: i32,
665> {
666    _phantom: PhantomData<(PK, SK)>,
667
668    /// used for streaming the message for both signing and verifying
669    mu_builder: MuBuilder,
670
671    signer_rnd: Option<[u8; MLDSA_RND_LEN]>,
672
673    /// only used in streaming sign operations
674    sk: Option<SK>,
675
676    /// only used in streaming sign operations instead of sk
677    seed: Option<KeyMaterial<32>>,
678
679    /// only used in streaming verify operations
680    pk: Option<PK>,
681}
682
683impl<
684    const PK_LEN: usize,
685    const SK_LEN: usize,
686    const SIG_LEN: usize,
687    PK: MLDSAPublicKeyTrait<k, l, PK_LEN> + MLDSAPublicKeyInternalTrait<k, PK_LEN>,
688    SK: MLDSAPrivateKeyTrait<k, l, ETA, SK_LEN, PK_LEN>
689        + MLDSAPrivateKeyInternalTrait<k, l, ETA, SK_LEN, PK_LEN>,
690    const TAU: i32,
691    const LAMBDA: i32,
692    const GAMMA1: i32,
693    const GAMMA2: i32,
694    const k: usize,
695    const l: usize,
696    const ETA: usize,
697    const BETA: i32,
698    const OMEGA: i32,
699    const C_TILDE: usize,
700    const POLY_Z_PACKED_LEN: usize,
701    const POLY_W1_PACKED_LEN: usize,
702    const LAMBDA_over_4: usize,
703    const GAMMA1_MASK_LEN: usize,
704    const GAMMA1_MINUS_BETA: i32,
705    const GAMMA2_MINUS_BETA: i32,
706>
707    MLDSA<
708        PK_LEN,
709        SK_LEN,
710        SIG_LEN,
711        PK,
712        SK,
713        TAU,
714        LAMBDA,
715        GAMMA1,
716        GAMMA2,
717        k,
718        l,
719        ETA,
720        BETA,
721        OMEGA,
722        C_TILDE,
723        POLY_Z_PACKED_LEN,
724        POLY_W1_PACKED_LEN,
725        LAMBDA_over_4,
726        GAMMA1_MASK_LEN,
727        GAMMA1_MINUS_BETA,
728        GAMMA2_MINUS_BETA,
729    >
730{
731    /// Should still be ok in FIPS mode
732    pub fn keygen_from_os_rng() -> Result<(PK, SK), SignatureError> {
733        let mut seed = KeyMaterial256::new();
734        HashDRBG_SHA512::new_from_os().fill_keymaterial_out(&mut seed)?;
735        Self::keygen_internal(&seed)
736    }
737    /// Implements Algorithm 6 of FIPS 204
738    /// Note: NIST has made a special exception in the FIPS 204 FAQ that this _internal function
739    /// may in fact be exposed outside the crypto module.
740    ///
741    /// Unlike other interfaces across the library that take an &impl KeyMaterial, this one
742    /// specifically takes a 32-byte [KeyMaterial256] and checks that it has [KeyType::Seed] and
743    /// [SecurityStrength::_256bit].
744    /// If you happen to have your seed in a larger KeyMaterial, you'll have to copy it using
745    /// [KeyMaterialTrait::from_key]
746    pub(crate) fn keygen_internal(seed: &KeyMaterial256) -> Result<(PK, SK), SignatureError> {
747        if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy)
748            || seed.key_len() != 32
749        {
750            return Err(SignatureError::KeyGenError(
751                "Seed must be 32 bytes and KeyType::Seed or KeyType::BytesFullEntropy.",
752            ));
753        }
754
755        if seed.security_strength() < SecurityStrength::from_bits(LAMBDA as usize) {
756            return Err(SignatureError::KeyGenError(
757                "Seed SecurityStrength must match algorithm security strength",
758            ));
759        }
760
761        // Alg 6 line 1: (rho, rho_prime, K) <- H(πœ‰||IntegerToBytes(π‘˜, 1)||IntegerToBytes(β„“, 1), 128)
762        //   β–· expand seed
763        let mut rho: [u8; 32] = [0u8; 32];
764        let mut K: [u8; 32] = [0u8; 32];
765
766        let (s1_hat, mut s2) = {
767            // scope for h
768            let mut h = H::default();
769            h.absorb(seed.ref_to_bytes());
770            h.absorb(&(k as u8).to_le_bytes());
771            h.absorb(&(l as u8).to_le_bytes());
772            let bytes_written = h.squeeze_out(&mut rho);
773            debug_assert_eq!(bytes_written, 32);
774            let mut rho_prime: [u8; 64] = [0u8; 64];
775            let bytes_written = h.squeeze_out(&mut rho_prime);
776            debug_assert_eq!(bytes_written, 64);
777            let bytes_written = h.squeeze_out(&mut K);
778            debug_assert_eq!(bytes_written, 32);
779
780            // 4: (𝐬1, 𝐬2) ← ExpandS(πœŒβ€²)
781            let (mut s1, s2) = expandS::<k, l, ETA>(&rho_prime);
782
783            s1.ntt();
784            (s1, s2)
785        };
786
787        let t_hat = {
788            // scope for s1_hat
789            // 3: 𝐀_hat ← ExpandA(𝜌) β–· 𝐀 is generated and stored in NTT representation as 𝐀
790            let A_hat = expandA::<k, l>(&rho);
791
792            // 5: 𝐭 ← NTTβˆ’1(𝐀 ∘ NTT(𝐬1)) + 𝐬2
793            //   β–· compute 𝐭 = 𝐀𝐬1 + 𝐬2
794            A_hat.matrix_vector_ntt(&s1_hat)
795        };
796
797        let (t1, mut t0) = {
798            // scope for t
799            let mut t = t_hat;
800            t.inv_ntt();
801            t.add_vector_ntt(&s2);
802            t.conditional_add_q();
803
804            // 6: (𝐭1, 𝐭0) ← Power2Round(𝐭)
805            //   β–· compress 𝐭
806            //   β–· PowerTwoRound is applied componentwise (see explanatory text in Section 7.4)
807            power_2_round_vec::<k>(&t)
808        };
809
810        // 8: π‘π‘˜ ← pkEncode(𝜌, 𝐭1)
811        let pk = PK::new(rho, t1);
812
813        // 9: π‘‘π‘Ÿ ← H(π‘π‘˜, 64)
814        let tr = pk.compute_tr();
815
816        // 10: π‘ π‘˜ ← skEncode(𝜌, 𝐾, π‘‘π‘Ÿ, 𝐬1, 𝐬2, 𝐭0)
817        //   β–· 𝐾 and π‘‘π‘Ÿ are for use in signing
818        // Deviation from the FIPS:
819        //   Hold on to s1, s2, t0 in ntt form
820        //   Note: the result here is not necessarily in reduced form, but since .reduce() is expensive,
821        //   we'll save that for the encode() operation since that's the only place that it matters
822        //   to have them in normalized form.
823        s2.ntt();
824        t0.ntt();
825        // let sk = SK::new(&rho, &K, &tr, &s1_hat, &s2, &t0, Some(seed.clone()));
826        let sk = SK::new(rho, K, tr, s1_hat, s2, t0, Some(seed.clone()));
827
828        // Clear the secret data before returning memory to the OS
829        //   (SK::new() copies all values)
830        rho.fill(0u8);
831        K.fill(0u8);
832        // tr is public data, does not need to be zeroized
833        // s1, s2, t0 are all Vectors of Polynomials, so implement a zeroizing Drop
834
835        // 11: return (π‘π‘˜, π‘ π‘˜)
836        Ok((pk, sk))
837    }
838
839    /// Algorithm 7 ML-DSA.Sign_internal(π‘ π‘˜, 𝑀′, π‘Ÿπ‘›π‘‘)
840    /// modified to take an externally-computed mu instead of M', and to take the public matrix A_hat
841    fn sign_internal(
842        sk: &SK,
843        A_hat: &Matrix<k, l>,
844        mu: &[u8; 64],
845        rnd: [u8; 32],
846        output: &mut [u8; SIG_LEN],
847    ) -> Result<usize, SignatureError> {
848        // 1: (𝜌, 𝐾, π‘‘π‘Ÿ, 𝐬1, 𝐬2, 𝐭0) ← skDecode(π‘ π‘˜)
849        // 2: 𝐬1Μ‚_hat ← NTT(𝐬1)
850        // 3: 𝐬2Μ‚_hat ← NTT(𝐬2)
851        // 4: 𝐭0Μ‚_hat ← NTT(𝐭0)Μ‚
852        // Already done -- the sk struct is already decoded and in NTT form
853
854        // 5: 𝐀_hat ← ExpandA(𝜌)
855        // We're doing an optimization where the user can pre-expand A_hat within the
856        // public key object for faster repeated encapsulations against this public key.
857
858        // 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀 β€², 64)
859        // skip: mu has already been provided
860
861        let mut rho_p_p: [u8; 64] = {
862            // scope for h
863            // 7: πœŒβ€³ ← H(𝐾||π‘Ÿπ‘›π‘‘||πœ‡, 64)
864            let mut h = H::new();
865            h.absorb(sk.K());
866            h.absorb(&rnd);
867            h.absorb(mu);
868            let mut rho_p_p = [0u8; 64];
869            h.squeeze_out(&mut rho_p_p);
870
871            rho_p_p
872        };
873
874        // 8: πœ… ← 0
875        //  β–· initialize counter πœ…
876        let mut kappa: u16 = 0;
877
878        // 9: (𝐳, 𝐑) ← βŠ₯
879        // handled in the loop
880
881        // 10: while (𝐳, 𝐑) = βŠ₯ do
882        //  β–· rejection sampling loop
883
884        // these need to be outside the loop because they form the encoded signature value
885        let mut sig_val_c_tilde = [0u8; LAMBDA_over_4];
886        let mut sig_val_z: Vector<l>;
887        let mut sig_val_h: Vector<k>;
888        loop {
889            // FIPS 204 s. 6.2 allows:
890            //   "Implementations may limit the number of iterations in this loop to not exceed a finite maximum value."
891            if kappa > 1000 * k as u16 {
892                return Err(SignatureError::GenericError(
893                    "Rejection sampling loop exceeded max iterations, try again with a different signing nonce.",
894                ));
895            }
896
897            // 11: 𝐲 ∈ 𝑅^β„“ ← ExpandMask(πœŒβ€³, πœ…)
898            let mut y = expand_mask::<l, GAMMA1, GAMMA1_MASK_LEN>(&rho_p_p, kappa);
899
900            let w = {
901                // scope for y_hat
902                // 12: 𝐰 ← NTTβˆ’1(𝐀_hat * NTT(𝐲))
903                let mut y_hat = y.clone();
904                y_hat.ntt();
905                let mut w = A_hat.matrix_vector_ntt(&y_hat);
906                w.inv_ntt();
907                w.conditional_add_q();
908                w
909            };
910
911            // 13: 𝐰1 ← HighBits(𝐰)
912            //  β–· signer’s commitment
913            let w1 = w.high_bits::<GAMMA2>();
914
915            {
916                // scope for h
917                // 15: 𝑐_tilde ← H(πœ‡||w1Encode(𝐰1), πœ†/4)
918                //  β–· commitment hash
919                let mut hash = H::new();
920                hash.absorb(mu);
921                w1.w1_encode_and_hash::<POLY_W1_PACKED_LEN>(&mut hash);
922                hash.squeeze_out(&mut sig_val_c_tilde);
923            }
924
925            // 16: 𝑐 ∈ π‘…π‘ž ← SampleInBall(c_tilde)
926            //  β–· verifier’s challenge
927            let c_hat = {
928                // scope for c
929                let mut c = sample_in_ball::<LAMBDA_over_4, TAU>(&sig_val_c_tilde);
930
931                // 17: 𝑐_hat ← NTT(𝑐)
932                c.ntt();
933                c
934            };
935
936            // 18: βŸ¨βŸ¨π‘π¬1⟩⟩ ← NTTβˆ’1(𝑐_hat * 𝐬1_hat)
937            //  Note: <<.>> in FIPS 204 means that this value will be used again later, so you should hang on to it.
938            let mut cs1 = sk.s1_hat().scalar_vector_ntt(&c_hat);
939            cs1.inv_ntt();
940
941            // 20: 𝐳 ← 𝐲 + βŸ¨βŸ¨π‘π¬1⟩⟩
942            y.add_vector_ntt(&cs1);
943            sig_val_z = y;
944
945            // 23 (first half): if ||𝐳||∞ β‰₯ 𝛾1 βˆ’ 𝛽 or ||𝐫0||∞ β‰₯ 𝛾2 βˆ’ 𝛽 then (z, h) ← βŠ₯
946            //  β–· validity checks
947            // out-of-order on purpose for performance reasons:
948            //   might as well do the rejection sampling check before any extra heavy computation
949            if sig_val_z.check_norm::<GAMMA1_MINUS_BETA>() {
950                kappa += l as u16;
951                continue;
952            };
953
954            // 19: βŸ¨βŸ¨π‘π¬2⟩⟩ ← NTTβˆ’1(𝑐_hat * 𝐬2Μ‚_hat)
955            let mut cs2 = sk.s2_hat().scalar_vector_ntt(&c_hat);
956            cs2.inv_ntt();
957
958            // 21: 𝐫0 ← LowBits(𝐰 βˆ’ βŸ¨βŸ¨π‘π¬2⟩⟩)
959            let mut r0 = w.sub_vector(&cs2).low_bits::<GAMMA2>();
960
961            // 23 (second half): if ||𝐳||∞ β‰₯ 𝛾1 βˆ’ 𝛽 or ||𝐫0||∞ β‰₯ 𝛾2 βˆ’ 𝛽 then (z, h) ← βŠ₯
962            //  β–· validity checks
963            //  Note: this could be further optimized by using the optimization described in
964            //  https://pq-crystals.org/dilithium/data/dilithium-specification-round3-20210208.pdf section 5.1:
965            //    "instead of computing (r1, r0) = Decomposeq (w βˆ’ cs2, Ξ±)
966            //      and checking whether β€–r0β€–βˆž < Ξ³2 βˆ’ Ξ² and r1 = w1, it is equivalent to just check that
967            //      β€–w0 βˆ’ cs2β€–βˆž < Ξ³2 βˆ’ Ξ², where w0 is the low part of w. If this check passes, w0 βˆ’ cs2
968            //      is the low part of w βˆ’ cs2."
969            if r0.check_norm::<GAMMA2_MINUS_BETA>() {
970                kappa += l as u16;
971                continue;
972            };
973
974            // 25: βŸ¨βŸ¨π‘π­0⟩⟩ ← NTTβˆ’1(𝑐_hat * 𝐭0Μ‚_hat )
975            let mut ct0 = sk.t0_hat().scalar_vector_ntt(&c_hat);
976            ct0.inv_ntt();
977
978            // 28 (first half): if ||βŸ¨βŸ¨π‘π­0⟩⟩||∞ β‰₯ 𝛾2 or the number of 1’s in 𝐑 is greater than πœ”, then (z, h) ← βŠ₯
979            // out-of-order on purpose for performance reasons:
980            //   might as well do the rejection sampling check before any extra heavy computation
981            if ct0.check_norm::<GAMMA2>() {
982                kappa += l as u16;
983                continue;
984            };
985
986            // 26: 𝐑 ← MakeHint(βˆ’βŸ¨βŸ¨π‘π­0⟩⟩, 𝐰 βˆ’ βŸ¨βŸ¨π‘π¬2⟩⟩ + βŸ¨βŸ¨π‘π­0⟩⟩)
987            //  β–· Signer’s hint
988            r0.add_vector_ntt(&ct0);
989            r0.conditional_add_q();
990            let hint_hamming_weight: i32;
991            sig_val_h = {
992                // scope for hint
993                let (hint, inner_hint_hamming_weight) = make_hint_vecs::<k, GAMMA2>(&r0, &w1);
994                hint_hamming_weight = inner_hint_hamming_weight;
995                hint
996            };
997
998            // 28 (second half): if ||βŸ¨βŸ¨π‘π­0⟩⟩||∞ β‰₯ 𝛾2 or the number of 1’s in 𝐑 is greater than πœ”, then (z, h) ← βŠ₯
999            if hint_hamming_weight > OMEGA {
1000                kappa += l as u16;
1001                continue;
1002            };
1003
1004            break;
1005        }
1006
1007        // zeroize rho_p_p before returning it to the OS
1008        rho_p_p.fill(0u8);
1009
1010        // sig_encode does not necessarily write to all bytes of the output, so just to be safe:
1011        output.fill(0u8);
1012
1013        // 33: 𝜎 ← sigEncode(𝑐, 𝐳̃ modΒ±π‘ž, 𝐑)
1014        let bytes_written =
1015            sig_encode::<GAMMA1, k, l, LAMBDA_over_4, OMEGA, POLY_Z_PACKED_LEN, SIG_LEN>(
1016                &sig_val_c_tilde, &sig_val_z, &sig_val_h, output,
1017            );
1018
1019        Ok(bytes_written)
1020    }
1021}
1022
1023impl<
1024    const PK_LEN: usize,
1025    const SK_LEN: usize,
1026    const SIG_LEN: usize,
1027    PK: MLDSAPublicKeyTrait<k, l, PK_LEN> + MLDSAPublicKeyInternalTrait<k, PK_LEN>,
1028    SK: MLDSAPrivateKeyTrait<k, l, ETA, SK_LEN, PK_LEN>
1029        + MLDSAPrivateKeyInternalTrait<k, l, ETA, SK_LEN, PK_LEN>,
1030    const TAU: i32,
1031    const LAMBDA: i32,
1032    const GAMMA1: i32,
1033    const GAMMA2: i32,
1034    const k: usize,
1035    const l: usize,
1036    const ETA: usize,
1037    const BETA: i32,
1038    const OMEGA: i32,
1039    const C_TILDE: usize,
1040    const POLY_Z_PACKED_LEN: usize,
1041    const POLY_W1_PACKED_LEN: usize,
1042    const LAMBDA_over_4: usize,
1043    const GAMMA1_MASK_LEN: usize,
1044    const GAMMA1_MINUS_BETA: i32,
1045    const GAMMA2_MINUS_BETA: i32,
1046> MLDSATrait<PK_LEN, SK_LEN, SIG_LEN, PK, SK, k, l, ETA>
1047    for MLDSA<
1048        PK_LEN,
1049        SK_LEN,
1050        SIG_LEN,
1051        PK,
1052        SK,
1053        TAU,
1054        LAMBDA,
1055        GAMMA1,
1056        GAMMA2,
1057        k,
1058        l,
1059        ETA,
1060        BETA,
1061        OMEGA,
1062        C_TILDE,
1063        POLY_Z_PACKED_LEN,
1064        POLY_W1_PACKED_LEN,
1065        LAMBDA_over_4,
1066        GAMMA1_MASK_LEN,
1067        GAMMA1_MINUS_BETA,
1068        GAMMA2_MINUS_BETA,
1069    >
1070{
1071    fn keygen_from_seed(seed: &KeyMaterial<32>) -> Result<(PK, SK), SignatureError> {
1072        Self::keygen_internal(seed)
1073    }
1074    fn keygen_from_seed_and_encoded(
1075        seed: &KeyMaterial<32>,
1076        encoded_sk: &[u8; SK_LEN],
1077    ) -> Result<(PK, SK), SignatureError> {
1078        let (pk, sk) = Self::keygen_internal(seed)?;
1079
1080        let sk_from_bytes = SK::sk_decode(encoded_sk)?;
1081
1082        // MLDSAPrivateKey impls PartialEq with a constant-time equality check.
1083        if sk != sk_from_bytes {
1084            return Err(SignatureError::KeyGenError("Encoded key does not match generated key"));
1085        }
1086
1087        Ok((pk, sk))
1088    }
1089    fn keypair_consistency_check(pk: &PK, sk: &SK) -> Result<(), SignatureError> {
1090        // This is maybe a computationally heavy way to compare them, but it works
1091        let derived_pk = sk.derive_pk();
1092        if derived_pk.compute_tr() == pk.compute_tr() {
1093            Ok(())
1094        } else {
1095            Err(SignatureError::ConsistencyCheckFailed())
1096        }
1097    }
1098    fn compute_mu_from_tr(
1099        tr: &[u8; 64],
1100        msg: &[u8],
1101        ctx: Option<&[u8]>,
1102    ) -> Result<[u8; 64], SignatureError> {
1103        MuBuilder::compute_mu(tr, msg, ctx)
1104    }
1105    fn compute_mu_from_pk(
1106        pk: &impl MLDSAPublicKeyTrait<k, l, PK_LEN>,
1107        msg: &[u8],
1108        ctx: Option<&[u8]>,
1109    ) -> Result<[u8; 64], SignatureError> {
1110        MuBuilder::compute_mu(&pk.compute_tr(), msg, ctx)
1111    }
1112    fn compute_mu_from_sk(
1113        sk: &impl MLDSAPrivateKeyTrait<k, l, ETA, SK_LEN, PK_LEN>,
1114        msg: &[u8],
1115        ctx: Option<&[u8]>,
1116    ) -> Result<[u8; 64], SignatureError> {
1117        MuBuilder::compute_mu(&sk.tr(), msg, ctx)
1118    }
1119    fn sign_with_expanded_key(
1120        sk: &MLDSAPrivateKeyExpanded<k, l, ETA, PK, SK, SK_LEN, PK_LEN>,
1121        msg: &[u8],
1122        ctx: Option<&[u8]>,
1123    ) -> Result<[u8; SIG_LEN], SignatureError> {
1124        let mu = MuBuilder::compute_mu(&sk.tr(), msg, ctx)?;
1125        Self::sign_mu(&sk.sk, Some(&sk.A_hat), &mu)
1126    }
1127
1128    fn sign_with_expanded_key_out(
1129        sk: &MLDSAPrivateKeyExpanded<k, l, ETA, PK, SK, SK_LEN, PK_LEN>,
1130        msg: &[u8],
1131        ctx: Option<&[u8]>,
1132        out: &mut [u8; SIG_LEN],
1133    ) -> Result<usize, SignatureError> {
1134        let mu = MuBuilder::compute_mu(&sk.tr(), msg, ctx)?;
1135        Self::sign_mu_out(&sk.sk, Some(&sk.A_hat), &mu, out)
1136    }
1137
1138    fn sign_mu(
1139        sk: &SK,
1140        A_hat: Option<&Matrix<k, l>>,
1141        mu: &[u8; 64],
1142    ) -> Result<[u8; SIG_LEN], SignatureError> {
1143        let mut out: [u8; SIG_LEN] = [0u8; SIG_LEN];
1144        Self::sign_mu_out(sk, A_hat, mu, &mut out)?;
1145
1146        Ok(out)
1147    }
1148    fn sign_mu_out(
1149        sk: &SK,
1150        A_hat: Option<&Matrix<k, l>>,
1151        mu: &[u8; 64],
1152        output: &mut [u8; SIG_LEN],
1153    ) -> Result<usize, SignatureError> {
1154        let mut rnd: [u8; MLDSA_RND_LEN] = [0u8; MLDSA_RND_LEN];
1155        HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?;
1156
1157        Self::sign_mu_deterministic_out(sk, A_hat, mu, rnd, output)
1158    }
1159    fn sign_mu_with_expanded_key(
1160        sk: &MLDSAPrivateKeyExpanded<k, l, ETA, PK, SK, SK_LEN, PK_LEN>,
1161        A_hat: Option<&Matrix<k, l>>,
1162        mu: &[u8; 64],
1163    ) -> Result<[u8; SIG_LEN], SignatureError> {
1164        let mut out: [u8; SIG_LEN] = [0u8; SIG_LEN];
1165        Self::sign_mu_with_expanded_key_out(sk, A_hat, mu, &mut out)?;
1166
1167        Ok(out)
1168    }
1169    fn sign_mu_with_expanded_key_out(
1170        sk: &MLDSAPrivateKeyExpanded<k, l, ETA, PK, SK, SK_LEN, PK_LEN>,
1171        A_hat: Option<&Matrix<k, l>>,
1172        mu: &[u8; 64],
1173        out: &mut [u8; SIG_LEN],
1174    ) -> Result<usize, SignatureError> {
1175        Self::sign_mu_out(&sk.sk, A_hat, mu, out)
1176    }
1177
1178    fn sign_mu_deterministic(
1179        sk: &SK,
1180        A_hat: Option<&Matrix<k, l>>,
1181        mu: &[u8; 64],
1182        rnd: [u8; 32],
1183    ) -> Result<[u8; SIG_LEN], SignatureError> {
1184        let mut out: [u8; SIG_LEN] = [0u8; SIG_LEN];
1185        Self::sign_mu_deterministic_out(sk, A_hat, mu, rnd, &mut out)?;
1186
1187        Ok(out)
1188    }
1189    fn sign_mu_deterministic_out(
1190        sk: &SK,
1191        A_hat: Option<&Matrix<k, l>>,
1192        mu: &[u8; 64],
1193        rnd: [u8; 32],
1194        output: &mut [u8; SIG_LEN],
1195    ) -> Result<usize, SignatureError> {
1196        match A_hat {
1197            Some(A_hat) => Self::sign_internal(sk, A_hat, mu, rnd, output),
1198            None => Self::sign_internal(sk, &sk.A_hat(), mu, rnd, output),
1199        }
1200    }
1201    fn sign_mu_deterministic_from_seed(
1202        seed: &KeyMaterial<32>,
1203        mu: &[u8; 64],
1204        rnd: [u8; 32],
1205    ) -> Result<[u8; SIG_LEN], SignatureError> {
1206        let mut out: [u8; SIG_LEN] = [0u8; SIG_LEN];
1207        Self::sign_mu_deterministic_from_seed_out(seed, mu, rnd, &mut out)?;
1208        Ok(out)
1209    }
1210    /// This function is a mash-up of keyGen (Algorithm 6) and sign (Algorithm 7)
1211    /// Although, while this algorithm is a precursor to the lowmemory implementation, I'm not
1212    /// sure that it actually gains you anything over a keygen_from_seed() followed by a sign(),
1213    /// and maybe I should change its implementation to that.
1214    fn sign_mu_deterministic_from_seed_out(
1215        seed: &KeyMaterial<32>,
1216        mu: &[u8; 64],
1217        rnd: [u8; 32],
1218        output: &mut [u8; SIG_LEN],
1219    ) -> Result<usize, SignatureError> {
1220        // This has been kept as clean as possible for correspondence with the FIPS,
1221        // but things have been moved around so that unnamed scopes can be used to limit how many
1222        // stack variables are alive at the same time.
1223
1224        // 1: (𝜌, 𝐾, π‘‘π‘Ÿ, 𝐬1, 𝐬2, 𝐭0) ← skDecode(π‘ π‘˜)
1225        // to avoid having all of it in memory at the same time,
1226        // we're gonna derive what we need as we need it.
1227
1228        if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy)
1229            || seed.key_len() != 32
1230        {
1231            return Err(SignatureError::KeyGenError(
1232                "Seed must be 32 bytes and KeyType::Seed or KeyType::BytesFullEntropy.",
1233            ));
1234        }
1235
1236        if seed.security_strength() < SecurityStrength::from_bits(LAMBDA as usize) {
1237            return Err(SignatureError::KeyGenError(
1238                "Seed SecurityStrength must match algorithm security strength: 128-bit (ML-DSA-44), 192-bit (ML-DSA-65), or 256-bit (ML-DSA-87).",
1239            ));
1240        }
1241
1242        // Alg 7; 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀 β€², 64)
1243        // skip: mu has already been provided
1244
1245        let (rho, mut rho_p_p, s1, s2) = {
1246            // scope for h
1247            // derive sk.K
1248            // Alg 6; 1: (rho, rho_prime, K) <- H(πœ‰||IntegerToBytes(π‘˜, 1)||IntegerToBytes(β„“, 1), 128)
1249            //   β–· expand seed
1250            let (rho, rho_prime, K) = {
1251                let mut h = H::default();
1252                h.absorb(seed.ref_to_bytes());
1253                h.absorb(&(k as u8).to_le_bytes());
1254                h.absorb(&(l as u8).to_le_bytes());
1255                let mut rho = [0u8; 32];
1256                let bytes_written = h.squeeze_out(&mut rho);
1257                debug_assert_eq!(bytes_written, 32);
1258                let mut rho_prime = [0u8; 64];
1259                let bytes_written = h.squeeze_out(&mut rho_prime);
1260                debug_assert_eq!(bytes_written, 64);
1261                let mut K: [u8; 32] = [0u8; 32];
1262                let bytes_written = h.squeeze_out(&mut K);
1263                debug_assert_eq!(bytes_written, 32);
1264
1265                (rho, rho_prime, K)
1266            };
1267
1268            // Alg 7; 7: πœŒβ€³ ← H(𝐾||π‘Ÿπ‘›π‘‘||πœ‡, 64)
1269            let rho_p_p = {
1270                let mut h = H::new();
1271                h.absorb(&K);
1272                h.absorb(&rnd);
1273                h.absorb(mu);
1274                let mut rho_p_p = [0u8; 64];
1275                h.squeeze_out(&mut rho_p_p);
1276
1277                rho_p_p
1278            };
1279
1280            // 4: (𝐬1, 𝐬2) ← ExpandS(πœŒβ€²)
1281            let (s1, s2) = expandS::<k, l, ETA>(&rho_prime);
1282
1283            (rho, rho_p_p, s1, s2)
1284        };
1285
1286        // Alg 7; 5: 𝐀_hat ← ExpandA(𝜌)
1287        // Note on memory optimization:
1288        // A_hat consumes a large bit of memory and technically could move inside the loop --
1289        // -- or even more aggressively, could be derived and multiplied by y_hat row-by-row --
1290        // But in my unit tests, I see the loop typically execute 1 - 3 times, sometimes as many
1291        // as 20 or even 80 times. So moving expandA() inside the loop would be a pretty drastic speed-for-memory tradeoff
1292        // that I'm not willing to make in general, so I leave that as an optimization that people
1293        // can make on a private fork if you really really need the memory squeeze.
1294        let A_hat = expandA::<k, l>(&rho);
1295
1296        // Alg 7; 8: πœ… ← 0
1297        //  β–· initialize counter πœ…
1298        let mut kappa: u16 = 0;
1299
1300        // Alg 7; 9: (𝐳, 𝐑) ← βŠ₯
1301        // handled in the loop
1302
1303        // Alg 7; 10: while (𝐳, 𝐑) = βŠ₯ do
1304        //  β–· rejection sampling loop
1305
1306        // these need to be outside the loop because they form the encoded signature value
1307        let mut sig_val_c_tilde = [0u8; LAMBDA_over_4];
1308        let mut sig_val_z: Vector<l>;
1309        let mut sig_val_h: Vector<k>;
1310        loop {
1311            // FIPS 204 s. 6.2 allows:
1312            //   "Implementations may limit the number of iterations in this loop to not exceed a finite maximum value."
1313            if kappa > 1000 * k as u16 {
1314                return Err(SignatureError::GenericError(
1315                    "Rejection sampling loop exceeded max iterations, try again with a different signing nonce.",
1316                ));
1317            }
1318
1319            // Alg 7; 11: 𝐲 ∈ 𝑅^β„“ ← ExpandMask(πœŒβ€³, πœ…)
1320            let mut y = expand_mask::<l, GAMMA1, GAMMA1_MASK_LEN>(&rho_p_p, kappa);
1321
1322            let w = {
1323                // scope for y_hat
1324                // Alg 7; 12: 𝐰 ← NTTβˆ’1(𝐀_hat * NTT(𝐲))
1325                let mut y_hat = y.clone();
1326                y_hat.ntt();
1327                let mut w = A_hat.matrix_vector_ntt(&y_hat);
1328                w.inv_ntt();
1329                w.conditional_add_q();
1330                w
1331            };
1332
1333            // Alg 7; 13: 𝐰1 ← HighBits(𝐰)
1334            //  β–· signer’s commitment
1335            let w1 = w.high_bits::<GAMMA2>();
1336
1337            {
1338                // scope for h
1339                // 15: 𝑐_tilde ← H(πœ‡||w1Encode(𝐰1), πœ†/4)
1340                //  β–· commitment hash
1341                let mut hash = H::new();
1342                hash.absorb(mu);
1343                w1.w1_encode_and_hash::<POLY_W1_PACKED_LEN>(&mut hash);
1344                hash.squeeze_out(&mut sig_val_c_tilde);
1345            }
1346
1347            // Alg 7; 16: 𝑐 ∈ π‘…π‘ž ← SampleInBall(c_tilde)
1348            //  β–· verifier’s challenge
1349            let c_hat = {
1350                // scope for c
1351                let mut c = sample_in_ball::<LAMBDA_over_4, TAU>(&sig_val_c_tilde);
1352
1353                // 17: 𝑐_hat ← NTT(𝑐)
1354                c.ntt();
1355                c
1356            };
1357
1358            let t_hat: Vector<k>;
1359            sig_val_z = {
1360                // scope for s1_hat, cs1
1361                // Alg 7; 2: 𝐬1Μ‚_hat ← NTT(𝐬1)
1362                let mut s1_hat = s1.clone();
1363                s1_hat.ntt();
1364
1365                y = {
1366                    // scope for cs1
1367                    // Alg 7; 18: βŸ¨βŸ¨π‘π¬1⟩⟩ ← NTTβˆ’1(𝑐_hat * 𝐬1_hat)
1368                    //  Note: <<.>> in FIPS 204 means that this value will be used again later, so you should hang on to it.
1369                    let mut cs1 = s1_hat.scalar_vector_ntt(&c_hat);
1370                    cs1.inv_ntt();
1371
1372                    // Alg 7; 20: 𝐳 ← 𝐲 + βŸ¨βŸ¨π‘π¬1⟩⟩
1373                    y.add_vector_ntt(&cs1);
1374                    y
1375                };
1376
1377                // also, while we have s1_hat in memory, compute t_hat
1378                // Alg 6; 5: 𝐭 ← NTTβˆ’1(𝐀 ∘ NTT(𝐬1)) + 𝐬2
1379                //   β–· compute 𝐭 = 𝐀𝐬1 + 𝐬2
1380                t_hat = A_hat.matrix_vector_ntt(&s1_hat);
1381
1382                y
1383            };
1384
1385            // Alg 7; 23 (first half): if ||𝐳||∞ β‰₯ 𝛾1 βˆ’ 𝛽 or ||𝐫0||∞ β‰₯ 𝛾2 βˆ’ 𝛽 then (z, h) ← βŠ₯
1386            //  β–· validity checks
1387            // out-of-order on purpose for performance reasons:
1388            //   might as well do the rejection sampling check before any extra heavy computation
1389            if sig_val_z.check_norm::<GAMMA1_MINUS_BETA>() {
1390                kappa += l as u16;
1391                continue;
1392            };
1393
1394            let t0: Vector<k>;
1395            let mut r0: Vector<k> = {
1396                // scope for s2_hat and cs2
1397                // 3: 𝐬2Μ‚_hat ← NTT(𝐬2)
1398                let mut s2_hat = s2.clone();
1399                s2_hat.ntt();
1400
1401                // 19: βŸ¨βŸ¨π‘π¬2⟩⟩ ← NTTβˆ’1(𝑐_hat * 𝐬2Μ‚_hat)
1402                let mut cs2 = s2_hat.scalar_vector_ntt(&c_hat);
1403                cs2.inv_ntt();
1404
1405                // 21: 𝐫0 ← LowBits(𝐰 βˆ’ βŸ¨βŸ¨π‘π¬2⟩⟩)
1406                let r0 = w.sub_vector(&cs2).low_bits::<GAMMA2>();
1407
1408                // while we have s2_hat in scope, derive t0
1409                let mut t = t_hat;
1410                t.inv_ntt();
1411                t.add_vector_ntt(&s2);
1412                t.conditional_add_q();
1413
1414                // 6: (𝐭1, 𝐭0) ← Power2Round(𝐭)
1415                //   β–· compress 𝐭
1416                //   β–· PowerTwoRound is applied componentwise (see explanatory text in Section 7.4)
1417                let (_t1tmp, t0tmp) = power_2_round_vec::<k>(&t);
1418                t0 = t0tmp;
1419
1420                r0
1421            };
1422
1423            // Alg 7; 23 (second half): if ||𝐳||∞ β‰₯ 𝛾1 βˆ’ 𝛽 or ||𝐫0||∞ β‰₯ 𝛾2 βˆ’ 𝛽 then (z, h) ← βŠ₯
1424            //  β–· validity checks
1425            if r0.check_norm::<GAMMA2_MINUS_BETA>() {
1426                // mutants note: mutants thinks you can replace this with -=, but in practice that makes
1427                //               the rejection sampling loop go forever, so is a false positive.
1428                kappa += l as u16;
1429                continue;
1430            };
1431
1432            let ct0: Vector<k> = {
1433                // scope for t0_hat
1434                // 4: 𝐭0Μ‚_hat ← NTT(𝐭0)Μ‚
1435                let mut t0_hat = t0.clone();
1436                t0_hat.ntt();
1437
1438                // 25: βŸ¨βŸ¨π‘π­0⟩⟩ ← NTTβˆ’1(𝑐_hat * 𝐭0Μ‚_hat )
1439                let mut ct0 = t0_hat.scalar_vector_ntt(&c_hat);
1440                ct0.inv_ntt();
1441                ct0
1442            };
1443
1444            // Alg 7; 28 (first half): if ||βŸ¨βŸ¨π‘π­0⟩⟩||∞ β‰₯ 𝛾2 or the number of 1’s in 𝐑 is greater than πœ”, then (z, h) ← βŠ₯
1445            // out-of-order on purpose for performance reasons:
1446            //   might as well do the rejection sampling check before any extra heavy computation
1447            if ct0.check_norm::<GAMMA2>() {
1448                kappa += l as u16;
1449                continue;
1450            };
1451
1452            // Alg 7; 26: 𝐑 ← MakeHint(βˆ’βŸ¨βŸ¨π‘π­0⟩⟩, 𝐰 βˆ’ βŸ¨βŸ¨π‘π¬2⟩⟩ + βŸ¨βŸ¨π‘π­0⟩⟩)
1453            //  β–· Signer’s hint
1454            r0.add_vector_ntt(&ct0);
1455            r0.conditional_add_q();
1456            let hint_hamming_weight: i32;
1457            sig_val_h = {
1458                // scope for hint
1459                let (hint, inner_hint_hamming_weight) = make_hint_vecs::<k, GAMMA2>(&r0, &w1);
1460                hint_hamming_weight = inner_hint_hamming_weight;
1461                hint
1462            };
1463
1464            // Alg 7; 28 (second half): if ||βŸ¨βŸ¨π‘π­0⟩⟩||∞ β‰₯ 𝛾2 or the number of 1’s in 𝐑 is greater than πœ”, then (z, h) ← βŠ₯
1465            if hint_hamming_weight > OMEGA {
1466                kappa += l as u16;
1467                continue;
1468            };
1469
1470            break;
1471        }
1472
1473        // zeroize rho_p_p before returning it to the OS
1474        rho_p_p.fill(0u8);
1475
1476        // sig_encode does not necessarily write to all bytes of the output, so just to be safe:
1477        output.fill(0u8);
1478
1479        // Alg 7; 33: 𝜎 ← sigEncode(𝑐, 𝐳̃ modΒ±π‘ž, 𝐑)
1480        let bytes_written =
1481            sig_encode::<GAMMA1, k, l, LAMBDA_over_4, OMEGA, POLY_Z_PACKED_LEN, SIG_LEN>(
1482                &sig_val_c_tilde, &sig_val_z, &sig_val_h, output,
1483            );
1484
1485        Ok(bytes_written)
1486    }
1487    fn set_signer_rnd(&mut self, rnd: [u8; 32]) {
1488        self.signer_rnd = Some(rnd);
1489    }
1490    fn sign_init_from_seed(
1491        seed: &KeyMaterial<32>,
1492        ctx: Option<&[u8]>,
1493    ) -> Result<Self, SignatureError> {
1494        let (_pk, sk) = Self::keygen_from_seed(seed)?;
1495        Ok(Self {
1496            _phantom: PhantomData,
1497            mu_builder: MuBuilder::do_init(&sk.tr(), ctx)?,
1498            signer_rnd: None,
1499            sk: None,
1500            seed: Some(seed.clone()),
1501            pk: None,
1502        })
1503    }
1504
1505    fn verify_with_expanded_key(
1506        pk: &MLDSAPublicKeyExpanded<k, l, PK, PK_LEN>,
1507        msg: &[u8],
1508        ctx: Option<&[u8]>,
1509        sig: &[u8],
1510    ) -> Result<(), SignatureError> {
1511        let mu = MuBuilder::compute_mu(&pk.compute_tr(), msg, ctx)?;
1512
1513        if sig.len() != SIG_LEN {
1514            return Err(SignatureError::LengthError("Signature value is not the correct length."));
1515        }
1516        if Self::verify_mu_internal(&pk.pk, &pk.A_hat(), &mu, &sig[..SIG_LEN].try_into().unwrap()) {
1517            Ok(())
1518        } else {
1519            Err(SignatureError::SignatureVerificationFailed)
1520        }
1521    }
1522
1523    /// Algorithm 8 ML-DSA.Verify_internal(π‘π‘˜, 𝑀′, 𝜎)
1524    /// Internal function to verify a signature 𝜎 for a formatted message 𝑀′ .
1525    /// Input: Public key π‘π‘˜ ∈ 𝔹32+32π‘˜(bitlen (π‘žβˆ’1)βˆ’π‘‘) and message 𝑀′ ∈ {0, 1}βˆ— .
1526    /// Input: Signature 𝜎 ∈ π”Ήπœ†/4+β„“β‹…32β‹…(1+bitlen (𝛾1βˆ’1))+πœ”+π‘˜.
1527    fn verify_mu_internal(
1528        pk: &PK,
1529        A_hat: &Matrix<k, l>,
1530        mu: &[u8; 64],
1531        sig: &[u8; SIG_LEN],
1532    ) -> bool {
1533        // 1: (𝜌, 𝐭1) ← pkDecode(π‘π‘˜)
1534        // Already done -- the pk struct is already decoded
1535
1536        // 2: (𝑐_tilde, 𝐳, 𝐑) ← sigDecode(𝜎)
1537        //  β–· signer’s commitment hash c_tilde, response 𝐳, and hint 𝐑
1538        // 3: if 𝐑 = βŠ₯ then return false
1539        let (c_tilde, z, h) = match sig_decode::<
1540            GAMMA1,
1541            k,
1542            l,
1543            LAMBDA_over_4,
1544            OMEGA,
1545            POLY_Z_PACKED_LEN,
1546            SIG_LEN,
1547        >(&sig)
1548        {
1549            Ok((c_tilde, z, h)) => (c_tilde, z, h),
1550            Err(_) => return false,
1551        };
1552
1553        // 13 (first half) return [[ ||𝐳||∞ < 𝛾1 βˆ’ 𝛽]]
1554        if z.check_norm::<GAMMA1_MINUS_BETA>() {
1555            return false;
1556        }
1557
1558        // 5: 𝐀 ← ExpandA(𝜌)
1559        //   β–· 𝐀 is generated and stored in NTT representation as 𝐀
1560        // We're doing an optimization where the user can pre-expand A_hat within the
1561        // public key object for faster repeated encapsulations against this public key.
1562
1563        // 6: π‘‘π‘Ÿ ← H(π‘π‘˜, 64)
1564        // 7: πœ‡ ← (H(BytesToBits(π‘‘π‘Ÿ)||𝑀 β€², 64))
1565        //   β–· message representative that may optionally be
1566        //     computed in a different cryptographic module
1567        // skip because this function is being handed mu
1568
1569        // 8: 𝑐 ∈ π‘…π‘ž ← SampleInBall(c_tilde)
1570        let c_hat = {
1571            let mut c = sample_in_ball::<LAMBDA_over_4, TAU>(&c_tilde);
1572            c.ntt();
1573
1574            c
1575        };
1576
1577        // 9: 𝐰′_approx ← NTTβˆ’1(𝐀_hat ∘ NTT(𝐳) βˆ’ NTT(𝑐) ∘ NTT(𝐭1 β‹… 2^𝑑))
1578        //   broken out for clarity:
1579        //   NTTβˆ’1(
1580        //      𝐀_hat ∘ NTT(𝐳) βˆ’
1581        //                  NTT(𝑐) ∘ NTT(𝐭1 β‹… 2^𝑑)
1582        //   )
1583        // β–· 𝐰'_approx = 𝐀𝐳 βˆ’ 𝑐𝐭1 β‹… 2^𝑑
1584        // weird nested scoping is to reduce peak stack memory usage
1585        let w1p = {
1586            let Az = {
1587                let mut z_hat = z.clone();
1588                z_hat.ntt();
1589                A_hat.matrix_vector_ntt(&z_hat)
1590            };
1591            let ct1 = {
1592                // potential optimization -- pre-compute this on key load?
1593                let mut t1_shift_hat = pk.t1().shift_left::<d>();
1594                t1_shift_hat.ntt();
1595                t1_shift_hat.scalar_vector_ntt(&c_hat)
1596            };
1597            let mut wp_approx = Az.sub_vector(&ct1);
1598            wp_approx.inv_ntt();
1599            wp_approx.conditional_add_q();
1600
1601            // 10: 𝐰1β€² ← UseHint(𝐑, 𝐰'_approx)
1602            // β–· reconstruction of signer’s commitment
1603            use_hint_vecs::<k, GAMMA2>(&h, &wp_approx)
1604        };
1605        // 12: 𝑐_tilde_p ← H(πœ‡||w1Encode(𝐰1'), πœ†/4)
1606        // β–· hash it; this should match 𝑐_tilde
1607        let c_tilde_p = {
1608            let mut c_tilde_p = [0u8; LAMBDA_over_4];
1609            let mut hash = H::new();
1610            hash.absorb(mu);
1611            w1p.w1_encode_and_hash::<POLY_W1_PACKED_LEN>(&mut hash);
1612            hash.squeeze_out(&mut c_tilde_p);
1613
1614            c_tilde_p
1615        };
1616
1617        // verification probably doesn't technically need to be constant-time, but why not?
1618        // 13 (second half): return [[ ||𝐳||∞ < 𝛾1 βˆ’ 𝛽]] and [[𝑐 Μƒ = 𝑐′ ]]
1619        bouncycastle_utils::ct::ct_eq_bytes(&c_tilde, &c_tilde_p)
1620    }
1621}
1622
1623/// Trait for all three of the ML-DSA algorithm variants.
1624pub trait MLDSATrait<
1625    const PK_LEN: usize,
1626    const SK_LEN: usize,
1627    const SIG_LEN: usize,
1628    PK: MLDSAPublicKeyTrait<k, l, PK_LEN> + MLDSAPublicKeyInternalTrait<k, PK_LEN>,
1629    SK: MLDSAPrivateKeyTrait<k, l, ETA, SK_LEN, PK_LEN>
1630        + MLDSAPrivateKeyInternalTrait<k, l, ETA, SK_LEN, PK_LEN>,
1631    const k: usize,
1632    const l: usize,
1633    const ETA: usize,
1634>: Sized
1635{
1636    /// Imports a secret key from a seed.
1637    fn keygen_from_seed(seed: &KeyMaterial<32>) -> Result<(PK, SK), SignatureError>;
1638    /// Imports a secret key from both a seed and an encoded_sk.
1639    ///
1640    /// This is a convenience function to expand the key from seed and compare it against
1641    /// the provided `encoded_sk` using a constant-time equality check.
1642    /// If everything checks out, the secret key is returned fully populated with pk and seed.
1643    /// If the provided key and derived key don't match, an error is returned.
1644    fn keygen_from_seed_and_encoded(
1645        seed: &KeyMaterial<32>,
1646        encoded_sk: &[u8; SK_LEN],
1647    ) -> Result<(PK, SK), SignatureError>;
1648    /// Given a public key and a secret key, check that the public key matches the secret key.
1649    /// This is a sanity check that the public key was generated correctly from the secret key.
1650    ///
1651    /// At the current time, this is only possible if `sk` either contains a public key (in which case
1652    /// the two pk's are encoded and compared for byte equality), or if `sk` contains a seed
1653    /// (in which case a keygen_from_seed is run and then the pk's compared).
1654    ///
1655    /// Returns either `()` or [SignatureError::ConsistencyCheckFailed].
1656    fn keypair_consistency_check(pk: &PK, sk: &SK) -> Result<(), SignatureError>;
1657    /// This provides the first half of the "External Mu" interface to ML-DSA which is described
1658    /// in, and allowed under, NIST's FAQ that accompanies FIPS 204.
1659    ///
1660    /// This function, together with [MLDSATrait::sign_mu] perform a complete ML-DSA signature which is indistinguishable
1661    /// from one produced by the one-shot sign APIs.
1662    ///
1663    /// The utility of this function is exactly as described
1664    /// on Line 6 of Algorithm 7 of FIPS 204:
1665    ///
1666    ///    message representative that may optionally be computed in a different cryptographic module
1667    ///
1668    /// The utility is when an extremely large message needs to be signed, where the message exists on one
1669    /// computing system and the private key to sign it is held on another and either the transfer time or bandwidth
1670    /// causes operational concerns (this is common for example with network HSMs or sending large messages
1671    /// to be signed by a smartcard communicating over near-field radio). Another use case is if the
1672    /// contents of the message are sensitive and the signer does not want to transmit the message itself
1673    /// for fear of leaking it via proxy logging and instead would prefer to only transmit a hash of it.
1674    ///
1675    /// Since "External Mu" mode is well-defined by FIPS 204 and allowed by NIST, the mu value produced here
1676    /// can be used with many hardware crypto modules.
1677    ///
1678    /// This "External Mu" mode of ML-DSA provides an alternative to the HashML-DSA algorithm in that it
1679    /// allows the message to be externally pre-hashed, however, unlike HashML-DSA, this is merely an optimization
1680    /// between the application holding the to-be-signed message and the cryptographic module holding the private key
1681    /// -- in particular, while HashML-DSA requires the verifier to know whether ML-DSA or HashML-DSA was used to sign
1682    /// the message, both "direct" ML-DSA and "External Mu" signatures can be verified with a standard
1683    /// ML-DSA verifier.
1684    ///
1685    /// This function requires the public key hash `tr`, which can be computed from the public key
1686    /// using [MLDSAPublicKeyTrait::compute_tr].
1687    ///
1688    /// For a streaming version of this, see [MuBuilder].
1689    fn compute_mu_from_tr(
1690        tr: &[u8; 64],
1691        msg: &[u8],
1692        ctx: Option<&[u8]>,
1693    ) -> Result<[u8; 64], SignatureError>;
1694    /// Same as [MLDSATrait::compute_mu_from_tr], but extracts tr from the public key.
1695    fn compute_mu_from_pk(
1696        pk: &impl MLDSAPublicKeyTrait<k, l, PK_LEN>,
1697        msg: &[u8],
1698        ctx: Option<&[u8]>,
1699    ) -> Result<[u8; 64], SignatureError>;
1700    /// Same as [MLDSATrait::compute_mu_from_tr], but extracts tr from the private key.
1701    // dev note: defined sk this way so that it accepts either MLDSAPrivateKey or MLDSAPRivateKeyExpanded
1702    fn compute_mu_from_sk(
1703        sk: &impl MLDSAPrivateKeyTrait<k, l, ETA, SK_LEN, PK_LEN>,
1704        msg: &[u8],
1705        ctx: Option<&[u8]>,
1706    ) -> Result<[u8; 64], SignatureError>;
1707    /// Same as [Signature::sign], but signs from an [MLDSAPrivateKeyExpanded].
1708    fn sign_with_expanded_key(
1709        sk: &MLDSAPrivateKeyExpanded<k, l, ETA, PK, SK, SK_LEN, PK_LEN>,
1710        msg: &[u8],
1711        ctx: Option<&[u8]>,
1712    ) -> Result<[u8; SIG_LEN], SignatureError>;
1713    /// Same as [MLDSATrait::sign_with_expanded_key], but takes an output array.
1714    fn sign_with_expanded_key_out(
1715        sk: &MLDSAPrivateKeyExpanded<k, l, ETA, PK, SK, SK_LEN, PK_LEN>,
1716        msg: &[u8],
1717        ctx: Option<&[u8]>,
1718        out: &mut [u8; SIG_LEN],
1719    ) -> Result<usize, SignatureError>;
1720    /// Performs an ML-DSA signature using the provided external message representative `mu`.
1721    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
1722    /// FIPS 204 itself, as well as subsequent FAQ documents.
1723    /// This mode uses randomized signing (called "hedged mode" in FIPS 204) using an internal RNG.
1724    fn sign_mu(
1725        sk: &SK,
1726        A_hat: Option<&Matrix<k, l>>,
1727        mu: &[u8; 64],
1728    ) -> Result<[u8; SIG_LEN], SignatureError>;
1729    /// Performs an ML-DSA signature using the provided external message representative `mu`.
1730    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
1731    /// FIPS 204 itself, as well as subsequent FAQ documents.
1732    /// This mode uses randomized signing (called "hedged mode" in FIPS 204) using an internal RNG.
1733    ///
1734    /// Optionally takes the public matrix A_hat which can be extracted from either the public key or private
1735    /// key object -- although the more ergonomic way to use this functionality is via the
1736    /// [MLDSAPublicKeyExpanded] object.
1737    ///
1738    /// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer.
1739    fn sign_mu_out(
1740        sk: &SK,
1741        A_hat: Option<&Matrix<k, l>>,
1742        mu: &[u8; 64],
1743        output: &mut [u8; SIG_LEN],
1744    ) -> Result<usize, SignatureError>;
1745    /// Same as [Signature::sign_mu], but signs from an [MLDSAPrivateKeyExpanded].
1746    fn sign_mu_with_expanded_key(
1747        sk: &MLDSAPrivateKeyExpanded<k, l, ETA, PK, SK, SK_LEN, PK_LEN>,
1748        A_hat: Option<&Matrix<k, l>>,
1749        mu: &[u8; 64],
1750    ) -> Result<[u8; SIG_LEN], SignatureError>;
1751    /// Same as [Signature::sign_mu_out], but signs from an [MLDSAPrivateKeyExpanded].
1752    fn sign_mu_with_expanded_key_out(
1753        sk: &MLDSAPrivateKeyExpanded<k, l, ETA, PK, SK, SK_LEN, PK_LEN>,
1754        A_hat: Option<&Matrix<k, l>>,
1755        mu: &[u8; 64],
1756        output: &mut [u8; SIG_LEN],
1757    ) -> Result<usize, SignatureError>;
1758    /// Algorithm 7 ML-DSA.Sign_internal(π‘ π‘˜, 𝑀′, π‘Ÿπ‘›π‘‘)
1759    /// (modified to take an externally-computed mu instead of M')
1760    ///
1761    /// Performs an ML-DSA signature using the provided external message representative `mu`.
1762    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
1763    /// FIPS 204 itself, as well as subsequent FAQ documents.
1764    ///
1765    /// Optionally takes the public matrix A_hat which can be extracted from either the public key or private
1766    /// key object -- although the more ergonomic way to use this functionality is via the
1767    /// [MLDSAPublicKeyExpanded] object.
1768    ///
1769    /// Security note about deterministic mode:
1770    /// This mode exposes deterministic signing (called "hedged mode" and allowed by FIPS 204).
1771    /// The ML-DSA algorithm is considered safe to use in deterministic mode, but be aware that
1772    /// the responsibility is on you to ensure that your nonce `rnd` is unique per signature.
1773    /// If not, you may lose some privacy properties; for example it becomes easy to tell if a signer
1774    /// has signed the same message twice or two different messagase, or to tell if the same message
1775    /// has been signed by the same signer twice or two different signers.
1776    ///
1777    /// Since `rnd` should be either a per-signature nonce, or a fixed value, therefore, to help
1778    /// prevent accidental nonce reuse, this function moves `rnd`.
1779    fn sign_mu_deterministic(
1780        sk: &SK,
1781        A_hat: Option<&Matrix<k, l>>,
1782        mu: &[u8; 64],
1783        rnd: [u8; 32],
1784    ) -> Result<[u8; SIG_LEN], SignatureError>;
1785    /// Algorithm 7 ML-DSA.Sign_internal(π‘ π‘˜, 𝑀′, π‘Ÿπ‘›π‘‘)
1786    /// (modified to take an externally-computed mu instead of M')
1787    ///
1788    /// Performs an ML-DSA signature using the provided external message representative `mu`.
1789    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
1790    /// FIPS 204 itself, as well as subsequent FAQ documents.
1791    /// This mode exposes deterministic signing (called "hedged mode" in FIPS 204) using an internal RNG.
1792    ///
1793    /// This mode exposes the signing nonce `rnd` either for users who wish to source the signing
1794    /// nonce from a source other than the library's default internal RNG, or who wish to use the
1795    /// "deterministic mode" defined in FIPS 204 by providing `rnd = [0u8; 32]`.
1796    /// In order to help prevent against accidental nonce reuse, this function moves `rnd` instead
1797    /// of taking it by reference.
1798    ///
1799    /// Security note about deterministic mode:
1800    /// This mode exposes deterministic signing (called "hedged mode" and allowed by FIPS 204).
1801    /// The ML-DSA algorithm is considered safe to use in deterministic mode, but be aware that
1802    /// the responsibility is on you to ensure that your nonce `rnd` is unique per signature.
1803    /// If not, you may lose some privacy properties; for example, it becomes easy to tell if a signer
1804    /// has signed the same message twice or two different messages, or to tell if the same message
1805    /// has been signed by the same signer twice or two different signers.
1806    ///
1807    /// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer.
1808    fn sign_mu_deterministic_out(
1809        sk: &SK,
1810        A_hat: Option<&Matrix<k, l>>,
1811        mu: &[u8; 64],
1812        rnd: [u8; 32],
1813        output: &mut [u8; SIG_LEN],
1814    ) -> Result<usize, SignatureError>;
1815    /// This contains a heavily-optimized combined keygen() and sign() which greatly reduces peak
1816    /// memory usage by never having the full secret key in memory at the same time,
1817    /// and by deriving intermediate values piece-wise as needed.
1818    fn sign_mu_deterministic_from_seed(
1819        seed: &KeyMaterial<32>,
1820        mu: &[u8; 64],
1821        rnd: [u8; 32],
1822    ) -> Result<[u8; SIG_LEN], SignatureError>;
1823    /// This contains a heavily-optimized combined keygen() and sign() which greatly reduces peak
1824    /// memory usage by never having the full secret key in memory at the same time,
1825    /// and by deriving intermediate values piece-wise as needed.
1826    fn sign_mu_deterministic_from_seed_out(
1827        seed: &KeyMaterial<32>,
1828        mu: &[u8; 64],
1829        rnd: [u8; 32],
1830        output: &mut [u8; SIG_LEN],
1831    ) -> Result<usize, SignatureError>;
1832    /// To be used for deterministic signing in conjunction with the [MLDSA44::sign_init], [MLDSA44::sign_update], and [MLDSA44::sign_final] flow.
1833    /// Can be set anywhere after [MLDSA44::sign_init] and before [MLDSA44::sign_final].
1834    fn set_signer_rnd(&mut self, rnd: [u8; 32]);
1835    /// Alternative initialization of the streaming signer where you have your private key
1836    /// as a seed and you want to delay its expansion as late as possible for memory-usage reasons.
1837    fn sign_init_from_seed(
1838        seed: &KeyMaterial<32>,
1839        ctx: Option<&[u8]>,
1840    ) -> Result<Self, SignatureError>;
1841    /// Same as [Signature::verify], but signs from an expanded key object.
1842    fn verify_with_expanded_key(
1843        pk: &MLDSAPublicKeyExpanded<k, l, PK, PK_LEN>,
1844        msg: &[u8],
1845        ctx: Option<&[u8]>,
1846        sig: &[u8],
1847    ) -> Result<(), SignatureError>;
1848    /// Algorithm 8 ML-DSA.Verify_internal(π‘π‘˜, 𝑀′, 𝜎)
1849    /// Internal function to verify a signature 𝜎 for a formatted message 𝑀′ .
1850    /// Input: Public key π‘π‘˜ ∈ 𝔹32+32π‘˜(bitlen (π‘žβˆ’1)βˆ’π‘‘) and message 𝑀′ ∈ {0, 1}βˆ— .
1851    /// Input: Signature 𝜎 ∈ π”Ήπœ†/4+β„“β‹…32β‹…(1+bitlen (𝛾1βˆ’1))+πœ”+π‘˜.
1852    fn verify_mu_internal(
1853        pk: &PK,
1854        A_hat: &Matrix<k, l>,
1855        mu: &[u8; 64],
1856        sig: &[u8; SIG_LEN],
1857    ) -> bool;
1858}
1859
1860impl<
1861    const PK_LEN: usize,
1862    const SK_LEN: usize,
1863    const SIG_LEN: usize,
1864    PK: MLDSAPublicKeyTrait<k, l, PK_LEN> + MLDSAPublicKeyInternalTrait<k, PK_LEN>,
1865    SK: MLDSAPrivateKeyTrait<k, l, ETA, SK_LEN, PK_LEN>
1866        + MLDSAPrivateKeyInternalTrait<k, l, ETA, SK_LEN, PK_LEN>,
1867    const TAU: i32,
1868    const LAMBDA: i32,
1869    const GAMMA1: i32,
1870    const GAMMA2: i32,
1871    const k: usize,
1872    const l: usize,
1873    const ETA: usize,
1874    const BETA: i32,
1875    const OMEGA: i32,
1876    const C_TILDE: usize,
1877    const POLY_Z_PACKED_LEN: usize,
1878    const POLY_W1_PACKED_LEN: usize,
1879    const LAMBDA_over_4: usize,
1880    const GAMMA1_MASK_LEN: usize,
1881    const GAMMA1_MINUS_BETA: i32,
1882    const GAMMA2_MINUS_BETA: i32,
1883> Signature<PK, SK, PK_LEN, SK_LEN, SIG_LEN>
1884    for MLDSA<
1885        PK_LEN,
1886        SK_LEN,
1887        SIG_LEN,
1888        PK,
1889        SK,
1890        TAU,
1891        LAMBDA,
1892        GAMMA1,
1893        GAMMA2,
1894        k,
1895        l,
1896        ETA,
1897        BETA,
1898        OMEGA,
1899        C_TILDE,
1900        POLY_Z_PACKED_LEN,
1901        POLY_W1_PACKED_LEN,
1902        LAMBDA_over_4,
1903        GAMMA1_MASK_LEN,
1904        GAMMA1_MINUS_BETA,
1905        GAMMA2_MINUS_BETA,
1906    >
1907{
1908    fn keygen() -> Result<(PK, SK), SignatureError> {
1909        Self::keygen_from_os_rng()
1910    }
1911
1912    fn sign(sk: &SK, msg: &[u8], ctx: Option<&[u8]>) -> Result<[u8; SIG_LEN], SignatureError> {
1913        let mut out = [0u8; SIG_LEN];
1914        Self::sign_out(sk, msg, ctx, &mut out)?;
1915
1916        Ok(out)
1917    }
1918
1919    fn sign_out(
1920        sk: &SK,
1921        msg: &[u8],
1922        ctx: Option<&[u8]>,
1923        output: &mut [u8; SIG_LEN],
1924    ) -> Result<usize, SignatureError> {
1925        let mu = MuBuilder::compute_mu(&sk.tr(), msg, ctx)?;
1926        let bytes_written = Self::sign_mu_out(sk, None, &mu, output)?;
1927
1928        Ok(bytes_written)
1929    }
1930
1931    fn sign_init(sk: &SK, ctx: Option<&[u8]>) -> Result<Self, SignatureError> {
1932        Ok(Self {
1933            _phantom: PhantomData,
1934            mu_builder: MuBuilder::do_init(&sk.tr(), ctx)?,
1935            signer_rnd: None,
1936            sk: Some(sk.clone()),
1937            seed: None,
1938            pk: None,
1939        })
1940    }
1941
1942    fn sign_update(&mut self, msg_chunk: &[u8]) {
1943        self.mu_builder.do_update(msg_chunk);
1944    }
1945
1946    fn sign_final(self) -> Result<[u8; SIG_LEN], SignatureError> {
1947        let mut out = [0u8; SIG_LEN];
1948        self.sign_final_out(&mut out)?;
1949        Ok(out)
1950    }
1951
1952    fn sign_final_out(self, output: &mut [u8; SIG_LEN]) -> Result<usize, SignatureError> {
1953        let mu = self.mu_builder.do_final();
1954
1955        if self.sk.is_none() && self.seed.is_none() {
1956            return Err(SignatureError::GenericError(
1957                "Somehow you managed to construct a streaming signer without a private key, impressive!",
1958            ));
1959        }
1960
1961        if self.sk.is_some() {
1962            if self.signer_rnd.is_none() {
1963                Self::sign_mu_out(&self.sk.unwrap(), None, &mu, output)
1964            } else {
1965                Self::sign_mu_deterministic_out(
1966                    &self.sk.unwrap(),
1967                    None,
1968                    &mu,
1969                    self.signer_rnd.unwrap(),
1970                    output,
1971                )
1972            }
1973        } else if self.seed.is_some() {
1974            let rnd = if self.signer_rnd.is_some() {
1975                self.signer_rnd.unwrap()
1976            } else {
1977                let mut rnd: [u8; MLDSA_RND_LEN] = [0u8; MLDSA_RND_LEN];
1978                HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?;
1979                rnd
1980            };
1981            Self::sign_mu_deterministic_from_seed_out(&self.seed.unwrap(), &mu, rnd, output)
1982        } else {
1983            unreachable!()
1984        }
1985    }
1986
1987    fn verify(pk: &PK, msg: &[u8], ctx: Option<&[u8]>, sig: &[u8]) -> Result<(), SignatureError> {
1988        let mu = MuBuilder::compute_mu(&pk.compute_tr(), msg, ctx)?;
1989
1990        if sig.len() != SIG_LEN {
1991            return Err(SignatureError::LengthError("Signature value is not the correct length."));
1992        }
1993        if Self::verify_mu_internal(pk, &pk.A_hat(), &mu, &sig[..SIG_LEN].try_into().unwrap()) {
1994            Ok(())
1995        } else {
1996            Err(SignatureError::SignatureVerificationFailed)
1997        }
1998    }
1999
2000    fn verify_init(pk: &PK, ctx: Option<&[u8]>) -> Result<Self, SignatureError> {
2001        Ok(Self {
2002            _phantom: Default::default(),
2003            mu_builder: MuBuilder::do_init(&pk.compute_tr(), ctx)?,
2004            signer_rnd: None,
2005            sk: None,
2006            seed: None,
2007            pk: Some(pk.clone()),
2008        })
2009    }
2010
2011    fn verify_update(&mut self, msg_chunk: &[u8]) {
2012        self.mu_builder.do_update(msg_chunk);
2013    }
2014
2015    fn verify_final(self, sig: &[u8]) -> Result<(), SignatureError> {
2016        let mu = self.mu_builder.do_final();
2017
2018        assert!(
2019            self.pk.is_some(),
2020            "Somehow you managed to construct a streaming verifier without a public key, impressive!"
2021        );
2022        let pk: &PK = &self.pk.unwrap();
2023
2024        if sig.len() != SIG_LEN {
2025            return Err(SignatureError::LengthError("Signature value is not the correct length."));
2026        }
2027        if Self::verify_mu_internal(pk, &pk.A_hat(), &mu, &sig[..SIG_LEN].try_into().unwrap()) {
2028            Ok(())
2029        } else {
2030            Err(SignatureError::SignatureVerificationFailed)
2031        }
2032    }
2033}
2034
2035/// Implements parts of Algorithm 2 and Line 6 of Algorithm 7 of FIPS 204.
2036/// Provides a stateful version of [MLDSATrait::compute_mu_from_pk] and [MLDSATrait::compute_mu_from_tr]
2037/// that supports streaming
2038/// large to-be-signed messages.
2039///
2040/// Note: this struct is only exposed for "pure" ML-DSA and not for HashML-DSA because HashML-DSA
2041/// does not benefit from allowing external construction of the message representative mu.
2042/// You can get the same behaviour by computing the pre-hash `ph` with the appropriate hash function
2043/// and providing that to HashMLDSA via [PHSignature::sign_ph].
2044pub struct MuBuilder {
2045    h: H,
2046}
2047
2048impl MuBuilder {
2049    /// Algorithm 7
2050    /// 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀′, 64)
2051    pub fn compute_mu(
2052        tr: &[u8; 64],
2053        msg: &[u8],
2054        ctx: Option<&[u8]>,
2055    ) -> Result<[u8; 64], SignatureError> {
2056        let mut mu_builder = MuBuilder::do_init(&tr, ctx)?;
2057        mu_builder.do_update(msg);
2058        let mu = mu_builder.do_final();
2059
2060        Ok(mu)
2061    }
2062
2063    /// This function requires the public key hash `tr`, which can be computed from the public key
2064    /// using [MLDSAPublicKeyTrait::compute_tr].
2065    pub fn do_init(tr: &[u8; 64], ctx: Option<&[u8]>) -> Result<Self, SignatureError> {
2066        let ctx = match ctx {
2067            Some(ctx) => ctx,
2068            None => &[],
2069        };
2070
2071        // Algorithm 2
2072        // 1: if |𝑐𝑑π‘₯| > 255 then
2073        if ctx.len() > 255 {
2074            return Err(SignatureError::LengthError("ctx value is longer than 255 bytes"));
2075        }
2076
2077        // Algorithm 7
2078        // 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀', 64)
2079        let mut mb = Self { h: H::new() };
2080        mb.h.absorb(tr);
2081
2082        // Algorithm 2
2083        // 10: 𝑀′ ← BytesToBits(IntegerToBytes(0, 1) βˆ₯ IntegerToBytes(|𝑐𝑑π‘₯|, 1) βˆ₯ 𝑐𝑑π‘₯) βˆ₯ 𝑀
2084        // all done together
2085        mb.h.absorb(&[0u8]);
2086        mb.h.absorb(&[ctx.len() as u8]);
2087        mb.h.absorb(ctx);
2088
2089        // now ready to absorb M
2090        Ok(mb)
2091    }
2092
2093    /// Stream a chunk of the message.
2094    pub fn do_update(&mut self, msg_chunk: &[u8]) {
2095        self.h.absorb(msg_chunk);
2096    }
2097
2098    /// Finalize and return the mu value.
2099    pub fn do_final(mut self) -> [u8; 64] {
2100        // Completion of
2101        // Algorithm 7
2102        // 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀 β€², 64)
2103        let mut mu = [0u8; 64];
2104        self.h.squeeze_out(&mut mu);
2105
2106        mu
2107    }
2108}