arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/gglwe_ciphertext.rs
core/src/gglwe_ciphertext.rsRaw
  1use base2k::{
  2    Backend, FFT64, MatZnxDft, MatZnxDftAlloc, MatZnxDftOps, MatZnxDftScratch, MatZnxDftToMut, MatZnxDftToRef, Module, ScalarZnx,
  3    ScalarZnxDft, ScalarZnxDftToRef, ScalarZnxToRef, Scratch, VecZnx, VecZnxAlloc, VecZnxBig, VecZnxBigOps, VecZnxBigScratch,
  4    VecZnxDft, VecZnxDftAlloc, VecZnxDftOps, VecZnxDftToMut, VecZnxDftToRef, VecZnxOps, VecZnxToMut, VecZnxToRef, ZnxInfos,
  5    ZnxZero,
  6};
  7use sampling::source::Source;
  8
  9use crate::{
 10    elem::{GetRow, Infos, SetRow},
 11    glwe_ciphertext::GLWECiphertext,
 12    glwe_ciphertext_fourier::GLWECiphertextFourier,
 13    glwe_plaintext::GLWEPlaintext,
 14    keys::SecretKeyFourier,
 15    utils::derive_size,
 16    vec_glwe_product::{VecGLWEProduct, VecGLWEProductScratchSpace},
 17};
 18
 19pub struct GGLWECiphertext<C, B: Backend> {
 20    pub(crate) data: MatZnxDft<C, B>,
 21    pub(crate) basek: usize,
 22    pub(crate) k: usize,
 23}
 24
 25impl<B: Backend> GGLWECiphertext<Vec<u8>, B> {
 26    pub fn new(module: &Module<B>, base2k: usize, k: usize, rows: usize, rank_in: usize, rank_out: usize) -> Self {
 27        Self {
 28            data: module.new_mat_znx_dft(rows, rank_in, rank_out + 1, derive_size(base2k, k)),
 29            basek: base2k,
 30            k,
 31        }
 32    }
 33}
 34
 35impl<T, B: Backend> Infos for GGLWECiphertext<T, B> {
 36    type Inner = MatZnxDft<T, B>;
 37
 38    fn inner(&self) -> &Self::Inner {
 39        &self.data
 40    }
 41
 42    fn basek(&self) -> usize {
 43        self.basek
 44    }
 45
 46    fn k(&self) -> usize {
 47        self.k
 48    }
 49}
 50
 51impl<T, B: Backend> GGLWECiphertext<T, B> {
 52    pub fn rank(&self) -> usize {
 53        self.data.cols_out() - 1
 54    }
 55
 56    pub fn rank_in(&self) -> usize {
 57        self.data.cols_in()
 58    }
 59
 60    pub fn rank_out(&self) -> usize {
 61        self.data.cols_out() - 1
 62    }
 63}
 64
 65impl<C, B: Backend> MatZnxDftToMut<B> for GGLWECiphertext<C, B>
 66where
 67    MatZnxDft<C, B>: MatZnxDftToMut<B>,
 68{
 69    fn to_mut(&mut self) -> MatZnxDft<&mut [u8], B> {
 70        self.data.to_mut()
 71    }
 72}
 73
 74impl<C, B: Backend> MatZnxDftToRef<B> for GGLWECiphertext<C, B>
 75where
 76    MatZnxDft<C, B>: MatZnxDftToRef<B>,
 77{
 78    fn to_ref(&self) -> MatZnxDft<&[u8], B> {
 79        self.data.to_ref()
 80    }
 81}
 82
 83impl GGLWECiphertext<Vec<u8>, FFT64> {
 84    pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, rank: usize, size: usize) -> usize {
 85        GLWECiphertext::encrypt_sk_scratch_space(module, size)
 86            + module.bytes_of_vec_znx(rank + 1, size)
 87            + module.bytes_of_vec_znx(1, size)
 88            + module.bytes_of_vec_znx_dft(rank + 1, size)
 89    }
 90
 91    pub fn encrypt_pk_scratch_space(_module: &Module<FFT64>, _rank: usize, _pk_size: usize) -> usize {
 92        unimplemented!()
 93    }
 94}
 95
 96impl<DataSelf> GGLWECiphertext<DataSelf, FFT64>
 97where
 98    MatZnxDft<DataSelf, FFT64>: MatZnxDftToMut<FFT64> + ZnxInfos,
 99{
100    pub fn encrypt_sk<DataPt, DataSk>(
101        &mut self,
102        module: &Module<FFT64>,
103        pt: &ScalarZnx<DataPt>,
104        sk_dft: &SecretKeyFourier<DataSk, FFT64>,
105        source_xa: &mut Source,
106        source_xe: &mut Source,
107        sigma: f64,
108        scratch: &mut Scratch,
109    ) where
110        ScalarZnx<DataPt>: ScalarZnxToRef,
111        ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
112    {
113        #[cfg(debug_assertions)]
114        {
115            assert_eq!(self.rank_in(), pt.cols());
116            assert_eq!(self.rank_out(), sk_dft.rank());
117            assert_eq!(self.n(), module.n());
118            assert_eq!(sk_dft.n(), module.n());
119            assert_eq!(pt.n(), module.n());
120        }
121
122        let rows: usize = self.rows();
123        let size: usize = self.size();
124        let basek: usize = self.basek();
125        let k: usize = self.k();
126
127        let cols_in: usize = self.rank_in();
128        let cols_out: usize = self.rank_out() + 1;
129
130        let (tmp_znx_pt, scrach_1) = scratch.tmp_vec_znx(module, 1, size);
131        let (tmp_znx_ct, scrach_2) = scrach_1.tmp_vec_znx(module, cols_out, size);
132        let (tmp_znx_dft_ct, scratch_3) = scrach_2.tmp_vec_znx_dft(module, cols_out, size);
133
134        let mut vec_znx_pt: GLWEPlaintext<&mut [u8]> = GLWEPlaintext {
135            data: tmp_znx_pt,
136            basek,
137            k,
138        };
139
140        let mut vec_znx_ct: GLWECiphertext<&mut [u8]> = GLWECiphertext {
141            data: tmp_znx_ct,
142            basek,
143            k,
144        };
145
146        let mut vec_znx_ct_dft: GLWECiphertextFourier<&mut [u8], FFT64> = GLWECiphertextFourier {
147            data: tmp_znx_dft_ct,
148            basek,
149            k,
150        };
151
152        // For each input column (i.e. rank) produces a GGLWE ciphertext of rank_out+1 columns
153        //
154        // Example for ksk rank 2 to rank 3:
155        //
156        // (-(a0*s0 + a1*s1 + a2*s2) + s0', a0, a1, a2)
157        // (-(b0*s0 + b1*s1 + b2*s2) + s0', b0, b1, b2)
158        //
159        // Example ksk rank 2 to rank 1
160        //
161        // (-(a*s) + s0, a)
162        // (-(b*s) + s1, b)
163        (0..cols_in).for_each(|col_i| {
164            (0..rows).for_each(|row_i| {
165                // Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
166                module.vec_znx_add_scalar_inplace(&mut vec_znx_pt, 0, row_i, pt, col_i); // Selects the i-th
167                module.vec_znx_normalize_inplace(basek, &mut vec_znx_pt, 0, scratch_3);
168
169                // rlwe encrypt of vec_znx_pt into vec_znx_ct
170                vec_znx_ct.encrypt_sk(
171                    module,
172                    &vec_znx_pt,
173                    sk_dft,
174                    source_xa,
175                    source_xe,
176                    sigma,
177                    scratch_3,
178                );
179
180                vec_znx_pt.data.zero(); // zeroes for next iteration
181
182                // Switch vec_znx_ct into DFT domain
183                vec_znx_ct.dft(module, &mut vec_znx_ct_dft);
184
185                // Stores vec_znx_dft_ct into thw i-th row of the MatZnxDft
186                module.vmp_prepare_row(self, row_i, col_i, &vec_znx_ct_dft);
187            });
188        });
189    }
190}
191
192impl<C> GetRow<FFT64> for GGLWECiphertext<C, FFT64>
193where
194    MatZnxDft<C, FFT64>: MatZnxDftToRef<FFT64>,
195{
196    fn get_row<R>(&self, module: &Module<FFT64>, row_i: usize, col_j: usize, res: &mut GLWECiphertextFourier<R, FFT64>)
197    where
198        VecZnxDft<R, FFT64>: VecZnxDftToMut<FFT64>,
199    {
200        module.vmp_extract_row(res, self, row_i, col_j);
201    }
202}
203
204impl<C> SetRow<FFT64> for GGLWECiphertext<C, FFT64>
205where
206    MatZnxDft<C, FFT64>: MatZnxDftToMut<FFT64>,
207{
208    fn set_row<R>(&mut self, module: &Module<FFT64>, row_i: usize, col_j: usize, a: &GLWECiphertextFourier<R, FFT64>)
209    where
210        VecZnxDft<R, FFT64>: VecZnxDftToRef<FFT64>,
211    {
212        module.vmp_prepare_row(self, row_i, col_j, a);
213    }
214}
215
216impl VecGLWEProductScratchSpace for GGLWECiphertext<Vec<u8>, FFT64> {
217    fn prod_with_glwe_scratch_space(
218        module: &Module<FFT64>,
219        res_size: usize,
220        a_size: usize,
221        grlwe_size: usize,
222        rank_in: usize,
223        rank_out: usize,
224    ) -> usize {
225        module.bytes_of_vec_znx_dft(rank_out + 1, grlwe_size)
226            + (module.vec_znx_big_normalize_tmp_bytes()
227                | (module.vmp_apply_tmp_bytes(res_size, a_size, a_size, rank_in, rank_out + 1, grlwe_size)
228                    + module.bytes_of_vec_znx_dft(rank_in, a_size)))
229    }
230}
231
232impl<C> VecGLWEProduct for GGLWECiphertext<C, FFT64>
233where
234    MatZnxDft<C, FFT64>: MatZnxDftToRef<FFT64> + ZnxInfos,
235{
236    fn prod_with_glwe<R, A>(
237        &self,
238        module: &Module<FFT64>,
239        res: &mut GLWECiphertext<R>,
240        a: &GLWECiphertext<A>,
241        scratch: &mut Scratch,
242    ) where
243        MatZnxDft<C, FFT64>: MatZnxDftToRef<FFT64>,
244        VecZnx<R>: VecZnxToMut,
245        VecZnx<A>: VecZnxToRef,
246    {
247        let basek: usize = self.basek();
248
249        #[cfg(debug_assertions)]
250        {
251            assert_eq!(a.rank(), self.rank_in());
252            assert_eq!(res.rank(), self.rank_out());
253            assert_eq!(res.basek(), basek);
254            assert_eq!(a.basek(), basek);
255            assert_eq!(self.n(), module.n());
256            assert_eq!(res.n(), module.n());
257            assert_eq!(a.n(), module.n());
258        }
259
260        let cols_in: usize = self.rank_in();
261        let cols_out: usize = self.rank_out() + 1;
262
263        let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, self.size()); // Todo optimise
264
265        {
266            let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, a.size());
267            (0..cols_in).for_each(|col_i| {
268                module.vec_znx_dft(&mut ai_dft, col_i, a, col_i + 1);
269            });
270            module.vmp_apply(&mut res_dft, &ai_dft, self, scratch2);
271        }
272
273        let mut res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
274
275        module.vec_znx_big_add_small_inplace(&mut res_big, 0, a, 0);
276
277        (0..cols_out).for_each(|i| {
278            module.vec_znx_big_normalize(basek, res, i, &res_big, i, scratch1);
279        });
280    }
281}