arnaucube
arnaucube/poulpyPublic Self-hosted repository
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}