1use crate::utils::flatten::{FlattenSlice, FlattenSliceMut, FlattenTwice, FlattenTwiceMut};
2use crate::utils::gcd_u8;
3use crate::{
4 poly::{
5 kyber::{
6 compress_d, poly_compressed_bytes, polyvec_compressed_bytes_for_k, KyberPoly, KYBER_N,
7 },
8 Polynomial,
9 },
10 polyvec::{KyberPolyVec, PolynomialVector},
11 utils::{flatten::FlattenArray, split::Splitter},
12};
13
14#[cfg(any(feature = "std", feature = "alloc", test))]
15use crate::poly::kyber::poly_compressed_bytes_for_k;
16
17#[derive(Debug)]
18#[rustfmt::skip] pub struct Ciphertext<
20 const D_POLY: usize,
21 const D_PV: usize,
22 const K: usize,
23 const M: usize = { KYBER_N / 8 },
24>
25{
26 v: [[u8; D_POLY]; M],
27 b: [[[u8; D_PV]; M]; K],
28}
29
30impl<const D_POLY: usize, const D_PV: usize, const M: usize, const K: usize> Default
31 for Ciphertext<D_POLY, D_PV, K, M>
32{
33 fn default() -> Self {
34 Ciphertext {
35 v: [[0u8; D_POLY]; M],
36 b: [[[0u8; D_PV]; M]; K],
37 }
38 }
39}
40
41pub trait CompressedCiphertex {
42 const M: usize = KYBER_N / 8;
43
44 fn poly_bytes(&self) -> &[u8];
45 fn polyvec_bytes(&self) -> &[u8];
46 fn poly_bytes_mut(&mut self) -> &mut [u8];
47 fn polyvec_bytes_mut(&mut self) -> &mut [u8];
48}
49
50pub trait CompressCiphertext {
51 type PolyType: Polynomial;
52 type PolyVecType: PolynomialVector;
53
54 fn compress_poly(&mut self, v: &Self::PolyType);
55 fn decompress_poly(&self, v: &mut Self::PolyType);
56
57 fn compress_polyvec(&mut self, b: &Self::PolyVecType);
58 fn decompress_polyvec(&self, b: &mut Self::PolyVecType);
59}
60
61const K23_POLY_COMPRESSED_BYTES: usize = poly_compressed_bytes(4);
62const K4_POLY_COMPRESSED_BYTES: usize = poly_compressed_bytes(5);
63const K2_POLYVEC_COMPRESSED_BYTES: usize = polyvec_compressed_bytes_for_k::<2>();
64const K3_POLYVEC_COMPRESSED_BYTES: usize = polyvec_compressed_bytes_for_k::<3>();
65const K4_POLYVEC_COMPRESSED_BYTES: usize = polyvec_compressed_bytes_for_k::<4>();
66
67pub const K2_CT_BYTES: usize = K23_POLY_COMPRESSED_BYTES + K2_POLYVEC_COMPRESSED_BYTES;
68pub const K3_CT_BYTES: usize = K23_POLY_COMPRESSED_BYTES + K3_POLYVEC_COMPRESSED_BYTES;
69pub const K4_CT_BYTES: usize = K4_POLY_COMPRESSED_BYTES + K4_POLYVEC_COMPRESSED_BYTES;
70
71#[inline]
73#[allow(dead_code)]
74fn polycompress_d4(ct: &mut [u8; poly_compressed_bytes(4)], poly: &KyberPoly) {
75 const D: u8 = 4;
76 for (f, r) in poly.into_iter().zip(ct.iter_mut()) {
83 let t = f.0.map(|x| compress_d::<{ D as usize }>(x) as u8);
84 *r = t[0] | (t[1] << D);
85 }
86}
87
88#[inline]
90#[allow(dead_code)]
91fn polycompress_d5(ct: &mut [u8; poly_compressed_bytes(5)], poly: &KyberPoly) {
92 const D: u8 = 5;
93 const M: usize = 4 / gcd_u8(D, 4) as usize;
96 const N: usize = (D / gcd_u8(D, 4)) as usize;
97 for (f4, r) in poly
110 .as_ref()
111 .as_array_chunks::<M>()
112 .zip(ct.as_array_chunks_mut::<N>())
113 {
114 let a = f4.map(|f| f.0.map(|x| compress_d::<{ D as usize }>(x) as u8));
115 let t: &[u8; 8] = a.flatten_array();
116
117 r[0] = t[0] | (t[1] << 5);
118 r[1] = (t[1] >> 3) | (t[2] << 2) | (t[3] << 7);
119 r[2] = (t[3] >> 1) | (t[4] << 4);
120 r[3] = (t[4] >> 4) | (t[5] << 1) | (t[6] << 6);
121 r[4] = (t[6] >> 2) | (t[7] << 3);
122 }
123}
124
125#[inline]
127#[allow(dead_code)]
128fn polycompress_d10(ct: &mut [u8; poly_compressed_bytes(10)], poly: &KyberPoly) {
129 const D: u8 = 10;
130 const M: usize = 4 / gcd_u8(D, 4) as usize;
133 const N: usize = (D / gcd_u8(D, 4)) as usize;
134 for (f2, r) in poly
135 .as_ref()
136 .as_array_chunks::<M>()
137 .zip(ct.as_array_chunks_mut::<N>())
138 {
139 let a = f2.map(|f| f.0.map(compress_d::<{ D as usize }>));
140 let t: &[u16; 4] = a.flatten_array();
141
142 r[0] = t[0] as u8;
143 r[1] = (t[0] >> 8) as u8 | (t[1] << 2) as u8;
144 r[2] = (t[1] >> 6) as u8 | (t[2] << 4) as u8;
145 r[3] = (t[2] >> 4) as u8 | (t[3] << 6) as u8;
146 r[4] = (t[3] >> 2) as u8;
147 }
148}
149
150#[inline]
152#[allow(dead_code)]
153fn polycompress_d11(ct: &mut [u8; poly_compressed_bytes(11)], poly: &KyberPoly) {
154 const D: u8 = 11;
155 const M: usize = 4 / gcd_u8(D, 4) as usize;
158 const N: usize = (D / gcd_u8(D, 4)) as usize;
159 for (f4, r) in poly
160 .as_ref()
161 .as_array_chunks::<4>()
162 .zip(ct.as_array_chunks_mut::<11>())
163 {
164 let a = f4.map(|f| f.0.map(compress_d::<{ D as usize }>));
165 let t: &[u16; 8] = a.flatten_array();
166
167 r[0] = t[0] as u8;
168 r[1] = (t[0] >> 8) as u8 | (t[1] << 3) as u8;
169 r[2] = (t[1] >> 5) as u8 | (t[2] << 6) as u8;
170 r[3] = (t[2] >> 2) as u8;
171 r[4] = (t[2] >> 10) as u8 | (t[3] << 1) as u8;
172 r[5] = (t[3] >> 7) as u8 | (t[4] << 4) as u8;
173 r[6] = (t[4] >> 4) as u8 | (t[5] << 7) as u8;
174 r[7] = (t[5] >> 1) as u8;
175 r[8] = (t[5] >> 9) as u8 | (t[6] << 2) as u8;
176 r[9] = (t[6] >> 6) as u8 | (t[7] << 5) as u8;
177 r[10] = (t[7] >> 3) as u8;
178 }
179}
180
181impl<const D_POLY: usize, const D_PV: usize, const M: usize, const K: usize> CompressedCiphertex
182 for Ciphertext<D_POLY, D_PV, K, M>
183{
184 const M: usize = M;
185
186 fn poly_bytes(&self) -> &[u8] {
187 self.v.flatten_slice()
188 }
189 fn polyvec_bytes(&self) -> &[u8] {
190 self.b.flatten_twice()
191 }
192
193 fn poly_bytes_mut(&mut self) -> &mut [u8] {
194 self.v.flatten_slice_mut()
195 }
196
197 fn polyvec_bytes_mut(&mut self) -> &mut [u8] {
198 self.b.flatten_twice_mut()
199 }
200}
201
202impl<const K: usize, const D1: usize, const D2: usize> CompressCiphertext
203 for Ciphertext<D1, D2, K, 32>
204{
205 type PolyType = KyberPoly;
206
207 type PolyVecType = KyberPolyVec<K>;
208
209 fn compress_poly(&mut self, v: &Self::PolyType) {
210 v.compress(&mut self.v)
211 }
212
213 fn decompress_poly(&self, v: &mut Self::PolyType) {
214 v.decompress(&self.v)
215 }
216
217 fn compress_polyvec(&mut self, b: &Self::PolyVecType) {
218 b.compress(&mut self.b)
219 }
220
221 fn decompress_polyvec(&self, b: &mut Self::PolyVecType) {
222 b.decompress(&self.b)
223 }
224}
225
226#[cfg(any(feature = "std", feature = "alloc", test))]
227pub struct VecCipherText<const K: usize>(crate::lib::Vec<u8>);
228
229#[cfg(any(feature = "std", feature = "alloc", test))]
230impl<const K: usize> Default for VecCipherText<K> {
231 fn default() -> Self {
232 VecCipherText(crate::lib::from_elem(
233 0,
234 polyvec_compressed_bytes_for_k::<K>() + poly_compressed_bytes_for_k::<K>(),
235 ))
236 }
237}
238
239#[cfg(any(feature = "std", feature = "alloc", test))]
240impl<const K: usize> AsRef<[u8]> for VecCipherText<K> {
241 fn as_ref(&self) -> &[u8] {
242 &self.0
243 }
244}
245
246#[cfg(any(feature = "std", feature = "alloc", test))]
247impl<const K: usize> AsMut<[u8]> for VecCipherText<K> {
248 fn as_mut(&mut self) -> &mut [u8] {
249 &mut self.0
250 }
251}
252
253#[cfg(any(feature = "std", feature = "alloc", test))]
254impl<const K: usize> CompressedCiphertex for VecCipherText<K> {
255 const M: usize = KYBER_N / 8;
256
257 fn poly_bytes(&self) -> &[u8] {
258 &self.0[polyvec_compressed_bytes_for_k::<K>()..]
259 }
260
261 fn polyvec_bytes(&self) -> &[u8] {
262 &self.0[..polyvec_compressed_bytes_for_k::<K>()]
263 }
264
265 fn poly_bytes_mut(&mut self) -> &mut [u8] {
266 &mut self.0[polyvec_compressed_bytes_for_k::<K>()..]
267 }
268
269 fn polyvec_bytes_mut(&mut self) -> &mut [u8] {
270 &mut self.0[..polyvec_compressed_bytes_for_k::<K>()]
271 }
272}
273
274#[cfg(any(feature = "std", feature = "alloc", test))]
275impl<const K: usize> CompressCiphertext for VecCipherText<K> {
276 type PolyType = KyberPoly;
277 type PolyVecType = KyberPolyVec<K>;
278
279 fn compress_poly(&mut self, v: &KyberPoly) {
280 let ct_v = self.poly_bytes_mut();
281 assert_eq!(ct_v.len(), poly_compressed_bytes_for_k::<K>());
282 match K {
283 2 | 3 => {
284 v.compress_slice::<4>(ct_v);
285 }
286 4 => {
287 v.compress_slice::<5>(ct_v);
288 }
289 _ => unreachable!(),
290 }
291 }
292
293 fn compress_polyvec(&mut self, b: &KyberPolyVec<K>) {
294 let ct_b = self.polyvec_bytes_mut();
295 let ct_per_poly_len = polyvec_compressed_bytes_for_k::<K>() / K;
296 for (poly, bytes) in b.into_iter().zip(ct_b.chunks_exact_mut(ct_per_poly_len)) {
297 match K {
298 2 | 3 => {
299 poly.compress_slice::<10>(bytes);
300 }
301 4 => {
302 poly.compress_slice::<11>(bytes);
303 }
304 _ => unreachable!(),
305 }
306 }
307 }
308
309 fn decompress_poly(&self, v: &mut Self::PolyType) {
310 let ct = &self.0[polyvec_compressed_bytes_for_k::<K>()..];
311 assert_eq!(ct.len(), poly_compressed_bytes_for_k::<K>());
312 match K {
313 2 | 3 => {
314 v.decompress_slice::<4>(ct);
315 }
316 4 => {
317 v.decompress_slice::<5>(ct);
318 }
319 _ => unreachable!(),
320 }
321 }
322
323 fn decompress_polyvec(&self, b: &mut Self::PolyVecType) {
324 let pvct = &self.0[..polyvec_compressed_bytes_for_k::<K>()];
325 let ct_per_poly_len = polyvec_compressed_bytes_for_k::<K>() / K;
326 for (poly, bytes) in b.into_iter().zip(pvct.chunks_exact(ct_per_poly_len)) {
327 match K {
328 2 | 3 => {
329 poly.decompress_slice::<10>(bytes);
330 }
331 4 => {
332 poly.decompress_slice::<11>(bytes);
333 }
334 _ => unreachable!(),
335 }
336 }
337 }
338}
339
340#[cfg(test)]
403mod tests {
404 extern crate std;
405 use super::*;
406 use crate::poly::SizedPolynomial;
407 use crystals_cref::kyber as cref;
408 use std::*;
409
410 const M: usize = KyberPoly::NUM_SCALARS / 8;
411
412 #[test]
413 #[cfg(not(miri))] fn test_polycompress_d_vs_ref_pv() {
415 for _ in 0..1_000 {
416 {
417 const K: usize = 2;
418 const D: u8 = 10;
419
420 let pv = KyberPolyVec::<K>::new_random(&mut rand::thread_rng());
421 let mut ct = [[[0u8; { D as usize }]; M]; K];
422 let mut ct_ref = [0u8; poly_compressed_bytes(D) * K];
423
424 pv.compress::<{ D as usize }>(&mut ct);
425 cref::polyvec_compress::<K>(&mut ct_ref, &pv.as_ref().map(|p| p.into_array()));
426 assert_eq!(
427 FlattenArray::<_, { D as usize }, {M * K}, { M * K* D as usize }>::flatten_array(
428 ct.flatten_array()
429 ),
430 &ct_ref
431 );
432 }
433
434 {
435 const K: usize = 3;
436 const D: u8 = 10;
437
438 let pv = KyberPolyVec::<K>::new_random(&mut rand::thread_rng());
439 let mut ct = [[[0u8; { D as usize }]; M]; K];
440 let mut ct_ref = [0u8; polyvec_compressed_bytes_for_k::<K>()];
441
442 pv.compress::<{ D as usize }>(&mut ct);
443 cref::polyvec_compress::<K>(&mut ct_ref, &pv.as_ref().map(|p| p.into_array()));
444 let t: &[[_; D as usize]; K * M] = ct.flatten_array();
445 assert_eq!(t.flatten_array(), &ct_ref, "\nct_ref: {:?}\n", ct_ref,);
446 }
447 {
448 const K: usize = 4;
449 const D: u8 = 11;
450
451 let pv = KyberPolyVec::<K>::new_random(&mut rand::thread_rng());
452 let mut ct = [[[0u8; { D as usize }]; M]; K];
453 let mut ct_ref = [0u8; polyvec_compressed_bytes_for_k::<K>()];
454
455 pv.compress::<{ D as usize }>(&mut ct);
456 cref::polyvec_compress::<K>(&mut ct_ref, &pv.as_ref().map(|p| p.into_array()));
457 let t: &[[_; D as usize]; K * M] = ct.flatten_array();
458 assert_eq!(t.flatten_array(), &ct_ref, "\nct_ref: {:?}\n", ct_ref,);
459 }
460 }
461 }
462}