crystals_rs/poly/
dilithium.rs1use crate::{
2 field::{
3 dilithium::{fqmul, DilithiumFq, DILITHIUM_Q, MONT},
4 Field,
5 },
6 keccak::{fips202::Shake128Params, KeccakParams},
7};
8
9use super::{Poly, Polynomial, SizedPolynomial};
10
11pub(crate) const DILITHIUM_N: usize = 256;
12
13const ROOT_OF_UNITY: i32 = 1753; pub type DilithiumPoly = Poly<DilithiumFq, DILITHIUM_N>;
16
17const ZETAS: [i32; DILITHIUM_N - 1] = {
18 let mut zetas = [0i32; DilithiumPoly::N - 1];
19 let mut i = 1;
20 const ROOT_OF_UNITY_MONT: i32 =
21 ((MONT as i64 * ROOT_OF_UNITY as i64) % DILITHIUM_Q as i64) as i32;
22 let mut omega = MONT;
23 while i < DilithiumPoly::N {
24 let br = (i as u8).reverse_bits() as usize;
25 omega = fqmul(omega % DILITHIUM_Q, ROOT_OF_UNITY_MONT); zetas[br - 1] = omega;
27 i += 1;
28 }
29 zetas
30};
31
32impl Polynomial for DilithiumPoly {
33 type F = DilithiumFq;
34}
35
36impl SizedPolynomial<DILITHIUM_N> for DilithiumPoly {
37 const INV_NTT_SCALE: <Self::F as Field>::E = 41_978;
38
39 #[inline(always)]
40 fn zetas(k: usize) -> <Self::F as Field>::E {
41 ZETAS[k]
42 }
43
44 fn pointwise(&self, other: &Self, result: &mut Self) {
45 for ((a, b), c) in self
46 .as_ref()
47 .iter()
48 .zip(other.as_ref().iter())
49 .zip(result.as_mut().iter_mut())
50 {
51 c.0 = fqmul(a.0, b.0);
52 }
53 }
65
66 fn rej_uniform(&mut self, mut ctr: usize, bytes: &[u8; Shake128Params::RATE_BYTES]) -> usize {
67 debug_assert!(ctr < Self::N);
68
69 let mut piter = self.0[ctr..].as_mut().iter_mut();
70
71 const_assert_eq!(Shake128Params::RATE_BYTES % 3, 0);
72
73 for buf in bytes.chunks_exact(3) {
74 let t = (u32::from_le_bytes([buf[0], buf[1], buf[2], 0]) & 0x7FFFFF) as i32;
77 if t < DILITHIUM_Q {
78 match piter.next() {
79 Some(f) => {
80 f.0 = t;
81 ctr += 1;
82 }
83 None => break,
84 }
85 }
86 }
87 ctr
88 }
89
90 fn pointwise_acc(&self, other: &Self, result: &mut Self) {
91 for ((a, b), r) in self
92 .as_ref()
93 .iter()
94 .zip(other.as_ref().iter())
95 .zip(result.as_mut().iter_mut())
96 {
97 r.0 += fqmul(a.0, b.0);
98 }
99 }
100}
101
102use rand::{CryptoRng, Rng, RngCore};
104
105impl DilithiumPoly {
107 pub fn new_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
108 let mut poly = Self::default();
109 for i in 0..Self::N {
110 poly[i].0 = rng.gen_range(-DILITHIUM_Q / 2..=DILITHIUM_Q / 2);
111 }
112 poly
113 }
114
115 pub fn into_array(self) -> [<<Self as Polynomial>::F as Field>::E; Self::N] {
116 self.0.map(|x| x.0)
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use crystals_cref::dilithium as cref;
124
125 #[test]
126 fn test_ntt_and_then_invtt() {
127 let mut rng = rand::thread_rng();
128 for testcase in 0..3_000 {
129 let mut poly = DilithiumPoly::new_random(&mut rng);
130 let poly_copy = poly.clone();
131 poly.ntt();
132 poly.reduce();
133 poly.inv_ntt();
134
135 for f in poly.as_mut() {
136 *f = *f * 1; }
138
139 assert_eq!(poly, poly_copy, "failed testcase #{}", testcase);
140 }
141 }
142
143 #[test]
155 fn test_ntt_vs_ref() {
156 let mut rng = rand::thread_rng();
157 for _ in 0..3_000 {
158 let mut poly = DilithiumPoly::new_random(&mut rng);
159 let mut p = poly.into_array();
161 poly.ntt();
162 cref::ntt(&mut p);
166
167 for i in 0..DilithiumPoly::N {
168 assert_eq!(p[i], poly[i].0);
169 }
170 }
172 }
173
174 #[test]
175 fn test_invntt_vs_ref() {
176 let mut rng = rand::thread_rng();
177 for _ in 0..3_000 {
178 let mut poly = DilithiumPoly::new_random(&mut rng);
179 let mut p = [0i32; DilithiumPoly::N];
181 for i in 0..DilithiumPoly::N {
182 p[i] = poly[i].0;
183 }
184 poly.inv_ntt();
185 cref::inv_ntt(&mut p);
186
187 for i in 0..DilithiumPoly::N {
188 assert_eq!(p[i], poly[i].0);
189 }
190 }
191 }
192
193 #[test]
194 fn dilithium_poly_uniform() {
195 let mut poly = DilithiumPoly::default();
196 let mut poly_ref = [0i32; DilithiumPoly::N];
197 let mut rng = rand::thread_rng();
198 let mut seed = [0u8; 32];
199
200 const N_TESTS: usize = if cfg!(miri) { 6 } else { 666 };
201
202 for _ in 0..N_TESTS {
203 rng.fill(&mut seed);
204 let i: u8 = rand::random();
205 let j: u8 = rand::random();
206
207 poly.uniform(&seed, i, j);
208
209 let nonce = ((j as u16) << 8) | i as u16;
210 cref::uniform(&mut poly_ref, &seed, nonce);
211
212 assert_eq!(
213 poly_ref,
214 poly.into_array(),
215 "i:{} j:{} seed:{:?}",
216 i,
217 j,
218 seed
219 );
220 }
221 }
222}