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