poulpy/core/src/glwe_ciphertext.rs
core/src/glwe_ciphertext.rsRaw
1use backend::{
2 AddNormal, Backend, FFT64, FillUniform, MatZnxDftOps, MatZnxDftScratch, Module, ScalarZnxAlloc, ScalarZnxDftAlloc,
3 ScalarZnxDftOps, Scratch, VecZnx, VecZnxAlloc, VecZnxBig, VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDftAlloc,
4 VecZnxDftOps, VecZnxOps, VecZnxToMut, VecZnxToRef, ZnxZero,
5};
6use sampling::source::Source;
7
8use crate::{
9 AutomorphismKey, GGSWCiphertext, GLWECiphertextFourier, GLWEOps, GLWEPlaintext, GLWEPublicKey, GLWESecret, GLWESwitchingKey,
10 Infos, SIX_SIGMA, SecretDistribution, SetMetaData, div_ceil,
11};
12
13pub struct GLWECiphertext<C> {
14 pub data: VecZnx<C>,
15 pub basek: usize,
16 pub k: usize,
17}
18
19impl GLWECiphertext<Vec<u8>> {
20 pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
21 Self {
22 data: module.new_vec_znx(rank + 1, div_ceil(k, basek)),
23 basek,
24 k,
25 }
26 }
27
28 pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
29 module.bytes_of_vec_znx(rank + 1, div_ceil(k, basek))
30 }
31}
32
33impl<T> Infos for GLWECiphertext<T> {
34 type Inner = VecZnx<T>;
35
36 fn inner(&self) -> &Self::Inner {
37 &self.data
38 }
39
40 fn basek(&self) -> usize {
41 self.basek
42 }
43
44 fn k(&self) -> usize {
45 self.k
46 }
47}
48
49impl<T> GLWECiphertext<T> {
50 pub fn rank(&self) -> usize {
51 self.cols() - 1
52 }
53}
54
55impl<C: AsRef<[u8]>> GLWECiphertext<C> {
56 #[allow(dead_code)]
57 pub(crate) fn dft<R: AsMut<[u8]> + AsRef<[u8]>>(&self, module: &Module<FFT64>, res: &mut GLWECiphertextFourier<R, FFT64>) {
58 #[cfg(debug_assertions)]
59 {
60 assert_eq!(self.rank(), res.rank());
61 assert_eq!(self.basek(), res.basek())
62 }
63
64 (0..self.rank() + 1).for_each(|i| {
65 module.vec_znx_dft(1, 0, &mut res.data, i, &self.data, i);
66 })
67 }
68}
69
70impl GLWECiphertext<Vec<u8>> {
71 pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
72 let size: usize = div_ceil(k, basek);
73 module.vec_znx_big_normalize_tmp_bytes() + module.bytes_of_vec_znx_dft(1, size) + module.bytes_of_vec_znx(1, size)
74 }
75 pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
76 let size: usize = div_ceil(k, basek);
77 ((module.bytes_of_vec_znx_dft(1, size) + module.bytes_of_vec_znx_big(1, size)) | module.bytes_of_scalar_znx(1))
78 + module.bytes_of_scalar_znx_dft(1)
79 + module.vec_znx_big_normalize_tmp_bytes()
80 }
81
82 pub fn decrypt_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
83 let size: usize = div_ceil(k, basek);
84 (module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, size)) + module.bytes_of_vec_znx_big(1, size)
85 }
86
87 pub fn keyswitch_scratch_space(
88 module: &Module<FFT64>,
89 basek: usize,
90 k_out: usize,
91 k_in: usize,
92 k_ksk: usize,
93 digits: usize,
94 rank_in: usize,
95 rank_out: usize,
96 ) -> usize {
97 let res_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, k_out, rank_out);
98 let in_size: usize = div_ceil(div_ceil(k_in, basek), digits);
99 let out_size: usize = div_ceil(k_out, basek);
100 let ksk_size: usize = div_ceil(k_ksk, basek);
101 let vmp: usize = module.vmp_apply_tmp_bytes(out_size, in_size, in_size, rank_in, rank_out + 1, ksk_size)
102 + module.bytes_of_vec_znx_dft(rank_in, in_size);
103 let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
104 return res_dft + (vmp | normalize);
105 }
106
107 pub fn keyswitch_from_fourier_scratch_space(
108 module: &Module<FFT64>,
109 basek: usize,
110 k_out: usize,
111 k_in: usize,
112 k_ksk: usize,
113 digits: usize,
114 rank_in: usize,
115 rank_out: usize,
116 ) -> usize {
117 Self::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out)
118 }
119
120 pub fn keyswitch_inplace_scratch_space(
121 module: &Module<FFT64>,
122 basek: usize,
123 k_out: usize,
124 k_ksk: usize,
125 digits: usize,
126 rank: usize,
127 ) -> usize {
128 Self::keyswitch_scratch_space(module, basek, k_out, k_out, k_ksk, digits, rank, rank)
129 }
130
131 pub fn automorphism_scratch_space(
132 module: &Module<FFT64>,
133 basek: usize,
134 k_out: usize,
135 k_in: usize,
136 k_ksk: usize,
137 digits: usize,
138 rank: usize,
139 ) -> usize {
140 Self::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank, rank)
141 }
142
143 pub fn automorphism_inplace_scratch_space(
144 module: &Module<FFT64>,
145 basek: usize,
146 k_out: usize,
147 k_ksk: usize,
148 digits: usize,
149 rank: usize,
150 ) -> usize {
151 Self::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank)
152 }
153
154 pub fn external_product_scratch_space(
155 module: &Module<FFT64>,
156 basek: usize,
157 k_out: usize,
158 k_in: usize,
159 ggsw_k: usize,
160 digits: usize,
161 rank: usize,
162 ) -> usize {
163 let res_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, k_out, rank);
164 let in_size: usize = div_ceil(div_ceil(k_in, basek), digits);
165 let out_size: usize = div_ceil(k_out, basek);
166 let ggsw_size: usize = div_ceil(ggsw_k, basek);
167 let vmp: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size)
168 + module.vmp_apply_tmp_bytes(
169 out_size,
170 in_size,
171 in_size, // rows
172 rank + 1, // cols in
173 rank + 1, // cols out
174 ggsw_size,
175 );
176 let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
177 res_dft + (vmp | normalize)
178 }
179
180 pub fn external_product_inplace_scratch_space(
181 module: &Module<FFT64>,
182 basek: usize,
183 k_out: usize,
184 ggsw_k: usize,
185 digits: usize,
186 rank: usize,
187 ) -> usize {
188 Self::external_product_scratch_space(module, basek, k_out, k_out, ggsw_k, digits, rank)
189 }
190}
191
192impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> SetMetaData for GLWECiphertext<DataSelf> {
193 fn set_k(&mut self, k: usize) {
194 self.k = k
195 }
196
197 fn set_basek(&mut self, basek: usize) {
198 self.basek = basek
199 }
200}
201
202impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
203 pub fn encrypt_sk<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
204 &mut self,
205 module: &Module<FFT64>,
206 pt: &GLWEPlaintext<DataPt>,
207 sk: &GLWESecret<DataSk, FFT64>,
208 source_xa: &mut Source,
209 source_xe: &mut Source,
210 sigma: f64,
211 scratch: &mut Scratch,
212 ) {
213 self.encrypt_sk_private(
214 module,
215 Some((pt, 0)),
216 sk,
217 source_xa,
218 source_xe,
219 sigma,
220 scratch,
221 );
222 }
223
224 pub fn encrypt_zero_sk<DataSk: AsRef<[u8]>>(
225 &mut self,
226 module: &Module<FFT64>,
227 sk: &GLWESecret<DataSk, FFT64>,
228 source_xa: &mut Source,
229 source_xe: &mut Source,
230 sigma: f64,
231 scratch: &mut Scratch,
232 ) {
233 self.encrypt_sk_private(
234 module,
235 None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
236 sk,
237 source_xa,
238 source_xe,
239 sigma,
240 scratch,
241 );
242 }
243
244 pub fn encrypt_pk<DataPt: AsRef<[u8]>, DataPk: AsRef<[u8]>>(
245 &mut self,
246 module: &Module<FFT64>,
247 pt: &GLWEPlaintext<DataPt>,
248 pk: &GLWEPublicKey<DataPk, FFT64>,
249 source_xu: &mut Source,
250 source_xe: &mut Source,
251 sigma: f64,
252 scratch: &mut Scratch,
253 ) {
254 self.encrypt_pk_private(
255 module,
256 Some((pt, 0)),
257 pk,
258 source_xu,
259 source_xe,
260 sigma,
261 scratch,
262 );
263 }
264
265 pub fn encrypt_zero_pk<DataPk: AsRef<[u8]>>(
266 &mut self,
267 module: &Module<FFT64>,
268 pk: &GLWEPublicKey<DataPk, FFT64>,
269 source_xu: &mut Source,
270 source_xe: &mut Source,
271 sigma: f64,
272 scratch: &mut Scratch,
273 ) {
274 self.encrypt_pk_private(
275 module,
276 None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
277 pk,
278 source_xu,
279 source_xe,
280 sigma,
281 scratch,
282 );
283 }
284
285 pub fn automorphism<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
286 &mut self,
287 module: &Module<FFT64>,
288 lhs: &GLWECiphertext<DataLhs>,
289 rhs: &AutomorphismKey<DataRhs, FFT64>,
290 scratch: &mut Scratch,
291 ) {
292 self.keyswitch(module, lhs, &rhs.key, scratch);
293 (0..self.rank() + 1).for_each(|i| {
294 module.vec_znx_automorphism_inplace(rhs.p(), &mut self.data, i);
295 })
296 }
297
298 pub fn automorphism_inplace<DataRhs: AsRef<[u8]>>(
299 &mut self,
300 module: &Module<FFT64>,
301 rhs: &AutomorphismKey<DataRhs, FFT64>,
302 scratch: &mut Scratch,
303 ) {
304 self.keyswitch_inplace(module, &rhs.key, scratch);
305 (0..self.rank() + 1).for_each(|i| {
306 module.vec_znx_automorphism_inplace(rhs.p(), &mut self.data, i);
307 })
308 }
309
310 pub fn automorphism_add<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
311 &mut self,
312 module: &Module<FFT64>,
313 lhs: &GLWECiphertext<DataLhs>,
314 rhs: &AutomorphismKey<DataRhs, FFT64>,
315 scratch: &mut Scratch,
316 ) {
317 Self::keyswitch_private::<_, _, 1>(self, rhs.p(), module, lhs, &rhs.key, scratch);
318 }
319
320 pub fn automorphism_add_inplace<DataRhs: AsRef<[u8]>>(
321 &mut self,
322 module: &Module<FFT64>,
323 rhs: &AutomorphismKey<DataRhs, FFT64>,
324 scratch: &mut Scratch,
325 ) {
326 unsafe {
327 let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
328 Self::keyswitch_private::<_, _, 1>(self, rhs.p(), module, &*self_ptr, &rhs.key, scratch);
329 }
330 }
331
332 pub fn automorphism_sub_ab<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
333 &mut self,
334 module: &Module<FFT64>,
335 lhs: &GLWECiphertext<DataLhs>,
336 rhs: &AutomorphismKey<DataRhs, FFT64>,
337 scratch: &mut Scratch,
338 ) {
339 Self::keyswitch_private::<_, _, 2>(self, rhs.p(), module, lhs, &rhs.key, scratch);
340 }
341
342 pub fn automorphism_sub_ab_inplace<DataRhs: AsRef<[u8]>>(
343 &mut self,
344 module: &Module<FFT64>,
345 rhs: &AutomorphismKey<DataRhs, FFT64>,
346 scratch: &mut Scratch,
347 ) {
348 unsafe {
349 let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
350 Self::keyswitch_private::<_, _, 2>(self, rhs.p(), module, &*self_ptr, &rhs.key, scratch);
351 }
352 }
353
354 pub fn automorphism_sub_ba<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
355 &mut self,
356 module: &Module<FFT64>,
357 lhs: &GLWECiphertext<DataLhs>,
358 rhs: &AutomorphismKey<DataRhs, FFT64>,
359 scratch: &mut Scratch,
360 ) {
361 Self::keyswitch_private::<_, _, 3>(self, rhs.p(), module, lhs, &rhs.key, scratch);
362 }
363
364 pub fn automorphism_sub_ba_inplace<DataRhs: AsRef<[u8]>>(
365 &mut self,
366 module: &Module<FFT64>,
367 rhs: &AutomorphismKey<DataRhs, FFT64>,
368 scratch: &mut Scratch,
369 ) {
370 unsafe {
371 let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
372 Self::keyswitch_private::<_, _, 3>(self, rhs.p(), module, &*self_ptr, &rhs.key, scratch);
373 }
374 }
375
376 pub(crate) fn keyswitch_from_fourier<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
377 &mut self,
378 module: &Module<FFT64>,
379 lhs: &GLWECiphertextFourier<DataLhs, FFT64>,
380 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
381 scratch: &mut Scratch,
382 ) {
383 let basek: usize = self.basek();
384
385 #[cfg(debug_assertions)]
386 {
387 assert_eq!(lhs.rank(), rhs.rank_in());
388 assert_eq!(self.rank(), rhs.rank_out());
389 assert_eq!(self.basek(), basek);
390 assert_eq!(lhs.basek(), basek);
391 assert_eq!(rhs.n(), module.n());
392 assert_eq!(self.n(), module.n());
393 assert_eq!(lhs.n(), module.n());
394 assert!(
395 scratch.available()
396 >= GLWECiphertext::keyswitch_from_fourier_scratch_space(
397 module,
398 self.basek(),
399 self.k(),
400 lhs.k(),
401 rhs.k(),
402 rhs.digits(),
403 rhs.rank_in(),
404 rhs.rank_out(),
405 )
406 );
407 }
408
409 let cols_in: usize = rhs.rank_in();
410 let cols_out: usize = rhs.rank_out() + 1;
411
412 // Buffer of the result of VMP in DFT
413 let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, rhs.size()); // Todo optimise
414
415 {
416 let digits = rhs.digits();
417
418 (0..digits).for_each(|di| {
419 // (lhs.size() + di) / digits = (a - (digit - di - 1) + digit - 1) / digits
420 let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, (lhs.size() + di) / digits);
421
422 (0..cols_in).for_each(|col_i| {
423 module.vec_znx_dft_copy(
424 digits,
425 digits - 1 - di,
426 &mut ai_dft,
427 col_i,
428 &lhs.data,
429 col_i + 1,
430 );
431 });
432
433 if di == 0 {
434 module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2);
435 } else {
436 module.vmp_apply_add(&mut res_dft, &ai_dft, &rhs.0.data, di, scratch2);
437 }
438 });
439 }
440
441 module.vec_znx_dft_add_inplace(&mut res_dft, 0, &lhs.data, 0);
442
443 // Switches result of VMP outside of DFT
444 let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume::<&mut [u8]>(res_dft);
445
446 (0..cols_out).for_each(|i| {
447 module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
448 });
449 }
450
451 pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
452 &mut self,
453 module: &Module<FFT64>,
454 lhs: &GLWECiphertext<DataLhs>,
455 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
456 scratch: &mut Scratch,
457 ) {
458 Self::keyswitch_private::<_, _, 0>(self, 0, module, lhs, rhs, scratch);
459 }
460
461 pub(crate) fn keyswitch_private<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>, const OP: u8>(
462 &mut self,
463 apply_auto: i64,
464 module: &Module<FFT64>,
465 lhs: &GLWECiphertext<DataLhs>,
466 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
467 scratch: &mut Scratch,
468 ) {
469 let basek: usize = self.basek();
470
471 #[cfg(debug_assertions)]
472 {
473 assert_eq!(lhs.rank(), rhs.rank_in());
474 assert_eq!(self.rank(), rhs.rank_out());
475 assert_eq!(self.basek(), basek);
476 assert_eq!(lhs.basek(), basek);
477 assert_eq!(rhs.n(), module.n());
478 assert_eq!(self.n(), module.n());
479 assert_eq!(lhs.n(), module.n());
480 assert!(
481 scratch.available()
482 >= GLWECiphertext::keyswitch_scratch_space(
483 module,
484 self.basek(),
485 self.k(),
486 lhs.k(),
487 rhs.k(),
488 rhs.digits(),
489 rhs.rank_in(),
490 rhs.rank_out(),
491 )
492 );
493 }
494
495 let cols_in: usize = rhs.rank_in();
496 let cols_out: usize = rhs.rank_out() + 1;
497
498 let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, rhs.size()); // Todo optimise
499
500 {
501 let digits = rhs.digits();
502
503 (0..digits).for_each(|di| {
504 // (lhs.size() + di) / digits = (a - (digit - di - 1) + digit - 1) / digits
505 let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, (lhs.size() + di) / digits);
506
507 (0..cols_in).for_each(|col_i| {
508 module.vec_znx_dft(
509 digits,
510 digits - 1 - di,
511 &mut ai_dft,
512 col_i,
513 &lhs.data,
514 col_i + 1,
515 );
516 });
517
518 if di == 0 {
519 module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2);
520 } else {
521 module.vmp_apply_add(&mut res_dft, &ai_dft, &rhs.0.data, di, scratch2);
522 }
523 });
524 }
525
526 let mut res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
527
528 module.vec_znx_big_add_small_inplace(&mut res_big, 0, &lhs.data, 0);
529
530 (0..cols_out).for_each(|i| {
531 if apply_auto != 0 {
532 module.vec_znx_big_automorphism_inplace(apply_auto, &mut res_big, i);
533 }
534
535 match OP {
536 1 => module.vec_znx_big_add_small_inplace(&mut res_big, i, &lhs.data, i),
537 2 => module.vec_znx_big_sub_small_a_inplace(&mut res_big, i, &lhs.data, i),
538 3 => module.vec_znx_big_sub_small_b_inplace(&mut res_big, i, &lhs.data, i),
539 _ => {}
540 }
541 module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
542 });
543 }
544
545 pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>(
546 &mut self,
547 module: &Module<FFT64>,
548 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
549 scratch: &mut Scratch,
550 ) {
551 unsafe {
552 let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
553 self.keyswitch(&module, &*self_ptr, rhs, scratch);
554 }
555 }
556
557 pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
558 &mut self,
559 module: &Module<FFT64>,
560 lhs: &GLWECiphertext<DataLhs>,
561 rhs: &GGSWCiphertext<DataRhs, FFT64>,
562 scratch: &mut Scratch,
563 ) {
564 let basek: usize = self.basek();
565
566 #[cfg(debug_assertions)]
567 {
568 assert_eq!(rhs.rank(), lhs.rank());
569 assert_eq!(rhs.rank(), self.rank());
570 assert_eq!(self.basek(), basek);
571 assert_eq!(lhs.basek(), basek);
572 assert_eq!(rhs.n(), module.n());
573 assert_eq!(self.n(), module.n());
574 assert_eq!(lhs.n(), module.n());
575 assert!(
576 scratch.available()
577 >= GLWECiphertext::external_product_scratch_space(
578 module,
579 self.basek(),
580 self.k(),
581 lhs.k(),
582 rhs.k(),
583 rhs.digits(),
584 rhs.rank(),
585 )
586 );
587 }
588
589 let cols: usize = rhs.rank() + 1;
590
591 let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols, rhs.size()); // Todo optimise
592
593 {
594 let digits = rhs.digits();
595
596 (0..digits).for_each(|di| {
597 // (lhs.size() + di) / digits = (a - (digit - di - 1) + digit - 1) / digits
598 let (mut a_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, (lhs.size() + di) / digits);
599
600 (0..cols).for_each(|col_i| {
601 module.vec_znx_dft(digits, digits - 1 - di, &mut a_dft, col_i, &lhs.data, col_i);
602 });
603
604 if di == 0 {
605 module.vmp_apply(&mut res_dft, &a_dft, &rhs.data, scratch2);
606 } else {
607 module.vmp_apply_add(&mut res_dft, &a_dft, &rhs.data, di, scratch2);
608 }
609 });
610 }
611
612 let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
613
614 (0..cols).for_each(|i| {
615 module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
616 });
617 }
618
619 pub fn external_product_inplace<DataRhs: AsRef<[u8]>>(
620 &mut self,
621 module: &Module<FFT64>,
622 rhs: &GGSWCiphertext<DataRhs, FFT64>,
623 scratch: &mut Scratch,
624 ) {
625 unsafe {
626 let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
627 self.external_product(&module, &*self_ptr, rhs, scratch);
628 }
629 }
630
631 pub(crate) fn encrypt_sk_private<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
632 &mut self,
633 module: &Module<FFT64>,
634 pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
635 sk: &GLWESecret<DataSk, FFT64>,
636 source_xa: &mut Source,
637 source_xe: &mut Source,
638 sigma: f64,
639 scratch: &mut Scratch,
640 ) {
641 #[cfg(debug_assertions)]
642 {
643 assert_eq!(self.rank(), sk.rank());
644 assert_eq!(sk.n(), module.n());
645 assert_eq!(self.n(), module.n());
646 if let Some((pt, col)) = pt {
647 assert_eq!(pt.n(), module.n());
648 assert!(col < self.rank() + 1);
649 }
650 assert!(
651 scratch.available() >= GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k()),
652 "scratch.available(): {} < GLWECiphertext::encrypt_sk_scratch_space: {}",
653 scratch.available(),
654 GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k())
655 )
656 }
657
658 let basek: usize = self.basek();
659 let k: usize = self.k();
660 let size: usize = self.size();
661 let cols: usize = self.rank() + 1;
662
663 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx(module, 1, size);
664 c0_big.zero();
665
666 {
667 // c[i] = uniform
668 // c[0] -= c[i] * s[i],
669 (1..cols).for_each(|i| {
670 let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size);
671
672 // c[i] = uniform
673 self.data.fill_uniform(basek, i, size, source_xa);
674
675 // c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i])))
676 module.vec_znx_dft(1, 0, &mut ci_dft, 0, &self.data, i);
677 module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1);
678 let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft);
679
680 // use c[0] as buffer, which is overwritten later by the normalization step
681 module.vec_znx_big_normalize(basek, &mut self.data, 0, &ci_big, 0, scratch_2);
682
683 // c0_tmp = -c[i] * s[i] (use c[0] as buffer)
684 module.vec_znx_sub_ab_inplace(&mut c0_big, 0, &self.data, 0);
685
686 // c[i] += m if col = i
687 if let Some((pt, col)) = pt {
688 if i == col {
689 module.vec_znx_add_inplace(&mut self.data, i, &pt.data, 0);
690 module.vec_znx_normalize_inplace(basek, &mut self.data, i, scratch_2);
691 }
692 }
693 });
694 }
695
696 // c[0] += e
697 c0_big.add_normal(basek, 0, k, source_xe, sigma, sigma * SIX_SIGMA);
698
699 // c[0] += m if col = 0
700 if let Some((pt, col)) = pt {
701 if col == 0 {
702 module.vec_znx_add_inplace(&mut c0_big, 0, &pt.data, 0);
703 }
704 }
705
706 // c[0] = norm(c[0])
707 module.vec_znx_normalize(basek, &mut self.data, 0, &c0_big, 0, scratch_1);
708 }
709
710 pub(crate) fn encrypt_pk_private<DataPt: AsRef<[u8]>, DataPk: AsRef<[u8]>>(
711 &mut self,
712 module: &Module<FFT64>,
713 pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
714 pk: &GLWEPublicKey<DataPk, FFT64>,
715 source_xu: &mut Source,
716 source_xe: &mut Source,
717 sigma: f64,
718 scratch: &mut Scratch,
719 ) {
720 #[cfg(debug_assertions)]
721 {
722 assert_eq!(self.basek(), pk.basek());
723 assert_eq!(self.n(), module.n());
724 assert_eq!(pk.n(), module.n());
725 assert_eq!(self.rank(), pk.rank());
726 if let Some((pt, _)) = pt {
727 assert_eq!(pt.basek(), pk.basek());
728 assert_eq!(pt.n(), module.n());
729 }
730 }
731
732 let basek: usize = pk.basek();
733 let size_pk: usize = pk.size();
734 let cols: usize = self.rank() + 1;
735
736 // Generates u according to the underlying secret distribution.
737 let (mut u_dft, scratch_1) = scratch.tmp_scalar_znx_dft(module, 1);
738
739 {
740 let (mut u, _) = scratch_1.tmp_scalar_znx(module, 1);
741 match pk.dist {
742 SecretDistribution::NONE => panic!(
743 "invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through \
744 Self::generate"
745 ),
746 SecretDistribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu),
747 SecretDistribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu),
748 SecretDistribution::ZERO => {}
749 }
750
751 module.svp_prepare(&mut u_dft, 0, &u, 0);
752 }
753
754 // ct[i] = pk[i] * u + ei (+ m if col = i)
755 (0..cols).for_each(|i| {
756 let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size_pk);
757 // ci_dft = DFT(u) * DFT(pk[i])
758 module.svp_apply(&mut ci_dft, 0, &u_dft, 0, &pk.data.data, i);
759
760 // ci_big = u * p[i]
761 let mut ci_big = module.vec_znx_idft_consume(ci_dft);
762
763 // ci_big = u * pk[i] + e
764 ci_big.add_normal(basek, 0, pk.k(), source_xe, sigma, sigma * SIX_SIGMA);
765
766 // ci_big = u * pk[i] + e + m (if col = i)
767 if let Some((pt, col)) = pt {
768 if col == i {
769 module.vec_znx_big_add_small_inplace(&mut ci_big, 0, &pt.data, 0);
770 }
771 }
772
773 // ct[i] = norm(ci_big)
774 module.vec_znx_big_normalize(basek, &mut self.data, i, &ci_big, 0, scratch_2);
775 });
776 }
777}
778
779impl<DataSelf: AsRef<[u8]>> GLWECiphertext<DataSelf> {
780 pub fn clone(&self) -> GLWECiphertext<Vec<u8>> {
781 GLWECiphertext {
782 data: self.data.clone(),
783 basek: self.basek(),
784 k: self.k(),
785 }
786 }
787
788 pub fn decrypt<DataPt: AsMut<[u8]> + AsRef<[u8]>, DataSk: AsRef<[u8]>>(
789 &self,
790 module: &Module<FFT64>,
791 pt: &mut GLWEPlaintext<DataPt>,
792 sk: &GLWESecret<DataSk, FFT64>,
793 scratch: &mut Scratch,
794 ) {
795 #[cfg(debug_assertions)]
796 {
797 assert_eq!(self.rank(), sk.rank());
798 assert_eq!(self.n(), module.n());
799 assert_eq!(pt.n(), module.n());
800 assert_eq!(sk.n(), module.n());
801 }
802
803 let cols: usize = self.rank() + 1;
804
805 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, self.size()); // TODO optimize size when pt << ct
806 c0_big.zero();
807
808 {
809 (1..cols).for_each(|i| {
810 // ci_dft = DFT(a[i]) * DFT(s[i])
811 let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct
812 module.vec_znx_dft(1, 0, &mut ci_dft, 0, &self.data, i);
813 module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1);
814 let ci_big = module.vec_znx_idft_consume(ci_dft);
815
816 // c0_big += a[i] * s[i]
817 module.vec_znx_big_add_inplace(&mut c0_big, 0, &ci_big, 0);
818 });
819 }
820
821 // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
822 module.vec_znx_big_add_small_inplace(&mut c0_big, 0, &self.data, 0);
823
824 // pt = norm(BIG(m + e))
825 module.vec_znx_big_normalize(self.basek(), &mut pt.data, 0, &mut c0_big, 0, scratch_1);
826
827 pt.basek = self.basek();
828 pt.k = pt.k().min(self.k());
829 }
830}
831
832pub trait GLWECiphertextToRef {
833 fn to_ref(&self) -> GLWECiphertext<&[u8]>;
834}
835
836impl<D: AsRef<[u8]>> GLWECiphertextToRef for GLWECiphertext<D> {
837 fn to_ref(&self) -> GLWECiphertext<&[u8]> {
838 GLWECiphertext {
839 data: self.data.to_ref(),
840 basek: self.basek,
841 k: self.k,
842 }
843 }
844}
845
846pub trait GLWECiphertextToMut {
847 fn to_mut(&mut self) -> GLWECiphertext<&mut [u8]>;
848}
849
850impl<D: AsMut<[u8]> + AsRef<[u8]>> GLWECiphertextToMut for GLWECiphertext<D> {
851 fn to_mut(&mut self) -> GLWECiphertext<&mut [u8]> {
852 GLWECiphertext {
853 data: self.data.to_mut(),
854 basek: self.basek,
855 k: self.k,
856 }
857 }
858}
859
860impl<D> GLWEOps for GLWECiphertext<D>
861where
862 D: AsRef<[u8]> + AsMut<[u8]>,
863 GLWECiphertext<D>: GLWECiphertextToMut + Infos + SetMetaData,
864{
865}