From 829b8be610bb518765be3eb857e58ffe09683c2d Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bossuat Date: Fri, 13 Jun 2025 20:45:24 +0200 Subject: [PATCH] wip on BR + added enc/dec for LWE --- backend/src/lib.rs | 4 +- backend/src/mat_znx_dft_ops.rs | 136 +++++++- backend/src/scalar_znx.rs | 4 +- backend/src/vec_znx.rs | 10 +- core/benches/external_product_glwe_fft64.rs | 12 +- core/src/blind_rotation/ccgi.rs | 139 ++++++++ core/src/blind_rotation/key.rs | 39 ++- core/src/blind_rotation/mod.rs | 4 + core/src/blind_rotation/test_fft64/cggi.rs | 85 +++++ core/src/blind_rotation/test_fft64/mod.rs | 1 + core/src/elem.rs | 2 +- core/src/fourier_glwe.rs | 321 ------------------ core/src/fourier_glwe/ciphertext.rs | 6 +- core/src/fourier_glwe/decryption.rs | 4 +- core/src/fourier_glwe/encryption.rs | 6 +- core/src/fourier_glwe/external_product.rs | 8 +- .../test_fft64/external_product.rs | 10 +- core/src/fourier_glwe/test_fft64/keyswitch.rs | 10 +- core/src/gglwe.rs | 236 ------------- core/src/gglwe/automorphism.rs | 52 +-- core/src/gglwe/ciphertext.rs | 6 +- core/src/gglwe/encryption.rs | 4 +- core/src/gglwe/test_fft64/automorphism_key.rs | 2 +- core/src/gglwe/test_fft64/gglwe.rs | 1 - core/src/ggsw/ciphertext.rs | 6 +- core/src/ggsw/test_fft64/ggsw.rs | 2 +- core/src/glwe/ciphertext.rs | 22 +- core/src/glwe/decryption.rs | 18 +- core/src/glwe/encryption.rs | 10 +- core/src/glwe/external_product.rs | 14 +- core/src/glwe/keyswitch.rs | 8 +- core/src/glwe/plaintext.rs | 2 +- core/src/glwe/test_fft64/automorphism.rs | 10 +- core/src/glwe/test_fft64/external_product.rs | 4 +- core/src/glwe/test_fft64/keyswitch.rs | 10 +- core/src/glwe/test_fft64/packing.rs | 2 +- core/src/glwe/test_fft64/trace.rs | 2 +- core/src/lib.rs | 9 +- core/src/lwe/ciphertext.rs | 77 +++++ core/src/lwe/decryption.rs | 21 ++ core/src/lwe/encryption.rs | 35 ++ core/src/lwe/mod.rs | 6 + core/src/lwe/plaintext.rs | 73 ++++ 43 files changed, 745 insertions(+), 688 deletions(-) create mode 100644 core/src/blind_rotation/test_fft64/cggi.rs create mode 100644 core/src/blind_rotation/test_fft64/mod.rs delete mode 100644 core/src/fourier_glwe.rs delete mode 100644 core/src/gglwe.rs create mode 100644 core/src/lwe/ciphertext.rs create mode 100644 core/src/lwe/decryption.rs create mode 100644 core/src/lwe/encryption.rs create mode 100644 core/src/lwe/plaintext.rs diff --git a/backend/src/lib.rs b/backend/src/lib.rs index dcf4325..09e5556 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -103,7 +103,7 @@ fn alloc_aligned_custom_u8(size: usize, align: usize) -> Vec { /// Size of T * size msut be a multiple of [DEFAULTALIGN]. pub fn alloc_aligned_custom(size: usize, align: usize) -> Vec { assert_eq!( - (size * size_of::()) % align, + (size * size_of::()) % (align/ size_of::()), 0, "size={} must be a multiple of align={}", size, @@ -121,7 +121,7 @@ pub fn alloc_aligned_custom(size: usize, align: usize) -> Vec { /// of [DEFAULTALIGN]/size_of::() that is equal or greater to `size`. pub fn alloc_aligned(size: usize) -> Vec { alloc_aligned_custom::( - size + (size % (DEFAULTALIGN / size_of::())), + size + (DEFAULTALIGN - (size % (DEFAULTALIGN / size_of::()))), DEFAULTALIGN, ) } diff --git a/backend/src/mat_znx_dft_ops.rs b/backend/src/mat_znx_dft_ops.rs index 5f08a89..b48cb1a 100644 --- a/backend/src/mat_znx_dft_ops.rs +++ b/backend/src/mat_znx_dft_ops.rs @@ -82,6 +82,12 @@ pub trait MatZnxDftOps { where A: MatZnxToMut; + /// Multiplies A by (X^{k} - 1). + fn mat_znx_dft_mul_x_pow_minus_one_add_inplace(&self, k: i64, res: &mut R, a: &A, scratch: &mut Scratch) + where + R: MatZnxToMut, + A: MatZnxToRef; + /// Applies the vector matrix product [VecZnxDft] x [MatZnxDft]. /// The size of `buf` is given by [MatZnxDftOps::vmp_apply_dft_to_dft_tmp_bytes]. /// @@ -212,7 +218,7 @@ impl MatZnxDftOps for Module { self.mat_znx_dft_set_row(&mut res, row_i, col_j, &tmp_1); }); - }) + }); } fn mat_znx_dft_mul_x_pow_minus_one_inplace(&self, k: i64, a: &mut A, scratch: &mut Scratch) @@ -249,7 +255,52 @@ impl MatZnxDftOps for Module { self.mat_znx_dft_set_row(&mut a, row_i, col_j, &tmp_1); }); - }) + }); + } + + fn mat_znx_dft_mul_x_pow_minus_one_add_inplace(&self, k: i64, res: &mut R, a: &A, scratch: &mut Scratch) + where + R: MatZnxToMut, + A: MatZnxToRef, + { + let mut res: MatZnxDft<&mut [u8], FFT64> = res.to_mut(); + let a: MatZnxDft<&[u8], FFT64> = a.to_ref(); + + #[cfg(debug_assertions)] + { + assert_eq!(a.n(), self.n()); + } + + let (mut xpm1_dft, scratch1) = scratch.tmp_scalar_znx_dft(self, 1); + + { + let (mut xpm1, _) = scratch1.tmp_scalar_znx(self, 1); + xpm1.data[0] = 1; + self.vec_znx_rotate_inplace(k, &mut xpm1, 0); + self.svp_prepare(&mut xpm1_dft, 0, &xpm1, 0); + } + + let (mut tmp_0, scratch2) = scratch1.tmp_vec_znx_dft(self, a.cols_out(), a.size()); + let (mut tmp_1, _) = scratch2.tmp_vec_znx_dft(self, a.cols_out(), a.size()); + + (0..a.rows()).for_each(|row_i| { + (0..a.cols_in()).for_each(|col_j| { + self.mat_znx_dft_get_row(&mut tmp_0, &a, row_i, col_j); + + (0..tmp_0.cols()).for_each(|i| { + self.svp_apply(&mut tmp_1, i, &xpm1_dft, 0, &tmp_0, i); + self.vec_znx_dft_sub_ab_inplace(&mut tmp_1, i, &tmp_0, i); + }); + + self.mat_znx_dft_get_row(&mut tmp_0, &res, row_i, col_j); + + (0..tmp_0.cols()).for_each(|i| { + self.vec_znx_dft_add_inplace(&mut tmp_0, i, &tmp_1, i); + }); + + self.mat_znx_dft_set_row(&mut res, row_i, col_j, &tmp_0); + }); + }); } fn mat_znx_dft_set_row(&self, res: &mut R, res_row: usize, res_col_in: usize, a: &A) @@ -845,7 +896,6 @@ mod tests { (0..cols_out).for_each(|j| { module.vec_znx_idft(&mut tmp_big, 0, &tmp_dft, j, scratch.borrow()); - // module.vec_znx_big_normalize(basek, &mut want, j, &tmp_big, 0, scratch.borrow()); module.vec_znx_big_normalize(basek, &mut tmp, 0, &tmp_big, 0, scratch.borrow()); module.vec_znx_rotate(k, &mut want, j, &tmp, 0); module.vec_znx_sub_ab_inplace(&mut want, j, &tmp, 0); @@ -863,4 +913,84 @@ mod tests { }); }); } + + #[test] + fn mat_znx_dft_mul_x_pow_minus_one_add_inplace() { + let log_n: i32 = 5; + let n: usize = 1 << log_n; + + let module: Module = Module::::new(n); + let basek: usize = 8; + let rows: usize = 2; + let cols_in: usize = 2; + let cols_out: usize = 2; + let size: usize = 4; + + let mut scratch: ScratchOwned = ScratchOwned::new(module.mat_znx_dft_mul_x_pow_minus_one_scratch_space(size, cols_out)); + + let mut mat_want: MatZnxDft, FFT64> = module.new_mat_znx_dft(rows, cols_in, cols_out, size); + let mut mat_have: MatZnxDft, FFT64> = module.new_mat_znx_dft(rows, cols_in, cols_out, size); + + let mut tmp: VecZnx> = module.new_vec_znx(1, size); + let mut tmp_dft: VecZnxDft, FFT64> = module.new_vec_znx_dft(cols_out, size); + + let mut source: Source = Source::new([0u8; 32]); + + (0..mat_have.rows()).for_each(|row_i| { + (0..mat_have.cols_in()).for_each(|col_i| { + (0..cols_out).for_each(|j| { + tmp.fill_uniform(basek, 0, size, &mut source); + module.vec_znx_dft(1, 0, &mut tmp_dft, j, &tmp, 0); + }); + + module.mat_znx_dft_set_row(&mut mat_have, row_i, col_i, &tmp_dft); + }); + }); + + (0..mat_want.rows()).for_each(|row_i| { + (0..mat_want.cols_in()).for_each(|col_i| { + (0..cols_out).for_each(|j| { + tmp.fill_uniform(basek, 0, size, &mut source); + module.vec_znx_dft(1, 0, &mut tmp_dft, j, &tmp, 0); + }); + + module.mat_znx_dft_set_row(&mut mat_want, row_i, col_i, &tmp_dft); + }); + }); + + let k: i64 = 1; + + module.mat_znx_dft_mul_x_pow_minus_one_add_inplace(k, &mut mat_have, &mat_want, scratch.borrow()); + + let mut have: VecZnx> = module.new_vec_znx(cols_out, size); + let mut want: VecZnx> = module.new_vec_znx(cols_out, size); + let mut tmp_big: VecZnxBig, FFT64> = module.new_vec_znx_big(1, size); + + let mut source: Source = Source::new([0u8; 32]); + (0..mat_want.rows()).for_each(|row_i| { + (0..mat_want.cols_in()).for_each(|col_i| { + module.mat_znx_dft_get_row(&mut tmp_dft, &mat_want, row_i, col_i); + + (0..cols_out).for_each(|j| { + module.vec_znx_idft(&mut tmp_big, 0, &tmp_dft, j, scratch.borrow()); + module.vec_znx_big_normalize(basek, &mut tmp, 0, &tmp_big, 0, scratch.borrow()); + module.vec_znx_rotate(k, &mut want, j, &tmp, 0); + module.vec_znx_sub_ab_inplace(&mut want, j, &tmp, 0); + + tmp.fill_uniform(basek, 0, size, &mut source); + module.vec_znx_add_inplace(&mut want, j, &tmp, 0); + module.vec_znx_normalize_inplace(basek, &mut want, j, scratch.borrow()); + }); + + module.mat_znx_dft_get_row(&mut tmp_dft, &mat_have, row_i, col_i); + + (0..cols_out).for_each(|j| { + module.vec_znx_idft(&mut tmp_big, 0, &tmp_dft, j, scratch.borrow()); + module.vec_znx_big_normalize(basek, &mut have, j, &tmp_big, 0, scratch.borrow()); + }); + + assert_eq!(have, want) + }); + }); + } } diff --git a/backend/src/scalar_znx.rs b/backend/src/scalar_znx.rs index 09e7292..0a5bb64 100644 --- a/backend/src/scalar_znx.rs +++ b/backend/src/scalar_znx.rs @@ -91,9 +91,9 @@ impl + AsRef<[u8]>> ScalarZnx { } pub fn fill_binary_block(&mut self, col: usize, block_size: usize, source: &mut Source) { - assert!(block_size & (block_size - 1) == 0); + assert!(self.n() % block_size == 0); let max_idx: u64 = (block_size + 1) as u64; - let mask_idx: u64 = (2 * block_size - 1) as u64; + let mask_idx: u64 = (1<<((u64::BITS - max_idx.leading_zeros())as u64)) - 1 ; for block in self.at_mut(col, 0).chunks_mut(block_size) { let idx: usize = source.next_u64n(max_idx, mask_idx) as usize; if idx != block_size { diff --git a/backend/src/vec_znx.rs b/backend/src/vec_znx.rs index 84b9a84..8213d5e 100644 --- a/backend/src/vec_znx.rs +++ b/backend/src/vec_znx.rs @@ -177,7 +177,7 @@ impl>> VecZnx { n * cols * size * size_of::() } - pub(crate) fn new(n: usize, cols: usize, size: usize) -> Self { + pub fn new(n: usize, cols: usize, size: usize) -> Self { let data = alloc_aligned::(Self::bytes_of::(n, cols, size)); Self { data: data.into(), @@ -243,7 +243,13 @@ fn normalize_tmp_bytes(n: usize) -> usize { n * std::mem::size_of::() } -#[allow(dead_code)] +impl + AsMut<[u8]>> VecZnx{ + pub fn normalize(&mut self, basek: usize, a_col: usize, tmp_bytes: &mut [u8]){ + normalize(basek, self, a_col, tmp_bytes); + } +} + + fn normalize + AsRef<[u8]>>(basek: usize, a: &mut VecZnx, a_col: usize, tmp_bytes: &mut [u8]) { let n: usize = a.n(); diff --git a/core/benches/external_product_glwe_fft64.rs b/core/benches/external_product_glwe_fft64.rs index c48c626..fd6508a 100644 --- a/core/benches/external_product_glwe_fft64.rs +++ b/core/benches/external_product_glwe_fft64.rs @@ -26,7 +26,7 @@ fn bench_external_product_glwe_fft64(c: &mut Criterion) { let rank: usize = p.rank; let digits: usize = 1; - let rows: usize = (p.k_ct_in + p.basek - 1) / p.basek; + let rows: usize = 1; //(p.k_ct_in + p.basek - 1) / p.basek; let sigma: f64 = 3.2; let mut ct_ggsw: GGSWCiphertext, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank); @@ -81,11 +81,11 @@ fn bench_external_product_glwe_fft64(c: &mut Criterion) { } let params_set: Vec = vec![Params { - log_n: 10, - basek: 7, - k_ct_in: 27, - k_ct_out: 27, - k_ggsw: 27, + log_n: 11, + basek: 22, + k_ct_in: 44, + k_ct_out: 44, + k_ggsw: 54, rank: 1, }]; diff --git a/core/src/blind_rotation/ccgi.rs b/core/src/blind_rotation/ccgi.rs index e69de29..341045e 100644 --- a/core/src/blind_rotation/ccgi.rs +++ b/core/src/blind_rotation/ccgi.rs @@ -0,0 +1,139 @@ +use std::time::Instant; + +use backend::{MatZnxDftOps, MatZnxDftScratch, Module, ScalarZnxDftOps, Scratch, VecZnxDftOps, VecZnxOps, ZnxView, ZnxViewMut, ZnxZero, FFT64}; +use itertools::izip; + +use crate::{ + GGSWCiphertext, GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef, GLWEPlaintext, Infos, LWECiphertext, + ScratchCore, blind_rotation::key::BlindRotationKeyCGGI, lwe::ciphertext::LWECiphertextToRef, +}; + +pub fn cggi_blind_rotate_scratch_space( + module: &Module, + basek: usize, + k_lut: usize, + k_brk: usize, + rows: usize, + rank: usize, +) -> usize { + let size = k_brk.div_ceil(basek); + GGSWCiphertext::, FFT64>::bytes_of(module, basek, k_brk, rows, 1, rank) + + (module.mat_znx_dft_mul_x_pow_minus_one_scratch_space(size, rank + 1) + | GLWECiphertext::external_product_inplace_scratch_space(module, basek, k_lut, k_brk, 1, rank)) +} + +pub fn cggi_blind_rotate( + module: &Module, + res: &mut GLWECiphertext, + lwe: &LWECiphertext, + lut: &GLWEPlaintext, + brk: &BlindRotationKeyCGGI, + scratch: &mut Scratch, +) where + DataRes: AsRef<[u8]> + AsMut<[u8]>, + DataIn: AsRef<[u8]>, + DataLUT: AsRef<[u8]>, +{ + + println!("{}", lwe.n()); + + let mut lwe_2n: Vec = vec![0i64; lwe.n() + 1]; // TODO: from scratch space + let mut out_mut: GLWECiphertext<&mut [u8]> = res.to_mut(); + let lwe_ref: LWECiphertext<&[u8]> = lwe.to_ref(); + let lut_ref: GLWECiphertext<&[u8]> = lut.to_ref(); + + let cols = out_mut.rank()+1; + + mod_switch_2n(module, &mut lwe_2n, &lwe_ref); + + let a: &[i64] = &lwe_2n[1..]; + let b: i64 = lwe_2n[0]; + + out_mut.data.zero(); + + // Initialize out to X^{b} * LUT(X) + module.vec_znx_rotate(b, &mut out_mut.data, 0, &lut_ref.data, 0); + + let block_size: usize = brk.block_size(); + + // ACC + [sum DFT(X^ai -1) * (DFT(ACC) x BRKi)] + + let (mut acc_dft, scratch1) = scratch.tmp_glwe_fourier(module, brk.basek(), out_mut.k(), out_mut.rank()); + let (mut acc_add_dft, scratch2) = scratch1.tmp_glwe_fourier(module, brk.basek(), out_mut.k(), out_mut.rank()); + let (mut vmp_res, scratch3) = scratch2.tmp_vec_znx_dft(module, acc_dft.rank()+1, acc_dft.size()); + let (mut xai_minus_one, scratch4) = scratch3.tmp_scalar_znx(module, 1); + let (mut xai_minus_one_dft, scratch5) = scratch4.tmp_scalar_znx_dft(module, 1); + + let start: Instant = Instant::now(); + izip!( + a.chunks_exact(block_size), + brk.data.chunks_exact(block_size) + ) + .for_each(|(ai, ski)| { + + out_mut.dft(module, &mut acc_dft); + acc_add_dft.data.zero(); + + izip!(ai.iter(), ski.iter()) + .enumerate() + .for_each(|(i, (aii, skii))| { + + // vmp_res = DFT(acc) * BRK[i] + module.vmp_apply(&mut vmp_res, &acc_dft.data, &skii.data, scratch5); + + // DFT(X^ai -1) + xai_minus_one.zero(); + xai_minus_one.at_mut(0, 0)[0] = 1; + module.vec_znx_rotate_inplace(*aii, &mut xai_minus_one, 0); + xai_minus_one.at_mut(0, 0)[0] -= 1; + module.svp_prepare(&mut xai_minus_one_dft, 0, &xai_minus_one, 0); + + // DFT(X^ai -1) * (DFT(acc) * BRK[i]) + (0..cols).for_each(|i|{ + module.svp_apply_inplace(&mut vmp_res, i, &xai_minus_one_dft, 0); + module.vec_znx_dft_add_inplace(&mut acc_add_dft.data, i, &vmp_res, i); + }); + + }); + + acc_add_dft.idft(module, &mut out_mut, scratch5); + }); + let duration: std::time::Duration = start.elapsed(); + println!("external products: {} us", duration.as_micros()); +} + +fn mod_switch_2n(module: &Module, res: &mut [i64], lwe: &LWECiphertext<&[u8]>) { + let basek: usize = lwe.basek(); + + let log2n: usize = module.log_n() + 1; + + res.copy_from_slice(&lwe.data.at(0, 0)); + + if basek > log2n { + let diff: usize = basek - log2n; + res.iter_mut().for_each(|x| { + *x = div_signed_by_pow2(x, diff); + }) + } else { + let rem: usize = basek - (log2n % basek); + let size: usize = log2n.div_ceil(basek); + (1..size).for_each(|i| { + if i == size - 1 && rem != basek { + let k_rem: usize = basek - rem; + izip!(lwe.data.at(0, i).iter(), res.iter_mut()).for_each(|(x, y)| { + *y = (*y << k_rem) + (x >> rem); + }); + } else { + izip!(lwe.data.at(0, i).iter(), res.iter_mut()).for_each(|(x, y)| { + *y = (*y << basek) + x; + }); + } + }) + } +} + +#[inline(always)] +fn div_signed_by_pow2(x: &i64, k: usize) -> i64 { + let bias: i64 = (1 << k) - 1; + (x + ((x >> 63) & bias)) >> k +} diff --git a/core/src/blind_rotation/key.rs b/core/src/blind_rotation/key.rs index 270080d..9f23c61 100644 --- a/core/src/blind_rotation/key.rs +++ b/core/src/blind_rotation/key.rs @@ -1,22 +1,22 @@ use backend::{Backend, FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxToRef, Scratch, ZnxView, ZnxViewMut}; use sampling::source::Source; -use crate::{Distribution, FourierGLWESecret, GGSWCiphertext, GLWEAutomorphismKey, LWESecret}; +use crate::{Distribution, FourierGLWESecret, GGSWCiphertext, Infos, LWESecret}; pub struct BlindRotationKeyCGGI { pub(crate) data: Vec, B>>, pub(crate) dist: Distribution, } -pub struct BlindRotationKeyFHEW { - pub(crate) data: Vec, B>>, - pub(crate) auto: Vec, B>>, -} +// pub struct BlindRotationKeyFHEW { +// pub(crate) data: Vec, B>>, +// pub(crate) auto: Vec, B>>, +//} impl BlindRotationKeyCGGI { - pub fn allocate(module: &Module, lwe_degree: usize, basek: usize, k: usize, rows: usize, rank: usize) -> Self { - let mut data: Vec, FFT64>> = Vec::with_capacity(lwe_degree); - (0..lwe_degree).for_each(|_| data.push(GGSWCiphertext::alloc(module, basek, k, rows, 1, rank))); + pub fn allocate(module: &Module, n_lwe: usize, basek: usize, k: usize, rows: usize, rank: usize) -> Self { + let mut data: Vec, FFT64>> = Vec::with_capacity(n_lwe); + (0..n_lwe).for_each(|_| data.push(GGSWCiphertext::alloc(module, basek, k, rows, 1, rank))); Self { data, dist: Distribution::NONE, @@ -61,4 +61,27 @@ impl BlindRotationKeyCGGI { ggsw.encrypt_sk(module, &pt, sk_glwe, source_xa, source_xe, sigma, scratch); }) } + + pub(crate) fn block_size(&self) -> usize { + match self.dist { + Distribution::BinaryBlock(value) => value, + _ => 1, + } + } + + pub(crate) fn rows(&self) -> usize { + self.data[0].rows() + } + + pub(crate) fn k(&self) -> usize { + self.data[0].k() + } + + pub(crate) fn rank(&self) -> usize { + self.data[0].rank() + } + + pub(crate) fn basek(&self) -> usize { + self.data[0].basek() + } } diff --git a/core/src/blind_rotation/mod.rs b/core/src/blind_rotation/mod.rs index c531781..63fb3fd 100644 --- a/core/src/blind_rotation/mod.rs +++ b/core/src/blind_rotation/mod.rs @@ -1,2 +1,6 @@ // pub mod cggi; +pub mod ccgi; pub mod key; + +#[cfg(test)] +pub mod test_fft64; diff --git a/core/src/blind_rotation/test_fft64/cggi.rs b/core/src/blind_rotation/test_fft64/cggi.rs new file mode 100644 index 0000000..8a9246f --- /dev/null +++ b/core/src/blind_rotation/test_fft64/cggi.rs @@ -0,0 +1,85 @@ +use core::time; +use std::time::Instant; + +use backend::{Encoding, Module, ScratchOwned, FFT64}; +use sampling::source::Source; + +use crate::{ + blind_rotation::{ccgi::{cggi_blind_rotate, cggi_blind_rotate_scratch_space}, key::BlindRotationKeyCGGI}, lwe::LWEPlaintext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, LWECiphertext, LWESecret +}; + +#[test] +fn blind_rotation() { + let module: Module = Module::::new(2048); + let basek: usize = 17; + + let n_lwe: usize = 1071; + + let k_lwe: usize = 22; + let k_brk: usize = 54; + let rows_brk: usize = 1; + let k_lut: usize = 44; + let rank: usize = 1; + let block_size: usize = 7; + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + + let mut sk_glwe: GLWESecret> = GLWESecret::alloc(&module, rank); + sk_glwe.fill_ternary_prob(0.5, &mut source_xs); + let sk_glwe_dft: FourierGLWESecret, FFT64> = FourierGLWESecret::from(&module, &sk_glwe); + + let mut sk_lwe: LWESecret> = LWESecret::alloc(n_lwe); + sk_lwe.fill_binary_block(block_size, &mut source_xs); + + let mut scratch: ScratchOwned = ScratchOwned::new(BlindRotationKeyCGGI::generate_from_sk_scratch_space( + &module, basek, k_brk, rank, + ) | cggi_blind_rotate_scratch_space(&module, basek, k_lut, k_brk, rows_brk, rank)); + + let start: Instant = Instant::now(); + let mut brk: BlindRotationKeyCGGI = BlindRotationKeyCGGI::allocate(&module, n_lwe, basek, k_brk, rows_brk, rank); + brk.generate_from_sk( + &module, + &sk_glwe_dft, + &sk_lwe, + &mut source_xa, + &mut source_xe, + 3.2, + scratch.borrow(), + ); + let duration: std::time::Duration = start.elapsed(); + println!("brk-gen: {} ms", duration.as_millis()); + + let mut lwe: LWECiphertext> = LWECiphertext::alloc(n_lwe, basek, k_lwe); + + let mut pt_lwe: LWEPlaintext> = LWEPlaintext::alloc(basek, k_lwe); + + pt_lwe.data.encode_coeff_i64(0, basek, 7, 0, 63, 7); + + println!("{}", pt_lwe.data); + + lwe.encrypt_sk(&pt_lwe, &sk_lwe, &mut source_xa, &mut source_xe, 3.2); + + lwe.decrypt(&mut pt_lwe, &sk_lwe); + + println!("{}", pt_lwe.data); + + let lut: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_lut); + + let mut res: GLWECiphertext> = GLWECiphertext::alloc(&module, basek, k_lut, rank); + + let start: Instant = Instant::now(); + (0..32).for_each(|i|{ + cggi_blind_rotate(&module, &mut res, &lwe, &lut, &brk, scratch.borrow()); + }); + + let duration: std::time::Duration = start.elapsed(); + println!("blind-rotate: {} ms", duration.as_millis()); + + let mut pt: GLWEPlaintext> = GLWEPlaintext::alloc(&module, basek, k_lut); + + res.decrypt(&module , &mut pt, &sk_glwe_dft, scratch.borrow()); + + println!("{}", pt.data); +} diff --git a/core/src/blind_rotation/test_fft64/mod.rs b/core/src/blind_rotation/test_fft64/mod.rs new file mode 100644 index 0000000..1a23dff --- /dev/null +++ b/core/src/blind_rotation/test_fft64/mod.rs @@ -0,0 +1 @@ +pub mod cggi; diff --git a/core/src/elem.rs b/core/src/elem.rs index e659e1e..9a1de39 100644 --- a/core/src/elem.rs +++ b/core/src/elem.rs @@ -1,6 +1,6 @@ use backend::{Backend, Module, ZnxInfos}; -use crate::{FourierGLWECiphertext, div_ceil}; +use crate::FourierGLWECiphertext; pub trait Infos { type Inner: ZnxInfos; diff --git a/core/src/fourier_glwe.rs b/core/src/fourier_glwe.rs deleted file mode 100644 index 0e024a9..0000000 --- a/core/src/fourier_glwe.rs +++ /dev/null @@ -1,321 +0,0 @@ -use backend::{ - Backend, FFT64, MatZnxDftOps, MatZnxDftScratch, Module, ScalarZnxDftOps, Scratch, VecZnxAlloc, VecZnxBig, VecZnxBigAlloc, - VecZnxBigOps, VecZnxBigScratch, VecZnxDft, VecZnxDftAlloc, VecZnxDftOps, ZnxZero, -}; -use sampling::source::Source; - -use crate::{GGSWCiphertext, GLWECiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos, ScratchCore}; - -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, k.div_ceil(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, k.div_ceil(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 - } -} - -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, k.div_ceil(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, k.div_ceil(basek)) + GLWECiphertext::encrypt_sk_scratch_space(module, basek, k) - } - - pub fn decrypt_scratch_space(module: &Module, basek: usize, k: usize) -> usize { - let size: usize = k.div_ceil(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) - } - - 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) - } - - // 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 = k_ggsw.div_ceil(basek); - let res_dft: usize = module.bytes_of_vec_znx_dft(rank + 1, ggsw_size); - let in_size: usize = k_in.div_ceil(basek).div_ceil(digits); - let ggsw_size: usize = k_ggsw.div_ceil(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 encrypt_zero_sk>( - &mut self, - module: &Module, - sk: &GLWESecret, - 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); - } - - 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); - } - } - - 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); - } - } -} - -impl> FourierGLWECiphertext { - pub fn decrypt + AsMut<[u8]>, DataSk: AsRef<[u8]>>( - &self, - module: &Module, - pt: &mut GLWEPlaintext, - sk: &GLWESecret, - 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_fourier, 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/ciphertext.rs b/core/src/fourier_glwe/ciphertext.rs index 425191f..a742e31 100644 --- a/core/src/fourier_glwe/ciphertext.rs +++ b/core/src/fourier_glwe/ciphertext.rs @@ -1,6 +1,6 @@ use backend::{Backend, Module, VecZnxDft, VecZnxDftAlloc}; -use crate::{Infos, div_ceil}; +use crate::Infos; pub struct FourierGLWECiphertext { pub data: VecZnxDft, @@ -11,14 +11,14 @@ pub struct FourierGLWECiphertext { 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)), + data: module.new_vec_znx_dft(rank + 1, k.div_ceil(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)) + module.bytes_of_vec_znx_dft(rank + 1, k.div_ceil(basek)) } } diff --git a/core/src/fourier_glwe/decryption.rs b/core/src/fourier_glwe/decryption.rs index 882be61..6c18383 100644 --- a/core/src/fourier_glwe/decryption.rs +++ b/core/src/fourier_glwe/decryption.rs @@ -3,11 +3,11 @@ use backend::{ VecZnxDftOps, ZnxZero, }; -use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, Infos, div_ceil}; +use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, Infos}; impl FourierGLWECiphertext, FFT64> { pub fn decrypt_scratch_space(module: &Module, basek: usize, k: usize) -> usize { - let size: usize = div_ceil(k, basek); + let size: usize = k.div_ceil(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())) diff --git a/core/src/fourier_glwe/encryption.rs b/core/src/fourier_glwe/encryption.rs index d23ff4a..fd08709 100644 --- a/core/src/fourier_glwe/encryption.rs +++ b/core/src/fourier_glwe/encryption.rs @@ -1,17 +1,17 @@ use backend::{FFT64, Module, Scratch, VecZnxAlloc, VecZnxBigScratch, VecZnxDftOps}; use sampling::source::Source; -use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, Infos, ScratchCore, div_ceil}; +use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, Infos, ScratchCore}; 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.bytes_of_vec_znx(1, k.div_ceil(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) + module.bytes_of_vec_znx(rank + 1, k.div_ceil(basek)) + GLWECiphertext::encrypt_sk_scratch_space(module, basek, k) } } diff --git a/core/src/fourier_glwe/external_product.rs b/core/src/fourier_glwe/external_product.rs index 116416b..01a7371 100644 --- a/core/src/fourier_glwe/external_product.rs +++ b/core/src/fourier_glwe/external_product.rs @@ -3,7 +3,7 @@ use backend::{ VecZnxDftAlloc, VecZnxDftOps, }; -use crate::{FourierGLWECiphertext, GGSWCiphertext, Infos, div_ceil}; +use crate::{FourierGLWECiphertext, GGSWCiphertext, Infos}; impl FourierGLWECiphertext, FFT64> { // WARNING TODO: UPDATE @@ -16,10 +16,10 @@ impl FourierGLWECiphertext, FFT64> { digits: usize, rank: usize, ) -> usize { - let ggsw_size: usize = div_ceil(k_ggsw, basek); + let ggsw_size: usize = k_ggsw.div_ceil(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 in_size: usize = k_in.div_ceil(basek).div_ceil(digits); + let ggsw_size: usize = k_ggsw.div_ceil(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); diff --git a/core/src/fourier_glwe/test_fft64/external_product.rs b/core/src/fourier_glwe/test_fft64/external_product.rs index 2228d29..80c9c9a 100644 --- a/core/src/fourier_glwe/test_fft64/external_product.rs +++ b/core/src/fourier_glwe/test_fft64/external_product.rs @@ -1,6 +1,6 @@ use crate::{ FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWECiphertext, GLWEOps, GLWEPlaintext, GLWESecret, Infos, - div_ceil, noise::noise_ggsw_product, + noise::noise_ggsw_product, }; use backend::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut}; use sampling::source::Source; @@ -10,7 +10,7 @@ fn apply() { let log_n: usize = 8; let basek: usize = 12; let k_in: usize = 45; - let digits: usize = div_ceil(k_in, basek); + let digits: usize = k_in.div_ceil(basek); (1..4).for_each(|rank| { (1..digits + 1).for_each(|di| { let k_ggsw: usize = k_in + basek * di; @@ -26,7 +26,7 @@ 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); + let digits: usize = k_ct.div_ceil(basek); (1..4).for_each(|rank| { (1..digits + 1).for_each(|di| { let k_ggsw: usize = k_ct + basek * di; @@ -39,7 +39,7 @@ fn apply_inplace() { 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 rows: usize = k_in.div_ceil(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); @@ -147,7 +147,7 @@ fn test_apply(log_n: usize, basek: usize, k_out: usize, k_in: usize, k_ggsw: usi 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 rows: usize = k_ct.div_ceil(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); diff --git a/core/src/fourier_glwe/test_fft64/keyswitch.rs b/core/src/fourier_glwe/test_fft64/keyswitch.rs index a459964..50d56ee 100644 --- a/core/src/fourier_glwe/test_fft64/keyswitch.rs +++ b/core/src/fourier_glwe/test_fft64/keyswitch.rs @@ -1,5 +1,5 @@ use crate::{ - FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos, div_ceil, + FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos, noise::log2_std_noise_gglwe_product, }; use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps}; @@ -10,7 +10,7 @@ fn apply() { let log_n: usize = 8; let basek: usize = 12; let k_in: usize = 45; - let digits: usize = div_ceil(k_in, basek); + 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| { @@ -31,7 +31,7 @@ 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); + 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; @@ -54,7 +54,7 @@ fn test_apply( ) { let module: Module = Module::::new(1 << log_n); - let rows: usize = div_ceil(k_in, basek * digits); + 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); @@ -152,7 +152,7 @@ fn test_apply( 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 rows: usize = k_ct.div_ceil(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); diff --git a/core/src/gglwe.rs b/core/src/gglwe.rs deleted file mode 100644 index 66a5238..0000000 --- a/core/src/gglwe.rs +++ /dev/null @@ -1,236 +0,0 @@ -use backend::{ - Backend, FFT64, MatZnxDft, MatZnxDftAlloc, MatZnxDftOps, Module, ScalarZnx, Scratch, VecZnxAlloc, VecZnxDftAlloc, VecZnxOps, - ZnxInfos, ZnxZero, -}; -use sampling::source::Source; - -use crate::{FourierGLWECiphertext, GLWECiphertext, GLWESecret, GetRow, Infos, ScratchCore, 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 = k.div_ceil(basek); - debug_assert!( - size > digits, - "invalid gglwe: ceil(k/basek): {} <= digits: {}", - size, - digits - ); - - debug_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 = k.div_ceil(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, size) - } -} - -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 GGLWECiphertext, FFT64> { - pub fn generate_from_sk_scratch_space(module: &Module, basek: usize, k: usize, rank: usize) -> usize { - let size = k.div_ceil(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: &GLWESecret, - 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> 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/automorphism.rs b/core/src/gglwe/automorphism.rs index 5460511..e18e65a 100644 --- a/core/src/gglwe/automorphism.rs +++ b/core/src/gglwe/automorphism.rs @@ -62,24 +62,33 @@ impl + AsRef<[u8]>> GLWEAutomorphismKey { self.rank_out(), rhs.rank_out() ); + assert!( + self.k() <= lhs.k(), + "output k={} cannot be greater than input k={}", + self.k(), + lhs.k() + ) } 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); + let (mut tmp_idft_data, scratct1) = scratch.tmp_vec_znx_big(module, cols_out, self.size()); - // Get a VecZnxBig from scratch space - let (mut tmp_idft_data, scratch2) = scratch1.tmp_vec_znx_big(module, cols_out, self.size()); + { + let (mut tmp_dft, scratch2) = scratct1.tmp_glwe_fourier(module, lhs.basek(), lhs.k(), lhs.rank()); - // 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); - }); + // Extracts relevant row + lhs.get_row(module, row_j, col_i, &mut tmp_dft); + + // Get a VecZnxBig from scratch space + + // 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(); @@ -97,20 +106,25 @@ impl + AsRef<[u8]>> GLWEAutomorphismKey { }; // 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); + tmp_idft.keyswitch_inplace(module, &rhs.key, scratct1); - // 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); - }); + { + let (mut tmp_dft, _) = scratct1.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank()); - // Sets back the relevant row - self.set_row(module, row_j, col_i, &tmp_dft); + // 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); + } }); }); + let (mut tmp_dft, _) = scratch.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank()); tmp_dft.data.zero(); (self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| { diff --git a/core/src/gglwe/ciphertext.rs b/core/src/gglwe/ciphertext.rs index a4c2f1d..340b897 100644 --- a/core/src/gglwe/ciphertext.rs +++ b/core/src/gglwe/ciphertext.rs @@ -1,6 +1,6 @@ use backend::{Backend, FFT64, MatZnxDft, MatZnxDftAlloc, MatZnxDftOps, Module}; -use crate::{FourierGLWECiphertext, GetRow, Infos, SetRow, div_ceil}; +use crate::{FourierGLWECiphertext, GetRow, Infos, SetRow}; pub struct GGLWECiphertext { pub(crate) data: MatZnxDft, @@ -19,7 +19,7 @@ impl GGLWECiphertext, B> { rank_in: usize, rank_out: usize, ) -> Self { - let size: usize = div_ceil(k, basek); + let size: usize = k.div_ceil(basek); debug_assert!( size > digits, "invalid gglwe: ceil(k/basek): {} <= digits: {}", @@ -52,7 +52,7 @@ impl GGLWECiphertext, B> { rank_in: usize, rank_out: usize, ) -> usize { - let size: usize = div_ceil(k, basek); + let size: usize = k.div_ceil(basek); debug_assert!( size > digits, "invalid gglwe: ceil(k/basek): {} <= digits: {}", diff --git a/core/src/gglwe/encryption.rs b/core/src/gglwe/encryption.rs index 7c4838b..6c31b2a 100644 --- a/core/src/gglwe/encryption.rs +++ b/core/src/gglwe/encryption.rs @@ -5,12 +5,12 @@ use sampling::source::Source; use crate::{ FourierGLWESecret, GGLWECiphertext, GLWEAutomorphismKey, GLWECiphertext, GLWESecret, GLWESwitchingKey, GLWETensorKey, Infos, - ScratchCore, SetRow, div_ceil, + ScratchCore, SetRow, }; 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); + let size = k.div_ceil(basek); GLWECiphertext::encrypt_sk_scratch_space(module, basek, k) + module.bytes_of_vec_znx(rank + 1, size) + module.bytes_of_vec_znx(1, size) diff --git a/core/src/gglwe/test_fft64/automorphism_key.rs b/core/src/gglwe/test_fft64/automorphism_key.rs index c06f0be..61f6825 100644 --- a/core/src/gglwe/test_fft64/automorphism_key.rs +++ b/core/src/gglwe/test_fft64/automorphism_key.rs @@ -2,7 +2,7 @@ use backend::{FFT64, Module, ScalarZnxOps, ScratchOwned, Stats, VecZnxOps}; use sampling::source::Source; use crate::{ - FourierGLWECiphertext, FourierGLWESecret, GLWEAutomorphismKey, GLWEPlaintext, GLWESecret, GetRow, Infos, div_ceil, + FourierGLWECiphertext, FourierGLWESecret, GLWEAutomorphismKey, GLWEPlaintext, GLWESecret, GetRow, Infos, noise::log2_std_noise_gglwe_product, }; diff --git a/core/src/gglwe/test_fft64/gglwe.rs b/core/src/gglwe/test_fft64/gglwe.rs index 39aad9f..0e3796f 100644 --- a/core/src/gglwe/test_fft64/gglwe.rs +++ b/core/src/gglwe/test_fft64/gglwe.rs @@ -3,7 +3,6 @@ use sampling::source::Source; use crate::{ FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos, - div_ceil, noise::{log2_std_noise_gglwe_product, noise_ggsw_product}, }; diff --git a/core/src/ggsw/ciphertext.rs b/core/src/ggsw/ciphertext.rs index ac1fae5..12e9723 100644 --- a/core/src/ggsw/ciphertext.rs +++ b/core/src/ggsw/ciphertext.rs @@ -7,7 +7,7 @@ use sampling::source::Source; use crate::{ FourierGLWECiphertext, FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWESwitchingKey, GLWETensorKey, GetRow, - Infos, ScratchCore, SetRow, div_ceil, + Infos, ScratchCore, SetRow, }; pub struct GGSWCiphertext { @@ -17,8 +17,8 @@ pub struct GGSWCiphertext { pub(crate) digits: usize, } -impl GGSWCiphertext, B> { - pub fn alloc(module: &Module, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self { +impl GGSWCiphertext, FFT64> { + pub fn alloc(module: &Module, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self { let size: usize = k.div_ceil(basek); debug_assert!( size > digits, diff --git a/core/src/ggsw/test_fft64/ggsw.rs b/core/src/ggsw/test_fft64/ggsw.rs index 9219703..714ed2c 100644 --- a/core/src/ggsw/test_fft64/ggsw.rs +++ b/core/src/ggsw/test_fft64/ggsw.rs @@ -6,7 +6,7 @@ use sampling::source::Source; use crate::{ FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWEAutomorphismKey, GLWEPlaintext, GLWESecret, GLWESwitchingKey, - GLWETensorKey, GetRow, Infos, div_ceil, + GLWETensorKey, GetRow, Infos, noise::{noise_ggsw_keyswitch, noise_ggsw_product}, }; diff --git a/core/src/glwe/ciphertext.rs b/core/src/glwe/ciphertext.rs index ff634bd..d0fb39c 100644 --- a/core/src/glwe/ciphertext.rs +++ b/core/src/glwe/ciphertext.rs @@ -1,9 +1,6 @@ -use backend::{ - Backend, FFT64, Module, VecZnx, VecZnxAlloc, VecZnxBigAlloc, VecZnxBigScratch, VecZnxDftAlloc, VecZnxDftOps, VecZnxToMut, - VecZnxToRef, -}; +use backend::{Backend, FFT64, Module, VecZnx, VecZnxAlloc, VecZnxDftOps, VecZnxToMut, VecZnxToRef}; -use crate::{FourierGLWECiphertext, GLWEOps, Infos, SetMetaData, div_ceil}; +use crate::{FourierGLWECiphertext, GLWEOps, Infos, SetMetaData}; pub struct GLWECiphertext { pub data: VecZnx, @@ -14,14 +11,14 @@ pub struct GLWECiphertext { impl GLWECiphertext> { pub fn alloc(module: &Module, basek: usize, k: usize, rank: usize) -> Self { Self { - data: module.new_vec_znx(rank + 1, div_ceil(k, basek)), + data: module.new_vec_znx(rank + 1, k.div_ceil(basek)), basek, k, } } pub fn bytes_of(module: &Module, basek: usize, k: usize, rank: usize) -> usize { - module.bytes_of_vec_znx(rank + 1, div_ceil(k, basek)) + module.bytes_of_vec_znx(rank + 1, k.div_ceil(basek)) } } @@ -62,10 +59,13 @@ impl> GLWECiphertext { } } -impl GLWECiphertext> { - 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) +impl> GLWECiphertext { + pub fn clone(&self) -> GLWECiphertext> { + GLWECiphertext { + data: self.data.clone(), + basek: self.basek(), + k: self.k(), + } } } diff --git a/core/src/glwe/decryption.rs b/core/src/glwe/decryption.rs index eac91d6..e543963 100644 --- a/core/src/glwe/decryption.rs +++ b/core/src/glwe/decryption.rs @@ -1,16 +1,18 @@ -use backend::{FFT64, Module, ScalarZnxDftOps, Scratch, VecZnxBigOps, VecZnxDftOps, ZnxZero}; +use backend::{ + FFT64, Module, ScalarZnxDftOps, Scratch, VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDftAlloc, VecZnxDftOps, + ZnxZero, +}; use crate::{FourierGLWESecret, GLWECiphertext, GLWEPlaintext, Infos}; -impl> GLWECiphertext { - pub fn clone(&self) -> GLWECiphertext> { - GLWECiphertext { - data: self.data.clone(), - basek: self.basek(), - k: self.k(), - } +impl GLWECiphertext> { + pub fn decrypt_scratch_space(module: &Module, basek: usize, k: usize) -> usize { + let size: usize = k.div_ceil(basek); + (module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, size)) + module.bytes_of_vec_znx_big(1, size) } +} +impl> GLWECiphertext { pub fn decrypt + AsRef<[u8]>, DataSk: AsRef<[u8]>>( &self, module: &Module, diff --git a/core/src/glwe/encryption.rs b/core/src/glwe/encryption.rs index 3b70d99..b0a7615 100644 --- a/core/src/glwe/encryption.rs +++ b/core/src/glwe/encryption.rs @@ -4,15 +4,15 @@ use backend::{ }; use sampling::source::Source; -use crate::{FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWEPublicKey, Infos, SIX_SIGMA, dist::Distribution, div_ceil}; +use crate::{FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWEPublicKey, Infos, SIX_SIGMA, dist::Distribution}; impl GLWECiphertext> { pub fn encrypt_sk_scratch_space(module: &Module, basek: usize, k: usize) -> usize { - let size: usize = div_ceil(k, basek); + let size: usize = k.div_ceil(basek); module.vec_znx_big_normalize_tmp_bytes() + module.bytes_of_vec_znx_dft(1, size) + module.bytes_of_vec_znx(1, size) } pub fn encrypt_pk_scratch_space(module: &Module, basek: usize, k: usize) -> usize { - let size: usize = div_ceil(k, basek); + let size: usize = k.div_ceil(basek); ((module.bytes_of_vec_znx_dft(1, size) + module.bytes_of_vec_znx_big(1, size)) | module.bytes_of_scalar_znx(1)) + module.bytes_of_scalar_znx_dft(1) + module.vec_znx_big_normalize_tmp_bytes() @@ -71,7 +71,7 @@ impl + AsMut<[u8]>> GLWECiphertext { sigma: f64, scratch: &mut Scratch, ) { - self.encrypt_pk_private( + self.encrypt_pk_private::( module, Some((pt, 0)), pk, @@ -91,7 +91,7 @@ impl + AsMut<[u8]>> GLWECiphertext { sigma: f64, scratch: &mut Scratch, ) { - self.encrypt_pk_private( + self.encrypt_pk_private::, DataPk>( module, None::<(&GLWEPlaintext>, usize)>, pk, diff --git a/core/src/glwe/external_product.rs b/core/src/glwe/external_product.rs index c44ab75..3ebf339 100644 --- a/core/src/glwe/external_product.rs +++ b/core/src/glwe/external_product.rs @@ -2,7 +2,7 @@ use backend::{ FFT64, MatZnxDftOps, MatZnxDftScratch, Module, Scratch, VecZnxBig, VecZnxBigOps, VecZnxDftAlloc, VecZnxDftOps, VecZnxScratch, }; -use crate::{FourierGLWECiphertext, GGSWCiphertext, GLWECiphertext, Infos, div_ceil}; +use crate::{FourierGLWECiphertext, GGSWCiphertext, GLWECiphertext, Infos}; impl GLWECiphertext> { pub fn external_product_scratch_space( @@ -10,14 +10,14 @@ impl GLWECiphertext> { basek: usize, k_out: usize, k_in: usize, - ggsw_k: usize, + k_ggsw: usize, digits: usize, rank: usize, ) -> usize { let res_dft: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank); - let in_size: usize = div_ceil(div_ceil(k_in, basek), digits); - let out_size: usize = div_ceil(k_out, basek); - let ggsw_size: usize = div_ceil(ggsw_k, basek); + let in_size: usize = k_in.div_ceil(basek).div_ceil(digits); + let out_size: usize = k_out.div_ceil(basek); + let ggsw_size: usize = k_ggsw.div_ceil(basek); let vmp: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size) + module.vmp_apply_tmp_bytes( out_size, @@ -35,11 +35,11 @@ impl GLWECiphertext> { module: &Module, basek: usize, k_out: usize, - ggsw_k: usize, + k_ggsw: usize, digits: usize, rank: usize, ) -> usize { - Self::external_product_scratch_space(module, basek, k_out, k_out, ggsw_k, digits, rank) + Self::external_product_scratch_space(module, basek, k_out, k_out, k_ggsw, digits, rank) } } diff --git a/core/src/glwe/keyswitch.rs b/core/src/glwe/keyswitch.rs index eace187..5fb12fe 100644 --- a/core/src/glwe/keyswitch.rs +++ b/core/src/glwe/keyswitch.rs @@ -3,7 +3,7 @@ use backend::{ VecZnxDftOps, ZnxZero, }; -use crate::{FourierGLWECiphertext, GLWECiphertext, GLWESwitchingKey, Infos, div_ceil}; +use crate::{FourierGLWECiphertext, GLWECiphertext, GLWESwitchingKey, Infos}; impl GLWECiphertext> { pub fn keyswitch_scratch_space( @@ -17,9 +17,9 @@ impl GLWECiphertext> { rank_out: usize, ) -> usize { let res_dft: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank_out + 1); - let in_size: usize = div_ceil(div_ceil(k_in, basek), digits); - let out_size: usize = div_ceil(k_out, basek); - let ksk_size: usize = div_ceil(k_ksk, basek); + let in_size: usize = k_in.div_ceil(basek).div_ceil(digits); + let out_size: usize = k_out.div_ceil(basek); + let ksk_size: usize = k_ksk.div_ceil(basek); let ai_dft: usize = module.bytes_of_vec_znx_dft(rank_in, in_size); let vmp: usize = module.vmp_apply_tmp_bytes(out_size, in_size, in_size, rank_in, rank_out + 1, ksk_size) + module.bytes_of_vec_znx_dft(rank_in, in_size); diff --git a/core/src/glwe/plaintext.rs b/core/src/glwe/plaintext.rs index 9f24be0..5bebc68 100644 --- a/core/src/glwe/plaintext.rs +++ b/core/src/glwe/plaintext.rs @@ -1,6 +1,6 @@ use backend::{Backend, FFT64, Module, VecZnx, VecZnxAlloc, VecZnxToMut, VecZnxToRef}; -use crate::{GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef, GLWEOps, Infos, SetMetaData, div_ceil}; +use crate::{GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef, GLWEOps, Infos, SetMetaData}; pub struct GLWEPlaintext { pub data: VecZnx, diff --git a/core/src/glwe/test_fft64/automorphism.rs b/core/src/glwe/test_fft64/automorphism.rs index 963083b..1b46e63 100644 --- a/core/src/glwe/test_fft64/automorphism.rs +++ b/core/src/glwe/test_fft64/automorphism.rs @@ -3,7 +3,7 @@ use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps}; use sampling::source::Source; use crate::{ - FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, div_ceil, + FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, noise::log2_std_noise_gglwe_product, }; @@ -12,7 +12,7 @@ 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); + 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; @@ -27,7 +27,7 @@ fn apply() { let log_n: usize = 8; let basek: usize = 12; let k_in: usize = 60; - let digits: usize = div_ceil(k_in, basek); + 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; @@ -51,7 +51,7 @@ fn test_automorphism( ) { let module: Module = Module::::new(1 << log_n); - let rows: usize = div_ceil(k_in, basek * digits); + let rows: usize = k_in.div_ceil(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); @@ -149,7 +149,7 @@ fn test_automorphism_inplace( ) { let module: Module = Module::::new(1 << log_n); - let rows: usize = div_ceil(k_ct, basek * digits); + let rows: usize = k_ct.div_ceil(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); diff --git a/core/src/glwe/test_fft64/external_product.rs b/core/src/glwe/test_fft64/external_product.rs index 4ba77c3..aec23b6 100644 --- a/core/src/glwe/test_fft64/external_product.rs +++ b/core/src/glwe/test_fft64/external_product.rs @@ -2,7 +2,7 @@ use backend::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwne use sampling::source::Source; use crate::{ - FourierGLWESecret, GGSWCiphertext, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, div_ceil, noise::noise_ggsw_product, + FourierGLWESecret, GGSWCiphertext, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, noise::noise_ggsw_product, }; #[test] @@ -10,7 +10,7 @@ fn apply() { let log_n: usize = 8; let basek: usize = 12; let k_in: usize = 45; - let digits: usize = div_ceil(k_in, basek); + let digits: usize = k_in.div_ceil(basek); (1..4).for_each(|rank| { (1..digits + 1).for_each(|di| { let k_ggsw: usize = k_in + basek * di; diff --git a/core/src/glwe/test_fft64/keyswitch.rs b/core/src/glwe/test_fft64/keyswitch.rs index f80ae86..732ef03 100644 --- a/core/src/glwe/test_fft64/keyswitch.rs +++ b/core/src/glwe/test_fft64/keyswitch.rs @@ -2,7 +2,7 @@ use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps}; use sampling::source::Source; use crate::{ - FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos, div_ceil, + FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos, noise::log2_std_noise_gglwe_product, }; @@ -11,7 +11,7 @@ fn apply() { let log_n: usize = 8; let basek: usize = 12; let k_in: usize = 45; - let digits: usize = div_ceil(k_in, basek); + 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| { @@ -32,7 +32,7 @@ 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); + 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; @@ -55,7 +55,7 @@ fn test_keyswitch( ) { let module: Module = Module::::new(1 << log_n); - let rows: usize = div_ceil(k_in, basek * digits); + 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); @@ -148,7 +148,7 @@ fn test_keyswitch( 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 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); diff --git a/core/src/glwe/test_fft64/packing.rs b/core/src/glwe/test_fft64/packing.rs index da817e6..0e9ee71 100644 --- a/core/src/glwe/test_fft64/packing.rs +++ b/core/src/glwe/test_fft64/packing.rs @@ -1,4 +1,4 @@ -use crate::{FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEOps, GLWEPacker, GLWEPlaintext, GLWESecret, div_ceil}; +use crate::{FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEOps, GLWEPacker, GLWEPlaintext, GLWESecret}; use std::collections::HashMap; use backend::{Encoding, FFT64, Module, ScratchOwned, Stats}; diff --git a/core/src/glwe/test_fft64/trace.rs b/core/src/glwe/test_fft64/trace.rs index eae2bce..24b0a75 100644 --- a/core/src/glwe/test_fft64/trace.rs +++ b/core/src/glwe/test_fft64/trace.rs @@ -4,7 +4,7 @@ use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps, ZnxVie use sampling::source::Source; use crate::{ - FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, div_ceil, + FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, noise::var_noise_gglwe_product, }; diff --git a/core/src/lib.rs b/core/src/lib.rs index fa6d009..afdcc55 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -7,18 +7,17 @@ pub mod ggsw; pub mod glwe; pub mod lwe; pub mod noise; -mod utils; use backend::Backend; use backend::FFT64; use backend::Module; -pub use elem::*; +pub use elem::{GetRow, Infos, SetMetaData, SetRow}; pub use fourier_glwe::{FourierGLWECiphertext, FourierGLWESecret}; pub use gglwe::{GGLWECiphertext, GLWEAutomorphismKey, GLWESwitchingKey, GLWETensorKey}; -pub use ggsw::*; +pub use ggsw::GGSWCiphertext; pub use glwe::{GLWECiphertext, GLWEOps, GLWEPacker, GLWEPlaintext, GLWEPublicKey, GLWESecret}; pub(crate) use glwe::{GLWECiphertextToMut, GLWECiphertextToRef}; -pub use lwe::*; +pub use lwe::{LWECiphertext, LWESecret}; pub use backend::Scratch; pub use backend::ScratchOwned; @@ -174,7 +173,7 @@ impl ScratchCore for Scratch { k: usize, rank: usize, ) -> (FourierGLWECiphertext<&mut [u8], FFT64>, &mut Self) { - let (data, scratch) = self.tmp_vec_znx_dft(module, rank + 1, div_ceil(k, basek)); + let (data, scratch) = self.tmp_vec_znx_dft(module, rank + 1, k.div_ceil(basek)); (FourierGLWECiphertext { data, basek, k }, scratch) } diff --git a/core/src/lwe/ciphertext.rs b/core/src/lwe/ciphertext.rs new file mode 100644 index 0000000..432a866 --- /dev/null +++ b/core/src/lwe/ciphertext.rs @@ -0,0 +1,77 @@ +use backend::{VecZnx, VecZnxToMut, VecZnxToRef}; + +use crate::{Infos, SetMetaData}; + +pub struct LWECiphertext { + pub(crate) data: VecZnx, + pub(crate) k: usize, + pub(crate) basek: usize, +} + +impl LWECiphertext> { + pub fn alloc(n: usize, basek: usize, k: usize) -> Self { + Self { + data: VecZnx::new::(n, 1, k.div_ceil(basek)), + k: k, + basek: basek, + } + } +} + +impl Infos for LWECiphertext { + type Inner = VecZnx; + + fn n(&self) -> usize{ + &self.inner().n-1 + } + + fn inner(&self) -> &Self::Inner { + &self.data + } + + fn basek(&self) -> usize { + self.basek + } + + fn k(&self) -> usize { + self.k + } +} + +impl + AsRef<[u8]>> SetMetaData for LWECiphertext { + fn set_k(&mut self, k: usize) { + self.k = k + } + + fn set_basek(&mut self, basek: usize) { + self.basek = basek + } +} + +pub trait LWECiphertextToRef { + fn to_ref(&self) -> LWECiphertext<&[u8]>; +} + +impl> LWECiphertextToRef for LWECiphertext { + fn to_ref(&self) -> LWECiphertext<&[u8]> { + LWECiphertext { + data: self.data.to_ref(), + basek: self.basek, + k: self.k, + } + } +} + +pub trait LWECiphertextToMut { + fn to_mut(&mut self) -> LWECiphertext<&mut [u8]>; +} + +impl + AsRef<[u8]>> LWECiphertextToMut for LWECiphertext { + fn to_mut(&mut self) -> LWECiphertext<&mut [u8]> { + LWECiphertext { + data: self.data.to_mut(), + basek: self.basek, + k: self.k, + } + } +} diff --git a/core/src/lwe/decryption.rs b/core/src/lwe/decryption.rs new file mode 100644 index 0000000..5f7c201 --- /dev/null +++ b/core/src/lwe/decryption.rs @@ -0,0 +1,21 @@ +use backend::{alloc_aligned, ZnxView, ZnxViewMut}; + +use crate::{lwe::{LWEPlaintext}, Infos, LWECiphertext, LWESecret, SetMetaData}; + +impl LWECiphertext where DataSelf: AsRef<[u8]>{ + pub fn decrypt(&self, pt: &mut LWEPlaintext, sk: &LWESecret) where DataPt: AsRef<[u8]> + AsMut<[u8]>, DataSk: AsRef<[u8]>{ + #[cfg(debug_assertions)]{ + assert_eq!(self.n(), sk.n()); + } + + (0..pt.size().min(self.size())).for_each(|i|{ + pt.data.at_mut(0, i)[0] = self.data.at(0, i)[0] + self.data.at(0, i)[1..].iter().zip(sk.data.at(0, 0)).map(|(x, y)| x * y).sum::(); + }); + + let mut tmp_bytes: Vec = alloc_aligned(size_of::()); + pt.data.normalize(self.basek(), 0, &mut tmp_bytes); + + pt.set_basek(self.basek()); + pt.set_k(self.k().min(pt.size() * self.basek())); + } +} \ No newline at end of file diff --git a/core/src/lwe/encryption.rs b/core/src/lwe/encryption.rs new file mode 100644 index 0000000..5e993d9 --- /dev/null +++ b/core/src/lwe/encryption.rs @@ -0,0 +1,35 @@ +use backend::{alloc_aligned, AddNormal, FillUniform, VecZnx, ZnxView, ZnxViewMut}; +use sampling::source::Source; + +use crate::{lwe::LWEPlaintext, Infos, LWECiphertext, LWESecret, SIX_SIGMA}; + + + +impl LWECiphertext where DataSelf: AsMut<[u8]> + AsRef<[u8]>{ + pub fn encrypt_sk(&mut self, pt: &LWEPlaintext, sk: &LWESecret, source_xa: &mut Source, source_xe: &mut Source, sigma: f64) where DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>{ + + #[cfg(debug_assertions)]{ + assert_eq!(self.n(), sk.n()) + } + + let basek: usize = self.basek(); + + self.data.fill_uniform(basek, 0, self.size(), source_xa); + let mut tmp_znx: VecZnx> = VecZnx::>::new::(1, 1, self.size()); + + (0..self.size()).for_each(|i|{ + tmp_znx.at_mut(0, i)[0] = pt.data.at(0, i)[0] - self.data.at(0, i)[1..].iter().zip(sk.data.at(0, 0)).map(|(x, y)| x * y).sum::(); + }); + + tmp_znx.add_normal(basek, 0, self.k(), source_xe, sigma, sigma*SIX_SIGMA); + + let mut tmp_bytes: Vec = alloc_aligned(size_of::()); + + tmp_znx.normalize(basek, 0, &mut tmp_bytes); + + (0..self.size()).for_each(|i|{ + self.data.at_mut(0, i)[0] = tmp_znx.at(0, i)[0]; + }); + + } +} diff --git a/core/src/lwe/mod.rs b/core/src/lwe/mod.rs index d91ce80..19a8362 100644 --- a/core/src/lwe/mod.rs +++ b/core/src/lwe/mod.rs @@ -1,3 +1,9 @@ +pub mod ciphertext; pub mod secret; +pub mod encryption; +pub mod decryption; +pub mod plaintext; +pub use ciphertext::LWECiphertext; pub use secret::LWESecret; +pub use plaintext::LWEPlaintext; diff --git a/core/src/lwe/plaintext.rs b/core/src/lwe/plaintext.rs new file mode 100644 index 0000000..a1c0482 --- /dev/null +++ b/core/src/lwe/plaintext.rs @@ -0,0 +1,73 @@ +use backend::{VecZnx, VecZnxToMut, VecZnxToRef}; + +use crate::{Infos, SetMetaData}; + +pub struct LWEPlaintext{ + pub(crate) data: VecZnx, + pub(crate) k: usize, + pub(crate) basek: usize, +} + +impl LWEPlaintext> { + pub fn alloc(basek: usize, k: usize) -> Self { + Self { + data: VecZnx::new::(1, 1, k.div_ceil(basek)), + k: k, + basek: basek, + } + } +} + +impl Infos for LWEPlaintext { + type Inner = VecZnx; + + fn inner(&self) -> &Self::Inner { + &self.data + } + + fn basek(&self) -> usize { + self.basek + } + + fn k(&self) -> usize { + self.k + } +} + +impl + AsRef<[u8]>> SetMetaData for LWEPlaintext { + fn set_k(&mut self, k: usize) { + self.k = k + } + + fn set_basek(&mut self, basek: usize) { + self.basek = basek + } +} + +pub trait LWEPlaintextToRef { + fn to_ref(&self) -> LWEPlaintext<&[u8]>; +} + +impl> LWEPlaintextToRef for LWEPlaintext { + fn to_ref(&self) -> LWEPlaintext<&[u8]> { + LWEPlaintext { + data: self.data.to_ref(), + basek: self.basek, + k: self.k, + } + } +} + +pub trait LWEPlaintextToMut { + fn to_mut(&mut self) -> LWEPlaintext<&mut [u8]>; +} + +impl + AsRef<[u8]>> LWEPlaintextToMut for LWEPlaintext { + fn to_mut(&mut self) -> LWEPlaintext<&mut [u8]> { + LWEPlaintext { + data: self.data.to_mut(), + basek: self.basek, + k: self.k, + } + } +} \ No newline at end of file