Skip to main content

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