Skip to main content

crystals_rs/
polyvec.rs

1use core::ops::{AddAssign, Index, IndexMut};
2
3use rand::{CryptoRng, RngCore};
4
5use crate::{
6    keccak::fips202::{CrystalsPrf, CrystalsXof, SpongeOps},
7    poly::{
8        dilithium::DilithiumPoly,
9        kyber::{KyberPoly, Prf, Xof, KYBER_N, NOISE_SEED_BYTES, POLYBYTES, XOF_BLOCK_BYTES},
10        Polynomial, UNIFORM_SEED_BYTES,
11    },
12};
13
14use super::poly::SizedPolynomial;
15
16pub trait PolynomialVector: Default + Sized + Index<usize> + IndexMut<usize> {
17    type Poly: Polynomial;
18    const K: usize;
19
20    fn ntt(&mut self);
21    fn ntt_and_reduce(&mut self);
22    fn inv_ntt_tomont(&mut self);
23
24    fn reduce(&mut self);
25    fn uniform_xof<const TRANSPOSED: bool>(&mut self, seed: &[u8; UNIFORM_SEED_BYTES], i: u8);
26
27    fn basemul_acc(&self, other: &Self, result: &mut <Self as PolynomialVector>::Poly);
28}
29
30#[derive(Debug, Clone, Copy)]
31pub struct PolyVec<P, const N: usize, const K: usize>([P; K])
32where
33    P: Polynomial;
34
35impl<P, const N: usize, const K: usize> Default for PolyVec<P, N, K>
36where
37    P: Polynomial,
38{
39    fn default() -> Self {
40        PolyVec([P::default(); K])
41    }
42}
43
44impl<P, const N: usize, const K: usize> Index<usize> for PolyVec<P, N, K>
45where
46    P: Polynomial,
47{
48    type Output = P;
49    fn index(&self, i: usize) -> &Self::Output {
50        &self.0[i]
51    }
52}
53
54impl<P, const N: usize, const K: usize> IndexMut<usize> for PolyVec<P, N, K>
55where
56    P: Polynomial,
57{
58    fn index_mut(&mut self, i: usize) -> &mut Self::Output {
59        &mut self.0[i]
60    }
61}
62
63impl<P, const N: usize, const K: usize> AsRef<[P; K]> for PolyVec<P, N, K>
64where
65    P: Polynomial,
66{
67    #[inline(always)]
68    fn as_ref(&self) -> &[P; K] {
69        &self.0
70    }
71}
72
73impl<P, const N: usize, const K: usize> AsMut<[P; K]> for PolyVec<P, N, K>
74where
75    P: Polynomial,
76{
77    #[inline(always)]
78    fn as_mut(&mut self) -> &mut [P; K] {
79        &mut self.0
80    }
81}
82
83impl<P, const N: usize, const K: usize> PolynomialVector for PolyVec<P, N, K>
84where
85    P: SizedPolynomial<N>,
86{
87    const K: usize = K;
88    type Poly = P;
89
90    #[inline]
91    fn ntt(&mut self) {
92        for poly in self {
93            poly.ntt();
94        }
95    }
96
97    #[inline]
98    fn ntt_and_reduce(&mut self) {
99        for poly in self {
100            poly.ntt();
101            poly.reduce();
102        }
103    }
104
105    #[inline]
106    fn inv_ntt_tomont(&mut self) {
107        for poly in self {
108            poly.inv_ntt();
109        }
110    }
111
112    #[inline]
113    fn reduce(&mut self) {
114        for poly in self {
115            poly.reduce();
116        }
117    }
118
119    #[inline]
120    fn uniform_xof<const TRANSPOSED: bool>(&mut self, seed: &[u8; UNIFORM_SEED_BYTES], i: u8) {
121        let mut shake128 = Xof::default();
122        let mut xof_out = [0u8; XOF_BLOCK_BYTES];
123
124        for (j, poly) in self.as_mut().iter_mut().enumerate() {
125            let (i, j) = if TRANSPOSED {
126                (i, j as u8)
127            } else {
128                (j as u8, i)
129            };
130
131            shake128.absorb_xof_with_nonces(seed, i, j);
132
133            let mut ctr = 0;
134            while ctr < Self::Poly::NUM_SCALARS {
135                shake128.squeeze(&mut xof_out);
136                ctr = poly.rej_uniform(ctr, &xof_out);
137            }
138
139            debug_assert_eq!(ctr, Self::Poly::NUM_SCALARS);
140        }
141    }
142
143    fn basemul_acc(&self, other: &Self, result: &mut <Self as PolynomialVector>::Poly) {
144        // TODO: optimize
145        for (left, right) in self.into_iter().zip(other) {
146            left.pointwise_acc(right, result);
147        }
148        result.reduce();
149    }
150}
151
152impl<'a, const N: usize, P, const K: usize> IntoIterator for &'a mut PolyVec<P, N, K>
153where
154    P: Polynomial,
155{
156    type Item = &'a mut P;
157
158    type IntoIter = core::slice::IterMut<'a, P>;
159
160    fn into_iter(self) -> Self::IntoIter {
161        (*self).as_mut().iter_mut()
162    }
163}
164
165impl<'a, P, const N: usize, const K: usize> IntoIterator for &'a PolyVec<P, N, K>
166where
167    P: Polynomial,
168{
169    type Item = &'a P;
170
171    type IntoIter = core::slice::Iter<'a, P>;
172
173    fn into_iter(self) -> Self::IntoIter {
174        (*self).as_ref().iter()
175    }
176}
177
178impl<P, const N: usize, const K: usize> AddAssign<&Self> for PolyVec<P, N, K>
179where
180    P: Polynomial,
181{
182    fn add_assign(&mut self, rhs: &Self) {
183        for i in 0..K {
184            self[i] += &rhs[i];
185        }
186    }
187}
188
189impl<const N: usize, const K: usize> PolyVec<KyberPoly, N, K> {
190    #[inline(always)]
191    pub fn new_deserialize(bytes: &[[u8; POLYBYTES]; K]) -> Self {
192        let mut pv = Self::default();
193        pv.deserialize(bytes);
194        pv
195    }
196
197    #[inline(always)]
198    pub fn deserialize(&mut self, bytes: &[[u8; POLYBYTES]; K]) {
199        for (poly, b) in self.into_iter().zip(bytes) {
200            poly.deserialize(b);
201        }
202    }
203
204    #[inline(always)]
205    pub fn serialize(&self, r: &mut [[u8; POLYBYTES]; K]) {
206        for (poly, bytes) in self.into_iter().zip(r) {
207            poly.serialize(bytes);
208        }
209    }
210}
211pub type KyberPolyVec<const K: usize> = PolyVec<KyberPoly, { KyberPoly::N }, K>;
212
213impl<const K: usize> KyberPolyVec<K> {
214    #[doc(hidden)]
215    pub fn new_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
216        let mut pv = Self::default();
217        for i in 0..K {
218            pv[i] = KyberPoly::new_random(rng);
219        }
220        pv
221    }
222
223    #[inline]
224    pub fn getnoise_eta1(&mut self, prf: &mut Prf, seed: &[u8; NOISE_SEED_BYTES], nonce: u8) {
225        if K == 2 {
226            const ETA1: usize = 3;
227            let mut buf = [0u8; ETA1 * KYBER_N / 4];
228            for (i, poly) in self.as_mut().iter_mut().enumerate() {
229                prf.absorb_prf(seed, i as u8 + nonce);
230                prf.squeeze(&mut buf);
231                poly.cbd3(&buf);
232            }
233        } else {
234            self.getnoise_eta2(prf, seed, nonce);
235        }
236    }
237
238    #[inline]
239    pub fn getnoise_eta2(&mut self, prf: &mut Prf, seed: &[u8; NOISE_SEED_BYTES], nonce: u8) {
240        const ETA2: usize = 2;
241        prf.absorb_prf(seed, nonce);
242        let mut buf = [0u8; ETA2 * KYBER_N / 4];
243        for (i, poly) in self.as_mut().iter_mut().enumerate() {
244            prf.absorb_prf(seed, i as u8 + nonce);
245            prf.squeeze(&mut buf);
246            poly.cbd2(&buf);
247        }
248    }
249
250    #[inline] // more possibilities for code with constant d to be optimized?
251    pub fn compress<const D: usize>(&self, ct: &mut [[[u8; D]; 32]; K]) {
252        for (poly, a) in self.into_iter().zip(ct.iter_mut()) {
253            poly.compress(a);
254        }
255    }
256
257    #[inline]
258    pub fn decompress<const D: usize>(&mut self, ct: &[[[u8; D]; 32]; K]) {
259        for (poly, a) in self.into_iter().zip(ct.iter()) {
260            poly.decompress(a);
261        }
262    }
263}
264
265pub type DilithiumPolyVec<const K: usize> = PolyVec<DilithiumPoly, { DilithiumPoly::N }, K>;
266
267impl<const K: usize> DilithiumPolyVec<K> {
268    pub fn new_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
269        let mut pv = DilithiumPolyVec::<K>::default();
270        for i in 0..K {
271            pv[i] = DilithiumPoly::new_random(rng);
272        }
273        pv
274    }
275}