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