Skip to main content

bouncycastle_mldsa_lowmemory/
hash_mldsa.rs

1//! This implements the HashML-DSA algorithm specified in FIPS 204 which is useful for cases
2//! where you need to process the to-be-signed message in chunks, and you cannot use the external mu
3//! mode of [MLDSA]; possibly because you have to digest the message before you know which public key
4//! will sign it.
5//!
6//! HashML-DSA is a full signature algorithm implementing the [Signature] trait:
7//!
8//! ```rust
9//! use bouncycastle_core::errors::SignatureError;
10//! use bouncycastle_core::traits::Signature;
11//! use bouncycastle_mldsa_lowmemory::{MLDSATrait, HashMLDSA65_with_SHA512, HashMLDSA44_with_SHA512};
12//!
13//! let msg = b"The quick brown fox jumped over the lazy dog";
14//!
15//! let (pk, sk) = HashMLDSA65_with_SHA512::keygen().unwrap();
16//!
17//! let sig = HashMLDSA65_with_SHA512::sign(&sk, msg, None).unwrap();
18//! // This is the signature value that you can save to a file or whatever you need.
19//!
20//! match HashMLDSA65_with_SHA512::verify(&pk, msg, None, &sig) {
21//!     Ok(()) => println!("Signature is valid!"),
22//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
23//!     Err(e) => panic!("Something else went wrong: {:?}", e),
24//! }
25//! ```
26//!
27//! But you also have access to the pre-hashed function available from [PHSignature]:
28//!
29//! ```rust
30//! use bouncycastle_core::errors::SignatureError;
31//! use bouncycastle_core::traits::{Signature, PHSignature, Hash};
32//! use bouncycastle_sha2::SHA512;
33//! use bouncycastle_mldsa_lowmemory::{MLDSATrait, HashMLDSA65_with_SHA512, HashMLDSA44_with_SHA512};
34//!
35//! let msg = b"The quick brown fox jumped over the lazy dog";
36//!
37//! // Here, and in contrast to External Mu mode of ML-DSA, we can pre-hash the message before
38//! // even generating the signing key.
39//! let ph: [u8; 64] = SHA512::default().hash(msg).as_slice().try_into().unwrap();
40//!
41//!
42//! let (pk, sk) = HashMLDSA65_with_SHA512::keygen().unwrap();
43//!
44//! let sig = HashMLDSA65_with_SHA512::sign_ph(&sk, &ph, None).unwrap();
45//! // This is the signature value that you can save to a file or whatever you need.
46//!
47//! // This verifies either through the usual one-shot API of the [Signature] trait
48//! match HashMLDSA65_with_SHA512::verify(&pk, msg, None, &sig) {
49//!     Ok(()) => println!("Signature is valid!"),
50//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
51//!     Err(e) => panic!("Something else went wrong: {:?}", e),
52//! }
53//!
54//! // Or though the verify_ph of the [PHSignature] trait
55//! match HashMLDSA65_with_SHA512::verify_ph(&pk, &ph, None, &sig) {
56//!     Ok(()) => println!("Signature is valid!"),
57//!     Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"),
58//!     Err(e) => panic!("Something else went wrong: {:?}", e),
59//! }
60//! ```
61//!
62//! Note that the [HashMLDSA] object is just a light wrapper around [MLDSA], and, for example, they share key types,
63//! so if you need the fancy keygen functions, just use them from [MLDSA].
64//! But a simple [HashMLDSA::keygen] is provided in order to have conformance to the [Signature] trait.
65
66use crate::mldsa::{H, MLDSA_MU_LEN, MLDSA_RND_LEN, MLDSATrait};
67use crate::mldsa::{
68    MLDSA44_BETA, MLDSA44_C_TILDE, MLDSA44_ETA, MLDSA44_FULL_SK_LEN, MLDSA44_GAMMA1,
69    MLDSA44_GAMMA1_MASK_LEN, MLDSA44_GAMMA2, MLDSA44_LAMBDA, MLDSA44_LAMBDA_over_4, MLDSA44_OMEGA,
70    MLDSA44_PK_LEN, MLDSA44_POLY_ETA_PACKED_LEN, MLDSA44_POLY_W1_PACKED_LEN,
71    MLDSA44_POLY_Z_PACKED_LEN, MLDSA44_S1_PACKED_LEN, MLDSA44_S2_PACKED_LEN, MLDSA44_SIG_LEN,
72    MLDSA44_SK_LEN, MLDSA44_TAU, MLDSA44_k, MLDSA44_l,
73};
74use crate::mldsa::{MLDSA44_T1_PACKED_LEN, MLDSA65_T1_PACKED_LEN, MLDSA87_T1_PACKED_LEN};
75use crate::mldsa::{
76    MLDSA65_BETA, MLDSA65_C_TILDE, MLDSA65_ETA, MLDSA65_FULL_SK_LEN, MLDSA65_GAMMA1,
77    MLDSA65_GAMMA1_MASK_LEN, MLDSA65_GAMMA2, MLDSA65_LAMBDA, MLDSA65_LAMBDA_over_4, MLDSA65_OMEGA,
78    MLDSA65_PK_LEN, MLDSA65_POLY_ETA_PACKED_LEN, MLDSA65_POLY_W1_PACKED_LEN,
79    MLDSA65_POLY_Z_PACKED_LEN, MLDSA65_S1_PACKED_LEN, MLDSA65_S2_PACKED_LEN, MLDSA65_SIG_LEN,
80    MLDSA65_SK_LEN, MLDSA65_TAU, MLDSA65_k, MLDSA65_l,
81};
82use crate::mldsa::{
83    MLDSA87_BETA, MLDSA87_C_TILDE, MLDSA87_ETA, MLDSA87_FULL_SK_LEN, MLDSA87_GAMMA1,
84    MLDSA87_GAMMA1_MASK_LEN, MLDSA87_GAMMA2, MLDSA87_LAMBDA, MLDSA87_LAMBDA_over_4, MLDSA87_OMEGA,
85    MLDSA87_PK_LEN, MLDSA87_POLY_ETA_PACKED_LEN, MLDSA87_POLY_W1_PACKED_LEN,
86    MLDSA87_POLY_Z_PACKED_LEN, MLDSA87_S1_PACKED_LEN, MLDSA87_S2_PACKED_LEN, MLDSA87_SIG_LEN,
87    MLDSA87_SK_LEN, MLDSA87_TAU, MLDSA87_k, MLDSA87_l,
88};
89use crate::mldsa_keys::{MLDSAPrivateKeyInternalTrait, MLDSAPublicKeyInternalTrait};
90use crate::{
91    MLDSA, MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65PrivateKey, MLDSA65PublicKey,
92    MLDSA87PrivateKey, MLDSA87PublicKey, MLDSAPrivateKeyTrait, MLDSAPublicKeyTrait,
93};
94use bouncycastle_core::errors::SignatureError;
95use bouncycastle_core::key_material::KeyMaterial;
96use bouncycastle_core::traits::{
97    Algorithm, Hash, PHSignature, RNG, SecurityStrength, Signature, XOF,
98};
99use bouncycastle_rng::HashDRBG_SHA512;
100use bouncycastle_sha2::{SHA256, SHA512};
101use core::marker::PhantomData;
102
103// Imports needed only for docs
104#[allow(unused_imports)]
105use crate::mldsa::MuBuilder;
106
107const SHA256_OID: &[u8] = &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01];
108const SHA512_OID: &[u8] = &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03];
109
110/*** Constants ***/
111
112///
113pub const HASH_ML_DSA_44_with_SHA256_NAME: &str = "HashML-DSA-44_with_SHA256";
114///
115pub const HASH_ML_DSA_65_WITH_SHA256_NAME: &str = "HashML-DSA-65_with_SHA256";
116///
117pub const HASH_ML_DSA_87_with_SHA256_NAME: &str = "HashML-DSA-87_with_SHA256";
118///
119pub const HASH_ML_DSA_44_with_SHA512_NAME: &str = "HashML-DSA-44_with_SHA512";
120///
121pub const HASH_ML_DSA_65_WITH_SHA512_NAME: &str = "HashML-DSA-65_with_SHA512";
122///
123pub const HASH_ML_DSA_87_WITH_SHA512_NAME: &str = "HashML-DSA-87_with_SHA512";
124
125/*** Pub Types ***/
126
127/// The HashML-DSA-44_with_SHA512 signature algorithm.
128#[allow(non_camel_case_types)]
129pub type HashMLDSA44_with_SHA256 = HashMLDSA<
130    SHA256,
131    32,
132    SHA256_OID,
133    MLDSA44_PK_LEN,
134    MLDSA44_SK_LEN,
135    MLDSA44_FULL_SK_LEN,
136    MLDSA44_SIG_LEN,
137    MLDSA44PublicKey,
138    MLDSA44PrivateKey,
139    MLDSA44_TAU,
140    MLDSA44_LAMBDA,
141    MLDSA44_GAMMA1,
142    MLDSA44_GAMMA2,
143    MLDSA44_k,
144    MLDSA44_l,
145    MLDSA44_ETA,
146    MLDSA44_BETA,
147    MLDSA44_OMEGA,
148    MLDSA44_C_TILDE,
149    MLDSA44_POLY_Z_PACKED_LEN,
150    MLDSA44_POLY_W1_PACKED_LEN,
151    MLDSA44_S1_PACKED_LEN,
152    MLDSA44_S2_PACKED_LEN,
153    MLDSA44_T1_PACKED_LEN,
154    MLDSA44_POLY_ETA_PACKED_LEN,
155    MLDSA44_LAMBDA_over_4,
156    MLDSA44_GAMMA1_MASK_LEN,
157>;
158
159impl Algorithm for HashMLDSA44_with_SHA256 {
160    const ALG_NAME: &'static str = HASH_ML_DSA_44_with_SHA256_NAME;
161    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit;
162}
163
164/// The HashML-DSA-65_with_SHA256 signature algorithm.
165#[allow(non_camel_case_types)]
166pub type HashMLDSA65_with_SHA256 = HashMLDSA<
167    SHA256,
168    32,
169    SHA256_OID,
170    MLDSA65_PK_LEN,
171    MLDSA65_SK_LEN,
172    MLDSA65_FULL_SK_LEN,
173    MLDSA65_SIG_LEN,
174    MLDSA65PublicKey,
175    MLDSA65PrivateKey,
176    MLDSA65_TAU,
177    MLDSA65_LAMBDA,
178    MLDSA65_GAMMA1,
179    MLDSA65_GAMMA2,
180    MLDSA65_k,
181    MLDSA65_l,
182    MLDSA65_ETA,
183    MLDSA65_BETA,
184    MLDSA65_OMEGA,
185    MLDSA65_C_TILDE,
186    MLDSA65_POLY_Z_PACKED_LEN,
187    MLDSA65_POLY_W1_PACKED_LEN,
188    MLDSA65_S1_PACKED_LEN,
189    MLDSA65_S2_PACKED_LEN,
190    MLDSA65_T1_PACKED_LEN,
191    MLDSA65_POLY_ETA_PACKED_LEN,
192    MLDSA65_LAMBDA_over_4,
193    MLDSA65_GAMMA1_MASK_LEN,
194>;
195
196impl Algorithm for HashMLDSA65_with_SHA256 {
197    const ALG_NAME: &'static str = HASH_ML_DSA_65_WITH_SHA256_NAME;
198    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit;
199}
200
201/// The HashML-DSA-87_with_SHA256 signature algorithm.
202#[allow(non_camel_case_types)]
203pub type HashMLDSA87_with_SHA256 = HashMLDSA<
204    SHA256,
205    32,
206    SHA256_OID,
207    MLDSA87_PK_LEN,
208    MLDSA87_SK_LEN,
209    MLDSA87_FULL_SK_LEN,
210    MLDSA87_SIG_LEN,
211    MLDSA87PublicKey,
212    MLDSA87PrivateKey,
213    MLDSA87_TAU,
214    MLDSA87_LAMBDA,
215    MLDSA87_GAMMA1,
216    MLDSA87_GAMMA2,
217    MLDSA87_k,
218    MLDSA87_l,
219    MLDSA87_ETA,
220    MLDSA87_BETA,
221    MLDSA87_OMEGA,
222    MLDSA87_C_TILDE,
223    MLDSA87_POLY_Z_PACKED_LEN,
224    MLDSA87_POLY_W1_PACKED_LEN,
225    MLDSA87_S1_PACKED_LEN,
226    MLDSA87_S2_PACKED_LEN,
227    MLDSA87_T1_PACKED_LEN,
228    MLDSA87_POLY_ETA_PACKED_LEN,
229    MLDSA87_LAMBDA_over_4,
230    MLDSA87_GAMMA1_MASK_LEN,
231>;
232
233impl Algorithm for HashMLDSA87_with_SHA256 {
234    const ALG_NAME: &'static str = HASH_ML_DSA_87_with_SHA256_NAME;
235    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit;
236}
237
238/// The HashML-DSA-44_with_SHA512 signature algorithm.
239#[allow(non_camel_case_types)]
240pub type HashMLDSA44_with_SHA512 = HashMLDSA<
241    SHA512,
242    64,
243    SHA512_OID,
244    MLDSA44_PK_LEN,
245    MLDSA44_SK_LEN,
246    MLDSA44_FULL_SK_LEN,
247    MLDSA44_SIG_LEN,
248    MLDSA44PublicKey,
249    MLDSA44PrivateKey,
250    MLDSA44_TAU,
251    MLDSA44_LAMBDA,
252    MLDSA44_GAMMA1,
253    MLDSA44_GAMMA2,
254    MLDSA44_k,
255    MLDSA44_l,
256    MLDSA44_ETA,
257    MLDSA44_BETA,
258    MLDSA44_OMEGA,
259    MLDSA44_C_TILDE,
260    MLDSA44_POLY_Z_PACKED_LEN,
261    MLDSA44_POLY_W1_PACKED_LEN,
262    MLDSA44_S1_PACKED_LEN,
263    MLDSA44_S2_PACKED_LEN,
264    MLDSA44_T1_PACKED_LEN,
265    MLDSA44_POLY_ETA_PACKED_LEN,
266    MLDSA44_LAMBDA_over_4,
267    MLDSA44_GAMMA1_MASK_LEN,
268>;
269
270impl Algorithm for HashMLDSA44_with_SHA512 {
271    const ALG_NAME: &'static str = HASH_ML_DSA_44_with_SHA512_NAME;
272    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit;
273}
274
275/// The HashML-DSA-65_with_SHA512 signature algorithm.
276#[allow(non_camel_case_types)]
277pub type HashMLDSA65_with_SHA512 = HashMLDSA<
278    SHA512,
279    64,
280    SHA512_OID,
281    MLDSA65_PK_LEN,
282    MLDSA65_SK_LEN,
283    MLDSA65_FULL_SK_LEN,
284    MLDSA65_SIG_LEN,
285    MLDSA65PublicKey,
286    MLDSA65PrivateKey,
287    MLDSA65_TAU,
288    MLDSA65_LAMBDA,
289    MLDSA65_GAMMA1,
290    MLDSA65_GAMMA2,
291    MLDSA65_k,
292    MLDSA65_l,
293    MLDSA65_ETA,
294    MLDSA65_BETA,
295    MLDSA65_OMEGA,
296    MLDSA65_C_TILDE,
297    MLDSA65_POLY_Z_PACKED_LEN,
298    MLDSA65_POLY_W1_PACKED_LEN,
299    MLDSA65_S1_PACKED_LEN,
300    MLDSA65_S2_PACKED_LEN,
301    MLDSA65_T1_PACKED_LEN,
302    MLDSA65_POLY_ETA_PACKED_LEN,
303    MLDSA65_LAMBDA_over_4,
304    MLDSA65_GAMMA1_MASK_LEN,
305>;
306
307impl Algorithm for HashMLDSA65_with_SHA512 {
308    const ALG_NAME: &'static str = HASH_ML_DSA_65_WITH_SHA512_NAME;
309    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit;
310}
311
312/// The HashML-DSA-87_with_SHA512 signature algorithm.
313#[allow(non_camel_case_types)]
314pub type HashMLDSA87_with_SHA512 = HashMLDSA<
315    SHA512,
316    64,
317    SHA512_OID,
318    MLDSA87_PK_LEN,
319    MLDSA87_SK_LEN,
320    MLDSA87_FULL_SK_LEN,
321    MLDSA87_SIG_LEN,
322    MLDSA87PublicKey,
323    MLDSA87PrivateKey,
324    MLDSA87_TAU,
325    MLDSA87_LAMBDA,
326    MLDSA87_GAMMA1,
327    MLDSA87_GAMMA2,
328    MLDSA87_k,
329    MLDSA87_l,
330    MLDSA87_ETA,
331    MLDSA87_BETA,
332    MLDSA87_OMEGA,
333    MLDSA87_C_TILDE,
334    MLDSA87_POLY_Z_PACKED_LEN,
335    MLDSA87_POLY_W1_PACKED_LEN,
336    MLDSA87_S1_PACKED_LEN,
337    MLDSA87_S2_PACKED_LEN,
338    MLDSA87_T1_PACKED_LEN,
339    MLDSA87_POLY_ETA_PACKED_LEN,
340    MLDSA87_LAMBDA_over_4,
341    MLDSA87_GAMMA1_MASK_LEN,
342>;
343
344impl Algorithm for HashMLDSA87_with_SHA512 {
345    const ALG_NAME: &'static str = HASH_ML_DSA_87_WITH_SHA512_NAME;
346    const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit;
347}
348
349/// An instance of the HashML-DSA algorithm.
350///
351/// We are exposing the HashMLDSA struct this way so that alternative hash functions can be used
352/// without requiring modification of this source code; you can add your own hash function
353/// by specifying the hash function to use (in the verifier), and specifying the bytes of the OID to
354/// to use as its domain separator in constructing the message representative M'.
355pub struct HashMLDSA<
356    HASH: Hash + Default,
357    const HASH_LEN: usize,
358    const oid: &'static [u8],
359    const PK_LEN: usize,
360    const SK_LEN: usize,
361    const FULL_SK_LEN: usize,
362    const SIG_LEN: usize,
363    PK: MLDSAPublicKeyTrait<k, T1_PACKED_LEN, PK_LEN>
364        + MLDSAPublicKeyInternalTrait<k, T1_PACKED_LEN, PK_LEN>,
365    SK: MLDSAPrivateKeyTrait<
366            k,
367            l,
368            S1_PACKED_LEN,
369            S2_PACKED_LEN,
370            T1_PACKED_LEN,
371            PK_LEN,
372            SK_LEN,
373            FULL_SK_LEN,
374        > + MLDSAPrivateKeyInternalTrait<
375            LAMBDA,
376            GAMMA2,
377            k,
378            l,
379            ETA,
380            S1_PACKED_LEN,
381            S2_PACKED_LEN,
382            PK_LEN,
383            SK_LEN,
384        >,
385    const TAU: i32,
386    const LAMBDA: i32,
387    const GAMMA1: i32,
388    const GAMMA2: i32,
389    const k: usize,
390    const l: usize,
391    const ETA: usize,
392    const BETA: i32,
393    const OMEGA: i32,
394    const C_TILDE: usize,
395    const POLY_Z_PACKED_LEN: usize,
396    const POLY_W1_PACKED_LEN: usize,
397    const S1_PACKED_LEN: usize,
398    const S2_PACKED_LEN: usize,
399    const T1_PACKED_LEN: usize,
400    const POLY_ETA_PACKED_LEN: usize,
401    const LAMBDA_over_4: usize,
402    const GAMMA1_MASK_LEN: usize,
403> {
404    _phantom: PhantomData<(PK, SK)>,
405
406    signer_rnd: Option<[u8; MLDSA_RND_LEN]>,
407
408    /// only used in streaming sign operations
409    sk: Option<SK>,
410
411    /// only used in streaming sign operations instead of sk
412    seed: Option<KeyMaterial<32>>,
413
414    /// only used in streaming verify operations
415    pk: Option<PK>,
416
417    /// Hash function instance for streaming message hashing
418    hash: HASH,
419
420    /// Since HashML-DSA does message buffering in the external pre-hash, not in mu,
421    /// we'll need to save this for later
422    ctx: [u8; 255],
423    ctx_len: usize,
424}
425
426impl<
427    HASH: Hash + Default,
428    const PH_LEN: usize,
429    const oid: &'static [u8],
430    const PK_LEN: usize,
431    const SK_LEN: usize,
432    const FULL_SK_LEN: usize,
433    const SIG_LEN: usize,
434    PK: MLDSAPublicKeyTrait<k, T1_PACKED_LEN, PK_LEN>
435        + MLDSAPublicKeyInternalTrait<k, T1_PACKED_LEN, PK_LEN>,
436    SK: MLDSAPrivateKeyTrait<
437            k,
438            l,
439            S1_PACKED_LEN,
440            S2_PACKED_LEN,
441            T1_PACKED_LEN,
442            PK_LEN,
443            SK_LEN,
444            FULL_SK_LEN,
445        > + MLDSAPrivateKeyInternalTrait<
446            LAMBDA,
447            GAMMA2,
448            k,
449            l,
450            ETA,
451            S1_PACKED_LEN,
452            S2_PACKED_LEN,
453            PK_LEN,
454            SK_LEN,
455        >,
456    const TAU: i32,
457    const LAMBDA: i32,
458    const GAMMA1: i32,
459    const GAMMA2: i32,
460    const k: usize,
461    const l: usize,
462    const ETA: usize,
463    const BETA: i32,
464    const OMEGA: i32,
465    const C_TILDE: usize,
466    const POLY_Z_PACKED_LEN: usize,
467    const POLY_W1_PACKED_LEN: usize,
468    const S1_PACKED_LEN: usize,
469    const S2_PACKED_LEN: usize,
470    const T1_PACKED_LEN: usize,
471    const POLY_ETA_PACKED_LEN: usize,
472    const LAMBDA_over_4: usize,
473    const GAMMA1_MASK_LEN: usize,
474>
475    HashMLDSA<
476        HASH,
477        PH_LEN,
478        oid,
479        PK_LEN,
480        SK_LEN,
481        FULL_SK_LEN,
482        SIG_LEN,
483        PK,
484        SK,
485        TAU,
486        LAMBDA,
487        GAMMA1,
488        GAMMA2,
489        k,
490        l,
491        ETA,
492        BETA,
493        OMEGA,
494        C_TILDE,
495        POLY_Z_PACKED_LEN,
496        POLY_W1_PACKED_LEN,
497        S1_PACKED_LEN,
498        S2_PACKED_LEN,
499        T1_PACKED_LEN,
500        POLY_ETA_PACKED_LEN,
501        LAMBDA_over_4,
502        GAMMA1_MASK_LEN,
503    >
504{
505    /// Imports a secret key from a seed.
506    pub fn keygen_from_seed(seed: &KeyMaterial<32>) -> Result<(PK, SK), SignatureError> {
507        MLDSA::<
508            PK_LEN,
509            SK_LEN,
510            FULL_SK_LEN,
511            SIG_LEN,
512            PK,
513            SK,
514            TAU,
515            LAMBDA,
516            GAMMA1,
517            GAMMA2,
518            k,
519            l,
520            ETA,
521            BETA,
522            OMEGA,
523            C_TILDE,
524            POLY_Z_PACKED_LEN,
525            POLY_W1_PACKED_LEN,
526            S1_PACKED_LEN,
527            S2_PACKED_LEN,
528            T1_PACKED_LEN,
529            POLY_ETA_PACKED_LEN,
530            LAMBDA_over_4,
531            GAMMA1_MASK_LEN,
532        >::keygen_internal(seed)
533    }
534
535    /// Algorithm 7 ML-DSA.Sign_internal(π‘ π‘˜, 𝑀′, π‘Ÿπ‘›π‘‘)
536    /// (modified to take an externally-computed ph instead of M', thus combining Algorithm 4 with Algorithm 7).
537    ///
538    /// Security note:
539    /// This mode exposes deterministic signing (called "hedged mode" and allowed by FIPS 204).
540    /// The ML-DSA algorithm is considered safe to use in deterministic mode, but be aware that
541    /// the responsibility is on you to ensure that your nonce `rnd` is unique per signature.
542    /// If not, you may lose some privacy properties; for example it becomes easy to tell if a signer
543    /// has signed the same message twice or two different messages, or to tell if the same message
544    /// has been signed by the same signer twice or two different signers.
545    ///
546    /// Since `rnd` should be either a per-signature nonce, or a fixed value, therefore, to help
547    /// prevent accidental nonce reuse, this function moves `rnd`.
548    pub fn sign_ph_deterministic(
549        sk: &SK,
550        ctx: Option<&[u8]>,
551        ph: &[u8; PH_LEN],
552        rnd: [u8; 32],
553    ) -> Result<[u8; SIG_LEN], SignatureError> {
554        let mut out: [u8; SIG_LEN] = [0u8; SIG_LEN];
555        Self::sign_ph_deterministic_out(sk, ctx, ph, rnd, &mut out)?;
556        Ok(out)
557    }
558
559    /// Algorithm 7 ML-DSA.Sign_internal(π‘ π‘˜, 𝑀′, π‘Ÿπ‘›π‘‘)
560    /// (modified to take an externally-computed ph instead of M', thus combining Algorithm 4 with Algorithm 7).
561    ///
562    /// Performs an ML-DSA signature using the provided external message representative `mu`.
563    /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both
564    /// FIPS 204 itself, as well as subsequent FAQ documents.
565    /// This mode exposes deterministic signing (called "hedged mode" in FIPS 204) using an internal RNG.
566    ///
567    /// Since `rnd` should be either a per-signature nonce, or a fixed value, therefore, to help
568    /// prevent accidental nonce reuse, this function moves `rnd`.
569    ///
570    /// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer.
571    pub fn sign_ph_deterministic_out(
572        sk: &SK,
573        ctx: Option<&[u8]>,
574        ph: &[u8; PH_LEN],
575        rnd: [u8; 32],
576        output: &mut [u8; SIG_LEN],
577    ) -> Result<usize, SignatureError> {
578        let ctx = if ctx.is_some() { ctx.unwrap() } else { &[] };
579
580        // Algorithm 4
581        // 1: if |𝑐𝑑π‘₯| > 255 then
582        if ctx.len() > 255 {
583            return Err(SignatureError::LengthError("ctx value is longer than 255 bytes"));
584        }
585
586        // Algorithm 7
587        // 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀', 64)
588        let mut h = H::new();
589        h.absorb(&sk.tr());
590
591        // Algorithm 4
592        // 23: 𝑀' ← BytesToBits(IntegerToBytes(1, 1) βˆ₯ IntegerToBytes(|𝑐𝑑π‘₯|, 1) βˆ₯ 𝑐𝑑π‘₯ βˆ₯ OID βˆ₯ PH𝑀)
593        // all done together
594        h.absorb(&[1u8]);
595        h.absorb(&[ctx.len() as u8]);
596        h.absorb(ctx);
597        h.absorb(oid);
598        h.absorb(ph);
599        let mut mu = [0u8; MLDSA_MU_LEN];
600        let bytes_written = h.squeeze_out(&mut mu);
601        debug_assert_eq!(bytes_written, MLDSA_MU_LEN);
602
603        // 24: 𝜎 ← ML-DSA.Sign_internal(π‘ π‘˜, 𝑀', π‘Ÿπ‘›π‘‘)
604        let bytes_written = MLDSA::<
605            PK_LEN,
606            SK_LEN,
607            FULL_SK_LEN,
608            SIG_LEN,
609            PK,
610            SK,
611            TAU,
612            LAMBDA,
613            GAMMA1,
614            GAMMA2,
615            k,
616            l,
617            ETA,
618            BETA,
619            OMEGA,
620            C_TILDE,
621            POLY_Z_PACKED_LEN,
622            POLY_W1_PACKED_LEN,
623            S1_PACKED_LEN,
624            S2_PACKED_LEN,
625            T1_PACKED_LEN,
626            POLY_ETA_PACKED_LEN,
627            LAMBDA_over_4,
628            GAMMA1_MASK_LEN,
629        >::sign_mu_deterministic_out(sk, &mu, rnd, output)?;
630
631        Ok(bytes_written)
632    }
633
634    /// To be used for deterministic signing in conjunction with the [Signature::sign_init],
635    /// [Signature::sign_update], and [Signature::sign_final] flow.
636    /// Can be set anywhere after [Signature::sign_init] and before [Signature::sign_final]
637    pub fn set_signer_rnd(&mut self, rnd: [u8; 32]) {
638        self.signer_rnd = Some(rnd);
639    }
640
641    fn parse_ctx(ctx: Option<&[u8]>) -> Result<([u8; 255], usize), SignatureError> {
642        if ctx.is_some() {
643            // Algorithm 2
644            // 1: if |𝑐𝑑π‘₯| > 255 then
645            if ctx.unwrap().len() > 255 {
646                return Err(SignatureError::LengthError("ctx value is longer than 255 bytes"));
647            }
648
649            let mut ctx_buf = [0u8; 255];
650            ctx_buf[..ctx.unwrap().len()].copy_from_slice(ctx.unwrap());
651            Ok((ctx_buf, ctx.unwrap().len()))
652        } else {
653            Ok(([0u8; 255], 0))
654        }
655    }
656
657    /// Alternative initialization of the streaming signer where you have your private key
658    /// as a seed and you want to delay its expansion as late as possible for memory-usage reasons.
659    pub fn sign_init_from_seed(
660        seed: &KeyMaterial<32>,
661        ctx: Option<&[u8]>,
662    ) -> Result<Self, SignatureError> {
663        let (ctx, ctx_len) = Self::parse_ctx(ctx)?;
664        Ok(Self {
665            _phantom: PhantomData,
666            signer_rnd: None,
667            sk: None,
668            seed: Some(seed.clone()),
669            pk: None,
670            hash: HASH::default(),
671            ctx,
672            ctx_len,
673        })
674    }
675}
676
677impl<
678    HASH: Hash + Default,
679    PK: MLDSAPublicKeyTrait<k, T1_PACKED_LEN, PK_LEN>
680        + MLDSAPublicKeyInternalTrait<k, T1_PACKED_LEN, PK_LEN>,
681    SK: MLDSAPrivateKeyTrait<
682            k,
683            l,
684            S1_PACKED_LEN,
685            S2_PACKED_LEN,
686            T1_PACKED_LEN,
687            PK_LEN,
688            SK_LEN,
689            FULL_SK_LEN,
690        > + MLDSAPrivateKeyInternalTrait<
691            LAMBDA,
692            GAMMA2,
693            k,
694            l,
695            ETA,
696            S1_PACKED_LEN,
697            S2_PACKED_LEN,
698            PK_LEN,
699            SK_LEN,
700        >,
701    const PH_LEN: usize,
702    const oid: &'static [u8],
703    const PK_LEN: usize,
704    const SK_LEN: usize,
705    const FULL_SK_LEN: usize,
706    const SIG_LEN: usize,
707    const TAU: i32,
708    const LAMBDA: i32,
709    const GAMMA1: i32,
710    const GAMMA2: i32,
711    const k: usize,
712    const l: usize,
713    const ETA: usize,
714    const BETA: i32,
715    const OMEGA: i32,
716    const C_TILDE: usize,
717    const POLY_Z_PACKED_LEN: usize,
718    const POLY_W1_PACKED_LEN: usize,
719    const S1_PACKED_LEN: usize,
720    const S2_PACKED_LEN: usize,
721    const T1_PACKED_LEN: usize,
722    const POLY_ETA_PACKED_LEN: usize,
723    const LAMBDA_over_4: usize,
724    const GAMMA1_MASK_LEN: usize,
725> Signature<PK, SK, PK_LEN, SK_LEN, SIG_LEN>
726    for HashMLDSA<
727        HASH,
728        PH_LEN,
729        oid,
730        PK_LEN,
731        SK_LEN,
732        FULL_SK_LEN,
733        SIG_LEN,
734        PK,
735        SK,
736        TAU,
737        LAMBDA,
738        GAMMA1,
739        GAMMA2,
740        k,
741        l,
742        ETA,
743        BETA,
744        OMEGA,
745        C_TILDE,
746        POLY_Z_PACKED_LEN,
747        POLY_W1_PACKED_LEN,
748        S1_PACKED_LEN,
749        S2_PACKED_LEN,
750        T1_PACKED_LEN,
751        POLY_ETA_PACKED_LEN,
752        LAMBDA_over_4,
753        GAMMA1_MASK_LEN,
754    >
755{
756    /// Keygen, and keys in general, are interchangeable between MLDSA and HashMLDSA.
757    fn keygen() -> Result<(PK, SK), SignatureError> {
758        MLDSA::<
759            PK_LEN,
760            SK_LEN,
761            FULL_SK_LEN,
762            SIG_LEN,
763            PK,
764            SK,
765            TAU,
766            LAMBDA,
767            GAMMA1,
768            GAMMA2,
769            k,
770            l,
771            ETA,
772            BETA,
773            OMEGA,
774            C_TILDE,
775            POLY_Z_PACKED_LEN,
776            POLY_W1_PACKED_LEN,
777            S1_PACKED_LEN,
778            S2_PACKED_LEN,
779            T1_PACKED_LEN,
780            POLY_ETA_PACKED_LEN,
781            LAMBDA_over_4,
782            GAMMA1_MASK_LEN,
783        >::keygen()
784    }
785
786    /// Algorithm 4 HashML-DSA.Sign(π‘ π‘˜, 𝑀 , 𝑐𝑑π‘₯, PH)
787    /// Generate a β€œpre-hash” ML-DSA signature.
788    fn sign(sk: &SK, msg: &[u8], ctx: Option<&[u8]>) -> Result<[u8; SIG_LEN], SignatureError> {
789        let mut out = [0u8; SIG_LEN];
790        Self::sign_out(sk, msg, ctx, &mut out)?;
791
792        Ok(out)
793    }
794
795    fn sign_out(
796        sk: &SK,
797        msg: &[u8],
798        ctx: Option<&[u8]>,
799        output: &mut [u8; SIG_LEN],
800    ) -> Result<usize, SignatureError> {
801        let mut ph_m = [0u8; PH_LEN];
802        _ = HASH::default().hash_out(msg, &mut ph_m);
803        Self::sign_ph_out(sk, &ph_m, ctx, output)
804    }
805
806    fn sign_init(sk: &SK, ctx: Option<&[u8]>) -> Result<Self, SignatureError> {
807        let (ctx, ctx_len) = Self::parse_ctx(ctx)?;
808        Ok(Self {
809            _phantom: PhantomData,
810            signer_rnd: None,
811            sk: Some(sk.clone()),
812            seed: None,
813            pk: None,
814            hash: HASH::default(),
815            ctx,
816            ctx_len,
817        })
818    }
819
820    fn sign_update(&mut self, msg_chunk: &[u8]) {
821        self.hash.do_update(msg_chunk);
822    }
823
824    fn sign_final(self) -> Result<[u8; SIG_LEN], SignatureError> {
825        let mut out = [0u8; SIG_LEN];
826        self.sign_final_out(&mut out)?;
827        Ok(out)
828    }
829
830    fn sign_final_out(self, output: &mut [u8; SIG_LEN]) -> Result<usize, SignatureError> {
831        let ph: [u8; PH_LEN] = self.hash.do_final().try_into().unwrap();
832
833        if self.sk.is_none() && self.seed.is_none() {
834            return Err(SignatureError::GenericError(
835                "Somehow you managed to construct a streaming signer without a private key, impressive!",
836            ));
837        }
838
839        if output.len() < SIG_LEN {
840            return Err(SignatureError::LengthError(
841                "Output buffer insufficient size to hold signature",
842            ));
843        }
844        let output_sized: &mut [u8; SIG_LEN] = output[..SIG_LEN].as_mut().try_into().unwrap();
845
846        if self.sk.is_some() {
847            if self.signer_rnd.is_none() {
848                Self::sign_ph_out(
849                    &self.sk.unwrap(),
850                    &ph,
851                    Some(&self.ctx[..self.ctx_len]),
852                    output_sized,
853                )
854            } else {
855                Self::sign_ph_deterministic_out(
856                    &self.sk.unwrap(),
857                    Some(&self.ctx[..self.ctx_len]),
858                    &ph,
859                    self.signer_rnd.unwrap(),
860                    output_sized,
861                )
862            }
863        } else if self.seed.is_some() {
864            let rnd = if self.signer_rnd.is_some() {
865                self.signer_rnd.unwrap()
866            } else {
867                let mut rnd: [u8; MLDSA_RND_LEN] = [0u8; MLDSA_RND_LEN];
868                HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?;
869                rnd
870            };
871            // since at this point we need to fully reconstruct SK in order to compute tr for mu anyway
872            // there is no savings to using the fancy MLDSA::sign_from_seed
873            let (_pk, sk) = Self::keygen_from_seed(&self.seed.unwrap())?;
874            Self::sign_ph_deterministic_out(
875                &sk,
876                Some(&self.ctx[..self.ctx_len]),
877                &ph,
878                rnd,
879                output_sized,
880            )
881        } else {
882            unreachable!()
883        }
884    }
885
886    fn verify(pk: &PK, msg: &[u8], ctx: Option<&[u8]>, sig: &[u8]) -> Result<(), SignatureError> {
887        let mut ph_m = [0u8; PH_LEN];
888        _ = HASH::default().hash_out(msg, &mut ph_m);
889
890        Self::verify_ph(pk, &ph_m, ctx, sig)
891    }
892
893    fn verify_init(pk: &PK, ctx: Option<&[u8]>) -> Result<Self, SignatureError> {
894        let (ctx, ctx_len) = Self::parse_ctx(ctx)?;
895        Ok(Self {
896            _phantom: Default::default(),
897            signer_rnd: None,
898            sk: None,
899            seed: None,
900            pk: Some(pk.clone()),
901            hash: HASH::default(),
902            ctx,
903            ctx_len,
904        })
905    }
906
907    fn verify_update(&mut self, msg_chunk: &[u8]) {
908        self.hash.do_update(msg_chunk);
909    }
910
911    fn verify_final(self, sig: &[u8]) -> Result<(), SignatureError> {
912        assert!(
913            self.pk.is_some(),
914            "Somehow you managed to construct a streaming verifier without a public key, impressive!"
915        );
916        let ph: [u8; PH_LEN] = self.hash.do_final().try_into().unwrap();
917        Self::verify_ph(&self.pk.unwrap(), &ph, Some(&self.ctx[..self.ctx_len]), &sig[..SIG_LEN])
918    }
919}
920
921impl<
922    HASH: Hash + Default,
923    const PH_LEN: usize,
924    const oid: &'static [u8],
925    const PK_LEN: usize,
926    const SK_LEN: usize,
927    const FULL_SK_LEN: usize,
928    const SIG_LEN: usize,
929    PK: MLDSAPublicKeyTrait<k, T1_PACKED_LEN, PK_LEN>
930        + MLDSAPublicKeyInternalTrait<k, T1_PACKED_LEN, PK_LEN>,
931    SK: MLDSAPrivateKeyTrait<
932            k,
933            l,
934            S1_PACKED_LEN,
935            S2_PACKED_LEN,
936            T1_PACKED_LEN,
937            PK_LEN,
938            SK_LEN,
939            FULL_SK_LEN,
940        > + MLDSAPrivateKeyInternalTrait<
941            LAMBDA,
942            GAMMA2,
943            k,
944            l,
945            ETA,
946            S1_PACKED_LEN,
947            S2_PACKED_LEN,
948            PK_LEN,
949            SK_LEN,
950        >,
951    const TAU: i32,
952    const LAMBDA: i32,
953    const GAMMA1: i32,
954    const GAMMA2: i32,
955    const k: usize,
956    const l: usize,
957    const ETA: usize,
958    const BETA: i32,
959    const OMEGA: i32,
960    const C_TILDE: usize,
961    const POLY_Z_PACKED_LEN: usize,
962    const POLY_W1_PACKED_LEN: usize,
963    const S1_PACKED_LEN: usize,
964    const S2_PACKED_LEN: usize,
965    const T1_PACKED_LEN: usize,
966    const POLY_ETA_PACKED_LEN: usize,
967    const LAMBDA_over_4: usize,
968    const GAMMA1_MASK_LEN: usize,
969> PHSignature<PK, SK, PK_LEN, SK_LEN, SIG_LEN, PH_LEN>
970    for HashMLDSA<
971        HASH,
972        PH_LEN,
973        oid,
974        PK_LEN,
975        SK_LEN,
976        FULL_SK_LEN,
977        SIG_LEN,
978        PK,
979        SK,
980        TAU,
981        LAMBDA,
982        GAMMA1,
983        GAMMA2,
984        k,
985        l,
986        ETA,
987        BETA,
988        OMEGA,
989        C_TILDE,
990        POLY_Z_PACKED_LEN,
991        POLY_W1_PACKED_LEN,
992        S1_PACKED_LEN,
993        S2_PACKED_LEN,
994        T1_PACKED_LEN,
995        POLY_ETA_PACKED_LEN,
996        LAMBDA_over_4,
997        GAMMA1_MASK_LEN,
998    >
999{
1000    fn sign_ph(
1001        sk: &SK,
1002        ph: &[u8; PH_LEN],
1003        ctx: Option<&[u8]>,
1004    ) -> Result<[u8; SIG_LEN], SignatureError> {
1005        let mut out = [0u8; SIG_LEN];
1006        Self::sign_out(sk, ph, ctx, &mut out)?;
1007
1008        Ok(out)
1009    }
1010
1011    /// Note that the PH expected here *is not the same* as the `mu` computed by [MuBuilder].
1012    /// To make use of this function, you need to compute a straight hash of the message using
1013    /// the same hash function as the indicated in the HashML-DSA variant; for example SHA256 for
1014    /// HashMDSA44_with_SHA256, SHA512 for HashMLDSA65_with_SHA512, etc.
1015    fn sign_ph_out(
1016        sk: &SK,
1017        ph: &[u8; PH_LEN],
1018        ctx: Option<&[u8]>,
1019        output: &mut [u8; SIG_LEN],
1020    ) -> Result<usize, SignatureError> {
1021        if output.len() < SIG_LEN {
1022            return Err(SignatureError::LengthError(
1023                "Output buffer insufficient size to hold signature",
1024            ));
1025        }
1026
1027        let mut rnd: [u8; MLDSA_RND_LEN] = [0u8; MLDSA_RND_LEN];
1028        HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?;
1029        Self::sign_ph_deterministic_out(sk, ctx, ph, rnd, output)
1030    }
1031
1032    fn verify_ph(
1033        pk: &PK,
1034        ph: &[u8; PH_LEN],
1035        ctx: Option<&[u8]>,
1036        sig: &[u8],
1037    ) -> Result<(), SignatureError> {
1038        if sig.len() != SIG_LEN {
1039            return Err(SignatureError::LengthError("Signature value is not the correct length."));
1040        }
1041        let sig_sized = &sig.try_into().unwrap();
1042
1043        let ctx = if ctx.is_some() { ctx.unwrap() } else { &[] };
1044
1045        // Algorithm 5
1046        // 1: if |𝑐𝑑π‘₯| > 255 then
1047        if ctx.len() > 255 {
1048            return Err(SignatureError::LengthError("ctx value is longer than 255 bytes"));
1049        }
1050
1051        // Algorithm 7
1052        // 6: πœ‡ ← H(BytesToBits(π‘‘π‘Ÿ)||𝑀', 64)
1053        let mut h = H::new();
1054        h.absorb(&pk.compute_tr());
1055
1056        // Algorithm 4
1057        // 23: 𝑀 ← BytesToBits(IntegerToBytes(1, 1) βˆ₯ IntegerToBytes(|𝑐𝑑π‘₯|, 1) βˆ₯ 𝑐𝑑π‘₯ βˆ₯ OID βˆ₯ PH𝑀)
1058        // all done together
1059        h.absorb(&[1u8]);
1060        h.absorb(&[ctx.len() as u8]);
1061        h.absorb(ctx);
1062        h.absorb(oid);
1063        h.absorb(ph);
1064        let mut mu = [0u8; MLDSA_MU_LEN];
1065        _ = h.squeeze_out(&mut mu);
1066
1067        if MLDSA::<
1068            PK_LEN,
1069            SK_LEN,
1070            FULL_SK_LEN,
1071            SIG_LEN,
1072            PK,
1073            SK,
1074            TAU,
1075            LAMBDA,
1076            GAMMA1,
1077            GAMMA2,
1078            k,
1079            l,
1080            ETA,
1081            BETA,
1082            OMEGA,
1083            C_TILDE,
1084            POLY_Z_PACKED_LEN,
1085            POLY_W1_PACKED_LEN,
1086            S1_PACKED_LEN,
1087            S2_PACKED_LEN,
1088            T1_PACKED_LEN,
1089            POLY_ETA_PACKED_LEN,
1090            LAMBDA_over_4,
1091            GAMMA1_MASK_LEN,
1092        >::verify_mu_internal(pk, &mu, sig_sized)
1093        {
1094            Ok(())
1095        } else {
1096            Err(SignatureError::SignatureVerificationFailed)
1097        }
1098    }
1099}