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    elem::Infos,
 11    gglwe_ciphertext::GGLWECiphertext,
 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    vec_glwe_product::{VecGLWEProduct, VecGLWEProductScratchSpace},
 19};
 20
 21pub struct GLWECiphertext<C> {
 22    pub data: VecZnx<C>,
 23    pub basek: usize,
 24    pub k: usize,
 25}
 26
 27impl GLWECiphertext<Vec<u8>> {
 28    pub fn new<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
 29        Self {
 30            data: module.new_vec_znx(rank + 1, derive_size(basek, k)),
 31            basek,
 32            k,
 33        }
 34    }
 35}
 36
 37impl<T> Infos for GLWECiphertext<T> {
 38    type Inner = VecZnx<T>;
 39
 40    fn inner(&self) -> &Self::Inner {
 41        &self.data
 42    }
 43
 44    fn basek(&self) -> usize {
 45        self.basek
 46    }
 47
 48    fn k(&self) -> usize {
 49        self.k
 50    }
 51}
 52
 53impl<T> GLWECiphertext<T> {
 54    pub fn rank(&self) -> usize {
 55        self.cols() - 1
 56    }
 57}
 58
 59impl<C> VecZnxToMut for GLWECiphertext<C>
 60where
 61    VecZnx<C>: VecZnxToMut,
 62{
 63    fn to_mut(&mut self) -> VecZnx<&mut [u8]> {
 64        self.data.to_mut()
 65    }
 66}
 67
 68impl<C> VecZnxToRef for GLWECiphertext<C>
 69where
 70    VecZnx<C>: VecZnxToRef,
 71{
 72    fn to_ref(&self) -> VecZnx<&[u8]> {
 73        self.data.to_ref()
 74    }
 75}
 76
 77impl<C> GLWECiphertext<C>
 78where
 79    VecZnx<C>: VecZnxToRef,
 80{
 81    #[allow(dead_code)]
 82    pub(crate) fn dft<R>(&self, module: &Module<FFT64>, res: &mut GLWECiphertextFourier<R, FFT64>)
 83    where
 84        VecZnxDft<R, FFT64>: VecZnxDftToMut<FFT64> + ZnxInfos,
 85    {
 86        #[cfg(debug_assertions)]
 87        {
 88            assert_eq!(self.rank(), res.rank());
 89            assert_eq!(self.basek(), res.basek())
 90        }
 91
 92        (0..self.rank() + 1).for_each(|i| {
 93            module.vec_znx_dft(res, i, self, i);
 94        })
 95    }
 96}
 97
 98impl GLWECiphertext<Vec<u8>> {
 99    pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, _rank: usize, ct_size: usize) -> usize {
100        module.vec_znx_big_normalize_tmp_bytes()
101            + module.bytes_of_vec_znx_dft(1, ct_size)
102            + module.bytes_of_vec_znx_big(1, ct_size)
103    }
104    pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, _rank: usize, pk_size: usize) -> usize {
105        ((module.bytes_of_vec_znx_dft(1, pk_size) + module.bytes_of_vec_znx_big(1, pk_size)) | module.bytes_of_scalar_znx(1))
106            + module.bytes_of_scalar_znx_dft(1)
107            + module.vec_znx_big_normalize_tmp_bytes()
108    }
109
110    pub fn decrypt_scratch_space(module: &Module<FFT64>, ct_size: usize) -> usize {
111        (module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, ct_size))
112            + module.bytes_of_vec_znx_big(1, ct_size)
113    }
114
115    pub fn keyswitch_scratch_space(module: &Module<FFT64>, res_size: usize, lhs: usize, rhs: usize) -> usize {
116        <GGLWECiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_scratch_space(module, res_size, lhs, rhs)
117    }
118
119    pub fn keyswitch_inplace_scratch_space(module: &Module<FFT64>, res_size: usize, rhs: usize) -> usize {
120        <GGLWECiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_inplace_scratch_space(
121            module, res_size, rhs,
122        )
123    }
124
125    pub fn external_product_scratch_space(module: &Module<FFT64>, res_size: usize, lhs: usize, rhs: usize) -> usize {
126        <GGSWCiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_scratch_space(module, res_size, lhs, rhs)
127    }
128
129    pub fn external_product_inplace_scratch_space(module: &Module<FFT64>, res_size: usize, rhs: usize) -> usize {
130        <GGSWCiphertext<Vec<u8>, FFT64> as VecGLWEProductScratchSpace>::prod_with_glwe_inplace_scratch_space(
131            module, res_size, rhs,
132        )
133    }
134}
135
136impl<DataSelf> GLWECiphertext<DataSelf>
137where
138    VecZnx<DataSelf>: VecZnxToMut + VecZnxToRef,
139{
140    pub fn encrypt_sk<DataPt, DataSk>(
141        &mut self,
142        module: &Module<FFT64>,
143        pt: &GLWEPlaintext<DataPt>,
144        sk_dft: &SecretKeyFourier<DataSk, FFT64>,
145        source_xa: &mut Source,
146        source_xe: &mut Source,
147        sigma: f64,
148        bound: f64,
149        scratch: &mut Scratch,
150    ) where
151        VecZnx<DataPt>: VecZnxToRef,
152        ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
153    {
154        self.encrypt_sk_private(
155            module,
156            Some((pt, 0)),
157            sk_dft,
158            source_xa,
159            source_xe,
160            sigma,
161            bound,
162            scratch,
163        );
164    }
165
166    pub fn encrypt_zero_sk<DataSk>(
167        &mut self,
168        module: &Module<FFT64>,
169        sk_dft: &SecretKeyFourier<DataSk, FFT64>,
170        source_xa: &mut Source,
171        source_xe: &mut Source,
172        sigma: f64,
173        bound: f64,
174        scratch: &mut Scratch,
175    ) where
176        ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
177    {
178        self.encrypt_sk_private(
179            module, None, sk_dft, source_xa, source_xe, sigma, bound, scratch,
180        );
181    }
182
183    pub fn encrypt_pk<DataPt, DataPk>(
184        &mut self,
185        module: &Module<FFT64>,
186        pt: &GLWEPlaintext<DataPt>,
187        pk: &GLWEPublicKey<DataPk, FFT64>,
188        source_xu: &mut Source,
189        source_xe: &mut Source,
190        sigma: f64,
191        bound: f64,
192        scratch: &mut Scratch,
193    ) where
194        VecZnx<DataPt>: VecZnxToRef,
195        VecZnxDft<DataPk, FFT64>: VecZnxDftToRef<FFT64>,
196    {
197        self.encrypt_pk_private(
198            module,
199            Some((pt, 0)),
200            pk,
201            source_xu,
202            source_xe,
203            sigma,
204            bound,
205            scratch,
206        );
207    }
208
209    pub fn encrypt_zero_pk<DataPk>(
210        &mut self,
211        module: &Module<FFT64>,
212        pk: &GLWEPublicKey<DataPk, FFT64>,
213        source_xu: &mut Source,
214        source_xe: &mut Source,
215        sigma: f64,
216        bound: f64,
217        scratch: &mut Scratch,
218    ) where
219        VecZnxDft<DataPk, FFT64>: VecZnxDftToRef<FFT64>,
220    {
221        self.encrypt_pk_private(
222            module, None, pk, source_xu, source_xe, sigma, bound, scratch,
223        );
224    }
225
226    pub fn keyswitch<DataLhs, DataRhs>(
227        &mut self,
228        module: &Module<FFT64>,
229        lhs: &GLWECiphertext<DataLhs>,
230        rhs: &GLWESwitchingKey<DataRhs, FFT64>,
231        scratch: &mut Scratch,
232    ) where
233        VecZnx<DataLhs>: VecZnxToRef,
234        MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
235    {
236        rhs.0.prod_with_glwe(module, self, lhs, scratch);
237    }
238
239    pub fn keyswitch_inplace<DataRhs>(
240        &mut self,
241        module: &Module<FFT64>,
242        rhs: &GLWESwitchingKey<DataRhs, FFT64>,
243        scratch: &mut Scratch,
244    ) where
245        MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
246    {
247        rhs.0.prod_with_glwe_inplace(module, self, scratch);
248    }
249
250    pub fn external_product<DataLhs, DataRhs>(
251        &mut self,
252        module: &Module<FFT64>,
253        lhs: &GLWECiphertext<DataLhs>,
254        rhs: &GGSWCiphertext<DataRhs, FFT64>,
255        scratch: &mut Scratch,
256    ) where
257        VecZnx<DataLhs>: VecZnxToRef,
258        MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
259    {
260        rhs.prod_with_glwe(module, self, lhs, scratch);
261    }
262
263    pub fn external_product_inplace<DataRhs>(
264        &mut self,
265        module: &Module<FFT64>,
266        rhs: &GGSWCiphertext<DataRhs, FFT64>,
267        scratch: &mut Scratch,
268    ) where
269        MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
270    {
271        rhs.prod_with_glwe_inplace(module, self, scratch);
272    }
273
274    pub(crate) fn encrypt_sk_private<DataPt, DataSk>(
275        &mut self,
276        module: &Module<FFT64>,
277        pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
278        sk_dft: &SecretKeyFourier<DataSk, FFT64>,
279        source_xa: &mut Source,
280        source_xe: &mut Source,
281        sigma: f64,
282        bound: f64,
283        scratch: &mut Scratch,
284    ) where
285        VecZnx<DataPt>: VecZnxToRef,
286        ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
287    {
288        #[cfg(debug_assertions)]
289        {
290            assert_eq!(self.rank(), sk_dft.rank());
291            assert_eq!(sk_dft.n(), module.n());
292            assert_eq!(self.n(), module.n());
293            if let Some((pt, col)) = pt {
294                assert_eq!(pt.n(), module.n());
295                assert!(col < self.rank() + 1);
296            }
297        }
298
299        let log_base2k: usize = self.basek();
300        let log_k: usize = self.k();
301        let size: usize = self.size();
302        let cols: usize = self.rank() + 1;
303
304        let (mut c0_big, scratch_1) = scratch.tmp_vec_znx(module, 1, size);
305        c0_big.zero();
306
307        {
308            // c[i] = uniform
309            // c[0] -= c[i] * s[i],
310            (1..cols).for_each(|i| {
311                let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size);
312
313                // c[i] = uniform
314                self.data.fill_uniform(log_base2k, i, size, source_xa);
315
316                // c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i])))
317                module.vec_znx_dft(&mut ci_dft, 0, self, i);
318                module.svp_apply_inplace(&mut ci_dft, 0, sk_dft, i - 1);
319                let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft);
320
321                // use c[0] as buffer, which is overwritten later by the normalization step
322                module.vec_znx_big_normalize(log_base2k, self, 0, &ci_big, 0, scratch_2);
323
324                // c0_tmp = -c[i] * s[i] (use c[0] as buffer)
325                module.vec_znx_sub_ab_inplace(&mut c0_big, 0, self, 0);
326
327                // c[i] += m if col = i
328                if let Some((pt, col)) = pt {
329                    if i == col {
330                        module.vec_znx_add_inplace(self, i, pt, 0);
331                        module.vec_znx_normalize_inplace(log_base2k, self, i, scratch_2);
332                    }
333                }
334            });
335        }
336
337        // c[0] += e
338        c0_big.add_normal(log_base2k, 0, log_k, source_xe, sigma, bound);
339
340        // c[0] += m if col = 0
341        if let Some((pt, col)) = pt {
342            if col == 0 {
343                module.vec_znx_add_inplace(&mut c0_big, 0, pt, 0);
344            }
345        }
346
347        // c[0] = norm(c[0])
348        module.vec_znx_normalize(log_base2k, self, 0, &c0_big, 0, scratch_1);
349    }
350
351    pub(crate) fn encrypt_pk_private<DataPt, DataPk>(
352        &mut self,
353        module: &Module<FFT64>,
354        pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
355        pk: &GLWEPublicKey<DataPk, FFT64>,
356        source_xu: &mut Source,
357        source_xe: &mut Source,
358        sigma: f64,
359        bound: 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, bound);
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}