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