Skip to main content

bouncycastle_mldsa/
mldsa.rs

1//! This page documents advanced features of the Module Lattice Digital Signature Algorithm (ML-DSA)
2//! available in this crate.
3//!
4//!
5//! # Streaming APIs
6//!
7//! Sometimes the message you need to sign or verify is too big to fit in device memory all at once.
8//! No worries, we got you covered!
9//!
10//! ```rust
11//! use bouncycastle_core_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 std::marker::PhantomData;
313use crate::aux_functions::{expand_mask, expandA, expandS, make_hint_vecs, ntt, power_2_round_vec, sample_in_ball, sig_encode, sig_decode, use_hint_vecs};
314use crate::matrix::Vector;
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::{
320    KeyMaterial, KeyMaterial256, KeyMaterialSized, KeyType,
321};
322use bouncycastle_core_interface::traits::{RNG, SecurityStrength, XOF, Signature, Algorithm};
323use bouncycastle_rng::{HashDRBG_SHA512};
324use bouncycastle_sha3::{SHAKE128, SHAKE256};
325
326
327// imports needed just for docs
328#[allow(unused_imports)]
329use bouncycastle_core_interface::traits::PHSignature;
330#[allow(unused_imports)] 
331use crate::hash_mldsa;
332
333/*** Constants ***/
334
335///
336pub const ML_DSA_44_NAME: &str = "ML-DSA-44";
337///
338pub const ML_DSA_65_NAME: &str = "ML-DSA-65";
339///
340pub const ML_DSA_87_NAME: &str = "ML-DSA-87";
341
342// From FIPS 204 Table 1 and Table 2
343
344// Constants that are the same for all parameter sets
345pub(crate) const N: usize = 256;
346pub(crate) const q: i32 = 8380417;
347pub(crate) const q_inv: i32 = 58728449; // q ^ (-1) mod 2 ^32
348pub(crate) const d: i32 = 13;
349/// Length of the \[u8] holding an ML-DSA seed value.
350pub const SEED_LEN: usize = 32;
351/// Length of the \[u8] holding an ML-DSA signing random value.
352pub const RND_LEN: usize = 32;
353/// Length of the \[u8] holding an ML-DSA tr value (which is the SHAKE256 hash of the public key).
354pub const TR_LEN: usize = 64;
355/// Length of the \[u8] holding an ML-DSA mu value.
356pub const MU_LEN: usize = 64;
357pub(crate) const POLY_T1PACKED_LEN: usize = 320;
358pub(crate) const POLY_T0PACKED_LEN: usize = 416;
359
360
361/* ML-DSA-44 params */
362
363/// Length of the \[u8] holding a ML-DSA-44 public key.
364pub const MLDSA44_PK_LEN: usize = 1312;
365/// Length of the \[u8] holding a ML-DSA-44 private key.
366pub const MLDSA44_SK_LEN: usize = 2560;
367/// Length of the \[u8] holding a ML-DSA-44 signature value.
368pub const MLDSA44_SIG_LEN: usize = 2420;
369pub(crate) const MLDSA44_TAU: i32 = 39;
370pub(crate) const MLDSA44_LAMBDA: i32 = 128;
371pub(crate) const MLDSA44_GAMMA1: i32 = 1 << 17;
372pub(crate) const MLDSA44_GAMMA2: i32 = (q - 1) / 88;
373pub(crate) const MLDSA44_k: usize = 4;
374pub(crate) const MLDSA44_l: usize = 4;
375pub(crate) const MLDSA44_ETA: usize = 2;
376pub(crate) const MLDSA44_BETA: i32 = 78;
377pub(crate) const MLDSA44_OMEGA: i32 = 80;
378
379// Useful derived values
380pub(crate) const MLDSA44_C_TILDE: usize = 32;
381pub(crate) const MLDSA44_POLY_Z_PACKED_LEN: usize = 576;
382pub(crate) const MLDSA44_POLY_W1_PACKED_LEN: usize = 192;
383pub(crate) const MLDSA44_W1_PACKED_LEN: usize = MLDSA44_k * MLDSA44_POLY_W1_PACKED_LEN;
384pub(crate) const MLDSA44_POLY_ETA_PACKED_LEN: usize = 32*3;
385pub(crate) const MLDSA44_LAMBDA_over_4: usize = 128/4;
386
387// Alg 32
388// 1: 𝑐 ← 1 + bitlen (𝛾1 βˆ’ 1)
389pub(crate) const MLDSA44_GAMMA1_MASK_LEN: usize = 576;  // 32*(1 + bitlen (𝛾1 βˆ’ 1) )
390
391
392/* ML-DSA-65 params */
393
394/// Length of the \[u8] holding a ML-DSA-65 public key.
395pub const MLDSA65_PK_LEN: usize = 1952;
396/// Length of the \[u8] holding a ML-DSA-65 private key.
397pub const MLDSA65_SK_LEN: usize = 4032;
398/// Length of the \[u8] holding a ML-DSA-65 signature value.
399pub const MLDSA65_SIG_LEN: usize = 3309;
400pub(crate) const MLDSA65_TAU: i32 = 49;
401pub(crate) const MLDSA65_LAMBDA: i32 = 192;
402pub(crate) const MLDSA65_GAMMA1: i32 = 1 << 19;
403pub(crate) const MLDSA65_GAMMA2: i32 = (q - 1) / 32;
404pub(crate) const MLDSA65_k: usize = 6;
405pub(crate) const MLDSA65_l: usize = 5;
406pub(crate) const MLDSA65_ETA: usize = 4;
407pub(crate) const MLDSA65_BETA: i32 = 196;
408pub(crate) const MLDSA65_OMEGA: i32 = 55;
409
410// Useful derived values
411pub(crate) const MLDSA65_C_TILDE: usize = 48;
412pub(crate) const MLDSA65_POLY_Z_PACKED_LEN: usize = 640;
413pub(crate) const MLDSA65_POLY_W1_PACKED_LEN: usize = 128;
414pub(crate) const MLDSA65_W1_PACKED_LEN: usize = MLDSA65_k * MLDSA65_POLY_W1_PACKED_LEN;
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.
429pub const MLDSA87_SK_LEN: usize = 4896;
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_W1_PACKED_LEN: usize = MLDSA87_k * MLDSA87_POLY_W1_PACKED_LEN;
447pub(crate) const MLDSA87_POLY_ETA_PACKED_LEN: usize = 32*3;
448pub(crate) const MLDSA87_LAMBDA_over_4: usize = 256/4;
449
450// Alg 32
451// 1: 𝑐 ← 1 + bitlen (𝛾1 βˆ’ 1)
452pub(crate) const MLDSA87_GAMMA1_MASK_LEN: usize = 640;
453
454
455
456// Typedefs just to make the algorithms look more like the FIPS 204 sample code.
457pub(crate) type H = SHAKE256;
458pub(crate) type G = SHAKE128;
459
460
461/*** Pub Types ***/
462
463/// The ML-DSA-44 algorithm.
464pub type MLDSA44 = MLDSA<
465    MLDSA44_PK_LEN,
466    MLDSA44_SK_LEN,
467    MLDSA44_SIG_LEN,
468    MLDSA44PublicKey,
469    MLDSA44PrivateKey,
470    MLDSA44_TAU,
471    MLDSA44_LAMBDA,
472    MLDSA44_GAMMA1,
473    MLDSA44_GAMMA2,
474    MLDSA44_k,
475    MLDSA44_l,
476    MLDSA44_ETA,
477    MLDSA44_BETA,
478    MLDSA44_OMEGA,
479    MLDSA44_C_TILDE,
480    MLDSA44_POLY_Z_PACKED_LEN,
481    MLDSA44_POLY_W1_PACKED_LEN,
482    MLDSA44_W1_PACKED_LEN,
483    MLDSA44_POLY_ETA_PACKED_LEN,
484    MLDSA44_LAMBDA_over_4,
485    MLDSA44_GAMMA1_MASK_LEN,
486>;
487
488impl Algorithm for MLDSA44 {
489    const ALG_NAME: &'static str = ML_DSA_44_NAME;
490    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit;
491}
492
493/// The ML-DSA-65 algorithm.
494pub type MLDSA65 = MLDSA<
495    MLDSA65_PK_LEN,
496    MLDSA65_SK_LEN,
497    MLDSA65_SIG_LEN,
498    MLDSA65PublicKey,
499    MLDSA65PrivateKey,
500    MLDSA65_TAU,
501    MLDSA65_LAMBDA,
502    MLDSA65_GAMMA1,
503    MLDSA65_GAMMA2,
504    MLDSA65_k,
505    MLDSA65_l,
506    MLDSA65_ETA,
507    MLDSA65_BETA,
508    MLDSA65_OMEGA,
509    MLDSA65_C_TILDE,
510    MLDSA65_POLY_Z_PACKED_LEN,
511    MLDSA65_POLY_W1_PACKED_LEN,
512    MLDSA65_W1_PACKED_LEN,
513    MLDSA65_POLY_ETA_PACKED_LEN,
514    MLDSA65_LAMBDA_over_4,
515    MLDSA65_GAMMA1_MASK_LEN,
516>;
517
518impl Algorithm for MLDSA65 {
519    const ALG_NAME: &'static str = ML_DSA_65_NAME;
520    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit;
521}
522
523/// The ML-DSA-87 algorithm.
524pub type MLDSA87 = MLDSA<
525    MLDSA87_PK_LEN,
526    MLDSA87_SK_LEN,
527    MLDSA87_SIG_LEN,
528    MLDSA87PublicKey,
529    MLDSA87PrivateKey,
530    MLDSA87_TAU,
531    MLDSA87_LAMBDA,
532    MLDSA87_GAMMA1,
533    MLDSA87_GAMMA2,
534    MLDSA87_k,
535    MLDSA87_l,
536    MLDSA87_ETA,
537    MLDSA87_BETA,
538    MLDSA87_OMEGA,
539    MLDSA87_C_TILDE,
540    MLDSA87_POLY_Z_PACKED_LEN,
541    MLDSA87_POLY_W1_PACKED_LEN,
542    MLDSA87_W1_PACKED_LEN,
543    MLDSA87_POLY_ETA_PACKED_LEN,
544    MLDSA87_LAMBDA_over_4,
545    MLDSA87_GAMMA1_MASK_LEN,
546>;
547
548impl Algorithm for MLDSA87 {
549    const ALG_NAME: &'static str = ML_DSA_87_NAME;
550    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit;
551}
552
553/// The core internal implementation of the ML-DSA algorithm.
554/// This needs to be public for the compiler to be able to find it, but you shouldn't ever
555/// need to use this directly. Please use the named public types.
556pub struct MLDSA<
557    const PK_LEN: usize,
558    const SK_LEN: usize,
559    const SIG_LEN: usize,
560    PK: MLDSAPublicKeyTrait<k, PK_LEN> + MLDSAPublicKeyInternalTrait<k, PK_LEN>,
561    SK: MLDSAPrivateKeyTrait<k, l, ETA, SK_LEN, PK_LEN> + MLDSAPrivateKeyInternalTrait<k, l, ETA, SK_LEN, PK_LEN>,
562    const TAU: i32,
563    const LAMBDA: i32,
564    const GAMMA1: i32,
565    const GAMMA2: i32,
566    const k: usize,
567    const l: usize,
568    const ETA: usize,
569    const BETA: i32,
570    const OMEGA: i32,
571    const C_TILDE: usize,
572    const POLY_VEC_H_PACKED_LEN: usize,
573    const POLY_W1_PACKED_LEN: usize,
574    const W1_PACKED_LEN: usize,
575    const POLY_ETA_PACKED_LEN: usize,
576    const LAMBDA_over_4: usize,
577    const GAMMA1_MASK_LEN: usize,
578> {
579    _phantom: PhantomData<(PK, SK)>,
580
581    /// used for streaming the message for both signing and verifying
582    mu_builder: MuBuilder,
583
584    signer_rnd: Option<[u8; RND_LEN]>,
585
586    /// only used in streaming sign operations
587    sk: Option<SK>,
588
589    /// only used in streaming sign operations instead of sk
590    seed: Option<KeyMaterialSized<32>>,
591
592    /// only used in streaming verify operations
593    pk: Option<PK>,
594}
595
596impl<
597    const PK_LEN: usize,
598    const SK_LEN: usize,
599    const SIG_LEN: usize,
600    PK: MLDSAPublicKeyTrait<k, PK_LEN> + MLDSAPublicKeyInternalTrait<k, PK_LEN>,
601    SK: MLDSAPrivateKeyTrait<k, l, ETA, SK_LEN, PK_LEN> + MLDSAPrivateKeyInternalTrait<k, l, ETA, SK_LEN, PK_LEN>,
602    const TAU: i32,
603    const LAMBDA: i32,
604    const GAMMA1: i32,
605    const GAMMA2: i32,
606    const k: usize,
607    const l: usize,
608    const ETA: usize,
609    const BETA: i32,
610    const OMEGA: i32,
611    const C_TILDE: usize,
612    const POLY_Z_PACKED_LEN: usize,
613    const POLY_W1_PACKED_LEN: usize,
614    const W1_PACKED_LEN: usize,
615    const POLY_ETA_PACKED_LEN: usize,
616    const LAMBDA_over_4: usize,
617    const GAMMA1_MASK_LEN: usize,
618> MLDSA<
619    PK_LEN,
620    SK_LEN,
621    SIG_LEN,
622    PK,
623    SK,
624    TAU,
625    LAMBDA,
626    GAMMA1,
627    GAMMA2,
628    k,
629    l,
630    ETA,
631    BETA,
632    OMEGA,
633    C_TILDE,
634    POLY_Z_PACKED_LEN,
635    POLY_W1_PACKED_LEN,
636    W1_PACKED_LEN,
637    POLY_ETA_PACKED_LEN,
638    LAMBDA_over_4,
639    GAMMA1_MASK_LEN,
640>
641{
642    /// Should still be ok in FIPS mode
643    pub fn keygen_from_os_rng() -> Result<
644        (PK, SK),
645        SignatureError,
646    > {
647        let mut seed = KeyMaterial256::new();
648        HashDRBG_SHA512::new_from_os().fill_keymaterial_out(&mut seed)?;
649        Self::keygen_internal(&seed)
650    }
651    /// Implements Algorithm 6 of FIPS 204
652    /// Note: NIST has made a special exception in the FIPS 204 FAQ that this _internal function
653    /// may in fact be exposed outside the crypto module.
654    ///
655    /// Unlike other interfaces across the library that take an &impl KeyMaterial, this one
656    /// specifically takes a 32-byte [KeyMaterial256] and checks that it has [KeyType::Seed] and
657    /// [SecurityStrength::_256bit].
658    /// If you happen to have your seed in a larger KeyMaterial, you'll have to copy it using
659    /// [KeyMaterial::from_key]
660    pub(crate) fn keygen_internal(
661        seed: &KeyMaterial256,
662    ) -> Result<
663        (PK, SK),
664        SignatureError,
665    > {
666        if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy)
667            || seed.key_len() != 32
668        {
669            return Err(SignatureError::KeyGenError(
670                "Seed must be 32 bytes and KeyType::Seed or KeyType::BytesFullEntropy.",
671            ));
672        }
673
674        if seed.security_strength() < SecurityStrength::from_bits(LAMBDA as usize) {
675            return Err(SignatureError::KeyGenError("Seed SecurityStrength must match algorithm security strength: 128-bit (ML-DSA-44), 192-bit (ML-DSA-65), or 256-bit (ML-DSA-87)."));
676        }
677
678        // Alg 6 line 1: (rho, rho_prime, K) <- H(πœ‰||IntegerToBytes(π‘˜, 1)||IntegerToBytes(β„“, 1), 128)
679        //   β–· expand seed
680        let mut rho: [u8; 32] = [0u8; 32];
681        let mut K: [u8; 32] = [0u8; 32];
682
683        let (s1, s2) = { // scope for h
684            let mut h = H::default();
685            h.absorb(seed.ref_to_bytes());
686            h.absorb(&(k as u8).to_le_bytes());
687            h.absorb(&(l as u8).to_le_bytes());
688            let bytes_written = h.squeeze_out(&mut rho);
689            debug_assert_eq!(bytes_written, 32);
690            let mut rho_prime: [u8; 64] = [0u8; 64];
691            let bytes_written = h.squeeze_out(&mut rho_prime);
692            debug_assert_eq!(bytes_written, 64);
693            let bytes_written = h.squeeze_out(&mut K);
694            debug_assert_eq!(bytes_written, 32);
695
696            // 4: (𝐬1, 𝐬2) ← ExpandS(πœŒβ€²)
697            let (s1, s2) = expandS::<k, l, ETA>(&rho_prime);
698
699            // Clear the secret data before returning memory to the OS
700            rho_prime.fill(0u8);
701            (s1, s2)
702        };
703
704        // 3: 𝐀_hat ← ExpandA(𝜌) β–· 𝐀 is generated and stored in NTT representation as 𝐀
705        let A_hat = expandA::<k, l>(&rho);
706
707        let t_hat = { // scope for s1_hat
708            // 5: 𝐭 ← NTTβˆ’1(𝐀 ∘ NTT(𝐬1)) + 𝐬2
709            //   β–· compute 𝐭 = 𝐀𝐬1 + 𝐬2
710            let mut s1_hat = s1.clone();
711            s1_hat.ntt();
712            A_hat.matrix_vector_ntt(&s1_hat)
713        };
714
715        let (t1, t0) = { // scope for t
716            let mut t = t_hat;
717            t.inv_ntt();
718            t.add_vector_ntt(&s2);
719            t.conditional_add_q();
720
721            // 6: (𝐭1, 𝐭0) ← Power2Round(𝐭)
722            //   β–· compress 𝐭
723            //   β–· PowerTwoRound is applied componentwise (see explanatory text in Section 7.4)
724            power_2_round_vec::<k>(&t)
725        };
726
727        // 8: π‘π‘˜ ← pkEncode(𝜌, 𝐭1)
728        let pk = PK::new(&rho, &t1);
729
730        // 9: π‘‘π‘Ÿ ← H(π‘π‘˜, 64)
731        let tr = pk.compute_tr();
732
733        // 10: π‘ π‘˜ ← skEncode(𝜌, 𝐾, π‘‘π‘Ÿ, 𝐬1, 𝐬2, 𝐭0)
734        //   β–· 𝐾 and π‘‘π‘Ÿ are for use in signing
735        let sk = SK::new(&rho, &K, &tr, &s1, &s2, &t0, Some(seed.clone()));
736
737        // Clear the secret data before returning memory to the OS
738        //   (SK::new() copies all values)
739        rho.fill(0u8);
740        K.fill(0u8);
741        // tr is public data, does not need to be zeroized
742        // s1, s2, t0 are all Vectors of Polynomials, so implement a zeroizing Drop
743
744        // 11: return (π‘π‘˜, π‘ π‘˜)
745        Ok((pk, sk))
746    }
747}
748
749impl<
750    const PK_LEN: usize,
751    const SK_LEN: usize,
752    const SIG_LEN: usize,
753    PK: MLDSAPublicKeyTrait<k, PK_LEN> + MLDSAPublicKeyInternalTrait<k, PK_LEN>,
754    SK: MLDSAPrivateKeyTrait<k, l, ETA, SK_LEN, PK_LEN> + MLDSAPrivateKeyInternalTrait<k, l, ETA, SK_LEN, PK_LEN>,
755    const TAU: i32,
756    const LAMBDA: i32,
757    const GAMMA1: i32,
758    const GAMMA2: i32,
759    const k: usize,
760    const l: usize,
761    const ETA: usize,
762    const BETA: i32,
763    const OMEGA: i32,
764    const C_TILDE: usize,
765    const POLY_Z_PACKED_LEN: usize,
766    const POLY_W1_PACKED_LEN: usize,
767    const W1_PACKED_LEN: usize,
768    const POLY_ETA_PACKED_LEN: usize,
769    const LAMBDA_over_4: usize,
770    const GAMMA1_MASK_LEN: usize,
771> MLDSATrait<PK_LEN, SK_LEN, SIG_LEN, PK, SK, k, l, ETA> for MLDSA<
772    PK_LEN,
773    SK_LEN,
774    SIG_LEN,
775    PK,
776    SK,
777    TAU,
778    LAMBDA,
779    GAMMA1,
780    GAMMA2,
781    k,
782    l,
783    ETA,
784    BETA,
785    OMEGA,
786    C_TILDE,
787    POLY_Z_PACKED_LEN,
788    POLY_W1_PACKED_LEN,
789    W1_PACKED_LEN,
790    POLY_ETA_PACKED_LEN,
791    LAMBDA_over_4,
792    GAMMA1_MASK_LEN,
793> {
794    /*** Key Generation and PK / SK consistency checks ***/
795
796    /// Imports a secret key from a seed.
797    fn keygen_from_seed(seed: &KeyMaterialSized<32>) -> Result<(PK, SK), SignatureError> {
798        Self::keygen_internal(seed)
799    }
800    /// Imports a secret key from both a seed and an encoded_sk.
801    ///
802    /// This is a convenience function to expand the key from seed and compare it against
803    /// the provided `encoded_sk` using a constant-time equality check.
804    /// If everything checks out, the secret key is returned fully populated with pk and seed.
805    /// If the provided key and derived key don't match, an error is returned.
806    fn keygen_from_seed_and_encoded(
807        seed: &KeyMaterialSized<32>,
808        encoded_sk: &[u8; SK_LEN],
809    ) -> Result<
810        (PK, SK),
811        SignatureError,
812    > {
813        let (pk, sk) = Self::keygen_internal(seed)?;
814
815        let sk_from_bytes = SK::sk_decode(encoded_sk);
816
817        // MLDSAPrivateKey impls PartialEq with a constant-time equality check.
818        if sk != sk_from_bytes {
819            return Err(SignatureError::KeyGenError("Encoded key does not match generated key"));
820        }
821
822        Ok((pk, sk))
823    }
824    /// Given a public key and a secret key, check that the public key matches the secret key.
825    /// This is a sanity check that the public key was generated correctly from the secret key.
826    ///
827    /// At the current time, this is only possible if `sk` either contains a public key (in which case
828    /// the two pk's are encoded and compared for byte equality), or if `sk` contains a seed
829    /// (in which case a keygen_from_seed is run and then the pk's compared).
830    ///
831    /// Returns either `()` or [SignatureError::ConsistencyCheckFailed].
832    fn keypair_consistency_check(
833        pk: &PK,
834        sk: &SK,
835    ) -> Result<(), SignatureError> {
836        // This is maybe a computationally heavy way to compare them, but it works
837        let derived_pk = sk.derive_pk();
838        if derived_pk.compute_tr() == pk.compute_tr() {
839            Ok(())
840        } else {
841            Err(SignatureError::ConsistencyCheckFailed())
842        }
843    }
844    /// This provides the first half of the "External Mu" interface to ML-DSA which is described
845    /// in, and allowed under, NIST's FAQ that accompanies FIPS 204.
846    ///
847    /// This function, together with [MLDSATrait::sign_mu] perform a complete ML-DSA signature which is indistinguishable
848    /// from one produced by the one-shot sign APIs.
849    ///
850    /// The utility of this function is exactly as described
851    /// on Line 6 of Algorithm 7 of FIPS 204:
852    ///
853    ///    message representative that may optionally be computed in a different cryptographic module
854    ///
855    /// The utility is when an extremely large message needs to be signed, where the message exists on one
856    /// computing system and the private key to sign it is held on another and either the transfer time or bandwidth
857    /// causes operational concerns (this is common for example with network HSMs or sending large messages
858    /// to be signed by a smartcard communicating over near-field radio). Another use case is if the
859    /// contents of the message are sensitive and the signer does not want to transmit the message itself
860    /// for fear of leaking it via proxy logging and instead would prefer to only transmit a hash of it.
861    ///
862    /// Since "External Mu" mode is well-defined by FIPS 204 and allowed by NIST, the mu value produced here
863    /// can be used with many hardware crypto modules.
864    ///
865    /// This "External Mu" mode of ML-DSA provides an alternative to the HashML-DSA algorithm in that it
866    /// allows the message to be externally pre-hashed, however, unlike HashML-DSA, this is merely an optimization
867    /// between the application holding the to-be-signed message and the cryptographic module holding the private key
868    /// -- in particular, while HashML-DSA requires the verifier to know whether ML-DSA or HashML-DSA was used to sign
869    /// the message, both "direct" ML-DSA and "External Mu" signatures can be verified with a standard
870    /// ML-DSA verifier.
871    ///
872    /// This function requires the public key hash `tr`, which can be computed from the public key
873    /// using [MLDSAPublicKeyTrait::compute_tr].
874    ///
875    /// For a streaming version of this, see [MuBuilder].
876    fn compute_mu_from_tr(
877        msg: &[u8],
878        ctx: Option<&[u8]>,
879        tr: &[u8; 64],
880    ) -> Result<[u8; 64], SignatureError> {
881        MuBuilder::compute_mu(msg, ctx, tr)
882    }
883    /// Same as [MLDSA::compute_mu_from_tr], but extracts tr from the public key.
884    fn compute_mu_from_pk(
885        msg: &[u8],
886        ctx: Option<&[u8]>,
887        pk: &PK,
888    ) -> Result<[u8; 64], SignatureError> {
889        MuBuilder::compute_mu(msg, ctx, &pk.compute_tr())
890    }
891    /// Same as [MLDSA::compute_mu_from_tr], but extracts tr from the private key.
892    fn compute_mu_from_sk(
893        msg: &[u8],
894        ctx: Option<&[u8]>,
895        sk: &SK,
896    ) -> Result<[u8; 64], SignatureError> {
897        MuBuilder::compute_mu(msg, ctx, &sk.tr())
898    }
899    /// Performs an ML-DSA signature using the provided external message representative `mu`.
900    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
901    /// FIPS 204 itself, as well as subsequent FAQ documents.
902    /// This mode uses randomized signing (called "hedged mode" in FIPS 204) using an internal RNG.
903    fn sign_mu(
904        sk: &SK,
905        mu: &[u8; 64],
906    ) -> Result<[u8; SIG_LEN], SignatureError> {
907        let mut out: [u8; SIG_LEN] = [0u8; SIG_LEN];
908        Self::sign_mu_out(sk, mu, &mut out)?;
909        Ok(out)
910    }
911    /// Performs an ML-DSA signature using the provided external message representative `mu`.
912    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
913    /// FIPS 204 itself, as well as subsequent FAQ documents.
914    /// This mode uses randomized signing (called "hedged mode" in FIPS 204) using an internal RNG.
915    ///
916    /// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer.
917    fn sign_mu_out(
918        sk: &SK,
919        mu: &[u8; 64],
920        output: &mut [u8; SIG_LEN],
921    ) -> Result<usize, SignatureError> {
922        let mut rnd: [u8; RND_LEN] = [0u8; RND_LEN];
923        HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?;
924
925        Self::sign_mu_deterministic_out(sk, mu, rnd, output)
926    }
927    /// Algorithm 7 ML-DSA.Sign_internal(π‘ π‘˜, 𝑀′, π‘Ÿπ‘›π‘‘)
928    /// (modified to take an externally-computed mu instead of M')
929    ///
930    /// Performs an ML-DSA signature using the provided external message representative `mu`.
931    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
932    /// FIPS 204 itself, as well as subsequent FAQ documents.
933    ///
934    /// Security note:
935    /// This mode exposes deterministic signing (called "hedged mode" and allowed by FIPS 204).
936    /// The ML-DSA algorithm is considered safe to use in deterministic mode, but be aware that
937    /// the responsibility is on you to ensure that your nonce `rnd` is unique per signature.
938    /// If not, you may lose some privacy properties; for example it becomes easy to tell if a signer
939    /// has signed the same message twice or two different messagase, or to tell if the same message
940    /// has been signed by the same signer twice or two different signers.
941    ///
942    /// Since `rnd` should be either a per-signature nonce, or a fixed value, therefore, to help
943    /// prevent accidental nonce reuse, this function moves `rnd`.
944    fn sign_mu_deterministic(
945        sk: &SK,
946        mu: &[u8; 64],
947        rnd: [u8; 32],
948    ) -> Result<[u8; SIG_LEN], SignatureError> {
949        let mut out: [u8; SIG_LEN] = [0u8; SIG_LEN];
950        Self::sign_mu_deterministic_out(sk, mu, rnd, &mut out)?;
951        Ok(out)
952    }
953    /// Algorithm 7 ML-DSA.Sign_internal(π‘ π‘˜, 𝑀′, π‘Ÿπ‘›π‘‘)
954    /// (modified to take an externally-computed mu instead of M')
955    ///
956    /// Performs an ML-DSA signature using the provided external message representative `mu`.
957    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
958    /// FIPS 204 itself, as well as subsequent FAQ documents.
959    /// This mode exposes deterministic signing (called "hedged mode" in FIPS 204) using an internal RNG.
960    ///
961    /// Since `rnd` should be either a per-signature nonce, or a fixed value, therefore, to help
962    /// prevent accidental nonce reuse, this function moves `rnd`.
963    ///
964    /// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer.
965    fn sign_mu_deterministic_out(
966        sk: &SK,
967        mu: &[u8; 64],
968        rnd: [u8; 32],
969        output: &mut [u8; SIG_LEN],
970    ) -> Result<usize, SignatureError> {
971        // 1: (𝜌, 𝐾, π‘‘π‘Ÿ, 𝐬1, 𝐬2, 𝐭0) ← skDecode(π‘ π‘˜)
972        // Already done -- the sk struct is already decoded
973
974        // 2: 𝐬1Μ‚_hat ← NTT(𝐬1)
975        let mut s1_hat = sk.s1().clone();
976        s1_hat.ntt();
977
978        // 3: 𝐬2Μ‚_hat ← NTT(𝐬2)
979        let mut s2_hat = sk.s2().clone();
980        s2_hat.ntt();
981
982        // 4: 𝐭0Μ‚_hat ← NTT(𝐭0)Μ‚
983        let mut t0_hat = sk.t0().clone();
984        t0_hat.ntt();
985
986        // 5: 𝐀_hat ← ExpandA(𝜌)
987        let A_hat = expandA::<k, l>(&sk.rho());
988
989        // 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀 β€², 64)
990        // skip: mu has already been provided
991
992        let mut rho_p_p:[u8; 64] = { // scope for h
993            // 7: πœŒβ€³ ← H(𝐾||π‘Ÿπ‘›π‘‘||πœ‡, 64)
994            let mut h = H::new();
995            h.absorb(sk.K());
996            h.absorb(&rnd);
997            h.absorb(mu);
998            let mut rho_p_p = [0u8; 64];
999            h.squeeze_out(&mut rho_p_p);
1000            rho_p_p
1001        };
1002
1003        // 8: πœ… ← 0
1004        //  β–· initialize counter πœ…
1005        let mut kappa: u16 = 0;
1006
1007        // 9: (𝐳, 𝐑) ← βŠ₯
1008        // handled in the loop
1009
1010        // 10: while (𝐳, 𝐑) = βŠ₯ do
1011        //  β–· rejection sampling loop
1012
1013        // these need to be outside the loop because they form the encoded signature value
1014        let mut sig_val_c_tilde = [0u8; LAMBDA_over_4];
1015        let mut sig_val_z: Vector<l>;
1016        let mut sig_val_h: Vector<k>;
1017        loop {
1018            // FIPS 204 s. 6.2 allows:
1019            //   "Implementations may limit the number of iterations in this loop to not exceed a finite maximum value."
1020            if kappa > 1000 * k as u16 { return Err(SignatureError::GenericError("Rejection sampling loop exceeded max iterations, try again with a different signing nonce.")) }
1021
1022            // 11: 𝐲 ∈ 𝑅^β„“ ← ExpandMask(πœŒβ€³, πœ…)
1023            let mut y = expand_mask::<l, GAMMA1, GAMMA1_MASK_LEN>(&rho_p_p, kappa);
1024
1025            let w = { // scope for y_hat
1026                // 12: 𝐰 ← NTTβˆ’1(𝐀_hat * NTT(𝐲))
1027                let mut y_hat = y.clone();
1028                y_hat.ntt();
1029                let mut w = A_hat.matrix_vector_ntt(&y_hat);
1030                w.inv_ntt();
1031                w.conditional_add_q();
1032                w
1033            };
1034
1035            // 13: 𝐰1 ← HighBits(𝐰)
1036            //  β–· signer’s commitment
1037            let w1 = w.high_bits::<GAMMA2>();
1038
1039            { // scope for h
1040                // 15: 𝑐_tilde ← H(πœ‡||w1Encode(𝐰1), πœ†/4)
1041                //  β–· commitment hash
1042                let mut hash = H::new();
1043                hash.absorb(mu);
1044                w1.w1_encode_and_hash::<W1_PACKED_LEN, POLY_W1_PACKED_LEN>(&mut hash);
1045                hash.squeeze_out(&mut sig_val_c_tilde);
1046            }
1047
1048            // 16: 𝑐 ∈ π‘…π‘ž ← SampleInBall(c_tilde)
1049            //  β–· verifier’s challenge
1050            let c_hat = { // scope for c
1051                let c = sample_in_ball::<LAMBDA_over_4, TAU>(&sig_val_c_tilde);
1052
1053                // 17: 𝑐_hat ← NTT(𝑐)
1054                ntt(&c)
1055            };
1056            // 18: βŸ¨βŸ¨π‘π¬1⟩⟩ ← NTTβˆ’1(𝑐_hat * 𝐬1_hat)
1057            //  Note: <<.>> in FIPS 204 means that this value will be used again later, so you should hang on to it.
1058            let mut cs1 = s1_hat.scalar_vector_ntt(&c_hat);
1059            cs1.inv_ntt();
1060
1061            // 20: 𝐳 ← 𝐲 + βŸ¨βŸ¨π‘π¬1⟩⟩
1062            y.add_vector_ntt(&cs1);
1063            sig_val_z = y;
1064
1065            // 23 (first half): if ||𝐳||∞ β‰₯ 𝛾1 βˆ’ 𝛽 or ||𝐫0||∞ β‰₯ 𝛾2 βˆ’ 𝛽 then (z, h) ← βŠ₯
1066            //  β–· validity checks
1067            // out-of-order on purpose for performance reasons:
1068            //   might as well do the rejection sampling check before any extra heavy computation
1069            if sig_val_z.check_norm(GAMMA1 - BETA) {
1070                kappa += l as u16;
1071                continue;
1072            };
1073
1074            // 19: βŸ¨βŸ¨π‘π¬2⟩⟩ ← NTTβˆ’1(𝑐_hat * 𝐬2Μ‚_hat)
1075            let mut cs2 = s2_hat.scalar_vector_ntt(&c_hat);
1076            cs2.inv_ntt();
1077
1078            // 21: 𝐫0 ← LowBits(𝐰 βˆ’ βŸ¨βŸ¨π‘π¬2⟩⟩)
1079            let mut r0 = w.sub_vector(&cs2).low_bits::<GAMMA2>();
1080
1081            // 23 (second half): if ||𝐳||∞ β‰₯ 𝛾1 βˆ’ 𝛽 or ||𝐫0||∞ β‰₯ 𝛾2 βˆ’ 𝛽 then (z, h) ← βŠ₯
1082            //  β–· validity checks
1083            if r0.check_norm(GAMMA2 - BETA) {
1084                kappa += l as u16;
1085                continue;
1086            };
1087
1088            // 25: βŸ¨βŸ¨π‘π­0⟩⟩ ← NTTβˆ’1(𝑐_hat * 𝐭0Μ‚_hat )
1089            let mut ct0 = t0_hat.scalar_vector_ntt(&c_hat);
1090            ct0.inv_ntt();
1091
1092            // 28 (first half): if ||βŸ¨βŸ¨π‘π­0⟩⟩||∞ β‰₯ 𝛾2 or the number of 1’s in 𝐑 is greater than πœ”, then (z, h) ← βŠ₯
1093            // out-of-order on purpose for performance reasons:
1094            //   might as well do the rejection sampling check before any extra heavy computation
1095            if ct0.check_norm(GAMMA2) {
1096                kappa += l as u16;
1097                continue;
1098            };
1099
1100            // 26: 𝐑 ← MakeHint(βˆ’βŸ¨βŸ¨π‘π­0⟩⟩, 𝐰 βˆ’ βŸ¨βŸ¨π‘π¬2⟩⟩ + βŸ¨βŸ¨π‘π­0⟩⟩)
1101            //  β–· Signer’s hint
1102            r0.add_vector_ntt(&ct0);
1103            r0.conditional_add_q();
1104            let hint_hamming_weight: i32;
1105            sig_val_h = { // scope for hint
1106                let (hint, inner_hint_hamming_weight) =
1107                    make_hint_vecs::<k, GAMMA2>(&r0, &w1);
1108                hint_hamming_weight = inner_hint_hamming_weight;
1109                hint
1110            };
1111
1112            // 28 (second half): if ||βŸ¨βŸ¨π‘π­0⟩⟩||∞ β‰₯ 𝛾2 or the number of 1’s in 𝐑 is greater than πœ”, then (z, h) ← βŠ₯
1113            if hint_hamming_weight > OMEGA {
1114                kappa += l as u16;
1115                continue;
1116            };
1117
1118            // "In addition, there is an alternative way of implementing the validity checks on 𝐳 and the computation of
1119            // 𝐑, which is described in Section 5.1 of [6] (dilithium-specification-round3-20210208.pdf).
1120            // This method may also be used in implementations of ML-DSA."
1121            // todo -- I believe this code is already using this optimization, but it could use a deeper look to see if more optimization is possible.
1122
1123            break;
1124        }
1125
1126        // zeroize rho_p_p before returning it to the OS
1127        rho_p_p.fill(0u8);
1128
1129        // sig_encode does not necessarily write to all bytes of the output, so just to be safe:
1130        output.fill(0u8);
1131
1132        // 33: 𝜎 ← sigEncode(𝑐, 𝐳̃ modΒ±π‘ž, 𝐑)
1133        let bytes_written = sig_encode::<GAMMA1, k, l, LAMBDA_over_4, OMEGA, POLY_Z_PACKED_LEN, SIG_LEN>
1134            (&sig_val_c_tilde, &sig_val_z, &sig_val_h, output);
1135
1136        Ok(bytes_written)
1137    }
1138
1139    fn sign_mu_deterministic_from_seed(seed: &KeyMaterialSized<32>, mu: &[u8; 64], rnd: [u8; 32]) -> Result<[u8; SIG_LEN], SignatureError> {
1140        let mut out: [u8; SIG_LEN] = [0u8; SIG_LEN];
1141        Self::sign_mu_deterministic_from_seed_out(seed, mu, rnd, &mut out)?;
1142        Ok(out)
1143    }
1144
1145    fn sign_mu_deterministic_from_seed_out(
1146        seed: &KeyMaterialSized<32>,
1147        mu: &[u8; 64],
1148        rnd: [u8; 32],
1149        output: &mut [u8; SIG_LEN],
1150    ) -> Result<usize, SignatureError> {
1151        // This function is a mash-up of keyGen (Algorithm 6) and sign (Algorithm 7)
1152
1153        // I have tried to keep this as clean as possible for correspondence with the FIPS,
1154        // but I have moved things around so that I can use unnamed scopes to limit how many
1155        // stack variables are alive at the same time.
1156
1157        // 1: (𝜌, 𝐾, π‘‘π‘Ÿ, 𝐬1, 𝐬2, 𝐭0) ← skDecode(π‘ π‘˜)
1158        // to avoid having all of it in memory at the same time,
1159        // we're gonna derive what we need as we need it.
1160
1161        if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy)
1162            || seed.key_len() != 32
1163        {
1164            return Err(SignatureError::KeyGenError(
1165                "Seed must be 32 bytes and KeyType::Seed or KeyType::BytesFullEntropy.",
1166            ));
1167        }
1168
1169        if seed.security_strength() < SecurityStrength::from_bits(LAMBDA as usize) {
1170            return Err(SignatureError::KeyGenError("Seed SecurityStrength must match algorithm security strength: 128-bit (ML-DSA-44), 192-bit (ML-DSA-65), or 256-bit (ML-DSA-87)."));
1171        }
1172
1173        // Alg 7; 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀 β€², 64)
1174        // skip: mu has already been provided
1175
1176        let rho: [u8; 32];
1177        let mut rho_p_p:[u8; 64];
1178        let (s1, s2) = { // scope for h
1179            // derive sk.K
1180            // Alg 6; 1: (rho, rho_prime, K) <- H(πœ‰||IntegerToBytes(π‘˜, 1)||IntegerToBytes(β„“, 1), 128)
1181            //   β–· expand seed
1182            let mut tmp_rho: [u8; 32] = [0u8; 32];
1183            let mut rho_prime: [u8; 64] = [0u8; 64];
1184            let mut K: [u8; 32] = [0u8; 32];
1185
1186            let mut h = H::default();
1187            h.absorb(seed.ref_to_bytes());
1188            h.absorb(&(k as u8).to_le_bytes());
1189            h.absorb(&(l as u8).to_le_bytes());
1190            let bytes_written = h.squeeze_out(&mut tmp_rho);
1191            debug_assert_eq!(bytes_written, 32);
1192            let bytes_written = h.squeeze_out(&mut rho_prime);
1193            debug_assert_eq!(bytes_written, 64);
1194            let bytes_written = h.squeeze_out(&mut K);
1195            debug_assert_eq!(bytes_written, 32);
1196            rho = tmp_rho;
1197
1198
1199            // Alg 7; 7: πœŒβ€³ ← H(𝐾||π‘Ÿπ‘›π‘‘||πœ‡, 64)
1200            let mut h = H::new();
1201            h.absorb(&K);
1202            h.absorb(&rnd);
1203            h.absorb(mu);
1204            let mut tmp_rho_p_p = [0u8; 64];
1205            h.squeeze_out(&mut tmp_rho_p_p);
1206            rho_p_p = tmp_rho_p_p;
1207
1208            // 4: (𝐬1, 𝐬2) ← ExpandS(πœŒβ€²)
1209            expandS::<k, l, ETA>(&rho_prime)
1210        };
1211
1212        // Alg 7; 5: 𝐀_hat ← ExpandA(𝜌)
1213        // Note on memory optimization:
1214        // A_hat consumes a large bit of memory and technically could move inside the loop --
1215        // -- or even more aggressively, could be derived and multiplied by y_hat row-by-row --
1216        // But in my unit tests, I see the loop typically execute 1 - 3 times, sometimes as many
1217        // as 20 or even 80 times. So moving expandA() inside the loop would be a pretty drastic speed-for-memory tradeoff
1218        // that I'm not willing to make in general, so I leave that as an optimization that people
1219        // can make on a private fork if you really really need the memory squeeze.
1220        let A_hat = expandA::<k, l>(&rho);
1221
1222        // Alg 7; 8: πœ… ← 0
1223        //  β–· initialize counter πœ…
1224        let mut kappa: u16 = 0;
1225
1226        // Alg 7; 9: (𝐳, 𝐑) ← βŠ₯
1227        // handled in the loop
1228
1229        // Alg 7; 10: while (𝐳, 𝐑) = βŠ₯ do
1230        //  β–· rejection sampling loop
1231
1232        // these need to be outside the loop because they form the encoded signature value
1233        let mut sig_val_c_tilde = [0u8; LAMBDA_over_4];
1234        let mut sig_val_z: Vector<l>;
1235        let mut sig_val_h: Vector<k>;
1236        loop {
1237            // FIPS 204 s. 6.2 allows:
1238            //   "Implementations may limit the number of iterations in this loop to not exceed a finite maximum value."
1239            if kappa > 1000 * k as u16 { return Err(SignatureError::GenericError("Rejection sampling loop exceeded max iterations, try again with a different signing nonce.")) }
1240
1241            // Alg 7; 11: 𝐲 ∈ 𝑅^β„“ ← ExpandMask(πœŒβ€³, πœ…)
1242            let mut y = expand_mask::<l, GAMMA1, GAMMA1_MASK_LEN>(&rho_p_p, kappa);
1243
1244            let w = { // scope for y_hat
1245                // Alg 7; 12: 𝐰 ← NTTβˆ’1(𝐀_hat * NTT(𝐲))
1246                let mut y_hat = y.clone();
1247                y_hat.ntt();
1248                let mut w = A_hat.matrix_vector_ntt(&y_hat);
1249                w.inv_ntt();
1250                w.conditional_add_q();
1251                w
1252            };
1253
1254            // Alg 7; 13: 𝐰1 ← HighBits(𝐰)
1255            //  β–· signer’s commitment
1256            let w1 = w.high_bits::<GAMMA2>();
1257
1258            { // scope for h
1259                // 15: 𝑐_tilde ← H(πœ‡||w1Encode(𝐰1), πœ†/4)
1260                //  β–· commitment hash
1261                let mut hash = H::new();
1262                hash.absorb(mu);
1263                // hash.absorb(&w1.w1_encode::<W1_PACKED_LEN, POLY_W1_PACKED_LEN>());
1264                w1.w1_encode_and_hash::<W1_PACKED_LEN, POLY_W1_PACKED_LEN>(&mut hash);
1265                hash.squeeze_out(&mut sig_val_c_tilde);
1266            }
1267
1268            // Alg 7; 16: 𝑐 ∈ π‘…π‘ž ← SampleInBall(c_tilde)
1269            //  β–· verifier’s challenge
1270            let c_hat = { // scope for c
1271                let c = sample_in_ball::<LAMBDA_over_4, TAU>(&sig_val_c_tilde);
1272
1273                // 17: 𝑐_hat ← NTT(𝑐)
1274                ntt(&c)
1275            };
1276
1277            let t_hat: Vector<k>;
1278            sig_val_z = { // scope for s1_hat, cs1
1279                // Alg 7; 2: 𝐬1Μ‚_hat ← NTT(𝐬1)
1280                let mut s1_hat = s1.clone();
1281                s1_hat.ntt();
1282
1283                y = { // scope for cs1
1284                    // Alg 7; 18: βŸ¨βŸ¨π‘π¬1⟩⟩ ← NTTβˆ’1(𝑐_hat * 𝐬1_hat)
1285                    //  Note: <<.>> in FIPS 204 means that this value will be used again later, so you should hang on to it.
1286                    let mut cs1 = s1_hat.scalar_vector_ntt(&c_hat);
1287                    cs1.inv_ntt();
1288
1289                    // Alg 7; 20: 𝐳 ← 𝐲 + βŸ¨βŸ¨π‘π¬1⟩⟩
1290                    y.add_vector_ntt(&cs1);
1291                    y
1292                };
1293
1294                // also, while we have s1_hat in memory, compute t_hat
1295                // Alg 6; 5: 𝐭 ← NTTβˆ’1(𝐀 ∘ NTT(𝐬1)) + 𝐬2
1296                //   β–· compute 𝐭 = 𝐀𝐬1 + 𝐬2
1297                t_hat = A_hat.matrix_vector_ntt(&s1_hat);
1298
1299                y
1300            };
1301
1302            // Alg 7; 23 (first half): if ||𝐳||∞ β‰₯ 𝛾1 βˆ’ 𝛽 or ||𝐫0||∞ β‰₯ 𝛾2 βˆ’ 𝛽 then (z, h) ← βŠ₯
1303            //  β–· validity checks
1304            // out-of-order on purpose for performance reasons:
1305            //   might as well do the rejection sampling check before any extra heavy computation
1306            if sig_val_z.check_norm(GAMMA1 - BETA) {
1307                kappa += l as u16;
1308                continue;
1309            };
1310
1311            let t0: Vector<k>;
1312            let mut r0: Vector<k> = { // scope for s2_hat and cs2
1313                // 3: 𝐬2Μ‚_hat ← NTT(𝐬2)
1314                let mut s2_hat = s2.clone();
1315                s2_hat.ntt();
1316
1317                // 19: βŸ¨βŸ¨π‘π¬2⟩⟩ ← NTTβˆ’1(𝑐_hat * 𝐬2Μ‚_hat)
1318                let mut cs2 = s2_hat.scalar_vector_ntt(&c_hat);
1319                cs2.inv_ntt();
1320
1321                // 21: 𝐫0 ← LowBits(𝐰 βˆ’ βŸ¨βŸ¨π‘π¬2⟩⟩)
1322                let r0 = w.sub_vector(&cs2).low_bits::<GAMMA2>();
1323
1324                // while we have s2_hat in scope, derive t0
1325                let mut t = t_hat;
1326                t.inv_ntt();
1327                t.add_vector_ntt(&s2);
1328                t.conditional_add_q();
1329
1330                // 6: (𝐭1, 𝐭0) ← Power2Round(𝐭)
1331                //   β–· compress 𝐭
1332                //   β–· PowerTwoRound is applied componentwise (see explanatory text in Section 7.4)
1333                let (_t1tmp, t0tmp) = power_2_round_vec::<k>(&t);
1334                t0 = t0tmp;
1335
1336                r0
1337            };
1338
1339            // Alg 7; 23 (second half): if ||𝐳||∞ β‰₯ 𝛾1 βˆ’ 𝛽 or ||𝐫0||∞ β‰₯ 𝛾2 βˆ’ 𝛽 then (z, h) ← βŠ₯
1340            //  β–· validity checks
1341            if r0.check_norm(GAMMA2 - BETA) {
1342                kappa += l as u16;
1343                continue;
1344            };
1345
1346            let ct0: Vector<k> = { // scope for t0_hat
1347                // 4: 𝐭0Μ‚_hat ← NTT(𝐭0)Μ‚
1348                let mut t0_hat = t0.clone();
1349                t0_hat.ntt();
1350
1351                // 25: βŸ¨βŸ¨π‘π­0⟩⟩ ← NTTβˆ’1(𝑐_hat * 𝐭0Μ‚_hat )
1352                let mut ct0 = t0_hat.scalar_vector_ntt(&c_hat);
1353                ct0.inv_ntt();
1354                ct0
1355            };
1356
1357            // Alg 7; 28 (first half): if ||βŸ¨βŸ¨π‘π­0⟩⟩||∞ β‰₯ 𝛾2 or the number of 1’s in 𝐑 is greater than πœ”, then (z, h) ← βŠ₯
1358            // out-of-order on purpose for performance reasons:
1359            //   might as well do the rejection sampling check before any extra heavy computation
1360            if ct0.check_norm(GAMMA2) {
1361                kappa += l as u16;
1362                continue;
1363            };
1364
1365            // Alg 7; 26: 𝐑 ← MakeHint(βˆ’βŸ¨βŸ¨π‘π­0⟩⟩, 𝐰 βˆ’ βŸ¨βŸ¨π‘π¬2⟩⟩ + βŸ¨βŸ¨π‘π­0⟩⟩)
1366            //  β–· Signer’s hint
1367            r0.add_vector_ntt(&ct0);
1368            r0.conditional_add_q();
1369            let hint_hamming_weight: i32;
1370            sig_val_h = { // scope for hint
1371                let (hint, inner_hint_hamming_weight) =
1372                    make_hint_vecs::<k, GAMMA2>(&r0, &w1);
1373                hint_hamming_weight = inner_hint_hamming_weight;
1374                hint
1375            };
1376
1377            // Alg 7; 28 (second half): if ||βŸ¨βŸ¨π‘π­0⟩⟩||∞ β‰₯ 𝛾2 or the number of 1’s in 𝐑 is greater than πœ”, then (z, h) ← βŠ₯
1378            if hint_hamming_weight > OMEGA {
1379                kappa += l as u16;
1380                continue;
1381            };
1382
1383            // "In addition, there is an alternative way of implementing the validity checks on 𝐳 and the computation of
1384            // 𝐑, which is described in Section 5.1 of [6] (dilithium-specification-round3-20210208.pdf).
1385            // This method may also be used in implementations of ML-DSA."
1386            // todo -- I believe this code is already using this optimization, but it could use a deeper look to see if more optimization is possible.
1387
1388            break;
1389        }
1390
1391        // zeroize rho_p_p before returning it to the OS
1392        rho_p_p.fill(0u8);
1393
1394        // sig_encode does not necessarily write to all bytes of the output, so just to be safe:
1395        output.fill(0u8);
1396
1397        // Alg 7; 33: 𝜎 ← sigEncode(𝑐, 𝐳̃ modΒ±π‘ž, 𝐑)
1398        let bytes_written = sig_encode::<GAMMA1, k, l, LAMBDA_over_4, OMEGA, POLY_Z_PACKED_LEN, SIG_LEN>
1399            (&sig_val_c_tilde, &sig_val_z, &sig_val_h, output);
1400
1401        Ok(bytes_written)
1402    }
1403    /// To be used for deterministic signing in conjunction with the [MLDSA44::sign_init], [MLDSA44::sign_update], and [MLDSA44::sign_final] flow.
1404    /// Can be set anywhere after [MLDSA44::sign_init] and before [MLDSA44::sign_final]
1405    fn set_signer_rnd(&mut self, rnd: [u8; 32]) {
1406        self.signer_rnd = Some(rnd);
1407    }
1408
1409    /// Alternative initialization of the streaming signer where you have your private key
1410    /// as a seed and you want to delay its expansion as late as possible for memory-usage reasons.
1411    fn sign_init_from_seed(seed: &KeyMaterialSized<32>, ctx: Option<&[u8]>) -> Result<Self, SignatureError> {
1412        let (_pk, sk) = Self::keygen_from_seed(seed)?;
1413        Ok(
1414            Self {
1415                _phantom: PhantomData,
1416                mu_builder: MuBuilder::do_init(&sk.tr(), ctx)?,
1417                signer_rnd: None,
1418                sk: None,
1419                seed: Some(seed.clone()),
1420                pk: None }
1421        )
1422    }
1423
1424    /// Algorithm 8 ML-DSA.Verify_internal(π‘π‘˜, 𝑀′, 𝜎)
1425    /// Internal function to verify a signature 𝜎 for a formatted message 𝑀′ .
1426    /// Input: Public key π‘π‘˜ ∈ 𝔹32+32π‘˜(bitlen (π‘žβˆ’1)βˆ’π‘‘) and message 𝑀′ ∈ {0, 1}βˆ— .
1427    /// Input: Signature 𝜎 ∈ π”Ήπœ†/4+β„“β‹…32β‹…(1+bitlen (𝛾1βˆ’1))+πœ”+π‘˜.
1428    fn verify_mu_internal(
1429        pk: &PK,
1430        mu: &[u8; 64],
1431        sig: &[u8; SIG_LEN],
1432    ) -> bool {
1433        // 1: (𝜌, 𝐭1) ← pkDecode(π‘π‘˜)
1434        // Already done -- the pk struct is already decoded
1435
1436        // 2: (𝑐_tilde, 𝐳, 𝐑) ← sigDecode(𝜎)
1437        //  β–· signer’s commitment hash c_tilde, response 𝐳, and hint 𝐑
1438        // 3: if 𝐑 = βŠ₯ then return false
1439        let (c_tilde, z, h) = match sig_decode::<GAMMA1, k, l, LAMBDA_over_4, OMEGA, POLY_Z_PACKED_LEN, SIG_LEN>(&sig) {
1440            Ok((c_tilde, z, h)) => (c_tilde, z, h),
1441            Err(_) => return false,
1442        };
1443
1444        // 13 (first half) return [[ ||𝐳||∞ < 𝛾1 βˆ’ 𝛽]]
1445        if z.check_norm(GAMMA1 - BETA) {
1446            return false;
1447        }
1448
1449        // 5: 𝐀 ← ExpandA(𝜌)
1450        //   β–· 𝐀 is generated and stored in NTT representation as 𝐀
1451        #[allow(non_snake_case)]
1452        let A_hat = expandA::<k, l>(&pk.rho());
1453
1454        // 6: π‘‘π‘Ÿ ← H(π‘π‘˜, 64)
1455        // 7: πœ‡ ← (H(BytesToBits(π‘‘π‘Ÿ)||𝑀 β€², 64))
1456        //   β–· message representative that may optionally be
1457        //     computed in a different cryptographic module
1458        // skip because this function is being handed mu
1459
1460        // 8: 𝑐 ∈ π‘…π‘ž ← SampleInBall(c_tilde)
1461        let c = sample_in_ball::<LAMBDA_over_4, TAU>(&c_tilde);
1462
1463
1464        // 9: 𝐰′_approx ← NTTβˆ’1(𝐀_hat ∘ NTT(𝐳) βˆ’ NTT(𝑐) ∘ NTT(𝐭1 β‹… 2^𝑑))
1465        //   broken out for clarity:
1466        //   NTTβˆ’1(
1467        //      𝐀_hat ∘ NTT(𝐳) βˆ’
1468        //                  NTT(𝑐) ∘ NTT(𝐭1 β‹… 2^𝑑)
1469        //   )
1470        // β–· 𝐰'_approx = 𝐀𝐳 βˆ’ 𝑐𝐭1 β‹… 2^𝑑
1471        // weird nested scoping is to reduce peak stack memory usage
1472        let w1p = {
1473            let w1 = {
1474                let mut z_hat = z.clone();
1475                z_hat.ntt();
1476                A_hat.matrix_vector_ntt(&z_hat)
1477            };
1478            let w2 = {
1479                let mut t1_shift_hat = pk.t1().shift_left::<d>();
1480                t1_shift_hat.ntt();
1481                t1_shift_hat.scalar_vector_ntt(&ntt(&c))
1482            };
1483            let mut wp_approx = w1.sub_vector(&w2);
1484            wp_approx.inv_ntt();
1485            wp_approx.conditional_add_q();
1486            // bc-java does a wp_approx.conditional_add_q();
1487
1488            // 10: 𝐰1β€² ← UseHint(𝐑, 𝐰'_approx)
1489            // β–· reconstruction of signer’s commitment
1490            use_hint_vecs::<k, GAMMA2>(&h, &wp_approx)
1491        };
1492        // 12: 𝑐_tilde_p ← H(πœ‡||w1Encode(𝐰1'), πœ†/4)
1493        // β–· hash it; this should match 𝑐_tilde
1494        let mut c_tilde_p = [0u8; LAMBDA_over_4];
1495        let mut hash = H::new();
1496        hash.absorb(mu);
1497        w1p.w1_encode_and_hash::<W1_PACKED_LEN, POLY_W1_PACKED_LEN>(&mut hash);
1498        hash.squeeze_out(&mut c_tilde_p);
1499
1500
1501        // verification probably doesn't technically need to be constant-time, but why not?
1502        // 13 (second half): return [[ ||𝐳||∞ < 𝛾1 βˆ’ 𝛽]] and [[𝑐 Μƒ = 𝑐′ ]]
1503        bouncycastle_utils::ct::ct_eq_bytes(&c_tilde, &c_tilde_p)
1504    }
1505}
1506
1507/// Trait for all three of the ML-DSA algorithm variants.
1508pub trait MLDSATrait<
1509    const PK_LEN: usize,
1510    const SK_LEN: usize,
1511    const SIG_LEN: usize,
1512    PK: MLDSAPublicKeyTrait<k, PK_LEN> + MLDSAPublicKeyInternalTrait<k, PK_LEN>,
1513    SK: MLDSAPrivateKeyTrait<k, l, ETA, SK_LEN, PK_LEN> + MLDSAPrivateKeyInternalTrait<k, l, ETA, SK_LEN, PK_LEN>,
1514    const k: usize,
1515    const l: usize,
1516    const ETA: usize
1517> : Sized {
1518    /// Imports a secret key from a seed.
1519    fn keygen_from_seed(seed: &KeyMaterialSized<32>) -> Result<(PK, SK), SignatureError>;
1520    /// Imports a secret key from both a seed and an encoded_sk.
1521    ///
1522    /// This is a convenience function to expand the key from seed and compare it against
1523    /// the provided `encoded_sk` using a constant-time equality check.
1524    /// If everything checks out, the secret key is returned fully populated with pk and seed.
1525    /// If the provided key and derived key don't match, an error is returned.
1526    fn keygen_from_seed_and_encoded(
1527        seed: &KeyMaterialSized<32>,
1528        encoded_sk: &[u8; SK_LEN],
1529    ) -> Result<
1530        (PK, SK),
1531        SignatureError,
1532    >;
1533    /// Given a public key and a secret key, check that the public key matches the secret key.
1534    /// This is a sanity check that the public key was generated correctly from the secret key.
1535    ///
1536    /// At the current time, this is only possible if `sk` either contains a public key (in which case
1537    /// the two pk's are encoded and compared for byte equality), or if `sk` contains a seed
1538    /// (in which case a keygen_from_seed is run and then the pk's compared).
1539    ///
1540    /// Returns either `()` or [SignatureError::ConsistencyCheckFailed].
1541    fn keypair_consistency_check(
1542        pk: &PK,
1543        sk: &SK,
1544    ) -> Result<(), SignatureError>;
1545    /// This provides the first half of the "External Mu" interface to ML-DSA which is described
1546    /// in, and allowed under, NIST's FAQ that accompanies FIPS 204.
1547    ///
1548    /// This function, together with [MLDSATrait::sign_mu] perform a complete ML-DSA signature which is indistinguishable
1549    /// from one produced by the one-shot sign APIs.
1550    ///
1551    /// The utility of this function is exactly as described
1552    /// on Line 6 of Algorithm 7 of FIPS 204:
1553    ///
1554    ///    message representative that may optionally be computed in a different cryptographic module
1555    ///
1556    /// The utility is when an extremely large message needs to be signed, where the message exists on one
1557    /// computing system and the private key to sign it is held on another and either the transfer time or bandwidth
1558    /// causes operational concerns (this is common for example with network HSMs or sending large messages
1559    /// to be signed by a smartcard communicating over near-field radio). Another use case is if the
1560    /// contents of the message are sensitive and the signer does not want to transmit the message itself
1561    /// for fear of leaking it via proxy logging and instead would prefer to only transmit a hash of it.
1562    ///
1563    /// Since "External Mu" mode is well-defined by FIPS 204 and allowed by NIST, the mu value produced here
1564    /// can be used with many hardware crypto modules.
1565    ///
1566    /// This "External Mu" mode of ML-DSA provides an alternative to the HashML-DSA algorithm in that it
1567    /// allows the message to be externally pre-hashed, however, unlike HashML-DSA, this is merely an optimization
1568    /// between the application holding the to-be-signed message and the cryptographic module holding the private key
1569    /// -- in particular, while HashML-DSA requires the verifier to know whether ML-DSA or HashML-DSA was used to sign
1570    /// the message, both "direct" ML-DSA and "External Mu" signatures can be verified with a standard
1571    /// ML-DSA verifier.
1572    ///
1573    /// This function requires the public key hash `tr`, which can be computed from the public key
1574    /// using [MLDSAPublicKeyTrait::compute_tr].
1575    ///
1576    /// For a streaming version of this, see [MuBuilder].
1577    fn compute_mu_from_tr(
1578        msg: &[u8],
1579        ctx: Option<&[u8]>,
1580        tr: &[u8; 64],
1581    ) -> Result<[u8; 64], SignatureError>;
1582    /// Same as [MLDSATrait::compute_mu_from_tr], but extracts tr from the public key.
1583    fn compute_mu_from_pk(
1584        msg: &[u8],
1585        ctx: Option<&[u8]>,
1586        pk: &PK,
1587    ) -> Result<[u8; 64], SignatureError>;
1588    /// Same as [MLDSATrait::compute_mu_from_tr], but extracts tr from the private key.
1589    fn compute_mu_from_sk(
1590        msg: &[u8],
1591        ctx: Option<&[u8]>,
1592        sk: &SK,
1593    ) -> Result<[u8; 64], SignatureError>;
1594    /// Performs an ML-DSA signature using the provided external message representative `mu`.
1595    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
1596    /// FIPS 204 itself, as well as subsequent FAQ documents.
1597    /// This mode uses randomized signing (called "hedged mode" in FIPS 204) using an internal RNG.
1598    fn sign_mu(
1599        sk: &SK,
1600        mu: &[u8; 64],
1601    ) -> Result<[u8; SIG_LEN], SignatureError>;
1602    /// Performs an ML-DSA signature using the provided external message representative `mu`.
1603    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
1604    /// FIPS 204 itself, as well as subsequent FAQ documents.
1605    /// This mode uses randomized signing (called "hedged mode" in FIPS 204) using an internal RNG.
1606    ///
1607    /// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer.
1608    fn sign_mu_out(
1609        sk: &SK,
1610        mu: &[u8; 64],
1611        output: &mut [u8; SIG_LEN],
1612    ) -> Result<usize, SignatureError>;
1613    /// Algorithm 7 ML-DSA.Sign_internal(π‘ π‘˜, 𝑀′, π‘Ÿπ‘›π‘‘)
1614    /// (modified to take an externally-computed mu instead of M')
1615    ///
1616    /// Performs an ML-DSA signature using the provided external message representative `mu`.
1617    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
1618    /// FIPS 204 itself, as well as subsequent FAQ documents.
1619    ///
1620    /// Security note:
1621    /// This mode exposes deterministic signing (called "hedged mode" and allowed by FIPS 204).
1622    /// The ML-DSA algorithm is considered safe to use in deterministic mode, but be aware that
1623    /// the responsibility is on you to ensure that your nonce `rnd` is unique per signature.
1624    /// If not, you may lose some privacy properties; for example it becomes easy to tell if a signer
1625    /// has signed the same message twice or two different messagase, or to tell if the same message
1626    /// has been signed by the same signer twice or two different signers.
1627    ///
1628    /// Since `rnd` should be either a per-signature nonce, or a fixed value, therefore, to help
1629    /// prevent accidental nonce reuse, this function moves `rnd`.
1630    fn sign_mu_deterministic(
1631        sk: &SK,
1632        mu: &[u8; 64],
1633        rnd: [u8; 32],
1634    ) -> Result<[u8; SIG_LEN], SignatureError>;
1635    /// Algorithm 7 ML-DSA.Sign_internal(π‘ π‘˜, 𝑀′, π‘Ÿπ‘›π‘‘)
1636    /// (modified to take an externally-computed mu instead of M')
1637    ///
1638    /// Performs an ML-DSA signature using the provided external message representative `mu`.
1639    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
1640    /// FIPS 204 itself, as well as subsequent FAQ documents.
1641    /// This mode exposes deterministic signing (called "hedged mode" in FIPS 204) using an internal RNG.
1642    ///
1643    /// Since `rnd` should be either a per-signature nonce, or a fixed value, therefore, to help
1644    /// prevent accidental nonce reuse, this function moves `rnd`.
1645    ///
1646    /// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer.
1647    fn sign_mu_deterministic_out(
1648        sk: &SK,
1649        mu: &[u8; 64],
1650        rnd: [u8; 32],
1651        output: &mut [u8; SIG_LEN],
1652    ) -> Result<usize, SignatureError>;
1653    /// This contains a heavily-optimized combined keygen() and sign() which aims to reduce peak
1654    /// memory usage by never having the full secret key in memory at the same time,
1655    /// and by deriving intermediate values piece-wise as needed.
1656    fn sign_mu_deterministic_from_seed(
1657        seed: &KeyMaterialSized<32>,
1658        mu: &[u8; 64],
1659        rnd: [u8; 32],
1660    ) -> Result<[u8; SIG_LEN], SignatureError>;
1661    /// This contains a heavily-optimized combined keygen() and sign() which aims to reduce peak
1662    /// memory usage by never having the full secret key in memory at the same time,
1663    /// and by deriving intermediate values piece-wise as needed.
1664    fn sign_mu_deterministic_from_seed_out(
1665        seed: &KeyMaterialSized<32>,
1666        mu: &[u8; 64],
1667        rnd: [u8; 32],
1668        output: &mut [u8; SIG_LEN],
1669    ) -> Result<usize, SignatureError>;
1670    /// To be used for deterministic signing in conjunction with the [MLDSA44::sign_init], [MLDSA44::sign_update], and [MLDSA44::sign_final] flow.
1671    /// Can be set anywhere after [MLDSA44::sign_init] and before [MLDSA44::sign_final]
1672    fn set_signer_rnd(&mut self, rnd: [u8; 32]);
1673    /// An alternate way to start the streaming signing mode by providing a private key seed instead of an expanded private key
1674    fn sign_init_from_seed(seed: &KeyMaterialSized<32>, ctx: Option<&[u8]>) -> Result<Self, SignatureError>;
1675    /// Algorithm 8 ML-DSA.Verify_internal(π‘π‘˜, 𝑀′, 𝜎)
1676    /// Internal function to verify a signature 𝜎 for a formatted message 𝑀′ .
1677    /// Input: Public key π‘π‘˜ ∈ 𝔹32+32π‘˜(bitlen (π‘žβˆ’1)βˆ’π‘‘) and message 𝑀′ ∈ {0, 1}βˆ— .
1678    /// Input: Signature 𝜎 ∈ π”Ήπœ†/4+β„“β‹…32β‹…(1+bitlen (𝛾1βˆ’1))+πœ”+π‘˜.
1679    fn verify_mu_internal(
1680        pk: &PK,
1681        mu: &[u8; 64],
1682        sig: &[u8; SIG_LEN],
1683    ) -> bool;
1684}
1685
1686impl<
1687    const PK_LEN: usize,
1688    const SK_LEN: usize,
1689    const SIG_LEN: usize,
1690    PK: MLDSAPublicKeyTrait<k, PK_LEN> + MLDSAPublicKeyInternalTrait<k, PK_LEN>,
1691    SK: MLDSAPrivateKeyTrait<k, l, ETA, SK_LEN, PK_LEN> + MLDSAPrivateKeyInternalTrait<k, l, ETA, SK_LEN, PK_LEN>,
1692    const TAU: i32,
1693    const LAMBDA: i32,
1694    const GAMMA1: i32,
1695    const GAMMA2: i32,
1696    const k: usize,
1697    const l: usize,
1698    const ETA: usize,
1699    const BETA: i32,
1700    const OMEGA: i32,
1701    const C_TILDE: usize,
1702    const POLY_Z_PACKED_LEN: usize,
1703    const POLY_W1_PACKED_LEN: usize,
1704    const W1_PACKED_LEN: usize,
1705    const POLY_ETA_PACKED_LEN: usize,
1706    const LAMBDA_over_4: usize,
1707    const GAMMA1_MASK_LEN: usize,
1708> Signature<PK, SK> for MLDSA<
1709    PK_LEN,
1710    SK_LEN,
1711    SIG_LEN,
1712    PK,
1713    SK,
1714    TAU,
1715    LAMBDA,
1716    GAMMA1,
1717    GAMMA2,
1718    k,
1719    l,
1720    ETA,
1721    BETA,
1722    OMEGA,
1723    C_TILDE,
1724    POLY_Z_PACKED_LEN,
1725    POLY_W1_PACKED_LEN,
1726    W1_PACKED_LEN,
1727    POLY_ETA_PACKED_LEN,
1728    LAMBDA_over_4,
1729    GAMMA1_MASK_LEN,
1730> {
1731
1732    fn keygen() -> Result<(PK, SK), SignatureError> {
1733        Self::keygen_from_os_rng()
1734    }
1735
1736    fn sign(sk: &SK, msg: &[u8], ctx: Option<&[u8]>) -> Result<Vec<u8>, SignatureError> {
1737        let mut out = vec![0u8; SIG_LEN];
1738        Self::sign_out(sk, msg, ctx, &mut out)?;
1739
1740        Ok(out)
1741    }
1742
1743    fn sign_out(sk: &SK, msg: &[u8], ctx: Option<&[u8]>, output: &mut [u8]) -> Result<usize, SignatureError> {
1744        let mu = MuBuilder::compute_mu(msg, ctx, &sk.tr())?;
1745        if output.len() < SIG_LEN { return Err(SignatureError::LengthError("Output buffer insufficient size to hold signature")) }
1746        let output_sized: &mut [u8; SIG_LEN] = output[..SIG_LEN].as_mut().try_into().unwrap();
1747        let bytes_written = Self::sign_mu_out(sk, &mu, output_sized)?;
1748
1749        Ok(bytes_written)
1750    }
1751
1752    fn sign_init(sk: &SK, ctx: Option<&[u8]>) -> Result<Self, SignatureError> {
1753        Ok(
1754            Self {
1755                _phantom: PhantomData,
1756                mu_builder: MuBuilder::do_init(&sk.tr(), ctx)?,
1757                signer_rnd: None,
1758                sk: Some(sk.clone()),
1759                seed: None,
1760                pk: None }
1761        )
1762    }
1763
1764    fn sign_update(&mut self, msg_chunk: &[u8]) {
1765        self.mu_builder.do_update(msg_chunk);
1766    }
1767
1768    fn sign_final(self) -> Result<Vec<u8>, SignatureError> {
1769        let mut out = [0u8; SIG_LEN];
1770        self.sign_final_out(&mut out)?;
1771        Ok(Vec::from(out))
1772    }
1773
1774    fn sign_final_out(self, output: &mut [u8]) -> Result<usize, SignatureError> {
1775        let mu = self.mu_builder.do_final();
1776
1777        if self.sk.is_none() && self.seed.is_none() {
1778            return Err(SignatureError::GenericError("Somehow you managed to construct a streaming signer without a private key, impressive!"))
1779        }
1780
1781        if output.len() < SIG_LEN { return Err(SignatureError::LengthError("Output buffer insufficient size to hold signature")) }
1782        let output_sized: &mut [u8; SIG_LEN] = output[..SIG_LEN].as_mut().try_into().unwrap();
1783
1784        if self.sk.is_some() {
1785            if self.signer_rnd.is_none() {
1786                Self::sign_mu_out(&self.sk.unwrap(), &mu, output_sized)
1787            } else {
1788                Self::sign_mu_deterministic_out(&self.sk.unwrap(), &mu, self.signer_rnd.unwrap(), output_sized)
1789            }
1790        } else if self.seed.is_some() {
1791            let rnd = if self.signer_rnd.is_some() {
1792                self.signer_rnd.unwrap()
1793            } else {
1794                let mut rnd: [u8; RND_LEN] = [0u8; RND_LEN];
1795                HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?;
1796                rnd
1797            };
1798            Self::sign_mu_deterministic_from_seed_out(&self.seed.unwrap(), &mu, rnd, output_sized)
1799        } else { unreachable!() }
1800    }
1801
1802    fn verify(pk: &PK, msg: &[u8], ctx: Option<&[u8]>, sig: &[u8]) -> Result<(), SignatureError> {
1803        let mu = MuBuilder::compute_mu(msg, ctx, &pk.compute_tr())?;
1804
1805        if sig.len() != SIG_LEN { return Err(SignatureError::LengthError("Signature value is not the correct length.")) }
1806        if Self::verify_mu_internal(pk, &mu, &sig[..SIG_LEN].try_into().unwrap()) {
1807            Ok(())
1808        } else {
1809            Err(SignatureError::SignatureVerificationFailed)
1810        }
1811    }
1812
1813    fn verify_init(pk: &PK, ctx: Option<&[u8]>) -> Result<Self, SignatureError> {
1814        Ok(
1815            Self {
1816                _phantom: Default::default(),
1817                mu_builder: MuBuilder::do_init(&pk.compute_tr(), ctx)?,
1818                signer_rnd: None,
1819                sk: None,
1820                seed: None,
1821                pk: Some(pk.clone()) }
1822        )
1823    }
1824
1825    fn verify_update(&mut self, msg_chunk: &[u8]) {
1826        self.mu_builder.do_update(msg_chunk);
1827    }
1828
1829    fn verify_final(self, sig: &[u8]) -> Result<(), SignatureError> {
1830        let mu = self.mu_builder.do_final();
1831
1832        assert!(self.pk.is_some(), "Somehow you managed to construct a streaming verifier without a public key, impressive!");
1833
1834        if sig.len() != SIG_LEN { return Err(SignatureError::LengthError("Signature value is not the correct length.")) }
1835        if Self::verify_mu_internal(&self.pk.unwrap(), &mu, &sig[..SIG_LEN].try_into().unwrap()) {
1836            Ok(())
1837        } else {
1838            Err(SignatureError::SignatureVerificationFailed)
1839        }
1840    }
1841}
1842
1843
1844/// Implements parts of Algorithm 2 and Line 6 of Algorithm 7 of FIPS 204.
1845/// Provides a stateful version of [MLDSATrait::compute_mu_from_pk] and [MLDSATrait::compute_mu_from_tr]
1846/// that supports streaming
1847/// large to-be-signed messages.
1848///
1849/// Note: this struct is only exposed for "pure" ML-DSA and not for HashML-DSA because HashML-DSA
1850/// does not benefit from allowing external construction of the message representative mu.
1851/// You can get the same behaviour by computing the pre-hash `ph` with the appropriate hash function
1852/// and providing that to HashMLDSA via [PHSignature::sign_ph].
1853pub struct MuBuilder {
1854    h: H,
1855}
1856
1857impl MuBuilder {
1858    /// Algorithm 7
1859    /// 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀′, 64)
1860    pub fn compute_mu(msg: &[u8], ctx: Option<&[u8]>, tr: &[u8; 64]) -> Result<[u8; 64], SignatureError> {
1861        let mut mu_builder = MuBuilder::do_init(&tr, ctx)?;
1862        mu_builder.do_update(msg);
1863        let mu = mu_builder.do_final();
1864
1865        Ok(mu)
1866    }
1867
1868    /// This function requires the public key hash `tr`, which can be computed from the public key
1869    /// using [MLDSAPublicKeyTrait::compute_tr].
1870    pub fn do_init(tr: &[u8; 64], ctx: Option<&[u8]>) -> Result<Self, SignatureError> {
1871        let ctx = match ctx { Some(ctx) => ctx, None => &[] };
1872
1873        // Algorithm 2
1874        // 1: if |𝑐𝑑π‘₯| > 255 then
1875        if ctx.len() > 255 {
1876            return Err(SignatureError::LengthError("ctx value is longer than 255 bytes"));
1877        }
1878
1879        // Algorithm 7
1880        // 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀', 64)
1881        let mut mb = Self { h: H::new() };
1882        mb.h.absorb(tr);
1883
1884        // Algorithm 2
1885        // 10: 𝑀′ ← BytesToBits(IntegerToBytes(0, 1) βˆ₯ IntegerToBytes(|𝑐𝑑π‘₯|, 1) βˆ₯ 𝑐𝑑π‘₯) βˆ₯ 𝑀
1886        // all done together
1887        mb.h.absorb(&[0u8]);
1888        mb.h.absorb(&[ctx.len() as u8]);
1889        mb.h.absorb(ctx);
1890
1891        // now ready to absorb M
1892        Ok(mb)
1893    }
1894
1895    /// Stream a chunk of the message.
1896    pub fn do_update(&mut self, msg_chunk: &[u8]) {
1897        self.h.absorb(msg_chunk);
1898    }
1899
1900    /// Finalize and return the mu value.
1901    pub fn do_final(mut self) -> [u8; 64] {
1902        // Completion of
1903        // Algorithm 7
1904        // 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀 β€², 64)
1905        let mut mu = [0u8; 64];
1906        self.h.squeeze_out(&mut mu);
1907
1908        mu
1909    }
1910}