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