poulpy/rlwe/src/elem_rlwe.rs
rlwe/src/elem_rlwe.rsRaw
1use base2k::{
2 AddNormal, Backend, FFT64, FillUniform, Module, ScalarZnxAlloc, ScalarZnxDft, ScalarZnxDftAlloc, ScalarZnxDftOps,
3 ScalarZnxDftToRef, Scratch, VecZnx, VecZnxAlloc, VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDft, VecZnxDftAlloc,
4 VecZnxDftOps, VecZnxDftToMut, VecZnxDftToRef, VecZnxOps, VecZnxToMut, VecZnxToRef,
5};
6use sampling::source::Source;
7
8use crate::{
9 elem::Infos,
10 keys::{PublicKey, SecretDistribution, SecretKeyDft},
11 utils::derive_size,
12};
13
14pub struct RLWECt<C> {
15 pub data: VecZnx<C>,
16 pub log_base2k: usize,
17 pub log_k: usize,
18}
19
20impl RLWECt<Vec<u8>> {
21 pub fn new<B: Backend>(module: &Module<B>, log_base2k: usize, log_k: usize, cols: usize) -> Self {
22 Self {
23 data: module.new_vec_znx(cols, derive_size(log_base2k, log_k)),
24 log_base2k: log_base2k,
25 log_k: log_k,
26 }
27 }
28}
29
30impl<T> Infos for RLWECt<T> {
31 type Inner = VecZnx<T>;
32
33 fn inner(&self) -> &Self::Inner {
34 &self.data
35 }
36
37 fn log_base2k(&self) -> usize {
38 self.log_base2k
39 }
40
41 fn log_k(&self) -> usize {
42 self.log_k
43 }
44}
45
46impl<C> VecZnxToMut for RLWECt<C>
47where
48 VecZnx<C>: VecZnxToMut,
49{
50 fn to_mut(&mut self) -> VecZnx<&mut [u8]> {
51 self.data.to_mut()
52 }
53}
54
55impl<C> VecZnxToRef for RLWECt<C>
56where
57 VecZnx<C>: VecZnxToRef,
58{
59 fn to_ref(&self) -> VecZnx<&[u8]> {
60 self.data.to_ref()
61 }
62}
63
64pub struct RLWEPt<C> {
65 pub data: VecZnx<C>,
66 pub log_base2k: usize,
67 pub log_k: usize,
68}
69
70impl<T> Infos for RLWEPt<T> {
71 type Inner = VecZnx<T>;
72
73 fn inner(&self) -> &Self::Inner {
74 &self.data
75 }
76
77 fn log_base2k(&self) -> usize {
78 self.log_base2k
79 }
80
81 fn log_k(&self) -> usize {
82 self.log_k
83 }
84}
85
86impl<C> VecZnxToMut for RLWEPt<C>
87where
88 VecZnx<C>: VecZnxToMut,
89{
90 fn to_mut(&mut self) -> VecZnx<&mut [u8]> {
91 self.data.to_mut()
92 }
93}
94
95impl<C> VecZnxToRef for RLWEPt<C>
96where
97 VecZnx<C>: VecZnxToRef,
98{
99 fn to_ref(&self) -> VecZnx<&[u8]> {
100 self.data.to_ref()
101 }
102}
103
104impl RLWEPt<Vec<u8>> {
105 pub fn new<B: Backend>(module: &Module<B>, log_base2k: usize, log_k: usize) -> Self {
106 Self {
107 data: module.new_vec_znx(1, derive_size(log_base2k, log_k)),
108 log_base2k: log_base2k,
109 log_k: log_k,
110 }
111 }
112}
113
114pub struct RLWECtDft<C, B: Backend> {
115 pub data: VecZnxDft<C, B>,
116 pub log_base2k: usize,
117 pub log_k: usize,
118}
119
120impl<B: Backend> RLWECtDft<Vec<u8>, B> {
121 pub fn new(module: &Module<B>, log_base2k: usize, log_k: usize, cols: usize) -> Self {
122 Self {
123 data: module.new_vec_znx_dft(cols, derive_size(log_base2k, log_k)),
124 log_base2k: log_base2k,
125 log_k: log_k,
126 }
127 }
128}
129
130impl<T, B: Backend> Infos for RLWECtDft<T, B> {
131 type Inner = VecZnxDft<T, B>;
132
133 fn inner(&self) -> &Self::Inner {
134 &self.data
135 }
136
137 fn log_base2k(&self) -> usize {
138 self.log_base2k
139 }
140
141 fn log_k(&self) -> usize {
142 self.log_k
143 }
144}
145
146impl<C, B: Backend> VecZnxDftToMut<B> for RLWECtDft<C, B>
147where
148 VecZnxDft<C, B>: VecZnxDftToMut<B>,
149{
150 fn to_mut(&mut self) -> VecZnxDft<&mut [u8], B> {
151 self.data.to_mut()
152 }
153}
154
155impl<C, B: Backend> VecZnxDftToRef<B> for RLWECtDft<C, B>
156where
157 VecZnxDft<C, B>: VecZnxDftToRef<B>,
158{
159 fn to_ref(&self) -> VecZnxDft<&[u8], B> {
160 self.data.to_ref()
161 }
162}
163
164impl RLWECt<Vec<u8>> {
165 pub fn encrypt_sk_scratch_bytes<B: Backend>(module: &Module<B>, size: usize) -> usize {
166 (module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, size)) + module.bytes_of_vec_znx_big(1, size)
167 }
168
169 pub fn encrypt_pk_scratch_bytes<B: Backend>(module: &Module<B>, pk_size: usize) -> usize {
170 ((module.bytes_of_vec_znx_dft(1, pk_size) + module.bytes_of_vec_znx_big(1, pk_size)) | module.bytes_of_scalar_znx(1))
171 + module.bytes_of_scalar_znx_dft(1)
172 + module.vec_znx_big_normalize_tmp_bytes()
173 }
174
175 pub fn decrypt_scratch_bytes<B: Backend>(module: &Module<B>, size: usize) -> usize {
176 (module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, size)) + module.bytes_of_vec_znx_big(1, size)
177 }
178}
179
180pub fn encrypt_rlwe_sk<C, P, S>(
181 module: &Module<FFT64>,
182 ct: &mut RLWECt<C>,
183 pt: Option<&RLWEPt<P>>,
184 sk_dft: &SecretKeyDft<S, FFT64>,
185 source_xa: &mut Source,
186 source_xe: &mut Source,
187 sigma: f64,
188 bound: f64,
189 scratch: &mut Scratch,
190) where
191 VecZnx<C>: VecZnxToMut + VecZnxToRef,
192 VecZnx<P>: VecZnxToRef,
193 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
194{
195 let log_base2k: usize = ct.log_base2k();
196 let log_k: usize = ct.log_k();
197 let size: usize = ct.size();
198
199 // c1 = a
200 ct.data.fill_uniform(log_base2k, 1, size, source_xa);
201
202 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, size);
203
204 {
205 let (mut c0_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, size);
206 module.vec_znx_dft(&mut c0_dft, 0, ct, 1);
207
208 // c0_dft = DFT(a) * DFT(s)
209 module.svp_apply_inplace(&mut c0_dft, 0, sk_dft, 0);
210
211 // c0_big = IDFT(c0_dft)
212 module.vec_znx_idft_tmp_a(&mut c0_big, 0, &mut c0_dft, 0);
213 }
214
215 // c0_big = m - c0_big
216 if let Some(pt) = pt {
217 module.vec_znx_big_sub_small_b_inplace(&mut c0_big, 0, pt, 0);
218 }
219 // c0_big += e
220 c0_big.add_normal(log_base2k, 0, log_k, source_xe, sigma, bound);
221
222 // c0 = norm(c0_big = -as + m + e)
223 module.vec_znx_big_normalize(log_base2k, ct, 0, &c0_big, 0, scratch_1);
224}
225
226pub fn decrypt_rlwe<P, C, S>(
227 module: &Module<FFT64>,
228 pt: &mut RLWEPt<P>,
229 ct: &RLWECt<C>,
230 sk_dft: &SecretKeyDft<S, FFT64>,
231 scratch: &mut Scratch,
232) where
233 VecZnx<P>: VecZnxToMut + VecZnxToRef,
234 VecZnx<C>: VecZnxToRef,
235 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
236{
237 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, ct.size()); // TODO optimize size when pt << ct
238
239 {
240 let (mut c0_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, ct.size()); // TODO optimize size when pt << ct
241 module.vec_znx_dft(&mut c0_dft, 0, ct, 1);
242
243 // c0_dft = DFT(a) * DFT(s)
244 module.svp_apply_inplace(&mut c0_dft, 0, sk_dft, 0);
245
246 // c0_big = IDFT(c0_dft)
247 module.vec_znx_idft_tmp_a(&mut c0_big, 0, &mut c0_dft, 0);
248 }
249
250 // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
251 module.vec_znx_big_add_small_inplace(&mut c0_big, 0, ct, 0);
252
253 // pt = norm(BIG(m + e))
254 module.vec_znx_big_normalize(ct.log_base2k(), pt, 0, &mut c0_big, 0, scratch_1);
255
256 pt.log_base2k = ct.log_base2k();
257 pt.log_k = pt.log_k().min(ct.log_k());
258}
259
260impl<C> RLWECt<C> {
261 pub fn encrypt_sk<P, S>(
262 &mut self,
263 module: &Module<FFT64>,
264 pt: Option<&RLWEPt<P>>,
265 sk_dft: &SecretKeyDft<S, FFT64>,
266 source_xa: &mut Source,
267 source_xe: &mut Source,
268 sigma: f64,
269 bound: f64,
270 scratch: &mut Scratch,
271 ) where
272 VecZnx<C>: VecZnxToMut + VecZnxToRef,
273 VecZnx<P>: VecZnxToRef,
274 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
275 {
276 encrypt_rlwe_sk(
277 module, self, pt, sk_dft, source_xa, source_xe, sigma, bound, scratch,
278 )
279 }
280
281 pub fn decrypt<P, S>(
282 &self,
283 module: &Module<FFT64>,
284 pt: &mut RLWEPt<P>,
285 sk_dft: &SecretKeyDft<S, FFT64>,
286 scratch: &mut Scratch,
287 ) where
288 VecZnx<P>: VecZnxToMut + VecZnxToRef,
289 VecZnx<C>: VecZnxToRef,
290 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
291 {
292 decrypt_rlwe(module, pt, self, sk_dft, scratch);
293 }
294
295 pub fn encrypt_pk<P, S>(
296 &mut self,
297 module: &Module<FFT64>,
298 pt: Option<&RLWEPt<P>>,
299 pk: &PublicKey<S, FFT64>,
300 source_xu: &mut Source,
301 source_xe: &mut Source,
302 sigma: f64,
303 bound: f64,
304 scratch: &mut Scratch,
305 ) where
306 VecZnx<C>: VecZnxToMut + VecZnxToRef,
307 VecZnx<P>: VecZnxToRef,
308 VecZnxDft<S, FFT64>: VecZnxDftToRef<FFT64>,
309 {
310 encrypt_rlwe_pk(
311 module, self, pt, pk, source_xu, source_xe, sigma, bound, scratch,
312 )
313 }
314}
315
316pub(crate) fn encrypt_zero_rlwe_dft_sk<C, S>(
317 module: &Module<FFT64>,
318 ct: &mut RLWECtDft<C, FFT64>,
319 sk: &SecretKeyDft<S, FFT64>,
320 source_xa: &mut Source,
321 source_xe: &mut Source,
322 sigma: f64,
323 bound: f64,
324 scratch: &mut Scratch,
325) where
326 VecZnxDft<C, FFT64>: VecZnxDftToMut<FFT64> + VecZnxDftToRef<FFT64>,
327 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
328{
329 let log_base2k: usize = ct.log_base2k();
330 let log_k: usize = ct.log_k();
331 let size: usize = ct.size();
332
333 #[cfg(debug_assertions)]
334 {
335 match sk.dist {
336 SecretDistribution::NONE => panic!("invalid sk.dist = SecretDistribution::NONE"),
337 _ => {}
338 }
339 assert_eq!(ct.cols(), 2);
340 }
341
342 // ct[1] = DFT(a)
343 {
344 let (mut tmp_znx, _) = scratch.tmp_vec_znx(module, 1, size);
345 tmp_znx.fill_uniform(log_base2k, 0, size, source_xa);
346 module.vec_znx_dft(ct, 1, &tmp_znx, 0);
347 }
348
349 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, size);
350
351 {
352 let (mut tmp_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, size);
353 // c0_dft = ct[1] * DFT(s)
354 module.svp_apply(&mut tmp_dft, 0, sk, 0, ct, 1);
355
356 // c0_big = IDFT(c0_dft)
357 module.vec_znx_idft_tmp_a(&mut c0_big, 0, &mut tmp_dft, 0);
358 }
359
360 // c0_big += e
361 c0_big.add_normal(log_base2k, 0, log_k, source_xe, sigma, bound);
362
363 // c0 = norm(c0_big = -as - e), NOTE: e is centered at 0.
364 let (mut tmp_znx, scratch_2) = scratch_1.tmp_vec_znx(module, 1, size);
365 module.vec_znx_big_normalize(log_base2k, &mut tmp_znx, 0, &c0_big, 0, scratch_2);
366 module.vec_znx_negate_inplace(&mut tmp_znx, 0);
367 // ct[0] = DFT(-as + e)
368 module.vec_znx_dft(ct, 0, &tmp_znx, 0);
369}
370
371impl RLWECtDft<Vec<u8>, FFT64> {
372 pub fn encrypt_zero_sk_scratch_bytes(module: &Module<FFT64>, size: usize) -> usize {
373 (module.bytes_of_vec_znx(1, size) | module.bytes_of_vec_znx_dft(1, size))
374 + module.bytes_of_vec_znx_big(1, size)
375 + module.bytes_of_vec_znx(1, size)
376 + module.vec_znx_big_normalize_tmp_bytes()
377 }
378
379 pub fn decrypt_scratch_bytes(module: &Module<FFT64>, size: usize) -> usize {
380 (module.vec_znx_big_normalize_tmp_bytes()
381 | module.bytes_of_vec_znx_dft(1, size)
382 | (module.bytes_of_vec_znx_big(1, size) + module.vec_znx_idft_tmp_bytes()))
383 + module.bytes_of_vec_znx_big(1, size)
384 }
385}
386
387pub fn decrypt_rlwe_dft<P, C, S>(
388 module: &Module<FFT64>,
389 pt: &mut RLWEPt<P>,
390 ct: &RLWECtDft<C, FFT64>,
391 sk: &SecretKeyDft<S, FFT64>,
392 scratch: &mut Scratch,
393) where
394 VecZnx<P>: VecZnxToMut + VecZnxToRef,
395 VecZnxDft<C, FFT64>: VecZnxDftToRef<FFT64>,
396 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
397{
398 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, ct.size()); // TODO optimize size when pt << ct
399
400 {
401 let (mut c0_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, ct.size()); // TODO optimize size when pt << ct
402 // c0_dft = DFT(a) * DFT(s)
403 module.svp_apply(&mut c0_dft, 0, sk, 0, ct, 1);
404 // c0_big = IDFT(c0_dft)
405 module.vec_znx_idft_tmp_a(&mut c0_big, 0, &mut c0_dft, 0);
406 }
407
408 {
409 let (mut c1_big, scratch_2) = scratch_1.tmp_vec_znx_big(module, 1, ct.size());
410 // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
411 module.vec_znx_idft(&mut c1_big, 0, ct, 0, scratch_2);
412 module.vec_znx_big_add_inplace(&mut c0_big, 0, &c1_big, 0);
413 }
414
415 // pt = norm(BIG(m + e))
416 module.vec_znx_big_normalize(ct.log_base2k(), pt, 0, &mut c0_big, 0, scratch_1);
417
418 pt.log_base2k = ct.log_base2k();
419 pt.log_k = pt.log_k().min(ct.log_k());
420}
421
422impl<C> RLWECtDft<C, FFT64> {
423 pub(crate) fn encrypt_zero_sk<S>(
424 &mut self,
425 module: &Module<FFT64>,
426 sk_dft: &SecretKeyDft<S, FFT64>,
427 source_xa: &mut Source,
428 source_xe: &mut Source,
429 sigma: f64,
430 bound: f64,
431 scratch: &mut Scratch,
432 ) where
433 VecZnxDft<C, FFT64>: VecZnxDftToMut<FFT64> + VecZnxDftToRef<FFT64>,
434 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
435 {
436 encrypt_zero_rlwe_dft_sk(
437 module, self, sk_dft, source_xa, source_xe, sigma, bound, scratch,
438 )
439 }
440
441 pub fn decrypt<P, S>(
442 &self,
443 module: &Module<FFT64>,
444 pt: &mut RLWEPt<P>,
445 sk_dft: &SecretKeyDft<S, FFT64>,
446 scratch: &mut Scratch,
447 ) where
448 VecZnx<P>: VecZnxToMut + VecZnxToRef,
449 VecZnxDft<C, FFT64>: VecZnxDftToRef<FFT64>,
450 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
451 {
452 decrypt_rlwe_dft(module, pt, self, sk_dft, scratch);
453 }
454}
455
456pub(crate) fn encrypt_rlwe_pk<C, P, S>(
457 module: &Module<FFT64>,
458 ct: &mut RLWECt<C>,
459 pt: Option<&RLWEPt<P>>,
460 pk: &PublicKey<S, FFT64>,
461 source_xu: &mut Source,
462 source_xe: &mut Source,
463 sigma: f64,
464 bound: f64,
465 scratch: &mut Scratch,
466) where
467 VecZnx<C>: VecZnxToMut + VecZnxToRef,
468 VecZnx<P>: VecZnxToRef,
469 VecZnxDft<S, FFT64>: VecZnxDftToRef<FFT64>,
470{
471 #[cfg(debug_assertions)]
472 {
473 assert_eq!(ct.log_base2k(), pk.log_base2k());
474 assert_eq!(ct.n(), module.n());
475 assert_eq!(pk.n(), module.n());
476 if let Some(pt) = pt {
477 assert_eq!(pt.log_base2k(), pk.log_base2k());
478 assert_eq!(pt.n(), module.n());
479 }
480 }
481
482 let log_base2k: usize = pk.log_base2k();
483 let size_pk: usize = pk.size();
484
485 // Generates u according to the underlying secret distribution.
486 let (mut u_dft, scratch_1) = scratch.tmp_scalar_dft(module, 1);
487
488 {
489 let (mut u, _) = scratch_1.tmp_scalar(module, 1);
490 match pk.dist {
491 SecretDistribution::NONE => panic!(
492 "invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through Self::generate"
493 ),
494 SecretDistribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu),
495 SecretDistribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu),
496 }
497
498 module.svp_prepare(&mut u_dft, 0, &u, 0);
499 }
500
501 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)
502 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)
503
504 // ct[0] = pk[0] * u + m + e0
505 module.svp_apply(&mut tmp_dft, 0, &u_dft, 0, pk, 0);
506 module.vec_znx_idft_tmp_a(&mut tmp_big, 0, &mut tmp_dft, 0);
507 tmp_big.add_normal(log_base2k, 0, pk.log_k(), source_xe, sigma, bound);
508
509 if let Some(pt) = pt {
510 module.vec_znx_big_add_small_inplace(&mut tmp_big, 0, pt, 0);
511 }
512
513 module.vec_znx_big_normalize(log_base2k, ct, 0, &tmp_big, 0, scratch_3);
514
515 // ct[1] = pk[1] * u + e1
516 module.svp_apply(&mut tmp_dft, 0, &u_dft, 0, pk, 1);
517 module.vec_znx_idft_tmp_a(&mut tmp_big, 0, &mut tmp_dft, 0);
518 tmp_big.add_normal(log_base2k, 0, pk.log_k(), source_xe, sigma, bound);
519 module.vec_znx_big_normalize(log_base2k, ct, 1, &tmp_big, 0, scratch_3);
520}
521
522#[cfg(test)]
523mod tests {
524 use base2k::{Decoding, Encoding, FFT64, Module, ScratchOwned, Stats, VecZnxOps, ZnxZero};
525 use itertools::izip;
526 use sampling::source::Source;
527
528 use crate::{
529 elem_rlwe::{Infos, RLWECt, RLWECtDft, RLWEPt},
530 keys::{PublicKey, SecretKey, SecretKeyDft},
531 };
532
533 #[test]
534 fn encrypt_sk_fft64() {
535 let module: Module<FFT64> = Module::<FFT64>::new(32);
536 let log_base2k: usize = 8;
537 let log_k_ct: usize = 54;
538 let log_k_pt: usize = 30;
539
540 let sigma: f64 = 3.2;
541 let bound: f64 = sigma * 6.0;
542
543 let mut ct: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_ct, 2);
544 let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_pt);
545
546 let mut source_xs: Source = Source::new([0u8; 32]);
547 let mut source_xe: Source = Source::new([0u8; 32]);
548 let mut source_xa: Source = Source::new([0u8; 32]);
549
550 let mut scratch: ScratchOwned = ScratchOwned::new(
551 RLWECt::encrypt_sk_scratch_bytes(&module, ct.size()) | RLWECt::decrypt_scratch_bytes(&module, ct.size()),
552 );
553
554 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
555 sk.fill_ternary_prob(0.5, &mut source_xs);
556
557 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
558 sk_dft.dft(&module, &sk);
559
560 let mut data_want: Vec<i64> = vec![0i64; module.n()];
561
562 data_want
563 .iter_mut()
564 .for_each(|x| *x = source_xa.next_i64() & 0xFF);
565
566 pt.data
567 .encode_vec_i64(0, log_base2k, log_k_pt, &data_want, 10);
568
569 ct.encrypt_sk(
570 &module,
571 Some(&pt),
572 &sk_dft,
573 &mut source_xa,
574 &mut source_xe,
575 sigma,
576 bound,
577 scratch.borrow(),
578 );
579
580 pt.data.zero();
581
582 ct.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
583
584 let mut data_have: Vec<i64> = vec![0i64; module.n()];
585
586 pt.data
587 .decode_vec_i64(0, log_base2k, pt.size() * log_base2k, &mut data_have);
588
589 // TODO: properly assert the decryption noise through std(dec(ct) - pt)
590 let scale: f64 = (1 << (pt.size() * log_base2k - log_k_pt)) as f64;
591 izip!(data_want.iter(), data_have.iter()).for_each(|(a, b)| {
592 let b_scaled = (*b as f64) / scale;
593 assert!(
594 (*a as f64 - b_scaled).abs() < 0.1,
595 "{} {}",
596 *a as f64,
597 b_scaled
598 )
599 });
600
601 module.free();
602 }
603
604 #[test]
605 fn encrypt_zero_sk_fft64() {
606 let module: Module<FFT64> = Module::<FFT64>::new(1024);
607 let log_base2k: usize = 8;
608 let log_k_ct: usize = 55;
609
610 let sigma: f64 = 3.2;
611 let bound: f64 = sigma * 6.0;
612
613 let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
614
615 let mut source_xs: Source = Source::new([0u8; 32]);
616 let mut source_xe: Source = Source::new([1u8; 32]);
617 let mut source_xa: Source = Source::new([0u8; 32]);
618
619 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
620 sk.fill_ternary_prob(0.5, &mut source_xs);
621 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
622 sk_dft.dft(&module, &sk);
623
624 let mut ct_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_ct, 2);
625
626 let mut scratch: ScratchOwned = ScratchOwned::new(
627 RLWECtDft::decrypt_scratch_bytes(&module, ct_dft.size())
628 | RLWECtDft::encrypt_zero_sk_scratch_bytes(&module, ct_dft.size()),
629 );
630
631 ct_dft.encrypt_zero_sk(
632 &module,
633 &sk_dft,
634 &mut source_xa,
635 &mut source_xe,
636 sigma,
637 bound,
638 scratch.borrow(),
639 );
640 ct_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
641
642 assert!((sigma - pt.data.std(0, log_base2k) * (log_k_ct as f64).exp2()) <= 0.2);
643 module.free();
644 }
645
646 #[test]
647 fn encrypt_pk_fft64() {
648 let module: Module<FFT64> = Module::<FFT64>::new(32);
649 let log_base2k: usize = 8;
650 let log_k_ct: usize = 54;
651 let log_k_pk: usize = 64;
652
653 let sigma: f64 = 3.2;
654 let bound: f64 = sigma * 6.0;
655
656 let mut ct: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_ct, 2);
657 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
658
659 let mut source_xs: Source = Source::new([0u8; 32]);
660 let mut source_xe: Source = Source::new([0u8; 32]);
661 let mut source_xa: Source = Source::new([0u8; 32]);
662 let mut source_xu: Source = Source::new([0u8; 32]);
663
664 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
665 sk.fill_ternary_prob(0.5, &mut source_xs);
666 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
667 sk_dft.dft(&module, &sk);
668
669 let mut pk: PublicKey<Vec<u8>, FFT64> = PublicKey::new(&module, log_base2k, log_k_pk);
670 pk.generate(
671 &module,
672 &sk_dft,
673 &mut source_xa,
674 &mut source_xe,
675 sigma,
676 bound,
677 );
678
679 let mut scratch: ScratchOwned = ScratchOwned::new(
680 RLWECt::encrypt_sk_scratch_bytes(&module, ct.size())
681 | RLWECt::decrypt_scratch_bytes(&module, ct.size())
682 | RLWECt::encrypt_pk_scratch_bytes(&module, pk.size()),
683 );
684
685 let mut data_want: Vec<i64> = vec![0i64; module.n()];
686
687 data_want
688 .iter_mut()
689 .for_each(|x| *x = source_xa.next_i64() & 0);
690
691 pt_want
692 .data
693 .encode_vec_i64(0, log_base2k, log_k_ct, &data_want, 10);
694
695 ct.encrypt_pk(
696 &module,
697 Some(&pt_want),
698 &pk,
699 &mut source_xu,
700 &mut source_xe,
701 sigma,
702 bound,
703 scratch.borrow(),
704 );
705
706 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
707
708 ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
709
710 module.vec_znx_sub_ab_inplace(&mut pt_want, 0, &pt_have, 0);
711
712 assert!(((1.0f64 / 12.0).sqrt() - pt_want.data.std(0, log_base2k) * (log_k_ct as f64).exp2()).abs() < 0.2);
713
714 module.free();
715 }
716}