1pub const N: usize = 256;
10
11pub const Q: i32 = 8380417;
13
14pub const QINV: i64 = 58728449; pub const D: u32 = 13;
19
20pub const ROOT_OF_UNITY: i32 = 1753;
22
23pub const SEEDBYTES: usize = 32;
25
26pub const CRHBYTES: usize = 64;
28
29pub const TRBYTES: usize = 64;
31
32pub const RNDBYTES: usize = 32;
34
35pub const K_MAX: usize = 8;
37
38pub const L_MAX: usize = 7;
40
41pub const POLYT1_PACKEDBYTES: usize = 320;
43
44pub const POLYT0_PACKEDBYTES: usize = 416;
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54pub enum DilithiumMode {
55 Dilithium2,
57 Dilithium3,
59 Dilithium5,
61}
62
63pub const ML_DSA_44: DilithiumMode = DilithiumMode::Dilithium2;
65pub const ML_DSA_65: DilithiumMode = DilithiumMode::Dilithium3;
67pub const ML_DSA_87: DilithiumMode = DilithiumMode::Dilithium5;
69
70pub const HASH_ML_DSA_44_OID: &[u8] = &[
72 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x11,
73];
74pub const HASH_ML_DSA_65_OID: &[u8] = &[
76 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x12,
77];
78pub const HASH_ML_DSA_87_OID: &[u8] = &[
80 0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x13,
81];
82
83impl DilithiumMode {
84 #[inline]
86 #[must_use]
87 pub const fn mode_tag(self) -> u8 {
88 match self {
89 Self::Dilithium2 => 0x02,
90 Self::Dilithium3 => 0x03,
91 Self::Dilithium5 => 0x05,
92 }
93 }
94
95 #[must_use]
97 pub const fn from_tag(tag: u8) -> Option<Self> {
98 match tag {
99 0x02 => Some(Self::Dilithium2),
100 0x03 => Some(Self::Dilithium3),
101 0x05 => Some(Self::Dilithium5),
102 _ => None,
103 }
104 }
105
106 #[inline]
108 #[must_use]
109 pub const fn k(self) -> usize {
110 match self {
111 Self::Dilithium2 => 4,
112 Self::Dilithium3 => 6,
113 Self::Dilithium5 => 8,
114 }
115 }
116
117 #[inline]
119 #[must_use]
120 pub const fn l(self) -> usize {
121 match self {
122 Self::Dilithium2 => 4,
123 Self::Dilithium3 => 5,
124 Self::Dilithium5 => 7,
125 }
126 }
127
128 #[inline]
130 #[must_use]
131 pub const fn eta(self) -> i32 {
132 match self {
133 Self::Dilithium2 => 2,
134 Self::Dilithium3 => 4,
135 Self::Dilithium5 => 2,
136 }
137 }
138
139 #[inline]
141 #[must_use]
142 pub const fn tau(self) -> usize {
143 match self {
144 Self::Dilithium2 => 39,
145 Self::Dilithium3 => 49,
146 Self::Dilithium5 => 60,
147 }
148 }
149
150 #[inline]
152 #[must_use]
153 pub const fn beta(self) -> i32 {
154 match self {
155 Self::Dilithium2 => 78,
156 Self::Dilithium3 => 196,
157 Self::Dilithium5 => 120,
158 }
159 }
160
161 #[inline]
163 #[must_use]
164 pub const fn gamma1(self) -> i32 {
165 match self {
166 Self::Dilithium2 => 1 << 17, Self::Dilithium3 => 1 << 19, Self::Dilithium5 => 1 << 19,
169 }
170 }
171
172 #[inline]
174 #[must_use]
175 pub const fn gamma2(self) -> i32 {
176 match self {
177 Self::Dilithium2 => (Q - 1) / 88, Self::Dilithium3 => (Q - 1) / 32, Self::Dilithium5 => (Q - 1) / 32,
180 }
181 }
182
183 #[inline]
185 #[must_use]
186 pub const fn omega(self) -> usize {
187 match self {
188 Self::Dilithium2 => 80,
189 Self::Dilithium3 => 55,
190 Self::Dilithium5 => 75,
191 }
192 }
193
194 #[inline]
196 #[must_use]
197 pub const fn ctildebytes(self) -> usize {
198 match self {
199 Self::Dilithium2 => 32,
200 Self::Dilithium3 => 48,
201 Self::Dilithium5 => 64,
202 }
203 }
204
205 #[inline]
207 #[must_use]
208 pub const fn polyz_packedbytes(self) -> usize {
209 match self {
210 Self::Dilithium2 => 576, Self::Dilithium3 => 640, Self::Dilithium5 => 640,
213 }
214 }
215
216 #[inline]
218 #[must_use]
219 pub const fn polyw1_packedbytes(self) -> usize {
220 match self {
221 Self::Dilithium2 => 192, Self::Dilithium3 => 128, Self::Dilithium5 => 128,
224 }
225 }
226
227 #[inline]
229 #[must_use]
230 pub const fn polyeta_packedbytes(self) -> usize {
231 match self {
232 Self::Dilithium2 => 96, Self::Dilithium3 => 128, Self::Dilithium5 => 96,
235 }
236 }
237
238 #[inline]
240 #[must_use]
241 pub const fn public_key_bytes(self) -> usize {
242 SEEDBYTES + self.k() * POLYT1_PACKEDBYTES
243 }
244
245 #[inline]
247 #[must_use]
248 pub const fn secret_key_bytes(self) -> usize {
249 2 * SEEDBYTES
250 + TRBYTES
251 + self.l() * self.polyeta_packedbytes()
252 + self.k() * self.polyeta_packedbytes()
253 + self.k() * POLYT0_PACKEDBYTES
254 }
255
256 #[inline]
258 #[must_use]
259 pub const fn signature_bytes(self) -> usize {
260 self.ctildebytes() + self.l() * self.polyz_packedbytes() + self.omega() + self.k()
261 }
262
263 #[inline]
265 #[must_use]
266 pub fn hash_oid(self) -> &'static [u8] {
267 match self {
268 Self::Dilithium2 => HASH_ML_DSA_44_OID,
269 Self::Dilithium3 => HASH_ML_DSA_65_OID,
270 Self::Dilithium5 => HASH_ML_DSA_87_OID,
271 }
272 }
273
274 #[must_use]
276 pub fn fips_name(self) -> &'static str {
277 match self {
278 Self::Dilithium2 => "ML-DSA-44",
279 Self::Dilithium3 => "ML-DSA-65",
280 Self::Dilithium5 => "ML-DSA-87",
281 }
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::*;
288
289 #[test]
290 fn test_dilithium2_sizes() {
291 let m = DilithiumMode::Dilithium2;
292 assert_eq!(m.public_key_bytes(), 1312);
293 assert_eq!(m.secret_key_bytes(), 2560);
294 assert_eq!(m.signature_bytes(), 2420);
295 }
296
297 #[test]
298 fn test_dilithium3_sizes() {
299 let m = DilithiumMode::Dilithium3;
300 assert_eq!(m.public_key_bytes(), 1952);
301 assert_eq!(m.secret_key_bytes(), 4032);
302 assert_eq!(m.signature_bytes(), 3309);
303 }
304
305 #[test]
306 fn test_dilithium5_sizes() {
307 let m = DilithiumMode::Dilithium5;
308 assert_eq!(m.public_key_bytes(), 2592);
309 assert_eq!(m.secret_key_bytes(), 4896);
310 assert_eq!(m.signature_bytes(), 4627);
311 }
312}