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