Skip to main content

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