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