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: &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, 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: &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, 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: &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, source_xa, source_xe, sigma, bound, scratch,
278 )
279 }
280
281 pub fn decrypt<P, S>(&self, module: &Module<FFT64>, pt: &mut RLWEPt<P>, sk: &SecretKeyDft<S, FFT64>, scratch: &mut Scratch)
282 where
283 VecZnx<P>: VecZnxToMut + VecZnxToRef,
284 VecZnx<C>: VecZnxToRef,
285 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
286 {
287 decrypt_rlwe(module, pt, self, sk, scratch);
288 }
289
290 pub fn encrypt_pk<P, S>(
291 &mut self,
292 module: &Module<FFT64>,
293 pt: Option<&RLWEPt<P>>,
294 pk: &PublicKey<S, FFT64>,
295 source_xu: &mut Source,
296 source_xe: &mut Source,
297 sigma: f64,
298 bound: f64,
299 scratch: &mut Scratch,
300 ) where
301 VecZnx<C>: VecZnxToMut + VecZnxToRef,
302 VecZnx<P>: VecZnxToRef,
303 VecZnxDft<S, FFT64>: VecZnxDftToRef<FFT64>,
304 {
305 encrypt_rlwe_pk(
306 module, self, pt, pk, source_xu, source_xe, sigma, bound, scratch,
307 )
308 }
309}
310
311pub(crate) fn encrypt_zero_rlwe_dft_sk<C, S>(
312 module: &Module<FFT64>,
313 ct: &mut RLWECtDft<C, FFT64>,
314 sk: &SecretKeyDft<S, FFT64>,
315 source_xa: &mut Source,
316 source_xe: &mut Source,
317 sigma: f64,
318 bound: f64,
319 scratch: &mut Scratch,
320) where
321 VecZnxDft<C, FFT64>: VecZnxDftToMut<FFT64> + VecZnxDftToRef<FFT64>,
322 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
323{
324 let log_base2k: usize = ct.log_base2k();
325 let log_k: usize = ct.log_k();
326 let size: usize = ct.size();
327
328 #[cfg(debug_assertions)]
329 {
330 match sk.dist {
331 SecretDistribution::NONE => panic!("invalid sk.dist = SecretDistribution::NONE"),
332 _ => {}
333 }
334 assert_eq!(ct.cols(), 2);
335 }
336
337 // ct[1] = DFT(a)
338 {
339 let (mut tmp_znx, _) = scratch.tmp_vec_znx(module, 1, size);
340 tmp_znx.fill_uniform(log_base2k, 0, size, source_xa);
341 module.vec_znx_dft(ct, 1, &tmp_znx, 0);
342 }
343
344 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, size);
345
346 {
347 let (mut tmp_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, size);
348 // c0_dft = ct[1] * DFT(s)
349 module.svp_apply(&mut tmp_dft, 0, sk, 0, ct, 1);
350
351 // c0_big = IDFT(c0_dft)
352 module.vec_znx_idft_tmp_a(&mut c0_big, 0, &mut tmp_dft, 0);
353 }
354
355 // c0_big += e
356 c0_big.add_normal(log_base2k, 0, log_k, source_xe, sigma, bound);
357
358 // c0 = norm(c0_big = -as - e), NOTE: e is centered at 0.
359 let (mut tmp_znx, scratch_2) = scratch_1.tmp_vec_znx(module, 1, size);
360 module.vec_znx_big_normalize(log_base2k, &mut tmp_znx, 0, &c0_big, 0, scratch_2);
361 module.vec_znx_negate_inplace(&mut tmp_znx, 0);
362 // ct[0] = DFT(-as + e)
363 module.vec_znx_dft(ct, 0, &tmp_znx, 0);
364}
365
366impl RLWECtDft<Vec<u8>, FFT64> {
367 pub fn encrypt_zero_sk_scratch_bytes(module: &Module<FFT64>, size: usize) -> usize {
368 (module.bytes_of_vec_znx(1, size) | module.bytes_of_vec_znx_dft(1, size))
369 + module.bytes_of_vec_znx_big(1, size)
370 + module.bytes_of_vec_znx(1, size)
371 + module.vec_znx_big_normalize_tmp_bytes()
372 }
373
374 pub fn decrypt_scratch_bytes(module: &Module<FFT64>, size: usize) -> usize {
375 (module.vec_znx_big_normalize_tmp_bytes()
376 | module.bytes_of_vec_znx_dft(1, size)
377 | (module.bytes_of_vec_znx_big(1, size) + module.vec_znx_idft_tmp_bytes()))
378 + module.bytes_of_vec_znx_big(1, size)
379 }
380}
381
382pub fn decrypt_rlwe_dft<P, C, S>(
383 module: &Module<FFT64>,
384 pt: &mut RLWEPt<P>,
385 ct: &RLWECtDft<C, FFT64>,
386 sk: &SecretKeyDft<S, FFT64>,
387 scratch: &mut Scratch,
388) where
389 VecZnx<P>: VecZnxToMut + VecZnxToRef,
390 VecZnxDft<C, FFT64>: VecZnxDftToRef<FFT64>,
391 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
392{
393 let (mut c0_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, ct.size()); // TODO optimize size when pt << ct
394
395 {
396 let (mut c0_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, ct.size()); // TODO optimize size when pt << ct
397 // c0_dft = DFT(a) * DFT(s)
398 module.svp_apply(&mut c0_dft, 0, sk, 0, ct, 1);
399 // c0_big = IDFT(c0_dft)
400 module.vec_znx_idft_tmp_a(&mut c0_big, 0, &mut c0_dft, 0);
401 }
402
403 {
404 let (mut c1_big, scratch_2) = scratch_1.tmp_vec_znx_big(module, 1, ct.size());
405 // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
406 module.vec_znx_idft(&mut c1_big, 0, ct, 0, scratch_2);
407 module.vec_znx_big_add_inplace(&mut c0_big, 0, &c1_big, 0);
408 }
409
410 // pt = norm(BIG(m + e))
411 module.vec_znx_big_normalize(ct.log_base2k(), pt, 0, &mut c0_big, 0, scratch_1);
412
413 pt.log_base2k = ct.log_base2k();
414 pt.log_k = pt.log_k().min(ct.log_k());
415}
416
417impl<C> RLWECtDft<C, FFT64> {
418 pub(crate) fn encrypt_zero_sk<S>(
419 &mut self,
420 module: &Module<FFT64>,
421 sk_dft: &SecretKeyDft<S, FFT64>,
422 source_xa: &mut Source,
423 source_xe: &mut Source,
424 sigma: f64,
425 bound: f64,
426 scratch: &mut Scratch,
427 ) where
428 VecZnxDft<C, FFT64>: VecZnxDftToMut<FFT64> + VecZnxDftToRef<FFT64>,
429 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
430 {
431 encrypt_zero_rlwe_dft_sk(
432 module, self, sk_dft, source_xa, source_xe, sigma, bound, scratch,
433 )
434 }
435
436 pub fn decrypt<P, S>(
437 &self,
438 module: &Module<FFT64>,
439 pt: &mut RLWEPt<P>,
440 sk_dft: &SecretKeyDft<S, FFT64>,
441 scratch: &mut Scratch,
442 ) where
443 VecZnx<P>: VecZnxToMut + VecZnxToRef,
444 VecZnxDft<C, FFT64>: VecZnxDftToRef<FFT64>,
445 ScalarZnxDft<S, FFT64>: ScalarZnxDftToRef<FFT64>,
446 {
447 decrypt_rlwe_dft(module, pt, self, sk_dft, scratch);
448 }
449}
450
451pub(crate) fn encrypt_rlwe_pk<C, P, S>(
452 module: &Module<FFT64>,
453 ct: &mut RLWECt<C>,
454 pt: Option<&RLWEPt<P>>,
455 pk: &PublicKey<S, FFT64>,
456 source_xu: &mut Source,
457 source_xe: &mut Source,
458 sigma: f64,
459 bound: f64,
460 scratch: &mut Scratch,
461) where
462 VecZnx<C>: VecZnxToMut + VecZnxToRef,
463 VecZnx<P>: VecZnxToRef,
464 VecZnxDft<S, FFT64>: VecZnxDftToRef<FFT64>,
465{
466 #[cfg(debug_assertions)]
467 {
468 assert_eq!(ct.log_base2k(), pk.log_base2k());
469 assert_eq!(ct.n(), module.n());
470 assert_eq!(pk.n(), module.n());
471 if let Some(pt) = pt {
472 assert_eq!(pt.log_base2k(), pk.log_base2k());
473 assert_eq!(pt.n(), module.n());
474 }
475 }
476
477 let log_base2k: usize = pk.log_base2k();
478 let size_pk: usize = pk.size();
479
480 // Generates u according to the underlying secret distribution.
481 let (mut u_dft, scratch_1) = scratch.tmp_scalar_dft(module, 1);
482
483 {
484 let (mut u, _) = scratch_1.tmp_scalar(module, 1);
485 match pk.dist {
486 SecretDistribution::NONE => panic!(
487 "invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through Self::generate"
488 ),
489 SecretDistribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu),
490 SecretDistribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu),
491 }
492
493 module.svp_prepare(&mut u_dft, 0, &u, 0);
494 }
495
496 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)
497 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)
498
499 // ct[0] = pk[0] * u + m + e0
500 module.svp_apply(&mut tmp_dft, 0, &u_dft, 0, pk, 0);
501 module.vec_znx_idft_tmp_a(&mut tmp_big, 0, &mut tmp_dft, 0);
502 tmp_big.add_normal(log_base2k, 0, pk.log_k(), source_xe, sigma, bound);
503
504 if let Some(pt) = pt {
505 module.vec_znx_big_add_small_inplace(&mut tmp_big, 0, pt, 0);
506 }
507
508 module.vec_znx_big_normalize(log_base2k, ct, 0, &tmp_big, 0, scratch_3);
509
510 // ct[1] = pk[1] * u + e1
511 module.svp_apply(&mut tmp_dft, 0, &u_dft, 0, pk, 1);
512 module.vec_znx_idft_tmp_a(&mut tmp_big, 0, &mut tmp_dft, 0);
513 tmp_big.add_normal(log_base2k, 0, pk.log_k(), source_xe, sigma, bound);
514 module.vec_znx_big_normalize(log_base2k, ct, 1, &tmp_big, 0, scratch_3);
515}
516
517#[cfg(test)]
518mod tests {
519 use base2k::{Decoding, Encoding, FFT64, Module, ScratchOwned, Stats, VecZnxOps, ZnxZero};
520 use itertools::izip;
521 use sampling::source::Source;
522
523 use crate::{
524 elem_rlwe::{Infos, RLWECt, RLWECtDft, RLWEPt},
525 keys::{PublicKey, SecretKey, SecretKeyDft},
526 };
527
528 #[test]
529 fn encrypt_sk_vec_znx_fft64() {
530 let module: Module<FFT64> = Module::<FFT64>::new(32);
531 let log_base2k: usize = 8;
532 let log_k_ct: usize = 54;
533 let log_k_pt: usize = 30;
534
535 let sigma: f64 = 3.2;
536 let bound: f64 = sigma * 6.0;
537
538 let mut ct: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_ct, 2);
539 let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_pt);
540
541 let mut source_xs: Source = Source::new([0u8; 32]);
542 let mut source_xe: Source = Source::new([0u8; 32]);
543 let mut source_xa: Source = Source::new([0u8; 32]);
544
545 let mut scratch: ScratchOwned = ScratchOwned::new(
546 RLWECt::encrypt_sk_scratch_bytes(&module, ct.size()) | RLWECt::decrypt_scratch_bytes(&module, ct.size()),
547 );
548
549 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
550 sk.fill_ternary_prob(0.5, &mut source_xs);
551
552 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
553 sk_dft.dft(&module, &sk);
554
555 let mut data_want: Vec<i64> = vec![0i64; module.n()];
556
557 data_want
558 .iter_mut()
559 .for_each(|x| *x = source_xa.next_i64() & 0xFF);
560
561 pt.data
562 .encode_vec_i64(0, log_base2k, log_k_pt, &data_want, 10);
563
564 ct.encrypt_sk(
565 &module,
566 Some(&pt),
567 &sk_dft,
568 &mut source_xa,
569 &mut source_xe,
570 sigma,
571 bound,
572 scratch.borrow(),
573 );
574
575 pt.data.zero();
576
577 ct.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
578
579 let mut data_have: Vec<i64> = vec![0i64; module.n()];
580
581 pt.data
582 .decode_vec_i64(0, log_base2k, pt.size() * log_base2k, &mut data_have);
583
584 // TODO: properly assert the decryption noise through std(dec(ct) - pt)
585 let scale: f64 = (1 << (pt.size() * log_base2k - log_k_pt)) as f64;
586 izip!(data_want.iter(), data_have.iter()).for_each(|(a, b)| {
587 let b_scaled = (*b as f64) / scale;
588 assert!(
589 (*a as f64 - b_scaled).abs() < 0.1,
590 "{} {}",
591 *a as f64,
592 b_scaled
593 )
594 });
595
596 module.free();
597 }
598
599 #[test]
600 fn encrypt_zero_rlwe_dft_sk_fft64() {
601 let module: Module<FFT64> = Module::<FFT64>::new(1024);
602 let log_base2k: usize = 8;
603 let log_k_ct: usize = 55;
604
605 let sigma: f64 = 3.2;
606 let bound: f64 = sigma * 6.0;
607
608 let mut pt: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
609
610 let mut source_xs: Source = Source::new([0u8; 32]);
611 let mut source_xe: Source = Source::new([1u8; 32]);
612 let mut source_xa: Source = Source::new([0u8; 32]);
613
614 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
615 sk.fill_ternary_prob(0.5, &mut source_xs);
616 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
617 sk_dft.dft(&module, &sk);
618
619 let mut ct_dft: RLWECtDft<Vec<u8>, FFT64> = RLWECtDft::new(&module, log_base2k, log_k_ct, 2);
620
621 let mut scratch: ScratchOwned = ScratchOwned::new(
622 RLWECtDft::decrypt_scratch_bytes(&module, ct_dft.size())
623 | RLWECtDft::encrypt_zero_sk_scratch_bytes(&module, ct_dft.size()),
624 );
625
626 ct_dft.encrypt_zero_sk(
627 &module,
628 &sk_dft,
629 &mut source_xa,
630 &mut source_xe,
631 sigma,
632 bound,
633 scratch.borrow(),
634 );
635 ct_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
636
637 assert!((sigma - pt.data.std(0, log_base2k) * (log_k_ct as f64).exp2()) <= 0.2);
638 module.free();
639 }
640
641 #[test]
642 fn encrypt_pk_vec_znx_fft64() {
643 let module: Module<FFT64> = Module::<FFT64>::new(32);
644 let log_base2k: usize = 8;
645 let log_k_ct: usize = 54;
646 let log_k_pk: usize = 64;
647
648 let sigma: f64 = 3.2;
649 let bound: f64 = sigma * 6.0;
650
651 let mut ct: RLWECt<Vec<u8>> = RLWECt::new(&module, log_base2k, log_k_ct, 2);
652 let mut pt_want: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
653
654 let mut source_xs: Source = Source::new([0u8; 32]);
655 let mut source_xe: Source = Source::new([0u8; 32]);
656 let mut source_xa: Source = Source::new([0u8; 32]);
657 let mut source_xu: Source = Source::new([0u8; 32]);
658
659 let mut sk: SecretKey<Vec<u8>> = SecretKey::new(&module);
660 sk.fill_ternary_prob(0.5, &mut source_xs);
661 let mut sk_dft: SecretKeyDft<Vec<u8>, FFT64> = SecretKeyDft::new(&module);
662 sk_dft.dft(&module, &sk);
663
664 let mut pk: PublicKey<Vec<u8>, FFT64> = PublicKey::new(&module, log_base2k, log_k_pk);
665 pk.generate(
666 &module,
667 &sk_dft,
668 &mut source_xa,
669 &mut source_xe,
670 sigma,
671 bound,
672 );
673
674 let mut scratch: ScratchOwned = ScratchOwned::new(
675 RLWECt::encrypt_sk_scratch_bytes(&module, ct.size())
676 | RLWECt::decrypt_scratch_bytes(&module, ct.size())
677 | RLWECt::encrypt_pk_scratch_bytes(&module, pk.size()),
678 );
679
680 let mut data_want: Vec<i64> = vec![0i64; module.n()];
681
682 data_want
683 .iter_mut()
684 .for_each(|x| *x = source_xa.next_i64() & 0);
685
686 pt_want
687 .data
688 .encode_vec_i64(0, log_base2k, log_k_ct, &data_want, 10);
689
690 ct.encrypt_pk(
691 &module,
692 Some(&pt_want),
693 &pk,
694 &mut source_xu,
695 &mut source_xe,
696 sigma,
697 bound,
698 scratch.borrow(),
699 );
700
701 let mut pt_have: RLWEPt<Vec<u8>> = RLWEPt::new(&module, log_base2k, log_k_ct);
702
703 ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
704
705 module.vec_znx_sub_ab_inplace(&mut pt_want, 0, &pt_have, 0);
706
707 assert!(((1.0f64 / 12.0).sqrt() - pt_want.data.std(0, log_base2k) * (log_k_ct as f64).exp2()).abs() < 0.2);
708
709 module.free();
710 }
711}