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