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, derive_size,
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, derive_size(basek, k)),
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, derive_size(basek, k))
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(&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 = derive_size(basek, k);
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 = derive_size(basek, k);
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 = derive_size(basek, k);
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 out_k: usize,
91 out_rank: usize,
92 in_k: usize,
93 in_rank: usize,
94 ksk_k: usize,
95 ) -> usize {
96 let res_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, out_rank);
97 let in_size: usize = derive_size(basek, in_k);
98 let out_size: usize = derive_size(basek, out_k);
99 let ksk_size: usize = derive_size(basek, ksk_k);
100 let vmp: usize = module.vmp_apply_tmp_bytes(out_size, in_size, in_size, in_rank, out_rank + 1, ksk_size)
101 + module.bytes_of_vec_znx_dft(in_rank, in_size);
102 let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
103 return res_dft + (vmp | normalize);
104 }
105
106 pub fn keyswitch_from_fourier_scratch_space(
107 module: &Module<FFT64>,
108 basek: usize,
109 out_k: usize,
110 out_rank: usize,
111 in_k: usize,
112 in_rank: usize,
113 ksk_k: usize,
114 ) -> usize {
115 Self::keyswitch_scratch_space(module, basek, out_k, out_rank, in_k, in_rank, ksk_k)
116 }
117
118 pub fn keyswitch_inplace_scratch_space(
119 module: &Module<FFT64>,
120 basek: usize,
121 out_k: usize,
122 out_rank: usize,
123 ksk_k: usize,
124 ) -> usize {
125 Self::keyswitch_scratch_space(module, basek, out_k, out_rank, out_k, out_rank, ksk_k)
126 }
127
128 pub fn automorphism_scratch_space(
129 module: &Module<FFT64>,
130 basek: usize,
131 out_k: usize,
132 in_k: usize,
133 atk_k: usize,
134 rank: usize,
135 ) -> usize {
136 Self::keyswitch_scratch_space(module, basek, out_k, rank, in_k, rank, atk_k)
137 }
138
139 pub fn automorphism_inplace_scratch_space(
140 module: &Module<FFT64>,
141 basek: usize,
142 out_k: usize,
143 atk_k: usize,
144 rank: usize,
145 ) -> usize {
146 Self::keyswitch_scratch_space(module, basek, out_k, rank, out_k, rank, atk_k)
147 }
148
149 pub fn external_product_scratch_space(
150 module: &Module<FFT64>,
151 basek: usize,
152 out_k: usize,
153 in_k: usize,
154 ggsw_k: usize,
155 rank: usize,
156 ) -> usize {
157 let res_dft: usize = GLWECiphertextFourier::bytes_of(module, basek, out_k, rank);
158 let in_size: usize = derive_size(basek, in_k);
159 let out_size: usize = derive_size(basek, out_k);
160 let ggsw_size: usize = derive_size(basek, ggsw_k);
161 let vmp: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size)
162 + module.vmp_apply_tmp_bytes(
163 out_size,
164 in_size,
165 in_size, // rows
166 rank + 1, // cols in
167 rank + 1, // cols out
168 ggsw_size,
169 );
170 let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
171 res_dft + (vmp | normalize)
172 }
173
174 pub fn external_product_inplace_scratch_space(
175 module: &Module<FFT64>,
176 basek: usize,
177 out_k: usize,
178 ggsw_k: usize,
179 rank: usize,
180 ) -> usize {
181 Self::external_product_scratch_space(module, basek, out_k, out_k, ggsw_k, rank)
182 }
183}
184
185impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> SetMetaData for GLWECiphertext<DataSelf> {
186 fn set_k(&mut self, k: usize) {
187 self.k = k
188 }
189
190 fn set_basek(&mut self, basek: usize) {
191 self.basek = basek
192 }
193}
194
195impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
196 pub fn encrypt_sk<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
197 &mut self,
198 module: &Module<FFT64>,
199 pt: &GLWEPlaintext<DataPt>,
200 sk: &GLWESecret<DataSk, FFT64>,
201 source_xa: &mut Source,
202 source_xe: &mut Source,
203 sigma: f64,
204 scratch: &mut Scratch,
205 ) {
206 self.encrypt_sk_private(
207 module,
208 Some((pt, 0)),
209 sk,
210 source_xa,
211 source_xe,
212 sigma,
213 scratch,
214 );
215 }
216
217 pub fn encrypt_zero_sk<DataSk: AsRef<[u8]>>(
218 &mut self,
219 module: &Module<FFT64>,
220 sk: &GLWESecret<DataSk, FFT64>,
221 source_xa: &mut Source,
222 source_xe: &mut Source,
223 sigma: f64,
224 scratch: &mut Scratch,
225 ) {
226 self.encrypt_sk_private(
227 module,
228 None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
229 sk,
230 source_xa,
231 source_xe,
232 sigma,
233 scratch,
234 );
235 }
236
237 pub fn encrypt_pk<DataPt: AsRef<[u8]>, DataPk: AsRef<[u8]>>(
238 &mut self,
239 module: &Module<FFT64>,
240 pt: &GLWEPlaintext<DataPt>,
241 pk: &GLWEPublicKey<DataPk, FFT64>,
242 source_xu: &mut Source,
243 source_xe: &mut Source,
244 sigma: f64,
245 scratch: &mut Scratch,
246 ) {
247 self.encrypt_pk_private(
248 module,
249 Some((pt, 0)),
250 pk,
251 source_xu,
252 source_xe,
253 sigma,
254 scratch,
255 );
256 }
257
258 pub fn encrypt_zero_pk<DataPk: AsRef<[u8]>>(
259 &mut self,
260 module: &Module<FFT64>,
261 pk: &GLWEPublicKey<DataPk, FFT64>,
262 source_xu: &mut Source,
263 source_xe: &mut Source,
264 sigma: f64,
265 scratch: &mut Scratch,
266 ) {
267 self.encrypt_pk_private(
268 module,
269 None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
270 pk,
271 source_xu,
272 source_xe,
273 sigma,
274 scratch,
275 );
276 }
277
278 pub fn automorphism<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
279 &mut self,
280 module: &Module<FFT64>,
281 lhs: &GLWECiphertext<DataLhs>,
282 rhs: &AutomorphismKey<DataRhs, FFT64>,
283 scratch: &mut Scratch,
284 ) {
285 self.keyswitch(module, lhs, &rhs.key, scratch);
286 (0..self.rank() + 1).for_each(|i| {
287 module.vec_znx_automorphism_inplace(rhs.p(), &mut self.data, i);
288 })
289 }
290
291 pub fn automorphism_inplace<DataRhs: AsRef<[u8]>>(
292 &mut self,
293 module: &Module<FFT64>,
294 rhs: &AutomorphismKey<DataRhs, FFT64>,
295 scratch: &mut Scratch,
296 ) {
297 self.keyswitch_inplace(module, &rhs.key, scratch);
298 (0..self.rank() + 1).for_each(|i| {
299 module.vec_znx_automorphism_inplace(rhs.p(), &mut self.data, i);
300 })
301 }
302
303 pub fn automorphism_add<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
304 &mut self,
305 module: &Module<FFT64>,
306 lhs: &GLWECiphertext<DataLhs>,
307 rhs: &AutomorphismKey<DataRhs, FFT64>,
308 scratch: &mut Scratch,
309 ) {
310 Self::keyswitch_private::<_, _, 1>(self, rhs.p(), module, lhs, &rhs.key, scratch);
311 }
312
313 pub fn automorphism_add_inplace<DataRhs: AsRef<[u8]>>(
314 &mut self,
315 module: &Module<FFT64>,
316 rhs: &AutomorphismKey<DataRhs, FFT64>,
317 scratch: &mut Scratch,
318 ) {
319 unsafe {
320 let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
321 Self::keyswitch_private::<_, _, 1>(self, rhs.p(), module, &*self_ptr, &rhs.key, scratch);
322 }
323 }
324
325 pub fn automorphism_sub_ab<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
326 &mut self,
327 module: &Module<FFT64>,
328 lhs: &GLWECiphertext<DataLhs>,
329 rhs: &AutomorphismKey<DataRhs, FFT64>,
330 scratch: &mut Scratch,
331 ) {
332 Self::keyswitch_private::<_, _, 2>(self, rhs.p(), module, lhs, &rhs.key, scratch);
333 }
334
335 pub fn automorphism_sub_ab_inplace<DataRhs: AsRef<[u8]>>(
336 &mut self,
337 module: &Module<FFT64>,
338 rhs: &AutomorphismKey<DataRhs, FFT64>,
339 scratch: &mut Scratch,
340 ) {
341 unsafe {
342 let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
343 Self::keyswitch_private::<_, _, 2>(self, rhs.p(), module, &*self_ptr, &rhs.key, scratch);
344 }
345 }
346
347 pub fn automorphism_sub_ba<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
348 &mut self,
349 module: &Module<FFT64>,
350 lhs: &GLWECiphertext<DataLhs>,
351 rhs: &AutomorphismKey<DataRhs, FFT64>,
352 scratch: &mut Scratch,
353 ) {
354 Self::keyswitch_private::<_, _, 3>(self, rhs.p(), module, lhs, &rhs.key, scratch);
355 }
356
357 pub fn automorphism_sub_ba_inplace<DataRhs: AsRef<[u8]>>(
358 &mut self,
359 module: &Module<FFT64>,
360 rhs: &AutomorphismKey<DataRhs, FFT64>,
361 scratch: &mut Scratch,
362 ) {
363 unsafe {
364 let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
365 Self::keyswitch_private::<_, _, 3>(self, rhs.p(), module, &*self_ptr, &rhs.key, scratch);
366 }
367 }
368
369 pub(crate) fn keyswitch_from_fourier<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
370 &mut self,
371 module: &Module<FFT64>,
372 lhs: &GLWECiphertextFourier<DataLhs, FFT64>,
373 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
374 scratch: &mut Scratch,
375 ) {
376 let basek: usize = self.basek();
377
378 #[cfg(debug_assertions)]
379 {
380 assert_eq!(lhs.rank(), rhs.rank_in());
381 assert_eq!(self.rank(), rhs.rank_out());
382 assert_eq!(self.basek(), basek);
383 assert_eq!(lhs.basek(), basek);
384 assert_eq!(rhs.n(), module.n());
385 assert_eq!(self.n(), module.n());
386 assert_eq!(lhs.n(), module.n());
387 assert!(
388 scratch.available()
389 >= GLWECiphertext::keyswitch_from_fourier_scratch_space(
390 module,
391 self.basek(),
392 self.k(),
393 self.rank(),
394 lhs.k(),
395 lhs.rank(),
396 rhs.k(),
397 )
398 );
399 }
400
401 let cols_in: usize = rhs.rank_in();
402 let cols_out: usize = rhs.rank_out() + 1;
403
404 // Buffer of the result of VMP in DFT
405 let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, rhs.size()); // Todo optimise
406
407 {
408 // Applies VMP
409 let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, lhs.size());
410 (0..cols_in).for_each(|col_i| {
411 module.vec_znx_dft_copy(&mut ai_dft, col_i, &lhs.data, col_i + 1);
412 });
413 module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2);
414 }
415
416 module.vec_znx_dft_add_inplace(&mut res_dft, 0, &lhs.data, 0);
417
418 // Switches result of VMP outside of DFT
419 let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume::<&mut [u8]>(res_dft);
420
421 (0..cols_out).for_each(|i| {
422 module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
423 });
424 }
425
426 pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
427 &mut self,
428 module: &Module<FFT64>,
429 lhs: &GLWECiphertext<DataLhs>,
430 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
431 scratch: &mut Scratch,
432 ) {
433 Self::keyswitch_private::<_, _, 0>(self, 0, module, lhs, rhs, scratch);
434 }
435
436 pub(crate) fn keyswitch_private<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>, const OP: u8>(
437 &mut self,
438 apply_auto: i64,
439 module: &Module<FFT64>,
440 lhs: &GLWECiphertext<DataLhs>,
441 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
442 scratch: &mut Scratch,
443 ) {
444 let basek: usize = self.basek();
445
446 #[cfg(debug_assertions)]
447 {
448 assert_eq!(lhs.rank(), rhs.rank_in());
449 assert_eq!(self.rank(), rhs.rank_out());
450 assert_eq!(self.basek(), basek);
451 assert_eq!(lhs.basek(), basek);
452 assert_eq!(rhs.n(), module.n());
453 assert_eq!(self.n(), module.n());
454 assert_eq!(lhs.n(), module.n());
455 assert!(
456 scratch.available()
457 >= GLWECiphertext::keyswitch_scratch_space(
458 module,
459 self.basek(),
460 self.k(),
461 self.rank(),
462 lhs.k(),
463 lhs.rank(),
464 rhs.k(),
465 )
466 );
467 }
468
469 let cols_in: usize = rhs.rank_in();
470 let cols_out: usize = rhs.rank_out() + 1;
471
472 let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols_out, rhs.size()); // Todo optimise
473
474 {
475 let (mut ai_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols_in, lhs.size());
476 (0..cols_in).for_each(|col_i| {
477 module.vec_znx_dft(&mut ai_dft, col_i, &lhs.data, col_i + 1);
478 });
479 module.vmp_apply(&mut res_dft, &ai_dft, &rhs.0.data, scratch2);
480 }
481
482 let mut res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
483
484 module.vec_znx_big_add_small_inplace(&mut res_big, 0, &lhs.data, 0);
485
486 (0..cols_out).for_each(|i| {
487 if apply_auto != 0 {
488 module.vec_znx_big_automorphism_inplace(apply_auto, &mut res_big, i);
489 }
490
491 match OP {
492 1 => module.vec_znx_big_add_small_inplace(&mut res_big, i, &lhs.data, i),
493 2 => module.vec_znx_big_sub_small_a_inplace(&mut res_big, i, &lhs.data, i),
494 3 => module.vec_znx_big_sub_small_b_inplace(&mut res_big, i, &lhs.data, i),
495 _ => {}
496 }
497 module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
498 });
499 }
500
501 pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>(
502 &mut self,
503 module: &Module<FFT64>,
504 rhs: &GLWESwitchingKey<DataRhs, FFT64>,
505 scratch: &mut Scratch,
506 ) {
507 unsafe {
508 let self_ptr: *mut GLWECiphertext<DataSelf> = self as *mut GLWECiphertext<DataSelf>;
509 self.keyswitch(&module, &*self_ptr, rhs, scratch);
510 }
511 }
512
513 pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
514 &mut self,
515 module: &Module<FFT64>,
516 lhs: &GLWECiphertext<DataLhs>,
517 rhs: &GGSWCiphertext<DataRhs, FFT64>,
518 scratch: &mut Scratch,
519 ) {
520 let basek: usize = self.basek();
521
522 #[cfg(debug_assertions)]
523 {
524 assert_eq!(rhs.rank(), lhs.rank());
525 assert_eq!(rhs.rank(), self.rank());
526 assert_eq!(self.basek(), basek);
527 assert_eq!(lhs.basek(), basek);
528 assert_eq!(rhs.n(), module.n());
529 assert_eq!(self.n(), module.n());
530 assert_eq!(lhs.n(), module.n());
531 }
532
533 let cols: usize = rhs.rank() + 1;
534
535 let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols, rhs.size()); // Todo optimise
536
537 {
538 let (mut a_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, lhs.size());
539 (0..cols).for_each(|col_i| {
540 module.vec_znx_dft(&mut a_dft, col_i, &lhs.data, col_i);
541 });
542 module.vmp_apply(&mut res_dft, &a_dft, &rhs.data, scratch2);
543 }
544
545 let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(res_dft);
546
547 (0..cols).for_each(|i| {
548 module.vec_znx_big_normalize(basek, &mut self.data, i, &res_big, i, scratch1);
549 });
550 }
551
552 pub fn external_product_inplace<DataRhs: AsRef<[u8]>>(
553 &mut self,
554 module: &Module<FFT64>,
555 rhs: &GGSWCiphertext<DataRhs, FFT64>,
556 scratch: &mut Scratch,
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: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
565 &mut self,
566 module: &Module<FFT64>,
567 pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
568 sk: &GLWESecret<DataSk, FFT64>,
569 source_xa: &mut Source,
570 source_xe: &mut Source,
571 sigma: f64,
572 scratch: &mut Scratch,
573 ) {
574 #[cfg(debug_assertions)]
575 {
576 assert_eq!(self.rank(), sk.rank());
577 assert_eq!(sk.n(), module.n());
578 assert_eq!(self.n(), module.n());
579 if let Some((pt, col)) = pt {
580 assert_eq!(pt.n(), module.n());
581 assert!(col < self.rank() + 1);
582 }
583 assert!(
584 scratch.available() >= GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k()),
585 "scratch.available(): {} < GLWECiphertext::encrypt_sk_scratch_space: {}",
586 scratch.available(),
587 GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k())
588 )
589 }
590
591 let basek: usize = self.basek();
592 let k: usize = self.k();
593 let size: usize = self.size();
594 let cols: usize = self.rank() + 1;
595
596 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx(module, 1, size);
597 c0_big.zero();
598
599 {
600 // c[i] = uniform
601 // c[0] -= c[i] * s[i],
602 (1..cols).for_each(|i| {
603 let (mut ci_dft, scratch_2) = scratch_1.tmp_vec_znx_dft(module, 1, size);
604
605 // c[i] = uniform
606 self.data.fill_uniform(basek, i, size, source_xa);
607
608 // c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i])))
609 module.vec_znx_dft(&mut ci_dft, 0, &self.data, i);
610 module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1);
611 let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft);
612
613 // use c[0] as buffer, which is overwritten later by the normalization step
614 module.vec_znx_big_normalize(basek, &mut self.data, 0, &ci_big, 0, scratch_2);
615
616 // c0_tmp = -c[i] * s[i] (use c[0] as buffer)
617 module.vec_znx_sub_ab_inplace(&mut c0_big, 0, &self.data, 0);
618
619 // c[i] += m if col = i
620 if let Some((pt, col)) = pt {
621 if i == col {
622 module.vec_znx_add_inplace(&mut self.data, i, &pt.data, 0);
623 module.vec_znx_normalize_inplace(basek, &mut self.data, i, scratch_2);
624 }
625 }
626 });
627 }
628
629 // c[0] += e
630 c0_big.add_normal(basek, 0, k, source_xe, sigma, sigma * SIX_SIGMA);
631
632 // c[0] += m if col = 0
633 if let Some((pt, col)) = pt {
634 if col == 0 {
635 module.vec_znx_add_inplace(&mut c0_big, 0, &pt.data, 0);
636 }
637 }
638
639 // c[0] = norm(c[0])
640 module.vec_znx_normalize(basek, &mut self.data, 0, &c0_big, 0, scratch_1);
641 }
642
643 pub(crate) fn encrypt_pk_private<DataPt: AsRef<[u8]>, DataPk: AsRef<[u8]>>(
644 &mut self,
645 module: &Module<FFT64>,
646 pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
647 pk: &GLWEPublicKey<DataPk, FFT64>,
648 source_xu: &mut Source,
649 source_xe: &mut Source,
650 sigma: f64,
651 scratch: &mut Scratch,
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.data.data, 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.data, 0);
703 }
704 }
705
706 // ct[i] = norm(ci_big)
707 module.vec_znx_big_normalize(basek, &mut self.data, i, &ci_big, 0, scratch_2);
708 });
709 }
710}
711
712impl<DataSelf: AsRef<[u8]>> GLWECiphertext<DataSelf> {
713 pub fn clone(&self) -> GLWECiphertext<Vec<u8>> {
714 GLWECiphertext {
715 data: self.data.clone(),
716 basek: self.basek(),
717 k: self.k(),
718 }
719 }
720
721 pub fn decrypt<DataPt: AsMut<[u8]> + AsRef<[u8]>, DataSk: AsRef<[u8]>>(
722 &self,
723 module: &Module<FFT64>,
724 pt: &mut GLWEPlaintext<DataPt>,
725 sk: &GLWESecret<DataSk, FFT64>,
726 scratch: &mut Scratch,
727 ) {
728 #[cfg(debug_assertions)]
729 {
730 assert_eq!(self.rank(), sk.rank());
731 assert_eq!(self.n(), module.n());
732 assert_eq!(pt.n(), module.n());
733 assert_eq!(sk.n(), module.n());
734 }
735
736 let cols: usize = self.rank() + 1;
737
738 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, self.size()); // TODO optimize size when pt << ct
739 c0_big.zero();
740
741 {
742 (1..cols).for_each(|i| {
743 // ci_dft = DFT(a[i]) * DFT(s[i])
744 let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct
745 module.vec_znx_dft(&mut ci_dft, 0, &self.data, i);
746 module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1);
747 let ci_big = module.vec_znx_idft_consume(ci_dft);
748
749 // c0_big += a[i] * s[i]
750 module.vec_znx_big_add_inplace(&mut c0_big, 0, &ci_big, 0);
751 });
752 }
753
754 // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
755 module.vec_znx_big_add_small_inplace(&mut c0_big, 0, &self.data, 0);
756
757 // pt = norm(BIG(m + e))
758 module.vec_znx_big_normalize(self.basek(), &mut pt.data, 0, &mut c0_big, 0, scratch_1);
759
760 pt.basek = self.basek();
761 pt.k = pt.k().min(self.k());
762 }
763}
764
765pub trait GLWECiphertextToRef {
766 fn to_ref(&self) -> GLWECiphertext<&[u8]>;
767}
768
769impl<D: AsRef<[u8]>> GLWECiphertextToRef for GLWECiphertext<D> {
770 fn to_ref(&self) -> GLWECiphertext<&[u8]> {
771 GLWECiphertext {
772 data: self.data.to_ref(),
773 basek: self.basek,
774 k: self.k,
775 }
776 }
777}
778
779pub trait GLWECiphertextToMut {
780 fn to_mut(&mut self) -> GLWECiphertext<&mut [u8]>;
781}
782
783impl<D: AsMut<[u8]> + AsRef<[u8]>> GLWECiphertextToMut for GLWECiphertext<D> {
784 fn to_mut(&mut self) -> GLWECiphertext<&mut [u8]> {
785 GLWECiphertext {
786 data: self.data.to_mut(),
787 basek: self.basek,
788 k: self.k,
789 }
790 }
791}
792
793impl<D> GLWEOps for GLWECiphertext<D>
794where
795 D: AsRef<[u8]> + AsMut<[u8]>,
796 GLWECiphertext<D>: GLWECiphertextToMut + Infos + SetMetaData,
797{
798}