arnaucube
arnaucube/poulpyPublic Self-hosted repository
poulpy/core/src/encryption/glwe_ct.rs
core/src/encryption/glwe_ct.rsRaw
  1use backend::hal::{
  2    api::{
  3        ScratchAvailable, SvpApply, SvpApplyInplace, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx, TakeSvpPPol, TakeVecZnx,
  4        TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal, VecZnxBigAddNormal, VecZnxBigAddSmallInplace, VecZnxBigAllocBytes,
  5        VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftFromVecZnx, VecZnxDftToVecZnxBigConsume, VecZnxFillUniform,
  6        VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubABInplace, ZnxInfos, ZnxZero,
  7    },
  8    layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, Scratch, VecZnx, VecZnxBig},
  9};
 10use sampling::source::Source;
 11
 12use crate::{
 13    SIX_SIGMA,
 14    dist::Distribution,
 15    layouts::{
 16        GLWECiphertext, GLWEPlaintext, Infos,
 17        prepared::{GLWEPublicKeyPrepared, GLWESecretPrepared},
 18    },
 19};
 20
 21impl GLWECiphertext<Vec<u8>> {
 22    pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, n: usize, basek: usize, k: usize) -> usize
 23    where
 24        Module<B>: VecZnxNormalizeTmpBytes + VecZnxDftAllocBytes,
 25    {
 26        let size: usize = k.div_ceil(basek);
 27        module.vec_znx_normalize_tmp_bytes(n) + 2 * VecZnx::alloc_bytes(n, 1, size) + module.vec_znx_dft_alloc_bytes(n, 1, size)
 28    }
 29    pub fn encrypt_pk_scratch_space<B: Backend>(module: &Module<B>, n: usize, basek: usize, k: usize) -> usize
 30    where
 31        Module<B>: VecZnxDftAllocBytes + SvpPPolAllocBytes + VecZnxBigAllocBytes + VecZnxNormalizeTmpBytes,
 32    {
 33        let size: usize = k.div_ceil(basek);
 34        ((module.vec_znx_dft_alloc_bytes(n, 1, size) + module.vec_znx_big_alloc_bytes(n, 1, size)) | ScalarZnx::alloc_bytes(n, 1))
 35            + module.svp_ppol_alloc_bytes(n, 1)
 36            + module.vec_znx_normalize_tmp_bytes(n)
 37    }
 38}
 39
 40impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
 41    pub fn encrypt_sk<DataPt: DataRef, DataSk: DataRef, B: Backend>(
 42        &mut self,
 43        module: &Module<B>,
 44        pt: &GLWEPlaintext<DataPt>,
 45        sk: &GLWESecretPrepared<DataSk, B>,
 46        source_xa: &mut Source,
 47        source_xe: &mut Source,
 48        sigma: f64,
 49        scratch: &mut Scratch<B>,
 50    ) where
 51        Module<B>: VecZnxDftAllocBytes
 52            + VecZnxBigNormalize<B>
 53            + VecZnxDftFromVecZnx<B>
 54            + SvpApplyInplace<B>
 55            + VecZnxDftToVecZnxBigConsume<B>
 56            + VecZnxNormalizeTmpBytes
 57            + VecZnxFillUniform
 58            + VecZnxSubABInplace
 59            + VecZnxAddInplace
 60            + VecZnxNormalizeInplace<B>
 61            + VecZnxAddNormal
 62            + VecZnxNormalize<B>
 63            + VecZnxSub,
 64        Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
 65    {
 66        #[cfg(debug_assertions)]
 67        {
 68            assert_eq!(self.rank(), sk.rank());
 69            assert_eq!(sk.n(), self.n());
 70            assert_eq!(pt.n(), self.n());
 71            assert!(
 72                scratch.available() >= GLWECiphertext::encrypt_sk_scratch_space(module, self.n(), self.basek(), self.k()),
 73                "scratch.available(): {} < GLWECiphertext::encrypt_sk_scratch_space: {}",
 74                scratch.available(),
 75                GLWECiphertext::encrypt_sk_scratch_space(module, self.n(), self.basek(), self.k())
 76            )
 77        }
 78
 79        self.encrypt_sk_internal(
 80            module,
 81            Some((pt, 0)),
 82            sk,
 83            source_xa,
 84            source_xe,
 85            sigma,
 86            scratch,
 87        );
 88    }
 89
 90    pub fn encrypt_zero_sk<DataSk: DataRef, B: Backend>(
 91        &mut self,
 92        module: &Module<B>,
 93        sk: &GLWESecretPrepared<DataSk, B>,
 94        source_xa: &mut Source,
 95        source_xe: &mut Source,
 96        sigma: f64,
 97        scratch: &mut Scratch<B>,
 98    ) where
 99        Module<B>: VecZnxDftAllocBytes
100            + VecZnxBigNormalize<B>
101            + VecZnxDftFromVecZnx<B>
102            + SvpApplyInplace<B>
103            + VecZnxDftToVecZnxBigConsume<B>
104            + VecZnxNormalizeTmpBytes
105            + VecZnxFillUniform
106            + VecZnxSubABInplace
107            + VecZnxAddInplace
108            + VecZnxNormalizeInplace<B>
109            + VecZnxAddNormal
110            + VecZnxNormalize<B>
111            + VecZnxSub,
112        Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
113    {
114        #[cfg(debug_assertions)]
115        {
116            assert_eq!(self.rank(), sk.rank());
117            assert_eq!(sk.n(), self.n());
118            assert!(
119                scratch.available() >= GLWECiphertext::encrypt_sk_scratch_space(module, self.n(), self.basek(), self.k()),
120                "scratch.available(): {} < GLWECiphertext::encrypt_sk_scratch_space: {}",
121                scratch.available(),
122                GLWECiphertext::encrypt_sk_scratch_space(module, self.n(), self.basek(), self.k())
123            )
124        }
125        self.encrypt_sk_internal(
126            module,
127            None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
128            sk,
129            source_xa,
130            source_xe,
131            sigma,
132            scratch,
133        );
134    }
135
136    pub(crate) fn encrypt_sk_internal<DataPt: DataRef, DataSk: DataRef, B: Backend>(
137        &mut self,
138        module: &Module<B>,
139        pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
140        sk: &GLWESecretPrepared<DataSk, B>,
141        source_xa: &mut Source,
142        source_xe: &mut Source,
143        sigma: f64,
144        scratch: &mut Scratch<B>,
145    ) where
146        Module<B>: VecZnxDftAllocBytes
147            + VecZnxBigNormalize<B>
148            + VecZnxDftFromVecZnx<B>
149            + SvpApplyInplace<B>
150            + VecZnxDftToVecZnxBigConsume<B>
151            + VecZnxNormalizeTmpBytes
152            + VecZnxFillUniform
153            + VecZnxSubABInplace
154            + VecZnxAddInplace
155            + VecZnxNormalizeInplace<B>
156            + VecZnxAddNormal
157            + VecZnxNormalize<B>
158            + VecZnxSub,
159        Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
160    {
161        let cols: usize = self.rank() + 1;
162        glwe_encrypt_sk_internal(
163            module,
164            self.basek(),
165            self.k(),
166            &mut self.data,
167            cols,
168            false,
169            pt,
170            sk,
171            source_xa,
172            source_xe,
173            sigma,
174            scratch,
175        );
176    }
177
178    pub fn encrypt_pk<DataPt: DataRef, DataPk: DataRef, B: Backend>(
179        &mut self,
180        module: &Module<B>,
181        pt: &GLWEPlaintext<DataPt>,
182        pk: &GLWEPublicKeyPrepared<DataPk, B>,
183        source_xu: &mut Source,
184        source_xe: &mut Source,
185        sigma: f64,
186        scratch: &mut Scratch<B>,
187    ) where
188        Module<B>: SvpPrepare<B>
189            + SvpApply<B>
190            + VecZnxDftToVecZnxBigConsume<B>
191            + VecZnxBigAddNormal<B>
192            + VecZnxBigAddSmallInplace<B>
193            + VecZnxBigNormalize<B>,
194        Scratch<B>: TakeSvpPPol<B> + TakeScalarZnx + TakeVecZnxDft<B>,
195    {
196        self.encrypt_pk_internal::<DataPt, DataPk, B>(
197            module,
198            Some((pt, 0)),
199            pk,
200            source_xu,
201            source_xe,
202            sigma,
203            scratch,
204        );
205    }
206
207    pub fn encrypt_zero_pk<DataPk: DataRef, B: Backend>(
208        &mut self,
209        module: &Module<B>,
210        pk: &GLWEPublicKeyPrepared<DataPk, B>,
211        source_xu: &mut Source,
212        source_xe: &mut Source,
213        sigma: f64,
214        scratch: &mut Scratch<B>,
215    ) where
216        Module<B>: SvpPrepare<B>
217            + SvpApply<B>
218            + VecZnxDftToVecZnxBigConsume<B>
219            + VecZnxBigAddNormal<B>
220            + VecZnxBigAddSmallInplace<B>
221            + VecZnxBigNormalize<B>,
222        Scratch<B>: TakeSvpPPol<B> + TakeScalarZnx + TakeVecZnxDft<B>,
223    {
224        self.encrypt_pk_internal::<Vec<u8>, DataPk, B>(
225            module,
226            None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
227            pk,
228            source_xu,
229            source_xe,
230            sigma,
231            scratch,
232        );
233    }
234
235    pub(crate) fn encrypt_pk_internal<DataPt: DataRef, DataPk: DataRef, B: Backend>(
236        &mut self,
237        module: &Module<B>,
238        pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
239        pk: &GLWEPublicKeyPrepared<DataPk, B>,
240        source_xu: &mut Source,
241        source_xe: &mut Source,
242        sigma: f64,
243        scratch: &mut Scratch<B>,
244    ) where
245        Module<B>: SvpPrepare<B>
246            + SvpApply<B>
247            + VecZnxDftToVecZnxBigConsume<B>
248            + VecZnxBigAddNormal<B>
249            + VecZnxBigAddSmallInplace<B>
250            + VecZnxBigNormalize<B>,
251        Scratch<B>: TakeSvpPPol<B> + TakeScalarZnx + TakeVecZnxDft<B>,
252    {
253        #[cfg(debug_assertions)]
254        {
255            assert_eq!(self.basek(), pk.basek());
256            assert_eq!(self.n(), pk.n());
257            assert_eq!(self.rank(), pk.rank());
258            if let Some((pt, _)) = pt {
259                assert_eq!(pt.basek(), pk.basek());
260                assert_eq!(pt.n(), pk.n());
261            }
262        }
263
264        let basek: usize = pk.basek();
265        let size_pk: usize = pk.size();
266        let cols: usize = self.rank() + 1;
267
268        // Generates u according to the underlying secret distribution.
269        let (mut u_dft, scratch_1) = scratch.take_svp_ppol(self.n(), 1);
270
271        {
272            let (mut u, _) = scratch_1.take_scalar_znx(self.n(), 1);
273            match pk.dist {
274                Distribution::NONE => panic!(
275                    "invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through \
276                     Self::generate"
277                ),
278                Distribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu),
279                Distribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu),
280                Distribution::BinaryFixed(hw) => u.fill_binary_hw(0, hw, source_xu),
281                Distribution::BinaryProb(prob) => u.fill_binary_prob(0, prob, source_xu),
282                Distribution::BinaryBlock(block_size) => u.fill_binary_block(0, block_size, source_xu),
283                Distribution::ZERO => {}
284            }
285
286            module.svp_prepare(&mut u_dft, 0, &u, 0);
287        }
288
289        // ct[i] = pk[i] * u + ei (+ m if col = i)
290        (0..cols).for_each(|i| {
291            let (mut ci_dft, scratch_2) = scratch_1.take_vec_znx_dft(self.n(), 1, size_pk);
292            // ci_dft = DFT(u) * DFT(pk[i])
293            module.svp_apply(&mut ci_dft, 0, &u_dft, 0, &pk.data, i);
294
295            // ci_big = u * p[i]
296            let mut ci_big = module.vec_znx_dft_to_vec_znx_big_consume(ci_dft);
297
298            // ci_big = u * pk[i] + e
299            module.vec_znx_big_add_normal(
300                basek,
301                &mut ci_big,
302                0,
303                pk.k(),
304                source_xe,
305                sigma,
306                sigma * SIX_SIGMA,
307            );
308
309            // ci_big = u * pk[i] + e + m (if col = i)
310            if let Some((pt, col)) = pt {
311                if col == i {
312                    module.vec_znx_big_add_small_inplace(&mut ci_big, 0, &pt.data, 0);
313                }
314            }
315
316            // ct[i] = norm(ci_big)
317            module.vec_znx_big_normalize(basek, &mut self.data, i, &ci_big, 0, scratch_2);
318        });
319    }
320}
321
322pub(crate) fn glwe_encrypt_sk_internal<DataCt: DataMut, DataPt: DataRef, DataSk: DataRef, B: Backend>(
323    module: &Module<B>,
324    basek: usize,
325    k: usize,
326    ct: &mut VecZnx<DataCt>,
327    cols: usize,
328    compressed: bool,
329    pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
330    sk: &GLWESecretPrepared<DataSk, B>,
331    source_xa: &mut Source,
332    source_xe: &mut Source,
333    sigma: f64,
334    scratch: &mut Scratch<B>,
335) where
336    Module<B>: VecZnxDftAllocBytes
337        + VecZnxBigNormalize<B>
338        + VecZnxDftFromVecZnx<B>
339        + SvpApplyInplace<B>
340        + VecZnxDftToVecZnxBigConsume<B>
341        + VecZnxNormalizeTmpBytes
342        + VecZnxFillUniform
343        + VecZnxSubABInplace
344        + VecZnxAddInplace
345        + VecZnxNormalizeInplace<B>
346        + VecZnxAddNormal
347        + VecZnxNormalize<B>
348        + VecZnxSub,
349    Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx,
350{
351    #[cfg(debug_assertions)]
352    {
353        if compressed {
354            use backend::hal::api::ZnxInfos;
355
356            assert_eq!(
357                ct.cols(),
358                1,
359                "invalid ciphertext: compressed tag=true but #cols={} != 1",
360                ct.cols()
361            )
362        }
363    }
364
365    let size: usize = ct.size();
366
367    let (mut c0, scratch_1) = scratch.take_vec_znx(ct.n(), 1, size);
368    c0.zero();
369
370    {
371        let (mut ci, scratch_2) = scratch_1.take_vec_znx(ct.n(), 1, size);
372
373        // ct[i] = uniform
374        // ct[0] -= c[i] * s[i],
375        (1..cols).for_each(|i| {
376            let col_ct: usize;
377            if compressed {
378                col_ct = 0;
379            } else {
380                col_ct = i;
381            }
382
383            // ct[i] = uniform (+ pt)
384            module.vec_znx_fill_uniform(basek, ct, col_ct, k, source_xa);
385
386            let (mut ci_dft, scratch_3) = scratch_2.take_vec_znx_dft(ct.n(), 1, size);
387
388            // ci = ct[i] - pt
389            // i.e. we act as we sample ct[i] already as uniform + pt
390            // and if there is a pt, then we subtract it before applying DFT
391            if let Some((pt, col)) = pt {
392                if i == col {
393                    module.vec_znx_sub(&mut ci, 0, ct, col_ct, &pt.data, 0);
394                    module.vec_znx_normalize_inplace(basek, &mut ci, 0, scratch_3);
395                    module.vec_znx_dft_from_vec_znx(1, 0, &mut ci_dft, 0, &ci, 0);
396                } else {
397                    module.vec_znx_dft_from_vec_znx(1, 0, &mut ci_dft, 0, ct, col_ct);
398                }
399            } else {
400                module.vec_znx_dft_from_vec_znx(1, 0, &mut ci_dft, 0, ct, col_ct);
401            }
402
403            module.svp_apply_inplace(&mut ci_dft, 0, &sk.data, i - 1);
404            let ci_big: VecZnxBig<&mut [u8], B> = module.vec_znx_dft_to_vec_znx_big_consume(ci_dft);
405
406            // use c[0] as buffer, which is overwritten later by the normalization step
407            module.vec_znx_big_normalize(basek, &mut ci, 0, &ci_big, 0, scratch_3);
408
409            // c0_tmp = -c[i] * s[i] (use c[0] as buffer)
410            module.vec_znx_sub_ab_inplace(&mut c0, 0, &ci, 0);
411        });
412    }
413
414    // c[0] += e
415    module.vec_znx_add_normal(basek, &mut c0, 0, k, source_xe, sigma, sigma * SIX_SIGMA);
416
417    // c[0] += m if col = 0
418    if let Some((pt, col)) = pt {
419        if col == 0 {
420            module.vec_znx_add_inplace(&mut c0, 0, &pt.data, 0);
421        }
422    }
423
424    // c[0] = norm(c[0])
425    module.vec_znx_normalize(basek, ct, 0, &c0, 0, scratch_1);
426}