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