Skip to main content

dcrypt_params/pqc/
dilithium.rs

1//! Constants for Module-Lattice-Based Digital Signature Algorithm (ML-DSA)
2//!
3//! This module implements the parameter sets defined in FIPS 204 (August 2024)
4//! "Module-Lattice-Based Digital Signature Standard"
5//! <https://doi.org/10.6028/NIST.FIPS.204>
6//!
7//! All constants in this file are taken directly from the final FIPS 204 standard,
8//! NOT from earlier CRYSTALS-Dilithium submissions or draft specifications.
9
10/// Dilithium polynomial degree (n = 256)
11/// FIPS 204, Table 1: Common to all ML-DSA parameter sets
12pub const DILITHIUM_N: usize = 256;
13
14/// Dilithium modulus (q = 8380417 = 2²³ - 2¹³ + 1)
15/// FIPS 204, Table 1: Common to all ML-DSA parameter sets
16pub const DILITHIUM_Q: u32 = 8380417;
17
18/// FIPS 204 Appendix C, Table 3 minimum allowable loop limit for `ML-DSA.Sign_internal`.
19///
20/// NIST states that implementations which cap the signing rejection loop "shall not use
21/// a limit lower than" this value, and that this limit yields an approximately `2^-256`
22/// or smaller probability of being reached under the standard output-distribution
23/// assumptions for the XOF and hash functions.
24pub const FIPS204_SIGN_INTERNAL_MIN_LOOP_LIMIT: u16 = 814;
25
26/// Common trait for ML-DSA parameter sets as defined in FIPS 204
27pub trait DilithiumSchemeParams: Send + Sync + 'static {
28    /// Algorithm name (ML-DSA-44, ML-DSA-65, ML-DSA-87)
29    const NAME: &'static str;
30
31    // Ring parameters (FIPS 204, Section 3)
32    /// Polynomial degree n = 256 (FIPS 204, Table 1)
33    const N: usize = DILITHIUM_N;
34    /// Prime modulus q = 2²³ - 2¹³ + 1 (FIPS 204, Table 1)
35    const Q: u32 = DILITHIUM_Q;
36    /// Dropped bits parameter d (FIPS 204, Table 1)
37    const D_PARAM: u32;
38
39    // Matrix dimensions (FIPS 204, Table 1)
40    /// Number of polynomials in s₂ and t (rows in matrix A)
41    const K_DIM: usize;
42    /// Number of polynomials in s₁ and y (columns in matrix A)
43    const L_DIM: usize;
44
45    // Security parameters (FIPS 204, Table 1)
46    /// Classical security parameter λ in bits
47    /// ML-DSA-44: λ = 128, ML-DSA-65: λ = 192, ML-DSA-87: λ = 256
48    const LAMBDA: usize;
49
50    /// Challenge hash size in bytes (λ/4)
51    /// ML-DSA-44: 32 bytes, ML-DSA-65: 48 bytes, ML-DSA-87: 64 bytes
52    const CHALLENGE_BYTES: usize;
53
54    // Norm bounds (FIPS 204, Table 1)
55    /// Bound η for secret polynomials s₁, s₂
56    const ETA_S1S2: u32;
57    /// Range parameter γ₁ for masking vector y
58    const GAMMA1_PARAM: u32;
59    /// Number of bits to represent z coefficients
60    /// Computed as ceil(log₂(2·γ₁))
61    /// DEPRECATED: Use Z_BITS for packing z coefficients
62    const GAMMA1_BITS: usize;
63    /// Decomposition parameter γ₂
64    const GAMMA2_PARAM: u32;
65    /// Rejection bound β = τ·η (FIPS 204, Table 1)
66    const BETA_PARAM: u32;
67    /// Maximum number of hint bits ω (FIPS 204, Table 1)
68    const OMEGA_PARAM: u32;
69    /// Number of ±1 coefficients in challenge polynomial c
70    const TAU_PARAM: usize;
71
72    // Byte sizes (FIPS 204, Table 1)
73    /// Public key size in bytes
74    const PUBLIC_KEY_BYTES: usize;
75    /// Secret key size in bytes (includes 32-byte K seed)
76    const SECRET_KEY_BYTES: usize;
77    /// Signature size in bytes
78    const SIGNATURE_SIZE: usize;
79
80    // Seed sizes (FIPS 204, Section 5.1)
81    /// Seed size for matrix A generation (ρ)
82    const SEED_RHO_BYTES: usize = 32;
83    /// Seed size for secret/error sampling (ρ')
84    const SEED_KEY_BYTES: usize = 32;
85    /// Master seed size for key generation (ζ)
86    const SEED_ZETA_BYTES: usize = 32;
87    /// Hash output size for tr = H(pk)
88    const HASH_TR_BYTES: usize = 32;
89
90    // Additional parameters
91    /// Maximum signing attempts before failure
92    const MAX_SIGN_ABORTS: u16 = 1000;
93    /// Fixed public signing window used by the constant-time signer.
94    ///
95    /// For a formally justified rejection cap, implementations must not go below the
96    /// Appendix C, Table 3 bound from FIPS 204.
97    const FIXED_SIGNING_WINDOW: u16 = FIPS204_SIGN_INTERNAL_MIN_LOOP_LIMIT;
98    /// Bits for packing w₁ coefficients
99    /// FIPS 204, Algorithm 28: b = bitlen((q-1)/(2·γ₂) − 1)
100    const W1_BITS: usize;
101
102    /// Number of bits used when packing each z-coefficient in signatures
103    /// This is determined by the range [-γ₁+β, γ₁-β] which requires:
104    /// - ML-DSA-44: 18 bits (since 2·(γ₁-β) < 2¹⁸)
105    /// - ML-DSA-65/87: 20 bits (since 2·(γ₁-β) < 2²⁰)
106    const Z_BITS: usize;
107}
108
109/// Structure containing ML-DSA-44 parameters
110/// FIPS 204, Table 1: ML-DSA-44 (NIST security category 2)
111pub struct Dilithium2Params {
112    /// Polynomial degree n = 256
113    pub n: usize,
114
115    /// Modulus q = 8380417
116    pub q: u32,
117
118    /// Dropped bits d = 13
119    pub d: u32,
120
121    /// Matrix dimension k = 4 (rows)
122    pub k: usize,
123
124    /// Matrix dimension ℓ = 4 (columns)
125    pub l: usize,
126
127    /// Infinity norm bound η = 2
128    pub eta: u32,
129
130    /// Challenge weight τ = 39
131    pub tau: usize,
132
133    /// Public key size = 1312 bytes
134    pub public_key_size: usize,
135
136    /// Secret key size = 2560 bytes (includes 32-byte K seed)
137    pub secret_key_size: usize,
138
139    /// Signature size = 2420 bytes
140    pub signature_size: usize,
141}
142
143/// ML-DSA-44 parameter set (FIPS 204, Table 1)
144/// Targets NIST security category 2 (collision resistance of SHA-256)
145pub const DILITHIUM2: Dilithium2Params = Dilithium2Params {
146    n: DILITHIUM_N,
147    q: DILITHIUM_Q,
148    d: 13,
149    k: 4,
150    l: 4,
151    eta: 2,
152    tau: 39,
153    public_key_size: 1312,
154    secret_key_size: 2560, // FIPS 204 final: includes 32-byte K seed
155    signature_size: 2420,  // Updated: 32 + 2304 + 80 + 4 = 2420 bytes
156};
157
158impl DilithiumSchemeParams for Dilithium2Params {
159    const NAME: &'static str = "Dilithium2";
160    const D_PARAM: u32 = 13;
161    const K_DIM: usize = 4;
162    const L_DIM: usize = 4;
163    const LAMBDA: usize = 128; // Classical security parameter
164    const CHALLENGE_BYTES: usize = 32; // λ/4 = 128/4 = 32
165    const ETA_S1S2: u32 = 2;
166    const GAMMA1_PARAM: u32 = 1 << 17; // 2¹⁷ = 131072
167    const GAMMA1_BITS: usize = 18; // ceil(log₂(2·2¹⁷)) = 18
168                                   // γ₂ = (q − 1)/88 = 95232 (FIPS 204, Table 1, ML-DSA-44)
169    const GAMMA2_PARAM: u32 = (DILITHIUM_Q - 1) / 88; // = 95232
170    const BETA_PARAM: u32 = 78; // β = τ·η = 39·2 = 78
171                                // FIXED: OMEGA must be 80 for ML-DSA-44 per FIPS 204 Table 1
172    const OMEGA_PARAM: u32 = 80;
173    const TAU_PARAM: usize = 39;
174    const PUBLIC_KEY_BYTES: usize = 1312;
175    const SECRET_KEY_BYTES: usize = 2560;
176    // SIGNATURE_SIZE updated: 32 (challenge) + 2304 (z) + 80 (hints) + 4 (counters) = 2420
177    const SIGNATURE_SIZE: usize = 2420;
178    // w₁ encoding: (q-1)/(2·γ₂) = 8380416/(2·95232) = 44
179    // Decompose returns r₁ ∈ [0, 43], which still requires 6 bits.
180    const W1_BITS: usize = 6;
181    // Number of bits for packing z coefficients
182    // Range [-γ₁+β, γ₁-β] = [-131072+78, 131072-78] = [-130994, 130994]
183    // Maximum absolute value: 130994 < 2¹⁷, so 2·130994 < 2¹⁸
184    // Therefore 18 bits are sufficient
185    const Z_BITS: usize = 18;
186}
187
188/// Structure containing ML-DSA-65 parameters
189/// FIPS 204, Table 1: ML-DSA-65 (NIST security category 3)
190pub struct Dilithium3Params {
191    /// Polynomial degree n = 256
192    pub n: usize,
193
194    /// Modulus q = 8380417
195    pub q: u32,
196
197    /// Dropped bits d = 13
198    pub d: u32,
199
200    /// Matrix dimension k = 6 (rows)
201    pub k: usize,
202
203    /// Matrix dimension ℓ = 5 (columns)
204    pub l: usize,
205
206    /// Infinity norm bound η = 4
207    pub eta: u32,
208
209    /// Challenge weight τ = 49
210    pub tau: usize,
211
212    /// Public key size = 1952 bytes
213    pub public_key_size: usize,
214
215    /// Secret key size = 4032 bytes (includes 32-byte K seed)
216    pub secret_key_size: usize,
217
218    /// Signature size = 3309 bytes
219    pub signature_size: usize,
220}
221
222/// ML-DSA-65 parameter set (FIPS 204, Table 1)
223/// Targets NIST security category 3 (collision resistance of SHA-384)
224pub const DILITHIUM3: Dilithium3Params = Dilithium3Params {
225    n: DILITHIUM_N,
226    q: DILITHIUM_Q,
227    d: 13,
228    k: 6,
229    l: 5,
230    eta: 4,
231    tau: 49,
232    public_key_size: 1952,
233    secret_key_size: 4032, // FIPS 204 final: includes 32-byte K seed
234    signature_size: 3309,  // FIPS 204 final value
235};
236
237impl DilithiumSchemeParams for Dilithium3Params {
238    const NAME: &'static str = "Dilithium3";
239    const D_PARAM: u32 = 13;
240    const K_DIM: usize = 6;
241    const L_DIM: usize = 5;
242    const LAMBDA: usize = 192; // Classical security parameter
243    const CHALLENGE_BYTES: usize = 48; // λ/4 = 192/4 = 48
244    const ETA_S1S2: u32 = 4;
245    const GAMMA1_PARAM: u32 = 1 << 19; // 2¹⁹ = 524288
246    const GAMMA1_BITS: usize = 20; // ceil(log₂(2·2¹⁹)) = 20
247                                   // CORRECTED: γ₂ = (q − 1)/32 = 261888 (FIPS 204, Table 1, ML-DSA-65)
248    const GAMMA2_PARAM: u32 = (DILITHIUM_Q - 1) / 32; // = 261888
249    const BETA_PARAM: u32 = 196; // β = τ·η = 49·4 = 196
250                                 // CORRECTED: OMEGA must be 55 for ML-DSA-65 per FIPS 204 Table 1
251    const OMEGA_PARAM: u32 = 55;
252    const TAU_PARAM: usize = 49;
253    const PUBLIC_KEY_BYTES: usize = 1952;
254    const SECRET_KEY_BYTES: usize = 4032;
255    const SIGNATURE_SIZE: usize = 3309;
256    // w₁ encoding for γ₂ = 261888
257    // (q-1)/(2·γ₂) = 8380416/(2·261888) = 16
258    // Decompose returns r₁ ∈ [0, 15], which requires 5 bits.
259    const W1_BITS: usize = 5;
260    // Number of bits for packing z coefficients
261    // Range [-γ₁+β, γ₁-β] = [-524288+196, 524288-196] = [-524092, 524092]
262    // Maximum absolute value: 524092 < 2¹⁹, so 2·524092 < 2²⁰
263    // Therefore 20 bits are sufficient
264    const Z_BITS: usize = 20;
265}
266
267/// Structure containing ML-DSA-87 parameters
268/// FIPS 204, Table 1: ML-DSA-87 (NIST security category 5)
269pub struct Dilithium5Params {
270    /// Polynomial degree n = 256
271    pub n: usize,
272
273    /// Modulus q = 8380417
274    pub q: u32,
275
276    /// Dropped bits d = 13
277    pub d: u32,
278
279    /// Matrix dimension k = 8 (rows)
280    pub k: usize,
281
282    /// Matrix dimension ℓ = 7 (columns)
283    pub l: usize,
284
285    /// Infinity norm bound η = 2
286    pub eta: u32,
287
288    /// Challenge weight τ = 60
289    pub tau: usize,
290
291    /// Public key size = 2592 bytes
292    pub public_key_size: usize,
293
294    /// Secret key size = 4896 bytes (includes 32-byte K seed)
295    pub secret_key_size: usize,
296
297    /// Signature size = 4627 bytes
298    pub signature_size: usize,
299}
300
301/// ML-DSA-87 parameter set (FIPS 204, Table 1)
302/// Targets NIST security category 5 (collision resistance of SHA-512)
303pub const DILITHIUM5: Dilithium5Params = Dilithium5Params {
304    n: DILITHIUM_N,
305    q: DILITHIUM_Q,
306    d: 13,
307    k: 8,
308    l: 7,
309    eta: 2,
310    tau: 60,
311    public_key_size: 2592,
312    secret_key_size: 4896, // FIPS 204 final: includes 32-byte K seed
313    signature_size: 4627,  // FIPS 204 final value
314};
315
316impl DilithiumSchemeParams for Dilithium5Params {
317    const NAME: &'static str = "Dilithium5";
318    const D_PARAM: u32 = 13;
319    const K_DIM: usize = 8;
320    const L_DIM: usize = 7;
321    const LAMBDA: usize = 256; // Classical security parameter
322    const CHALLENGE_BYTES: usize = 64; // λ/4 = 256/4 = 64
323    const ETA_S1S2: u32 = 2;
324    const GAMMA1_PARAM: u32 = 1 << 19; // 2¹⁹ = 524288
325    const GAMMA1_BITS: usize = 20; // ceil(log₂(2·2¹⁹)) = 20
326                                   // γ₂ = (q − 1)/32 = 261888 (FIPS 204, Table 1, ML-DSA-87)
327    const GAMMA2_PARAM: u32 = (DILITHIUM_Q - 1) / 32; // = 261888
328                                                      // β = τ·η = 60·2 = 120 (FIPS 204, Table 1, ML-DSA-87)
329    const BETA_PARAM: u32 = 120; // Corrected from earlier drafts
330                                 // FIPS 204, Table 1 specifies Ω = 75 for ML-DSA-87.
331    const OMEGA_PARAM: u32 = 75;
332    const TAU_PARAM: usize = 60;
333    const PUBLIC_KEY_BYTES: usize = 2592;
334    const SECRET_KEY_BYTES: usize = 4896;
335    const SIGNATURE_SIZE: usize = 4627;
336    // w₁ encoding: (q-1)/(2·γ₂) = 8380416/(2·261888) = 16
337    // Decompose returns r₁ ∈ [0, 15], which requires 5 bits.
338    const W1_BITS: usize = 5;
339    // Number of bits for packing z coefficients
340    // Range [-γ₁+β, γ₁-β] = [-524288+120, 524288-120] = [-524168, 524168]
341    // Maximum absolute value: 524168 < 2¹⁹, so 2·524168 < 2²⁰
342    // Therefore 20 bits are sufficient
343    const Z_BITS: usize = 20;
344}