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