Reorganized other crates
jump to
@@ -29,7 +29,7 @@ where
R: VecZnxDftToMut<BACKEND>, A: ScalarZnxDftToRef<BACKEND>; - fn svp_idft<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch) + fn scalar_znx_idft<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch) where R: ScalarZnxToMut, A: ScalarZnxDftToRef<BACKEND>;@@ -50,7 +50,7 @@ }
} impl ScalarZnxDftOps<FFT64> for Module<FFT64> { - fn svp_idft<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch) + fn scalar_znx_idft<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch) where R: ScalarZnxToMut, A: ScalarZnxDftToRef<FFT64>,
@@ -1,5 +1,5 @@
use backend::{FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned}; -use core::{GGSWCiphertext, GLWECiphertext, GLWESecret, Infos}; +use core::{FourierGLWESecret, GGSWCiphertext, GLWECiphertext, GLWESecret, Infos}; use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use sampling::source::Source; use std::hint::black_box;@@ -52,13 +52,14 @@ let mut source_xs = Source::new([0u8; 32]);
let mut source_xe = Source::new([0u8; 32]); let mut source_xa = Source::new([0u8; 32]); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); ct_ggsw.encrypt_sk( &module, &pt_rgsw, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -67,7 +68,7 @@ );
ct_glwe_in.encrypt_zero_sk( &module, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -134,13 +135,14 @@ let mut source_xs = Source::new([0u8; 32]);
let mut source_xe = Source::new([0u8; 32]); let mut source_xa = Source::new([0u8; 32]); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); ct_ggsw.encrypt_sk( &module, &pt_rgsw, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -149,7 +151,7 @@ );
ct_glwe.encrypt_zero_sk( &module, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,
@@ -1,5 +1,5 @@
use backend::{FFT64, Module, ScratchOwned}; -use core::{AutomorphismKey, GLWECiphertext, GLWESecret, GLWESwitchingKey, Infos}; +use core::{FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWESecret, GLWESwitchingKey, Infos}; use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use sampling::source::Source; use std::{hint::black_box, time::Duration};@@ -32,7 +32,8 @@
let rows: usize = (p.k_ct_in + (p.basek * digits) - 1) / (p.basek * digits); let sigma: f64 = 3.2; - let mut ksk: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_grlwe, rows, digits, rank_out); + let mut ksk: GLWEAutomorphismKey<Vec<u8>, FFT64> = + GLWEAutomorphismKey::alloc(&module, basek, k_grlwe, rows, digits, rank_out); let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_rlwe_in, rank_in); let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_rlwe_out, rank_out);@@ -55,11 +56,12 @@ let mut source_xs = Source::new([0u8; 32]);
let mut source_xe = Source::new([0u8; 32]); let mut source_xa = Source::new([0u8; 32]); - let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in); - sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in); + sk_in.fill_ternary_prob(0.5, &mut source_xs); + let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in); - let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out); - sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out); + sk_out.fill_ternary_prob(0.5, &mut source_xs); ksk.generate_from_sk( &module,@@ -73,7 +75,7 @@ );
ct_in.encrypt_zero_sk( &module, - &sk_in, + &sk_in_dft, &mut source_xa, &mut source_xe, sigma,@@ -146,16 +148,18 @@ let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]); let mut source_xa: Source = Source::new([0u8; 32]); - let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk_in.fill_ternary_prob(&&module, 0.5, &mut source_xs); + let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk_in.fill_ternary_prob(0.5, &mut source_xs); + let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in); - let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk_out.fill_ternary_prob(&&module, 0.5, &mut source_xs); + let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out); ksk.generate_from_sk( &module, &sk_in, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma,@@ -164,7 +168,7 @@ );
ct.encrypt_zero_sk( &module, - &sk_in, + &sk_in_dft, &mut source_xa, &mut source_xe, sigma,
@@ -1,16 +1,16 @@
use backend::{Backend, FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxToRef, Scratch, ZnxView, ZnxViewMut}; use sampling::source::Source; -use crate::{AutomorphismKey, GGSWCiphertext, GLWESecret, LWESecret, SecretDistribution}; +use crate::{Distribution, FourierGLWESecret, GGSWCiphertext, GLWEAutomorphismKey, LWESecret}; pub struct BlindRotationKeyCGGI<B: Backend> { pub(crate) data: Vec<GGSWCiphertext<Vec<u8>, B>>, - pub(crate) dist: SecretDistribution, + pub(crate) dist: Distribution, } pub struct BlindRotationKeyFHEW<B: Backend> { pub(crate) data: Vec<GGSWCiphertext<Vec<u8>, B>>, - pub(crate) auto: Vec<AutomorphismKey<Vec<u8>, B>>, + pub(crate) auto: Vec<GLWEAutomorphismKey<Vec<u8>, B>>, } impl BlindRotationKeyCGGI<FFT64> {@@ -19,7 +19,7 @@ let mut data: Vec<GGSWCiphertext<Vec<u8>, FFT64>> = Vec::with_capacity(lwe_degree);
(0..lwe_degree).for_each(|_| data.push(GGSWCiphertext::alloc(module, basek, k, rows, 1, rank))); Self { data, - dist: SecretDistribution::NONE, + dist: Distribution::NONE, } }@@ -30,7 +30,7 @@
pub fn generate_from_sk<DataSkGLWE, DataSkLWE>( &mut self, module: &Module<FFT64>, - sk_glwe: &GLWESecret<DataSkGLWE, FFT64>, + sk_glwe: &FourierGLWESecret<DataSkGLWE, FFT64>, sk_lwe: &LWESecret<DataSkLWE>, source_xa: &mut Source, source_xe: &mut Source,@@ -46,7 +46,7 @@ assert_eq!(self.data.len(), sk_lwe.n());
assert_eq!(sk_glwe.n(), module.n()); assert_eq!(sk_glwe.rank(), self.data[0].rank()); match sk_lwe.dist { - SecretDistribution::BinaryBlock(_) | SecretDistribution::BinaryFixed(_) | SecretDistribution::BinaryProb(_) => {} + Distribution::BinaryBlock(_) | Distribution::BinaryFixed(_) | Distribution::BinaryProb(_) => {} _ => panic!("invalid GLWESecret distribution: must be BinaryBlock, BinaryFixed or BinaryProb"), } }
@@ -0,0 +1,45 @@
+use backend::{Backend, Module, VecZnxDft, VecZnxDftAlloc}; + +use crate::{Infos, div_ceil}; + +pub struct FourierGLWECiphertext<C, B: Backend> { + pub data: VecZnxDft<C, B>, + pub basek: usize, + pub k: usize, +} + +impl<B: Backend> FourierGLWECiphertext<Vec<u8>, B> { + pub fn alloc(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self { + Self { + data: module.new_vec_znx_dft(rank + 1, div_ceil(k, basek)), + basek: basek, + k: k, + } + } + + pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize { + module.bytes_of_vec_znx_dft(rank + 1, div_ceil(k, basek)) + } +} + +impl<T, B: Backend> Infos for FourierGLWECiphertext<T, B> { + type Inner = VecZnxDft<T, B>; + + fn inner(&self) -> &Self::Inner { + &self.data + } + + fn basek(&self) -> usize { + self.basek + } + + fn k(&self) -> usize { + self.k + } +} + +impl<T, B: Backend> FourierGLWECiphertext<T, B> { + pub fn rank(&self) -> usize { + self.cols() - 1 + } +}
@@ -0,0 +1,84 @@
+use backend::{ + FFT64, Module, ScalarZnxDftOps, Scratch, VecZnxBig, VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDftAlloc, + VecZnxDftOps, ZnxZero, +}; + +use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, Infos, div_ceil}; + +impl FourierGLWECiphertext<Vec<u8>, FFT64> { + pub fn decrypt_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize { + let size: usize = div_ceil(k, basek); + (module.vec_znx_big_normalize_tmp_bytes() + | module.bytes_of_vec_znx_dft(1, size) + | (module.bytes_of_vec_znx_big(1, size) + module.vec_znx_idft_tmp_bytes())) + + module.bytes_of_vec_znx_big(1, size) + } +} + +impl<DataSelf: AsRef<[u8]>> FourierGLWECiphertext<DataSelf, FFT64> { + pub fn decrypt<DataPt: AsRef<[u8]> + AsMut<[u8]>, DataSk: AsRef<[u8]>>( + &self, + module: &Module<FFT64>, + pt: &mut GLWEPlaintext<DataPt>, + sk: &FourierGLWESecret<DataSk, FFT64>, + scratch: &mut Scratch, + ) { + #[cfg(debug_assertions)] + { + assert_eq!(self.rank(), sk.rank()); + assert_eq!(self.n(), module.n()); + assert_eq!(pt.n(), module.n()); + assert_eq!(sk.n(), module.n()); + } + + let cols = self.rank() + 1; + + let (mut pt_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, self.size()); // TODO optimize size when pt << ct + pt_big.zero(); + + { + (1..cols).for_each(|i| { + let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct + module.svp_apply(&mut ci_dft, 0, &sk.data, i - 1, &self.data, i); + let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft); + module.vec_znx_big_add_inplace(&mut pt_big, 0, &ci_big, 0); + }); + } + + { + let (mut c0_big, scratch_2) = scratch_1.tmp_vec_znx_big(module, 1, self.size()); + // c0_big = (a * s) + (-a * s + m + e) = BIG(m + e) + module.vec_znx_idft(&mut c0_big, 0, &self.data, 0, scratch_2); + module.vec_znx_big_add_inplace(&mut pt_big, 0, &c0_big, 0); + } + + // pt = norm(BIG(m + e)) + module.vec_znx_big_normalize(self.basek(), &mut pt.data, 0, &mut pt_big, 0, scratch_1); + + pt.basek = self.basek(); + pt.k = pt.k().min(self.k()); + } + + #[allow(dead_code)] + pub(crate) fn idft<DataRes: AsRef<[u8]> + AsMut<[u8]>>( + &self, + module: &Module<FFT64>, + res: &mut GLWECiphertext<DataRes>, + scratch: &mut Scratch, + ) { + #[cfg(debug_assertions)] + { + assert_eq!(self.rank(), res.rank()); + assert_eq!(self.basek(), res.basek()) + } + + let min_size: usize = self.size().min(res.size()); + + let (mut res_big, scratch1) = scratch.tmp_vec_znx_big(module, 1, min_size); + + (0..self.rank() + 1).for_each(|i| { + module.vec_znx_idft(&mut res_big, 0, &self.data, i, scratch1); + module.vec_znx_big_normalize(self.basek(), &mut res.data, i, &res_big, 0, scratch1); + }); + } +}
@@ -0,0 +1,32 @@
+use backend::{FFT64, Module, Scratch, VecZnxAlloc, VecZnxBigScratch, VecZnxDftOps}; +use sampling::source::Source; + +use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, Infos, ScratchCore, div_ceil}; + +impl FourierGLWECiphertext<Vec<u8>, FFT64> { + #[allow(dead_code)] + pub(crate) fn idft_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize { + module.bytes_of_vec_znx(1, div_ceil(k, basek)) + + (module.vec_znx_big_normalize_tmp_bytes() | module.vec_znx_idft_tmp_bytes()) + } + + pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize { + module.bytes_of_vec_znx(rank + 1, div_ceil(k, basek)) + GLWECiphertext::encrypt_sk_scratch_space(module, basek, k) + } +} + +impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> FourierGLWECiphertext<DataSelf, FFT64> { + pub fn encrypt_zero_sk<DataSk: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + sk: &FourierGLWESecret<DataSk, FFT64>, + source_xa: &mut Source, + source_xe: &mut Source, + sigma: f64, + scratch: &mut Scratch, + ) { + let (mut tmp_ct, scratch1) = scratch.tmp_glwe_ct(module, self.basek(), self.k(), self.rank()); + tmp_ct.encrypt_zero_sk(module, sk, source_xa, source_xe, sigma, scratch1); + tmp_ct.dft(module, self); + } +}
@@ -0,0 +1,129 @@
+use backend::{ + FFT64, MatZnxDftOps, MatZnxDftScratch, Module, Scratch, VecZnxAlloc, VecZnxBig, VecZnxBigOps, VecZnxBigScratch, + VecZnxDftAlloc, VecZnxDftOps, +}; + +use crate::{FourierGLWECiphertext, GGSWCiphertext, Infos, div_ceil}; + +impl FourierGLWECiphertext<Vec<u8>, FFT64> { + // WARNING TODO: UPDATE + pub fn external_product_scratch_space( + module: &Module<FFT64>, + basek: usize, + _k_out: usize, + k_in: usize, + k_ggsw: usize, + digits: usize, + rank: usize, + ) -> usize { + let ggsw_size: usize = div_ceil(k_ggsw, basek); + let res_dft: usize = module.bytes_of_vec_znx_dft(rank + 1, ggsw_size); + let in_size: usize = div_ceil(div_ceil(k_in, basek), digits); + let ggsw_size: usize = div_ceil(k_ggsw, basek); + let vmp: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size) + + module.vmp_apply_tmp_bytes(ggsw_size, in_size, in_size, rank + 1, rank + 1, ggsw_size); + let res_small: usize = module.bytes_of_vec_znx(rank + 1, ggsw_size); + let normalize: usize = module.vec_znx_big_normalize_tmp_bytes(); + res_dft + (vmp | (res_small + normalize)) + } + + pub fn external_product_inplace_scratch_space( + module: &Module<FFT64>, + basek: usize, + k_out: usize, + k_ggsw: usize, + digits: usize, + rank: usize, + ) -> usize { + Self::external_product_scratch_space(module, basek, k_out, k_out, k_ggsw, digits, rank) + } +} + +impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> FourierGLWECiphertext<DataSelf, FFT64> { + pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + lhs: &FourierGLWECiphertext<DataLhs, FFT64>, + rhs: &GGSWCiphertext<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + let basek: usize = self.basek(); + + #[cfg(debug_assertions)] + { + assert_eq!(rhs.rank(), lhs.rank()); + assert_eq!(rhs.rank(), self.rank()); + assert_eq!(self.basek(), basek); + assert_eq!(lhs.basek(), basek); + assert_eq!(rhs.n(), module.n()); + assert_eq!(self.n(), module.n()); + assert_eq!(lhs.n(), module.n()); + assert!( + scratch.available() + >= FourierGLWECiphertext::external_product_scratch_space( + module, + self.basek(), + self.k(), + lhs.k(), + rhs.k(), + rhs.digits(), + rhs.rank(), + ) + ); + } + + let cols: usize = rhs.rank() + 1; + let digits = rhs.digits(); + + // Space for VMP result in DFT domain and high precision + let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols, rhs.size()); + let (mut a_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, (lhs.size() + digits - 1) / digits); + + { + (0..digits).for_each(|di| { + a_dft.set_size((lhs.size() + di) / digits); + + // Small optimization for digits > 2 + // VMP produce some error e, and since we aggregate vmp * 2^{di * B}, then + // we also aggregate ei * 2^{di * B}, with the largest error being ei * 2^{(digits-1) * B}. + // As such we can ignore the last digits-2 limbs safely of the sum of vmp products. + // It is possible to further ignore the last digits-1 limbs, but this introduce + // ~0.5 to 1 bit of additional noise, and thus not chosen here to ensure that the same + // noise is kept with respect to the ideal functionality. + res_dft.set_size(rhs.size() - ((digits - di) as isize - 2).max(0) as usize); + + (0..cols).for_each(|col_i| { + module.vec_znx_dft_copy(digits, digits - 1 - di, &mut a_dft, col_i, &lhs.data, col_i); + }); + + if di == 0 { + module.vmp_apply(&mut res_dft, &a_dft, &rhs.data, scratch2); + } else { + module.vmp_apply_add(&mut res_dft, &a_dft, &rhs.data, di, scratch2); + } + }); + } + + // VMP result in high precision + let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume::<&mut [u8]>(res_dft); + + // Space for VMP result normalized + let (mut res_small, scratch2) = scratch1.tmp_vec_znx(module, cols, rhs.size()); + (0..cols).for_each(|i| { + module.vec_znx_big_normalize(basek, &mut res_small, i, &res_big, i, scratch2); + module.vec_znx_dft(1, 0, &mut self.data, i, &res_small, i); + }); + } + + pub fn external_product_inplace<DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + rhs: &GGSWCiphertext<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + unsafe { + let self_ptr: *mut FourierGLWECiphertext<DataSelf, FFT64> = self as *mut FourierGLWECiphertext<DataSelf, FFT64>; + self.external_product(&module, &*self_ptr, rhs, scratch); + } + } +}
@@ -0,0 +1,56 @@
+use backend::{FFT64, Module, Scratch}; + +use crate::{FourierGLWECiphertext, GLWECiphertext, GLWESwitchingKey, Infos, ScratchCore}; + +impl FourierGLWECiphertext<Vec<u8>, FFT64> { + pub fn keyswitch_scratch_space( + module: &Module<FFT64>, + basek: usize, + k_out: usize, + k_in: usize, + k_ksk: usize, + digits: usize, + rank_in: usize, + rank_out: usize, + ) -> usize { + GLWECiphertext::bytes_of(module, basek, k_out, rank_out) + + GLWECiphertext::keyswitch_from_fourier_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out) + } + + pub fn keyswitch_inplace_scratch_space( + module: &Module<FFT64>, + basek: usize, + k_out: usize, + k_ksk: usize, + digits: usize, + rank: usize, + ) -> usize { + Self::keyswitch_scratch_space(module, basek, k_out, k_out, k_ksk, digits, rank, rank) + } +} + +impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> FourierGLWECiphertext<DataSelf, FFT64> { + pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + lhs: &FourierGLWECiphertext<DataLhs, FFT64>, + rhs: &GLWESwitchingKey<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + let (mut tmp_ct, scratch1) = scratch.tmp_glwe_ct(module, self.basek(), self.k(), self.rank()); + tmp_ct.keyswitch_from_fourier(module, lhs, rhs, scratch1); + tmp_ct.dft(module, self); + } + + pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + rhs: &GLWESwitchingKey<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + unsafe { + let self_ptr: *mut FourierGLWECiphertext<DataSelf, FFT64> = self as *mut FourierGLWECiphertext<DataSelf, FFT64>; + self.keyswitch(&module, &*self_ptr, rhs, scratch); + } + } +}
@@ -0,0 +1,12 @@
+pub mod ciphertext; +pub mod decryption; +pub mod encryption; +pub mod external_product; +pub mod keyswitch; +pub mod secret; + +pub use ciphertext::FourierGLWECiphertext; +pub use secret::FourierGLWESecret; + +#[cfg(test)] +pub mod test_fft64;
@@ -0,0 +1,58 @@
+use backend::{Backend, FFT64, Module, ScalarZnxDft, ScalarZnxDftAlloc, ScalarZnxDftOps, ZnxInfos}; + +use crate::{GLWESecret, dist::Distribution}; + +pub struct FourierGLWESecret<T, B: Backend> { + pub(crate) data: ScalarZnxDft<T, B>, + pub(crate) dist: Distribution, +} + +impl<B: Backend> FourierGLWESecret<Vec<u8>, B> { + pub fn alloc(module: &Module<B>, rank: usize) -> Self { + Self { + data: module.new_scalar_znx_dft(rank), + dist: Distribution::NONE, + } + } + + pub fn bytes_of(module: &Module<B>, rank: usize) -> usize { + module.bytes_of_scalar_znx_dft(rank) + } +} + +impl FourierGLWESecret<Vec<u8>, FFT64> { + pub fn from<D>(module: &Module<FFT64>, sk: &GLWESecret<D>) -> Self + where + D: AsRef<[u8]>, + { + let mut sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = Self::alloc(module, sk.rank()); + sk_dft.set(module, sk); + sk_dft + } +} + +impl<DataSelf, B: Backend> FourierGLWESecret<DataSelf, B> { + pub fn n(&self) -> usize { + self.data.n() + } + + pub fn log_n(&self) -> usize { + self.data.log_n() + } + + pub fn rank(&self) -> usize { + self.data.cols() + } +} + +impl<S: AsMut<[u8]> + AsRef<[u8]>> FourierGLWESecret<S, FFT64> { + pub(crate) fn set<D>(&mut self, module: &Module<FFT64>, sk: &GLWESecret<D>) + where + D: AsRef<[u8]>, + { + (0..self.rank()).for_each(|i| { + module.svp_prepare(&mut self.data, i, &sk.data, i); + }); + self.dist = sk.dist + } +}
@@ -0,0 +1,246 @@
+use crate::{ + FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWECiphertext, GLWEOps, GLWEPlaintext, GLWESecret, Infos, + div_ceil, noise::noise_ggsw_product, +}; +use backend::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut}; +use sampling::source::Source; + +#[test] +fn apply() { + let log_n: usize = 8; + let basek: usize = 12; + let k_in: usize = 45; + let digits: usize = div_ceil(k_in, basek); + (1..4).for_each(|rank| { + (1..digits + 1).for_each(|di| { + let k_ggsw: usize = k_in + basek * di; + println!("test external_product digits: {} rank: {}", di, rank); + let k_out: usize = k_ggsw; // Better capture noise. + test_apply(log_n, basek, k_out, k_in, k_ggsw, di, rank, 3.2); + }); + }); +} + +#[test] +fn apply_inplace() { + let log_n: usize = 8; + let basek: usize = 12; + let k_ct: usize = 60; + let digits: usize = div_ceil(k_ct, basek); + (1..4).for_each(|rank| { + (1..digits + 1).for_each(|di| { + let k_ggsw: usize = k_ct + basek * di; + println!("test external_product digits: {} rank: {}", di, rank); + test_apply_inplace(log_n, basek, k_ct, k_ggsw, di, rank, 3.2); + }); + }); +} + +fn test_apply(log_n: usize, basek: usize, k_out: usize, k_in: usize, k_ggsw: usize, digits: usize, rank: usize, sigma: f64) { + let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); + + let rows: usize = div_ceil(k_in, digits * basek); + + let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank); + let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank); + let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank); + let mut ct_in_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank); + let mut ct_out_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_out, rank); + let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1); + let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in); + let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out); + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + + // Random input plaintext + pt_want + .data + .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); + + pt_want.data.at_mut(0, 0)[1] = 1; + + let k: i64 = 1; + + pt_rgsw.raw_mut()[0] = 1; // X^{0} + module.vec_znx_rotate_inplace(k, &mut pt_rgsw, 0); // X^{k} + + let mut scratch: ScratchOwned = ScratchOwned::new( + GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank) + | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k()) + | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k()) + | FourierGLWECiphertext::external_product_scratch_space( + &module, + basek, + ct_out.k(), + ct_in.k(), + ct_ggsw.k(), + digits, + rank, + ), + ); + + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); + + ct_ggsw.encrypt_sk( + &module, + &pt_rgsw, + &sk_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct_in.encrypt_sk( + &module, + &pt_want, + &sk_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct_in.dft(&module, &mut ct_in_dft); + ct_out_dft.external_product(&module, &ct_in_dft, &ct_ggsw, scratch.borrow()); + ct_out_dft.idft(&module, &mut ct_out, scratch.borrow()); + + ct_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow()); + + pt_want.rotate_inplace(&module, k); + pt_have.sub_inplace_ab(&module, &pt_want); + + let noise_have: f64 = pt_have.data.std(0, basek).log2(); + + let var_gct_err_lhs: f64 = sigma * sigma; + let var_gct_err_rhs: f64 = 0f64; + + let var_msg: f64 = 1f64 / module.n() as f64; // X^{k} + let var_a0_err: f64 = sigma * sigma; + let var_a1_err: f64 = 1f64 / 12f64; + + let noise_want: f64 = noise_ggsw_product( + module.n() as f64, + basek * digits, + 0.5, + var_msg, + var_a0_err, + var_a1_err, + var_gct_err_lhs, + var_gct_err_rhs, + rank as f64, + k_in, + k_ggsw, + ); + + assert!( + (noise_have - noise_want).abs() <= 0.5, + "{} {}", + noise_have, + noise_want + ); +} + +fn test_apply_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw: usize, digits: usize, rank: usize, sigma: f64) { + let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); + let rows: usize = div_ceil(k_ct, digits * basek); + + let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank); + let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank); + let mut ct_rlwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank); + let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1); + let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); + let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + + // Random input plaintext + pt_want + .data + .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); + + pt_want.data.at_mut(0, 0)[1] = 1; + + let k: i64 = 1; + + pt_rgsw.raw_mut()[0] = 1; // X^{0} + module.vec_znx_rotate_inplace(k, &mut pt_rgsw, 0); // X^{k} + + let mut scratch: ScratchOwned = ScratchOwned::new( + GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank) + | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()) + | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k()) + | FourierGLWECiphertext::external_product_inplace_scratch_space(&module, basek, ct.k(), ct_ggsw.k(), digits, rank), + ); + + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); + + ct_ggsw.encrypt_sk( + &module, + &pt_rgsw, + &sk_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct.encrypt_sk( + &module, + &pt_want, + &sk_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct.dft(&module, &mut ct_rlwe_dft); + ct_rlwe_dft.external_product_inplace(&module, &ct_ggsw, scratch.borrow()); + ct_rlwe_dft.idft(&module, &mut ct, scratch.borrow()); + + ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow()); + + pt_want.rotate_inplace(&module, k); + pt_have.sub_inplace_ab(&module, &pt_want); + + let noise_have: f64 = pt_have.data.std(0, basek).log2(); + + let var_gct_err_lhs: f64 = sigma * sigma; + let var_gct_err_rhs: f64 = 0f64; + + let var_msg: f64 = 1f64 / module.n() as f64; // X^{k} + let var_a0_err: f64 = sigma * sigma; + let var_a1_err: f64 = 1f64 / 12f64; + + let noise_want: f64 = noise_ggsw_product( + module.n() as f64, + basek * digits, + 0.5, + var_msg, + var_a0_err, + var_a1_err, + var_gct_err_lhs, + var_gct_err_rhs, + rank as f64, + k_ct, + k_ggsw, + ); + + assert!( + (noise_have - noise_want).abs() <= 0.5, + "{} {}", + noise_have, + noise_want + ); + + println!("{} {}", noise_have, noise_want); +}
@@ -0,0 +1,235 @@
+use crate::{ + FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos, div_ceil, + noise::log2_std_noise_gglwe_product, +}; +use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps}; +use sampling::source::Source; + +#[test] +fn apply() { + let log_n: usize = 8; + let basek: usize = 12; + let k_in: usize = 45; + let digits: usize = div_ceil(k_in, basek); + (1..4).for_each(|rank_in| { + (1..4).for_each(|rank_out| { + (1..digits + 1).for_each(|di| { + let k_ksk: usize = k_in + basek * di; + println!( + "test keyswitch digits: {} rank_in: {} rank_out: {}", + di, rank_in, rank_out + ); + let k_out: usize = k_ksk; // Better capture noise. + test_apply(log_n, basek, k_in, k_out, k_ksk, di, rank_in, rank_out, 3.2); + }) + }); + }); +} + +#[test] +fn apply_inplace() { + let log_n: usize = 8; + let basek: usize = 12; + let k_ct: usize = 45; + let digits: usize = div_ceil(k_ct, basek); + (1..4).for_each(|rank| { + (1..digits + 1).for_each(|di| { + let k_ksk: usize = k_ct + basek * di; + println!("test keyswitch_inplace digits: {} rank: {}", di, rank); + test_apply_inplace(log_n, basek, k_ct, k_ksk, di, rank, 3.2); + }); + }); +} + +fn test_apply( + log_n: usize, + basek: usize, + k_in: usize, + k_out: usize, + k_ksk: usize, + digits: usize, + rank_in: usize, + rank_out: usize, + sigma: f64, +) { + let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); + + let rows: usize = div_ceil(k_in, basek * digits); + + let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = + GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out); + let mut ct_glwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank_in); + let mut ct_glwe_dft_in: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank_in); + let mut ct_glwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank_out); + let mut ct_glwe_dft_out: FourierGLWECiphertext<Vec<u8>, FFT64> = + FourierGLWECiphertext::alloc(&module, basek, k_out, rank_out); + let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in); + let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out); + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + + // Random input plaintext + pt_want + .data + .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); + + let mut scratch: ScratchOwned = ScratchOwned::new( + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_out) + | GLWECiphertext::decrypt_scratch_space(&module, basek, k_out) + | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_in) + | FourierGLWECiphertext::keyswitch_scratch_space( + &module, + basek, + ct_glwe_out.k(), + ksk.k(), + ct_glwe_in.k(), + digits, + rank_in, + rank_out, + ), + ); + + let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in); + sk_in.fill_ternary_prob(0.5, &mut source_xs); + let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in); + + let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out); + + ksk.generate_from_sk( + &module, + &sk_in, + &sk_out_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct_glwe_in.encrypt_sk( + &module, + &pt_want, + &sk_in_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct_glwe_in.dft(&module, &mut ct_glwe_dft_in); + ct_glwe_dft_out.keyswitch(&module, &ct_glwe_dft_in, &ksk, scratch.borrow()); + ct_glwe_dft_out.idft(&module, &mut ct_glwe_out, scratch.borrow()); + + ct_glwe_out.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow()); + + module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0); + + let noise_have: f64 = pt_have.data.std(0, basek).log2(); + let noise_want: f64 = log2_std_noise_gglwe_product( + module.n() as f64, + basek * digits, + 0.5, + 0.5, + 0f64, + sigma * sigma, + 0f64, + rank_in as f64, + k_in, + k_ksk, + ); + + assert!( + (noise_have - noise_want).abs() <= 0.5, + "{} {}", + noise_have, + noise_want + ); +} + +fn test_apply_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, digits: usize, rank: usize, sigma: f64) { + let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); + + let rows: usize = div_ceil(k_ct, basek * digits); + + let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank); + let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank); + let mut ct_rlwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank); + let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); + let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + + // Random input plaintext + pt_want + .data + .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); + + let mut scratch: ScratchOwned = ScratchOwned::new( + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank) + | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k()) + | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k()) + | FourierGLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct_rlwe_dft.k(), ksk.k(), digits, rank), + ); + + let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk_in.fill_ternary_prob(0.5, &mut source_xs); + let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in); + + let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out); + + ksk.generate_from_sk( + &module, + &sk_in, + &sk_out_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct_glwe.encrypt_sk( + &module, + &pt_want, + &sk_in_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct_glwe.dft(&module, &mut ct_rlwe_dft); + ct_rlwe_dft.keyswitch_inplace(&module, &ksk, scratch.borrow()); + ct_rlwe_dft.idft(&module, &mut ct_glwe, scratch.borrow()); + + ct_glwe.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow()); + + module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0); + + let noise_have: f64 = pt_have.data.std(0, basek).log2(); + let noise_want: f64 = log2_std_noise_gglwe_product( + module.n() as f64, + basek * digits, + 0.5, + 0.5, + 0f64, + sigma * sigma, + 0f64, + rank as f64, + k_ct, + k_ksk, + ); + + assert!( + (noise_have - noise_want).abs() <= 0.5, + "{} {}", + noise_have, + noise_want + ); +}
@@ -0,0 +1,2 @@
+pub mod external_product; +pub mod keyswitch;
@@ -0,0 +1,136 @@
+use backend::{FFT64, Module, Scratch, VecZnx, VecZnxDftOps, VecZnxOps, ZnxZero}; + +use crate::{FourierGLWECiphertext, GLWEAutomorphismKey, GLWECiphertext, GetRow, Infos, ScratchCore, SetRow}; + +impl GLWEAutomorphismKey<Vec<u8>, FFT64> { + pub fn automorphism_scratch_space( + module: &Module<FFT64>, + basek: usize, + k_out: usize, + k_in: usize, + k_ksk: usize, + digits: usize, + rank: usize, + ) -> usize { + let tmp_dft: usize = FourierGLWECiphertext::bytes_of(module, basek, k_in, rank); + let tmp_idft: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank); + let idft: usize = module.vec_znx_idft_tmp_bytes(); + let keyswitch: usize = GLWECiphertext::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank); + tmp_dft + tmp_idft + idft + keyswitch + } + + pub fn automorphism_inplace_scratch_space( + module: &Module<FFT64>, + basek: usize, + k_out: usize, + k_ksk: usize, + digits: usize, + rank: usize, + ) -> usize { + GLWEAutomorphismKey::automorphism_scratch_space(module, basek, k_out, k_out, k_ksk, digits, rank) + } +} + +impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> { + pub fn automorphism<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + lhs: &GLWEAutomorphismKey<DataLhs, FFT64>, + rhs: &GLWEAutomorphismKey<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + #[cfg(debug_assertions)] + { + assert_eq!( + self.rank_in(), + lhs.rank_in(), + "ksk_out input rank: {} != ksk_in input rank: {}", + self.rank_in(), + lhs.rank_in() + ); + assert_eq!( + lhs.rank_out(), + rhs.rank_in(), + "ksk_in output rank: {} != ksk_apply input rank: {}", + self.rank_out(), + rhs.rank_in() + ); + assert_eq!( + self.rank_out(), + rhs.rank_out(), + "ksk_out output rank: {} != ksk_apply output rank: {}", + self.rank_out(), + rhs.rank_out() + ); + } + + let cols_out: usize = rhs.rank_out() + 1; + + let (mut tmp_dft, scratch1) = scratch.tmp_glwe_fourier(module, lhs.basek(), lhs.k(), lhs.rank()); + + (0..self.rank_in()).for_each(|col_i| { + (0..self.rows()).for_each(|row_j| { + // Extracts relevant row + lhs.get_row(module, row_j, col_i, &mut tmp_dft); + + // Get a VecZnxBig from scratch space + let (mut tmp_idft_data, scratch2) = scratch1.tmp_vec_znx_big(module, cols_out, self.size()); + + // Switches input outside of DFT + (0..cols_out).for_each(|i| { + module.vec_znx_idft(&mut tmp_idft_data, i, &tmp_dft.data, i, scratch2); + }); + + // Consumes to small vec znx + let mut tmp_idft_small_data: VecZnx<&mut [u8]> = tmp_idft_data.to_vec_znx_small(); + + // Reverts the automorphis key from (-pi^{-1}_{k}(s)a + s, a) to (-sa + pi_{k}(s), a) + (0..cols_out).for_each(|i| { + module.vec_znx_automorphism_inplace(lhs.p(), &mut tmp_idft_small_data, i); + }); + + // Wraps into ciphertext + let mut tmp_idft: GLWECiphertext<&mut [u8]> = GLWECiphertext::<&mut [u8]> { + data: tmp_idft_small_data, + basek: self.basek(), + k: self.k(), + }; + + // Key-switch (-sa + pi_{k}(s), a) to (-pi^{-1}_{k'}(s)a + pi_{k}(s), a) + tmp_idft.keyswitch_inplace(module, &rhs.key, scratch2); + + // Applies back the automorphism X^{k}: (-pi^{-1}_{k'}(s)a + pi_{k}(s), a) -> (-pi^{-1}_{k'+k}(s)a + s, a) + // and switches back to DFT domain + (0..self.rank_out() + 1).for_each(|i| { + module.vec_znx_automorphism_inplace(lhs.p(), &mut tmp_idft.data, i); + module.vec_znx_dft(1, 0, &mut tmp_dft.data, i, &tmp_idft.data, i); + }); + + // Sets back the relevant row + self.set_row(module, row_j, col_i, &tmp_dft); + }); + }); + + tmp_dft.data.zero(); + + (self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| { + (0..self.rank_in()).for_each(|col_j| { + self.set_row(module, row_i, col_j, &tmp_dft); + }); + }); + + self.p = (lhs.p * rhs.p) % (module.cyclotomic_order() as i64); + } + + pub fn automorphism_inplace<DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + rhs: &GLWEAutomorphismKey<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + unsafe { + let self_ptr: *mut GLWEAutomorphismKey<DataSelf, FFT64> = self as *mut GLWEAutomorphismKey<DataSelf, FFT64>; + self.automorphism(&module, &*self_ptr, rhs, scratch); + } + } +}
@@ -0,0 +1,83 @@
+use backend::{Backend, FFT64, MatZnxDft, MatZnxDftOps, Module}; + +use crate::{FourierGLWECiphertext, GLWESwitchingKey, GetRow, Infos, SetRow}; + +pub struct GLWEAutomorphismKey<Data, B: Backend> { + pub(crate) key: GLWESwitchingKey<Data, B>, + pub(crate) p: i64, +} + +impl GLWEAutomorphismKey<Vec<u8>, FFT64> { + pub fn alloc(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self { + GLWEAutomorphismKey { + key: GLWESwitchingKey::alloc(module, basek, k, rows, digits, rank, rank), + p: 0, + } + } + + pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize { + GLWESwitchingKey::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, rank, rank) + } +} + +impl<T, B: Backend> Infos for GLWEAutomorphismKey<T, B> { + type Inner = MatZnxDft<T, B>; + + fn inner(&self) -> &Self::Inner { + &self.key.inner() + } + + fn basek(&self) -> usize { + self.key.basek() + } + + fn k(&self) -> usize { + self.key.k() + } +} + +impl<T, B: Backend> GLWEAutomorphismKey<T, B> { + pub fn p(&self) -> i64 { + self.p + } + + pub fn digits(&self) -> usize { + self.key.digits() + } + + pub fn rank(&self) -> usize { + self.key.rank() + } + + pub fn rank_in(&self) -> usize { + self.key.rank_in() + } + + pub fn rank_out(&self) -> usize { + self.key.rank_out() + } +} + +impl<C: AsRef<[u8]>> GetRow<FFT64> for GLWEAutomorphismKey<C, FFT64> { + fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>( + &self, + module: &Module<FFT64>, + row_i: usize, + col_j: usize, + res: &mut FourierGLWECiphertext<R, FFT64>, + ) { + module.mat_znx_dft_get_row(&mut res.data, &self.key.0.data, row_i, col_j); + } +} + +impl<C: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GLWEAutomorphismKey<C, FFT64> { + fn set_row<R: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + row_i: usize, + col_j: usize, + a: &FourierGLWECiphertext<R, FFT64>, + ) { + module.mat_znx_dft_set_row(&mut self.key.0.data, row_i, col_j, &a.data); + } +}
@@ -0,0 +1,131 @@
+use backend::{Backend, FFT64, MatZnxDft, MatZnxDftAlloc, MatZnxDftOps, Module}; + +use crate::{FourierGLWECiphertext, GetRow, Infos, SetRow, div_ceil}; + +pub struct GGLWECiphertext<C, B: Backend> { + pub(crate) data: MatZnxDft<C, B>, + pub(crate) basek: usize, + pub(crate) k: usize, + pub(crate) digits: usize, +} + +impl<B: Backend> GGLWECiphertext<Vec<u8>, B> { + pub fn alloc( + module: &Module<B>, + basek: usize, + k: usize, + rows: usize, + digits: usize, + rank_in: usize, + rank_out: usize, + ) -> Self { + let size: usize = div_ceil(k, basek); + debug_assert!( + size > digits, + "invalid gglwe: ceil(k/basek): {} <= digits: {}", + size, + digits + ); + + assert!( + rows * digits <= size, + "invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}", + rows, + digits, + size + ); + + Self { + data: module.new_mat_znx_dft(rows, rank_in, rank_out + 1, size), + basek: basek, + k, + digits, + } + } + + pub fn bytes_of( + module: &Module<FFT64>, + basek: usize, + k: usize, + rows: usize, + digits: usize, + rank_in: usize, + rank_out: usize, + ) -> usize { + let size: usize = div_ceil(k, basek); + debug_assert!( + size > digits, + "invalid gglwe: ceil(k/basek): {} <= digits: {}", + size, + digits + ); + + assert!( + rows * digits <= size, + "invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}", + rows, + digits, + size + ); + + module.bytes_of_mat_znx_dft(rows, rank_in, rank_out + 1, rows) + } +} + +impl<T, B: Backend> Infos for GGLWECiphertext<T, B> { + type Inner = MatZnxDft<T, B>; + + fn inner(&self) -> &Self::Inner { + &self.data + } + + fn basek(&self) -> usize { + self.basek + } + + fn k(&self) -> usize { + self.k + } +} + +impl<T, B: Backend> GGLWECiphertext<T, B> { + pub fn rank(&self) -> usize { + self.data.cols_out() - 1 + } + + pub fn digits(&self) -> usize { + self.digits + } + + pub fn rank_in(&self) -> usize { + self.data.cols_in() + } + + pub fn rank_out(&self) -> usize { + self.data.cols_out() - 1 + } +} + +impl<C: AsRef<[u8]>> GetRow<FFT64> for GGLWECiphertext<C, FFT64> { + fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>( + &self, + module: &Module<FFT64>, + row_i: usize, + col_j: usize, + res: &mut FourierGLWECiphertext<R, FFT64>, + ) { + module.mat_znx_dft_get_row(&mut res.data, &self.data, row_i, col_j); + } +} + +impl<C: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GGLWECiphertext<C, FFT64> { + fn set_row<R: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + row_i: usize, + col_j: usize, + a: &FourierGLWECiphertext<R, FFT64>, + ) { + module.mat_znx_dft_set_row(&mut self.data, row_i, col_j, &a.data); + } +}
@@ -0,0 +1,253 @@
+use backend::{ + FFT64, Module, ScalarZnx, ScalarZnxDftOps, ScalarZnxOps, Scratch, VecZnxAlloc, VecZnxDftAlloc, VecZnxOps, ZnxInfos, ZnxZero, +}; +use sampling::source::Source; + +use crate::{ + FourierGLWESecret, GGLWECiphertext, GLWEAutomorphismKey, GLWECiphertext, GLWESecret, GLWESwitchingKey, GLWETensorKey, Infos, + ScratchCore, SetRow, div_ceil, +}; + +impl GGLWECiphertext<Vec<u8>, FFT64> { + pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize { + let size = div_ceil(k, basek); + GLWECiphertext::encrypt_sk_scratch_space(module, basek, k) + + module.bytes_of_vec_znx(rank + 1, size) + + module.bytes_of_vec_znx(1, size) + + module.bytes_of_vec_znx_dft(rank + 1, size) + } + + pub fn generate_from_pk_scratch_space(_module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize { + unimplemented!() + } +} + +impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGLWECiphertext<DataSelf, FFT64> { + pub fn encrypt_sk<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + pt: &ScalarZnx<DataPt>, + sk: &FourierGLWESecret<DataSk, FFT64>, + source_xa: &mut Source, + source_xe: &mut Source, + sigma: f64, + scratch: &mut Scratch, + ) { + #[cfg(debug_assertions)] + { + assert_eq!(self.rank_in(), pt.cols()); + assert_eq!(self.rank_out(), sk.rank()); + assert_eq!(self.n(), module.n()); + assert_eq!(sk.n(), module.n()); + assert_eq!(pt.n(), module.n()); + assert!( + scratch.available() + >= GGLWECiphertext::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()), + "scratch.available: {} < GGLWECiphertext::generate_from_sk_scratch_space(module, self.rank()={}, \ + self.size()={}): {}", + scratch.available(), + self.rank(), + self.size(), + GGLWECiphertext::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()) + ); + assert!( + self.rows() * self.digits() * self.basek() <= self.k(), + "self.rows() : {} * self.digits() : {} * self.basek() : {} = {} >= self.k() = {}", + self.rows(), + self.digits(), + self.basek(), + self.rows() * self.digits() * self.basek(), + self.k() + ); + } + + let rows: usize = self.rows(); + let digits: usize = self.digits(); + let basek: usize = self.basek(); + let k: usize = self.k(); + let rank_in: usize = self.rank_in(); + let rank_out: usize = self.rank_out(); + + let (mut tmp_pt, scrach_1) = scratch.tmp_glwe_pt(module, basek, k); + let (mut tmp_ct, scrach_2) = scrach_1.tmp_glwe_ct(module, basek, k, rank_out); + let (mut tmp_ct_dft, scratch_3) = scrach_2.tmp_glwe_fourier(module, basek, k, rank_out); + + // For each input column (i.e. rank) produces a GGLWE ciphertext of rank_out+1 columns + // + // Example for ksk rank 2 to rank 3: + // + // (-(a0*s0 + a1*s1 + a2*s2) + s0', a0, a1, a2) + // (-(b0*s0 + b1*s1 + b2*s2) + s0', b0, b1, b2) + // + // Example ksk rank 2 to rank 1 + // + // (-(a*s) + s0, a) + // (-(b*s) + s1, b) + (0..rank_in).for_each(|col_i| { + (0..rows).for_each(|row_i| { + // Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt + tmp_pt.data.zero(); // zeroes for next iteration + module.vec_znx_add_scalar_inplace( + &mut tmp_pt.data, + 0, + (digits - 1) + row_i * digits, + pt, + col_i, + ); + module.vec_znx_normalize_inplace(basek, &mut tmp_pt.data, 0, scratch_3); + + // rlwe encrypt of vec_znx_pt into vec_znx_ct + tmp_ct.encrypt_sk(module, &tmp_pt, sk, source_xa, source_xe, sigma, scratch_3); + + // Switch vec_znx_ct into DFT domain + tmp_ct.dft(module, &mut tmp_ct_dft); + + // Stores vec_znx_dft_ct into thw i-th row of the MatZnxDft + self.set_row(module, row_i, col_i, &tmp_ct_dft); + }); + }); + } +} + +impl GLWESwitchingKey<Vec<u8>, FFT64> { + pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize { + GGLWECiphertext::generate_from_sk_scratch_space(module, basek, k, rank) + } + + pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize { + GGLWECiphertext::generate_from_pk_scratch_space(module, _basek, _k, _rank) + } +} + +impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> { + pub fn generate_from_sk<DataSkIn: AsRef<[u8]>, DataSkOut: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + sk_in: &GLWESecret<DataSkIn>, + sk_out: &FourierGLWESecret<DataSkOut, FFT64>, + source_xa: &mut Source, + source_xe: &mut Source, + sigma: f64, + scratch: &mut Scratch, + ) { + self.0.encrypt_sk( + module, + &sk_in.data, + sk_out, + source_xa, + source_xe, + sigma, + scratch, + ); + } +} + +impl GLWEAutomorphismKey<Vec<u8>, FFT64> { + pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize { + GGLWECiphertext::generate_from_sk_scratch_space(module, basek, k, rank) + GLWESecret::bytes_of(module, rank) + } + + pub fn generate_from_pk_scratch_space(module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize { + GGLWECiphertext::generate_from_pk_scratch_space(module, _basek, _k, _rank) + } +} + +impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> { + pub fn generate_from_sk<DataSk: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + p: i64, + sk: &GLWESecret<DataSk>, + source_xa: &mut Source, + source_xe: &mut Source, + sigma: f64, + scratch: &mut Scratch, + ) { + #[cfg(debug_assertions)] + { + assert_eq!(self.n(), module.n()); + assert_eq!(sk.n(), module.n()); + assert_eq!(self.rank_out(), self.rank_in()); + assert_eq!(sk.rank(), self.rank()); + assert!( + scratch.available() + >= GLWEAutomorphismKey::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()), + "scratch.available(): {} < AutomorphismKey::generate_from_sk_scratch_space(module, self.rank()={}, \ + self.size()={}): {}", + scratch.available(), + self.rank(), + self.size(), + GLWEAutomorphismKey::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()) + ) + } + + let (mut sk_out_dft, scratch_1) = scratch.tmp_fourier_sk(module, sk.rank()); + + { + let (mut sk_out, _) = scratch_1.tmp_sk(module, sk.rank()); + (0..self.rank()).for_each(|i| { + module.scalar_znx_automorphism( + module.galois_element_inv(p), + &mut sk_out.data, + i, + &sk.data, + i, + ); + }); + sk_out_dft.set(module, &sk_out); + } + + self.key.generate_from_sk( + module, + &sk, + &sk_out_dft, + source_xa, + source_xe, + sigma, + scratch_1, + ); + + self.p = p; + } +} + +impl GLWETensorKey<Vec<u8>, FFT64> { + pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize { + GLWESecret::bytes_of(module, 1) + + FourierGLWESecret::bytes_of(module, 1) + + GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank) + } +} + +impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWETensorKey<DataSelf, FFT64> { + pub fn generate_from_sk<DataSk: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + sk: &FourierGLWESecret<DataSk, FFT64>, + source_xa: &mut Source, + source_xe: &mut Source, + sigma: f64, + scratch: &mut Scratch, + ) { + #[cfg(debug_assertions)] + { + assert_eq!(self.rank(), sk.rank()); + assert_eq!(self.n(), module.n()); + assert_eq!(sk.n(), module.n()); + } + + let rank: usize = self.rank(); + + let (mut sk_ij, scratch1) = scratch.tmp_sk(module, 1); + let (mut sk_ij_dft, scratch2) = scratch1.tmp_fourier_sk(module, 1); + + (0..rank).for_each(|i| { + (i..rank).for_each(|j| { + module.svp_apply(&mut sk_ij_dft.data, 0, &sk.data, i, &sk.data, j); + module.scalar_znx_idft(&mut sk_ij.data, 0, &sk_ij_dft.data, 0, scratch2); + self.at_mut(i, j) + .generate_from_sk(module, &sk_ij, sk, source_xa, source_xe, sigma, scratch2); + }); + }) + } +}
@@ -0,0 +1,162 @@
+use backend::{FFT64, Module, Scratch, ZnxZero}; + +use crate::{FourierGLWECiphertext, GGSWCiphertext, GLWEAutomorphismKey, GLWESwitchingKey, GetRow, Infos, ScratchCore, SetRow}; + +impl GLWESwitchingKey<Vec<u8>, FFT64> { + pub fn external_product_scratch_space( + module: &Module<FFT64>, + basek: usize, + k_out: usize, + k_in: usize, + k_ggsw: usize, + digits: usize, + rank: usize, + ) -> usize { + let tmp_in: usize = FourierGLWECiphertext::bytes_of(module, basek, k_in, rank); + let tmp_out: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank); + let ggsw: usize = FourierGLWECiphertext::external_product_scratch_space(module, basek, k_out, k_in, k_ggsw, digits, rank); + tmp_in + tmp_out + ggsw + } + + pub fn external_product_inplace_scratch_space( + module: &Module<FFT64>, + basek: usize, + k_out: usize, + k_ggsw: usize, + digits: usize, + rank: usize, + ) -> usize { + let tmp: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank); + let ggsw: usize = + FourierGLWECiphertext::external_product_inplace_scratch_space(module, basek, k_out, k_ggsw, digits, rank); + tmp + ggsw + } +} + +impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> { + pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + lhs: &GLWESwitchingKey<DataLhs, FFT64>, + rhs: &GGSWCiphertext<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + #[cfg(debug_assertions)] + { + assert_eq!( + self.rank_in(), + lhs.rank_in(), + "ksk_out input rank: {} != ksk_in input rank: {}", + self.rank_in(), + lhs.rank_in() + ); + assert_eq!( + lhs.rank_out(), + rhs.rank(), + "ksk_in output rank: {} != ggsw rank: {}", + self.rank_out(), + rhs.rank() + ); + assert_eq!( + self.rank_out(), + rhs.rank(), + "ksk_out output rank: {} != ggsw rank: {}", + self.rank_out(), + rhs.rank() + ); + } + + let (mut tmp_in, scratch1) = scratch.tmp_glwe_fourier(module, lhs.basek(), lhs.k(), lhs.rank()); + let (mut tmp_out, scratch2) = scratch1.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank()); + + (0..self.rank_in()).for_each(|col_i| { + (0..self.rows()).for_each(|row_j| { + lhs.get_row(module, row_j, col_i, &mut tmp_in); + tmp_out.external_product(module, &tmp_in, rhs, scratch2); + self.set_row(module, row_j, col_i, &tmp_out); + }); + }); + + tmp_out.data.zero(); + + (self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| { + (0..self.rank_in()).for_each(|col_j| { + self.set_row(module, row_i, col_j, &tmp_out); + }); + }); + } + + pub fn external_product_inplace<DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + rhs: &GGSWCiphertext<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + #[cfg(debug_assertions)] + { + assert_eq!( + self.rank_out(), + rhs.rank(), + "ksk_out output rank: {} != ggsw rank: {}", + self.rank_out(), + rhs.rank() + ); + } + + let (mut tmp, scratch1) = scratch.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank()); + println!("tmp: {}", tmp.size()); + (0..self.rank_in()).for_each(|col_i| { + (0..self.rows()).for_each(|row_j| { + self.get_row(module, row_j, col_i, &mut tmp); + tmp.external_product_inplace(module, rhs, scratch1); + self.set_row(module, row_j, col_i, &tmp); + }); + }); + } +} + +impl GLWEAutomorphismKey<Vec<u8>, FFT64> { + pub fn external_product_scratch_space( + module: &Module<FFT64>, + basek: usize, + k_out: usize, + k_in: usize, + ggsw_k: usize, + digits: usize, + rank: usize, + ) -> usize { + GLWESwitchingKey::external_product_scratch_space(module, basek, k_out, k_in, ggsw_k, digits, rank) + } + + pub fn external_product_inplace_scratch_space( + module: &Module<FFT64>, + basek: usize, + k_out: usize, + ggsw_k: usize, + digits: usize, + rank: usize, + ) -> usize { + GLWESwitchingKey::external_product_inplace_scratch_space(module, basek, k_out, ggsw_k, digits, rank) + } +} + +impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> { + pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + lhs: &GLWEAutomorphismKey<DataLhs, FFT64>, + rhs: &GGSWCiphertext<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + self.key.external_product(module, &lhs.key, rhs, scratch); + } + + pub fn external_product_inplace<DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + rhs: &GGSWCiphertext<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + self.key.external_product_inplace(module, rhs, scratch); + } +}
@@ -0,0 +1,163 @@
+use backend::{FFT64, Module, Scratch, ZnxZero}; + +use crate::{FourierGLWECiphertext, GLWEAutomorphismKey, GLWESwitchingKey, GetRow, Infos, ScratchCore, SetRow}; + +impl GLWEAutomorphismKey<Vec<u8>, FFT64> { + pub fn keyswitch_scratch_space( + module: &Module<FFT64>, + basek: usize, + k_out: usize, + k_in: usize, + k_ksk: usize, + digits: usize, + rank: usize, + ) -> usize { + GLWESwitchingKey::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank, rank) + } + + pub fn keyswitch_inplace_scratch_space( + module: &Module<FFT64>, + basek: usize, + k_out: usize, + k_ksk: usize, + digits: usize, + rank: usize, + ) -> usize { + GLWESwitchingKey::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank) + } +} + +impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> { + pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + lhs: &GLWEAutomorphismKey<DataLhs, FFT64>, + rhs: &GLWESwitchingKey<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + self.key.keyswitch(module, &lhs.key, rhs, scratch); + } + + pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + rhs: &GLWEAutomorphismKey<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + self.key.keyswitch_inplace(module, &rhs.key, scratch); + } +} + +impl GLWESwitchingKey<Vec<u8>, FFT64> { + pub fn keyswitch_scratch_space( + module: &Module<FFT64>, + basek: usize, + k_out: usize, + k_in: usize, + k_ksk: usize, + digits: usize, + rank_in: usize, + rank_out: usize, + ) -> usize { + let tmp_in: usize = FourierGLWECiphertext::bytes_of(module, basek, k_in, rank_in); + let tmp_out: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank_out); + let ksk: usize = + FourierGLWECiphertext::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out); + tmp_in + tmp_out + ksk + } + + pub fn keyswitch_inplace_scratch_space( + module: &Module<FFT64>, + basek: usize, + k_out: usize, + k_ksk: usize, + digits: usize, + rank: usize, + ) -> usize { + let tmp: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank); + let ksk: usize = FourierGLWECiphertext::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank); + tmp + ksk + } +} + +impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> { + pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + lhs: &GLWESwitchingKey<DataLhs, FFT64>, + rhs: &GLWESwitchingKey<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + #[cfg(debug_assertions)] + { + assert_eq!( + self.rank_in(), + lhs.rank_in(), + "ksk_out input rank: {} != ksk_in input rank: {}", + self.rank_in(), + lhs.rank_in() + ); + assert_eq!( + lhs.rank_out(), + rhs.rank_in(), + "ksk_in output rank: {} != ksk_apply input rank: {}", + self.rank_out(), + rhs.rank_in() + ); + assert_eq!( + self.rank_out(), + rhs.rank_out(), + "ksk_out output rank: {} != ksk_apply output rank: {}", + self.rank_out(), + rhs.rank_out() + ); + } + + let (mut tmp_in, scratch1) = scratch.tmp_glwe_fourier(module, lhs.basek(), lhs.k(), lhs.rank()); + let (mut tmp_out, scratch2) = scratch1.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank()); + + (0..self.rank_in()).for_each(|col_i| { + (0..self.rows()).for_each(|row_j| { + lhs.get_row(module, row_j, col_i, &mut tmp_in); + tmp_out.keyswitch(module, &tmp_in, rhs, scratch2); + self.set_row(module, row_j, col_i, &tmp_out); + }); + }); + + tmp_out.data.zero(); + + (self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| { + (0..self.rank_in()).for_each(|col_j| { + self.set_row(module, row_i, col_j, &tmp_out); + }); + }); + } + + pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + rhs: &GLWESwitchingKey<DataRhs, FFT64>, + scratch: &mut Scratch, + ) { + #[cfg(debug_assertions)] + { + assert_eq!( + self.rank_out(), + rhs.rank_out(), + "ksk_out output rank: {} != ksk_apply output rank: {}", + self.rank_out(), + rhs.rank_out() + ); + } + + let (mut tmp, scratch1) = scratch.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank()); + + (0..self.rank_in()).for_each(|col_i| { + (0..self.rows()).for_each(|row_j| { + self.get_row(module, row_j, col_i, &mut tmp); + tmp.keyswitch_inplace(module, rhs, scratch1); + self.set_row(module, row_j, col_i, &tmp); + }); + }); + } +}
@@ -0,0 +1,91 @@
+use backend::{Backend, FFT64, MatZnxDft, MatZnxDftOps, Module}; + +use crate::{FourierGLWECiphertext, GGLWECiphertext, GetRow, Infos, SetRow}; + +pub struct GLWESwitchingKey<Data, B: Backend>(pub(crate) GGLWECiphertext<Data, B>); + +impl GLWESwitchingKey<Vec<u8>, FFT64> { + pub fn alloc( + module: &Module<FFT64>, + basek: usize, + k: usize, + rows: usize, + digits: usize, + rank_in: usize, + rank_out: usize, + ) -> Self { + GLWESwitchingKey(GGLWECiphertext::alloc( + module, basek, k, rows, digits, rank_in, rank_out, + )) + } + + pub fn bytes_of( + module: &Module<FFT64>, + basek: usize, + k: usize, + rows: usize, + digits: usize, + rank_in: usize, + rank_out: usize, + ) -> usize { + GGLWECiphertext::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out) + } +} + +impl<T, B: Backend> Infos for GLWESwitchingKey<T, B> { + type Inner = MatZnxDft<T, B>; + + fn inner(&self) -> &Self::Inner { + self.0.inner() + } + + fn basek(&self) -> usize { + self.0.basek() + } + + fn k(&self) -> usize { + self.0.k() + } +} + +impl<T, B: Backend> GLWESwitchingKey<T, B> { + pub fn rank(&self) -> usize { + self.0.data.cols_out() - 1 + } + + pub fn rank_in(&self) -> usize { + self.0.data.cols_in() + } + + pub fn rank_out(&self) -> usize { + self.0.data.cols_out() - 1 + } + + pub fn digits(&self) -> usize { + self.0.digits() + } +} + +impl<C: AsRef<[u8]>> GetRow<FFT64> for GLWESwitchingKey<C, FFT64> { + fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>( + &self, + module: &Module<FFT64>, + row_i: usize, + col_j: usize, + res: &mut FourierGLWECiphertext<R, FFT64>, + ) { + module.mat_znx_dft_get_row(&mut res.data, &self.0.data, row_i, col_j); + } +} + +impl<C: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GLWESwitchingKey<C, FFT64> { + fn set_row<R: AsRef<[u8]>>( + &mut self, + module: &Module<FFT64>, + row_i: usize, + col_j: usize, + a: &FourierGLWECiphertext<R, FFT64>, + ) { + module.mat_znx_dft_set_row(&mut self.0.data, row_i, col_j, &a.data); + } +}
@@ -0,0 +1,16 @@
+pub mod automorphism; +pub mod automorphism_key; +pub mod ciphertext; +pub mod encryption; +pub mod external_product; +pub mod keyswitch; +pub mod keyswitch_key; +pub mod tensor_key; + +pub use automorphism_key::GLWEAutomorphismKey; +pub use ciphertext::GGLWECiphertext; +pub use keyswitch_key::GLWESwitchingKey; +pub use tensor_key::GLWETensorKey; + +#[cfg(test)] +mod test_fft64;
@@ -0,0 +1,81 @@
+use backend::{Backend, FFT64, MatZnxDft, Module}; + +use crate::{GLWESwitchingKey, Infos}; + +pub struct GLWETensorKey<C, B: Backend> { + pub(crate) keys: Vec<GLWESwitchingKey<C, B>>, +} + +impl GLWETensorKey<Vec<u8>, FFT64> { + pub fn alloc(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self { + let mut keys: Vec<GLWESwitchingKey<Vec<u8>, FFT64>> = Vec::new(); + let pairs: usize = (((rank + 1) * rank) >> 1).max(1); + (0..pairs).for_each(|_| { + keys.push(GLWESwitchingKey::alloc( + module, basek, k, rows, digits, 1, rank, + )); + }); + Self { keys: keys } + } + + pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize { + let pairs: usize = (((rank + 1) * rank) >> 1).max(1); + pairs * GLWESwitchingKey::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, 1, rank) + } +} + +impl<T, B: Backend> Infos for GLWETensorKey<T, B> { + type Inner = MatZnxDft<T, B>; + + fn inner(&self) -> &Self::Inner { + &self.keys[0].inner() + } + + fn basek(&self) -> usize { + self.keys[0].basek() + } + + fn k(&self) -> usize { + self.keys[0].k() + } +} + +impl<T, B: Backend> GLWETensorKey<T, B> { + pub fn rank(&self) -> usize { + self.keys[0].rank() + } + + pub fn rank_in(&self) -> usize { + self.keys[0].rank_in() + } + + pub fn rank_out(&self) -> usize { + self.keys[0].rank_out() + } + + pub fn digits(&self) -> usize { + self.keys[0].digits() + } +} + +impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWETensorKey<DataSelf, FFT64> { + // Returns a mutable reference to GLWESwitchingKey_{s}(s[i] * s[j]) + pub fn at_mut(&mut self, mut i: usize, mut j: usize) -> &mut GLWESwitchingKey<DataSelf, FFT64> { + if i > j { + std::mem::swap(&mut i, &mut j); + }; + let rank: usize = self.rank(); + &mut self.keys[i * rank + j - (i * (i + 1) / 2)] + } +} + +impl<DataSelf: AsRef<[u8]>> GLWETensorKey<DataSelf, FFT64> { + // Returns a reference to GLWESwitchingKey_{s}(s[i] * s[j]) + pub fn at(&self, mut i: usize, mut j: usize) -> &GLWESwitchingKey<DataSelf, FFT64> { + if i > j { + std::mem::swap(&mut i, &mut j); + }; + let rank: usize = self.rank(); + &self.keys[i * rank + j - (i * (i + 1) / 2)] + } +}
@@ -0,0 +1,3 @@
+pub mod automorphism_key; +pub mod gglwe; +pub mod tensor_key;
@@ -6,8 +6,8 @@ };
use sampling::source::Source; use crate::{ - AutomorphismKey, FourierGLWECiphertext, GLWECiphertext, GLWESecret, GLWESwitchingKey, GetRow, Infos, ScratchCore, SetRow, - TensorKey, div_ceil, + FourierGLWECiphertext, FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWESwitchingKey, GLWETensorKey, GetRow, + Infos, ScratchCore, SetRow, div_ceil, }; pub struct GGSWCiphertext<C, B: Backend> {@@ -246,7 +246,7 @@ pub fn encrypt_sk<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
&mut self, module: &Module<FFT64>, pt: &ScalarZnx<DataPt>, - sk: &GLWESecret<DataSk, FFT64>, + sk: &FourierGLWESecret<DataSk, FFT64>, source_xa: &mut Source, source_xe: &mut Source, sigma: f64,@@ -304,7 +304,7 @@ module: &Module<FFT64>,
col_j: usize, res: &mut R, ci_dft: &VecZnxDft<DataCi, FFT64>, - tsk: &TensorKey<DataTsk, FFT64>, + tsk: &GLWETensorKey<DataTsk, FFT64>, scratch: &mut Scratch, ) where R: VecZnxToMut,@@ -408,7 +408,7 @@ &mut self,
module: &Module<FFT64>, lhs: &GGSWCiphertext<DataLhs, FFT64>, ksk: &GLWESwitchingKey<DataKsk, FFT64>, - tsk: &TensorKey<DataTsk, FFT64>, + tsk: &GLWETensorKey<DataTsk, FFT64>, scratch: &mut Scratch, ) { let rank: usize = self.rank();@@ -449,7 +449,7 @@ pub fn keyswitch_inplace<DataKsk: AsRef<[u8]>, DataTsk: AsRef<[u8]>>(
&mut self, module: &Module<FFT64>, ksk: &GLWESwitchingKey<DataKsk, FFT64>, - tsk: &TensorKey<DataTsk, FFT64>, + tsk: &GLWETensorKey<DataTsk, FFT64>, scratch: &mut Scratch, ) { unsafe {@@ -462,8 +462,8 @@ pub fn automorphism<DataLhs: AsRef<[u8]>, DataAk: AsRef<[u8]>, DataTsk: AsRef<[u8]>>(
&mut self, module: &Module<FFT64>, lhs: &GGSWCiphertext<DataLhs, FFT64>, - auto_key: &AutomorphismKey<DataAk, FFT64>, - tensor_key: &TensorKey<DataTsk, FFT64>, + auto_key: &GLWEAutomorphismKey<DataAk, FFT64>, + tensor_key: &GLWETensorKey<DataTsk, FFT64>, scratch: &mut Scratch, ) { #[cfg(debug_assertions)]@@ -551,8 +551,8 @@
pub fn automorphism_inplace<DataKsk: AsRef<[u8]>, DataTsk: AsRef<[u8]>>( &mut self, module: &Module<FFT64>, - auto_key: &AutomorphismKey<DataKsk, FFT64>, - tensor_key: &TensorKey<DataTsk, FFT64>, + auto_key: &GLWEAutomorphismKey<DataKsk, FFT64>, + tensor_key: &GLWETensorKey<DataTsk, FFT64>, scratch: &mut Scratch, ) { unsafe {
@@ -0,0 +1,6 @@
+pub mod ciphertext; + +pub use ciphertext::GGSWCiphertext; + +#[cfg(test)] +mod test_fft64;
@@ -1,6 +1,6 @@
use backend::{FFT64, Module, Scratch, VecZnxOps}; -use crate::{AutomorphismKey, GLWECiphertext}; +use crate::{GLWEAutomorphismKey, GLWECiphertext}; impl GLWECiphertext<Vec<u8>> { pub fn automorphism_scratch_space(@@ -32,7 +32,7 @@ pub fn automorphism<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self, module: &Module<FFT64>, lhs: &GLWECiphertext<DataLhs>, - rhs: &AutomorphismKey<DataRhs, FFT64>, + rhs: &GLWEAutomorphismKey<DataRhs, FFT64>, scratch: &mut Scratch, ) { self.keyswitch(module, lhs, &rhs.key, scratch);@@ -44,7 +44,7 @@
pub fn automorphism_inplace<DataRhs: AsRef<[u8]>>( &mut self, module: &Module<FFT64>, - rhs: &AutomorphismKey<DataRhs, FFT64>, + rhs: &GLWEAutomorphismKey<DataRhs, FFT64>, scratch: &mut Scratch, ) { self.keyswitch_inplace(module, &rhs.key, scratch);@@ -57,7 +57,7 @@ pub fn automorphism_add<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self, module: &Module<FFT64>, lhs: &GLWECiphertext<DataLhs>, - rhs: &AutomorphismKey<DataRhs, FFT64>, + rhs: &GLWEAutomorphismKey<DataRhs, FFT64>, scratch: &mut Scratch, ) { Self::keyswitch_private::<_, _, 1>(self, rhs.p(), module, lhs, &rhs.key, scratch);@@ -66,7 +66,7 @@
pub fn automorphism_add_inplace<DataRhs: AsRef<[u8]>>( &mut self, module: &Module<FFT64>, - rhs: &AutomorphismKey<DataRhs, FFT64>, + rhs: &GLWEAutomorphismKey<DataRhs, FFT64>, scratch: &mut Scratch, ) { unsafe {@@ -79,7 +79,7 @@ pub fn automorphism_sub_ab<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self, module: &Module<FFT64>, lhs: &GLWECiphertext<DataLhs>, - rhs: &AutomorphismKey<DataRhs, FFT64>, + rhs: &GLWEAutomorphismKey<DataRhs, FFT64>, scratch: &mut Scratch, ) { Self::keyswitch_private::<_, _, 2>(self, rhs.p(), module, lhs, &rhs.key, scratch);@@ -88,7 +88,7 @@
pub fn automorphism_sub_ab_inplace<DataRhs: AsRef<[u8]>>( &mut self, module: &Module<FFT64>, - rhs: &AutomorphismKey<DataRhs, FFT64>, + rhs: &GLWEAutomorphismKey<DataRhs, FFT64>, scratch: &mut Scratch, ) { unsafe {@@ -101,7 +101,7 @@ pub fn automorphism_sub_ba<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self, module: &Module<FFT64>, lhs: &GLWECiphertext<DataLhs>, - rhs: &AutomorphismKey<DataRhs, FFT64>, + rhs: &GLWEAutomorphismKey<DataRhs, FFT64>, scratch: &mut Scratch, ) { Self::keyswitch_private::<_, _, 3>(self, rhs.p(), module, lhs, &rhs.key, scratch);@@ -110,7 +110,7 @@
pub fn automorphism_sub_ba_inplace<DataRhs: AsRef<[u8]>>( &mut self, module: &Module<FFT64>, - rhs: &AutomorphismKey<DataRhs, FFT64>, + rhs: &GLWEAutomorphismKey<DataRhs, FFT64>, scratch: &mut Scratch, ) { unsafe {
@@ -1,6 +1,6 @@
use backend::{FFT64, Module, ScalarZnxDftOps, Scratch, VecZnxBigOps, VecZnxDftOps, ZnxZero}; -use crate::{GLWECiphertext, GLWEPlaintext, GLWESecret, Infos}; +use crate::{FourierGLWESecret, GLWECiphertext, GLWEPlaintext, Infos}; impl<DataSelf: AsRef<[u8]>> GLWECiphertext<DataSelf> { pub fn clone(&self) -> GLWECiphertext<Vec<u8>> {@@ -15,7 +15,7 @@ pub fn decrypt<DataPt: AsMut<[u8]> + AsRef<[u8]>, DataSk: AsRef<[u8]>>(
&self, module: &Module<FFT64>, pt: &mut GLWEPlaintext<DataPt>, - sk: &GLWESecret<DataSk, FFT64>, + sk: &FourierGLWESecret<DataSk, FFT64>, scratch: &mut Scratch, ) { #[cfg(debug_assertions)]@@ -36,7 +36,7 @@ (1..cols).for_each(|i| {
// ci_dft = DFT(a[i]) * DFT(s[i]) let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct module.vec_znx_dft(1, 0, &mut ci_dft, 0, &self.data, i); - module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1); + module.svp_apply_inplace(&mut ci_dft, 0, &sk.data, i - 1); let ci_big = module.vec_znx_idft_consume(ci_dft); // c0_big += a[i] * s[i]
@@ -4,7 +4,7 @@ VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDftAlloc, VecZnxDftOps, VecZnxOps, ZnxZero,
}; use sampling::source::Source; -use crate::{GLWECiphertext, GLWEPlaintext, GLWEPublicKey, GLWESecret, Infos, SIX_SIGMA, div_ceil, keys::SecretDistribution}; +use crate::{FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWEPublicKey, Infos, SIX_SIGMA, dist::Distribution, div_ceil}; impl GLWECiphertext<Vec<u8>> { pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {@@ -24,7 +24,7 @@ pub fn encrypt_sk<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
&mut self, module: &Module<FFT64>, pt: &GLWEPlaintext<DataPt>, - sk: &GLWESecret<DataSk, FFT64>, + sk: &FourierGLWESecret<DataSk, FFT64>, source_xa: &mut Source, source_xe: &mut Source, sigma: f64,@@ -44,7 +44,7 @@
pub fn encrypt_zero_sk<DataSk: AsRef<[u8]>>( &mut self, module: &Module<FFT64>, - sk: &GLWESecret<DataSk, FFT64>, + sk: &FourierGLWESecret<DataSk, FFT64>, source_xa: &mut Source, source_xe: &mut Source, sigma: f64,@@ -106,7 +106,7 @@ pub(crate) fn encrypt_sk_private<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
&mut self, module: &Module<FFT64>, pt: Option<(&GLWEPlaintext<DataPt>, usize)>, - sk: &GLWESecret<DataSk, FFT64>, + sk: &FourierGLWESecret<DataSk, FFT64>, source_xa: &mut Source, source_xe: &mut Source, sigma: f64,@@ -148,7 +148,7 @@ self.data.fill_uniform(basek, i, size, source_xa);
// c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i]))) module.vec_znx_dft(1, 0, &mut ci_dft, 0, &self.data, i); - module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1); + module.svp_apply_inplace(&mut ci_dft, 0, &sk.data, i - 1); let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft); // use c[0] as buffer, which is overwritten later by the normalization step@@ -213,16 +213,16 @@
{ let (mut u, _) = scratch_1.tmp_scalar_znx(module, 1); match pk.dist { - SecretDistribution::NONE => panic!( + Distribution::NONE => panic!( "invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through \ Self::generate" ), - SecretDistribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu), - SecretDistribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu), - SecretDistribution::BinaryFixed(hw) => u.fill_binary_hw(0, hw, source_xu), - SecretDistribution::BinaryProb(prob) => u.fill_binary_prob(0, prob, source_xu), - SecretDistribution::BinaryBlock(block_size) => u.fill_binary_block(0, block_size, source_xu), - SecretDistribution::ZERO => {} + Distribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu), + Distribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu), + Distribution::BinaryFixed(hw) => u.fill_binary_hw(0, hw, source_xu), + Distribution::BinaryProb(prob) => u.fill_binary_prob(0, prob, source_xu), + Distribution::BinaryBlock(block_size) => u.fill_binary_block(0, block_size, source_xu), + Distribution::ZERO => {} } module.svp_prepare(&mut u_dft, 0, &u, 0);
@@ -11,24 +11,13 @@ pub mod public_key;
pub mod secret; pub mod trace; -#[allow(unused_imports)] -pub use automorphism::*; -pub use ciphertext::*; -#[allow(unused_imports)] -pub use decryption::*; -#[allow(unused_imports)] -pub use encryption::*; -#[allow(unused_imports)] -pub use external_product::*; -#[allow(unused_imports)] -pub use keyswitch::*; -pub use ops::*; -pub use packing::*; -pub use plaintext::*; -pub use public_key::*; -pub use secret::*; -#[allow(unused_imports)] -pub use trace::*; +pub use ciphertext::GLWECiphertext; +pub(crate) use ciphertext::{GLWECiphertextToMut, GLWECiphertextToRef}; +pub use ops::GLWEOps; +pub use packing::GLWEPacker; +pub use plaintext::GLWEPlaintext; +pub use public_key::GLWEPublicKey; +pub use secret::GLWESecret; #[cfg(test)] mod test_fft64;
@@ -1,4 +1,4 @@
-use crate::{AutomorphismKey, GLWECiphertext, GLWEOps, Infos, ScratchCore}; +use crate::{GLWEAutomorphismKey, GLWECiphertext, GLWEOps, Infos, ScratchCore}; use std::collections::HashMap; use backend::{FFT64, Module, Scratch};@@ -7,7 +7,7 @@ /// [StreamPacker] enables only the fly GLWE packing
/// with constant memory of Log(N) ciphertexts. /// Main difference with usual GLWE packing is that /// the output is bit-reversed. -pub struct StreamPacker { +pub struct GLWEPacker { accumulators: Vec<Accumulator>, log_batch: usize, counter: usize,@@ -39,7 +39,7 @@ }
} } -impl StreamPacker { +impl GLWEPacker { /// Instantiates a new [StreamPacker]. /// /// #Arguments@@ -98,7 +98,7 @@ &mut self,
module: &Module<FFT64>, res: &mut Vec<GLWECiphertext<Vec<u8>>>, a: Option<&GLWECiphertext<DataA>>, - auto_keys: &HashMap<i64, AutomorphismKey<DataAK, FFT64>>, + auto_keys: &HashMap<i64, GLWEAutomorphismKey<DataAK, FFT64>>, scratch: &mut Scratch, ) { pack_core(@@ -125,7 +125,7 @@ pub fn flush<DataAK: AsRef<[u8]>>(
&mut self, module: &Module<FFT64>, res: &mut Vec<GLWECiphertext<Vec<u8>>>, - auto_keys: &HashMap<i64, AutomorphismKey<DataAK, FFT64>>, + auto_keys: &HashMap<i64, GLWEAutomorphismKey<DataAK, FFT64>>, scratch: &mut Scratch, ) { if self.counter != 0 {@@ -151,7 +151,7 @@ module: &Module<FFT64>,
a: Option<&GLWECiphertext<D>>, accumulators: &mut [Accumulator], i: usize, - auto_keys: &HashMap<i64, AutomorphismKey<DataAK, FFT64>>, + auto_keys: &HashMap<i64, GLWEAutomorphismKey<DataAK, FFT64>>, scratch: &mut Scratch, ) { let log_n: usize = module.log_n();@@ -215,7 +215,7 @@ module: &Module<FFT64>,
acc: &mut Accumulator, b: Option<&GLWECiphertext<D>>, i: usize, - auto_keys: &HashMap<i64, AutomorphismKey<DataAK, FFT64>>, + auto_keys: &HashMap<i64, GLWEAutomorphismKey<DataAK, FFT64>>, scratch: &mut Scratch, ) { let log_n: usize = module.log_n();
@@ -1,10 +1,6 @@
use backend::{Backend, FFT64, Module, VecZnx, VecZnxAlloc, VecZnxToMut, VecZnxToRef}; -use crate::{ - GLWEOps, Infos, SetMetaData, - ciphertext::{GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef}, - div_ceil, -}; +use crate::{GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef, GLWEOps, Infos, SetMetaData, div_ceil}; pub struct GLWEPlaintext<C> { pub data: VecZnx<C>,
@@ -1,18 +1,18 @@
use backend::{Backend, FFT64, Module, ScratchOwned, VecZnxDft}; use sampling::source::Source; -use crate::{FourierGLWECiphertext, GLWESecret, Infos, keys::SecretDistribution}; +use crate::{FourierGLWECiphertext, FourierGLWESecret, Infos, dist::Distribution}; pub struct GLWEPublicKey<D, B: Backend> { pub(crate) data: FourierGLWECiphertext<D, B>, - pub(crate) dist: SecretDistribution, + pub(crate) dist: Distribution, } impl<B: Backend> GLWEPublicKey<Vec<u8>, B> { pub fn alloc(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self { Self { data: FourierGLWECiphertext::alloc(module, basek, k, rank), - dist: SecretDistribution::NONE, + dist: Distribution::NONE, } }@@ -47,7 +47,7 @@ impl<C: AsRef<[u8]> + AsMut<[u8]>> GLWEPublicKey<C, FFT64> {
pub fn generate_from_sk<S: AsRef<[u8]>>( &mut self, module: &Module<FFT64>, - sk: &GLWESecret<S, FFT64>, + sk: &FourierGLWESecret<S, FFT64>, source_xa: &mut Source, source_xe: &mut Source, sigma: f64,@@ -55,7 +55,7 @@ ) {
#[cfg(debug_assertions)] { match sk.dist { - SecretDistribution::NONE => panic!("invalid sk: SecretDistribution::NONE"), + Distribution::NONE => panic!("invalid sk: SecretDistribution::NONE"), _ => {} } }
@@ -1,31 +1,27 @@
-use backend::{ - Backend, FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxDft, ScalarZnxDftAlloc, ScalarZnxDftOps, ZnxInfos, ZnxZero, -}; +use backend::{Backend, Module, ScalarZnx, ScalarZnxAlloc, ZnxInfos, ZnxZero}; use sampling::source::Source; -use crate::keys::SecretDistribution; +use crate::dist::Distribution; -pub struct GLWESecret<T, B: Backend> { +pub struct GLWESecret<T> { pub(crate) data: ScalarZnx<T>, - pub(crate) data_fourier: ScalarZnxDft<T, B>, - pub(crate) dist: SecretDistribution, + pub(crate) dist: Distribution, } -impl<B: Backend> GLWESecret<Vec<u8>, B> { - pub fn alloc(module: &Module<B>, rank: usize) -> Self { +impl GLWESecret<Vec<u8>> { + pub fn alloc<B: Backend>(module: &Module<B>, rank: usize) -> Self { Self { data: module.new_scalar_znx(rank), - data_fourier: module.new_scalar_znx_dft(rank), - dist: SecretDistribution::NONE, + dist: Distribution::NONE, } } - pub fn bytes_of(module: &Module<B>, rank: usize) -> usize { - module.bytes_of_scalar_znx(rank) + module.bytes_of_scalar_znx_dft(rank) + pub fn bytes_of<B: Backend>(module: &Module<B>, rank: usize) -> usize { + module.bytes_of_scalar_znx(rank) } } -impl<DataSelf, B: Backend> GLWESecret<DataSelf, B> { +impl<DataSelf> GLWESecret<DataSelf> { pub fn n(&self) -> usize { self.data.n() }@@ -39,55 +35,50 @@ self.data.cols()
} } -impl<S: AsMut<[u8]> + AsRef<[u8]>> GLWESecret<S, FFT64> { - pub fn fill_ternary_prob(&mut self, module: &Module<FFT64>, prob: f64, source: &mut Source) { +impl<S: AsMut<[u8]> + AsRef<[u8]>> GLWESecret<S> { + pub fn fill_ternary_prob(&mut self, prob: f64, source: &mut Source) { (0..self.rank()).for_each(|i| { self.data.fill_ternary_prob(i, prob, source); }); - self.prep_fourier(module); - self.dist = SecretDistribution::TernaryProb(prob); + self.dist = Distribution::TernaryProb(prob); } - pub fn fill_ternary_hw(&mut self, module: &Module<FFT64>, hw: usize, source: &mut Source) { + pub fn fill_ternary_hw(&mut self, hw: usize, source: &mut Source) { (0..self.rank()).for_each(|i| { self.data.fill_ternary_hw(i, hw, source); }); - self.prep_fourier(module); - self.dist = SecretDistribution::TernaryFixed(hw); + self.dist = Distribution::TernaryFixed(hw); } - pub fn fill_binary_prob(&mut self, module: &Module<FFT64>, prob: f64, source: &mut Source) { + pub fn fill_binary_prob(&mut self, prob: f64, source: &mut Source) { (0..self.rank()).for_each(|i| { self.data.fill_binary_prob(i, prob, source); }); - self.prep_fourier(module); - self.dist = SecretDistribution::BinaryProb(prob); + self.dist = Distribution::BinaryProb(prob); } - pub fn fill_binary_hw(&mut self, module: &Module<FFT64>, hw: usize, source: &mut Source) { + pub fn fill_binary_hw(&mut self, hw: usize, source: &mut Source) { (0..self.rank()).for_each(|i| { self.data.fill_binary_hw(i, hw, source); }); - self.prep_fourier(module); - self.dist = SecretDistribution::BinaryFixed(hw); + self.dist = Distribution::BinaryFixed(hw); } - pub fn fill_binary_block(&mut self, module: &Module<FFT64>, block_size: usize, source: &mut Source) { + pub fn fill_binary_block(&mut self, block_size: usize, source: &mut Source) { (0..self.rank()).for_each(|i| { self.data.fill_binary_block(i, block_size, source); }); - self.prep_fourier(module); - self.dist = SecretDistribution::BinaryBlock(block_size); + self.dist = Distribution::BinaryBlock(block_size); } pub fn fill_zero(&mut self) { self.data.zero(); - self.dist = SecretDistribution::ZERO; + self.dist = Distribution::ZERO; } - pub(crate) fn prep_fourier(&mut self, module: &Module<FFT64>) { - (0..self.rank()).for_each(|i| { - module.svp_prepare(&mut self.data_fourier, i, &self.data, i); - }); - } + // pub(crate) fn prep_fourier(&mut self, module: &Module<FFT64>) { + // (0..self.rank()).for_each(|i| { + // module.svp_prepare(&mut self.data_fourier, i, &self.data, i); + // }); + // } }
@@ -1,1 +1,224 @@
+use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps}; +use sampling::source::Source; + +use crate::{ + FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, div_ceil, + noise::log2_std_noise_gglwe_product, +}; + +#[test] +fn apply_inplace() { + let log_n: usize = 8; + let basek: usize = 12; + let k_ct: usize = 60; + let digits: usize = div_ceil(k_ct, basek); + (1..4).for_each(|rank| { + (1..digits + 1).for_each(|di| { + let k_ksk: usize = k_ct + basek * di; + println!("test automorphism_inplace digits: {} rank: {}", di, rank); + test_automorphism_inplace(log_n, basek, -5, k_ct, k_ksk, di, rank, 3.2); + }); + }); +} + +#[test] +fn apply() { + let log_n: usize = 8; + let basek: usize = 12; + let k_in: usize = 60; + let digits: usize = div_ceil(k_in, basek); + (1..4).for_each(|rank| { + (1..digits + 1).for_each(|di| { + let k_ksk: usize = k_in + basek * di; + let k_out: usize = k_ksk; // Better capture noise. + println!("test automorphism digits: {} rank: {}", di, rank); + test_automorphism(log_n, basek, -5, k_out, k_in, k_ksk, di, rank, 3.2); + }) + }); +} + +fn test_automorphism( + log_n: usize, + basek: usize, + p: i64, + k_out: usize, + k_in: usize, + k_ksk: usize, + digits: usize, + rank: usize, + sigma: f64, +) { + let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); + + let rows: usize = div_ceil(k_in, basek * digits); + + let mut autokey: GLWEAutomorphismKey<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); + let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank); + let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank); + let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in); + let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out); + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + + pt_want + .data + .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); + + let mut scratch: ScratchOwned = ScratchOwned::new( + GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, autokey.k(), rank) + | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k()) + | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k()) + | GLWECiphertext::automorphism_scratch_space( + &module, + basek, + ct_out.k(), + ct_in.k(), + autokey.k(), + digits, + rank, + ), + ); + + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); + + autokey.generate_from_sk( + &module, + p, + &sk, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct_in.encrypt_sk( + &module, + &pt_want, + &sk_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct_out.automorphism(&module, &ct_in, &autokey, scratch.borrow()); + ct_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow()); + module.vec_znx_automorphism_inplace(p, &mut pt_want.data, 0); + module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0); + module.vec_znx_normalize_inplace(basek, &mut pt_have.data, 0, scratch.borrow()); + + let noise_have: f64 = pt_have.data.std(0, basek).log2(); + + println!("{}", noise_have); + + let noise_want: f64 = log2_std_noise_gglwe_product( + module.n() as f64, + basek * digits, + 0.5, + 0.5, + 0f64, + sigma * sigma, + 0f64, + rank as f64, + k_in, + k_ksk, + ); + + assert!( + noise_have <= noise_want + 1.0, + "{} {}", + noise_have, + noise_want + ); +} + +fn test_automorphism_inplace( + log_n: usize, + basek: usize, + p: i64, + k_ct: usize, + k_ksk: usize, + digits: usize, + rank: usize, + sigma: f64, +) { + let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); + + let rows: usize = div_ceil(k_ct, basek * digits); + + let mut autokey: GLWEAutomorphismKey<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); + let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank); + let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); + let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + + pt_want + .data + .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); + + let mut scratch: ScratchOwned = ScratchOwned::new( + GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, autokey.k(), rank) + | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()) + | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k()) + | GLWECiphertext::automorphism_inplace_scratch_space(&module, basek, ct.k(), autokey.k(), digits, rank), + ); + + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); + + autokey.generate_from_sk( + &module, + p, + &sk, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct.encrypt_sk( + &module, + &pt_want, + &sk_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct.automorphism_inplace(&module, &autokey, scratch.borrow()); + ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow()); + module.vec_znx_automorphism_inplace(p, &mut pt_want.data, 0); + module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0); + module.vec_znx_normalize_inplace(basek, &mut pt_have.data, 0, scratch.borrow()); + + let noise_have: f64 = pt_have.data.std(0, basek).log2(); + let noise_want: f64 = log2_std_noise_gglwe_product( + module.n() as f64, + basek * digits, + 0.5, + 0.5, + 0f64, + sigma * sigma, + 0f64, + rank as f64, + k_ct, + k_ksk, + ); + + assert!( + (noise_have - noise_want).abs() <= 0.5, + "{} {}", + noise_have, + noise_want + ); +}
@@ -2,7 +2,7 @@ use backend::{Decoding, Encoding, FFT64, Module, ScratchOwned, Stats, VecZnxOps, ZnxZero};
use itertools::izip; use sampling::source::Source; -use crate::{FourierGLWECiphertext, GLWECiphertext, GLWEPlaintext, GLWEPublicKey, GLWESecret, Infos}; +use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWEPublicKey, GLWESecret, Infos}; #[test] fn encrypt_sk() {@@ -46,8 +46,9 @@ GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()), ); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); let mut data_want: Vec<i64> = vec![0i64; module.n()];@@ -60,7 +61,7 @@
ct.encrypt_sk( &module, &pt, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -69,7 +70,7 @@ );
pt.data.zero(); - ct.decrypt(&module, &mut pt, &sk, scratch.borrow()); + ct.decrypt(&module, &mut pt, &sk_dft, scratch.borrow()); let mut data_have: Vec<i64> = vec![0i64; module.n()];@@ -98,8 +99,9 @@ let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([1u8; 32]); let mut source_xa: Source = Source::new([0u8; 32]); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); let mut ct_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank);@@ -110,13 +112,13 @@ );
ct_dft.encrypt_zero_sk( &module, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, scratch.borrow(), ); - ct_dft.decrypt(&module, &mut pt, &sk, scratch.borrow()); + ct_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow()); assert!((sigma - pt.data.std(0, basek) * (k_ct as f64).exp2()) <= 0.2); }@@ -132,11 +134,12 @@ let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]); let mut source_xu: Source = Source::new([0u8; 32]); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); let mut pk: GLWEPublicKey<Vec<u8>, FFT64> = GLWEPublicKey::alloc(&module, basek, k_pk, rank); - pk.generate_from_sk(&module, &sk, &mut source_xa, &mut source_xe, sigma); + pk.generate_from_sk(&module, &sk_dft, &mut source_xa, &mut source_xe, sigma); let mut scratch: ScratchOwned = ScratchOwned::new( GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())@@ -164,7 +167,7 @@ );
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); - ct.decrypt(&module, &mut pt_have, &sk, scratch.borrow()); + ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow()); module.vec_znx_sub_ab_inplace(&mut pt_want.data, 0, &pt_have.data, 0);
@@ -1,86 +1,16 @@
-use backend::{ - Decoding, Encoding, FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut, - ZnxZero, -}; -use itertools::izip; +use backend::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut}; use sampling::source::Source; use crate::{ - FourierGLWECiphertext, GGSWCiphertext, GLWECiphertext, GLWEPlaintext, GLWEPublicKey, GLWESecret, Infos, - automorphism::AutomorphismKey, - keyswitch_key::GLWESwitchingKey, - test_fft64::{log2_std_noise_gglwe_product, noise_ggsw_product}, + FourierGLWESecret, GGSWCiphertext, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, div_ceil, noise::noise_ggsw_product, }; #[test] -fn encrypt_sk() { - let log_n: usize = 8; - (1..4).for_each(|rank| { - println!("test encrypt_sk rank: {}", rank); - test_encrypt_sk(log_n, 8, 54, 30, 3.2, rank); - }); -} - -#[test] -fn encrypt_zero_sk() { - let log_n: usize = 8; - (1..4).for_each(|rank| { - println!("test encrypt_zero_sk rank: {}", rank); - test_encrypt_zero_sk(log_n, 8, 64, 3.2, rank); - }); -} - -#[test] -fn encrypt_pk() { - let log_n: usize = 8; - (1..4).for_each(|rank| { - println!("test encrypt_pk rank: {}", rank); - test_encrypt_pk(log_n, 8, 64, 64, 3.2, rank) - }); -} - -#[test] -fn keyswitch() { +fn apply() { let log_n: usize = 8; let basek: usize = 12; let k_in: usize = 45; - let digits: usize = k_in.div_ceil(basek); - (1..4).for_each(|rank_in| { - (1..4).for_each(|rank_out| { - (1..digits + 1).for_each(|di| { - let k_ksk: usize = k_in + basek * di; - let k_out: usize = k_ksk; // better capture noise - println!( - "test keyswitch digits: {} rank_in: {} rank_out: {}", - di, rank_in, rank_out - ); - test_keyswitch(log_n, basek, k_out, k_in, k_ksk, di, rank_in, rank_out, 3.2); - }) - }); - }); -} - -#[test] -fn keyswitch_inplace() { - let log_n: usize = 8; - let basek: usize = 12; - let k_ct: usize = 45; - let digits: usize = k_ct.div_ceil(basek); - (1..4).for_each(|rank| { - (1..digits + 1).for_each(|di| { - let k_ksk: usize = k_ct + basek * di; - println!("test keyswitch_inplace digits: {} rank: {}", di, rank); - test_keyswitch_inplace(log_n, basek, k_ct, k_ksk, di, rank, 3.2); - }); - }); -} - -#[test] -fn external_product() { - let log_n: usize = 8; - let basek: usize = 12; - let k_in: usize = 45; - let digits: usize = k_in.div_ceil(basek); + let digits: usize = div_ceil(k_in, basek); (1..4).for_each(|rank| { (1..digits + 1).for_each(|di| { let k_ggsw: usize = k_in + basek * di;@@ -92,7 +22,7 @@ });
} #[test] -fn external_product_inplace() { +fn apply_inplace() { let log_n: usize = 8; let basek: usize = 12; let k_ct: usize = 60;@@ -106,548 +36,6 @@ });
}); } -#[test] -fn automorphism_inplace() { - let log_n: usize = 8; - let basek: usize = 12; - let k_ct: usize = 60; - let digits: usize = k_ct.div_ceil(basek); - (1..4).for_each(|rank| { - (1..digits + 1).for_each(|di| { - let k_ksk: usize = k_ct + basek * di; - println!("test automorphism_inplace digits: {} rank: {}", di, rank); - test_automorphism_inplace(log_n, basek, -5, k_ct, k_ksk, di, rank, 3.2); - }); - }); -} - -#[test] -fn automorphism() { - let log_n: usize = 8; - let basek: usize = 12; - let k_in: usize = 60; - let digits: usize = k_in.div_ceil(basek); - (1..4).for_each(|rank| { - (1..digits + 1).for_each(|di| { - let k_ksk: usize = k_in + basek * di; - let k_out: usize = k_ksk; // Better capture noise. - println!("test automorphism digits: {} rank: {}", di, rank); - test_automorphism(log_n, basek, -5, k_out, k_in, k_ksk, di, rank, 3.2); - }) - }); -} - -fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma: f64, rank: usize) { - let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); - - let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank); - let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_pt); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([0u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - - let mut scratch: ScratchOwned = ScratchOwned::new( - GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k()) - | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()), - ); - - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); - - let mut data_want: Vec<i64> = vec![0i64; module.n()]; - - data_want - .iter_mut() - .for_each(|x| *x = source_xa.next_i64() & 0xFF); - - pt.data.encode_vec_i64(0, basek, k_pt, &data_want, 10); - - ct.encrypt_sk( - &module, - &pt, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - pt.data.zero(); - - ct.decrypt(&module, &mut pt, &sk, scratch.borrow()); - - let mut data_have: Vec<i64> = vec![0i64; module.n()]; - - pt.data - .decode_vec_i64(0, basek, pt.size() * basek, &mut data_have); - - // TODO: properly assert the decryption noise through std(dec(ct) - pt) - let scale: f64 = (1 << (pt.size() * basek - k_pt)) as f64; - izip!(data_want.iter(), data_have.iter()).for_each(|(a, b)| { - let b_scaled = (*b as f64) / scale; - assert!( - (*a as f64 - b_scaled).abs() < 0.1, - "{} {}", - *a as f64, - b_scaled - ) - }); -} - -fn test_encrypt_zero_sk(log_n: usize, basek: usize, k_ct: usize, sigma: f64, rank: usize) { - let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); - - let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([1u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); - - let mut ct_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank); - - let mut scratch: ScratchOwned = ScratchOwned::new( - FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ct) - | FourierGLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_ct, rank), - ); - - ct_dft.encrypt_zero_sk( - &module, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - ct_dft.decrypt(&module, &mut pt, &sk, scratch.borrow()); - - assert!((sigma - pt.data.std(0, basek) * (k_ct as f64).exp2()) <= 0.2); -} - -fn test_encrypt_pk(log_n: usize, basek: usize, k_ct: usize, k_pk: usize, sigma: f64, rank: usize) { - let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); - - let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank); - let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([0u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - let mut source_xu: Source = Source::new([0u8; 32]); - - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); - - let mut pk: GLWEPublicKey<Vec<u8>, FFT64> = GLWEPublicKey::alloc(&module, basek, k_pk, rank); - pk.generate_from_sk(&module, &sk, &mut source_xa, &mut source_xe, sigma); - - let mut scratch: ScratchOwned = ScratchOwned::new( - GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k()) - | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()) - | GLWECiphertext::encrypt_pk_scratch_space(&module, basek, pk.k()), - ); - - let mut data_want: Vec<i64> = vec![0i64; module.n()]; - - data_want - .iter_mut() - .for_each(|x| *x = source_xa.next_i64() & 0); - - pt_want.data.encode_vec_i64(0, basek, k_ct, &data_want, 10); - - ct.encrypt_pk( - &module, - &pt_want, - &pk, - &mut source_xu, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); - - ct.decrypt(&module, &mut pt_have, &sk, scratch.borrow()); - - module.vec_znx_sub_ab_inplace(&mut pt_want.data, 0, &pt_have.data, 0); - - let noise_have: f64 = pt_want.data.std(0, basek).log2(); - let noise_want: f64 = ((((rank as f64) + 1.0) * module.n() as f64 * 0.5 * sigma * sigma).sqrt()).log2() - (k_ct as f64); - - assert!( - (noise_have - noise_want).abs() < 0.2, - "{} {}", - noise_have, - noise_want - ); -} - -fn test_keyswitch( - log_n: usize, - basek: usize, - k_out: usize, - k_in: usize, - k_ksk: usize, - digits: usize, - rank_in: usize, - rank_out: usize, - sigma: f64, -) { - let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); - - let rows: usize = k_in.div_ceil(basek * digits); - - let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = - GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out); - let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank_in); - let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank_out); - let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in); - let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([0u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - - pt_want - .data - .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); - - let mut scratch: ScratchOwned = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank_out) - | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k()) - | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k()) - | GLWECiphertext::keyswitch_scratch_space( - &module, - basek, - ct_out.k(), - ct_in.k(), - ksk.k(), - digits, - rank_in, - rank_out, - ), - ); - - let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in); - sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs); - - let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out); - sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs); - - ksk.generate_from_sk( - &module, - &sk_in, - &sk_out, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct_in.encrypt_sk( - &module, - &pt_want, - &sk_in, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct_out.keyswitch(&module, &ct_in, &ksk, scratch.borrow()); - ct_out.decrypt(&module, &mut pt_have, &sk_out, scratch.borrow()); - - module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0); - - let noise_have: f64 = pt_have.data.std(0, basek).log2(); - let noise_want: f64 = log2_std_noise_gglwe_product( - module.n() as f64, - basek * digits, - 0.5, - 0.5, - 0f64, - sigma * sigma, - 0f64, - rank_in as f64, - k_in, - k_ksk, - ); - - println!("{} vs. {}", noise_have, noise_want); - - assert!( - (noise_have - noise_want).abs() <= 0.5, - "{} {}", - noise_have, - noise_want - ); -} - -fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, digits: usize, rank: usize, sigma: f64) { - let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); - - let rows: usize = k_ct.div_ceil(basek * digits); - - let mut ct_grlwe: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank); - let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank); - let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); - let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([0u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - - pt_want - .data - .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); - - let mut scratch: ScratchOwned = ScratchOwned::new( - GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ct_grlwe.k(), rank) - | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k()) - | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k()) - | GLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_grlwe.k(), digits, rank), - ); - - let mut sk0: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk0.fill_ternary_prob(&module, 0.5, &mut source_xs); - - let mut sk1: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk1.fill_ternary_prob(&module, 0.5, &mut source_xs); - - ct_grlwe.generate_from_sk( - &module, - &sk0, - &sk1, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct_glwe.encrypt_sk( - &module, - &pt_want, - &sk0, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct_glwe.keyswitch_inplace(&module, &ct_grlwe, scratch.borrow()); - - ct_glwe.decrypt(&module, &mut pt_have, &sk1, scratch.borrow()); - - module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0); - - let noise_have: f64 = pt_have.data.std(0, basek).log2(); - let noise_want: f64 = log2_std_noise_gglwe_product( - module.n() as f64, - basek * digits, - 0.5, - 0.5, - 0f64, - sigma * sigma, - 0f64, - rank as f64, - k_ct, - k_ksk, - ); - - assert!( - (noise_have - noise_want).abs() <= 0.5, - "{} {}", - noise_have, - noise_want - ); -} - -fn test_automorphism( - log_n: usize, - basek: usize, - p: i64, - k_out: usize, - k_in: usize, - k_ksk: usize, - digits: usize, - rank: usize, - sigma: f64, -) { - let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); - - let rows: usize = k_in.div_ceil(basek * digits); - - let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); - let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank); - let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank); - let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in); - let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([0u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - - pt_want - .data - .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); - - let mut scratch: ScratchOwned = ScratchOwned::new( - AutomorphismKey::generate_from_sk_scratch_space(&module, basek, autokey.k(), rank) - | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k()) - | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k()) - | GLWECiphertext::automorphism_scratch_space( - &module, - basek, - ct_out.k(), - ct_in.k(), - autokey.k(), - digits, - rank, - ), - ); - - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); - - autokey.generate_from_sk( - &module, - p, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct_in.encrypt_sk( - &module, - &pt_want, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct_out.automorphism(&module, &ct_in, &autokey, scratch.borrow()); - ct_out.decrypt(&module, &mut pt_have, &sk, scratch.borrow()); - module.vec_znx_automorphism_inplace(p, &mut pt_want.data, 0); - module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0); - module.vec_znx_normalize_inplace(basek, &mut pt_have.data, 0, scratch.borrow()); - - let noise_have: f64 = pt_have.data.std(0, basek).log2(); - - println!("{}", noise_have); - - let noise_want: f64 = log2_std_noise_gglwe_product( - module.n() as f64, - basek * digits, - 0.5, - 0.5, - 0f64, - sigma * sigma, - 0f64, - rank as f64, - k_in, - k_ksk, - ); - - assert!( - noise_have <= noise_want + 1.0, - "{} {}", - noise_have, - noise_want - ); -} - -fn test_automorphism_inplace( - log_n: usize, - basek: usize, - p: i64, - k_ct: usize, - k_ksk: usize, - digits: usize, - rank: usize, - sigma: f64, -) { - let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); - - let rows: usize = k_ct.div_ceil(basek * digits); - - let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); - let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank); - let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); - let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([0u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - - pt_want - .data - .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); - - let mut scratch: ScratchOwned = ScratchOwned::new( - AutomorphismKey::generate_from_sk_scratch_space(&module, basek, autokey.k(), rank) - | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()) - | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k()) - | GLWECiphertext::automorphism_inplace_scratch_space(&module, basek, ct.k(), autokey.k(), digits, rank), - ); - - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); - - autokey.generate_from_sk( - &module, - p, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct.encrypt_sk( - &module, - &pt_want, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - ct.automorphism_inplace(&module, &autokey, scratch.borrow()); - ct.decrypt(&module, &mut pt_have, &sk, scratch.borrow()); - module.vec_znx_automorphism_inplace(p, &mut pt_want.data, 0); - module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0); - module.vec_znx_normalize_inplace(basek, &mut pt_have.data, 0, scratch.borrow()); - - let noise_have: f64 = pt_have.data.std(0, basek).log2(); - let noise_want: f64 = log2_std_noise_gglwe_product( - module.n() as f64, - basek * digits, - 0.5, - 0.5, - 0f64, - sigma * sigma, - 0f64, - rank as f64, - k_ct, - k_ksk, - ); - - assert!( - (noise_have - noise_want).abs() <= 0.5, - "{} {}", - noise_have, - noise_want - ); -} - fn test_external_product( log_n: usize, basek: usize,@@ -699,13 +87,14 @@ rank,
), ); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); ct_ggsw.encrypt_sk( &module, &pt_rgsw, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -715,7 +104,7 @@
ct_glwe_in.encrypt_sk( &module, &pt_want, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -724,7 +113,7 @@ );
ct_glwe_out.external_product(&module, &ct_glwe_in, &ct_ggsw, scratch.borrow()); - ct_glwe_out.decrypt(&module, &mut pt_have, &sk, scratch.borrow()); + ct_glwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow()); module.vec_znx_rotate_inplace(k as i64, &mut pt_want.data, 0);@@ -793,13 +182,14 @@ | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k())
| GLWECiphertext::external_product_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_ggsw.k(), digits, rank), ); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); ct_ggsw.encrypt_sk( &module, &pt_rgsw, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -809,7 +199,7 @@
ct_glwe.encrypt_sk( &module, &pt_want, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -818,7 +208,7 @@ );
ct_glwe.external_product_inplace(&module, &ct_ggsw, scratch.borrow()); - ct_glwe.decrypt(&module, &mut pt_have, &sk, scratch.borrow()); + ct_glwe.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow()); module.vec_znx_rotate_inplace(k as i64, &mut pt_want.data, 0);
@@ -1,1 +1,227 @@
+use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps}; +use sampling::source::Source; +use crate::{ + FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos, div_ceil, + noise::log2_std_noise_gglwe_product, +}; + +#[test] +fn apply() { + let log_n: usize = 8; + let basek: usize = 12; + let k_in: usize = 45; + let digits: usize = div_ceil(k_in, basek); + (1..4).for_each(|rank_in| { + (1..4).for_each(|rank_out| { + (1..digits + 1).for_each(|di| { + let k_ksk: usize = k_in + basek * di; + let k_out: usize = k_ksk; // better capture noise + println!( + "test keyswitch digits: {} rank_in: {} rank_out: {}", + di, rank_in, rank_out + ); + test_keyswitch(log_n, basek, k_out, k_in, k_ksk, di, rank_in, rank_out, 3.2); + }) + }); + }); +} + +#[test] +fn apply_inplace() { + let log_n: usize = 8; + let basek: usize = 12; + let k_ct: usize = 45; + let digits: usize = div_ceil(k_ct, basek); + (1..4).for_each(|rank| { + (1..digits + 1).for_each(|di| { + let k_ksk: usize = k_ct + basek * di; + println!("test keyswitch_inplace digits: {} rank: {}", di, rank); + test_keyswitch_inplace(log_n, basek, k_ct, k_ksk, di, rank, 3.2); + }); + }); +} + +fn test_keyswitch( + log_n: usize, + basek: usize, + k_out: usize, + k_in: usize, + k_ksk: usize, + digits: usize, + rank_in: usize, + rank_out: usize, + sigma: f64, +) { + let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); + + let rows: usize = div_ceil(k_in, basek * digits); + + let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = + GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out); + let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank_in); + let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank_out); + let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in); + let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out); + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + + pt_want + .data + .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); + + let mut scratch: ScratchOwned = ScratchOwned::new( + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank_out) + | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k()) + | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k()) + | GLWECiphertext::keyswitch_scratch_space( + &module, + basek, + ct_out.k(), + ct_in.k(), + ksk.k(), + digits, + rank_in, + rank_out, + ), + ); + + let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in); + sk_in.fill_ternary_prob(0.5, &mut source_xs); + let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in); + + let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out); + + ksk.generate_from_sk( + &module, + &sk_in, + &sk_out_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct_in.encrypt_sk( + &module, + &pt_want, + &sk_in_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct_out.keyswitch(&module, &ct_in, &ksk, scratch.borrow()); + ct_out.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow()); + + module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0); + + let noise_have: f64 = pt_have.data.std(0, basek).log2(); + let noise_want: f64 = log2_std_noise_gglwe_product( + module.n() as f64, + basek * digits, + 0.5, + 0.5, + 0f64, + sigma * sigma, + 0f64, + rank_in as f64, + k_in, + k_ksk, + ); + + println!("{} vs. {}", noise_have, noise_want); + + assert!( + (noise_have - noise_want).abs() <= 0.5, + "{} {}", + noise_have, + noise_want + ); +} + +fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, digits: usize, rank: usize, sigma: f64) { + let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); + + let rows: usize = div_ceil(k_ct, basek * digits); + + let mut ct_grlwe: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank); + let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank); + let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); + let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + + pt_want + .data + .fill_uniform(basek, 0, pt_want.size(), &mut source_xa); + + let mut scratch: ScratchOwned = ScratchOwned::new( + GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ct_grlwe.k(), rank) + | GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k()) + | GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k()) + | GLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_grlwe.k(), digits, rank), + ); + + let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk_in.fill_ternary_prob(0.5, &mut source_xs); + let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in); + + let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out); + + ct_grlwe.generate_from_sk( + &module, + &sk_in, + &sk_out_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct_glwe.encrypt_sk( + &module, + &pt_want, + &sk_in_dft, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + ct_glwe.keyswitch_inplace(&module, &ct_grlwe, scratch.borrow()); + + ct_glwe.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow()); + + module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0); + + let noise_have: f64 = pt_have.data.std(0, basek).log2(); + let noise_want: f64 = log2_std_noise_gglwe_product( + module.n() as f64, + basek * digits, + 0.5, + 0.5, + 0f64, + sigma * sigma, + 0f64, + rank as f64, + k_ct, + k_ksk, + ); + + assert!( + (noise_have - noise_want).abs() <= 0.5, + "{} {}", + noise_have, + noise_want + ); +}
@@ -1,11 +1,11 @@
-use crate::{AutomorphismKey, GLWECiphertext, GLWEOps, GLWEPlaintext, GLWESecret, StreamPacker}; +use crate::{FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEOps, GLWEPacker, GLWEPlaintext, GLWESecret, div_ceil}; use std::collections::HashMap; use backend::{Encoding, FFT64, Module, ScratchOwned, Stats}; use sampling::source::Source; #[test] -fn packing() { +fn apply() { let log_n: usize = 5; let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);@@ -26,12 +26,13 @@
let mut scratch: ScratchOwned = ScratchOwned::new( GLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_ct) | GLWECiphertext::decrypt_scratch_space(&module, basek, k_ct) - | AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank) - | StreamPacker::scratch_space(&module, basek, k_ct, k_ksk, digits, rank), + | GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank) + | GLWEPacker::scratch_space(&module, basek, k_ct, k_ksk, digits, rank), ); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); let mut data: Vec<i64> = vec![0i64; module.n()];@@ -40,11 +41,11 @@ *x = i as i64;
}); pt.data.encode_vec_i64(0, basek, pt_k, &data, 32); - let gal_els: Vec<i64> = StreamPacker::galois_elements(&module); + let gal_els: Vec<i64> = GLWEPacker::galois_elements(&module); - let mut auto_keys: HashMap<i64, AutomorphismKey<Vec<u8>, FFT64>> = HashMap::new(); + let mut auto_keys: HashMap<i64, GLWEAutomorphismKey<Vec<u8>, FFT64>> = HashMap::new(); gal_els.iter().for_each(|gal_el| { - let mut key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); + let mut key: GLWEAutomorphismKey<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); key.generate_from_sk( &module, *gal_el,@@ -59,14 +60,14 @@ });
let log_batch: usize = 0; - let mut packer: StreamPacker = StreamPacker::new(&module, log_batch, basek, k_ct, rank); + let mut packer: GLWEPacker = GLWEPacker::new(&module, log_batch, basek, k_ct, rank); let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank); ct.encrypt_sk( &module, &pt, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -79,7 +80,7 @@ (0..module.n() >> log_batch).for_each(|i| {
ct.encrypt_sk( &module, &pt, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -115,7 +116,7 @@ }
}); pt_want.data.encode_vec_i64(0, basek, pt_k, &data, 32); - res_i.decrypt(&module, &mut pt, &sk, scratch.borrow()); + res_i.decrypt(&module, &mut pt, &sk_dft, scratch.borrow()); if i & 1 == 0 { pt.sub_inplace_ab(&module, &pt_want);
@@ -3,10 +3,13 @@
use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps, ZnxView, ZnxViewMut}; use sampling::source::Source; -use crate::{AutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, test_fft64::var_noise_gglwe_product}; +use crate::{ + FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, div_ceil, + noise::var_noise_gglwe_product, +}; #[test] -fn trace_inplace() { +fn apply_inplace() { let log_n: usize = 8; (1..4).for_each(|rank| { println!("test trace_inplace rank: {}", rank);@@ -33,12 +36,13 @@
let mut scratch: ScratchOwned = ScratchOwned::new( GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k()) | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()) - | AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_autokey, rank) + | GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_autokey, rank) | GLWECiphertext::trace_inplace_scratch_space(&module, basek, ct.k(), k_autokey, digits, rank), ); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); let mut data_want: Vec<i64> = vec![0i64; module.n()];@@ -53,17 +57,18 @@
ct.encrypt_sk( &module, &pt_have, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, scratch.borrow(), ); - let mut auto_keys: HashMap<i64, AutomorphismKey<Vec<u8>, FFT64>> = HashMap::new(); + let mut auto_keys: HashMap<i64, GLWEAutomorphismKey<Vec<u8>, FFT64>> = HashMap::new(); let gal_els: Vec<i64> = GLWECiphertext::trace_galois_elements(&module); gal_els.iter().for_each(|gal_el| { - let mut key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_autokey, rows, digits, rank); + let mut key: GLWEAutomorphismKey<Vec<u8>, FFT64> = + GLWEAutomorphismKey::alloc(&module, basek, k_autokey, rows, digits, rank); key.generate_from_sk( &module, *gal_el,@@ -81,7 +86,7 @@ ct.trace_inplace(&module, 5, log_n, &auto_keys, scratch.borrow());
(0..pt_want.size()).for_each(|i| pt_want.data.at_mut(0, i)[0] = pt_have.data.at(0, i)[0]); - ct.decrypt(&module, &mut pt_have, &sk, scratch.borrow()); + ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow()); module.vec_znx_sub_ab_inplace(&mut pt_want.data, 0, &pt_have.data, 0); module.vec_znx_normalize_inplace(basek, &mut pt_want.data, 0, scratch.borrow());
@@ -2,7 +2,7 @@ use std::collections::HashMap;
use backend::{FFT64, Module, Scratch}; -use crate::{AutomorphismKey, GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef, GLWEOps, Infos, SetMetaData}; +use crate::{GLWEAutomorphismKey, GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef, GLWEOps, Infos, SetMetaData}; impl GLWECiphertext<Vec<u8>> { pub fn trace_galois_elements(module: &Module<FFT64>) -> Vec<i64> {@@ -51,7 +51,7 @@ module: &Module<FFT64>,
start: usize, end: usize, lhs: &GLWECiphertext<DataLhs>, - auto_keys: &HashMap<i64, AutomorphismKey<DataAK, FFT64>>, + auto_keys: &HashMap<i64, GLWEAutomorphismKey<DataAK, FFT64>>, scratch: &mut Scratch, ) where GLWECiphertext<DataLhs>: GLWECiphertextToRef + Infos,@@ -65,7 +65,7 @@ &mut self,
module: &Module<FFT64>, start: usize, end: usize, - auto_keys: &HashMap<i64, AutomorphismKey<DataAK, FFT64>>, + auto_keys: &HashMap<i64, GLWEAutomorphismKey<DataAK, FFT64>>, scratch: &mut Scratch, ) { (start..end).for_each(|i| {
@@ -1,5 +1,5 @@
#[derive(Clone, Copy, Debug)] -pub(crate) enum SecretDistribution { +pub(crate) enum Distribution { TernaryFixed(usize), // Ternary with fixed Hamming weight TernaryProb(f64), // Ternary with probabilistic Hamming weight BinaryFixed(usize), // Binary with fixed Hamming weight
@@ -1,343 +0,0 @@
-use backend::{Backend, FFT64, MatZnxDft, MatZnxDftOps, Module, Scratch, ZnxZero}; -use sampling::source::Source; - -use crate::{FourierGLWECiphertext, GGLWECiphertext, GGSWCiphertext, GLWESecret, GetRow, Infos, ScratchCore, SetRow}; - -pub struct GLWESwitchingKey<Data, B: Backend>(pub(crate) GGLWECiphertext<Data, B>); - -impl GLWESwitchingKey<Vec<u8>, FFT64> { - pub fn alloc( - module: &Module<FFT64>, - basek: usize, - k: usize, - rows: usize, - digits: usize, - rank_in: usize, - rank_out: usize, - ) -> Self { - GLWESwitchingKey(GGLWECiphertext::alloc( - module, basek, k, rows, digits, rank_in, rank_out, - )) - } - - pub fn bytes_of( - module: &Module<FFT64>, - basek: usize, - k: usize, - rows: usize, - digits: usize, - rank_in: usize, - rank_out: usize, - ) -> usize { - GGLWECiphertext::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out) - } -} - -impl<T, B: Backend> Infos for GLWESwitchingKey<T, B> { - type Inner = MatZnxDft<T, B>; - - fn inner(&self) -> &Self::Inner { - self.0.inner() - } - - fn basek(&self) -> usize { - self.0.basek() - } - - fn k(&self) -> usize { - self.0.k() - } -} - -impl<T, B: Backend> GLWESwitchingKey<T, B> { - pub fn rank(&self) -> usize { - self.0.data.cols_out() - 1 - } - - pub fn rank_in(&self) -> usize { - self.0.data.cols_in() - } - - pub fn rank_out(&self) -> usize { - self.0.data.cols_out() - 1 - } - - pub fn digits(&self) -> usize { - self.0.digits() - } -} - -impl<C: AsRef<[u8]>> GetRow<FFT64> for GLWESwitchingKey<C, FFT64> { - fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>( - &self, - module: &Module<FFT64>, - row_i: usize, - col_j: usize, - res: &mut FourierGLWECiphertext<R, FFT64>, - ) { - module.mat_znx_dft_get_row(&mut res.data, &self.0.data, row_i, col_j); - } -} - -impl<C: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GLWESwitchingKey<C, FFT64> { - fn set_row<R: AsRef<[u8]>>( - &mut self, - module: &Module<FFT64>, - row_i: usize, - col_j: usize, - a: &FourierGLWECiphertext<R, FFT64>, - ) { - module.mat_znx_dft_set_row(&mut self.0.data, row_i, col_j, &a.data); - } -} - -impl GLWESwitchingKey<Vec<u8>, FFT64> { - pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize { - GGLWECiphertext::generate_from_sk_scratch_space(module, basek, k, rank) - } - - pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize { - GGLWECiphertext::generate_from_pk_scratch_space(module, _basek, _k, _rank) - } - - pub fn keyswitch_scratch_space( - module: &Module<FFT64>, - basek: usize, - k_out: usize, - k_in: usize, - k_ksk: usize, - digits: usize, - rank_in: usize, - rank_out: usize, - ) -> usize { - let tmp_in: usize = FourierGLWECiphertext::bytes_of(module, basek, k_in, rank_in); - let tmp_out: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank_out); - let ksk: usize = - FourierGLWECiphertext::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out); - tmp_in + tmp_out + ksk - } - - pub fn keyswitch_inplace_scratch_space( - module: &Module<FFT64>, - basek: usize, - k_out: usize, - k_ksk: usize, - digits: usize, - rank: usize, - ) -> usize { - let tmp: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank); - let ksk: usize = FourierGLWECiphertext::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank); - tmp + ksk - } - - pub fn external_product_scratch_space( - module: &Module<FFT64>, - basek: usize, - k_out: usize, - k_in: usize, - k_ggsw: usize, - digits: usize, - rank: usize, - ) -> usize { - let tmp_in: usize = FourierGLWECiphertext::bytes_of(module, basek, k_in, rank); - let tmp_out: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank); - let ggsw: usize = FourierGLWECiphertext::external_product_scratch_space(module, basek, k_out, k_in, k_ggsw, digits, rank); - tmp_in + tmp_out + ggsw - } - - pub fn external_product_inplace_scratch_space( - module: &Module<FFT64>, - basek: usize, - k_out: usize, - k_ggsw: usize, - digits: usize, - rank: usize, - ) -> usize { - let tmp: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank); - let ggsw: usize = - FourierGLWECiphertext::external_product_inplace_scratch_space(module, basek, k_out, k_ggsw, digits, rank); - tmp + ggsw - } -} -impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> { - pub fn generate_from_sk<DataSkIn: AsRef<[u8]>, DataSkOut: AsRef<[u8]>>( - &mut self, - module: &Module<FFT64>, - sk_in: &GLWESecret<DataSkIn, FFT64>, - sk_out: &GLWESecret<DataSkOut, FFT64>, - source_xa: &mut Source, - source_xe: &mut Source, - sigma: f64, - scratch: &mut Scratch, - ) { - self.0.encrypt_sk( - module, - &sk_in.data, - sk_out, - source_xa, - source_xe, - sigma, - scratch, - ); - } - - pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>( - &mut self, - module: &Module<FFT64>, - lhs: &GLWESwitchingKey<DataLhs, FFT64>, - rhs: &GLWESwitchingKey<DataRhs, FFT64>, - scratch: &mut Scratch, - ) { - #[cfg(debug_assertions)] - { - assert_eq!( - self.rank_in(), - lhs.rank_in(), - "ksk_out input rank: {} != ksk_in input rank: {}", - self.rank_in(), - lhs.rank_in() - ); - assert_eq!( - lhs.rank_out(), - rhs.rank_in(), - "ksk_in output rank: {} != ksk_apply input rank: {}", - self.rank_out(), - rhs.rank_in() - ); - assert_eq!( - self.rank_out(), - rhs.rank_out(), - "ksk_out output rank: {} != ksk_apply output rank: {}", - self.rank_out(), - rhs.rank_out() - ); - } - - let (mut tmp_in, scratch1) = scratch.tmp_glwe_fourier(module, lhs.basek(), lhs.k(), lhs.rank()); - let (mut tmp_out, scratch2) = scratch1.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank()); - - (0..self.rank_in()).for_each(|col_i| { - (0..self.rows()).for_each(|row_j| { - lhs.get_row(module, row_j, col_i, &mut tmp_in); - tmp_out.keyswitch(module, &tmp_in, rhs, scratch2); - self.set_row(module, row_j, col_i, &tmp_out); - }); - }); - - tmp_out.data.zero(); - - (self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| { - (0..self.rank_in()).for_each(|col_j| { - self.set_row(module, row_i, col_j, &tmp_out); - }); - }); - } - - pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>( - &mut self, - module: &Module<FFT64>, - rhs: &GLWESwitchingKey<DataRhs, FFT64>, - scratch: &mut Scratch, - ) { - #[cfg(debug_assertions)] - { - assert_eq!( - self.rank_out(), - rhs.rank_out(), - "ksk_out output rank: {} != ksk_apply output rank: {}", - self.rank_out(), - rhs.rank_out() - ); - } - - let (mut tmp, scratch1) = scratch.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank()); - - (0..self.rank_in()).for_each(|col_i| { - (0..self.rows()).for_each(|row_j| { - self.get_row(module, row_j, col_i, &mut tmp); - tmp.keyswitch_inplace(module, rhs, scratch1); - self.set_row(module, row_j, col_i, &tmp); - }); - }); - } - - pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>( - &mut self, - module: &Module<FFT64>, - lhs: &GLWESwitchingKey<DataLhs, FFT64>, - rhs: &GGSWCiphertext<DataRhs, FFT64>, - scratch: &mut Scratch, - ) { - #[cfg(debug_assertions)] - { - assert_eq!( - self.rank_in(), - lhs.rank_in(), - "ksk_out input rank: {} != ksk_in input rank: {}", - self.rank_in(), - lhs.rank_in() - ); - assert_eq!( - lhs.rank_out(), - rhs.rank(), - "ksk_in output rank: {} != ggsw rank: {}", - self.rank_out(), - rhs.rank() - ); - assert_eq!( - self.rank_out(), - rhs.rank(), - "ksk_out output rank: {} != ggsw rank: {}", - self.rank_out(), - rhs.rank() - ); - } - - let (mut tmp_in, scratch1) = scratch.tmp_glwe_fourier(module, lhs.basek(), lhs.k(), lhs.rank()); - let (mut tmp_out, scratch2) = scratch1.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank()); - - (0..self.rank_in()).for_each(|col_i| { - (0..self.rows()).for_each(|row_j| { - lhs.get_row(module, row_j, col_i, &mut tmp_in); - tmp_out.external_product(module, &tmp_in, rhs, scratch2); - self.set_row(module, row_j, col_i, &tmp_out); - }); - }); - - tmp_out.data.zero(); - - (self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| { - (0..self.rank_in()).for_each(|col_j| { - self.set_row(module, row_i, col_j, &tmp_out); - }); - }); - } - - pub fn external_product_inplace<DataRhs: AsRef<[u8]>>( - &mut self, - module: &Module<FFT64>, - rhs: &GGSWCiphertext<DataRhs, FFT64>, - scratch: &mut Scratch, - ) { - #[cfg(debug_assertions)] - { - assert_eq!( - self.rank_out(), - rhs.rank(), - "ksk_out output rank: {} != ggsw rank: {}", - self.rank_out(), - rhs.rank() - ); - } - - let (mut tmp, scratch1) = scratch.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank()); - println!("tmp: {}", tmp.size()); - (0..self.rank_in()).for_each(|col_i| { - (0..self.rows()).for_each(|row_j| { - self.get_row(module, row_j, col_i, &mut tmp); - tmp.external_product_inplace(module, rhs, scratch1); - self.set_row(module, row_j, col_i, &tmp); - }); - }); - } -}
@@ -1,35 +1,29 @@
-pub mod automorphism; pub mod blind_rotation; +pub mod dist; pub mod elem; pub mod fourier_glwe; pub mod gglwe; pub mod ggsw; pub mod glwe; -pub mod keys; -pub mod keyswitch_key; pub mod lwe; -pub mod tensor_key; -#[cfg(test)] -mod test_fft64; +pub mod noise; mod utils; -pub use automorphism::*; use backend::Backend; use backend::FFT64; use backend::Module; pub use elem::*; -pub use fourier_glwe::*; -pub use gglwe::*; +pub use fourier_glwe::{FourierGLWECiphertext, FourierGLWESecret}; +pub use gglwe::{GGLWECiphertext, GLWEAutomorphismKey, GLWESwitchingKey, GLWETensorKey}; pub use ggsw::*; -pub use glwe::*; -pub use keyswitch_key::*; +pub use glwe::{GLWECiphertext, GLWEOps, GLWEPacker, GLWEPlaintext, GLWEPublicKey, GLWESecret}; +pub(crate) use glwe::{GLWECiphertextToMut, GLWECiphertextToRef}; pub use lwe::*; -pub use tensor_key::*; pub use backend::Scratch; pub use backend::ScratchOwned; -use crate::keys::SecretDistribution; +use crate::dist::Distribution; pub(crate) const SIX_SIGMA: f64 = 6.0;@@ -62,7 +56,8 @@ basek: usize,
k: usize, rank: usize, ) -> (FourierGLWECiphertext<&mut [u8], B>, &mut Self); - fn tmp_sk(&mut self, module: &Module<B>, rank: usize) -> (GLWESecret<&mut [u8], B>, &mut Self); + fn tmp_sk(&mut self, module: &Module<B>, rank: usize) -> (GLWESecret<&mut [u8]>, &mut Self); + fn tmp_fourier_sk(&mut self, module: &Module<B>, rank: usize) -> (FourierGLWESecret<&mut [u8], B>, &mut Self); fn tmp_glwe_pk( &mut self, module: &Module<B>,@@ -88,7 +83,7 @@ k: usize,
rows: usize, digits: usize, rank: usize, - ) -> (TensorKey<&mut [u8], B>, &mut Self); + ) -> (GLWETensorKey<&mut [u8], B>, &mut Self); fn tmp_autokey( &mut self, module: &Module<B>,@@ -97,7 +92,7 @@ k: usize,
rows: usize, digits: usize, rank: usize, - ) -> (AutomorphismKey<&mut [u8], B>, &mut Self); + ) -> (GLWEAutomorphismKey<&mut [u8], B>, &mut Self); } impl ScratchCore<FFT64> for Scratch {@@ -194,22 +189,31 @@ let (data, scratch) = self.tmp_glwe_fourier(module, basek, k, rank);
( GLWEPublicKey { data, - dist: SecretDistribution::NONE, + dist: Distribution::NONE, }, scratch, ) } - fn tmp_sk(&mut self, module: &Module<FFT64>, rank: usize) -> (GLWESecret<&mut [u8], FFT64>, &mut Self) { + fn tmp_sk(&mut self, module: &Module<FFT64>, rank: usize) -> (GLWESecret<&mut [u8]>, &mut Self) { let (data, scratch) = self.tmp_scalar_znx(module, rank); - let (data_fourier, scratch1) = scratch.tmp_scalar_znx_dft(module, rank); ( GLWESecret { data, - data_fourier, - dist: SecretDistribution::NONE, + dist: Distribution::NONE, }, - scratch1, + scratch, + ) + } + + fn tmp_fourier_sk(&mut self, module: &Module<FFT64>, rank: usize) -> (FourierGLWESecret<&mut [u8], FFT64>, &mut Self) { + let (data, scratch) = self.tmp_scalar_znx_dft(module, rank); + ( + FourierGLWESecret { + data, + dist: Distribution::NONE, + }, + scratch, ) }@@ -235,9 +239,9 @@ k: usize,
rows: usize, digits: usize, rank: usize, - ) -> (AutomorphismKey<&mut [u8], FFT64>, &mut Self) { + ) -> (GLWEAutomorphismKey<&mut [u8], FFT64>, &mut Self) { let (data, scratch) = self.tmp_glwe_ksk(module, basek, k, rows, digits, rank, rank); - (AutomorphismKey { key: data, p: 0 }, scratch) + (GLWEAutomorphismKey { key: data, p: 0 }, scratch) } fn tmp_tsk(@@ -248,7 +252,7 @@ k: usize,
rows: usize, digits: usize, rank: usize, - ) -> (TensorKey<&mut [u8], FFT64>, &mut Self) { + ) -> (GLWETensorKey<&mut [u8], FFT64>, &mut Self) { let mut keys: Vec<GLWESwitchingKey<&mut [u8], FFT64>> = Vec::new(); let pairs: usize = (((rank + 1) * rank) >> 1).max(1);@@ -264,6 +268,6 @@ let (gglwe, s) = scratch.tmp_glwe_ksk(module, basek, k, rows, digits, 1, rank);
scratch = s; keys.push(gglwe); } - (TensorKey { keys }, scratch) + (GLWETensorKey { keys }, scratch) } }
@@ -1,18 +1,18 @@
use backend::{ScalarZnx, ZnxInfos, ZnxZero}; use sampling::source::Source; -use crate::SecretDistribution; +use crate::Distribution; pub struct LWESecret<T> { pub(crate) data: ScalarZnx<T>, - pub(crate) dist: SecretDistribution, + pub(crate) dist: Distribution, } impl LWESecret<Vec<u8>> { pub fn alloc(n: usize) -> Self { Self { data: ScalarZnx::new(n, 1), - dist: SecretDistribution::NONE, + dist: Distribution::NONE, } } }@@ -34,31 +34,31 @@
impl<D: AsRef<[u8]> + AsMut<[u8]>> LWESecret<D> { pub fn fill_ternary_prob(&mut self, prob: f64, source: &mut Source) { self.data.fill_ternary_prob(0, prob, source); - self.dist = SecretDistribution::TernaryProb(prob); + self.dist = Distribution::TernaryProb(prob); } pub fn fill_ternary_hw(&mut self, hw: usize, source: &mut Source) { self.data.fill_ternary_hw(0, hw, source); - self.dist = SecretDistribution::TernaryFixed(hw); + self.dist = Distribution::TernaryFixed(hw); } pub fn fill_binary_prob(&mut self, prob: f64, source: &mut Source) { self.data.fill_binary_prob(0, prob, source); - self.dist = SecretDistribution::BinaryProb(prob); + self.dist = Distribution::BinaryProb(prob); } pub fn fill_binary_hw(&mut self, hw: usize, source: &mut Source) { self.data.fill_binary_hw(0, hw, source); - self.dist = SecretDistribution::BinaryFixed(hw); + self.dist = Distribution::BinaryFixed(hw); } pub fn fill_binary_block(&mut self, block_size: usize, source: &mut Source) { self.data.fill_binary_block(0, block_size, source); - self.dist = SecretDistribution::BinaryBlock(block_size); + self.dist = Distribution::BinaryBlock(block_size); } pub fn fill_zero(&mut self) { self.data.zero(); - self.dist = SecretDistribution::ZERO; + self.dist = Distribution::ZERO; } }
@@ -1,125 +0,0 @@
-use backend::{Backend, FFT64, MatZnxDft, Module, ScalarZnxDftOps, Scratch}; -use sampling::source::Source; - -use crate::{GLWESecret, GLWESwitchingKey, Infos, ScratchCore}; - -pub struct TensorKey<C, B: Backend> { - pub(crate) keys: Vec<GLWESwitchingKey<C, B>>, -} - -impl TensorKey<Vec<u8>, FFT64> { - pub fn alloc(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self { - let mut keys: Vec<GLWESwitchingKey<Vec<u8>, FFT64>> = Vec::new(); - let pairs: usize = (((rank + 1) * rank) >> 1).max(1); - (0..pairs).for_each(|_| { - keys.push(GLWESwitchingKey::alloc( - module, basek, k, rows, digits, 1, rank, - )); - }); - Self { keys: keys } - } - - pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize { - let pairs: usize = (((rank + 1) * rank) >> 1).max(1); - pairs * GLWESwitchingKey::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, 1, rank) - } -} - -impl<T, B: Backend> Infos for TensorKey<T, B> { - type Inner = MatZnxDft<T, B>; - - fn inner(&self) -> &Self::Inner { - &self.keys[0].inner() - } - - fn basek(&self) -> usize { - self.keys[0].basek() - } - - fn k(&self) -> usize { - self.keys[0].k() - } -} - -impl<T, B: Backend> TensorKey<T, B> { - pub fn rank(&self) -> usize { - self.keys[0].rank() - } - - pub fn rank_in(&self) -> usize { - self.keys[0].rank_in() - } - - pub fn rank_out(&self) -> usize { - self.keys[0].rank_out() - } - - pub fn digits(&self) -> usize { - self.keys[0].digits() - } -} - -impl TensorKey<Vec<u8>, FFT64> { - pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize { - GLWESecret::bytes_of(module, 1) + GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank) - } -} - -impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> TensorKey<DataSelf, FFT64> { - pub fn generate_from_sk<DataSk: AsRef<[u8]>>( - &mut self, - module: &Module<FFT64>, - sk: &GLWESecret<DataSk, FFT64>, - source_xa: &mut Source, - source_xe: &mut Source, - sigma: f64, - scratch: &mut Scratch, - ) { - #[cfg(debug_assertions)] - { - assert_eq!(self.rank(), sk.rank()); - assert_eq!(self.n(), module.n()); - assert_eq!(sk.n(), module.n()); - } - - let rank: usize = self.rank(); - - let (mut sk_ij, scratch1) = scratch.tmp_sk(module, 1); - - (0..rank).for_each(|i| { - (i..rank).for_each(|j| { - module.svp_apply( - &mut sk_ij.data_fourier, - 0, - &sk.data_fourier, - i, - &sk.data_fourier, - j, - ); - module.svp_idft(&mut sk_ij.data, 0, &sk_ij.data_fourier, 0, scratch1); - self.at_mut(i, j) - .generate_from_sk(module, &sk_ij, sk, source_xa, source_xe, sigma, scratch1); - }); - }) - } - - // Returns a mutable reference to GLWESwitchingKey_{s}(s[i] * s[j]) - pub fn at_mut(&mut self, mut i: usize, mut j: usize) -> &mut GLWESwitchingKey<DataSelf, FFT64> { - if i > j { - std::mem::swap(&mut i, &mut j); - }; - let rank: usize = self.rank(); - &mut self.keys[i * rank + j - (i * (i + 1) / 2)] - } -} - -impl<DataSelf: AsRef<[u8]>> TensorKey<DataSelf, FFT64> { - // Returns a reference to GLWESwitchingKey_{s}(s[i] * s[j]) - pub fn at(&self, mut i: usize, mut j: usize) -> &GLWESwitchingKey<DataSelf, FFT64> { - if i > j { - std::mem::swap(&mut i, &mut j); - }; - let rank: usize = self.rank(); - &self.keys[i * rank + j - (i * (i + 1) / 2)] - } -}
@@ -2,8 +2,8 @@ use backend::{FFT64, Module, ScalarZnxOps, ScratchOwned, Stats, VecZnxOps};
use sampling::source::Source; use crate::{ - AutomorphismKey, FourierGLWECiphertext, GLWEPlaintext, GLWESecret, GetRow, Infos, div_ceil, - test_fft64::log2_std_noise_gglwe_product, + FourierGLWECiphertext, FourierGLWESecret, GLWEAutomorphismKey, GLWEPlaintext, GLWESecret, GetRow, Infos, div_ceil, + noise::log2_std_noise_gglwe_product, }; #[test]@@ -58,24 +58,25 @@
let rows_in: usize = k_in / (basek * digits); let rows_apply: usize = k_in.div_ceil(basek * digits); - let mut auto_key_in: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank); - let mut auto_key_out: AutomorphismKey<Vec<u8>, FFT64> = - AutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank); - let mut auto_key_apply: AutomorphismKey<Vec<u8>, FFT64> = - AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank); + let mut auto_key_in: GLWEAutomorphismKey<Vec<u8>, FFT64> = + GLWEAutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank); + let mut auto_key_out: GLWEAutomorphismKey<Vec<u8>, FFT64> = + GLWEAutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank); + let mut auto_key_apply: GLWEAutomorphismKey<Vec<u8>, FFT64> = + GLWEAutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank); let mut source_xs: Source = Source::new([0u8; 32]); let mut source_xe: Source = Source::new([0u8; 32]); let mut source_xa: Source = Source::new([0u8; 32]); let mut scratch: ScratchOwned = ScratchOwned::new( - AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank) + GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out) - | AutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank), + | GLWEAutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank), ); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); // gglwe_{s1}(s0) = s0 -> s1 auto_key_in.generate_from_sk(@@ -105,7 +106,7 @@
let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_out, rank); let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out); - let mut sk_auto: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); + let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk (0..rank).for_each(|i| { module.scalar_znx_automorphism(@@ -117,12 +118,12 @@ i,
); }); - sk_auto.prep_fourier(&module); + let sk_auto_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_auto); (0..auto_key_out.rank_in()).for_each(|col_i| { (0..auto_key_out.rows()).for_each(|row_i| { auto_key_out.get_row(&module, row_i, col_i, &mut ct_glwe_dft); - ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto, scratch.borrow()); + ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow()); module.vec_znx_sub_scalar_inplace( &mut pt.data,@@ -174,22 +175,23 @@
let rows_in: usize = k_in / (basek * digits); let rows_apply: usize = k_in.div_ceil(basek * digits); - let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank); - let mut auto_key_apply: AutomorphismKey<Vec<u8>, FFT64> = - AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank); + let mut auto_key: GLWEAutomorphismKey<Vec<u8>, FFT64> = + GLWEAutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank); + let mut auto_key_apply: GLWEAutomorphismKey<Vec<u8>, FFT64> = + GLWEAutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank); let mut source_xs: Source = Source::new([0u8; 32]); let mut source_xe: Source = Source::new([0u8; 32]); let mut source_xa: Source = Source::new([0u8; 32]); let mut scratch: ScratchOwned = ScratchOwned::new( - AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank) + GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_in) - | AutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank), + | GLWEAutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank), ); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); // gglwe_{s1}(s0) = s0 -> s1 auto_key.generate_from_sk(@@ -219,8 +221,9 @@
let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank); let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in); - let mut sk_auto: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); + let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk + (0..rank).for_each(|i| { module.scalar_znx_automorphism( module.galois_element_inv(p0 * p1),@@ -231,13 +234,13 @@ i,
); }); - sk_auto.prep_fourier(&module); + let sk_auto_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_auto); (0..auto_key.rank_in()).for_each(|col_i| { (0..auto_key.rows()).for_each(|row_i| { auto_key.get_row(&module, row_i, col_i, &mut ct_glwe_dft); - ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto, scratch.borrow()); + ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow()); module.vec_znx_sub_scalar_inplace( &mut pt.data, 0,
@@ -2,8 +2,9 @@ use backend::{FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxToMut, ScratchOwned, Stats, VecZnxOps, ZnxViewMut};
use sampling::source::Source; use crate::{ - FourierGLWECiphertext, GGSWCiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos, div_ceil, - test_fft64::{log2_std_noise_gglwe_product, noise_ggsw_product}, + FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos, + div_ceil, + noise::{log2_std_noise_gglwe_product, noise_ggsw_product}, }; #[test]@@ -148,16 +149,17 @@ GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_out)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ksk), ); - let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in); - sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in); + sk_in.fill_ternary_prob(0.5, &mut source_xs); - let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out); - sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out); ksk.generate_from_sk( &module, &sk_in, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma,@@ -170,7 +172,7 @@
(0..ksk.rank_in()).for_each(|col_i| { (0..ksk.rows()).for_each(|row_i| { ksk.get_row(&module, row_i, col_i, &mut ct_glwe_fourier); - ct_glwe_fourier.decrypt(&module, &mut pt, &sk_out, scratch.borrow()); + ct_glwe_fourier.decrypt(&module, &mut pt, &sk_out_dft, scratch.borrow()); module.vec_znx_sub_scalar_inplace( &mut pt.data, 0,@@ -247,20 +249,22 @@ ct_gglwe_s1s2.rank_out(),
), ); - let mut sk0: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in_s0s1); - sk0.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in_s0s1); + sk0.fill_ternary_prob(0.5, &mut source_xs); - let mut sk1: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out_s0s1); - sk1.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out_s0s1); + sk1.fill_ternary_prob(0.5, &mut source_xs); + let sk1_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk1); - let mut sk2: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out_s1s2); - sk2.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out_s1s2); + sk2.fill_ternary_prob(0.5, &mut source_xs); + let sk2_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk2); // gglwe_{s1}(s0) = s0 -> s1 ct_gglwe_s0s1.generate_from_sk( &module, &sk0, - &sk1, + &sk1_dft, &mut source_xa, &mut source_xe, sigma,@@ -271,7 +275,7 @@ // gglwe_{s2}(s1) -> s1 -> s2
ct_gglwe_s1s2.generate_from_sk( &module, &sk1, - &sk2, + &sk2_dft, &mut source_xa, &mut source_xe, sigma,@@ -288,7 +292,7 @@
(0..ct_gglwe_s0s2.rank_in()).for_each(|col_i| { (0..ct_gglwe_s0s2.rows()).for_each(|row_i| { ct_gglwe_s0s2.get_row(&module, row_i, col_i, &mut ct_glwe_dft); - ct_glwe_dft.decrypt(&module, &mut pt, &sk2, scratch.borrow()); + ct_glwe_dft.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow()); module.vec_znx_sub_scalar_inplace( &mut pt.data, 0,@@ -350,20 +354,22 @@ | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ksk)
| GLWESwitchingKey::keyswitch_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, rank_out), ); - let mut sk0: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in); - sk0.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in); + sk0.fill_ternary_prob(0.5, &mut source_xs); - let mut sk1: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out); - sk1.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out); + sk1.fill_ternary_prob(0.5, &mut source_xs); + let sk1_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk1); - let mut sk2: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out); - sk2.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out); + sk2.fill_ternary_prob(0.5, &mut source_xs); + let sk2_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk2); // gglwe_{s1}(s0) = s0 -> s1 ct_gglwe_s0s1.generate_from_sk( &module, &sk0, - &sk1, + &sk1_dft, &mut source_xa, &mut source_xe, sigma,@@ -374,7 +380,7 @@ // gglwe_{s2}(s1) -> s1 -> s2
ct_gglwe_s1s2.generate_from_sk( &module, &sk1, - &sk2, + &sk2_dft, &mut source_xa, &mut source_xe, sigma,@@ -392,7 +398,7 @@
(0..ct_gglwe_s0s2.rank_in()).for_each(|col_i| { (0..ct_gglwe_s0s2.rows()).for_each(|row_i| { ct_gglwe_s0s2.get_row(&module, row_i, col_i, &mut ct_glwe_dft); - ct_glwe_dft.decrypt(&module, &mut pt, &sk2, scratch.borrow()); + ct_glwe_dft.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow()); module.vec_znx_sub_scalar_inplace( &mut pt.data, 0,@@ -464,17 +470,18 @@ let r: usize = 1;
pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r} - let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in); - sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in); + sk_in.fill_ternary_prob(0.5, &mut source_xs); - let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out); - sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out); // gglwe_{s1}(s0) = s0 -> s1 ct_gglwe_in.generate_from_sk( &module, &sk_in, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma,@@ -484,7 +491,7 @@
ct_rgsw.encrypt_sk( &module, &pt_rgsw, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma,@@ -504,7 +511,7 @@
(0..rank_in).for_each(|col_i| { (0..ct_gglwe_out.rows()).for_each(|row_i| { ct_gglwe_out.get_row(&module, row_i, col_i, &mut ct_glwe_dft); - ct_glwe_dft.decrypt(&module, &mut pt, &sk_out, scratch.borrow()); + ct_glwe_dft.decrypt(&module, &mut pt, &sk_out_dft, scratch.borrow()); module.vec_znx_sub_scalar_inplace( &mut pt.data,@@ -584,17 +591,18 @@ let r: usize = 1;
pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r} - let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in); - sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in); + sk_in.fill_ternary_prob(0.5, &mut source_xs); - let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out); - sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out); // gglwe_{s1}(s0) = s0 -> s1 ct_gglwe.generate_from_sk( &module, &sk_in, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma,@@ -604,7 +612,7 @@
ct_rgsw.encrypt_sk( &module, &pt_rgsw, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma,@@ -624,7 +632,7 @@
(0..rank_in).for_each(|col_i| { (0..ct_gglwe.rows()).for_each(|row_i| { ct_gglwe.get_row(&module, row_i, col_i, &mut ct_glwe_dft); - ct_glwe_dft.decrypt(&module, &mut pt, &sk_out, scratch.borrow()); + ct_glwe_dft.decrypt(&module, &mut pt, &sk_out_dft, scratch.borrow()); module.vec_znx_sub_scalar_inplace( &mut pt.data,
@@ -5,9 +5,9 @@ };
use sampling::source::Source; use crate::{ - FourierGLWECiphertext, GGSWCiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos, TensorKey, - automorphism::AutomorphismKey, - test_fft64::{noise_ggsw_keyswitch, noise_ggsw_product}, + FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWEAutomorphismKey, GLWEPlaintext, GLWESecret, GLWESwitchingKey, + GLWETensorKey, GetRow, Infos, div_ceil, + noise::{noise_ggsw_keyswitch, noise_ggsw_product}, }; #[test]@@ -142,13 +142,14 @@ GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k, rank)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k), ); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); ct.encrypt_sk( &module, &pt_scalar, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -172,14 +173,14 @@
// mul with sk[col_j-1] if col_j > 0 { module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0); - module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1); + module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft.data, col_j - 1); module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0); module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow()); } ct.get_row(&module, row_i, col_j, &mut ct_glwe_fourier); - ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk, scratch.borrow()); + ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow()); module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);@@ -209,7 +210,7 @@ let digits_in: usize = 1;
let mut ct_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_in, rows, digits_in, rank); let mut ct_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_out, rows, digits_in, rank); - let mut tsk: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_ksk, rows, digits, rank); + let mut tsk: GLWETensorKey<Vec<u8>, FFT64> = GLWETensorKey::alloc(&module, basek, k_ksk, rows, digits, rank); let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank); let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out); let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);@@ -223,7 +224,7 @@ let mut scratch: ScratchOwned = ScratchOwned::new(
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_in, rank) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out) | GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank) - | TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank) + | GLWETensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank) | GGSWCiphertext::keyswitch_scratch_space( &module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank, ),@@ -231,16 +232,18 @@ );
let var_xs: f64 = 0.5; - let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk_in.fill_ternary_prob(&module, var_xs, &mut source_xs); + let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk_in.fill_ternary_prob(var_xs, &mut source_xs); + let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in); - let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk_out.fill_ternary_prob(&module, var_xs, &mut source_xs); + let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk_out.fill_ternary_prob(var_xs, &mut source_xs); + let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out); ksk.generate_from_sk( &module, &sk_in, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma,@@ -248,7 +251,7 @@ scratch.borrow(),
); tsk.generate_from_sk( &module, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma,@@ -260,7 +263,7 @@
ct_in.encrypt_sk( &module, &pt_scalar, - &sk_in, + &sk_in_dft, &mut source_xa, &mut source_xe, sigma,@@ -280,14 +283,14 @@
// mul with sk[col_j-1] if col_j > 0 { module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0); - module.svp_apply_inplace(&mut pt_dft, 0, &sk_out.data_fourier, col_j - 1); + module.svp_apply_inplace(&mut pt_dft, 0, &sk_out_dft.data, col_j - 1); module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0); module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow()); } ct_out.get_row(&module, row_i, col_j, &mut ct_glwe_fourier); - ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_out, scratch.borrow()); + ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow()); module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);@@ -336,7 +339,7 @@
let digits_in: usize = 1; let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ct, rows, digits_in, rank); - let mut tsk: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank); + let mut tsk: GLWETensorKey<Vec<u8>, FFT64> = GLWETensorKey::alloc(&module, basek, k_tsk, rows, digits, rank); let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank); let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);@@ -350,22 +353,24 @@ let mut scratch: ScratchOwned = ScratchOwned::new(
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ct, rank) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ct) | GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank) - | TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank) + | GLWETensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank) | GGSWCiphertext::keyswitch_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank), ); let var_xs: f64 = 0.5; - let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk_in.fill_ternary_prob(&module, var_xs, &mut source_xs); + let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk_in.fill_ternary_prob(var_xs, &mut source_xs); + let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in); - let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk_out.fill_ternary_prob(&module, var_xs, &mut source_xs); + let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk_out.fill_ternary_prob(var_xs, &mut source_xs); + let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out); ksk.generate_from_sk( &module, &sk_in, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma,@@ -373,7 +378,7 @@ scratch.borrow(),
); tsk.generate_from_sk( &module, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma,@@ -385,7 +390,7 @@
ct.encrypt_sk( &module, &pt_scalar, - &sk_in, + &sk_in_dft, &mut source_xa, &mut source_xe, sigma,@@ -411,14 +416,14 @@
// mul with sk[col_j-1] if col_j > 0 { module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0); - module.svp_apply_inplace(&mut pt_dft, 0, &sk_out.data_fourier, col_j - 1); + module.svp_apply_inplace(&mut pt_dft, 0, &sk_out_dft.data, col_j - 1); module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0); module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow()); } ct.get_row(&module, row_i, col_j, &mut ct_glwe_fourier); - ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_out, scratch.borrow()); + ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow()); module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);@@ -471,8 +476,8 @@ let digits_in: usize = 1;
let mut ct_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_in, rows_in, digits_in, rank); let mut ct_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_out, rows_in, digits_in, rank); - let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank); - let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); + let mut tensor_key: GLWETensorKey<Vec<u8>, FFT64> = GLWETensorKey::alloc(&module, basek, k_tsk, rows, digits, rank); + let mut auto_key: GLWEAutomorphismKey<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out); let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out); let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);@@ -484,8 +489,8 @@
let mut scratch: ScratchOwned = ScratchOwned::new( GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_in, rank) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out) - | AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank) - | TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank) + | GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank) + | GLWETensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank) | GGSWCiphertext::automorphism_scratch_space( &module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank, ),@@ -493,8 +498,9 @@ );
let var_xs: f64 = 0.5; - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, var_xs, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(var_xs, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); auto_key.generate_from_sk( &module,@@ -507,7 +513,7 @@ scratch.borrow(),
); tensor_key.generate_from_sk( &module, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -519,7 +525,7 @@
ct_in.encrypt_sk( &module, &pt_scalar, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -541,14 +547,14 @@
// mul with sk[col_j-1] if col_j > 0 { module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0); - module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1); + module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft.data, col_j - 1); module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0); module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow()); } ct_out.get_row(&module, row_i, col_j, &mut ct_glwe_fourier); - ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk, scratch.borrow()); + ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow()); module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);@@ -596,8 +602,8 @@ let rows_in: usize = k_ct.div_euclid(basek * digits);
let digits_in: usize = 1; let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ct, rows_in, digits_in, rank); - let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank); - let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); + let mut tensor_key: GLWETensorKey<Vec<u8>, FFT64> = GLWETensorKey::alloc(&module, basek, k_tsk, rows, digits, rank); + let mut auto_key: GLWEAutomorphismKey<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct); let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);@@ -609,15 +615,16 @@
let mut scratch: ScratchOwned = ScratchOwned::new( GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ct, rank) | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ct) - | AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank) - | TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank) + | GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank) + | GLWETensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank) | GGSWCiphertext::automorphism_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank), ); let var_xs: f64 = 0.5; - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, var_xs, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(var_xs, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); auto_key.generate_from_sk( &module,@@ -630,7 +637,7 @@ scratch.borrow(),
); tensor_key.generate_from_sk( &module, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -642,7 +649,7 @@
ct.encrypt_sk( &module, &pt_scalar, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -664,14 +671,14 @@
// mul with sk[col_j-1] if col_j > 0 { module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0); - module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1); + module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft.data, col_j - 1); module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0); module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow()); } ct.get_row(&module, row_i, col_j, &mut ct_glwe_fourier); - ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk, scratch.borrow()); + ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow()); module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);@@ -742,13 +749,14 @@ | GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ggsw, rank)
| GGSWCiphertext::external_product_scratch_space(&module, basek, k_out, k_in, k_ggsw, digits, rank), ); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); ct_ggsw_rhs.encrypt_sk( &module, &pt_ggsw_rhs, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -758,7 +766,7 @@
ct_ggsw_lhs_in.encrypt_sk( &module, &pt_ggsw_lhs, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -787,13 +795,13 @@ );
if col_j > 0 { module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0); - module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1); + module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft.data, col_j - 1); module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0); module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow()); } ct_ggsw_lhs_out.get_row(&module, row_i, col_j, &mut ct_glwe_fourier); - ct_glwe_fourier.decrypt(&module, &mut pt, &sk, scratch.borrow()); + ct_glwe_fourier.decrypt(&module, &mut pt, &sk_dft, scratch.borrow()); module.vec_znx_sub_ab_inplace(&mut pt.data, 0, &pt_want.data, 0);@@ -862,13 +870,14 @@ | GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ggsw, rank)
| GGSWCiphertext::external_product_inplace_scratch_space(&module, basek, k_ct, k_ggsw, digits, rank), ); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); ct_ggsw_rhs.encrypt_sk( &module, &pt_ggsw_rhs, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -878,7 +887,7 @@
ct_ggsw_lhs.encrypt_sk( &module, &pt_ggsw_lhs, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -907,13 +916,13 @@ );
if col_j > 0 { module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0); - module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1); + module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft.data, col_j - 1); module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0); module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow()); } ct_ggsw_lhs.get_row(&module, row_i, col_j, &mut ct_glwe_fourier); - ct_glwe_fourier.decrypt(&module, &mut pt, &sk, scratch.borrow()); + ct_glwe_fourier.decrypt(&module, &mut pt, &sk_dft, scratch.borrow()); module.vec_znx_sub_ab_inplace(&mut pt.data, 0, &pt_want.data, 0);
@@ -1,9 +1,4 @@
-mod automorphism_key; -mod gglwe; -mod ggsw; -mod glwe_fourier; -mod tensor_key; - +#[allow(dead_code)] pub(crate) fn var_noise_gglwe_product( n: f64, basek: usize,@@ -34,6 +29,7 @@ noise /= b_scale * b_scale;
noise } +#[allow(dead_code)] pub(crate) fn log2_std_noise_gglwe_product( n: f64, basek: usize,@@ -62,6 +58,7 @@ noise = noise.sqrt();
noise.log2().min(-1.0).max(-(a_logq as f64)) // max noise is [-2^{-1}, 2^{-1}] } +#[allow(dead_code)] pub(crate) fn noise_ggsw_product( n: f64, basek: usize,@@ -94,6 +91,7 @@ noise /= b_scale;
noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}] } +#[allow(dead_code)] pub(crate) fn noise_ggsw_keyswitch( n: f64, basek: usize,
@@ -1,7 +1,7 @@
use backend::{FFT64, Module, ScalarZnxDftOps, ScratchOwned, Stats, VecZnxOps}; use sampling::source::Source; -use crate::{FourierGLWECiphertext, GLWEPlaintext, GLWESecret, GetRow, Infos, TensorKey}; +use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWEPlaintext, GLWESecret, GLWETensorKey, GetRow, Infos}; #[test] fn encrypt_sk() {@@ -17,25 +17,26 @@ let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = k / basek; - let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k, rows, 1, rank); + let mut tensor_key: GLWETensorKey<Vec<u8>, FFT64> = GLWETensorKey::alloc(&module, basek, k, rows, 1, rank); let mut source_xs: Source = Source::new([0u8; 32]); let mut source_xe: Source = Source::new([0u8; 32]); let mut source_xa: Source = Source::new([0u8; 32]); - let mut scratch: ScratchOwned = ScratchOwned::new(TensorKey::generate_from_sk_scratch_space( + let mut scratch: ScratchOwned = ScratchOwned::new(GLWETensorKey::generate_from_sk_scratch_space( &module, basek, tensor_key.k(), rank, )); - let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk); tensor_key.generate_from_sk( &module, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma,@@ -45,25 +46,19 @@
let mut ct_glwe_fourier: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k, rank); let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k); - let mut sk_ij = GLWESecret::alloc(&module, 1); + let mut sk_ij_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::alloc(&module, 1); + let mut sk_ij: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, 1); (0..rank).for_each(|i| { (0..rank).for_each(|j| { - module.svp_apply( - &mut sk_ij.data_fourier, - 0, - &sk.data_fourier, - i, - &sk.data_fourier, - j, - ); - module.svp_idft(&mut sk_ij.data, 0, &sk_ij.data_fourier, 0, scratch.borrow()); + module.svp_apply(&mut sk_ij_dft.data, 0, &sk_dft.data, i, &sk_dft.data, j); + module.scalar_znx_idft(&mut sk_ij.data, 0, &sk_ij_dft.data, 0, scratch.borrow()); (0..tensor_key.rank_in()).for_each(|col_i| { (0..tensor_key.rows()).for_each(|row_i| { tensor_key .at(i, j) .get_row(&module, row_i, col_i, &mut ct_glwe_fourier); - ct_glwe_fourier.decrypt(&module, &mut pt, &sk, scratch.borrow()); + ct_glwe_fourier.decrypt(&module, &mut pt, &sk_dft, scratch.borrow()); module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk_ij.data, col_i); let std_pt: f64 = pt.data.std(0, basek) * (k as f64).exp2(); assert!((sigma - std_pt).abs() <= 0.5, "{} {}", sigma, std_pt);