arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/glwe_ciphertext.rs
core/src/glwe_ciphertext.rsRaw
  1use base2k::{
  2    AddNormal, Backend, FFT64, FillUniform, MatZnxDft, MatZnxDftOps, MatZnxDftScratch, MatZnxDftToRef, Module, ScalarZnxAlloc,
  3    ScalarZnxDft, ScalarZnxDftAlloc, ScalarZnxDftOps, ScalarZnxDftToRef, Scratch, VecZnx, VecZnxAlloc, VecZnxBig, VecZnxBigAlloc,
  4    VecZnxBigOps, VecZnxBigScratch, VecZnxDft, VecZnxDftAlloc, VecZnxDftOps, VecZnxDftToMut, VecZnxDftToRef, VecZnxOps,
  5    VecZnxToMut, VecZnxToRef, ZnxInfos, ZnxZero,
  6};
  7use sampling::source::Source;
  8
  9use crate::{
 10    SIX_SIGMA,
 11    elem::Infos,
 12    ggsw_ciphertext::GGSWCiphertext,
 13    glwe_ciphertext_fourier::GLWECiphertextFourier,
 14    glwe_plaintext::GLWEPlaintext,
 15    keys::{GLWEPublicKey, SecretDistribution, SecretKeyFourier},
 16    keyswitch_key::GLWESwitchingKey,
 17    utils::derive_size,
 18};
 19
 20pub struct GLWECiphertext<C> {
 21    pub data: VecZnx<C>,
 22    pub basek: usize,
 23    pub k: usize,
 24}
 25
 26impl GLWECiphertext<Vec<u8>> {
 27    pub fn new<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
 28        Self {
 29            data: module.new_vec_znx(rank + 1, derive_size(basek, k)),
 30            basek,
 31            k,
 32        }
 33    }
 34}
 35
 36impl<T> Infos for GLWECiphertext<T> {
 37    type Inner = VecZnx<T>;
 38
 39    fn inner(&self) -> &Self::Inner {
 40        &self.data
 41    }
 42
 43    fn basek(&self) -> usize {
 44        self.basek
 45    }
 46
 47    fn k(&self) -> usize {
 48        self.k
 49    }
 50}
 51
 52impl<T> GLWECiphertext<T> {
 53    pub fn rank(&self) -> usize {
 54        self.cols() - 1
 55    }
 56}
 57
 58impl<C> VecZnxToMut for GLWECiphertext<C>
 59where
 60    VecZnx<C>: VecZnxToMut,
 61{
 62    fn to_mut(&mut self) -> VecZnx<&mut [u8]> {
 63        self.data.to_mut()
 64    }
 65}
 66
 67impl<C> VecZnxToRef for GLWECiphertext<C>
 68where
 69    VecZnx<C>: VecZnxToRef,
 70{
 71    fn to_ref(&self) -> VecZnx<&[u8]> {
 72        self.data.to_ref()
 73    }
 74}
 75
 76impl<C> GLWECiphertext<C>
 77where
 78    VecZnx<C>: VecZnxToRef,
 79{
 80    #[allow(dead_code)]
 81    pub(crate) fn dft<R>(&self, module: &Module<FFT64>, res: &mut GLWECiphertextFourier<R, FFT64>)
 82    where
 83        VecZnxDft<R, FFT64>: VecZnxDftToMut<FFT64> + ZnxInfos,
 84    {
 85        #[cfg(debug_assertions)]
 86        {
 87            assert_eq!(self.rank(), res.rank());
 88            assert_eq!(self.basek(), res.basek())
 89        }
 90
 91        (0..self.rank() + 1).for_each(|i| {
 92            module.vec_znx_dft(res, i, self, i);
 93        })
 94    }
 95}
 96
 97impl GLWECiphertext<Vec<u8>> {
 98    pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, ct_size: usize) -> usize {
 99        module.vec_znx_big_normalize_tmp_bytes()
100            + module.bytes_of_vec_znx_dft(1, ct_size)
101            + module.bytes_of_vec_znx_big(1, ct_size)
102    }
103    pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, pk_size: usize) -> usize {
104        ((module.bytes_of_vec_znx_dft(1, pk_size) + module.bytes_of_vec_znx_big(1, pk_size)) | module.bytes_of_scalar_znx(1))
105            + module.bytes_of_scalar_znx_dft(1)
106            + module.vec_znx_big_normalize_tmp_bytes()
107    }
108
109    pub fn decrypt_scratch_space(module: &Module<FFT64>, ct_size: usize) -> usize {
110        (module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, ct_size))
111            + module.bytes_of_vec_znx_big(1, ct_size)
112    }
113
114    pub fn keyswitch_scratch_space(
115        module: &Module<FFT64>,
116        out_size: usize,
117        out_rank: usize,
118        in_size: usize,
119        in_rank: usize,
120        ksk_size: usize,
121    ) -> usize {
122        module.bytes_of_vec_znx_dft(out_rank + 1, ksk_size)
123            + (module.vec_znx_big_normalize_tmp_bytes()
124                | (module.vmp_apply_tmp_bytes(
125                    out_size,
126                    in_size,
127                    in_size,
128                    in_rank + 1,
129                    out_rank + 1,
130                    ksk_size,
131                ) + module.bytes_of_vec_znx_dft(in_size, in_size)))
132    }
133
134    pub fn keyswitch_inplace_scratch_space(module: &Module<FFT64>, out_size: usize, out_rank: usize, ksk_size: usize) -> usize {
135        GLWECiphertext::keyswitch_scratch_space(module, out_size, out_rank, out_size, out_rank, ksk_size)
136    }
137
138    pub fn external_product_scratch_space(
139        module: &Module<FFT64>,
140        out_size: usize,
141        in_size: usize,
142        ggsw_size: usize,
143        rank: usize,
144    ) -> usize {
145        module.bytes_of_vec_znx_dft(rank + 1, ggsw_size)
146            + ((module.bytes_of_vec_znx_dft(rank + 1, in_size)
147                + module.vmp_apply_tmp_bytes(
148                    out_size,
149                    in_size,
150                    in_size,  // rows
151                    rank + 1, // cols in
152                    rank + 1, // cols out
153                    ggsw_size,
154                ))
155                | module.vec_znx_big_normalize_tmp_bytes())
156    }
157
158    pub fn external_product_inplace_scratch_space(module: &Module<FFT64>, res_size: usize, rhs: usize, rank: usize) -> usize {
159        GLWECiphertext::external_product_scratch_space(module, res_size, res_size, rhs, rank)
160    }
161}
162
163impl<DataSelf> GLWECiphertext<DataSelf>
164where
165    VecZnx<DataSelf>: VecZnxToMut + VecZnxToRef,
166{
167    pub fn encrypt_sk<DataPt, DataSk>(
168        &mut self,
169        module: &Module<FFT64>,
170        pt: &GLWEPlaintext<DataPt>,
171        sk_dft: &SecretKeyFourier<DataSk, FFT64>,
172        source_xa: &mut Source,
173        source_xe: &mut Source,
174        sigma: f64,
175        scratch: &mut Scratch,
176    ) where
177        VecZnx<DataPt>: VecZnxToRef,
178        ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
179    {
180        self.encrypt_sk_private(
181            module,
182            Some((pt, 0)),
183            sk_dft,
184            source_xa,
185            source_xe,
186            sigma,
187            scratch,
188        );
189    }
190
191    pub fn encrypt_zero_sk<DataSk>(
192        &mut self,
193        module: &Module<FFT64>,
194        sk_dft: &SecretKeyFourier<DataSk, FFT64>,
195        source_xa: &mut Source,
196        source_xe: &mut Source,
197        sigma: f64,
198        scratch: &mut Scratch,
199    ) where
200        ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
201    {
202        self.encrypt_sk_private(module, None, sk_dft, source_xa, source_xe, sigma, scratch);
203    }
204
205    pub fn encrypt_pk<DataPt, DataPk>(
206        &mut self,
207        module: &Module<FFT64>,
208        pt: &GLWEPlaintext<DataPt>,
209        pk: &GLWEPublicKey<DataPk, FFT64>,
210        source_xu: &mut Source,
211        source_xe: &mut Source,
212        sigma: f64,
213        scratch: &mut Scratch,
214    ) where
215        VecZnx<DataPt>: VecZnxToRef,
216        VecZnxDft<DataPk, FFT64>: VecZnxDftToRef<FFT64>,
217    {
218        self.encrypt_pk_private(
219            module,
220            Some((pt, 0)),
221            pk,
222            source_xu,
223            source_xe,
224            sigma,
225            scratch,
226        );
227    }
228
229    pub fn encrypt_zero_pk<DataPk>(
230        &mut self,
231        module: &Module<FFT64>,
232        pk: &GLWEPublicKey<DataPk, FFT64>,
233        source_xu: &mut Source,
234        source_xe: &mut Source,
235        sigma: f64,
236        scratch: &mut Scratch,
237    ) where
238        VecZnxDft<DataPk, FFT64>: VecZnxDftToRef<FFT64>,
239    {
240        self.encrypt_pk_private(module, None, pk, source_xu, source_xe, sigma, scratch);
241    }
242
243    pub fn keyswitch<DataLhs, DataRhs>(
244        &mut self,
245        module: &Module<FFT64>,
246        lhs: &GLWECiphertext<DataLhs>,
247        rhs: &GLWESwitchingKey<DataRhs, FFT64>,
248        scratch: &mut Scratch,
249    ) where
250        VecZnx<DataLhs>: VecZnxToRef,
251        MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
252    {
253        let basek: usize = self.basek();
254
255        #[cfg(debug_assertions)]
256        {
257            assert_eq!(lhs.rank(), rhs.rank_in());
258            assert_eq!(self.rank(), rhs.rank_out());
259            assert_eq!(self.basek(), basek);
260            assert_eq!(lhs.basek(), basek);
261            assert_eq!(rhs.n(), module.n());
262            assert_eq!(self.n(), module.n());
263            assert_eq!(lhs.n(), module.n());
264            assert!(
265                scratch.available()
266                    >= GLWECiphertext::keyswitch_scratch_space(
267                        module,
268                        self.size(),
269                        self.rank(),
270                        lhs.size(),
271                        lhs.rank(),
272                        rhs.size(),
273                    )
274            );
275        }
276
277        let cols_in: usize = rhs.rank_in();
278        let cols_out: usize = rhs.rank_out() + 1;
279
280        let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, rhs.size()); // Todo optimise
281
282        {
283            let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, lhs.size());
284            (0..cols_in).for_each(|col_i| {
285                module.vec_znx_dft(&mut ai_dft, col_i, lhs, col_i + 1);
286            });
287            module.vmp_apply(&mut res_dft, &ai_dft, rhs, scratch2);
288        }
289
290        let mut res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
291
292        module.vec_znx_big_add_small_inplace(&mut res_big, 0, lhs, 0);
293
294        (0..cols_out).for_each(|i| {
295            module.vec_znx_big_normalize(basek, self, i, &res_big, i, scratch1);
296        });
297    }
298
299    pub fn keyswitch_inplace<DataRhs>(
300        &mut self,
301        module: &Module<FFT64>,
302        rhs: &GLWESwitchingKey<DataRhs, FFT64>,
303        scratch: &mut Scratch,
304    ) where
305        MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
306    {
307        unsafe {
308            let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
309            self.keyswitch(&module, &*self_ptr, rhs, scratch);
310        }
311    }
312
313    pub fn external_product<DataLhs, DataRhs>(
314        &mut self,
315        module: &Module<FFT64>,
316        lhs: &GLWECiphertext<DataLhs>,
317        rhs: &GGSWCiphertext<DataRhs, FFT64>,
318        scratch: &mut Scratch,
319    ) where
320        VecZnx<DataLhs>: VecZnxToRef,
321        MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
322    {
323        let basek: usize = self.basek();
324
325        #[cfg(debug_assertions)]
326        {
327            assert_eq!(rhs.rank(), lhs.rank());
328            assert_eq!(rhs.rank(), self.rank());
329            assert_eq!(self.basek(), basek);
330            assert_eq!(lhs.basek(), basek);
331            assert_eq!(rhs.n(), module.n());
332            assert_eq!(self.n(), module.n());
333            assert_eq!(lhs.n(), module.n());
334        }
335
336        let cols: usize = rhs.rank() + 1;
337
338        let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols, rhs.size()); // Todo optimise
339
340        {
341            let (mut a_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, lhs.size());
342            (0..cols).for_each(|col_i| {
343                module.vec_znx_dft(&mut a_dft, col_i, lhs, col_i);
344            });
345            module.vmp_apply(&mut res_dft, &a_dft, rhs, scratch2);
346        }
347
348        let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
349
350        (0..cols).for_each(|i| {
351            module.vec_znx_big_normalize(basek, self, i, &res_big, i, scratch1);
352        });
353    }
354
355    pub fn external_product_inplace<DataRhs>(
356        &mut self,
357        module: &Module<FFT64>,
358        rhs: &GGSWCiphertext<DataRhs, FFT64>,
359        scratch: &mut Scratch,
360    ) where
361        MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
362    {
363        unsafe {
364            let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
365            self.external_product(&module, &*self_ptr, rhs, scratch);
366        }
367    }
368
369    pub(crate) fn encrypt_sk_private<DataPt, DataSk>(
370        &mut self,
371        module: &Module<FFT64>,
372        pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
373        sk_dft: &SecretKeyFourier<DataSk, FFT64>,
374        source_xa: &mut Source,
375        source_xe: &mut Source,
376        sigma: f64,
377        scratch: &mut Scratch,
378    ) where
379        VecZnx<DataPt>: VecZnxToRef,
380        ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
381    {
382        #[cfg(debug_assertions)]
383        {
384            assert_eq!(self.rank(), sk_dft.rank());
385            assert_eq!(sk_dft.n(), module.n());
386            assert_eq!(self.n(), module.n());
387            if let Some((pt, col)) = pt {
388                assert_eq!(pt.n(), module.n());
389                assert!(col < self.rank() + 1);
390            }
391        }
392
393        let log_base2k: usize = self.basek();
394        let log_k: usize = self.k();
395        let size: usize = self.size();
396        let cols: usize = self.rank() + 1;
397
398        let (mut c0_big, scratch_1) = scratch.tmp_vec_znx(module, 1, size);
399        c0_big.zero();
400
401        {
402            // c[i] = uniform
403            // c[0] -= c[i] * s[i],
404            (1..cols).for_each(|i| {
405                let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size);
406
407                // c[i] = uniform
408                self.data.fill_uniform(log_base2k, i, size, source_xa);
409
410                // c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i])))
411                module.vec_znx_dft(&mut ci_dft, 0, self, i);
412                module.svp_apply_inplace(&mut ci_dft, 0, sk_dft, i - 1);
413                let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft);
414
415                // use c[0] as buffer, which is overwritten later by the normalization step
416                module.vec_znx_big_normalize(log_base2k, self, 0, &ci_big, 0, scratch_2);
417
418                // c0_tmp = -c[i] * s[i] (use c[0] as buffer)
419                module.vec_znx_sub_ab_inplace(&mut c0_big, 0, self, 0);
420
421                // c[i] += m if col = i
422                if let Some((pt, col)) = pt {
423                    if i == col {
424                        module.vec_znx_add_inplace(self, i, pt, 0);
425                        module.vec_znx_normalize_inplace(log_base2k, self, i, scratch_2);
426                    }
427                }
428            });
429        }
430
431        // c[0] += e
432        c0_big.add_normal(log_base2k, 0, log_k, source_xe, sigma, sigma * SIX_SIGMA);
433
434        // c[0] += m if col = 0
435        if let Some((pt, col)) = pt {
436            if col == 0 {
437                module.vec_znx_add_inplace(&mut c0_big, 0, pt, 0);
438            }
439        }
440
441        // c[0] = norm(c[0])
442        module.vec_znx_normalize(log_base2k, self, 0, &c0_big, 0, scratch_1);
443    }
444
445    pub(crate) fn encrypt_pk_private<DataPt, DataPk>(
446        &mut self,
447        module: &Module<FFT64>,
448        pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
449        pk: &GLWEPublicKey<DataPk, FFT64>,
450        source_xu: &mut Source,
451        source_xe: &mut Source,
452        sigma: f64,
453        scratch: &mut Scratch,
454    ) where
455        VecZnx<DataPt>: VecZnxToRef,
456        VecZnxDft<DataPk, FFT64>: VecZnxDftToRef<FFT64>,
457    {
458        #[cfg(debug_assertions)]
459        {
460            assert_eq!(self.basek(), pk.basek());
461            assert_eq!(self.n(), module.n());
462            assert_eq!(pk.n(), module.n());
463            assert_eq!(self.rank(), pk.rank());
464            if let Some((pt, _)) = pt {
465                assert_eq!(pt.basek(), pk.basek());
466                assert_eq!(pt.n(), module.n());
467            }
468        }
469
470        let log_base2k: usize = pk.basek();
471        let size_pk: usize = pk.size();
472        let cols: usize = self.rank() + 1;
473
474        // Generates u according to the underlying secret distribution.
475        let (mut u_dft, scratch_1) = scratch.tmp_scalar_znx_dft(module, 1);
476
477        {
478            let (mut u, _) = scratch_1.tmp_scalar_znx(module, 1);
479            match pk.dist {
480                SecretDistribution::NONE => panic!(
481                    "invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through \
482                     Self::generate"
483                ),
484                SecretDistribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu),
485                SecretDistribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu),
486                SecretDistribution::ZERO => {}
487            }
488
489            module.svp_prepare(&mut u_dft, 0, &u, 0);
490        }
491
492        // ct[i] = pk[i] * u + ei (+ m if col = i)
493        (0..cols).for_each(|i| {
494            let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size_pk);
495            // ci_dft = DFT(u) * DFT(pk[i])
496            module.svp_apply(&mut ci_dft, 0, &u_dft, 0, pk, i);
497
498            // ci_big = u * p[i]
499            let mut ci_big = module.vec_znx_idft_consume(ci_dft);
500
501            // ci_big = u * pk[i] + e
502            ci_big.add_normal(log_base2k, 0, pk.k(), source_xe, sigma, sigma * SIX_SIGMA);
503
504            // ci_big = u * pk[i] + e + m (if col = i)
505            if let Some((pt, col)) = pt {
506                if col == i {
507                    module.vec_znx_big_add_small_inplace(&mut ci_big, 0, pt, 0);
508                }
509            }
510
511            // ct[i] = norm(ci_big)
512            module.vec_znx_big_normalize(log_base2k, self, i, &ci_big, 0, scratch_2);
513        });
514    }
515}
516
517impl<DataSelf> GLWECiphertext<DataSelf>
518where
519    VecZnx<DataSelf>: VecZnxToRef,
520{
521    pub fn decrypt<DataPt, DataSk>(
522        &self,
523        module: &Module<FFT64>,
524        pt: &mut GLWEPlaintext<DataPt>,
525        sk_dft: &SecretKeyFourier<DataSk, FFT64>,
526        scratch: &mut Scratch,
527    ) where
528        VecZnx<DataPt>: VecZnxToMut,
529        ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
530    {
531        #[cfg(debug_assertions)]
532        {
533            assert_eq!(self.rank(), sk_dft.rank());
534            assert_eq!(self.n(), module.n());
535            assert_eq!(pt.n(), module.n());
536            assert_eq!(sk_dft.n(), module.n());
537        }
538
539        let cols: usize = self.rank() + 1;
540
541        let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, self.size()); // TODO optimize size when pt << ct
542        c0_big.zero();
543
544        {
545            (1..cols).for_each(|i| {
546                // ci_dft = DFT(a[i]) * DFT(s[i])
547                let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct
548                module.vec_znx_dft(&mut ci_dft, 0, self, i);
549                module.svp_apply_inplace(&mut ci_dft, 0, sk_dft, i - 1);
550                let ci_big = module.vec_znx_idft_consume(ci_dft);
551
552                // c0_big += a[i] * s[i]
553                module.vec_znx_big_add_inplace(&mut c0_big, 0, &ci_big, 0);
554            });
555        }
556
557        // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
558        module.vec_znx_big_add_small_inplace(&mut c0_big, 0, self, 0);
559
560        // pt = norm(BIG(m + e))
561        module.vec_znx_big_normalize(self.basek(), pt, 0, &mut c0_big, 0, scratch_1);
562
563        pt.basek = self.basek();
564        pt.k = pt.k().min(self.k());
565    }
566}