Skip to main content

crystals_rs/
ciphertext.rs

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] // rustfmt BUG
19pub 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// 4:1 compression
72#[inline]
73#[allow(dead_code)]
74fn polycompress_d4(ct: &mut [u8; poly_compressed_bytes(4)], poly: &KyberPoly) {
75    const D: u8 = 4;
76    // n: number of bytes, m: number of poly elements (2 * i16)
77    // 4*n = d*m
78    // const M: usize = 4 / gcd_4(D) as usize;
79    // const N: usize = (D / gcd_4(D)) as usize;
80    // debug_assert!(M == 1 && N == 1);
81
82    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// 16:5 compression
89#[inline]
90#[allow(dead_code)]
91fn polycompress_d5(ct: &mut [u8; poly_compressed_bytes(5)], poly: &KyberPoly) {
92    const D: u8 = 5;
93    // n: number of bytes, m: number of poly elements (2 * i16)
94    // 4*n = d*m
95    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.as_ref().chunks_exact(4).zip(ct.chunks_exact_mut(5)) {
98    //     debug_assert_eq!(f4.len(), 4); // 8 i16 scalars
99    //     debug_assert_eq!(r.len(), 5);
100
101    //     let mut t = [0u8; 8];
102    //     for (ti, xi) in t.iter_mut().zip(
103    //         f4.into_iter()
104    //             .map(|f| f.0.map(|x| compress_4_5(x, D)))
105    //             .flatten(),
106    //     ) {
107    //         *ti = xi;
108    //     }
109    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// 8:5 compression
126#[inline]
127#[allow(dead_code)]
128fn polycompress_d10(ct: &mut [u8; poly_compressed_bytes(10)], poly: &KyberPoly) {
129    const D: u8 = 10;
130    // n: number of bytes, m: number of poly elements (2 * i16)
131    // 4*n = d*m
132    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// 16:11 compression
151#[inline]
152#[allow(dead_code)]
153fn polycompress_d11(ct: &mut [u8; poly_compressed_bytes(11)], poly: &KyberPoly) {
154    const D: u8 = 11;
155    // n: number of bytes, m: number of poly elements (2 * i16)
156    // 4*n = d*m
157    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// impl CompressCiphertext<3> for Ciphertext<3, K3_CT_BYTES> {
341//     fn compress_poly(&mut self, v: &KyberPoly) {
342//         let (_, ct_poly): (
343//             &mut [u8; K3_POLYVEC_COMPRESSED_BYTES],
344//             &mut [u8; K23_POLY_COMPRESSED_BYTES],
345//         ) = self.0.dissect_mut();
346//         polycompress_d4(ct_poly, v);
347//     }
348
349//     fn compress_polyvec(&mut self, b: &KyberPolyVec<3>) {
350//         let (ct_polyvec, _): (
351//             &mut [u8; K3_POLYVEC_COMPRESSED_BYTES],
352//             &mut [u8; K23_POLY_COMPRESSED_BYTES],
353//         ) = self.0.dissect_mut();
354//         for (poly, ct_chunk) in b.into_iter().zip(ct_polyvec.as_array_chunks_mut()) {
355//             polycompress_d10(ct_chunk, poly);
356//         }
357//     }
358// }
359
360// impl CompressCiphertext<4> for Ciphertext<4, K4_CT_BYTES> {
361//     fn compress_poly(&mut self, v: &KyberPoly) {
362//         let (_, ct_poly): (
363//             &mut [u8; K4_POLYVEC_COMPRESSED_BYTES],
364//             &mut [u8; K4_POLY_COMPRESSED_BYTES],
365//         ) = self.0.dissect_mut();
366//         polycompress_d5(ct_poly, v);
367//     }
368
369//     fn compress_polyvec(&mut self, b: &KyberPolyVec<4>) {
370//         let (ct_polyvec, _): (
371//             &mut [u8; K4_POLYVEC_COMPRESSED_BYTES],
372//             &mut [u8; K4_POLY_COMPRESSED_BYTES],
373//         ) = self.0.dissect_mut();
374//         for (poly, ct_chunk) in b.into_iter().zip(ct_polyvec.as_array_chunks_mut()) {
375//             polycompress_d10(ct_chunk, poly);
376//         }
377//     }
378// }
379
380// impl<const K: usize> CompressCiphertext for &mut [u8] {
381//     fn compress_poly(&mut self, v: &KyberPoly) {
382//         assert_eq!(self.len(), KYBER_N * (K / 4 + 4) / 8);
383//         let (_, ct_poly): (
384//             &mut [u8; K3_POLYVEC_COMPRESSED_BYTES],
385//             &mut [u8; K23_POLY_COMPRESSED_BYTES],
386//         ) = self.0.dissect_mut();
387//         polycompress_d4(ct_poly, v);
388//     }
389
390//     fn compress_polyvec(&mut self, b: &KyberPolyVec<K>) {
391//         assert_eq!(self.len(), KYBER_N * (K / 4 + 10) / 8);
392//         let (ct_polyvec, _): (
393//             &mut [u8; K3_POLYVEC_COMPRESSED_BYTES],
394//             &mut [u8; K23_POLY_COMPRESSED_BYTES],
395//         ) = self.0.dissect_mut();
396//         for (poly, ct_chunk) in b.into_iter().zip(ct_polyvec.into_array_chunks_mut()) {
397//             polycompress_d10(ct_chunk, poly);
398//         }
399//     }
400// }
401
402#[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))] // miri does not support calling foreign functions
414    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}