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