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