1use crate::field::kyber::{caddq, fqmul, KyberFq, KYBER_Q, MONT};
2use crate::field::Field;
3use crate::keccak::fips202::{CrystalsPrf, HasParams, Shake128, Shake256, SpongeOps};
4use crate::keccak::KeccakParams;
5use crate::lib::mem::{size_of, transmute};
6use crate::utils::flatten::FlattenArray;
7use crate::utils::split::*;
8
9use super::{Poly, Polynomial, SizedPolynomial};
10
11pub const KYBER_N: usize = 256;
12
13const ROOT_OF_UNITY: i16 = 17; pub type KyberPoly = Poly<KyberFq, { KYBER_N / 2 }>;
16
17pub const POLYBYTES: usize = { KyberPoly::N } * 3;
18
19pub const MSG_BYTES: usize = 32;
20
21pub const NOISE_SEED_BYTES: usize = 32;
22
23pub type Xof = Shake128;
24pub const XOF_BLOCK_BYTES: usize = <Xof as HasParams<_>>::Params::RATE_BYTES;
25
26pub type Prf = Shake256;
27pub const PRF_BLOCK_BYTES: usize = <Prf as HasParams<_>>::Params::RATE_BYTES;
28
29pub(crate) const fn poly_compressed_bytes(d: u8) -> usize {
30 KYBER_N * d as usize / 8 }
32
33pub(crate) const fn poly_compressed_bytes_for_k<const K: usize>() -> usize {
34 match K {
35 2 | 3 => poly_compressed_bytes(4),
36 4 => poly_compressed_bytes(5),
37 _ => unreachable!(),
38 }
39}
40
41pub(crate) const fn polyvec_compressed_bytes_for_k<const K: usize>() -> usize {
42 (match K {
43 2 | 3 => poly_compressed_bytes(10),
44 4 => poly_compressed_bytes(11),
45 _ => unreachable!(),
46 }) * K
47}
48
49pub const fn kyber_ciphertext_bytes<const K: usize>() -> usize {
50 polyvec_compressed_bytes_for_k::<K>() + poly_compressed_bytes_for_k::<K>()
51}
52
53#[inline(always)]
54pub(crate) fn compress_d<const D: usize>(u: i16) -> u16 {
55 const Q: u32 = KYBER_Q as u32;
56 const HALF_Q: u32 = KYBER_Q as u32 / 2;
57
58 let u = caddq(u) as u32;
59
60 ((((u << D as u8) + HALF_Q) / Q) & ((1 << D as u8) - 1)) as u16
61}
62
63#[inline(always)]
64pub(crate) fn decompress_d<const D: usize>(u: u16) -> i16 {
65 debug_assert!(D <= 16);
66
67 const Q: u32 = KYBER_Q as u32;
68
69 let u = u & ((1 << D) - 1);
70
71 ((u as u32 * Q + (1 << (D - 1))) >> D) as i16
72}
73
74const ZETAS: [i16; KyberPoly::N - 1] = {
75 let mut zetas = [0i16; KyberPoly::N - 1];
76 let mut i = 1;
77 let mut omega = MONT;
78 const ROOT_OF_UNITY_MONT: i16 = (MONT * ROOT_OF_UNITY) % KYBER_Q;
79 while i < KyberPoly::N {
80 let br = (i as u8).reverse_bits() as usize >> 1;
81 omega = fqmul(omega, ROOT_OF_UNITY_MONT); zetas[br - 1] = omega;
83 i += 1;
84 }
85 zetas
86};
87
88impl Polynomial for KyberPoly {
89 type F = KyberFq;
90}
91
92impl SizedPolynomial<{ KYBER_N / 2 }> for KyberPoly {
93 const INV_NTT_SCALE: <Self::F as Field>::E = 1441; #[inline(always)]
96 fn zetas(k: usize) -> <Self::F as Field>::E {
97 ZETAS[k]
98 }
99
100 fn pointwise(&self, other: &Self, result: &mut Self) {
101 for (((tr, ta), tb), zeta) in result
102 .as_mut()
103 .as_array_chunks_mut::<2>()
104 .zip(self.as_ref().as_array_chunks::<2>())
105 .zip(other.as_ref().as_array_chunks::<2>())
106 .zip(ZETAS[63..].iter())
107 {
108 tr[0] = ta[0].basemul(tb[0], *zeta);
109 tr[1] = ta[1].basemul(tb[1], -*zeta);
110 }
111 }
112
113 fn rej_uniform(&mut self, mut ctr: usize, bytes: &[u8; XOF_BLOCK_BYTES]) -> usize {
114 debug_assert!(ctr < Self::NUM_SCALARS);
115 debug_assert!(bytes.len() % 3 == 0);
116
117 let p: &mut [i16; Self::NUM_SCALARS] = self.as_scalar_array_mut();
119
120 for buf in bytes.chunks_exact(3) {
121 if ctr >= Self::NUM_SCALARS {
123 break;
124 }
125 let val0 = (buf[0] as u16 | (buf[1] as u16) << 8) & 0xFFF;
126 if val0 < KYBER_Q as u16 {
127 p[ctr] = val0 as i16;
128 ctr += 1;
129 }
130
131 if ctr >= Self::NUM_SCALARS {
132 break;
133 }
134 let val1 = ((buf[1] >> 4) as u16 | (buf[2] as u16) << 4) & 0xFFF;
135 if val1 < KYBER_Q as u16 {
136 p[ctr] = val1 as i16;
137 ctr += 1;
138 }
139 }
140 ctr
141 }
142
143 fn pointwise_acc(&self, other: &Self, result: &mut Self) {
144 for (((r, a), b), zeta) in result
145 .as_mut()
146 .as_array_chunks_mut::<2>()
147 .zip(self.as_ref().as_array_chunks::<2>())
148 .zip(other.as_ref().as_array_chunks::<2>())
149 .zip(ZETAS[63..].iter())
150 {
151 r[0] += a[0].basemul(b[0], *zeta);
152 r[1] += a[1].basemul(b[1], -*zeta);
153 }
154 }
155}
156
157impl KyberPoly {
158 #[inline]
159 pub fn serialize(&self, bytes: &mut [u8; POLYBYTES]) {
160 for (f, r) in self.as_ref().iter().zip(bytes.as_array_chunks_mut::<3>()) {
161 let f = f.freeze();
163 let [t0, t1] = f.0;
164 r[0] = t0 as u8;
165 r[1] = ((t0 >> 8) | (t1 << 4)) as u8;
166 r[2] = (t1 >> 4) as u8;
167 }
168 }
169
170 #[inline]
171 pub fn deserialize(&mut self, bytes: &[u8]) {
172 for (f, a) in self.as_mut().iter_mut().zip(bytes.as_array_chunks::<3>()) {
173 f.0 = [
174 (a[0] as u16 | ((a[1] as u16) << 8) & 0xFFF) as i16,
175 ((a[1] >> 4) as u16 | ((a[2] as u16) << 4) & 0xFFF) as i16,
176 ];
177 }
178 }
179
180 #[inline]
181 pub fn cbd2(&mut self, buf: &[u8; KYBER_N / 2]) {
182 const MASK55: u32 = 0x55_55_55_55;
183
184 for (r, bytes) in self
185 .as_mut()
186 .as_array_chunks_mut::<4>()
187 .zip(buf.as_array_chunks::<4>())
188 {
189 let t = u32::from_le_bytes(*bytes);
190
191 let f0 = t & MASK55;
192 let f1 = (t >> 1) & MASK55;
193 let d = f0.wrapping_add(f1);
194
195 for (j, rj) in r.iter_mut().enumerate() {
196 let a = ((d >> (8 * j)) & 0x3) as i8;
197 let b = ((d >> (8 * j + 2)) & 0x3) as i8;
198 rj.0[0] = (a - b) as i16;
199
200 let a = ((d >> (8 * j + 4)) & 0x3) as i8;
201 let b = ((d >> (8 * j + 6)) & 0x3) as i8;
202 rj.0[1] = (a - b) as i16;
203 }
204 }
205 }
206
207 #[inline]
208 pub fn cbd3(&mut self, buf: &[u8; 3 * KYBER_N / 4]) {
209 const MASK249: u32 = 0x00249249; #[inline]
212 fn load24_littleendian(x: [u8; 3]) -> u32 {
213 let mut r = x[0] as u32;
214 r |= (x[1] as u32) << 8;
215 r |= (x[2] as u32) << 16;
216 r
217 }
218
219 for (r, bytes) in self
220 .as_mut()
221 .as_array_chunks_mut::<2>()
222 .zip(buf.as_array_chunks::<3>())
223 {
224 let t = load24_littleendian(*bytes);
225 let mut d = t & MASK249;
226 d += (t >> 1) & MASK249;
227 d += (t >> 2) & MASK249;
228
229 for c in r {
230 let a = d as i16 & 0x7;
231 d >>= 3;
232 let b = d as i16 & 0x7;
233 d >>= 3;
234 c.0[0] = a - b;
235
236 let a = d as i16 & 0x7;
237 d >>= 3;
238 let b = d as i16 & 0x7;
239 d >>= 3;
240 c.0[1] = a - b;
241 }
242 }
243 }
244
245 pub fn getnoise_eta1<const K: usize>(
246 &mut self,
247 prf: &mut Prf,
248 seed: &[u8; NOISE_SEED_BYTES],
249 nonce: u8,
250 ) {
251 if K == 2 {
252 const ETA1: usize = 3;
253 let mut buf = [0u8; ETA1 * KYBER_N / 4];
254 prf.absorb_prf(seed, nonce);
255 prf.squeeze(&mut buf);
256 self.cbd3(&buf);
257 } else {
258 self.getnoise_eta2(prf, seed, nonce);
259 }
260 }
261
262 pub fn getnoise_eta2(&mut self, prf: &mut Prf, seed: &[u8; NOISE_SEED_BYTES], nonce: u8) {
263 const ETA2: usize = 2;
264 prf.absorb_prf(seed, nonce);
265 let mut buf = [0u8; ETA2 * KYBER_N / 4];
266 prf.absorb_prf(seed, nonce);
267 prf.squeeze(&mut buf);
268 self.cbd2(&buf);
269 }
270
271 #[inline(always)]
272 pub fn ntt_and_reduce(&mut self) {
273 self.ntt();
274 self.reduce();
275 }
276
277 pub fn into_array(self) -> [<KyberFq as Field>::E; Self::NUM_SCALARS] {
278 *self.0.map(|f| f.0).flatten_array()
279 }
280
281 fn as_scalar_array_mut(
282 &mut self,
283 ) -> &mut [<<Self as Polynomial>::F as Field>::E; Self::NUM_SCALARS] {
284 debug_assert_eq!(
286 size_of::<[<<Self as Polynomial>::F as Field>::E; Self::NUM_SCALARS]>(),
287 size_of::<Self>()
288 );
289
290 #[allow(unsafe_code)]
291 unsafe {
292 transmute(self.as_mut())
293 }
294 }
295
296 fn as_scalar_array(&self) -> &[<<Self as Polynomial>::F as Field>::E; Self::NUM_SCALARS] {
297 debug_assert_eq!(
299 size_of::<[<<Self as Polynomial>::F as Field>::E; Self::NUM_SCALARS]>(),
300 size_of::<Self>()
301 );
302 #[allow(unsafe_code)]
303 unsafe {
304 transmute(self.as_ref())
305 }
306 }
307
308 #[inline]
309 pub fn compress_slice<const D: usize>(&self, ct: &mut [u8]) {
310 assert_eq!(ct.len(), poly_compressed_bytes(D as u8));
311
312 #[inline(always)]
313 fn shift_signed(x: u16, shl: i8) -> u8 {
314 if shl >= 0 {
315 debug_assert!(shl < 8);
316 (x << shl) as u8
317 } else {
318 let shr = (-shl) as u8;
319 (x >> shr) as u8
320 }
321 }
322
323 for (coeffs, bytes) in self
324 .as_scalar_array()
325 .as_array_chunks::<{ 4 * 2 }>()
326 .zip(ct.as_array_chunks_mut::<D>())
327 {
328 let mut shl: i8 = 0;
329 let mut idx = 0;
330 for b in bytes {
332 debug_assert!(shl < 8);
333
334 *b = shift_signed(compress_d::<D>(coeffs[idx]), shl);
335
336 while D as i8 + shl < 8 {
337 shl += D as i8;
338 idx += 1;
339 *b |= shift_signed(compress_d::<D>(coeffs[idx]), shl);
340 }
341 shl -= 8;
342 }
343 }
344 }
345
346 #[inline]
347 pub fn decompress_slice<const D: usize>(&mut self, ct: &[u8]) {
348 assert_eq!(ct.len(), poly_compressed_bytes(D as u8));
349
350 #[inline(always)]
351 fn shift_signed<const D: usize>(x: u8, shl: i8) -> u16 {
352 if shl >= 0 {
353 (x as u16) << shl
354 } else {
355 (x as u16) >> (-shl)
356 }
357 }
358
359 for (coeffs, bytes) in self
360 .as_scalar_array_mut()
361 .as_array_chunks_mut::<{ 4 * 2 }>()
362 .zip(ct.as_array_chunks::<D>())
363 {
364 let mut shl: i8 = 0;
365 let mut idx = 0;
366
367 for c in coeffs {
368 let mut d_bits = shift_signed::<D>(bytes[idx], shl);
369
370 while shl + 8 < D as i8 {
371 shl += 8;
372 idx += 1;
373 d_bits |= shift_signed::<D>(bytes[idx], shl);
374 }
375 shl -= D as i8;
376 *c = decompress_d::<D>(d_bits);
377 }
378 }
379 }
380
381 #[inline]
382 pub fn compress<const D: usize>(&self, ct: &mut [[u8; D]; 32]) {
383 #[inline(always)]
391 fn shift_signed(x: u16, shl: i8) -> u8 {
392 if shl >= 0 {
393 debug_assert!(shl < 8);
394 (x << shl) as u8
395 } else {
396 let shr = (-shl) as u8;
397 (x >> shr) as u8
398 }
399 }
400
401 for (coeffs, bytes) in self
402 .as_scalar_array()
403 .as_array_chunks::<{ 4 * 2 }>()
404 .zip(ct)
405 {
406 let mut shl: i8 = 0;
424 let mut idx = 0;
425
426 for b in bytes {
427 debug_assert!(shl < 8);
428
429 *b = shift_signed(compress_d::<D>(coeffs[idx]), shl);
430
431 while D as i8 + shl < 8 {
432 shl += D as i8;
433 idx += 1;
434 *b |= shift_signed(compress_d::<D>(coeffs[idx]), shl);
435 }
436 shl -= 8;
437 }
438 }
439 }
440
441 #[inline]
442 pub fn decompress<const D: usize>(&mut self, ct: &[[u8; D]; 32]) {
443 #[inline(always)]
444 fn shift_signed<const D: usize>(x: u8, shl: i8) -> u16 {
445 if shl >= 0 {
446 (x as u16) << shl
447 } else {
448 (x as u16) >> (-shl)
449 }
450 }
451
452 for (coeff_pairs, bytes) in self.as_mut().chunks_exact_mut(4).zip(ct) {
453 let mut shl: i8 = 0;
454 let mut idx = 0;
455
456 for c in coeff_pairs.iter_mut().flat_map(|f| f.0.iter_mut()) {
457 let mut d_bits = shift_signed::<D>(bytes[idx], shl);
458
459 while shl + 8 < D as i8 {
460 shl += 8;
461 idx += 1;
462 d_bits |= shift_signed::<D>(bytes[idx], shl);
463 }
464 shl -= D as i8;
465 *c = decompress_d::<D>(d_bits);
466 }
467 }
468 }
469
470 pub fn set_from_message(&mut self, msg: &[u8; KYBER_N / 8]) {
471 const ONE_COEFF: i16 = (KYBER_Q + 1) / 2;
472 for (i, byte) in msg.iter().enumerate() {
473 for j in 0..4 {
474 let mask0 = 0i16.wrapping_sub(((*byte) >> (2 * j) & 1) as i16);
475 let mask1 = 0i16.wrapping_sub(((*byte) >> (2 * j + 1) & 1) as i16);
476
477 debug_assert!(mask0 == 0 || mask0 == -1);
478 debug_assert!(mask1 == 0 || mask1 == -1);
479
480 self[4 * i + j].0 = [mask0 & ONE_COEFF, mask1 & ONE_COEFF];
481
482 let x = self[4 * i + j].0;
483 debug_assert!(x[0] == 0 || x[0] == ONE_COEFF);
484 debug_assert!(x[1] == 0 || x[1] == ONE_COEFF);
485 }
486 }
487 }
488
489 #[inline(always)]
490 pub fn from_message(msg: &[u8; KYBER_N / 8]) -> Self {
491 let mut poly = Self::default();
492 poly.set_from_message(msg);
493 poly
494 }
495
496 pub fn compress_to_message(&self, msg: &mut [u8; 32]) {
497 for (coeff_pairs, byte) in self.as_ref().chunks_exact(4).zip(msg.iter_mut()) {
498 *byte = 0;
499 for (j, c) in coeff_pairs
500 .iter()
501 .flat_map(|f| f.reduce().0.map(|u| compress_d::<1>(u) as u8))
502 .enumerate()
503 {
504 *byte |= c << j;
505 }
506 }
507 }
508
509 #[inline]
513 pub fn scale_mont(&mut self) {
514 for coeff in self {
515 coeff.scale_mont();
516 }
517 }
518}
519
520use rand::{CryptoRng, Rng, RngCore};
521
522impl KyberPoly {
523 #[doc(hidden)]
524 pub fn new_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
525 let mut poly = Self::default();
526 fn frand<R: RngCore + CryptoRng>(rng: &mut R) -> i16 {
527 rng.gen_range(-KYBER_Q / 2..=KYBER_Q / 2)
528 }
529 for c in poly.as_mut() {
530 c.0[0] = frand(rng);
531 c.0[1] = frand(rng);
532 }
533 poly
534 }
535}
536
537#[cfg(test)]
538mod tests {
539 use crate::field::kyber::{KYBER_Q, QINV};
540 use crate::polyvec::KyberPolyVec;
541 use crate::utils::*;
542 extern crate std;
543 use super::*;
544 use crate::poly::SizedPolynomial;
545 use crate::utils::unsafe_utils::flatten::{
546 FlattenArray, FlattenSlice, FlattenSliceMut, FlattenTwice, FlattenTwiceMut,
547 };
548 use crystals_cref::kyber as cref;
549 use std::*;
550
551 const M: usize = KyberPoly::NUM_SCALARS / 8;
552
553 #[test]
554 fn q_inv_test() {
555 assert_eq!(QINV, invm(KYBER_Q as i32, 1 << 16).unwrap() as i16);
556 }
557
558 #[test]
559 fn test_ntt_and_then_invtt() {
560 let mut rng = rand::thread_rng();
561 for testcase in 0..3_000 {
562 let mut poly = KyberPoly::new_random(&mut rng);
563 let poly_copy = poly.clone();
564 poly.ntt();
565 poly.inv_ntt();
566
567 for f in poly.as_mut() {
568 *f = *f * 1; }
570 assert_eq!(poly, poly_copy, "failed testcase #{}", testcase);
573 }
574 }
575
576 #[test]
577 #[cfg(not(miri))]
578 fn test_ntt_vs_ref() {
579 let mut rng = rand::thread_rng();
580 for _ in 0..3_000 {
581 let mut poly = KyberPoly::new_random(&mut rng);
582 let mut p = poly.into_array();
583
584 crystals_cref::kyber::ntt(&mut p);
585
586 poly.ntt();
587 poly.reduce();
588
589 assert_eq!(poly.into_array(), p);
590 }
591 }
592
593 #[test]
594 #[cfg(not(miri))]
595 fn test_invntt_vs_ref() {
596 let mut rng = rand::thread_rng();
597 for _ in 0..3_000 {
598 let mut poly = KyberPoly::new_random(&mut rng);
599 let mut p = poly.into_array();
600 poly.inv_ntt();
601 poly.reduce();
602 crystals_cref::kyber::inv_ntt(&mut p);
603 crystals_cref::kyber::poly_reduce(&mut p);
604
605 assert_eq!(poly.into_array(), p);
606 }
607 }
608
609 #[test]
610 #[cfg(not(miri))]
611 fn pwm_vs_ref() {
612 let mut rng = rand::thread_rng();
613 for _ in 0..1_000 {
614 let poly_a = KyberPoly::new_random(&mut rng);
615 let poly_b = KyberPoly::new_random(&mut rng);
616 let mut poly_r = KyberPoly::default();
617
618 let a = poly_a.into_array();
619 let b = poly_b.into_array();
620 let mut r = poly_r.into_array();
621
622 poly_a.pointwise(&poly_b, &mut poly_r);
623 poly_r.reduce();
624
625 crystals_cref::kyber::poly_pointwise_montgomery(&mut r, &a, &b);
626 crystals_cref::kyber::poly_reduce(&mut r);
627
628 assert_eq!(poly_r.into_array(), r);
629 }
630 }
631
632 #[test]
633 #[cfg(not(miri))]
634 fn test_cbd2_vs_ref() {
635 let mut rng = rand::thread_rng();
636 for _ in 0..1_000 {
637 let mut poly = KyberPoly::new_random(&mut rng);
638 let mut p = poly.into_array();
639
640 let mut buf = [0u8; 2 * KYBER_N / 4];
641 rng.fill(buf.as_mut());
642
643 poly.cbd2(&buf);
644 crystals_cref::kyber::poly_cbd_eta_eq_2(&mut p, &buf);
645
646 assert_eq!(poly.into_array(), p);
647 }
648 }
649
650 #[test]
651 #[cfg(not(miri))]
652 fn test_cbd3_vs_ref() {
653 let mut rng = rand::thread_rng();
654 for _ in 0..1_000 {
655 let mut poly = KyberPoly::new_random(&mut rng);
656 let mut p = poly.into_array();
657
658 let mut buf = [0u8; 3 * KYBER_N / 4];
659 rng.fill(buf.as_mut());
660
661 poly.cbd3(&buf);
662 crystals_cref::kyber::poly_cbd_eta_eq_3(&mut p, &buf);
663
664 assert_eq!(poly.into_array(), p);
665 }
666 }
667
668 #[test]
669 #[cfg(not(miri))]
670 fn test_getnoise_eta3_vs_ref() {
671 let mut rng = rand::thread_rng();
672 let mut prf = Prf::default();
673 for _ in 0..1_000 {
674 let mut poly = KyberPoly::new_random(&mut rng);
675 let mut p = poly.into_array();
676
677 let mut seed = [0u8; NOISE_SEED_BYTES];
678 rng.fill(seed.as_mut());
679 let nonce = rand::random();
680
681 poly.getnoise_eta1::<2>(&mut prf, &seed, nonce);
682 crystals_cref::kyber::poly_getnoise_eta_eq_3(&mut p, &seed, nonce);
683
684 assert_eq!(poly.into_array(), p);
685 }
686 }
687
688 #[test]
689 #[cfg(not(miri))] fn test_polycompress_vs_ref() {
691 for _ in 0..1_000 {
692 let poly = KyberPoly::new_random(&mut rand::thread_rng());
693
694 {
695 const D: u8 = 4;
696 let mut ct = [[0u8; { D as usize }]; M];
697 let mut ct_ref = [0u8; poly_compressed_bytes(D)];
698
699 poly.compress::<{ D as usize }>(&mut ct);
700 cref::poly_compress::<2>(&mut ct_ref, &poly.as_scalar_array());
701 assert_eq!(ct.flatten_array(), &ct_ref);
702 }
703
704 {
705 const D: u8 = 4;
706 let mut ct = [[0u8; { D as usize }]; M];
707 let mut ct_ref = [0u8; poly_compressed_bytes(D)];
708
709 poly.compress::<{ D as usize }>(&mut ct);
710 cref::poly_compress::<3>(&mut ct_ref, &poly.as_scalar_array());
711 assert_eq!(ct.flatten_array(), &ct_ref);
712 }
713 {
714 const D: u8 = 5;
715 let mut ct = [[0u8; { D as usize }]; M];
716 let mut ct_ref = [0u8; poly_compressed_bytes(D)];
717
718 poly.compress::<{ D as usize }>(&mut ct);
719 cref::poly_compress::<4>(&mut ct_ref, &poly.as_scalar_array());
720 assert_eq!(ct.flatten_array(), &ct_ref);
721 }
722 }
723 }
724
725 fn test_poly_decompress_vs_ref<const K: usize, const D: usize>() {
726 {
727 let mut rng = rand::thread_rng();
728 let mut poly_ref = [0i16; KYBER_N];
729 let mut poly = KyberPoly::default();
730 let mut ct = [[0u8; D]; M];
731
732 for _ in 0..5_000 {
733 rng.fill_bytes(ct.flatten_slice_mut());
734
735 poly.decompress::<D>(&mut ct);
736 cref::poly_decompress::<K>(&mut poly_ref, ct.flatten_slice());
737 assert_eq!(poly.as_scalar_array(), &poly_ref);
738 }
739 }
740 }
741
742 fn test_polyvec_decompress_vs_ref<const K: usize, const D: usize>() {
743 {
744 let mut rng = rand::thread_rng();
745 let mut pv_ref = [[0i16; KYBER_N]; K];
746 let mut pv = KyberPolyVec::<K>::default();
747 let mut ct = [[[0u8; D]; M]; K];
748
749 for _ in 0..1_000 {
750 rng.fill_bytes(ct.flatten_twice_mut());
751
752 pv.decompress::<D>(&mut ct);
753 cref::polyvec_decompress::<K>(&mut pv_ref, ct.flatten_twice());
754 for (p, p_ref) in pv.into_iter().zip(pv_ref) {
755 assert_eq!(p.as_scalar_array(), &p_ref);
756 }
757 }
758 }
759 }
760
761 #[test]
762 #[cfg(not(miri))] fn polyvec_decompress_vs_ref_2() {
764 test_polyvec_decompress_vs_ref::<2, 10>();
765 }
766
767 #[test]
768 #[cfg(not(miri))]
769 fn polyvec_decompress_vs_ref_3() {
770 test_polyvec_decompress_vs_ref::<3, 10>();
771 }
772
773 #[test]
774 #[cfg(not(miri))]
775 fn polyvec_decompress_vs_ref_4() {
776 test_polyvec_decompress_vs_ref::<4, 11>();
777 }
778
779 #[test]
780 #[cfg(not(miri))] fn poly_decompress_vs_ref_2() {
782 test_poly_decompress_vs_ref::<2, 4>();
783 }
784 #[test]
785 #[cfg(not(miri))]
786 fn poly_decompress_vs_ref_3() {
787 test_poly_decompress_vs_ref::<3, 4>();
788 }
789 #[test]
790 #[cfg(not(miri))]
791 fn poly_decompress_vs_ref_4() {
792 test_poly_decompress_vs_ref::<4, 5>();
793 }
794}