poulpy/core/src/glwe_ciphertext.rs
core/src/glwe_ciphertext.rsRaw
1use backend::{
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 automorphism::AutomorphismKey,
12 elem::{Infos, SetMetaData},
13 ggsw_ciphertext::GGSWCiphertext,
14 glwe_ciphertext_fourier::GLWECiphertextFourier,
15 glwe_ops::GLWEOps,
16 glwe_plaintext::GLWEPlaintext,
17 keys::{GLWEPublicKey, SecretDistribution, SecretKeyFourier},
18 keyswitch_key::GLWESwitchingKey,
19 utils::derive_size,
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 alloc<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 out_size: usize,
119 out_rank: usize,
120 in_size: usize,
121 in_rank: usize,
122 ksk_size: usize,
123 ) -> usize {
124 let res_dft: usize = module.bytes_of_vec_znx_dft(out_rank + 1, ksk_size);
125 let vmp: usize = module.vmp_apply_tmp_bytes(out_size, in_size, in_size, in_rank, out_rank + 1, ksk_size)
126 + module.bytes_of_vec_znx_dft(in_rank, in_size);
127 let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
128
129 return res_dft + (vmp | normalize);
130 }
131
132 pub fn keyswitch_from_fourier_scratch_space(
133 module: &Module<FFT64>,
134 out_size: usize,
135 out_rank: usize,
136 in_size: usize,
137 in_rank: usize,
138 ksk_size: usize,
139 ) -> usize {
140 let res_dft = module.bytes_of_vec_znx_dft(out_rank + 1, ksk_size);
141
142 let vmp: usize = module.vmp_apply_tmp_bytes(out_size, in_size, in_size, in_rank, out_rank + 1, ksk_size)
143 + module.bytes_of_vec_znx_dft(in_rank, in_size);
144
145 let norm: usize = module.vec_znx_big_normalize_tmp_bytes();
146
147 res_dft + (vmp | norm)
148 }
149
150 pub fn keyswitch_inplace_scratch_space(module: &Module<FFT64>, out_size: usize, out_rank: usize, ksk_size: usize) -> usize {
151 GLWECiphertext::keyswitch_scratch_space(module, out_size, out_rank, out_size, out_rank, ksk_size)
152 }
153
154 pub fn automorphism_scratch_space(
155 module: &Module<FFT64>,
156 out_size: usize,
157 out_rank: usize,
158 in_size: usize,
159 autokey_size: usize,
160 ) -> usize {
161 GLWECiphertext::keyswitch_scratch_space(module, out_size, out_rank, in_size, out_rank, autokey_size)
162 }
163
164 pub fn automorphism_inplace_scratch_space(
165 module: &Module<FFT64>,
166 out_size: usize,
167 out_rank: usize,
168 autokey_size: usize,
169 ) -> usize {
170 GLWECiphertext::keyswitch_scratch_space(module, out_size, out_rank, out_size, out_rank, autokey_size)
171 }
172
173 pub fn external_product_scratch_space(
174 module: &Module<FFT64>,
175 out_size: usize,
176 out_rank: usize,
177 in_size: usize,
178 ggsw_size: usize,
179 ) -> usize {
180 let res_dft: usize = module.bytes_of_vec_znx_dft(out_rank + 1, ggsw_size);
181 let vmp: usize = module.bytes_of_vec_znx_dft(out_rank + 1, in_size)
182 + module.vmp_apply_tmp_bytes(
183 out_size,
184 in_size,
185 in_size, // rows
186 out_rank + 1, // cols in
187 out_rank + 1, // cols out
188 ggsw_size,
189 );
190 let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
191
192 res_dft + (vmp | normalize)
193 }
194
195 pub fn external_product_inplace_scratch_space(
196 module: &Module<FFT64>,
197 out_size: usize,
198 out_rank: usize,
199 ggsw_size: usize,
200 ) -> usize {
201 GLWECiphertext::external_product_scratch_space(module, out_size, out_rank, out_size, ggsw_size)
202 }
203}
204
205impl<DataSelf> SetMetaData for GLWECiphertext<DataSelf>
206where
207 VecZnx<DataSelf>: VecZnxToMut,
208{
209 fn set_k(&mut self, k: usize) {
210 self.k = k
211 }
212
213 fn set_basek(&mut self, basek: usize) {
214 self.basek = basek
215 }
216}
217
218impl<DataSelf> GLWEOps<FFT64> for GLWECiphertext<DataSelf> where VecZnx<DataSelf>: VecZnxToMut {}
219
220impl<DataSelf> GLWECiphertext<DataSelf>
221where
222 VecZnx<DataSelf>: VecZnxToMut,
223{
224 pub fn encrypt_sk<DataPt, DataSk>(
225 &mut self,
226 module: &Module<FFT64>,
227 pt: &GLWEPlaintext<DataPt>,
228 sk_dft: &SecretKeyFourier<DataSk, FFT64>,
229 source_xa: &mut Source,
230 source_xe: &mut Source,
231 sigma: f64,
232 scratch: &mut Scratch,
233 ) where
234 VecZnx<DataPt>: VecZnxToRef,
235 ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
236 {
237 self.encrypt_sk_private(
238 module,
239 Some((pt, 0)),
240 sk_dft,
241 source_xa,
242 source_xe,
243 sigma,
244 scratch,
245 );
246 }
247
248 pub fn encrypt_zero_sk<DataSk>(
249 &mut self,
250 module: &Module<FFT64>,
251 sk_dft: &SecretKeyFourier<DataSk, FFT64>,
252 source_xa: &mut Source,
253 source_xe: &mut Source,
254 sigma: f64,
255 scratch: &mut Scratch,
256 ) where
257 ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
258 {
259 self.encrypt_sk_private(module, None, sk_dft, source_xa, source_xe, sigma, scratch);
260 }
261
262 pub fn encrypt_pk<DataPt, DataPk>(
263 &mut self,
264 module: &Module<FFT64>,
265 pt: &GLWEPlaintext<DataPt>,
266 pk: &GLWEPublicKey<DataPk, FFT64>,
267 source_xu: &mut Source,
268 source_xe: &mut Source,
269 sigma: f64,
270 scratch: &mut Scratch,
271 ) where
272 VecZnx<DataPt>: VecZnxToRef,
273 VecZnxDft<DataPk, FFT64>: VecZnxDftToRef<FFT64>,
274 {
275 self.encrypt_pk_private(
276 module,
277 Some((pt, 0)),
278 pk,
279 source_xu,
280 source_xe,
281 sigma,
282 scratch,
283 );
284 }
285
286 pub fn encrypt_zero_pk<DataPk>(
287 &mut self,
288 module: &Module<FFT64>,
289 pk: &GLWEPublicKey<DataPk, FFT64>,
290 source_xu: &mut Source,
291 source_xe: &mut Source,
292 sigma: f64,
293 scratch: &mut Scratch,
294 ) where
295 VecZnxDft<DataPk, FFT64>: VecZnxDftToRef<FFT64>,
296 {
297 self.encrypt_pk_private(module, None, pk, source_xu, source_xe, sigma, scratch);
298 }
299
300 pub fn automorphism<DataLhs, DataRhs>(
301 &mut self,
302 module: &Module<FFT64>,
303 lhs: &GLWECiphertext<DataLhs>,
304 rhs: &AutomorphismKey<DataRhs, FFT64>,
305 scratch: &mut Scratch,
306 ) where
307 VecZnx<DataLhs>: VecZnxToRef,
308 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
309 {
310 self.keyswitch(module, lhs, &rhs.key, scratch);
311 (0..self.rank() + 1).for_each(|i| {
312 module.vec_znx_automorphism_inplace(rhs.p(), self, i);
313 })
314 }
315
316 pub fn automorphism_inplace<DataRhs>(
317 &mut self,
318 module: &Module<FFT64>,
319 rhs: &AutomorphismKey<DataRhs, FFT64>,
320 scratch: &mut Scratch,
321 ) where
322 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
323 {
324 self.keyswitch_inplace(module, &rhs.key, scratch);
325 (0..self.rank() + 1).for_each(|i| {
326 module.vec_znx_automorphism_inplace(rhs.p(), self, i);
327 })
328 }
329
330 pub fn automorphism_add<DataLhs, DataRhs>(
331 &mut self,
332 module: &Module<FFT64>,
333 lhs: &GLWECiphertext<DataLhs>,
334 rhs: &AutomorphismKey<DataRhs, FFT64>,
335 scratch: &mut Scratch,
336 ) where
337 VecZnx<DataLhs>: VecZnxToRef,
338 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
339 {
340 Self::keyswitch_private(self, true, rhs.p(), module, lhs, &rhs.key, scratch);
341 }
342
343 pub fn automorphism_add_inplace<DataRhs>(
344 &mut self,
345 module: &Module<FFT64>,
346 rhs: &AutomorphismKey<DataRhs, FFT64>,
347 scratch: &mut Scratch,
348 ) where
349 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
350 {
351 unsafe {
352 let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
353 Self::keyswitch_private(self, true, rhs.p(), module, &*self_ptr, &rhs.key, scratch);
354 }
355 }
356
357 pub(crate) fn keyswitch_from_fourier<DataLhs, DataRhs>(
358 &mut self,
359 module: &Module<FFT64>,
360 lhs: &GLWECiphertextFourier<DataLhs, FFT64>,
361 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
362 scratch: &mut Scratch,
363 ) where
364 VecZnxDft<DataLhs, FFT64>: VecZnxDftToRef<FFT64>,
365 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
366 {
367 let basek: usize = self.basek();
368
369 #[cfg(debug_assertions)]
370 {
371 assert_eq!(lhs.rank(), rhs.rank_in());
372 assert_eq!(self.rank(), rhs.rank_out());
373 assert_eq!(self.basek(), basek);
374 assert_eq!(lhs.basek(), basek);
375 assert_eq!(rhs.n(), module.n());
376 assert_eq!(self.n(), module.n());
377 assert_eq!(lhs.n(), module.n());
378 assert!(
379 scratch.available()
380 >= GLWECiphertext::keyswitch_from_fourier_scratch_space(
381 module,
382 self.size(),
383 self.rank(),
384 lhs.size(),
385 lhs.rank(),
386 rhs.size(),
387 )
388 );
389 }
390
391 let cols_in: usize = rhs.rank_in();
392 let cols_out: usize = rhs.rank_out() + 1;
393
394 // Buffer of the result of VMP in DFT
395 let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, rhs.size()); // Todo optimise
396
397 {
398 // Applies VMP
399 let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, lhs.size());
400 (0..cols_in).for_each(|col_i| {
401 module.vec_znx_dft_copy(&mut ai_dft, col_i, lhs, col_i + 1);
402 });
403 module.vmp_apply(&mut res_dft, &ai_dft, rhs, scratch2);
404 }
405
406 module.vec_znx_dft_add_inplace(&mut res_dft, 0, lhs, 0);
407
408 // Switches result of VMP outside of DFT
409 let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume::<&mut [u8]>(res_dft);
410
411 (0..cols_out).for_each(|i| {
412 module.vec_znx_big_normalize(basek, self, i, &res_big, i, scratch1);
413 });
414 }
415
416 pub fn keyswitch<DataLhs, DataRhs>(
417 &mut self,
418 module: &Module<FFT64>,
419 lhs: &GLWECiphertext<DataLhs>,
420 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
421 scratch: &mut Scratch,
422 ) where
423 VecZnx<DataLhs>: VecZnxToRef,
424 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
425 {
426 Self::keyswitch_private(self, false, 0, module, lhs, rhs, scratch);
427 }
428
429 pub(crate) fn keyswitch_private<DataLhs, DataRhs>(
430 &mut self,
431 add_self: bool,
432 apply_auto: i64,
433 module: &Module<FFT64>,
434 lhs: &GLWECiphertext<DataLhs>,
435 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
436 scratch: &mut Scratch,
437 ) where
438 VecZnx<DataLhs>: VecZnxToRef,
439 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
440 {
441 let basek: usize = self.basek();
442
443 #[cfg(debug_assertions)]
444 {
445 assert_eq!(lhs.rank(), rhs.rank_in());
446 assert_eq!(self.rank(), rhs.rank_out());
447 assert_eq!(self.basek(), basek);
448 assert_eq!(lhs.basek(), basek);
449 assert_eq!(rhs.n(), module.n());
450 assert_eq!(self.n(), module.n());
451 assert_eq!(lhs.n(), module.n());
452 assert!(
453 scratch.available()
454 >= GLWECiphertext::keyswitch_scratch_space(
455 module,
456 self.size(),
457 self.rank(),
458 lhs.size(),
459 lhs.rank(),
460 rhs.size(),
461 )
462 );
463 }
464
465 let cols_in: usize = rhs.rank_in();
466 let cols_out: usize = rhs.rank_out() + 1;
467
468 let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, rhs.size()); // Todo optimise
469
470 {
471 let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, lhs.size());
472 (0..cols_in).for_each(|col_i| {
473 module.vec_znx_dft(&mut ai_dft, col_i, lhs, col_i + 1);
474 });
475 module.vmp_apply(&mut res_dft, &ai_dft, rhs, scratch2);
476 }
477
478 let mut res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
479
480 module.vec_znx_big_add_small_inplace(&mut res_big, 0, lhs, 0);
481
482 (0..cols_out).for_each(|i| {
483 if apply_auto != 0 {
484 module.vec_znx_big_automorphism_inplace(apply_auto, &mut res_big, i);
485 }
486
487 if add_self {
488 module.vec_znx_big_add_small_inplace(&mut res_big, i, lhs, i);
489 }
490 module.vec_znx_big_normalize(basek, self, i, &res_big, i, scratch1);
491 });
492 }
493
494 pub fn keyswitch_inplace<DataRhs>(
495 &mut self,
496 module: &Module<FFT64>,
497 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
498 scratch: &mut Scratch,
499 ) where
500 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
501 {
502 unsafe {
503 let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
504 self.keyswitch(&module, &*self_ptr, rhs, scratch);
505 }
506 }
507
508 pub fn external_product<DataLhs, DataRhs>(
509 &mut self,
510 module: &Module<FFT64>,
511 lhs: &GLWECiphertext<DataLhs>,
512 rhs: &GGSWCiphertext<DataRhs, FFT64>,
513 scratch: &mut Scratch,
514 ) where
515 VecZnx<DataLhs>: VecZnxToRef,
516 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
517 {
518 let basek: usize = self.basek();
519
520 #[cfg(debug_assertions)]
521 {
522 assert_eq!(rhs.rank(), lhs.rank());
523 assert_eq!(rhs.rank(), self.rank());
524 assert_eq!(self.basek(), basek);
525 assert_eq!(lhs.basek(), basek);
526 assert_eq!(rhs.n(), module.n());
527 assert_eq!(self.n(), module.n());
528 assert_eq!(lhs.n(), module.n());
529 }
530
531 let cols: usize = rhs.rank() + 1;
532
533 let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols, rhs.size()); // Todo optimise
534
535 {
536 let (mut a_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, lhs.size());
537 (0..cols).for_each(|col_i| {
538 module.vec_znx_dft(&mut a_dft, col_i, lhs, col_i);
539 });
540 module.vmp_apply(&mut res_dft, &a_dft, rhs, scratch2);
541 }
542
543 let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
544
545 (0..cols).for_each(|i| {
546 module.vec_znx_big_normalize(basek, self, i, &res_big, i, scratch1);
547 });
548 }
549
550 pub fn external_product_inplace<DataRhs>(
551 &mut self,
552 module: &Module<FFT64>,
553 rhs: &GGSWCiphertext<DataRhs, FFT64>,
554 scratch: &mut Scratch,
555 ) where
556 MatZnxDft<DataRhs, FFT64>: MatZnxDftToRef<FFT64>,
557 {
558 unsafe {
559 let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
560 self.external_product(&module, &*self_ptr, rhs, scratch);
561 }
562 }
563
564 pub(crate) fn encrypt_sk_private<DataPt, DataSk>(
565 &mut self,
566 module: &Module<FFT64>,
567 pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
568 sk_dft: &SecretKeyFourier<DataSk, FFT64>,
569 source_xa: &mut Source,
570 source_xe: &mut Source,
571 sigma: f64,
572 scratch: &mut Scratch,
573 ) where
574 VecZnx<DataPt>: VecZnxToRef,
575 ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
576 {
577 #[cfg(debug_assertions)]
578 {
579 assert_eq!(self.rank(), sk_dft.rank());
580 assert_eq!(sk_dft.n(), module.n());
581 assert_eq!(self.n(), module.n());
582 if let Some((pt, col)) = pt {
583 assert_eq!(pt.n(), module.n());
584 assert!(col < self.rank() + 1);
585 }
586 }
587
588 let basek: usize = self.basek();
589 let k: usize = self.k();
590 let size: usize = self.size();
591 let cols: usize = self.rank() + 1;
592
593 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx(module, 1, size);
594 c0_big.zero();
595
596 {
597 // c[i] = uniform
598 // c[0] -= c[i] * s[i],
599 (1..cols).for_each(|i| {
600 let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size);
601
602 // c[i] = uniform
603 self.data.fill_uniform(basek, i, size, source_xa);
604
605 // c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i])))
606 module.vec_znx_dft(&mut ci_dft, 0, self, i);
607 module.svp_apply_inplace(&mut ci_dft, 0, sk_dft, i - 1);
608 let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft);
609
610 // use c[0] as buffer, which is overwritten later by the normalization step
611 module.vec_znx_big_normalize(basek, self, 0, &ci_big, 0, scratch_2);
612
613 // c0_tmp = -c[i] * s[i] (use c[0] as buffer)
614 module.vec_znx_sub_ab_inplace(&mut c0_big, 0, self, 0);
615
616 // c[i] += m if col = i
617 if let Some((pt, col)) = pt {
618 if i == col {
619 module.vec_znx_add_inplace(self, i, pt, 0);
620 module.vec_znx_normalize_inplace(basek, self, i, scratch_2);
621 }
622 }
623 });
624 }
625
626 // c[0] += e
627 c0_big.add_normal(basek, 0, k, source_xe, sigma, sigma * SIX_SIGMA);
628
629 // c[0] += m if col = 0
630 if let Some((pt, col)) = pt {
631 if col == 0 {
632 module.vec_znx_add_inplace(&mut c0_big, 0, pt, 0);
633 }
634 }
635
636 // c[0] = norm(c[0])
637 module.vec_znx_normalize(basek, self, 0, &c0_big, 0, scratch_1);
638 }
639
640 pub(crate) fn encrypt_pk_private<DataPt, DataPk>(
641 &mut self,
642 module: &Module<FFT64>,
643 pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
644 pk: &GLWEPublicKey<DataPk, FFT64>,
645 source_xu: &mut Source,
646 source_xe: &mut Source,
647 sigma: f64,
648 scratch: &mut Scratch,
649 ) where
650 VecZnx<DataPt>: VecZnxToRef,
651 VecZnxDft<DataPk, FFT64>: VecZnxDftToRef<FFT64>,
652 {
653 #[cfg(debug_assertions)]
654 {
655 assert_eq!(self.basek(), pk.basek());
656 assert_eq!(self.n(), module.n());
657 assert_eq!(pk.n(), module.n());
658 assert_eq!(self.rank(), pk.rank());
659 if let Some((pt, _)) = pt {
660 assert_eq!(pt.basek(), pk.basek());
661 assert_eq!(pt.n(), module.n());
662 }
663 }
664
665 let basek: usize = pk.basek();
666 let size_pk: usize = pk.size();
667 let cols: usize = self.rank() + 1;
668
669 // Generates u according to the underlying secret distribution.
670 let (mut u_dft, scratch_1) = scratch.tmp_scalar_znx_dft(module, 1);
671
672 {
673 let (mut u, _) = scratch_1.tmp_scalar_znx(module, 1);
674 match pk.dist {
675 SecretDistribution::NONE => panic!(
676 "invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through \
677 Self::generate"
678 ),
679 SecretDistribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu),
680 SecretDistribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu),
681 SecretDistribution::ZERO => {}
682 }
683
684 module.svp_prepare(&mut u_dft, 0, &u, 0);
685 }
686
687 // ct[i] = pk[i] * u + ei (+ m if col = i)
688 (0..cols).for_each(|i| {
689 let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size_pk);
690 // ci_dft = DFT(u) * DFT(pk[i])
691 module.svp_apply(&mut ci_dft, 0, &u_dft, 0, pk, i);
692
693 // ci_big = u * p[i]
694 let mut ci_big = module.vec_znx_idft_consume(ci_dft);
695
696 // ci_big = u * pk[i] + e
697 ci_big.add_normal(basek, 0, pk.k(), source_xe, sigma, sigma * SIX_SIGMA);
698
699 // ci_big = u * pk[i] + e + m (if col = i)
700 if let Some((pt, col)) = pt {
701 if col == i {
702 module.vec_znx_big_add_small_inplace(&mut ci_big, 0, pt, 0);
703 }
704 }
705
706 // ct[i] = norm(ci_big)
707 module.vec_znx_big_normalize(basek, self, i, &ci_big, 0, scratch_2);
708 });
709 }
710}
711
712impl<DataSelf> GLWECiphertext<DataSelf>
713where
714 VecZnx<DataSelf>: VecZnxToRef,
715{
716 pub fn decrypt<DataPt, DataSk>(
717 &self,
718 module: &Module<FFT64>,
719 pt: &mut GLWEPlaintext<DataPt>,
720 sk_dft: &SecretKeyFourier<DataSk, FFT64>,
721 scratch: &mut Scratch,
722 ) where
723 VecZnx<DataPt>: VecZnxToMut,
724 ScalarZnxDft<DataSk, FFT64>: ScalarZnxDftToRef<FFT64>,
725 {
726 #[cfg(debug_assertions)]
727 {
728 assert_eq!(self.rank(), sk_dft.rank());
729 assert_eq!(self.n(), module.n());
730 assert_eq!(pt.n(), module.n());
731 assert_eq!(sk_dft.n(), module.n());
732 }
733
734 let cols: usize = self.rank() + 1;
735
736 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, self.size()); // TODO optimize size when pt << ct
737 c0_big.zero();
738
739 {
740 (1..cols).for_each(|i| {
741 // ci_dft = DFT(a[i]) * DFT(s[i])
742 let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct
743 module.vec_znx_dft(&mut ci_dft, 0, self, i);
744 module.svp_apply_inplace(&mut ci_dft, 0, sk_dft, i - 1);
745 let ci_big = module.vec_znx_idft_consume(ci_dft);
746
747 // c0_big += a[i] * s[i]
748 module.vec_znx_big_add_inplace(&mut c0_big, 0, &ci_big, 0);
749 });
750 }
751
752 // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
753 module.vec_znx_big_add_small_inplace(&mut c0_big, 0, self, 0);
754
755 // pt = norm(BIG(m + e))
756 module.vec_znx_big_normalize(self.basek(), pt, 0, &mut c0_big, 0, scratch_1);
757
758 pt.basek = self.basek();
759 pt.k = pt.k().min(self.k());
760 }
761}