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 you need to sign or verify is too big to fit in device memory all at once.
8//! No worries, we got you covered!
9//!
10//! ```rust
11//! use bouncycastle_core_interface::errors::SignatureError;
12//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
13//! use bouncycastle_core_interface::traits::Signature;
14//!
15//! let (pk, sk) = MLDSA65::keygen().unwrap();
16//!
17//! // Let's pretend this message was so long that you couldn't possibly
18//! // stream the whole thing over a network, and you need it pre-hashed.
19//! let msg_chunk1 = b"The quick brown fox ";
20//! let msg_chunk2 = b"jumped over the lazy dog";
21//!
22//! let mut signer = MLDSA65::sign_init(&sk, None).unwrap();
23//! signer.sign_update(msg_chunk1);
24//! signer.sign_update(msg_chunk2);
25//! let sig: Vec<u8> = signer.sign_final().unwrap();
26//! // This is the signature value that you can save to a file or whatever you need.
27//!
28//! // This is compatible with a verifies that takes the whole message as one chunk:
29//! let msg = b"The quick brown fox jumped over the lazy dog";
30//! match MLDSA65::verify(&pk, msg, None, &sig) {
31//!     Ok(()) => println!("Signature is valid!"),
32//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
33//!     Err(e) => panic!("Something else went wrong: {:?}", e),
34//! }
35//!
36//! // But of course there's also a streaming API for the verifier!
37//! let mut verifier = MLDSA65::verify_init(&pk, None).unwrap();
38//! verifier.verify_update(msg_chunk1);
39//! verifier.verify_update(msg_chunk2);
40//!
41//! match verifier.verify_final(&sig.as_slice()) {
42//!     Ok(()) => println!("Signature is valid!"),
43//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
44//!     Err(e) => panic!("Something else went wrong: {:?}", e),
45//! }
46//! ```
47//!
48//!
49//! Note that the streaming API also supports setting the signing context `ctx` and signing nonce `rnd`,
50//! which are explained in more detail below.
51//!
52//! ```rust
53//! use bouncycastle_core_interface::errors::SignatureError;
54//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
55//! use bouncycastle_core_interface::traits::Signature;
56//!
57//! let (pk, sk) = MLDSA65::keygen().unwrap();
58//!
59//! // Let's pretend this message was so long that you couldn't possibly
60//! // stream the whole thing over a network, and you need it pre-hashed.
61//! let msg_chunk1 = b"The quick brown fox ";
62//! let msg_chunk2 = b"jumped over the lazy dog";
63//!
64//! let mut signer = MLDSA65::sign_init(&sk, Some(b"signing ctx value")).unwrap();
65//! signer.set_signer_rnd([0u8; 32]); // an all-zero rnd is the "deterministic" mode of ML-DSA
66//! signer.sign_update(msg_chunk1);
67//! signer.sign_update(msg_chunk2);
68//! let sig: Vec<u8> = signer.sign_final().unwrap();
69//! ```
70//!
71//! # External Mu mode
72//!
73//! Here, `mu` refers to the message digest which is computed internally to the ML-DSA algorithm:
74//!
75//! > πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀′, 64)
76//! >   β–· message representative that may optionally be computed in a different cryptographic module
77//!
78//! The External Mu mode of ML-DSA fulfills a similar function to [hash_mldsa] in that it allows large
79//! messages to be pre-digested outside of the cryptographic module that holds the private key,
80//! but it does it in a way that is compatible with the ML-DSA verification function.
81//! In other works, whereas [hash_mldsa] represents a different signature algorithm, the external mu
82//! mode of ML-DSA is simply internal implementation detail of how the signature was computed and
83//! produces signatures that are indistinguishable from "direct" ML-DSA mode.
84//!
85//! The one potential complication with external mu mode -- that [hash_mldsa] does not have --
86//! is that it requires you to know the public key that you are about to sign the message with.
87//! Or, more specifically, the hash of the public key `tr`.
88//! `tr` is a public value (derivable from the public key), so there is no harm in, for example,
89//! sending it down to a client device so that it can pre-hash a large message and only send the
90//! 64-byte `mu` value up to the server to be signed.
91//! But in some contexts, the message has to be pre-hashed for performance reasons but
92//! the public key that will be used for signing cannot be known in advance.
93//! For those use cases, your only choice is to use [hash_mldsa].
94//!
95//! This library exposes [MuBuilder] which can be used to pre-hash a large to-be-signed message
96//! along with the public key hash `tr`:
97//!
98//! ```rust
99//! use bouncycastle_core_interface::errors::SignatureError;
100//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
101//! use bouncycastle_core_interface::traits::Signature;
102//!
103//! let (pk, _) = MLDSA65::keygen().unwrap();
104//!
105//! // Let's pretend this message was so long that you couldn't possibly
106//! // stream the whole thing over a network, and you need it pre-hashed.
107//! let msg = b"The quick brown fox jumped over the lazy dog";
108//!
109//! let mu: [u8; 64] = MuBuilder::compute_mu(msg, None, &pk.compute_tr()).unwrap();
110//! ```
111//!
112//! Note: if you are going to bind a `ctx` value (explained below), then you need to do in in [MuBuilder::compute_mu].
113//!
114//! If the message really is so huge that you can't hold it all in memory at once, then you might prefer a streaming API for
115//! computing mu:
116//!
117//! ```rust
118//! use bouncycastle_core_interface::errors::SignatureError;
119//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
120//! use bouncycastle_core_interface::traits::Signature;
121//!
122//! let (pk, _) = MLDSA65::keygen().unwrap();
123//!
124//! // Let's pretend this message was so long that you couldn't possibly
125//! // stream the whole thing over a network, and you need it pre-hashed.
126//! let msg_chunk1 = b"The quick brown fox ";
127//! let msg_chunk2 = b"jumped over the lazy dog";
128//!
129//! let mut mb = MuBuilder::do_init(&pk.compute_tr(), None).unwrap();
130//! mb.do_update(msg_chunk1);
131//! mb.do_update(msg_chunk2);
132//! let mu = mb.do_final();
133//! ```
134//!
135//! Given a mu value, you can compute a signature that verifies as normal (no mu's required!):
136//!
137//! ```rust
138//! use bouncycastle_core_interface::errors::SignatureError;
139//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
140//! use bouncycastle_core_interface::traits::Signature;
141//!
142//! let msg = b"The quick brown fox jumped over the lazy dog";
143//!
144//! let (pk, sk) = MLDSA65::keygen().unwrap();
145//!
146//! // Assume this was computed somewhere else and sent to you.
147//! // They would have had to know pk!
148//! let mu: [u8; 64] = MuBuilder::compute_mu(msg, None, &pk.compute_tr()).unwrap();
149//!
150//! let sig = MLDSA65::sign_mu(&sk, &mu).unwrap();
151//! // This is the signature value that you can save to a file or whatever you need.
152//!
153//! match MLDSA65::verify(&pk, msg, None, &sig) {
154//!     Ok(()) => println!("Signature is valid!"),
155//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
156//!     Err(e) => panic!("Something else went wrong: {:?}", e),
157//! }
158//!
159//! ```
160//!
161//! # Ctx and Rnd params
162//! Various functions in this crate let you set the signing context value (`ctx`) and the signing nonce (`rnd`).
163//! Let's talk about them both:
164//!
165//! ## ctx
166//! The `ctx` value allows the signer to bind the signature value to an extra piece of information
167//! (up to 255 bytes long) that must also be known to the verifier in order to successfully verify the signature.
168//! This optional parameter allows cryptographic protocol designers to get additional binding properties
169//! from the ML-DSA signature.
170//! The `ctx` value should be something that is known to both the signer and verifier,
171//! does not necessarily need to be a secret, but should not go over the wire as part of the not-yet-verified message.
172//! Examples of uses of the `ctx` could include binding the application data type (ex: `FooEmailData`) in order
173//! to disambiguate other data types that share an encoding (ex: `FooTextDocumentData`) and might otherwise be possible for an
174//! attacker to trick a verifier into accepting one in place of the other.
175//! In a network protocol, `ctx` could be used to bind a transaction ID or protocol nonce in order to strongly
176//! protect against replay attacks.
177//! Generally, `ctx` is one of those things that if you don't know what it does, then you're probably
178//! fine to ignore it.
179//!
180//! Example of signing and verifying with a `ctx` value:
181//!
182//! ```rust
183//! use bouncycastle_core_interface::errors::SignatureError;
184//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait};
185//! use bouncycastle_core_interface::traits::Signature;
186//!
187//! let msg = b"The quick brown fox";
188//! let ctx = b"FooTextDocumentFormat";
189//!
190//! let (pk, sk) = MLDSA65::keygen().unwrap();
191//!
192//! let sig: Vec<u8> = MLDSA65::sign(&sk, msg, Some(ctx)).unwrap();
193//! // This is the signature value that you can save to a file or whatever you need.
194//!
195//! match MLDSA65::verify(&pk, msg, Some(ctx), &sig) {
196//!     Ok(()) => println!("Signature is valid!"),
197//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
198//!     Err(e) => panic!("Something else went wrong: {:?}", e),
199//! }
200//! ```
201//!
202//! ## rnd
203//!
204//! This is the signature nonce, whose purpose is to ensure that you get different signature values
205//! if you sign the same message with the same public key multiple times.
206//!
207//! In general, the "deterministic" mode of ML-DSA (which usually uses an all-zero `rnd`) is considered
208//! secure and safe to use but you may lose certain privacy properties, because, for example,
209//! it becomes obvious that multiple identical signatures means that the same message was signed multiple times
210//! by the same private key.
211//!
212//! The default mode of ML-DSA uses a `rnd` generated by the library's OS-backed RNG, but you can set the `rnd`
213//! if you need to; for example if you are running on an embedded device that does not have access to an RNG.
214//!
215//! Note that in order to avoid combinatorial explosion of API functions, setting the `rnd` value is only
216//! available in conjunction with external mu or streaming modes. The example of setting `rnd` on the streaming
217//! API was shown above.
218//!
219//! Here is an example of using the [MLDSA::sign_mu_deterministic] function:
220//!
221//! ```rust
222//! use bouncycastle_core_interface::errors::SignatureError;
223//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
224//! use bouncycastle_core_interface::traits::Signature;
225//!
226//! let msg = b"The quick brown fox jumped over the lazy dog";
227//!
228//! let (pk, sk) = MLDSA65::keygen().unwrap();
229//!
230//! // Assume this was computed somewhere else and sent to you.
231//! // They would have had to know pk!
232//! let mu: [u8; 64] = MuBuilder::compute_mu(msg, None, &pk.compute_tr()).unwrap();
233//!
234//! // Typically, "deterministic" mode of ML-DSA will use an all-zero rnd,
235//! // but we've exposed it so you can set any value you need to.
236//! let sig = MLDSA65::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap();
237//! // This is the signature value that you can save to a file or whatever you need.
238//!
239//! match MLDSA65::verify(&pk, msg, None, &sig) {
240//!     Ok(()) => println!("Signature is valid!"),
241//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
242//!     Err(e) => panic!("Something else went wrong: {:?}", e),
243//! }
244//! ```
245//!
246//! # sign_from_seed
247//!
248//! This mode is intended for users with extreme performance or resource-limitation requirements.
249//!
250//! A very careful analysis of the ML-DSA signing algorithm will show that you don't actually need
251//! the entire ML-DSA private key to be in memory at the same time. In fact, it is possible to merge
252//! the keygen() and sign() functions
253//!
254//! We provide [MLDSA::sign_mu_deterministic_from_seed] which implements such an algorithm.
255//! It has a significantly lower peak-memory-footprint than the regular signing API (although there's
256//! always room for more optimization), and according to our benchmarks it is only around 25% slower
257//! than signing with a fully-expanded private key -- which is still faster than performing a full
258//! keygen followed by a regular sign since there are intermediate values common to keygen and sign
259//! that the merged function is able to only compute once.
260//!
261//! Since this is intended for hard-core embedded systems people, we have not wrapped this in all
262//! the beginner-friendly APIs. If you need this, then we assume you know what you're doing!
263//!
264//! Example usage:
265//!
266//! ```rust
267//! use bouncycastle_core_interface::errors::SignatureError;
268//! use bouncycastle_mldsa::{MLDSA44, MLDSA44_SIG_LEN, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder};
269//! use bouncycastle_core_interface::traits::Signature;
270//! use bouncycastle_core_interface::traits::KeyMaterial;
271//! use bouncycastle_core_interface::key_material::{KeyMaterial256, KeyType};
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//! // At some point, you'll need to compute the public key, both to get `tr`, and so other
281//! // people can verify your signature.
282//! // There's no possible short-cut to efficiently computing the public key or `tr` from the seed;
283//! // you have to run the full keygen to get the full private key, at least momentarily, then
284//! // you can discard it in 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 and sent to you.
289//! // They would have had to know pk!
290//! let mu: [u8; 64] = MuBuilder::compute_mu(msg, None, &tr).unwrap();
291//! let rnd: [u8; 32] = [0u8; 32]; // with this API, you're responsible for your 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
312use core::marker::PhantomData;
313use crate::aux_functions::{sample_in_ball, bitpack_gamma1, bitlen_eta, unpack_h_row, unpack_c_tilde};
314use crate::low_memory_helpers::{compute_ct0_component, compute_w0cs2_component, compute_w_row, compute_wp_approx_row, compute_z_component, s_unpack};
315use crate::mldsa_keys::{MLDSAPublicKeyTrait, MLDSAPublicKeyInternalTrait};
316use crate::mldsa_keys::{MLDSAPrivateKeyTrait, MLDSAPrivateKeyInternalTrait};
317use crate::{MLDSA44PublicKey, MLDSA44PrivateKey, MLDSA65PublicKey, MLDSA65PrivateKey, MLDSA87PublicKey, MLDSA87PrivateKey};
318use bouncycastle_core_interface::errors::SignatureError;
319use bouncycastle_core_interface::key_material::{KeyMaterialSized};
320use bouncycastle_core_interface::traits::{RNG, SecurityStrength, XOF, Signature, Algorithm};
321use bouncycastle_rng::{HashDRBG_SHA512};
322use bouncycastle_sha3::{SHAKE128, SHAKE256};
323
324
325// imports needed just for docs
326#[allow(unused_imports)]
327use bouncycastle_core_interface::traits::PHSignature;
328#[allow(unused_imports)]
329use crate::hash_mldsa;
330#[allow(unused_imports)]
331use bouncycastle_core_interface::key_material::KeyMaterial256;
332/*** Constants ***/
333
334///
335pub const ML_DSA_44_NAME: &str = "ML-DSA-44";
336///
337pub const ML_DSA_65_NAME: &str = "ML-DSA-65";
338///
339pub const ML_DSA_87_NAME: &str = "ML-DSA-87";
340
341// From FIPS 204 Table 1 and Table 2
342
343// Constants that are the same for all parameter sets
344pub(crate) const N: usize = 256;
345pub(crate) const q: i32 = 8380417;
346pub(crate) const q_inv: i32 = 58728449; // q ^ (-1) mod 2 ^32
347pub(crate) const d: i32 = 13;
348/// Length of the \[u8] holding a ML-DSA signing random value.
349pub const RND_LEN: usize = 32;
350/// Length of the \[u8] holding a ML-DSA tr value (which is the SHAKE256 hash of the public key).
351pub const TR_LEN: usize = 64;
352/// Length of the \[u8] holding a ML-DSA mu value.
353pub const MU_LEN: usize = 64;
354pub(crate) const POLY_T1PACKED_LEN: usize = 320;
355
356
357/* ML-DSA-44 params */
358
359/// Length of the \[u8] holding a ML-DSA-44 public key.
360pub const MLDSA44_PK_LEN: usize = 1312;
361/// Length of the \[u8] holding a ML-DSA-44 private key, which in this implementation is just a 32-byte seed.
362pub const MLDSA44_SK_LEN: usize = 32;
363/// Length of the \[u8] holding a ML-DSA-44 signature value.
364pub const MLDSA44_SIG_LEN: usize = 2420;
365pub(crate) const MLDSA44_TAU: i32 = 39;
366pub(crate) const MLDSA44_LAMBDA: i32 = 128;
367pub(crate) const MLDSA44_GAMMA1: i32 = 1 << 17;
368pub(crate) const MLDSA44_GAMMA2: i32 = (q - 1) / 88;
369pub(crate) const MLDSA44_k: usize = 4;
370pub(crate) const MLDSA44_l: usize = 4;
371pub(crate) const MLDSA44_ETA: usize = 2;
372pub(crate) const MLDSA44_BETA: i32 = 78;
373pub(crate) const MLDSA44_OMEGA: i32 = 80;
374
375// Useful derived values
376pub(crate) const MLDSA44_C_TILDE: usize = 32;
377pub(crate) const MLDSA44_POLY_Z_PACKED_LEN: usize = 576;
378pub(crate) const MLDSA44_POLY_W1_PACKED_LEN: usize = 192;
379pub(crate) const MLDSA44_S1_PACKED_LEN: usize = bitlen_eta(MLDSA44_ETA)  * MLDSA44_l;
380pub(crate) const MLDSA44_S2_PACKED_LEN: usize = bitlen_eta(MLDSA44_ETA)  * MLDSA44_k;
381pub(crate) const MLDSA44_T1_PACKED_LEN: usize = POLY_T1PACKED_LEN * MLDSA44_k;
382pub(crate) const MLDSA44_POLY_ETA_PACKED_LEN: usize = 32*3;
383pub(crate) const MLDSA44_LAMBDA_over_4: usize = 128/4;
384
385// Alg 32
386// 1: 𝑐 ← 1 + bitlen (𝛾1 βˆ’ 1)
387pub(crate) const MLDSA44_GAMMA1_MASK_LEN: usize = 576;  // 32*(1 + bitlen (𝛾1 βˆ’ 1) )
388
389
390/* ML-DSA-65 params */
391
392/// Length of the \[u8] holding a ML-DSA-65 public key.
393pub const MLDSA65_PK_LEN: usize = 1952;
394/// Length of the \[u8] holding a ML-DSA-65 private key, which in this implementation is just a 32-byte seed.
395pub const MLDSA65_SK_LEN: usize = 32;
396/// Length of the \[u8] holding a ML-DSA-65 signature value.
397pub const MLDSA65_SIG_LEN: usize = 3309;
398pub(crate) const MLDSA65_TAU: i32 = 49;
399pub(crate) const MLDSA65_LAMBDA: i32 = 192;
400pub(crate) const MLDSA65_GAMMA1: i32 = 1 << 19;
401pub(crate) const MLDSA65_GAMMA2: i32 = (q - 1) / 32;
402pub(crate) const MLDSA65_k: usize = 6;
403pub(crate) const MLDSA65_l: usize = 5;
404pub(crate) const MLDSA65_ETA: usize = 4;
405pub(crate) const MLDSA65_BETA: i32 = 196;
406pub(crate) const MLDSA65_OMEGA: i32 = 55;
407
408// Useful derived values
409pub(crate) const MLDSA65_C_TILDE: usize = 48;
410pub(crate) const MLDSA65_POLY_Z_PACKED_LEN: usize = 640;
411pub(crate) const MLDSA65_POLY_W1_PACKED_LEN: usize = 128;
412pub(crate) const MLDSA65_S1_PACKED_LEN: usize = bitlen_eta(MLDSA65_ETA)  * MLDSA65_l;
413pub(crate) const MLDSA65_S2_PACKED_LEN: usize = bitlen_eta(MLDSA65_ETA)  * MLDSA65_k;
414pub(crate) const MLDSA65_T1_PACKED_LEN: usize = POLY_T1PACKED_LEN * MLDSA65_k;
415pub(crate) const MLDSA65_POLY_ETA_PACKED_LEN: usize = 32*4;
416pub(crate) const MLDSA65_LAMBDA_over_4: usize = 192/4;
417
418// Alg 32
419// 1: 𝑐 ← 1 + bitlen (𝛾1 βˆ’ 1)
420pub(crate) const MLDSA65_GAMMA1_MASK_LEN: usize = 640;
421
422
423
424/* ML-DSA-87 params */
425
426/// Length of the \[u8] holding a ML-DSA-87 public key.
427pub const MLDSA87_PK_LEN: usize = 2592;
428/// Length of the \[u8] holding a ML-DSA-87 private key, which in this implementation is just a 32-byte seed.
429pub const MLDSA87_SK_LEN: usize = 32;
430/// Length of the \[u8] holding a ML-DSA-87 signature value.
431pub const MLDSA87_SIG_LEN: usize = 4627;
432pub(crate) const MLDSA87_TAU: i32 = 60;
433pub(crate) const MLDSA87_LAMBDA: i32 = 256;
434pub(crate) const MLDSA87_GAMMA1: i32 = 1 << 19;
435pub(crate) const MLDSA87_GAMMA2: i32 = (q - 1) / 32;
436pub(crate) const MLDSA87_k: usize = 8;
437pub(crate) const MLDSA87_l: usize = 7;
438pub(crate) const MLDSA87_ETA: usize = 2;
439pub(crate) const MLDSA87_BETA: i32 = 120;
440pub(crate) const MLDSA87_OMEGA: i32 = 75;
441
442// Useful derived values
443pub(crate) const MLDSA87_C_TILDE: usize = 64;
444pub(crate) const MLDSA87_POLY_Z_PACKED_LEN: usize = 640;
445pub(crate) const MLDSA87_POLY_W1_PACKED_LEN: usize = 128;
446pub(crate) const MLDSA87_S1_PACKED_LEN: usize = bitlen_eta(MLDSA87_ETA)  * MLDSA87_l;
447pub(crate) const MLDSA87_S2_PACKED_LEN: usize = bitlen_eta(MLDSA87_ETA)  * MLDSA87_k;
448pub(crate) const MLDSA87_T1_PACKED_LEN: usize = POLY_T1PACKED_LEN * MLDSA87_k;
449pub(crate) const MLDSA87_POLY_ETA_PACKED_LEN: usize = 32*3;
450pub(crate) const MLDSA87_LAMBDA_over_4: usize = 256/4;
451
452// Alg 32
453// 1: 𝑐 ← 1 + bitlen (𝛾1 βˆ’ 1)
454pub(crate) const MLDSA87_GAMMA1_MASK_LEN: usize = 640;
455
456
457
458// Typedefs just to make the algorithms look more like the FIPS 204 sample code.
459pub(crate) type H = SHAKE256;
460pub(crate) type G = SHAKE128;
461
462
463/*** Pub Types ***/
464
465/// The ML-DSA-44 algorithm.
466pub type MLDSA44 = MLDSA<
467    MLDSA44_PK_LEN,
468    MLDSA44_SK_LEN,
469    MLDSA44_SIG_LEN,
470    MLDSA44PublicKey,
471    MLDSA44PrivateKey,
472    MLDSA44_TAU,
473    MLDSA44_LAMBDA,
474    MLDSA44_GAMMA1,
475    MLDSA44_GAMMA2,
476    MLDSA44_k,
477    MLDSA44_l,
478    MLDSA44_ETA,
479    MLDSA44_BETA,
480    MLDSA44_OMEGA,
481    MLDSA44_C_TILDE,
482    MLDSA44_POLY_Z_PACKED_LEN,
483    MLDSA44_POLY_W1_PACKED_LEN,
484    MLDSA44_S1_PACKED_LEN,
485    MLDSA44_S2_PACKED_LEN,
486    MLDSA44_T1_PACKED_LEN,
487    MLDSA44_POLY_ETA_PACKED_LEN,
488    MLDSA44_LAMBDA_over_4,
489    MLDSA44_GAMMA1_MASK_LEN,
490>;
491
492impl Algorithm for MLDSA44 {
493    const ALG_NAME: &'static str = ML_DSA_44_NAME;
494    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit;
495}
496
497/// The ML-DSA-65 algorithm.
498pub type MLDSA65 = MLDSA<
499    MLDSA65_PK_LEN,
500    MLDSA65_SK_LEN,
501    MLDSA65_SIG_LEN,
502    MLDSA65PublicKey,
503    MLDSA65PrivateKey,
504    MLDSA65_TAU,
505    MLDSA65_LAMBDA,
506    MLDSA65_GAMMA1,
507    MLDSA65_GAMMA2,
508    MLDSA65_k,
509    MLDSA65_l,
510    MLDSA65_ETA,
511    MLDSA65_BETA,
512    MLDSA65_OMEGA,
513    MLDSA65_C_TILDE,
514    MLDSA65_POLY_Z_PACKED_LEN,
515    MLDSA65_POLY_W1_PACKED_LEN,
516    MLDSA65_S1_PACKED_LEN,
517    MLDSA65_S2_PACKED_LEN,
518    MLDSA65_T1_PACKED_LEN,
519    MLDSA65_POLY_ETA_PACKED_LEN,
520    MLDSA65_LAMBDA_over_4,
521    MLDSA65_GAMMA1_MASK_LEN,
522>;
523
524impl Algorithm for MLDSA65 {
525    const ALG_NAME: &'static str = ML_DSA_65_NAME;
526    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit;
527}
528
529/// The ML-DSA-87 algorithm.
530pub type MLDSA87 = MLDSA<
531    MLDSA87_PK_LEN,
532    MLDSA87_SK_LEN,
533    MLDSA87_SIG_LEN,
534    MLDSA87PublicKey,
535    MLDSA87PrivateKey,
536    MLDSA87_TAU,
537    MLDSA87_LAMBDA,
538    MLDSA87_GAMMA1,
539    MLDSA87_GAMMA2,
540    MLDSA87_k,
541    MLDSA87_l,
542    MLDSA87_ETA,
543    MLDSA87_BETA,
544    MLDSA87_OMEGA,
545    MLDSA87_C_TILDE,
546    MLDSA87_POLY_Z_PACKED_LEN,
547    MLDSA87_POLY_W1_PACKED_LEN,
548    MLDSA87_S1_PACKED_LEN,
549    MLDSA87_S2_PACKED_LEN,
550    MLDSA87_T1_PACKED_LEN,
551    MLDSA87_POLY_ETA_PACKED_LEN,
552    MLDSA87_LAMBDA_over_4,
553    MLDSA87_GAMMA1_MASK_LEN,
554>;
555
556impl Algorithm for MLDSA87 {
557    const ALG_NAME: &'static str = ML_DSA_87_NAME;
558    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit;
559}
560
561/// The core internal implementation of the ML-DSA algorithm.
562/// This needs to be public for the compiler to be able to find it, but you shouldn't ever
563/// need to use this directly. Please use the named public types.
564pub struct MLDSA<
565    const PK_LEN: usize,
566    const SK_LEN: usize,
567    const SIG_LEN: usize,
568    PK: MLDSAPublicKeyTrait<k, T1_PACKED_LEN, PK_LEN> + MLDSAPublicKeyInternalTrait<k, T1_PACKED_LEN, PK_LEN>,
569    SK: MLDSAPrivateKeyTrait<k, l, S1_PACKED_LEN, S2_PACKED_LEN, T1_PACKED_LEN, PK_LEN, SK_LEN>
570        + MLDSAPrivateKeyInternalTrait<LAMBDA, GAMMA2, k, l, ETA, S1_PACKED_LEN, S2_PACKED_LEN, PK_LEN, SK_LEN>,
571    const TAU: i32,
572    const LAMBDA: i32,
573    const GAMMA1: i32,
574    const GAMMA2: i32,
575    const k: usize,
576    const l: usize,
577    const ETA: usize,
578    const BETA: i32,
579    const OMEGA: i32,
580    const C_TILDE: usize,
581    const POLY_VEC_H_PACKED_LEN: usize,
582    const POLY_W1_PACKED_LEN: usize,
583    const S1_PACKED_LEN: usize,
584    const S2_PACKED_LEN: usize,
585    const T1_PACKED_LEN: usize,
586    const POLY_ETA_PACKED_LEN: usize,
587    const LAMBDA_over_4: usize,
588    const GAMMA1_MASK_LEN: usize,
589> {
590    _phantom: PhantomData<(PK, SK)>,
591
592    /// used for streaming the message for both signing and verifying
593    mu_builder: MuBuilder,
594
595    signer_rnd: Option<[u8; RND_LEN]>,
596
597    /// only used in streaming sign operations
598    sk: Option<SK>,
599
600    /// only used in streaming sign operations instead of sk
601    seed: Option<KeyMaterialSized<32>>,
602
603    /// only used in streaming verify operations
604    pk: Option<PK>,
605}
606
607impl<
608    const PK_LEN: usize,
609    const SK_LEN: usize,
610    const SIG_LEN: usize,
611    PK: MLDSAPublicKeyTrait<k, T1_PACKED_LEN, PK_LEN> + MLDSAPublicKeyInternalTrait<k, T1_PACKED_LEN, PK_LEN>,
612    SK: MLDSAPrivateKeyTrait<k, l, S1_PACKED_LEN, S2_PACKED_LEN, T1_PACKED_LEN, PK_LEN, SK_LEN>
613        + MLDSAPrivateKeyInternalTrait<LAMBDA, GAMMA2, k, l, ETA, S1_PACKED_LEN, S2_PACKED_LEN, PK_LEN, SK_LEN>,
614    const TAU: i32,
615    const LAMBDA: i32,
616    const GAMMA1: i32,
617    const GAMMA2: i32,
618    const k: usize,
619    const l: usize,
620    const ETA: usize,
621    const BETA: i32,
622    const OMEGA: i32,
623    const C_TILDE: usize,
624    const POLY_Z_PACKED_LEN: usize,
625    const POLY_W1_PACKED_LEN: usize,
626    const S1_PACKED_LEN: usize,
627    const S2_PACKED_LEN: usize,
628    const T1_PACKED_LEN: usize,
629    const POLY_ETA_PACKED_LEN: usize,
630    const LAMBDA_over_4: usize,
631    const GAMMA1_MASK_LEN: usize,
632> MLDSA<
633    PK_LEN,
634    SK_LEN,
635    SIG_LEN,
636    PK,
637    SK,
638    TAU,
639    LAMBDA,
640    GAMMA1,
641    GAMMA2,
642    k,
643    l,
644    ETA,
645    BETA,
646    OMEGA,
647    C_TILDE,
648    POLY_Z_PACKED_LEN,
649    POLY_W1_PACKED_LEN,
650    S1_PACKED_LEN,
651    S2_PACKED_LEN,
652    T1_PACKED_LEN,
653    POLY_ETA_PACKED_LEN,
654    LAMBDA_over_4,
655    GAMMA1_MASK_LEN,
656>
657{
658    /// Should still be ok in FIPS mode
659    pub fn keygen_from_os_rng() -> Result<
660        (PK, SK),
661        SignatureError,
662    > {
663        let mut seed = KeyMaterialSized::<32>::new();
664        HashDRBG_SHA512::new_from_os().fill_keymaterial_out(&mut seed)?;
665        Self::keygen_internal(&seed)
666    }
667    /// Implements Algorithm 6 of FIPS 204
668    /// Note: NIST has made a special exception in the FIPS 204 FAQ that this _internal function
669    /// may in fact be exposed outside the crypto module.
670    ///
671    /// Unlike other interfaces across the library that take an &impl KeyMaterial, this one
672    /// specifically takes a 32-byte [KeyMaterial256] and checks that it has [KeyType::Seed] and
673    /// [SecurityStrength::_256bit].
674    /// If you happen to have your seed in a larger KeyMaterial, you'll have to copy it using
675    /// [KeyMaterial::from_key]
676    pub(crate) fn keygen_internal(
677            seed: &KeyMaterialSized<32>,
678        ) -> Result<
679            (PK, SK),
680            SignatureError,
681        > {
682        let sk = SK::from_keymaterial(seed)?;
683        let pk = sk.derive_pk();
684        let pk = PK::new(&pk.rho, &pk.t1_packed); // stupid conversion, but it gets around these overly-generified rust types
685        Ok((pk, sk))
686    }
687}
688
689impl<
690    const PK_LEN: usize,
691    const SK_LEN: usize,
692    const SIG_LEN: usize,
693    PK: MLDSAPublicKeyTrait<k, T1_PACKED_LEN, PK_LEN> + MLDSAPublicKeyInternalTrait<k, T1_PACKED_LEN, PK_LEN>,
694    SK: MLDSAPrivateKeyTrait<k, l, S1_PACKED_LEN, S2_PACKED_LEN, T1_PACKED_LEN, PK_LEN, SK_LEN>
695        + MLDSAPrivateKeyInternalTrait<LAMBDA, GAMMA2, k, l, eta, S1_PACKED_LEN, S2_PACKED_LEN, PK_LEN, SK_LEN>,
696    const TAU: i32,
697    const LAMBDA: i32,
698    const GAMMA1: i32,
699    const GAMMA2: i32,
700    const k: usize,
701    const l: usize,
702    const eta: usize,
703    const BETA: i32,
704    const OMEGA: i32,
705    const C_TILDE: usize,
706    const POLY_Z_PACKED_LEN: usize,
707    const POLY_W1_PACKED_LEN: usize,
708    const S1_PACKED_LEN: usize,
709    const S2_PACKED_LEN: usize,
710    const T1_PACKED_LEN: usize,
711    const POLY_ETA_PACKED_LEN: usize,
712    const LAMBDA_over_4: usize,
713    const GAMMA1_MASK_LEN: usize,
714> MLDSATrait<PK_LEN, SK_LEN, SIG_LEN, PK, SK, LAMBDA, GAMMA2, k, l, S1_PACKED_LEN, S2_PACKED_LEN, T1_PACKED_LEN, eta> for MLDSA<
715    PK_LEN,
716    SK_LEN,
717    SIG_LEN,
718    PK,
719    SK,
720    TAU,
721    LAMBDA,
722    GAMMA1,
723    GAMMA2,
724    k,
725    l,
726    eta,
727    BETA,
728    OMEGA,
729    C_TILDE,
730    POLY_Z_PACKED_LEN,
731    POLY_W1_PACKED_LEN,
732    S1_PACKED_LEN,
733    S2_PACKED_LEN,
734    T1_PACKED_LEN,
735    POLY_ETA_PACKED_LEN,
736    LAMBDA_over_4,
737    GAMMA1_MASK_LEN,
738> {
739    /*** Key Generation and PK / SK consistency checks ***/
740
741    /// Imports a secret key from a seed.
742    fn keygen_from_seed(seed: &KeyMaterialSized<32>) -> Result<(PK, SK), SignatureError> {
743        Self::keygen_internal(seed)
744    }
745    /// Imports a secret key from both a seed and an encoded_sk.
746    ///
747    /// This is a convenience function to expand the key from seed and compare it against
748    /// the provided `encoded_sk` using a constant-time equality check.
749    /// If everything checks out, the secret key is returned fully populated with pk and seed.
750    /// If the provided key and derived key don't match, an error is returned.
751    fn keygen_from_seed_and_encoded(
752        seed: &KeyMaterialSized<32>,
753        encoded_sk: &[u8; SK_LEN],
754    ) -> Result<
755        (PK, SK),
756        SignatureError,
757    > {
758        let (pk, sk) = Self::keygen_internal(seed)?;
759
760        let sk_from_bytes = SK::sk_decode(encoded_sk);
761
762        // MLDSAPrivateKey impls PartialEq with a constant-time equality check.
763        if sk != sk_from_bytes {
764            return Err(SignatureError::KeyGenError("Encoded key does not match generated key"));
765        }
766
767        Ok((pk, sk))
768    }
769    /// Given a public key and a secret key, check that the public key matches the secret key.
770    /// This is a sanity check that the public key was generated correctly from the secret key.
771    ///
772    /// At the current time, this is only possible if `sk` either contains a public key (in which case
773    /// the two pk's are encoded and compared for byte equality), or if `sk` contains a seed
774    /// (in which case a keygen_from_seed is run and then the pk's compared).
775    ///
776    /// Returns either `()` or [SignatureError::ConsistencyCheckFailed].
777    fn keypair_consistency_check(
778        pk: &PK,
779        sk: &SK,
780    ) -> Result<(), SignatureError> {
781        // This is maybe a computationally heavy way to compare them, but it works
782        let derived_pk = sk.derive_pk();
783        if derived_pk.compute_tr() == pk.compute_tr() {
784            Ok(())
785        } else {
786            Err(SignatureError::ConsistencyCheckFailed())
787        }
788    }
789    /// This provides the first half of the "External Mu" interface to ML-DSA which is described
790    /// in, and allowed under, NIST's FAQ that accompanies FIPS 204.
791    ///
792    /// This function, together with [MLDSATrait::sign_mu] perform a complete ML-DSA signature which is indistinguishable
793    /// from one produced by the one-shot sign APIs.
794    ///
795    /// The utility of this function is exactly as described
796    /// on Line 6 of Algorithm 7 of FIPS 204:
797    ///
798    ///    message representative that may optionally be computed in a different cryptographic module
799    ///
800    /// The utility is when an extremely large message needs to be signed, where the message exists on one
801    /// computing system and the private key to sign it is held on another and either the transfer time or bandwidth
802    /// causes operational concerns (this is common for example with network HSMs or sending large messages
803    /// to be signed by a smartcard communicating over near-field radio). Another use case is if the
804    /// contents of the message are sensitive and the signer does not want to transmit the message itself
805    /// for fear of leaking it via proxy logging and instead would prefer to only transmit a hash of it.
806    ///
807    /// Since "External Mu" mode is well-defined by FIPS 204 and allowed by NIST, the mu value produced here
808    /// can be used with many hardware crypto modules.
809    ///
810    /// This "External Mu" mode of ML-DSA provides an alternative to the HashML-DSA algorithm in that it
811    /// allows the message to be externally pre-hashed, however, unlike HashML-DSA, this is merely an optimization
812    /// between the application holding the to-be-signed message and the cryptographic module holding the private key
813    /// -- in particular, while HashML-DSA requires the verifier to know whether ML-DSA or HashML-DSA was used to sign
814    /// the message, both "direct" ML-DSA and "External Mu" signatures can be verified with a standard
815    /// ML-DSA verifier.
816    ///
817    /// This function requires the public key hash `tr`, which can be computed from the public key
818    /// using [MLDSAPublicKeyTrait::compute_tr].
819    ///
820    /// For a streaming version of this, see [MuBuilder].
821    fn compute_mu_from_tr(
822        tr: &[u8; 64],
823        msg: &[u8],
824        ctx: Option<&[u8]>,
825    ) -> Result<[u8; 64], SignatureError> {
826        MuBuilder::compute_mu(tr, msg, ctx)
827    }
828    /// Same as [MLDSA::compute_mu_from_tr], but extracts tr from the public key.
829    fn compute_mu_from_pk(
830        pk: &PK,
831        msg: &[u8],
832        ctx: Option<&[u8]>,
833    ) -> Result<[u8; 64], SignatureError> {
834        MuBuilder::compute_mu(&pk.compute_tr(), msg, ctx)
835    }
836    /// Same as [MLDSA::compute_mu_from_tr], but extracts tr from the private key.
837    fn compute_mu_from_sk(
838        sk: &SK,
839        msg: &[u8],
840        ctx: Option<&[u8]>,
841    ) -> Result<[u8; 64], SignatureError> {
842        MuBuilder::compute_mu(&sk.tr(), msg, ctx)
843    }
844    /// Performs an ML-DSA signature using the provided external message representative `mu`.
845    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
846    /// FIPS 204 itself, as well as subsequent FAQ documents.
847    /// This mode uses randomized signing (called "hedged mode" in FIPS 204) using an internal RNG.
848    fn sign_mu(
849        sk: &SK,
850        mu: &[u8; 64],
851    ) -> Result<[u8; SIG_LEN], SignatureError> {
852        let mut out: [u8; SIG_LEN] = [0u8; SIG_LEN];
853        Self::sign_mu_out(sk, mu, &mut out)?;
854        Ok(out)
855    }
856    /// Performs an ML-DSA signature using the provided external message representative `mu`.
857    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
858    /// FIPS 204 itself, as well as subsequent FAQ documents.
859    /// This mode uses randomized signing (called "hedged mode" in FIPS 204) using an internal RNG.
860    ///
861    /// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer.
862    fn sign_mu_out(
863        sk: &SK,
864        mu: &[u8; 64],
865        output: &mut [u8; SIG_LEN],
866    ) -> Result<usize, SignatureError> {
867        let mut rnd: [u8; RND_LEN] = [0u8; RND_LEN];
868        HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?;
869
870        Self::sign_mu_deterministic_out(sk, mu, rnd, output)
871    }
872
873    fn sign_mu_deterministic(sk: &SK, mu: &[u8; 64], rnd: [u8; 32]) -> Result<[u8; SIG_LEN], SignatureError> {
874        let mut out = [0u8; SIG_LEN];
875        let bytes_written = Self::sign_mu_deterministic_out(sk, mu, rnd, &mut out)?;
876        debug_assert_eq!(bytes_written, SIG_LEN);
877        Ok(out)
878    }
879
880    fn sign_mu_deterministic_out(
881        sk: &SK,
882        mu: &[u8; 64],
883        rnd: [u8; 32],
884        output: &mut [u8; SIG_LEN],
885    ) -> Result<usize, SignatureError> {
886        // This function is a mash-up of keyGen (Algorithm 6) and sign (Algorithm 7),
887        // with a special emphasis on deriving values only as we need them, which in particular
888        // means that we'll process matrices and vectors row or component-wise.
889
890        // I have tried to keep this as clean as possible for correspondence with the FIPS,
891        // but I have moved things around so that I can use unnamed scopes to limit how many
892        // stack variables are alive at the same time.
893
894        // 1: (𝜌, 𝐾, π‘‘π‘Ÿ, 𝐬1, 𝐬2, 𝐭0) ← skDecode(π‘ π‘˜)
895        // to avoid having all of it in memory at the same time,
896        // we're gonna derive what we need as we need it.
897
898        // [Optimization Note]:
899        // s1 and s2 are normally part of the stored private key.
900        // We are going to need them many times through this function,
901        // so we'll compute them here and hold on to them in the compressed encoding specified in
902        // FIPS 204 Alg 17.
903        // We'll uncompress them as-needed, and only one polynomial at a time.
904        // You can avoid storing these in memory, but then all the sites where they are used
905        // will require calls to sk.compute_s1_row() and sk.compute_s2_row(), which are fairly expensive.
906        let s1_packed: [u8; S1_PACKED_LEN] = sk.compute_s1_packed();
907        let s2_packed: [u8; S2_PACKED_LEN] = sk.compute_s2_packed();
908
909        // 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀 β€², 64)
910        // skip: mu has already been provided
911
912        // Alg 7; 7: πœŒβ€³ ← H(𝐾||π‘Ÿπ‘›π‘‘||πœ‡, 64)
913        let rho_p_p: [u8; 64] = {
914            let mut h = H::new();
915            h.absorb(sk.K());
916            h.absorb(&rnd);
917            h.absorb(mu);
918            let mut rho_p_p = [0u8; 64];
919            h.squeeze_out(&mut rho_p_p);
920            rho_p_p
921        };
922
923        // 8: πœ… ← 0
924        //  β–· initialize counter πœ…
925        let mut kappa: u16 = 0;
926
927        let z_offset = LAMBDA_over_4;
928        let hint_offset = LAMBDA_over_4 + l * POLY_Z_PACKED_LEN;
929
930        loop {
931            // FIPS 204 s. 6.2 allows:
932            //   "Implementations may limit the number of iterations in this loop to not exceed a finite maximum value."
933            if kappa > 1000 * k as u16 {
934                return Err(SignatureError::GenericError(
935                    "Rejection sampling loop exceeded max iterations, try again with a different signing nonce.",
936                ));
937            }
938
939            // 11-15: derive c_tilde without materializing y_hat or w as full vectors.
940            let sig_val_c_tilde = { // scope for hash
941                let mut hash = H::new();
942                hash.absorb(mu);
943                for row in 0..k {
944                    let mut w = compute_w_row::<l, GAMMA1, GAMMA1_MASK_LEN>(&sk.rho(), &rho_p_p, kappa, row);
945                    w.high_bits::<GAMMA2>();
946                    hash.absorb(&w.w1_encode::<POLY_W1_PACKED_LEN>());
947                }
948                let mut sig_val_c_tilde = [0u8; LAMBDA_over_4];
949                hash.squeeze_out(&mut sig_val_c_tilde);
950                sig_val_c_tilde
951            };
952            // 16: 𝑐 ∈ π‘…π‘ž ← SampleInBall(c_tilde)
953            // 17: 𝑐_hat ← NTT(𝑐)
954            // optimization note: c_hat is used basically until the end, so we can't really scope it.
955            let mut c_hat = sample_in_ball::<LAMBDA_over_4, TAU>(&sig_val_c_tilde);
956            c_hat.ntt();
957
958            output.fill(0);
959            output[..LAMBDA_over_4].copy_from_slice(&sig_val_c_tilde);
960
961            let (z_chunks, z_remainder) = output[z_offset..z_offset + l * POLY_Z_PACKED_LEN]
962                .as_chunks_mut::<POLY_Z_PACKED_LEN>();
963            debug_assert_eq!(z_chunks.len(), l);
964            debug_assert_eq!(z_remainder.len(), 0);
965
966            // 18-23 (z path): compute and encode each z polynomial directly into the caller buffer.
967            let mut rejected = false;
968            for col in 0..l {
969                let z = match compute_z_component::<GAMMA1, GAMMA1_MASK_LEN, BETA>(
970                    // [Optimization Note]:
971                    // This is one of the places that a row of s1 can be re-computed instead of unpacked from the compressed form.
972                    // &sk.compute_s1_row(col),
973                    &s_unpack::<eta>(&s1_packed, col),
974                    &rho_p_p, &c_hat, kappa, col,
975                )? {
976                    Some(z) => z,
977                    None => {
978                        rejected = true;
979                        break;
980                    }
981                };
982
983                bitpack_gamma1::<POLY_Z_PACKED_LEN, GAMMA1>(&z, &mut z_chunks[col]);
984            }
985
986            if rejected {
987                kappa += l as u16;
988                continue;
989            }
990
991            // 19-28 (hint path): recompute rows as needed and write the packed hint directly.
992            let mut hint_count = 0usize;
993            for row in 0..k {
994                let mut w = compute_w_row::<l, GAMMA1, GAMMA1_MASK_LEN>(&sk.rho(), &rho_p_p, kappa, row);
995                let mut tmp =
996                    match compute_w0cs2_component::<GAMMA2, BETA>(
997                        // [Optimization Note]:
998                        // This is one of the places that a row of s1 can be re-computed instead of unpacked from the compressed form.
999                        // &sk.compute_s2_row(row),
1000                        &s_unpack::<eta>(&s2_packed, row),
1001                        &w, &c_hat) {
1002                        Some(tmp) => tmp,
1003                        None => {
1004                            rejected = true;
1005                            break;
1006                        }
1007                    };
1008
1009                let ct0 = match compute_ct0_component::<GAMMA2>(
1010                    // [Optimization Note]:
1011                    // This is one of the places that a row of s1 can be re-computed instead of unpacked from the compressed form.
1012                    // &sk.compute_t0_row(row), &c_hat) {
1013                    &sk.compute_t0_row(row, &s1_packed, &s2_packed), &c_hat) {
1014                    Some(ct0) => ct0,
1015                    None => {
1016                        rejected = true;
1017                        break;
1018                    }
1019                };
1020
1021                tmp.add_ntt(&ct0);
1022                tmp.conditional_add_q();
1023
1024                w.high_bits::<GAMMA2>();
1025                let (hint_row, weight) = tmp.make_hint_row::<GAMMA2>(&w);
1026                let next_hint_count = hint_count + weight as usize;
1027                if next_hint_count > OMEGA as usize {
1028                    rejected = true;
1029                    break;
1030                }
1031
1032                for idx in 0..N {
1033                    if hint_row.0[idx] != 0 {
1034                        output[hint_offset + hint_count] = idx as u8;
1035                        hint_count += 1;
1036                    }
1037                }
1038                debug_assert_eq!(hint_count, next_hint_count);
1039                output[hint_offset + OMEGA as usize + row] = hint_count as u8;
1040            }
1041
1042            if rejected {
1043                kappa += l as u16;
1044                continue;
1045            }
1046
1047            break;
1048        }
1049
1050        Ok(SIG_LEN)
1051    }
1052
1053    fn sign_mu_deterministic_from_seed(seed: &KeyMaterialSized<32>, mu: &[u8; 64], rnd: [u8; 32]) -> Result<[u8; SIG_LEN], SignatureError> {
1054        let mut out = [0u8; SIG_LEN];
1055        SK::from_keymaterial(&seed)?;
1056        Self::sign_mu_deterministic_out(&SK::from_keymaterial(&seed)?, mu, rnd, &mut out)?;
1057        Ok(out)
1058    }
1059
1060    fn sign_mu_deterministic_from_seed_out(seed: &KeyMaterialSized<32>, mu: &[u8; 64], rnd: [u8; 32], output: &mut [u8; SIG_LEN]) -> Result<usize, SignatureError> {
1061        SK::from_keymaterial(&seed)?;
1062        Self::sign_mu_deterministic_out(&SK::from_keymaterial(&seed)?, mu, rnd, output)
1063    }
1064
1065
1066    /// To be used for deterministic signing in conjunction with the [MLDSA44::sign_init], [MLDSA44::sign_update], and [MLDSA44::sign_final] flow.
1067    /// Can be set anywhere after [MLDSA44::sign_init] and before [MLDSA44::sign_final]
1068    fn set_signer_rnd(&mut self, rnd: [u8; 32]) {
1069        self.signer_rnd = Some(rnd);
1070    }
1071
1072    /// Alternative initialization of the streaming signer where you have your private key
1073    /// as a seed and you want to delay its expansion as late as possible for memory-usage reasons.
1074    fn sign_init_from_seed(seed: &KeyMaterialSized<32>, ctx: Option<&[u8]>) -> Result<Self, SignatureError> {
1075        let (_pk, sk) = Self::keygen_from_seed(seed)?;
1076        Ok(
1077            Self {
1078                _phantom: PhantomData,
1079                mu_builder: MuBuilder::do_init(&sk.tr(), ctx)?,
1080                signer_rnd: None,
1081                sk: None,
1082                seed: Some(seed.clone()),
1083                pk: None }
1084        )
1085    }
1086
1087    /// Algorithm 8 ML-DSA.Verify_internal(π‘π‘˜, 𝑀′, 𝜎)
1088    /// Internal function to verify a signature 𝜎 for a formatted message 𝑀′ .
1089    /// Input: Public key π‘π‘˜ ∈ 𝔹32+32π‘˜(bitlen (π‘žβˆ’1)βˆ’π‘‘) and message 𝑀′ ∈ {0, 1}βˆ— .
1090    /// Input: Signature 𝜎 ∈ π”Ήπœ†/4+β„“β‹…32β‹…(1+bitlen (𝛾1βˆ’1))+πœ”+π‘˜.
1091    fn verify_mu_internal(
1092        pk: &PK,
1093        mu: &[u8; 64],
1094        sig: &[u8; SIG_LEN],
1095    ) -> bool {
1096        // 1: (𝜌, 𝐭1) ← pkDecode(π‘π‘˜)
1097        // Already done -- the pk struct is already decoded
1098
1099        // 5: 𝐀 ← ExpandA(𝜌)
1100        //   β–· 𝐀 is generated and stored in NTT representation as 𝐀
1101        // We're gonna do this one row / polynomial at a time to reduce peak memory usage so that
1102        // the entirety of A_hat is never in memory at the same time.
1103
1104        // 6: π‘‘π‘Ÿ ← H(π‘π‘˜, 64)
1105        // 7: πœ‡ ← (H(BytesToBits(π‘‘π‘Ÿ)||𝑀 β€², 64))
1106        //   β–· message representative that may optionally be
1107        //     computed in a different cryptographic module
1108        // skip because this function is being handed mu
1109
1110        // 8: 𝑐 ∈ π‘…π‘ž ← SampleInBall(c_tilde)
1111        let c = sample_in_ball::<LAMBDA_over_4, TAU>(unpack_c_tilde(sig));
1112
1113        // 12: 𝑐_tilde_p ← H(πœ‡||w1Encode(𝐰1'), πœ†/4)
1114        // β–· hash it; this should match 𝑐_tilde
1115        let mut hash = H::new();
1116        hash.absorb(mu);
1117
1118        for row in 0..k {
1119            let mut wp_approx = match {
1120                // 9: 𝐰′_approx ← NTTβˆ’1(𝐀_hat ∘ NTT(𝐳) βˆ’ NTT(𝑐) ∘ NTT(𝐭1 β‹… 2^𝑑))
1121                compute_wp_approx_row::<GAMMA1, BETA, l, POLY_Z_PACKED_LEN, LAMBDA_over_4, SIG_LEN>(
1122                    pk.rho(),
1123                    sig,
1124                    &pk.unpack_t1_row(row),
1125                    &c,
1126                    row)
1127            } {
1128                Ok(wp_approx) => wp_approx,
1129                // means the norm check on z failed
1130                Err(_) => return false,
1131            };
1132
1133            let h_i
1134                = match unpack_h_row::<GAMMA1, k, l, OMEGA, LAMBDA_over_4, POLY_Z_PACKED_LEN, SIG_LEN>
1135                        (row, &sig) {
1136                    Some(h_i) => h_i,
1137                    // means there were more than OMEGA bits set in the hint
1138                    None => return false,
1139            };
1140
1141            // 10: 𝐰1β€² ← UseHint(𝐑, 𝐰'_approx)
1142            // β–· reconstruction of signer’s commitment
1143            wp_approx.use_hint::<GAMMA2>(&h_i);
1144            hash.absorb(&wp_approx.w1_encode::<POLY_W1_PACKED_LEN>());
1145        }
1146
1147        let mut c_tilde_p = [0u8; LAMBDA_over_4];
1148        hash.squeeze_out(&mut c_tilde_p);
1149
1150        // verification probably doesn't technically need to be constant-time, but why not?
1151        // 13 (second half): return [[ ||𝐳||∞ < 𝛾1 βˆ’ 𝛽]] and [[𝑐 Μƒ = 𝑐′ ]]
1152        //   note: the first half of this check (the norm check) is buried in unpack_z_row(),
1153        //         which is called from compute_wp_approx_row()
1154        bouncycastle_utils::ct::ct_eq_bytes(unpack_c_tilde::<LAMBDA_over_4>(sig), &c_tilde_p)
1155    }
1156}
1157
1158/// Trait for all three of the ML-DSA algorithm variants.
1159pub trait MLDSATrait<
1160    const PK_LEN: usize,
1161    const SK_LEN: usize,
1162    const SIG_LEN: usize,
1163    PK: MLDSAPublicKeyTrait<k, T1_PACKED_LEN, PK_LEN> + MLDSAPublicKeyInternalTrait<k, T1_PACKED_LEN, PK_LEN>,
1164    SK: MLDSAPrivateKeyTrait<k, l, S1_PACKED_LEN, S2_PACKED_LEN, T1_PACKED_LEN, PK_LEN, SK_LEN>
1165        + MLDSAPrivateKeyInternalTrait<LAMBDA, GAMMA2, k, l, ETA, S1_PACKED_LEN, S2_PACKED_LEN, PK_LEN, SK_LEN>,
1166    const LAMBDA: i32,
1167    const GAMMA2: i32,
1168    const k: usize,
1169    const l: usize,
1170    const S1_PACKED_LEN: usize,
1171    const S2_PACKED_LEN: usize,
1172    const T1_PACKED_LEN: usize,
1173    const ETA: usize
1174> : Sized {
1175    /// Imports a secret key from a seed.
1176    fn keygen_from_seed(seed: &KeyMaterialSized<32>) -> Result<(PK, SK), SignatureError>;
1177    /// Imports a secret key from both a seed and an encoded_sk.
1178    ///
1179    /// This is a convenience function to expand the key from seed and compare it against
1180    /// the provided `encoded_sk` using a constant-time equality check.
1181    /// If everything checks out, the secret key is returned fully populated with pk and seed.
1182    /// If the provided key and derived key don't match, an error is returned.
1183    fn keygen_from_seed_and_encoded(
1184        seed: &KeyMaterialSized<32>,
1185        encoded_sk: &[u8; SK_LEN],
1186    ) -> Result<
1187        (PK, SK),
1188        SignatureError,
1189    >;
1190    /// Given a public key and a secret key, check that the public key matches the secret key.
1191    /// This is a sanity check that the public key was generated correctly from the secret key.
1192    ///
1193    /// At the current time, this is only possible if `sk` either contains a public key (in which case
1194    /// the two pk's are encoded and compared for byte equality), or if `sk` contains a seed
1195    /// (in which case a keygen_from_seed is run and then the pk's compared).
1196    ///
1197    /// Returns either `()` or [SignatureError::ConsistencyCheckFailed].
1198    fn keypair_consistency_check(
1199        pk: &PK,
1200        sk: &SK,
1201    ) -> Result<(), SignatureError>;
1202    /// This provides the first half of the "External Mu" interface to ML-DSA which is described
1203    /// in, and allowed under, NIST's FAQ that accompanies FIPS 204.
1204    ///
1205    /// This function, together with [MLDSATrait::sign_mu] perform a complete ML-DSA signature which is indistinguishable
1206    /// from one produced by the one-shot sign APIs.
1207    ///
1208    /// The utility of this function is exactly as described
1209    /// on Line 6 of Algorithm 7 of FIPS 204:
1210    ///
1211    ///    message representative that may optionally be computed in a different cryptographic module
1212    ///
1213    /// The utility is when an extremely large message needs to be signed, where the message exists on one
1214    /// computing system and the private key to sign it is held on another and either the transfer time or bandwidth
1215    /// causes operational concerns (this is common for example with network HSMs or sending large messages
1216    /// to be signed by a smartcard communicating over near-field radio). Another use case is if the
1217    /// contents of the message are sensitive and the signer does not want to transmit the message itself
1218    /// for fear of leaking it via proxy logging and instead would prefer to only transmit a hash of it.
1219    ///
1220    /// Since "External Mu" mode is well-defined by FIPS 204 and allowed by NIST, the mu value produced here
1221    /// can be used with many hardware crypto modules.
1222    ///
1223    /// This "External Mu" mode of ML-DSA provides an alternative to the HashML-DSA algorithm in that it
1224    /// allows the message to be externally pre-hashed, however, unlike HashML-DSA, this is merely an optimization
1225    /// between the application holding the to-be-signed message and the cryptographic module holding the private key
1226    /// -- in particular, while HashML-DSA requires the verifier to know whether ML-DSA or HashML-DSA was used to sign
1227    /// the message, both "direct" ML-DSA and "External Mu" signatures can be verified with a standard
1228    /// ML-DSA verifier.
1229    ///
1230    /// This function requires the public key hash `tr`, which can be computed from the public key
1231    /// using [MLDSAPublicKeyTrait::compute_tr].
1232    ///
1233    /// For a streaming version of this, see [MuBuilder].
1234    fn compute_mu_from_tr(
1235        tr: &[u8; 64],
1236        msg: &[u8],
1237        ctx: Option<&[u8]>,
1238    ) -> Result<[u8; 64], SignatureError>;
1239    /// Same as [MLDSATrait::compute_mu_from_tr], but extracts tr from the public key.
1240    fn compute_mu_from_pk(
1241        pk: &PK,
1242        msg: &[u8],
1243        ctx: Option<&[u8]>,
1244    ) -> Result<[u8; 64], SignatureError>;
1245    /// Same as [MLDSATrait::compute_mu_from_tr], but extracts tr from the private key.
1246    fn compute_mu_from_sk(
1247        sk: &SK,
1248        msg: &[u8],
1249        ctx: Option<&[u8]>,
1250    ) -> Result<[u8; 64], SignatureError>;
1251    /// Performs an ML-DSA signature using the provided external message representative `mu`.
1252    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
1253    /// FIPS 204 itself, as well as subsequent FAQ documents.
1254    /// This mode uses randomized signing (called "hedged mode" in FIPS 204) using an internal RNG.
1255    fn sign_mu(
1256        sk: &SK,
1257        mu: &[u8; 64],
1258    ) -> Result<[u8; SIG_LEN], SignatureError>;
1259    /// Performs an ML-DSA signature using the provided external message representative `mu`.
1260    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
1261    /// FIPS 204 itself, as well as subsequent FAQ documents.
1262    /// This mode uses randomized signing (called "hedged mode" in FIPS 204) using an internal RNG.
1263    ///
1264    /// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer.
1265    fn sign_mu_out(
1266        sk: &SK,
1267        mu: &[u8; 64],
1268        output: &mut [u8; SIG_LEN],
1269    ) -> Result<usize, SignatureError>;
1270    /// Algorithm 7 ML-DSA.Sign_internal(π‘ π‘˜, 𝑀′, π‘Ÿπ‘›π‘‘)
1271    /// (modified to take an externally-computed mu instead of M')
1272    ///
1273    /// Performs an ML-DSA signature using the provided external message representative `mu`.
1274    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
1275    /// FIPS 204 itself, as well as subsequent FAQ documents.
1276    ///
1277    /// This mode exposes the signing nonce `rnd` either for users who wish to source the signing
1278    /// nonce from a source other than the library's default internal RNG, or who wish to use the
1279    /// "deterministic mode" defined in FIPS 204 by providing `rnd = [0u8; 32]`.
1280    /// In order to help prevent against accidental nonce reuse, this function moves `rnd` instead
1281    /// of taking it by reference.
1282    ///
1283    /// Security note about deterministic mode:
1284    /// This mode exposes deterministic signing (called "hedged mode" and allowed by FIPS 204).
1285    /// The ML-DSA algorithm is considered safe to use in deterministic mode, but be aware that
1286    /// the responsibility is on you to ensure that your nonce `rnd` is unique per signature.
1287    /// If not, you may lose some privacy properties; for example, it becomes easy to tell if a signer
1288    /// has signed the same message twice or two different messages, or to tell if the same message
1289    /// has been signed by the same signer twice or two different signers.
1290    fn sign_mu_deterministic(
1291        sk: &SK,
1292        mu: &[u8; 64],
1293        rnd: [u8; 32],
1294    ) -> Result<[u8; SIG_LEN], SignatureError>;
1295    /// Algorithm 7 ML-DSA.Sign_internal(π‘ π‘˜, 𝑀′, π‘Ÿπ‘›π‘‘)
1296    /// (modified to take an externally-computed mu instead of M')
1297    ///
1298    /// Performs an ML-DSA signature using the provided external message representative `mu`.
1299    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
1300    /// FIPS 204 itself, as well as subsequent FAQ documents.
1301    /// This mode exposes deterministic signing (called "hedged mode" in FIPS 204) using an internal RNG.
1302    ///
1303    /// This mode exposes the signing nonce `rnd` either for users who wish to source the signing
1304    /// nonce from a source other than the library's default internal RNG, or who wish to use the
1305    /// "deterministic mode" defined in FIPS 204 by providing `rnd = [0u8; 32]`.
1306    /// In order to help prevent against accidental nonce reuse, this function moves `rnd` instead
1307    /// of taking it by reference.
1308    ///
1309    /// Security note about deterministic mode:
1310    /// This mode exposes deterministic signing (called "hedged mode" and allowed by FIPS 204).
1311    /// The ML-DSA algorithm is considered safe to use in deterministic mode, but be aware that
1312    /// the responsibility is on you to ensure that your nonce `rnd` is unique per signature.
1313    /// If not, you may lose some privacy properties; for example, it becomes easy to tell if a signer
1314    /// has signed the same message twice or two different messages, or to tell if the same message
1315    /// has been signed by the same signer twice or two different signers.
1316    ///
1317    /// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer.
1318    fn sign_mu_deterministic_out(
1319        sk: &SK,
1320        mu: &[u8; 64],
1321        rnd: [u8; 32],
1322        output: &mut [u8; SIG_LEN],
1323    ) -> Result<usize, SignatureError>;
1324    /// This contains a heavily-optimized combined keygen() and sign() which greatly reduces peak
1325    /// memory usage by never having the full secret key in memory at the same time,
1326    /// and by deriving intermediate values piece-wise as needed.
1327    fn sign_mu_deterministic_from_seed(
1328        seed: &KeyMaterialSized<32>,
1329        mu: &[u8; 64],
1330        rnd: [u8; 32],
1331    ) -> Result<[u8; SIG_LEN], SignatureError>;
1332    /// This contains a heavily-optimized combined keygen() and sign() which greatly reduces peak
1333    /// memory usage by never having the full secret key in memory at the same time,
1334    /// and by deriving intermediate values piece-wise as needed.
1335    fn sign_mu_deterministic_from_seed_out(
1336        seed: &KeyMaterialSized<32>,
1337        mu: &[u8; 64],
1338        rnd: [u8; 32],
1339        output: &mut [u8; SIG_LEN],
1340    ) -> Result<usize, SignatureError>;
1341    /// To be used for deterministic signing in conjunction with the [MLDSA44::sign_init], [MLDSA44::sign_update], and [MLDSA44::sign_final] flow.
1342    /// Can be set anywhere after [MLDSA44::sign_init] and before [MLDSA44::sign_final]
1343    fn set_signer_rnd(&mut self, rnd: [u8; 32]);
1344    /// An alternate way to start the streaming signing mode by providing a private key seed instead of an expanded private key
1345    fn sign_init_from_seed(seed: &KeyMaterialSized<32>, ctx: Option<&[u8]>) -> Result<Self, SignatureError>;
1346    /// Algorithm 8 ML-DSA.Verify_internal(π‘π‘˜, 𝑀′, 𝜎)
1347    /// Internal function to verify a signature 𝜎 for a formatted message 𝑀′ .
1348    /// Input: Public key π‘π‘˜ ∈ 𝔹32+32π‘˜(bitlen (π‘žβˆ’1)βˆ’π‘‘) and message 𝑀′ ∈ {0, 1}βˆ— .
1349    /// Input: Signature 𝜎 ∈ π”Ήπœ†/4+β„“β‹…32β‹…(1+bitlen (𝛾1βˆ’1))+πœ”+π‘˜.
1350    fn verify_mu_internal(
1351        pk: &PK,
1352        mu: &[u8; 64],
1353        sig: &[u8; SIG_LEN],
1354    ) -> bool;
1355}
1356
1357impl<
1358    const PK_LEN: usize,
1359    const SK_LEN: usize,
1360    const SIG_LEN: usize,
1361    PK: MLDSAPublicKeyTrait<k, T1_PACKED_LEN, PK_LEN> + MLDSAPublicKeyInternalTrait<k, T1_PACKED_LEN, PK_LEN>,
1362    SK: MLDSAPrivateKeyTrait<k, l, S1_PACKED_LEN, S2_PACKED_LEN, T1_PACKED_LEN, PK_LEN, SK_LEN>
1363        + MLDSAPrivateKeyInternalTrait<LAMBDA, GAMMA2, k, l, ETA, S1_PACKED_LEN, S2_PACKED_LEN, PK_LEN, SK_LEN>,
1364    const TAU: i32,
1365    const LAMBDA: i32,
1366    const GAMMA1: i32,
1367    const GAMMA2: i32,
1368    const k: usize,
1369    const l: usize,
1370    const ETA: usize,
1371    const BETA: i32,
1372    const OMEGA: i32,
1373    const C_TILDE: usize,
1374    const POLY_Z_PACKED_LEN: usize,
1375    const POLY_W1_PACKED_LEN: usize,
1376    const S1_PACKED_LEN: usize,
1377    const S2_PACKED_LEN: usize,
1378    const T1_PACKED_LEN: usize,
1379    const POLY_ETA_PACKED_LEN: usize,
1380    const LAMBDA_over_4: usize,
1381    const GAMMA1_MASK_LEN: usize,
1382> Signature<PK, SK> for MLDSA<
1383    PK_LEN,
1384    SK_LEN,
1385    SIG_LEN,
1386    PK,
1387    SK,
1388    TAU,
1389    LAMBDA,
1390    GAMMA1,
1391    GAMMA2,
1392    k,
1393    l,
1394    ETA,
1395    BETA,
1396    OMEGA,
1397    C_TILDE,
1398    POLY_Z_PACKED_LEN,
1399    POLY_W1_PACKED_LEN,
1400    S1_PACKED_LEN,
1401    S2_PACKED_LEN,
1402    T1_PACKED_LEN,
1403    POLY_ETA_PACKED_LEN,
1404    LAMBDA_over_4,
1405    GAMMA1_MASK_LEN,
1406> {
1407
1408    fn keygen() -> Result<(PK, SK), SignatureError> {
1409        Self::keygen_from_os_rng()
1410    }
1411
1412    fn sign(sk: &SK, msg: &[u8], ctx: Option<&[u8]>) -> Result<Vec<u8>, SignatureError> {
1413        let mut out = vec![0u8; SIG_LEN];
1414        Self::sign_out(sk, msg, ctx, &mut out)?;
1415
1416        Ok(out)
1417    }
1418
1419    fn sign_out(sk: &SK, msg: &[u8], ctx: Option<&[u8]>, output: &mut [u8]) -> Result<usize, SignatureError> {
1420        let mu = MuBuilder::compute_mu(&sk.tr(), msg, ctx)?;
1421        if output.len() < SIG_LEN { return Err(SignatureError::LengthError("Output buffer insufficient size to hold signature")) }
1422        let output_sized: &mut [u8; SIG_LEN] = output[..SIG_LEN].as_mut().try_into().unwrap();
1423        let bytes_written = Self::sign_mu_out(sk, &mu, output_sized)?;
1424
1425        Ok(bytes_written)
1426    }
1427
1428    fn sign_init(sk: &SK, ctx: Option<&[u8]>) -> Result<Self, SignatureError> {
1429        Ok(
1430            Self {
1431                _phantom: PhantomData,
1432                mu_builder: MuBuilder::do_init(&sk.tr(), ctx)?,
1433                signer_rnd: None,
1434                sk: Some(sk.clone()),
1435                seed: None,
1436                pk: None }
1437        )
1438    }
1439
1440    fn sign_update(&mut self, msg_chunk: &[u8]) {
1441        self.mu_builder.do_update(msg_chunk);
1442    }
1443
1444    fn sign_final(self) -> Result<Vec<u8>, SignatureError> {
1445        let mut out = [0u8; SIG_LEN];
1446        self.sign_final_out(&mut out)?;
1447        Ok(Vec::from(out))
1448    }
1449
1450    fn sign_final_out(self, output: &mut [u8]) -> Result<usize, SignatureError> {
1451        let mu = self.mu_builder.do_final();
1452
1453        if self.sk.is_none() && self.seed.is_none() {
1454            return Err(SignatureError::GenericError("Somehow you managed to construct a streaming signer without a private key, impressive!"))
1455        }
1456
1457        if output.len() < SIG_LEN { return Err(SignatureError::LengthError("Output buffer insufficient size to hold signature")) }
1458        let output_sized: &mut [u8; SIG_LEN] = output[..SIG_LEN].as_mut().try_into().unwrap();
1459
1460        if self.sk.is_some() {
1461            if self.signer_rnd.is_none() {
1462                Self::sign_mu_out(&self.sk.unwrap(), &mu, output_sized)
1463            } else {
1464                Self::sign_mu_deterministic_out(&self.sk.unwrap(), &mu, self.signer_rnd.unwrap(), output_sized)
1465            }
1466        } else if self.seed.is_some() {
1467            let rnd = if self.signer_rnd.is_some() {
1468                self.signer_rnd.unwrap()
1469            } else {
1470                let mut rnd: [u8; RND_LEN] = [0u8; RND_LEN];
1471                HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?;
1472                rnd
1473            };
1474            Self::sign_mu_deterministic_from_seed_out(&self.seed.unwrap(), &mu, rnd, output_sized)
1475        } else { unreachable!() }
1476    }
1477
1478    fn verify(pk: &PK, msg: &[u8], ctx: Option<&[u8]>, sig: &[u8]) -> Result<(), SignatureError> {
1479        let mu = MuBuilder::compute_mu(&pk.compute_tr(), msg, ctx)?;
1480
1481        if sig.len() != SIG_LEN { return Err(SignatureError::LengthError("Signature value is not the correct length.")) }
1482        if Self::verify_mu_internal(pk, &mu, &sig[..SIG_LEN].try_into().unwrap()) {
1483            Ok(())
1484        } else {
1485            Err(SignatureError::SignatureVerificationFailed)
1486        }
1487    }
1488
1489    fn verify_init(pk: &PK, ctx: Option<&[u8]>) -> Result<Self, SignatureError> {
1490        Ok(
1491            Self {
1492                _phantom: Default::default(),
1493                mu_builder: MuBuilder::do_init(&pk.compute_tr(), ctx)?,
1494                signer_rnd: None,
1495                sk: None,
1496                seed: None,
1497                pk: Some(pk.clone()) }
1498        )
1499    }
1500
1501    fn verify_update(&mut self, msg_chunk: &[u8]) {
1502        self.mu_builder.do_update(msg_chunk);
1503    }
1504
1505    fn verify_final(self, sig: &[u8]) -> Result<(), SignatureError> {
1506        let mu = self.mu_builder.do_final();
1507
1508        assert!(self.pk.is_some(), "Somehow you managed to construct a streaming verifier without a public key, impressive!");
1509
1510        if sig.len() != SIG_LEN { return Err(SignatureError::LengthError("Signature value is not the correct length.")) }
1511        if Self::verify_mu_internal(&self.pk.unwrap(), &mu, &sig[..SIG_LEN].try_into().unwrap()) {
1512            Ok(())
1513        } else {
1514            Err(SignatureError::SignatureVerificationFailed)
1515        }
1516    }
1517}
1518
1519
1520/// Implements parts of Algorithm 2 and Line 6 of Algorithm 7 of FIPS 204.
1521/// Provides a stateful version of [MLDSATrait::compute_mu_from_pk] and [MLDSATrait::compute_mu_from_tr]
1522/// that supports streaming
1523/// large to-be-signed messages.
1524///
1525/// Note: this struct is only exposed for "pure" ML-DSA and not for HashML-DSA because HashML-DSA
1526/// does not benefit from allowing external construction of the message representative mu.
1527/// You can get the same behaviour by computing the pre-hash `ph` with the appropriate hash function
1528/// and providing that to HashMLDSA via [PHSignature::sign_ph].
1529pub struct MuBuilder {
1530    h: H,
1531}
1532
1533impl MuBuilder {
1534    /// Algorithm 7
1535    /// 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀′, 64)
1536    pub fn compute_mu(tr: &[u8; 64],msg: &[u8], ctx: Option<&[u8]>) -> Result<[u8; 64], SignatureError> {
1537        let mut mu_builder = MuBuilder::do_init(&tr, ctx)?;
1538        mu_builder.do_update(msg);
1539        let mu = mu_builder.do_final();
1540
1541        Ok(mu)
1542    }
1543
1544    /// This function requires the public key hash `tr`, which can be computed from the public key
1545    /// using [MLDSAPublicKeyTrait::compute_tr].
1546    pub fn do_init(tr: &[u8; 64], ctx: Option<&[u8]>) -> Result<Self, SignatureError> {
1547        let ctx = match ctx { Some(ctx) => ctx, None => &[] };
1548
1549        // Algorithm 2
1550        // 1: if |𝑐𝑑π‘₯| > 255 then
1551        if ctx.len() > 255 {
1552            return Err(SignatureError::LengthError("ctx value is longer than 255 bytes"));
1553        }
1554
1555        // Algorithm 7
1556        // 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀', 64)
1557        let mut mb = Self { h: H::new() };
1558        mb.h.absorb(tr);
1559
1560        // Algorithm 2
1561        // 10: 𝑀′ ← BytesToBits(IntegerToBytes(0, 1) βˆ₯ IntegerToBytes(|𝑐𝑑π‘₯|, 1) βˆ₯ 𝑐𝑑π‘₯) βˆ₯ 𝑀
1562        // all done together
1563        mb.h.absorb(&[0u8]);
1564        mb.h.absorb(&[ctx.len() as u8]);
1565        mb.h.absorb(ctx);
1566
1567        // now ready to absorb M
1568        Ok(mb)
1569    }
1570
1571    /// Stream a chunk of the message.
1572    pub fn do_update(&mut self, msg_chunk: &[u8]) {
1573        self.h.absorb(msg_chunk);
1574    }
1575
1576    /// Finalize and return the mu value.
1577    pub fn do_final(mut self) -> [u8; 64] {
1578        // Completion of
1579        // Algorithm 7
1580        // 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀 β€², 64)
1581        let mut mu = [0u8; 64];
1582        self.h.squeeze_out(&mut mu);
1583
1584        mu
1585    }
1586}