From e8cfb5e2ab647b8d4a64078444eac64d5e21af0b Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bossuat Date: Fri, 13 Jun 2025 18:57:28 +0200 Subject: [PATCH] Reorganized other crates --- backend/src/scalar_znx_dft_ops.rs | 4 +- core/benches/external_product_glwe_fft64.rs | 20 +- core/benches/keyswitch_glwe_fft64.rs | 30 +- core/src/blind_rotation/key.rs | 12 +- core/src/{keys.rs => dist.rs} | 2 +- core/src/fourier_glwe/ciphertext.rs | 45 ++ core/src/fourier_glwe/decryption.rs | 84 +++ core/src/fourier_glwe/encryption.rs | 32 + core/src/fourier_glwe/external_product.rs | 129 ++++ core/src/fourier_glwe/keyswitch.rs | 56 ++ core/src/fourier_glwe/mod.rs | 12 + core/src/fourier_glwe/secret.rs | 58 ++ .../test_fft64/external_product.rs | 246 +++++++ core/src/fourier_glwe/test_fft64/keyswitch.rs | 235 +++++++ core/src/fourier_glwe/test_fft64/mod.rs | 2 + core/src/gglwe/automorphism.rs | 136 ++++ core/src/gglwe/automorphism_key.rs | 83 +++ core/src/gglwe/ciphertext.rs | 131 ++++ core/src/gglwe/encryption.rs | 253 +++++++ core/src/gglwe/external_product.rs | 162 +++++ core/src/gglwe/keyswitch.rs | 163 +++++ core/src/gglwe/keyswitch_key.rs | 91 +++ core/src/gglwe/mod.rs | 16 + core/src/{ => gglwe}/tensor_key.rs | 60 +- .../test_fft64/automorphism_key.rs | 51 +- core/src/{ => gglwe}/test_fft64/gglwe.rs | 88 +-- core/src/gglwe/test_fft64/mod.rs | 3 + core/src/{ => gglwe}/test_fft64/tensor_key.rs | 29 +- core/src/{ggsw.rs => ggsw/ciphertext.rs} | 20 +- core/src/ggsw/mod.rs | 6 + core/src/{ => ggsw}/test_fft64/ggsw.rs | 133 ++-- core/src/ggsw/test_fft64/mod.rs | 1 + core/src/glwe/automorphism.rs | 18 +- core/src/glwe/decryption.rs | 6 +- core/src/glwe/encryption.rs | 24 +- core/src/glwe/mod.rs | 25 +- core/src/glwe/packing.rs | 14 +- core/src/glwe/plaintext.rs | 6 +- core/src/glwe/public_key.rs | 10 +- core/src/glwe/secret.rs | 63 +- core/src/glwe/test_fft64/automorphism.rs | 223 ++++++ core/src/glwe/test_fft64/encryption.rs | 29 +- core/src/glwe/test_fft64/external_product.rs | 644 +----------------- core/src/glwe/test_fft64/keyswitch.rs | 226 ++++++ core/src/glwe/test_fft64/packing.rs | 27 +- core/src/glwe/test_fft64/trace.rs | 23 +- core/src/glwe/trace.rs | 6 +- core/src/keyswitch_key.rs | 343 ---------- core/src/lib.rs | 56 +- core/src/lwe/mod.rs | 3 + core/src/{lwe.rs => lwe/secret.rs} | 18 +- core/src/{test_fft64/mod.rs => noise.rs} | 10 +- 52 files changed, 2787 insertions(+), 1380 deletions(-) rename core/src/{keys.rs => dist.rs} (92%) create mode 100644 core/src/fourier_glwe/ciphertext.rs create mode 100644 core/src/fourier_glwe/decryption.rs create mode 100644 core/src/fourier_glwe/encryption.rs create mode 100644 core/src/fourier_glwe/external_product.rs create mode 100644 core/src/fourier_glwe/keyswitch.rs create mode 100644 core/src/fourier_glwe/mod.rs create mode 100644 core/src/fourier_glwe/secret.rs create mode 100644 core/src/fourier_glwe/test_fft64/external_product.rs create mode 100644 core/src/fourier_glwe/test_fft64/keyswitch.rs create mode 100644 core/src/fourier_glwe/test_fft64/mod.rs create mode 100644 core/src/gglwe/automorphism.rs create mode 100644 core/src/gglwe/automorphism_key.rs create mode 100644 core/src/gglwe/ciphertext.rs create mode 100644 core/src/gglwe/encryption.rs create mode 100644 core/src/gglwe/external_product.rs create mode 100644 core/src/gglwe/keyswitch.rs create mode 100644 core/src/gglwe/keyswitch_key.rs create mode 100644 core/src/gglwe/mod.rs rename core/src/{ => gglwe}/tensor_key.rs (52%) rename core/src/{ => gglwe}/test_fft64/automorphism_key.rs (76%) rename core/src/{ => gglwe}/test_fft64/gglwe.rs (86%) create mode 100644 core/src/gglwe/test_fft64/mod.rs rename core/src/{ => gglwe}/test_fft64/tensor_key.rs (65%) rename core/src/{ggsw.rs => ggsw/ciphertext.rs} (97%) create mode 100644 core/src/ggsw/mod.rs rename core/src/{ => ggsw}/test_fft64/ggsw.rs (86%) create mode 100644 core/src/ggsw/test_fft64/mod.rs delete mode 100644 core/src/keyswitch_key.rs create mode 100644 core/src/lwe/mod.rs rename core/src/{lwe.rs => lwe/secret.rs} (73%) rename core/src/{test_fft64/mod.rs => noise.rs} (97%) diff --git a/backend/src/scalar_znx_dft_ops.rs b/backend/src/scalar_znx_dft_ops.rs index 6d227c7..c89808d 100644 --- a/backend/src/scalar_znx_dft_ops.rs +++ b/backend/src/scalar_znx_dft_ops.rs @@ -29,7 +29,7 @@ pub trait ScalarZnxDftOps { R: VecZnxDftToMut, A: ScalarZnxDftToRef; - fn svp_idft(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch) + fn scalar_znx_idft(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch) where R: ScalarZnxToMut, A: ScalarZnxDftToRef; @@ -50,7 +50,7 @@ impl ScalarZnxDftAlloc for Module { } impl ScalarZnxDftOps for Module { - fn svp_idft(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch) + fn scalar_znx_idft(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch) where R: ScalarZnxToMut, A: ScalarZnxDftToRef, diff --git a/core/benches/external_product_glwe_fft64.rs b/core/benches/external_product_glwe_fft64.rs index e81829c..c48c626 100644 --- a/core/benches/external_product_glwe_fft64.rs +++ b/core/benches/external_product_glwe_fft64.rs @@ -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 @@ fn bench_external_product_glwe_fft64(c: &mut Criterion) { let mut source_xe = Source::new([0u8; 32]); let mut source_xa = Source::new([0u8; 32]); - let mut sk: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, 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 @@ fn bench_external_product_glwe_fft64(c: &mut Criterion) { ct_glwe_in.encrypt_zero_sk( &module, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, @@ -134,13 +135,14 @@ fn bench_external_product_glwe_inplace_fft64(c: &mut Criterion) { let mut source_xe = Source::new([0u8; 32]); let mut source_xa = Source::new([0u8; 32]); - let mut sk: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, 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 @@ fn bench_external_product_glwe_inplace_fft64(c: &mut Criterion) { ct_glwe.encrypt_zero_sk( &module, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, diff --git a/core/benches/keyswitch_glwe_fft64.rs b/core/benches/keyswitch_glwe_fft64.rs index 66a1d02..9de1e9c 100644 --- a/core/benches/keyswitch_glwe_fft64.rs +++ b/core/benches/keyswitch_glwe_fft64.rs @@ -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 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) { let rows: usize = (p.k_ct_in + (p.basek * digits) - 1) / (p.basek * digits); let sigma: f64 = 3.2; - let mut ksk: AutomorphismKey, FFT64> = AutomorphismKey::alloc(&module, basek, k_grlwe, rows, digits, rank_out); + let mut ksk: GLWEAutomorphismKey, FFT64> = + GLWEAutomorphismKey::alloc(&module, basek, k_grlwe, rows, digits, rank_out); let mut ct_in: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_rlwe_in, rank_in); let mut ct_out: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_rlwe_out, rank_out); @@ -55,11 +56,12 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) { let mut source_xe = Source::new([0u8; 32]); let mut source_xa = Source::new([0u8; 32]); - let mut sk_in: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_in); - sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_in: GLWESecret> = GLWESecret::alloc(&module, rank_in); + sk_in.fill_ternary_prob(0.5, &mut source_xs); + let sk_in_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_in); - let mut sk_out: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_out); - sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_out: GLWESecret> = GLWESecret::alloc(&module, rank_out); + sk_out.fill_ternary_prob(0.5, &mut source_xs); ksk.generate_from_sk( &module, @@ -73,7 +75,7 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) { ct_in.encrypt_zero_sk( &module, - &sk_in, + &sk_in_dft, &mut source_xa, &mut source_xe, sigma, @@ -146,16 +148,18 @@ fn bench_keyswitch_glwe_inplace_fft64(c: &mut Criterion) { let mut source_xe: Source = Source::new([0u8; 32]); let mut source_xa: Source = Source::new([0u8; 32]); - let mut sk_in: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk_in.fill_ternary_prob(&&module, 0.5, &mut source_xs); + let mut sk_in: GLWESecret> = GLWESecret::alloc(&module, rank); + sk_in.fill_ternary_prob(0.5, &mut source_xs); + let sk_in_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_in); - let mut sk_out: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk_out.fill_ternary_prob(&&module, 0.5, &mut source_xs); + let mut sk_out: GLWESecret> = GLWESecret::alloc(&module, rank); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret, 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 @@ fn bench_keyswitch_glwe_inplace_fft64(c: &mut Criterion) { ct.encrypt_zero_sk( &module, - &sk_in, + &sk_in_dft, &mut source_xa, &mut source_xe, sigma, diff --git a/core/src/blind_rotation/key.rs b/core/src/blind_rotation/key.rs index ff4a887..270080d 100644 --- a/core/src/blind_rotation/key.rs +++ b/core/src/blind_rotation/key.rs @@ -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 { pub(crate) data: Vec, B>>, - pub(crate) dist: SecretDistribution, + pub(crate) dist: Distribution, } pub struct BlindRotationKeyFHEW { pub(crate) data: Vec, B>>, - pub(crate) auto: Vec, B>>, + pub(crate) auto: Vec, B>>, } impl BlindRotationKeyCGGI { @@ -19,7 +19,7 @@ impl BlindRotationKeyCGGI { (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 @@ impl BlindRotationKeyCGGI { pub fn generate_from_sk( &mut self, module: &Module, - sk_glwe: &GLWESecret, + sk_glwe: &FourierGLWESecret, sk_lwe: &LWESecret, source_xa: &mut Source, source_xe: &mut Source, @@ -46,7 +46,7 @@ impl BlindRotationKeyCGGI { 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"), } } diff --git a/core/src/keys.rs b/core/src/dist.rs similarity index 92% rename from core/src/keys.rs rename to core/src/dist.rs index 45bdc61..4a97369 100644 --- a/core/src/keys.rs +++ b/core/src/dist.rs @@ -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 diff --git a/core/src/fourier_glwe/ciphertext.rs b/core/src/fourier_glwe/ciphertext.rs new file mode 100644 index 0000000..425191f --- /dev/null +++ b/core/src/fourier_glwe/ciphertext.rs @@ -0,0 +1,45 @@ +use backend::{Backend, Module, VecZnxDft, VecZnxDftAlloc}; + +use crate::{Infos, div_ceil}; + +pub struct FourierGLWECiphertext { + pub data: VecZnxDft, + pub basek: usize, + pub k: usize, +} + +impl FourierGLWECiphertext, B> { + pub fn alloc(module: &Module, 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, basek: usize, k: usize, rank: usize) -> usize { + module.bytes_of_vec_znx_dft(rank + 1, div_ceil(k, basek)) + } +} + +impl Infos for FourierGLWECiphertext { + type Inner = VecZnxDft; + + fn inner(&self) -> &Self::Inner { + &self.data + } + + fn basek(&self) -> usize { + self.basek + } + + fn k(&self) -> usize { + self.k + } +} + +impl FourierGLWECiphertext { + pub fn rank(&self) -> usize { + self.cols() - 1 + } +} diff --git a/core/src/fourier_glwe/decryption.rs b/core/src/fourier_glwe/decryption.rs new file mode 100644 index 0000000..882be61 --- /dev/null +++ b/core/src/fourier_glwe/decryption.rs @@ -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, FFT64> { + pub fn decrypt_scratch_space(module: &Module, 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> FourierGLWECiphertext { + pub fn decrypt + AsMut<[u8]>, DataSk: AsRef<[u8]>>( + &self, + module: &Module, + pt: &mut GLWEPlaintext, + sk: &FourierGLWESecret, + 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 + AsMut<[u8]>>( + &self, + module: &Module, + res: &mut GLWECiphertext, + 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); + }); + } +} diff --git a/core/src/fourier_glwe/encryption.rs b/core/src/fourier_glwe/encryption.rs new file mode 100644 index 0000000..d23ff4a --- /dev/null +++ b/core/src/fourier_glwe/encryption.rs @@ -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, FFT64> { + #[allow(dead_code)] + pub(crate) fn idft_scratch_space(module: &Module, 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, 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 + AsRef<[u8]>> FourierGLWECiphertext { + pub fn encrypt_zero_sk>( + &mut self, + module: &Module, + sk: &FourierGLWESecret, + 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); + } +} diff --git a/core/src/fourier_glwe/external_product.rs b/core/src/fourier_glwe/external_product.rs new file mode 100644 index 0000000..116416b --- /dev/null +++ b/core/src/fourier_glwe/external_product.rs @@ -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, FFT64> { + // WARNING TODO: UPDATE + pub fn external_product_scratch_space( + module: &Module, + 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, + 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 + AsRef<[u8]>> FourierGLWECiphertext { + pub fn external_product, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module, + lhs: &FourierGLWECiphertext, + rhs: &GGSWCiphertext, + 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>( + &mut self, + module: &Module, + rhs: &GGSWCiphertext, + scratch: &mut Scratch, + ) { + unsafe { + let self_ptr: *mut FourierGLWECiphertext = self as *mut FourierGLWECiphertext; + self.external_product(&module, &*self_ptr, rhs, scratch); + } + } +} diff --git a/core/src/fourier_glwe/keyswitch.rs b/core/src/fourier_glwe/keyswitch.rs new file mode 100644 index 0000000..3abb26e --- /dev/null +++ b/core/src/fourier_glwe/keyswitch.rs @@ -0,0 +1,56 @@ +use backend::{FFT64, Module, Scratch}; + +use crate::{FourierGLWECiphertext, GLWECiphertext, GLWESwitchingKey, Infos, ScratchCore}; + +impl FourierGLWECiphertext, FFT64> { + pub fn keyswitch_scratch_space( + module: &Module, + 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, + 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 + AsRef<[u8]>> FourierGLWECiphertext { + pub fn keyswitch, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module, + lhs: &FourierGLWECiphertext, + rhs: &GLWESwitchingKey, + 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>( + &mut self, + module: &Module, + rhs: &GLWESwitchingKey, + scratch: &mut Scratch, + ) { + unsafe { + let self_ptr: *mut FourierGLWECiphertext = self as *mut FourierGLWECiphertext; + self.keyswitch(&module, &*self_ptr, rhs, scratch); + } + } +} diff --git a/core/src/fourier_glwe/mod.rs b/core/src/fourier_glwe/mod.rs new file mode 100644 index 0000000..35c9905 --- /dev/null +++ b/core/src/fourier_glwe/mod.rs @@ -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; diff --git a/core/src/fourier_glwe/secret.rs b/core/src/fourier_glwe/secret.rs new file mode 100644 index 0000000..0f28939 --- /dev/null +++ b/core/src/fourier_glwe/secret.rs @@ -0,0 +1,58 @@ +use backend::{Backend, FFT64, Module, ScalarZnxDft, ScalarZnxDftAlloc, ScalarZnxDftOps, ZnxInfos}; + +use crate::{GLWESecret, dist::Distribution}; + +pub struct FourierGLWESecret { + pub(crate) data: ScalarZnxDft, + pub(crate) dist: Distribution, +} + +impl FourierGLWESecret, B> { + pub fn alloc(module: &Module, rank: usize) -> Self { + Self { + data: module.new_scalar_znx_dft(rank), + dist: Distribution::NONE, + } + } + + pub fn bytes_of(module: &Module, rank: usize) -> usize { + module.bytes_of_scalar_znx_dft(rank) + } +} + +impl FourierGLWESecret, FFT64> { + pub fn from(module: &Module, sk: &GLWESecret) -> Self + where + D: AsRef<[u8]>, + { + let mut sk_dft: FourierGLWESecret, FFT64> = Self::alloc(module, sk.rank()); + sk_dft.set(module, sk); + sk_dft + } +} + +impl FourierGLWESecret { + 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 + AsRef<[u8]>> FourierGLWESecret { + pub(crate) fn set(&mut self, module: &Module, sk: &GLWESecret) + where + D: AsRef<[u8]>, + { + (0..self.rank()).for_each(|i| { + module.svp_prepare(&mut self.data, i, &sk.data, i); + }); + self.dist = sk.dist + } +} diff --git a/core/src/fourier_glwe/test_fft64/external_product.rs b/core/src/fourier_glwe/test_fft64/external_product.rs new file mode 100644 index 0000000..2228d29 --- /dev/null +++ b/core/src/fourier_glwe/test_fft64/external_product.rs @@ -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 = Module::::new(1 << log_n); + + let rows: usize = div_ceil(k_in, digits * basek); + + let mut ct_ggsw: GGSWCiphertext, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank); + let mut ct_in: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_in, rank); + let mut ct_out: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_out, rank); + let mut ct_in_dft: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank); + let mut ct_out_dft: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_out, rank); + let mut pt_rgsw: ScalarZnx> = module.new_scalar_znx(1); + let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_in); + let mut pt_have: GLWEPlaintext> = 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> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, 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 = Module::::new(1 << log_n); + let rows: usize = div_ceil(k_ct, digits * basek); + + let mut ct_ggsw: GGSWCiphertext, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank); + let mut ct: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_ct, rank); + let mut ct_rlwe_dft: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank); + let mut pt_rgsw: ScalarZnx> = module.new_scalar_znx(1); + let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); + let mut pt_have: GLWEPlaintext> = 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> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, 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); +} diff --git a/core/src/fourier_glwe/test_fft64/keyswitch.rs b/core/src/fourier_glwe/test_fft64/keyswitch.rs new file mode 100644 index 0000000..a459964 --- /dev/null +++ b/core/src/fourier_glwe/test_fft64/keyswitch.rs @@ -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 = Module::::new(1 << log_n); + + let rows: usize = div_ceil(k_in, basek * digits); + + let mut ksk: GLWESwitchingKey, FFT64> = + GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out); + let mut ct_glwe_in: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_in, rank_in); + let mut ct_glwe_dft_in: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank_in); + let mut ct_glwe_out: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_out, rank_out); + let mut ct_glwe_dft_out: FourierGLWECiphertext, FFT64> = + FourierGLWECiphertext::alloc(&module, basek, k_out, rank_out); + let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_in); + let mut pt_have: GLWEPlaintext> = 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> = GLWESecret::alloc(&module, rank_in); + sk_in.fill_ternary_prob(0.5, &mut source_xs); + let sk_in_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_in); + + let mut sk_out: GLWESecret> = GLWESecret::alloc(&module, rank_out); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret, 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 = Module::::new(1 << log_n); + + let rows: usize = div_ceil(k_ct, basek * digits); + + let mut ksk: GLWESwitchingKey, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank); + let mut ct_glwe: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_ct, rank); + let mut ct_rlwe_dft: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank); + let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); + let mut pt_have: GLWEPlaintext> = 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> = GLWESecret::alloc(&module, rank); + sk_in.fill_ternary_prob(0.5, &mut source_xs); + let sk_in_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_in); + + let mut sk_out: GLWESecret> = GLWESecret::alloc(&module, rank); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret, 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 + ); +} diff --git a/core/src/fourier_glwe/test_fft64/mod.rs b/core/src/fourier_glwe/test_fft64/mod.rs new file mode 100644 index 0000000..784c37c --- /dev/null +++ b/core/src/fourier_glwe/test_fft64/mod.rs @@ -0,0 +1,2 @@ +pub mod external_product; +pub mod keyswitch; diff --git a/core/src/gglwe/automorphism.rs b/core/src/gglwe/automorphism.rs new file mode 100644 index 0000000..5460511 --- /dev/null +++ b/core/src/gglwe/automorphism.rs @@ -0,0 +1,136 @@ +use backend::{FFT64, Module, Scratch, VecZnx, VecZnxDftOps, VecZnxOps, ZnxZero}; + +use crate::{FourierGLWECiphertext, GLWEAutomorphismKey, GLWECiphertext, GetRow, Infos, ScratchCore, SetRow}; + +impl GLWEAutomorphismKey, FFT64> { + pub fn automorphism_scratch_space( + module: &Module, + 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, + 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 + AsRef<[u8]>> GLWEAutomorphismKey { + pub fn automorphism, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module, + lhs: &GLWEAutomorphismKey, + rhs: &GLWEAutomorphismKey, + 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>( + &mut self, + module: &Module, + rhs: &GLWEAutomorphismKey, + scratch: &mut Scratch, + ) { + unsafe { + let self_ptr: *mut GLWEAutomorphismKey = self as *mut GLWEAutomorphismKey; + self.automorphism(&module, &*self_ptr, rhs, scratch); + } + } +} diff --git a/core/src/gglwe/automorphism_key.rs b/core/src/gglwe/automorphism_key.rs new file mode 100644 index 0000000..26fea52 --- /dev/null +++ b/core/src/gglwe/automorphism_key.rs @@ -0,0 +1,83 @@ +use backend::{Backend, FFT64, MatZnxDft, MatZnxDftOps, Module}; + +use crate::{FourierGLWECiphertext, GLWESwitchingKey, GetRow, Infos, SetRow}; + +pub struct GLWEAutomorphismKey { + pub(crate) key: GLWESwitchingKey, + pub(crate) p: i64, +} + +impl GLWEAutomorphismKey, FFT64> { + pub fn alloc(module: &Module, 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, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize { + GLWESwitchingKey::, FFT64>::bytes_of(module, basek, k, rows, digits, rank, rank) + } +} + +impl Infos for GLWEAutomorphismKey { + type Inner = MatZnxDft; + + fn inner(&self) -> &Self::Inner { + &self.key.inner() + } + + fn basek(&self) -> usize { + self.key.basek() + } + + fn k(&self) -> usize { + self.key.k() + } +} + +impl GLWEAutomorphismKey { + 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> GetRow for GLWEAutomorphismKey { + fn get_row + AsRef<[u8]>>( + &self, + module: &Module, + row_i: usize, + col_j: usize, + res: &mut FourierGLWECiphertext, + ) { + module.mat_znx_dft_get_row(&mut res.data, &self.key.0.data, row_i, col_j); + } +} + +impl + AsRef<[u8]>> SetRow for GLWEAutomorphismKey { + fn set_row>( + &mut self, + module: &Module, + row_i: usize, + col_j: usize, + a: &FourierGLWECiphertext, + ) { + module.mat_znx_dft_set_row(&mut self.key.0.data, row_i, col_j, &a.data); + } +} diff --git a/core/src/gglwe/ciphertext.rs b/core/src/gglwe/ciphertext.rs new file mode 100644 index 0000000..a4c2f1d --- /dev/null +++ b/core/src/gglwe/ciphertext.rs @@ -0,0 +1,131 @@ +use backend::{Backend, FFT64, MatZnxDft, MatZnxDftAlloc, MatZnxDftOps, Module}; + +use crate::{FourierGLWECiphertext, GetRow, Infos, SetRow, div_ceil}; + +pub struct GGLWECiphertext { + pub(crate) data: MatZnxDft, + pub(crate) basek: usize, + pub(crate) k: usize, + pub(crate) digits: usize, +} + +impl GGLWECiphertext, B> { + pub fn alloc( + module: &Module, + 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, + 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 Infos for GGLWECiphertext { + type Inner = MatZnxDft; + + fn inner(&self) -> &Self::Inner { + &self.data + } + + fn basek(&self) -> usize { + self.basek + } + + fn k(&self) -> usize { + self.k + } +} + +impl GGLWECiphertext { + 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> GetRow for GGLWECiphertext { + fn get_row + AsRef<[u8]>>( + &self, + module: &Module, + row_i: usize, + col_j: usize, + res: &mut FourierGLWECiphertext, + ) { + module.mat_znx_dft_get_row(&mut res.data, &self.data, row_i, col_j); + } +} + +impl + AsRef<[u8]>> SetRow for GGLWECiphertext { + fn set_row>( + &mut self, + module: &Module, + row_i: usize, + col_j: usize, + a: &FourierGLWECiphertext, + ) { + module.mat_znx_dft_set_row(&mut self.data, row_i, col_j, &a.data); + } +} diff --git a/core/src/gglwe/encryption.rs b/core/src/gglwe/encryption.rs new file mode 100644 index 0000000..7c4838b --- /dev/null +++ b/core/src/gglwe/encryption.rs @@ -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, FFT64> { + pub fn generate_from_sk_scratch_space(module: &Module, 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, _basek: usize, _k: usize, _rank: usize) -> usize { + unimplemented!() + } +} + +impl + AsRef<[u8]>> GGLWECiphertext { + pub fn encrypt_sk, DataSk: AsRef<[u8]>>( + &mut self, + module: &Module, + pt: &ScalarZnx, + sk: &FourierGLWESecret, + 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, FFT64> { + pub fn encrypt_sk_scratch_space(module: &Module, 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, _basek: usize, _k: usize, _rank: usize) -> usize { + GGLWECiphertext::generate_from_pk_scratch_space(module, _basek, _k, _rank) + } +} + +impl + AsRef<[u8]>> GLWESwitchingKey { + pub fn generate_from_sk, DataSkOut: AsRef<[u8]>>( + &mut self, + module: &Module, + sk_in: &GLWESecret, + sk_out: &FourierGLWESecret, + 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, FFT64> { + pub fn generate_from_sk_scratch_space(module: &Module, 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, _basek: usize, _k: usize, _rank: usize) -> usize { + GGLWECiphertext::generate_from_pk_scratch_space(module, _basek, _k, _rank) + } +} + +impl + AsRef<[u8]>> GLWEAutomorphismKey { + pub fn generate_from_sk>( + &mut self, + module: &Module, + p: i64, + sk: &GLWESecret, + 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, FFT64> { + pub fn generate_from_sk_scratch_space(module: &Module, 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 + AsRef<[u8]>> GLWETensorKey { + pub fn generate_from_sk>( + &mut self, + module: &Module, + sk: &FourierGLWESecret, + 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); + }); + }) + } +} diff --git a/core/src/gglwe/external_product.rs b/core/src/gglwe/external_product.rs new file mode 100644 index 0000000..2e063ef --- /dev/null +++ b/core/src/gglwe/external_product.rs @@ -0,0 +1,162 @@ +use backend::{FFT64, Module, Scratch, ZnxZero}; + +use crate::{FourierGLWECiphertext, GGSWCiphertext, GLWEAutomorphismKey, GLWESwitchingKey, GetRow, Infos, ScratchCore, SetRow}; + +impl GLWESwitchingKey, FFT64> { + pub fn external_product_scratch_space( + module: &Module, + 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, + 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 + AsRef<[u8]>> GLWESwitchingKey { + pub fn external_product, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module, + lhs: &GLWESwitchingKey, + rhs: &GGSWCiphertext, + 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>( + &mut self, + module: &Module, + rhs: &GGSWCiphertext, + 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, FFT64> { + pub fn external_product_scratch_space( + module: &Module, + 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, + 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 + AsRef<[u8]>> GLWEAutomorphismKey { + pub fn external_product, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module, + lhs: &GLWEAutomorphismKey, + rhs: &GGSWCiphertext, + scratch: &mut Scratch, + ) { + self.key.external_product(module, &lhs.key, rhs, scratch); + } + + pub fn external_product_inplace>( + &mut self, + module: &Module, + rhs: &GGSWCiphertext, + scratch: &mut Scratch, + ) { + self.key.external_product_inplace(module, rhs, scratch); + } +} diff --git a/core/src/gglwe/keyswitch.rs b/core/src/gglwe/keyswitch.rs new file mode 100644 index 0000000..632309d --- /dev/null +++ b/core/src/gglwe/keyswitch.rs @@ -0,0 +1,163 @@ +use backend::{FFT64, Module, Scratch, ZnxZero}; + +use crate::{FourierGLWECiphertext, GLWEAutomorphismKey, GLWESwitchingKey, GetRow, Infos, ScratchCore, SetRow}; + +impl GLWEAutomorphismKey, FFT64> { + pub fn keyswitch_scratch_space( + module: &Module, + 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, + 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 + AsRef<[u8]>> GLWEAutomorphismKey { + pub fn keyswitch, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module, + lhs: &GLWEAutomorphismKey, + rhs: &GLWESwitchingKey, + scratch: &mut Scratch, + ) { + self.key.keyswitch(module, &lhs.key, rhs, scratch); + } + + pub fn keyswitch_inplace>( + &mut self, + module: &Module, + rhs: &GLWEAutomorphismKey, + scratch: &mut Scratch, + ) { + self.key.keyswitch_inplace(module, &rhs.key, scratch); + } +} + +impl GLWESwitchingKey, FFT64> { + pub fn keyswitch_scratch_space( + module: &Module, + 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, + 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 + AsRef<[u8]>> GLWESwitchingKey { + pub fn keyswitch, DataRhs: AsRef<[u8]>>( + &mut self, + module: &Module, + lhs: &GLWESwitchingKey, + rhs: &GLWESwitchingKey, + 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>( + &mut self, + module: &Module, + rhs: &GLWESwitchingKey, + 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); + }); + }); + } +} diff --git a/core/src/gglwe/keyswitch_key.rs b/core/src/gglwe/keyswitch_key.rs new file mode 100644 index 0000000..965d596 --- /dev/null +++ b/core/src/gglwe/keyswitch_key.rs @@ -0,0 +1,91 @@ +use backend::{Backend, FFT64, MatZnxDft, MatZnxDftOps, Module}; + +use crate::{FourierGLWECiphertext, GGLWECiphertext, GetRow, Infos, SetRow}; + +pub struct GLWESwitchingKey(pub(crate) GGLWECiphertext); + +impl GLWESwitchingKey, FFT64> { + pub fn alloc( + module: &Module, + 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, + basek: usize, + k: usize, + rows: usize, + digits: usize, + rank_in: usize, + rank_out: usize, + ) -> usize { + GGLWECiphertext::, FFT64>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out) + } +} + +impl Infos for GLWESwitchingKey { + type Inner = MatZnxDft; + + fn inner(&self) -> &Self::Inner { + self.0.inner() + } + + fn basek(&self) -> usize { + self.0.basek() + } + + fn k(&self) -> usize { + self.0.k() + } +} + +impl GLWESwitchingKey { + 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> GetRow for GLWESwitchingKey { + fn get_row + AsRef<[u8]>>( + &self, + module: &Module, + row_i: usize, + col_j: usize, + res: &mut FourierGLWECiphertext, + ) { + module.mat_znx_dft_get_row(&mut res.data, &self.0.data, row_i, col_j); + } +} + +impl + AsRef<[u8]>> SetRow for GLWESwitchingKey { + fn set_row>( + &mut self, + module: &Module, + row_i: usize, + col_j: usize, + a: &FourierGLWECiphertext, + ) { + module.mat_znx_dft_set_row(&mut self.0.data, row_i, col_j, &a.data); + } +} diff --git a/core/src/gglwe/mod.rs b/core/src/gglwe/mod.rs new file mode 100644 index 0000000..4c2d20a --- /dev/null +++ b/core/src/gglwe/mod.rs @@ -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; diff --git a/core/src/tensor_key.rs b/core/src/gglwe/tensor_key.rs similarity index 52% rename from core/src/tensor_key.rs rename to core/src/gglwe/tensor_key.rs index c0887c9..c12c1f5 100644 --- a/core/src/tensor_key.rs +++ b/core/src/gglwe/tensor_key.rs @@ -1,13 +1,12 @@ -use backend::{Backend, FFT64, MatZnxDft, Module, ScalarZnxDftOps, Scratch}; -use sampling::source::Source; +use backend::{Backend, FFT64, MatZnxDft, Module}; -use crate::{GLWESecret, GLWESwitchingKey, Infos, ScratchCore}; +use crate::{GLWESwitchingKey, Infos}; -pub struct TensorKey { +pub struct GLWETensorKey { pub(crate) keys: Vec>, } -impl TensorKey, FFT64> { +impl GLWETensorKey, FFT64> { pub fn alloc(module: &Module, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self { let mut keys: Vec, FFT64>> = Vec::new(); let pairs: usize = (((rank + 1) * rank) >> 1).max(1); @@ -25,7 +24,7 @@ impl TensorKey, FFT64> { } } -impl Infos for TensorKey { +impl Infos for GLWETensorKey { type Inner = MatZnxDft; fn inner(&self) -> &Self::Inner { @@ -41,7 +40,7 @@ impl Infos for TensorKey { } } -impl TensorKey { +impl GLWETensorKey { pub fn rank(&self) -> usize { self.keys[0].rank() } @@ -59,50 +58,7 @@ impl TensorKey { } } -impl TensorKey, FFT64> { - pub fn generate_from_sk_scratch_space(module: &Module, basek: usize, k: usize, rank: usize) -> usize { - GLWESecret::bytes_of(module, 1) + GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank) - } -} - -impl + AsRef<[u8]>> TensorKey { - pub fn generate_from_sk>( - &mut self, - module: &Module, - sk: &GLWESecret, - 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); - }); - }) - } - +impl + AsRef<[u8]>> GLWETensorKey { // 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 { if i > j { @@ -113,7 +69,7 @@ impl + AsRef<[u8]>> TensorKey { } } -impl> TensorKey { +impl> GLWETensorKey { // Returns a reference to GLWESwitchingKey_{s}(s[i] * s[j]) pub fn at(&self, mut i: usize, mut j: usize) -> &GLWESwitchingKey { if i > j { diff --git a/core/src/test_fft64/automorphism_key.rs b/core/src/gglwe/test_fft64/automorphism_key.rs similarity index 76% rename from core/src/test_fft64/automorphism_key.rs rename to core/src/gglwe/test_fft64/automorphism_key.rs index 93cd5d6..c06f0be 100644 --- a/core/src/test_fft64/automorphism_key.rs +++ b/core/src/gglwe/test_fft64/automorphism_key.rs @@ -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 @@ fn test_automorphism( let rows_in: usize = k_in / (basek * digits); let rows_apply: usize = k_in.div_ceil(basek * digits); - let mut auto_key_in: AutomorphismKey, FFT64> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank); - let mut auto_key_out: AutomorphismKey, FFT64> = - AutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank); - let mut auto_key_apply: AutomorphismKey, FFT64> = - AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank); + let mut auto_key_in: GLWEAutomorphismKey, FFT64> = + GLWEAutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank); + let mut auto_key_out: GLWEAutomorphismKey, FFT64> = + GLWEAutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank); + let mut auto_key_apply: GLWEAutomorphismKey, 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, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = 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 @@ fn test_automorphism( let mut ct_glwe_dft: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_out, rank); let mut pt: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_out); - let mut sk_auto: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); + let mut sk_auto: GLWESecret> = 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 @@ fn test_automorphism( ); }); - sk_auto.prep_fourier(&module); + let sk_auto_dft: FourierGLWESecret, 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 @@ fn test_automorphism_inplace( let rows_in: usize = k_in / (basek * digits); let rows_apply: usize = k_in.div_ceil(basek * digits); - let mut auto_key: AutomorphismKey, FFT64> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank); - let mut auto_key_apply: AutomorphismKey, FFT64> = - AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank); + let mut auto_key: GLWEAutomorphismKey, FFT64> = + GLWEAutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank); + let mut auto_key_apply: GLWEAutomorphismKey, 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, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = 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 @@ fn test_automorphism_inplace( let mut ct_glwe_dft: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank); let mut pt: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_in); - let mut sk_auto: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); + let mut sk_auto: GLWESecret> = 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 @@ fn test_automorphism_inplace( ); }); - sk_auto.prep_fourier(&module); + let sk_auto_dft: FourierGLWESecret, 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, diff --git a/core/src/test_fft64/gglwe.rs b/core/src/gglwe/test_fft64/gglwe.rs similarity index 86% rename from core/src/test_fft64/gglwe.rs rename to core/src/gglwe/test_fft64/gglwe.rs index bde6308..39aad9f 100644 --- a/core/src/test_fft64/gglwe.rs +++ b/core/src/gglwe/test_fft64/gglwe.rs @@ -2,8 +2,9 @@ use backend::{FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxToMut, ScratchO 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 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ksk: usize, digits: usize, rank | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ksk), ); - let mut sk_in: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_in); - sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_in: GLWESecret> = GLWESecret::alloc(&module, rank_in); + sk_in.fill_ternary_prob(0.5, &mut source_xs); - let mut sk_out: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_out); - sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_out: GLWESecret> = GLWESecret::alloc(&module, rank_out); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret, 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 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ksk: usize, digits: usize, rank (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 @@ fn test_key_switch( ), ); - let mut sk0: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_in_s0s1); - sk0.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk0: GLWESecret> = GLWESecret::alloc(&module, rank_in_s0s1); + sk0.fill_ternary_prob(0.5, &mut source_xs); - let mut sk1: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_out_s0s1); - sk1.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk1: GLWESecret> = GLWESecret::alloc(&module, rank_out_s0s1); + sk1.fill_ternary_prob(0.5, &mut source_xs); + let sk1_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk1); - let mut sk2: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_out_s1s2); - sk2.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk2: GLWESecret> = GLWESecret::alloc(&module, rank_out_s1s2); + sk2.fill_ternary_prob(0.5, &mut source_xs); + let sk2_dft: FourierGLWESecret, 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 @@ fn test_key_switch( ct_gglwe_s1s2.generate_from_sk( &module, &sk1, - &sk2, + &sk2_dft, &mut source_xa, &mut source_xe, sigma, @@ -288,7 +292,7 @@ fn test_key_switch( (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 @@ fn test_key_switch_inplace( | GLWESwitchingKey::keyswitch_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, rank_out), ); - let mut sk0: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_in); - sk0.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk0: GLWESecret> = GLWESecret::alloc(&module, rank_in); + sk0.fill_ternary_prob(0.5, &mut source_xs); - let mut sk1: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_out); - sk1.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk1: GLWESecret> = GLWESecret::alloc(&module, rank_out); + sk1.fill_ternary_prob(0.5, &mut source_xs); + let sk1_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk1); - let mut sk2: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_out); - sk2.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk2: GLWESecret> = GLWESecret::alloc(&module, rank_out); + sk2.fill_ternary_prob(0.5, &mut source_xs); + let sk2_dft: FourierGLWESecret, 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 @@ fn test_key_switch_inplace( ct_gglwe_s1s2.generate_from_sk( &module, &sk1, - &sk2, + &sk2_dft, &mut source_xa, &mut source_xe, sigma, @@ -392,7 +398,7 @@ fn test_key_switch_inplace( (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 @@ fn test_external_product( pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r} - let mut sk_in: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_in); - sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_in: GLWESecret> = GLWESecret::alloc(&module, rank_in); + sk_in.fill_ternary_prob(0.5, &mut source_xs); - let mut sk_out: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_out); - sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_out: GLWESecret> = GLWESecret::alloc(&module, rank_out); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret, 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 @@ fn test_external_product( ct_rgsw.encrypt_sk( &module, &pt_rgsw, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma, @@ -504,7 +511,7 @@ fn test_external_product( (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 @@ fn test_external_product_inplace( pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r} - let mut sk_in: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_in); - sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_in: GLWESecret> = GLWESecret::alloc(&module, rank_in); + sk_in.fill_ternary_prob(0.5, &mut source_xs); - let mut sk_out: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank_out); - sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk_out: GLWESecret> = GLWESecret::alloc(&module, rank_out); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret, 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 @@ fn test_external_product_inplace( ct_rgsw.encrypt_sk( &module, &pt_rgsw, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma, @@ -624,7 +632,7 @@ fn test_external_product_inplace( (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, diff --git a/core/src/gglwe/test_fft64/mod.rs b/core/src/gglwe/test_fft64/mod.rs new file mode 100644 index 0000000..49d23cd --- /dev/null +++ b/core/src/gglwe/test_fft64/mod.rs @@ -0,0 +1,3 @@ +pub mod automorphism_key; +pub mod gglwe; +pub mod tensor_key; diff --git a/core/src/test_fft64/tensor_key.rs b/core/src/gglwe/test_fft64/tensor_key.rs similarity index 65% rename from core/src/test_fft64/tensor_key.rs rename to core/src/gglwe/test_fft64/tensor_key.rs index e9827cb..be69625 100644 --- a/core/src/test_fft64/tensor_key.rs +++ b/core/src/gglwe/test_fft64/tensor_key.rs @@ -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 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, sigma: f64, rank: usize let rows: usize = k / basek; - let mut tensor_key: TensorKey, FFT64> = TensorKey::alloc(&module, basek, k, rows, 1, rank); + let mut tensor_key: GLWETensorKey, 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, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, 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 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, sigma: f64, rank: usize let mut ct_glwe_fourier: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k, rank); let mut pt: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k); - let mut sk_ij = GLWESecret::alloc(&module, 1); + let mut sk_ij_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::alloc(&module, 1); + let mut sk_ij: GLWESecret> = 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); diff --git a/core/src/ggsw.rs b/core/src/ggsw/ciphertext.rs similarity index 97% rename from core/src/ggsw.rs rename to core/src/ggsw/ciphertext.rs index 38842df..ac1fae5 100644 --- a/core/src/ggsw.rs +++ b/core/src/ggsw/ciphertext.rs @@ -6,8 +6,8 @@ use backend::{ 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 { @@ -246,7 +246,7 @@ impl + AsRef<[u8]>> GGSWCiphertext { &mut self, module: &Module, pt: &ScalarZnx, - sk: &GLWESecret, + sk: &FourierGLWESecret, source_xa: &mut Source, source_xe: &mut Source, sigma: f64, @@ -304,7 +304,7 @@ impl + AsRef<[u8]>> GGSWCiphertext { col_j: usize, res: &mut R, ci_dft: &VecZnxDft, - tsk: &TensorKey, + tsk: &GLWETensorKey, scratch: &mut Scratch, ) where R: VecZnxToMut, @@ -408,7 +408,7 @@ impl + AsRef<[u8]>> GGSWCiphertext { module: &Module, lhs: &GGSWCiphertext, ksk: &GLWESwitchingKey, - tsk: &TensorKey, + tsk: &GLWETensorKey, scratch: &mut Scratch, ) { let rank: usize = self.rank(); @@ -449,7 +449,7 @@ impl + AsRef<[u8]>> GGSWCiphertext { &mut self, module: &Module, ksk: &GLWESwitchingKey, - tsk: &TensorKey, + tsk: &GLWETensorKey, scratch: &mut Scratch, ) { unsafe { @@ -462,8 +462,8 @@ impl + AsRef<[u8]>> GGSWCiphertext { &mut self, module: &Module, lhs: &GGSWCiphertext, - auto_key: &AutomorphismKey, - tensor_key: &TensorKey, + auto_key: &GLWEAutomorphismKey, + tensor_key: &GLWETensorKey, scratch: &mut Scratch, ) { #[cfg(debug_assertions)] @@ -551,8 +551,8 @@ impl + AsRef<[u8]>> GGSWCiphertext { pub fn automorphism_inplace, DataTsk: AsRef<[u8]>>( &mut self, module: &Module, - auto_key: &AutomorphismKey, - tensor_key: &TensorKey, + auto_key: &GLWEAutomorphismKey, + tensor_key: &GLWETensorKey, scratch: &mut Scratch, ) { unsafe { diff --git a/core/src/ggsw/mod.rs b/core/src/ggsw/mod.rs new file mode 100644 index 0000000..f27b96b --- /dev/null +++ b/core/src/ggsw/mod.rs @@ -0,0 +1,6 @@ +pub mod ciphertext; + +pub use ciphertext::GGSWCiphertext; + +#[cfg(test)] +mod test_fft64; diff --git a/core/src/test_fft64/ggsw.rs b/core/src/ggsw/test_fft64/ggsw.rs similarity index 86% rename from core/src/test_fft64/ggsw.rs rename to core/src/ggsw/test_fft64/ggsw.rs index bcfb950..9219703 100644 --- a/core/src/test_fft64/ggsw.rs +++ b/core/src/ggsw/test_fft64/ggsw.rs @@ -5,9 +5,9 @@ use backend::{ 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 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, digits: usize, rank: us | FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k), ); - let mut sk: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, 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 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, digits: usize, rank: us // 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 @@ fn test_keyswitch( let mut ct_in: GGSWCiphertext, FFT64> = GGSWCiphertext::alloc(&module, basek, k_in, rows, digits_in, rank); let mut ct_out: GGSWCiphertext, FFT64> = GGSWCiphertext::alloc(&module, basek, k_out, rows, digits_in, rank); - let mut tsk: TensorKey, FFT64> = TensorKey::alloc(&module, basek, k_ksk, rows, digits, rank); + let mut tsk: GLWETensorKey, FFT64> = GLWETensorKey::alloc(&module, basek, k_ksk, rows, digits, rank); let mut ksk: GLWESwitchingKey, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank); let mut pt_have: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_out); let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_out); @@ -223,7 +224,7 @@ fn test_keyswitch( 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 @@ fn test_keyswitch( let var_xs: f64 = 0.5; - let mut sk_in: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk_in.fill_ternary_prob(&module, var_xs, &mut source_xs); + let mut sk_in: GLWESecret> = GLWESecret::alloc(&module, rank); + sk_in.fill_ternary_prob(var_xs, &mut source_xs); + let sk_in_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_in); - let mut sk_out: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk_out.fill_ternary_prob(&module, var_xs, &mut source_xs); + let mut sk_out: GLWESecret> = GLWESecret::alloc(&module, rank); + sk_out.fill_ternary_prob(var_xs, &mut source_xs); + let sk_out_dft: FourierGLWESecret, 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 @@ fn test_keyswitch( ); tsk.generate_from_sk( &module, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma, @@ -260,7 +263,7 @@ fn test_keyswitch( ct_in.encrypt_sk( &module, &pt_scalar, - &sk_in, + &sk_in_dft, &mut source_xa, &mut source_xe, sigma, @@ -280,14 +283,14 @@ fn test_keyswitch( // 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 @@ fn test_keyswitch_inplace( let digits_in: usize = 1; let mut ct: GGSWCiphertext, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ct, rows, digits_in, rank); - let mut tsk: TensorKey, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank); + let mut tsk: GLWETensorKey, FFT64> = GLWETensorKey::alloc(&module, basek, k_tsk, rows, digits, rank); let mut ksk: GLWESwitchingKey, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank); let mut pt_have: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); @@ -350,22 +353,24 @@ fn test_keyswitch_inplace( 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, FFT64> = GLWESecret::alloc(&module, rank); - sk_in.fill_ternary_prob(&module, var_xs, &mut source_xs); + let mut sk_in: GLWESecret> = GLWESecret::alloc(&module, rank); + sk_in.fill_ternary_prob(var_xs, &mut source_xs); + let sk_in_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_in); - let mut sk_out: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk_out.fill_ternary_prob(&module, var_xs, &mut source_xs); + let mut sk_out: GLWESecret> = GLWESecret::alloc(&module, rank); + sk_out.fill_ternary_prob(var_xs, &mut source_xs); + let sk_out_dft: FourierGLWESecret, 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 @@ fn test_keyswitch_inplace( ); tsk.generate_from_sk( &module, - &sk_out, + &sk_out_dft, &mut source_xa, &mut source_xe, sigma, @@ -385,7 +390,7 @@ fn test_keyswitch_inplace( ct.encrypt_sk( &module, &pt_scalar, - &sk_in, + &sk_in_dft, &mut source_xa, &mut source_xe, sigma, @@ -411,14 +416,14 @@ fn test_keyswitch_inplace( // 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 @@ fn test_automorphism( let mut ct_in: GGSWCiphertext, FFT64> = GGSWCiphertext::alloc(&module, basek, k_in, rows_in, digits_in, rank); let mut ct_out: GGSWCiphertext, FFT64> = GGSWCiphertext::alloc(&module, basek, k_out, rows_in, digits_in, rank); - let mut tensor_key: TensorKey, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank); - let mut auto_key: AutomorphismKey, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); + let mut tensor_key: GLWETensorKey, FFT64> = GLWETensorKey::alloc(&module, basek, k_tsk, rows, digits, rank); + let mut auto_key: GLWEAutomorphismKey, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); let mut pt_have: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_out); let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_out); let mut pt_scalar: ScalarZnx> = module.new_scalar_znx(1); @@ -484,8 +489,8 @@ fn test_automorphism( 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 @@ fn test_automorphism( let var_xs: f64 = 0.5; - let mut sk: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, var_xs, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(var_xs, &mut source_xs); + let sk_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk); auto_key.generate_from_sk( &module, @@ -507,7 +513,7 @@ fn test_automorphism( ); tensor_key.generate_from_sk( &module, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, @@ -519,7 +525,7 @@ fn test_automorphism( ct_in.encrypt_sk( &module, &pt_scalar, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, @@ -541,14 +547,14 @@ fn test_automorphism( // 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 @@ fn test_automorphism_inplace( let digits_in: usize = 1; let mut ct: GGSWCiphertext, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ct, rows_in, digits_in, rank); - let mut tensor_key: TensorKey, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank); - let mut auto_key: AutomorphismKey, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); + let mut tensor_key: GLWETensorKey, FFT64> = GLWETensorKey::alloc(&module, basek, k_tsk, rows, digits, rank); + let mut auto_key: GLWEAutomorphismKey, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); let mut pt_have: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); let mut pt_scalar: ScalarZnx> = module.new_scalar_znx(1); @@ -609,15 +615,16 @@ fn test_automorphism_inplace( 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, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, var_xs, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(var_xs, &mut source_xs); + let sk_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk); auto_key.generate_from_sk( &module, @@ -630,7 +637,7 @@ fn test_automorphism_inplace( ); tensor_key.generate_from_sk( &module, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, @@ -642,7 +649,7 @@ fn test_automorphism_inplace( ct.encrypt_sk( &module, &pt_scalar, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, @@ -664,14 +671,14 @@ fn test_automorphism_inplace( // 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 @@ fn test_external_product( | GGSWCiphertext::external_product_scratch_space(&module, basek, k_out, k_in, k_ggsw, digits, rank), ); - let mut sk: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, 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 @@ fn test_external_product( ct_ggsw_lhs_in.encrypt_sk( &module, &pt_ggsw_lhs, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, @@ -787,13 +795,13 @@ fn test_external_product( 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 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw | GGSWCiphertext::external_product_inplace_scratch_space(&module, basek, k_ct, k_ggsw, digits, rank), ); - let mut sk: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, 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 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw ct_ggsw_lhs.encrypt_sk( &module, &pt_ggsw_lhs, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, @@ -907,13 +916,13 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw 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); diff --git a/core/src/ggsw/test_fft64/mod.rs b/core/src/ggsw/test_fft64/mod.rs new file mode 100644 index 0000000..3326f10 --- /dev/null +++ b/core/src/ggsw/test_fft64/mod.rs @@ -0,0 +1 @@ +mod ggsw; diff --git a/core/src/glwe/automorphism.rs b/core/src/glwe/automorphism.rs index a4165aa..1513362 100644 --- a/core/src/glwe/automorphism.rs +++ b/core/src/glwe/automorphism.rs @@ -1,6 +1,6 @@ use backend::{FFT64, Module, Scratch, VecZnxOps}; -use crate::{AutomorphismKey, GLWECiphertext}; +use crate::{GLWEAutomorphismKey, GLWECiphertext}; impl GLWECiphertext> { pub fn automorphism_scratch_space( @@ -32,7 +32,7 @@ impl + AsMut<[u8]>> GLWECiphertext { &mut self, module: &Module, lhs: &GLWECiphertext, - rhs: &AutomorphismKey, + rhs: &GLWEAutomorphismKey, scratch: &mut Scratch, ) { self.keyswitch(module, lhs, &rhs.key, scratch); @@ -44,7 +44,7 @@ impl + AsMut<[u8]>> GLWECiphertext { pub fn automorphism_inplace>( &mut self, module: &Module, - rhs: &AutomorphismKey, + rhs: &GLWEAutomorphismKey, scratch: &mut Scratch, ) { self.keyswitch_inplace(module, &rhs.key, scratch); @@ -57,7 +57,7 @@ impl + AsMut<[u8]>> GLWECiphertext { &mut self, module: &Module, lhs: &GLWECiphertext, - rhs: &AutomorphismKey, + rhs: &GLWEAutomorphismKey, scratch: &mut Scratch, ) { Self::keyswitch_private::<_, _, 1>(self, rhs.p(), module, lhs, &rhs.key, scratch); @@ -66,7 +66,7 @@ impl + AsMut<[u8]>> GLWECiphertext { pub fn automorphism_add_inplace>( &mut self, module: &Module, - rhs: &AutomorphismKey, + rhs: &GLWEAutomorphismKey, scratch: &mut Scratch, ) { unsafe { @@ -79,7 +79,7 @@ impl + AsMut<[u8]>> GLWECiphertext { &mut self, module: &Module, lhs: &GLWECiphertext, - rhs: &AutomorphismKey, + rhs: &GLWEAutomorphismKey, scratch: &mut Scratch, ) { Self::keyswitch_private::<_, _, 2>(self, rhs.p(), module, lhs, &rhs.key, scratch); @@ -88,7 +88,7 @@ impl + AsMut<[u8]>> GLWECiphertext { pub fn automorphism_sub_ab_inplace>( &mut self, module: &Module, - rhs: &AutomorphismKey, + rhs: &GLWEAutomorphismKey, scratch: &mut Scratch, ) { unsafe { @@ -101,7 +101,7 @@ impl + AsMut<[u8]>> GLWECiphertext { &mut self, module: &Module, lhs: &GLWECiphertext, - rhs: &AutomorphismKey, + rhs: &GLWEAutomorphismKey, scratch: &mut Scratch, ) { Self::keyswitch_private::<_, _, 3>(self, rhs.p(), module, lhs, &rhs.key, scratch); @@ -110,7 +110,7 @@ impl + AsMut<[u8]>> GLWECiphertext { pub fn automorphism_sub_ba_inplace>( &mut self, module: &Module, - rhs: &AutomorphismKey, + rhs: &GLWEAutomorphismKey, scratch: &mut Scratch, ) { unsafe { diff --git a/core/src/glwe/decryption.rs b/core/src/glwe/decryption.rs index dd6428d..eac91d6 100644 --- a/core/src/glwe/decryption.rs +++ b/core/src/glwe/decryption.rs @@ -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> GLWECiphertext { pub fn clone(&self) -> GLWECiphertext> { @@ -15,7 +15,7 @@ impl> GLWECiphertext { &self, module: &Module, pt: &mut GLWEPlaintext, - sk: &GLWESecret, + sk: &FourierGLWESecret, scratch: &mut Scratch, ) { #[cfg(debug_assertions)] @@ -36,7 +36,7 @@ impl> GLWECiphertext { // 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] diff --git a/core/src/glwe/encryption.rs b/core/src/glwe/encryption.rs index 1910f98..3b70d99 100644 --- a/core/src/glwe/encryption.rs +++ b/core/src/glwe/encryption.rs @@ -4,7 +4,7 @@ use backend::{ }; 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> { pub fn encrypt_sk_scratch_space(module: &Module, basek: usize, k: usize) -> usize { @@ -24,7 +24,7 @@ impl + AsMut<[u8]>> GLWECiphertext { &mut self, module: &Module, pt: &GLWEPlaintext, - sk: &GLWESecret, + sk: &FourierGLWESecret, source_xa: &mut Source, source_xe: &mut Source, sigma: f64, @@ -44,7 +44,7 @@ impl + AsMut<[u8]>> GLWECiphertext { pub fn encrypt_zero_sk>( &mut self, module: &Module, - sk: &GLWESecret, + sk: &FourierGLWESecret, source_xa: &mut Source, source_xe: &mut Source, sigma: f64, @@ -106,7 +106,7 @@ impl + AsMut<[u8]>> GLWECiphertext { &mut self, module: &Module, pt: Option<(&GLWEPlaintext, usize)>, - sk: &GLWESecret, + sk: &FourierGLWESecret, source_xa: &mut Source, source_xe: &mut Source, sigma: f64, @@ -148,7 +148,7 @@ impl + AsMut<[u8]>> GLWECiphertext { // 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 @@ impl + AsMut<[u8]>> GLWECiphertext { { 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); diff --git a/core/src/glwe/mod.rs b/core/src/glwe/mod.rs index 7f0c7aa..e3879cd 100644 --- a/core/src/glwe/mod.rs +++ b/core/src/glwe/mod.rs @@ -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; diff --git a/core/src/glwe/packing.rs b/core/src/glwe/packing.rs index 85aceb6..3496994 100644 --- a/core/src/glwe/packing.rs +++ b/core/src/glwe/packing.rs @@ -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 @@ use backend::{FFT64, Module, Scratch}; /// 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, log_batch: usize, counter: usize, @@ -39,7 +39,7 @@ impl Accumulator { } } -impl StreamPacker { +impl GLWEPacker { /// Instantiates a new [StreamPacker]. /// /// #Arguments @@ -98,7 +98,7 @@ impl StreamPacker { module: &Module, res: &mut Vec>>, a: Option<&GLWECiphertext>, - auto_keys: &HashMap>, + auto_keys: &HashMap>, scratch: &mut Scratch, ) { pack_core( @@ -125,7 +125,7 @@ impl StreamPacker { &mut self, module: &Module, res: &mut Vec>>, - auto_keys: &HashMap>, + auto_keys: &HashMap>, scratch: &mut Scratch, ) { if self.counter != 0 { @@ -151,7 +151,7 @@ fn pack_core, DataAK: AsRef<[u8]>>( a: Option<&GLWECiphertext>, accumulators: &mut [Accumulator], i: usize, - auto_keys: &HashMap>, + auto_keys: &HashMap>, scratch: &mut Scratch, ) { let log_n: usize = module.log_n(); @@ -215,7 +215,7 @@ fn combine, DataAK: AsRef<[u8]>>( acc: &mut Accumulator, b: Option<&GLWECiphertext>, i: usize, - auto_keys: &HashMap>, + auto_keys: &HashMap>, scratch: &mut Scratch, ) { let log_n: usize = module.log_n(); diff --git a/core/src/glwe/plaintext.rs b/core/src/glwe/plaintext.rs index c1e9175..9f24be0 100644 --- a/core/src/glwe/plaintext.rs +++ b/core/src/glwe/plaintext.rs @@ -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 { pub data: VecZnx, diff --git a/core/src/glwe/public_key.rs b/core/src/glwe/public_key.rs index 4a1ed15..f4871ad 100644 --- a/core/src/glwe/public_key.rs +++ b/core/src/glwe/public_key.rs @@ -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 { pub(crate) data: FourierGLWECiphertext, - pub(crate) dist: SecretDistribution, + pub(crate) dist: Distribution, } impl GLWEPublicKey, B> { pub fn alloc(module: &Module, 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 + AsMut<[u8]>> GLWEPublicKey { pub fn generate_from_sk>( &mut self, module: &Module, - sk: &GLWESecret, + sk: &FourierGLWESecret, source_xa: &mut Source, source_xe: &mut Source, sigma: f64, @@ -55,7 +55,7 @@ impl + AsMut<[u8]>> GLWEPublicKey { #[cfg(debug_assertions)] { match sk.dist { - SecretDistribution::NONE => panic!("invalid sk: SecretDistribution::NONE"), + Distribution::NONE => panic!("invalid sk: SecretDistribution::NONE"), _ => {} } } diff --git a/core/src/glwe/secret.rs b/core/src/glwe/secret.rs index b704365..5073d2b 100644 --- a/core/src/glwe/secret.rs +++ b/core/src/glwe/secret.rs @@ -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 { +pub struct GLWESecret { pub(crate) data: ScalarZnx, - pub(crate) data_fourier: ScalarZnxDft, - pub(crate) dist: SecretDistribution, + pub(crate) dist: Distribution, } -impl GLWESecret, B> { - pub fn alloc(module: &Module, rank: usize) -> Self { +impl GLWESecret> { + pub fn alloc(module: &Module, 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, rank: usize) -> usize { - module.bytes_of_scalar_znx(rank) + module.bytes_of_scalar_znx_dft(rank) + pub fn bytes_of(module: &Module, rank: usize) -> usize { + module.bytes_of_scalar_znx(rank) } } -impl GLWESecret { +impl GLWESecret { pub fn n(&self) -> usize { self.data.n() } @@ -39,55 +35,50 @@ impl GLWESecret { } } -impl + AsRef<[u8]>> GLWESecret { - pub fn fill_ternary_prob(&mut self, module: &Module, prob: f64, source: &mut Source) { +impl + AsRef<[u8]>> GLWESecret { + 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, 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, 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, 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, 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) { - (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) { + // (0..self.rank()).for_each(|i| { + // module.svp_prepare(&mut self.data_fourier, i, &self.data, i); + // }); + // } } diff --git a/core/src/glwe/test_fft64/automorphism.rs b/core/src/glwe/test_fft64/automorphism.rs index 8b13789..963083b 100644 --- a/core/src/glwe/test_fft64/automorphism.rs +++ b/core/src/glwe/test_fft64/automorphism.rs @@ -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 = Module::::new(1 << log_n); + + let rows: usize = div_ceil(k_in, basek * digits); + + let mut autokey: GLWEAutomorphismKey, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); + let mut ct_in: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_in, rank); + let mut ct_out: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_out, rank); + let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_in); + let mut pt_have: GLWEPlaintext> = 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> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, 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 = Module::::new(1 << log_n); + + let rows: usize = div_ceil(k_ct, basek * digits); + + let mut autokey: GLWEAutomorphismKey, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); + let mut ct: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_ct, rank); + let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); + let mut pt_have: GLWEPlaintext> = 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> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, 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 + ); +} diff --git a/core/src/glwe/test_fft64/encryption.rs b/core/src/glwe/test_fft64/encryption.rs index 06490c5..2d82575 100644 --- a/core/src/glwe/test_fft64/encryption.rs +++ b/core/src/glwe/test_fft64/encryption.rs @@ -2,7 +2,7 @@ use backend::{Decoding, Encoding, FFT64, Module, ScratchOwned, Stats, VecZnxOps, 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 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma: | GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()), ); - let mut sk: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk); let mut data_want: Vec = vec![0i64; module.n()]; @@ -60,7 +61,7 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma: ct.encrypt_sk( &module, &pt, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, @@ -69,7 +70,7 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma: 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 = vec![0i64; module.n()]; @@ -98,8 +99,9 @@ fn test_encrypt_zero_sk(log_n: usize, basek: usize, k_ct: usize, sigma: f64, ran let mut source_xe: Source = Source::new([1u8; 32]); let mut source_xa: Source = Source::new([0u8; 32]); - let mut sk: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk); let mut ct_dft: FourierGLWECiphertext, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank); @@ -110,13 +112,13 @@ fn test_encrypt_zero_sk(log_n: usize, basek: usize, k_ct: usize, sigma: f64, ran 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 @@ fn test_encrypt_pk(log_n: usize, basek: usize, k_ct: usize, k_pk: usize, sigma: let mut source_xa: Source = Source::new([0u8; 32]); let mut source_xu: Source = Source::new([0u8; 32]); - let mut sk: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk); let mut pk: GLWEPublicKey, 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 @@ fn test_encrypt_pk(log_n: usize, basek: usize, k_ct: usize, k_pk: usize, sigma: let mut pt_have: GLWEPlaintext> = 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); diff --git a/core/src/glwe/test_fft64/external_product.rs b/core/src/glwe/test_fft64/external_product.rs index 7a32343..4ba77c3 100644 --- a/core/src/glwe/test_fft64/external_product.rs +++ b/core/src/glwe/test_fft64/external_product.rs @@ -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 @@ fn external_product() { } #[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 @@ fn external_product_inplace() { }); } -#[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 = Module::::new(1 << log_n); - - let mut ct: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_ct, rank); - let mut pt: GLWEPlaintext> = 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, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); - - let mut data_want: Vec = 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 = 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 = Module::::new(1 << log_n); - - let mut pt: GLWEPlaintext> = 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, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); - - let mut ct_dft: FourierGLWECiphertext, 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 = Module::::new(1 << log_n); - - let mut ct: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_ct, rank); - let mut pt_want: GLWEPlaintext> = 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, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); - - let mut pk: GLWEPublicKey, 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 = 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> = 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 = Module::::new(1 << log_n); - - let rows: usize = k_in.div_ceil(basek * digits); - - let mut ksk: GLWESwitchingKey, FFT64> = - GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out); - let mut ct_in: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_in, rank_in); - let mut ct_out: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_out, rank_out); - let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_in); - let mut pt_have: GLWEPlaintext> = 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, FFT64> = GLWESecret::alloc(&module, rank_in); - sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs); - - let mut sk_out: GLWESecret, 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 = Module::::new(1 << log_n); - - let rows: usize = k_ct.div_ceil(basek * digits); - - let mut ct_grlwe: GLWESwitchingKey, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank); - let mut ct_glwe: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_ct, rank); - let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); - let mut pt_have: GLWEPlaintext> = 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, FFT64> = GLWESecret::alloc(&module, rank); - sk0.fill_ternary_prob(&module, 0.5, &mut source_xs); - - let mut sk1: GLWESecret, 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 = Module::::new(1 << log_n); - - let rows: usize = k_in.div_ceil(basek * digits); - - let mut autokey: AutomorphismKey, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); - let mut ct_in: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_in, rank); - let mut ct_out: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_out, rank); - let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_in); - let mut pt_have: GLWEPlaintext> = 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, 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 = Module::::new(1 << log_n); - - let rows: usize = k_ct.div_ceil(basek * digits); - - let mut autokey: AutomorphismKey, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); - let mut ct: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_ct, rank); - let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); - let mut pt_have: GLWEPlaintext> = 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, 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 @@ fn test_external_product( ), ); - let mut sk: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, 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 @@ fn test_external_product( ct_glwe_in.encrypt_sk( &module, &pt_want, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, @@ -724,7 +113,7 @@ fn test_external_product( 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 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw | GLWECiphertext::external_product_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_ggsw.k(), digits, rank), ); - let mut sk: GLWESecret, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, 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 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw ct_glwe.encrypt_sk( &module, &pt_want, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, @@ -818,7 +208,7 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw 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); diff --git a/core/src/glwe/test_fft64/keyswitch.rs b/core/src/glwe/test_fft64/keyswitch.rs index 8b13789..f80ae86 100644 --- a/core/src/glwe/test_fft64/keyswitch.rs +++ b/core/src/glwe/test_fft64/keyswitch.rs @@ -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 = Module::::new(1 << log_n); + + let rows: usize = div_ceil(k_in, basek * digits); + + let mut ksk: GLWESwitchingKey, FFT64> = + GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out); + let mut ct_in: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_in, rank_in); + let mut ct_out: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_out, rank_out); + let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_in); + let mut pt_have: GLWEPlaintext> = 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> = GLWESecret::alloc(&module, rank_in); + sk_in.fill_ternary_prob(0.5, &mut source_xs); + let sk_in_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_in); + + let mut sk_out: GLWESecret> = GLWESecret::alloc(&module, rank_out); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret, 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 = Module::::new(1 << log_n); + + let rows: usize = div_ceil(k_ct, basek * digits); + + let mut ct_grlwe: GLWESwitchingKey, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank); + let mut ct_glwe: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_ct, rank); + let mut pt_want: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); + let mut pt_have: GLWEPlaintext> = 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> = GLWESecret::alloc(&module, rank); + sk_in.fill_ternary_prob(0.5, &mut source_xs); + let sk_in_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_in); + + let mut sk_out: GLWESecret> = GLWESecret::alloc(&module, rank); + sk_out.fill_ternary_prob(0.5, &mut source_xs); + let sk_out_dft: FourierGLWESecret, 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 + ); +} diff --git a/core/src/glwe/test_fft64/packing.rs b/core/src/glwe/test_fft64/packing.rs index 107461c..da817e6 100644 --- a/core/src/glwe/test_fft64/packing.rs +++ b/core/src/glwe/test_fft64/packing.rs @@ -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 = Module::::new(1 << log_n); @@ -26,12 +26,13 @@ fn packing() { 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, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk); let mut pt: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_ct); let mut data: Vec = vec![0i64; module.n()]; @@ -40,11 +41,11 @@ fn packing() { }); pt.data.encode_vec_i64(0, basek, pt_k, &data, 32); - let gal_els: Vec = StreamPacker::galois_elements(&module); + let gal_els: Vec = GLWEPacker::galois_elements(&module); - let mut auto_keys: HashMap, FFT64>> = HashMap::new(); + let mut auto_keys: HashMap, FFT64>> = HashMap::new(); gal_els.iter().for_each(|gal_el| { - let mut key: AutomorphismKey, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); + let mut key: GLWEAutomorphismKey, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); key.generate_from_sk( &module, *gal_el, @@ -59,14 +60,14 @@ fn packing() { 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> = 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 @@ fn packing() { ct.encrypt_sk( &module, &pt, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, @@ -115,7 +116,7 @@ fn packing() { }); 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); diff --git a/core/src/glwe/test_fft64/trace.rs b/core/src/glwe/test_fft64/trace.rs index 885aa90..eae2bce 100644 --- a/core/src/glwe/test_fft64/trace.rs +++ b/core/src/glwe/test_fft64/trace.rs @@ -3,10 +3,13 @@ use std::collections::HashMap; 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 @@ fn test_trace_inplace(log_n: usize, basek: usize, k: usize, sigma: f64, rank: us 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, FFT64> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(&&module, 0.5, &mut source_xs); + let mut sk: GLWESecret> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + let sk_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk); let mut data_want: Vec = vec![0i64; module.n()]; @@ -53,17 +57,18 @@ fn test_trace_inplace(log_n: usize, basek: usize, k: usize, sigma: f64, rank: us ct.encrypt_sk( &module, &pt_have, - &sk, + &sk_dft, &mut source_xa, &mut source_xe, sigma, scratch.borrow(), ); - let mut auto_keys: HashMap, FFT64>> = HashMap::new(); + let mut auto_keys: HashMap, FFT64>> = HashMap::new(); let gal_els: Vec = GLWECiphertext::trace_galois_elements(&module); gal_els.iter().for_each(|gal_el| { - let mut key: AutomorphismKey, FFT64> = AutomorphismKey::alloc(&module, basek, k_autokey, rows, digits, rank); + let mut key: GLWEAutomorphismKey, FFT64> = + GLWEAutomorphismKey::alloc(&module, basek, k_autokey, rows, digits, rank); key.generate_from_sk( &module, *gal_el, @@ -81,7 +86,7 @@ fn test_trace_inplace(log_n: usize, basek: usize, k: usize, sigma: f64, rank: us (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()); diff --git a/core/src/glwe/trace.rs b/core/src/glwe/trace.rs index 3c6a5bb..c702489 100644 --- a/core/src/glwe/trace.rs +++ b/core/src/glwe/trace.rs @@ -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> { pub fn trace_galois_elements(module: &Module) -> Vec { @@ -51,7 +51,7 @@ where start: usize, end: usize, lhs: &GLWECiphertext, - auto_keys: &HashMap>, + auto_keys: &HashMap>, scratch: &mut Scratch, ) where GLWECiphertext: GLWECiphertextToRef + Infos, @@ -65,7 +65,7 @@ where module: &Module, start: usize, end: usize, - auto_keys: &HashMap>, + auto_keys: &HashMap>, scratch: &mut Scratch, ) { (start..end).for_each(|i| { diff --git a/core/src/keyswitch_key.rs b/core/src/keyswitch_key.rs deleted file mode 100644 index 201ae22..0000000 --- a/core/src/keyswitch_key.rs +++ /dev/null @@ -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(pub(crate) GGLWECiphertext); - -impl GLWESwitchingKey, FFT64> { - pub fn alloc( - module: &Module, - 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, - basek: usize, - k: usize, - rows: usize, - digits: usize, - rank_in: usize, - rank_out: usize, - ) -> usize { - GGLWECiphertext::, FFT64>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out) - } -} - -impl Infos for GLWESwitchingKey { - type Inner = MatZnxDft; - - fn inner(&self) -> &Self::Inner { - self.0.inner() - } - - fn basek(&self) -> usize { - self.0.basek() - } - - fn k(&self) -> usize { - self.0.k() - } -} - -impl GLWESwitchingKey { - 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> GetRow for GLWESwitchingKey { - fn get_row + AsRef<[u8]>>( - &self, - module: &Module, - row_i: usize, - col_j: usize, - res: &mut FourierGLWECiphertext, - ) { - module.mat_znx_dft_get_row(&mut res.data, &self.0.data, row_i, col_j); - } -} - -impl + AsRef<[u8]>> SetRow for GLWESwitchingKey { - fn set_row>( - &mut self, - module: &Module, - row_i: usize, - col_j: usize, - a: &FourierGLWECiphertext, - ) { - module.mat_znx_dft_set_row(&mut self.0.data, row_i, col_j, &a.data); - } -} - -impl GLWESwitchingKey, FFT64> { - pub fn encrypt_sk_scratch_space(module: &Module, 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, _basek: usize, _k: usize, _rank: usize) -> usize { - GGLWECiphertext::generate_from_pk_scratch_space(module, _basek, _k, _rank) - } - - pub fn keyswitch_scratch_space( - module: &Module, - 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, - 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, - 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, - 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 + AsRef<[u8]>> GLWESwitchingKey { - pub fn generate_from_sk, DataSkOut: AsRef<[u8]>>( - &mut self, - module: &Module, - sk_in: &GLWESecret, - sk_out: &GLWESecret, - 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, DataRhs: AsRef<[u8]>>( - &mut self, - module: &Module, - lhs: &GLWESwitchingKey, - rhs: &GLWESwitchingKey, - 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>( - &mut self, - module: &Module, - rhs: &GLWESwitchingKey, - 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, DataRhs: AsRef<[u8]>>( - &mut self, - module: &Module, - lhs: &GLWESwitchingKey, - rhs: &GGSWCiphertext, - 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>( - &mut self, - module: &Module, - rhs: &GGSWCiphertext, - 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); - }); - }); - } -} diff --git a/core/src/lib.rs b/core/src/lib.rs index ef181a1..fa6d009 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -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 @@ pub trait ScratchCore { k: usize, rank: usize, ) -> (FourierGLWECiphertext<&mut [u8], B>, &mut Self); - fn tmp_sk(&mut self, module: &Module, rank: usize) -> (GLWESecret<&mut [u8], B>, &mut Self); + fn tmp_sk(&mut self, module: &Module, rank: usize) -> (GLWESecret<&mut [u8]>, &mut Self); + fn tmp_fourier_sk(&mut self, module: &Module, rank: usize) -> (FourierGLWESecret<&mut [u8], B>, &mut Self); fn tmp_glwe_pk( &mut self, module: &Module, @@ -88,7 +83,7 @@ pub trait ScratchCore { 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, @@ -97,7 +92,7 @@ pub trait ScratchCore { rows: usize, digits: usize, rank: usize, - ) -> (AutomorphismKey<&mut [u8], B>, &mut Self); + ) -> (GLWEAutomorphismKey<&mut [u8], B>, &mut Self); } impl ScratchCore for Scratch { @@ -194,22 +189,31 @@ impl ScratchCore for Scratch { ( GLWEPublicKey { data, - dist: SecretDistribution::NONE, + dist: Distribution::NONE, }, scratch, ) } - fn tmp_sk(&mut self, module: &Module, rank: usize) -> (GLWESecret<&mut [u8], FFT64>, &mut Self) { + fn tmp_sk(&mut self, module: &Module, 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, 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 @@ impl ScratchCore for Scratch { 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 @@ impl ScratchCore for Scratch { rows: usize, digits: usize, rank: usize, - ) -> (TensorKey<&mut [u8], FFT64>, &mut Self) { + ) -> (GLWETensorKey<&mut [u8], FFT64>, &mut Self) { let mut keys: Vec> = Vec::new(); let pairs: usize = (((rank + 1) * rank) >> 1).max(1); @@ -264,6 +268,6 @@ impl ScratchCore for Scratch { scratch = s; keys.push(gglwe); } - (TensorKey { keys }, scratch) + (GLWETensorKey { keys }, scratch) } } diff --git a/core/src/lwe/mod.rs b/core/src/lwe/mod.rs new file mode 100644 index 0000000..d91ce80 --- /dev/null +++ b/core/src/lwe/mod.rs @@ -0,0 +1,3 @@ +pub mod secret; + +pub use secret::LWESecret; diff --git a/core/src/lwe.rs b/core/src/lwe/secret.rs similarity index 73% rename from core/src/lwe.rs rename to core/src/lwe/secret.rs index 3f0d749..90776a7 100644 --- a/core/src/lwe.rs +++ b/core/src/lwe/secret.rs @@ -1,18 +1,18 @@ use backend::{ScalarZnx, ZnxInfos, ZnxZero}; use sampling::source::Source; -use crate::SecretDistribution; +use crate::Distribution; pub struct LWESecret { pub(crate) data: ScalarZnx, - pub(crate) dist: SecretDistribution, + pub(crate) dist: Distribution, } impl LWESecret> { pub fn alloc(n: usize) -> Self { Self { data: ScalarZnx::new(n, 1), - dist: SecretDistribution::NONE, + dist: Distribution::NONE, } } } @@ -34,31 +34,31 @@ impl LWESecret { impl + AsMut<[u8]>> LWESecret { 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; } } diff --git a/core/src/test_fft64/mod.rs b/core/src/noise.rs similarity index 97% rename from core/src/test_fft64/mod.rs rename to core/src/noise.rs index 4c0f513..cfc7698 100644 --- a/core/src/test_fft64/mod.rs +++ b/core/src/noise.rs @@ -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 @@ pub(crate) fn var_noise_gglwe_product( noise } +#[allow(dead_code)] pub(crate) fn log2_std_noise_gglwe_product( n: f64, basek: usize, @@ -62,6 +58,7 @@ pub(crate) fn log2_std_noise_gglwe_product( 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 @@ pub(crate) fn noise_ggsw_product( 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,