Skip to main content

crystals_rs/poly/
mod.rs

1use crate::field::Field;
2use crate::keccak::fips202::{CrystalsXof, Shake128, Shake128Params, SpongeOps};
3use crate::keccak::KeccakParams;
4use crate::lib::fmt::Debug;
5use crate::lib::ops::{AddAssign, Index, IndexMut, SubAssign};
6use crate::lib::slice::{Iter, IterMut};
7use crate::polyvec::PolyVec;
8
9pub mod dilithium;
10pub mod kyber;
11
12// TODO use Parameters
13pub const UNIFORM_SEED_BYTES: usize = 32;
14
15pub trait Polynomial:
16    Index<usize, Output = Self::F>
17    + IndexMut<usize, Output = Self::F>
18    // + IntoIterator
19    + Default
20    + Sized
21    + Clone
22    + Copy // needed for PolyVec::default()
23    + for<'a> AddAssign<&'a Self>
24    + for<'a> SubAssign<&'a Self>
25{
26    type F: Field;
27}
28
29pub trait SizedPolynomial<const N: usize>:
30    Polynomial + AsRef<[Self::F; N]> + AsMut<[Self::F; N]>
31{
32    const N: usize = N;
33
34    const INV_NTT_SCALE: <Self::F as Field>::E;
35
36    const SCALAR_BYTES: usize = core::mem::size_of::<<Self::F as Field>::E>();
37
38    const UNIFORM_SEED_BYTES: usize = UNIFORM_SEED_BYTES; // not usable for array size
39
40    const NUM_SCALARS: usize =
41        Self::N * core::mem::size_of::<Self::F>() / core::mem::size_of::<<Self::F as Field>::E>();
42
43    fn zetas(k: usize) -> <Self::F as Field>::E;
44
45    // Lessons learnt from benchmarking:
46    // - Iterator::step_by is _VERY_ slow!
47    // - Rust (checked) array indexing is slow. Use iterators where possible.
48    //
49
50    // this is the cleaner version, but > %60 slower!
51    // fn ntt(&mut self) {
52    //     let mut k = 0;
53    //     let mut len = Self::N / 2;
54    //     while len > 0 {
55    //         for start in (0..Self::N).step_by(len << 1) {
56    //             let zeta = Self::zetas(k);
57    //             k += 1;
58    //             for j in start..(start + len) {
59    //                 let (u, v) = (self[j], self[j + len]);
60    //                 let t = v * zeta;
61    //                 self[j] = u + t;
62    //                 self[j + len] = u - t;
63    //             }
64    //         }
65    //         len >>= 1;
66    //     }
67    // }
68
69    // this seems to be one less "<< 1" operation but is actually > 3% slower for Kyber:
70    // let mut len = Self::N;
71    // loop {
72    //     let len_times_two = len;
73    //     len >>= 1; // moved from bottom of the loop
74    //     if len == 0 {
75    //         break;
76    //     }
77    //   ...
78    // }
79
80    fn ntt(&mut self) {
81        let mut k = 0;
82        let mut len = Self::N / 2;
83        while len > 0 {
84            let len_times_two = len << 1;
85            let mut start = 0;
86            while start < Self::N {
87                let zeta = Self::zetas(k);
88                k += 1;
89                let end = start + len_times_two;
90                let (top, bottom) = self.as_mut()[start..end].split_at_mut(len);
91
92                // u and v are len apart
93                for (u, v) in top.iter_mut().zip(bottom) {
94                    let t = *v * zeta;
95                    *v = *u - t;
96                    *u += t;
97                }
98                start = end;
99            }
100            len >>= 1;
101        }
102    }
103
104    // fn inv_ntt(&mut self) {
105    //     let mut k = Self::N - 1;
106    //     let mut len = 1; // 2
107    //     while len < Self::N {
108    //         for start in (0..Self::N).step_by(len << 1) {
109    //             k -= 1;
110    //             let zeta = Self::zetas(k);
111    //             for j in start..(start + len) {
112    //                 let (u, v) = (self[j], self[j + len]);
113    //                 self[j] = (u + v).maybe_reduce();
114    //                 self[j + len] = (v - u) * zeta;
115    //             }
116    //         }
117    //         len <<= 1;
118    //     }
119
120    //     for f in self.as_mut() {
121    //         *f *= Self::INV_NTT_SCALE;
122    //     }
123    // }
124
125    fn inv_ntt(&mut self) {
126        let mut k = Self::N - 1;
127        let mut len = 1;
128        while len < Self::N {
129            let mut start = 0;
130            let len_times_two = len << 1;
131            while start < Self::N {
132                k -= 1;
133                let zeta = Self::zetas(k);
134                let end = start + len_times_two;
135                let (left, right) = self.as_mut()[start..end].split_at_mut(len);
136
137                for (u, v) in left.iter_mut().zip(right) {
138                    let t = *u;
139                    *u = (t + *v).maybe_reduce();
140                    *v = (*v - t) * zeta;
141                }
142                start = end;
143            }
144            len = len_times_two;
145        }
146
147        for f in self.as_mut() {
148            *f *= Self::INV_NTT_SCALE;
149        }
150    }
151
152    /// Applies Barrett reduction to all coefficients of a polynomial
153    /// # Arguments
154    /// * `r` - Input/output polynomial
155    #[inline(always)]
156    fn reduce(&mut self) {
157        for f in self.as_mut() {
158            *f = f.reduce();
159        }
160    }
161
162    fn pointwise(&self, other: &Self, result: &mut Self);
163
164    fn pointwise_acc(&self, other: &Self, result: &mut Self);
165
166    fn vector_mul_acc<const K: usize>(
167        &mut self,
168        lhs: &PolyVec<Self, N, K>,
169        rhs: &PolyVec<Self, N, K>,
170    ) {
171        let mut t = Self::default();
172
173        for (l, r) in lhs.as_ref().iter().zip(rhs.as_ref().iter()) {
174            l.pointwise(r, &mut t);
175            self.add_assign(&t);
176        }
177    }
178
179    fn rej_uniform(&mut self, start: usize, bytes: &[u8; Shake128Params::RATE_BYTES]) -> usize;
180
181    #[inline]
182    fn uniform(&mut self, seed: &[u8; UNIFORM_SEED_BYTES], i: u8, j: u8) {
183        let mut shake128 = Shake128::default();
184        shake128.absorb_xof_with_nonces(seed, i, j);
185        let mut xof_out = [0u8; Shake128Params::RATE_BYTES];
186
187        let mut ctr = 0;
188        while ctr < Self::NUM_SCALARS {
189            shake128.squeeze(&mut xof_out);
190            ctr = self.rej_uniform(ctr, &xof_out);
191        }
192        debug_assert_eq!(ctr, Self::NUM_SCALARS);
193    }
194}
195
196#[derive(Debug, Clone, Copy, PartialEq)]
197pub struct Poly<T: Field, const N: usize>([T; N]);
198
199impl<T: Field, const N: usize> Default for Poly<T, N> {
200    #[inline(always)]
201    fn default() -> Self {
202        Poly([T::default(); N])
203    }
204}
205
206// impl<T: Field, const N: usize> AsRef<[T]> for Poly<T, N> {
207//     #[inline(always)]
208//     fn as_ref(&self) -> &[T] {
209//         &self.0
210//     }
211// }
212// impl<T: Field, const N: usize> AsMut<[T]> for Poly<T, N> {
213//     #[inline(always)]
214//     fn as_mut(&mut self) -> &mut [T] {
215//         &mut self.0
216//     }
217// }
218
219// impl<F: Field, const N: usize> IntoIterator for Poly<F, N> {
220//     type Item = F;
221//     type IntoIter = core::array::IntoIter<F, N>;
222
223//     #[inline(always)]
224//     fn into_iter(self) -> Self::IntoIter {
225//         self.0.into_iter()
226//     }
227// }
228
229impl<'a, F: Field, const N: usize> IntoIterator for &'a Poly<F, N> {
230    type Item = &'a F;
231    type IntoIter = Iter<'a, F>;
232
233    #[inline(always)]
234    fn into_iter(self) -> Iter<'a, F> {
235        self.0.iter()
236    }
237}
238
239impl<'a, F: Field, const N: usize> IntoIterator for &'a mut Poly<F, N> {
240    type Item = &'a mut F;
241    type IntoIter = IterMut<'a, F>;
242
243    #[inline(always)]
244    fn into_iter(self) -> IterMut<'a, F> {
245        self.0.iter_mut()
246    }
247}
248
249/// Adds `rhs` polynomial to self; no modular reduction is performed.
250/// # Arguments
251/// * `rhs` - Righthand-side input polynomial
252impl<T: Field, const N: usize> AddAssign<&Self> for Poly<T, N> {
253    #[inline]
254    fn add_assign(&mut self, rhs: &Self) {
255        for (l, r) in self.into_iter().zip(rhs) {
256            *l += *r;
257        }
258    }
259}
260
261/// Subtracts `rhs` polynomial from self, i.e. `self` <- `self` - `rhs` ; no modular reduction is performed.
262/// # Arguments
263/// * `rhs` - Righthand-side input polynomial
264impl<T: Field, const N: usize> SubAssign<&Self> for Poly<T, N> {
265    #[inline(always)]
266    fn sub_assign(&mut self, rhs: &Self) {
267        for i in 0..self.0.len() {
268            self[i] -= rhs[i];
269        }
270    }
271}
272
273impl<T: Field, const N: usize> Index<usize> for Poly<T, N> {
274    type Output = T;
275    #[inline(always)]
276    fn index(&self, i: usize) -> &Self::Output {
277        &self.0[i]
278    }
279}
280
281impl<T: Field, const N: usize> IndexMut<usize> for Poly<T, N> {
282    #[inline(always)]
283    fn index_mut(&mut self, i: usize) -> &mut Self::Output {
284        &mut self.0[i]
285    }
286}
287
288impl<F: Field, const N: usize> AsRef<[F; N]> for Poly<F, N> {
289    #[inline(always)]
290    fn as_ref(&self) -> &[F; N] {
291        &self.0
292    }
293}
294
295impl<F: Field, const N: usize> AsMut<[F; N]> for Poly<F, N> {
296    #[inline(always)]
297    fn as_mut(&mut self) -> &mut [F; N] {
298        &mut self.0
299    }
300}
301
302#[cfg(test)]
303mod tests {}