wip on BR + added enc/dec for LWE

This commit is contained in:
Jean-Philippe Bossuat
2025-06-13 20:45:24 +02:00
parent e8cfb5e2ab
commit 829b8be610
43 changed files with 745 additions and 688 deletions

View File

@@ -103,7 +103,7 @@ fn alloc_aligned_custom_u8(size: usize, align: usize) -> Vec<u8> {
/// Size of T * size msut be a multiple of [DEFAULTALIGN]. /// Size of T * size msut be a multiple of [DEFAULTALIGN].
pub fn alloc_aligned_custom<T>(size: usize, align: usize) -> Vec<T> { pub fn alloc_aligned_custom<T>(size: usize, align: usize) -> Vec<T> {
assert_eq!( assert_eq!(
(size * size_of::<T>()) % align, (size * size_of::<T>()) % (align/ size_of::<T>()),
0, 0,
"size={} must be a multiple of align={}", "size={} must be a multiple of align={}",
size, size,
@@ -121,7 +121,7 @@ pub fn alloc_aligned_custom<T>(size: usize, align: usize) -> Vec<T> {
/// of [DEFAULTALIGN]/size_of::<T>() that is equal or greater to `size`. /// of [DEFAULTALIGN]/size_of::<T>() that is equal or greater to `size`.
pub fn alloc_aligned<T>(size: usize) -> Vec<T> { pub fn alloc_aligned<T>(size: usize) -> Vec<T> {
alloc_aligned_custom::<T>( alloc_aligned_custom::<T>(
size + (size % (DEFAULTALIGN / size_of::<T>())), size + (DEFAULTALIGN - (size % (DEFAULTALIGN / size_of::<T>()))),
DEFAULTALIGN, DEFAULTALIGN,
) )
} }

View File

@@ -82,6 +82,12 @@ pub trait MatZnxDftOps<BACKEND: Backend> {
where where
A: MatZnxToMut<FFT64>; A: MatZnxToMut<FFT64>;
/// Multiplies A by (X^{k} - 1).
fn mat_znx_dft_mul_x_pow_minus_one_add_inplace<R, A>(&self, k: i64, res: &mut R, a: &A, scratch: &mut Scratch)
where
R: MatZnxToMut<FFT64>,
A: MatZnxToRef<FFT64>;
/// Applies the vector matrix product [VecZnxDft] x [MatZnxDft]. /// Applies the vector matrix product [VecZnxDft] x [MatZnxDft].
/// The size of `buf` is given by [MatZnxDftOps::vmp_apply_dft_to_dft_tmp_bytes]. /// The size of `buf` is given by [MatZnxDftOps::vmp_apply_dft_to_dft_tmp_bytes].
/// ///
@@ -212,7 +218,7 @@ impl MatZnxDftOps<FFT64> for Module<FFT64> {
self.mat_znx_dft_set_row(&mut res, row_i, col_j, &tmp_1); self.mat_znx_dft_set_row(&mut res, row_i, col_j, &tmp_1);
}); });
}) });
} }
fn mat_znx_dft_mul_x_pow_minus_one_inplace<A>(&self, k: i64, a: &mut A, scratch: &mut Scratch) fn mat_znx_dft_mul_x_pow_minus_one_inplace<A>(&self, k: i64, a: &mut A, scratch: &mut Scratch)
@@ -249,7 +255,52 @@ impl MatZnxDftOps<FFT64> for Module<FFT64> {
self.mat_znx_dft_set_row(&mut a, row_i, col_j, &tmp_1); 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<R, A>(&self, k: i64, res: &mut R, a: &A, scratch: &mut Scratch)
where
R: MatZnxToMut<FFT64>,
A: MatZnxToRef<FFT64>,
{
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<R, A>(&self, res: &mut R, res_row: usize, res_col_in: usize, a: &A) fn mat_znx_dft_set_row<R, A>(&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| { (0..cols_out).for_each(|j| {
module.vec_znx_idft(&mut tmp_big, 0, &tmp_dft, j, scratch.borrow()); 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_big_normalize(basek, &mut tmp, 0, &tmp_big, 0, scratch.borrow());
module.vec_znx_rotate(k, &mut want, j, &tmp, 0); module.vec_znx_rotate(k, &mut want, j, &tmp, 0);
module.vec_znx_sub_ab_inplace(&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<FFT64> = Module::<FFT64>::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<Vec<u8>, FFT64> = module.new_mat_znx_dft(rows, cols_in, cols_out, size);
let mut mat_have: MatZnxDft<Vec<u8>, FFT64> = module.new_mat_znx_dft(rows, cols_in, cols_out, size);
let mut tmp: VecZnx<Vec<u8>> = module.new_vec_znx(1, size);
let mut tmp_dft: VecZnxDft<Vec<u8>, 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<Vec<u8>> = module.new_vec_znx(cols_out, size);
let mut want: VecZnx<Vec<u8>> = module.new_vec_znx(cols_out, size);
let mut tmp_big: VecZnxBig<Vec<u8>, 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)
});
});
}
} }

View File

@@ -91,9 +91,9 @@ impl<D: AsMut<[u8]> + AsRef<[u8]>> ScalarZnx<D> {
} }
pub fn fill_binary_block(&mut self, col: usize, block_size: usize, source: &mut Source) { 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 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) { for block in self.at_mut(col, 0).chunks_mut(block_size) {
let idx: usize = source.next_u64n(max_idx, mask_idx) as usize; let idx: usize = source.next_u64n(max_idx, mask_idx) as usize;
if idx != block_size { if idx != block_size {

View File

@@ -177,7 +177,7 @@ impl<D: From<Vec<u8>>> VecZnx<D> {
n * cols * size * size_of::<Scalar>() n * cols * size * size_of::<Scalar>()
} }
pub(crate) fn new<Scalar: Sized>(n: usize, cols: usize, size: usize) -> Self { pub fn new<Scalar: Sized>(n: usize, cols: usize, size: usize) -> Self {
let data = alloc_aligned::<u8>(Self::bytes_of::<Scalar>(n, cols, size)); let data = alloc_aligned::<u8>(Self::bytes_of::<Scalar>(n, cols, size));
Self { Self {
data: data.into(), data: data.into(),
@@ -243,7 +243,13 @@ fn normalize_tmp_bytes(n: usize) -> usize {
n * std::mem::size_of::<i64>() n * std::mem::size_of::<i64>()
} }
#[allow(dead_code)] impl<D: AsRef<[u8]> + AsMut<[u8]>> VecZnx<D>{
pub fn normalize(&mut self, basek: usize, a_col: usize, tmp_bytes: &mut [u8]){
normalize(basek, self, a_col, tmp_bytes);
}
}
fn normalize<D: AsMut<[u8]> + AsRef<[u8]>>(basek: usize, a: &mut VecZnx<D>, a_col: usize, tmp_bytes: &mut [u8]) { fn normalize<D: AsMut<[u8]> + AsRef<[u8]>>(basek: usize, a: &mut VecZnx<D>, a_col: usize, tmp_bytes: &mut [u8]) {
let n: usize = a.n(); let n: usize = a.n();

View File

@@ -26,7 +26,7 @@ fn bench_external_product_glwe_fft64(c: &mut Criterion) {
let rank: usize = p.rank; let rank: usize = p.rank;
let digits: usize = 1; 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 sigma: f64 = 3.2;
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank); let mut ct_ggsw: GGSWCiphertext<Vec<u8>, 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<Params> = vec![Params { let params_set: Vec<Params> = vec![Params {
log_n: 10, log_n: 11,
basek: 7, basek: 22,
k_ct_in: 27, k_ct_in: 44,
k_ct_out: 27, k_ct_out: 44,
k_ggsw: 27, k_ggsw: 54,
rank: 1, rank: 1,
}]; }];

View File

@@ -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<FFT64>,
basek: usize,
k_lut: usize,
k_brk: usize,
rows: usize,
rank: usize,
) -> usize {
let size = k_brk.div_ceil(basek);
GGSWCiphertext::<Vec<u8>, 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<DataRes, DataIn, DataLUT>(
module: &Module<FFT64>,
res: &mut GLWECiphertext<DataRes>,
lwe: &LWECiphertext<DataIn>,
lut: &GLWEPlaintext<DataLUT>,
brk: &BlindRotationKeyCGGI<FFT64>,
scratch: &mut Scratch,
) where
DataRes: AsRef<[u8]> + AsMut<[u8]>,
DataIn: AsRef<[u8]>,
DataLUT: AsRef<[u8]>,
{
println!("{}", lwe.n());
let mut lwe_2n: Vec<i64> = 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<FFT64>, 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
}

View File

@@ -1,22 +1,22 @@
use backend::{Backend, FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxToRef, Scratch, ZnxView, ZnxViewMut}; use backend::{Backend, FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxToRef, Scratch, ZnxView, ZnxViewMut};
use sampling::source::Source; use sampling::source::Source;
use crate::{Distribution, FourierGLWESecret, GGSWCiphertext, GLWEAutomorphismKey, LWESecret}; use crate::{Distribution, FourierGLWESecret, GGSWCiphertext, Infos, LWESecret};
pub struct BlindRotationKeyCGGI<B: Backend> { pub struct BlindRotationKeyCGGI<B: Backend> {
pub(crate) data: Vec<GGSWCiphertext<Vec<u8>, B>>, pub(crate) data: Vec<GGSWCiphertext<Vec<u8>, B>>,
pub(crate) dist: Distribution, pub(crate) dist: Distribution,
} }
pub struct BlindRotationKeyFHEW<B: Backend> { // pub struct BlindRotationKeyFHEW<B: Backend> {
pub(crate) data: Vec<GGSWCiphertext<Vec<u8>, B>>, // pub(crate) data: Vec<GGSWCiphertext<Vec<u8>, B>>,
pub(crate) auto: Vec<GLWEAutomorphismKey<Vec<u8>, B>>, // pub(crate) auto: Vec<GLWEAutomorphismKey<Vec<u8>, B>>,
} //}
impl BlindRotationKeyCGGI<FFT64> { impl BlindRotationKeyCGGI<FFT64> {
pub fn allocate(module: &Module<FFT64>, lwe_degree: usize, basek: usize, k: usize, rows: usize, rank: usize) -> Self { pub fn allocate(module: &Module<FFT64>, n_lwe: usize, basek: usize, k: usize, rows: usize, rank: usize) -> Self {
let mut data: Vec<GGSWCiphertext<Vec<u8>, FFT64>> = Vec::with_capacity(lwe_degree); let mut data: Vec<GGSWCiphertext<Vec<u8>, FFT64>> = Vec::with_capacity(n_lwe);
(0..lwe_degree).for_each(|_| data.push(GGSWCiphertext::alloc(module, basek, k, rows, 1, rank))); (0..n_lwe).for_each(|_| data.push(GGSWCiphertext::alloc(module, basek, k, rows, 1, rank)));
Self { Self {
data, data,
dist: Distribution::NONE, dist: Distribution::NONE,
@@ -61,4 +61,27 @@ impl BlindRotationKeyCGGI<FFT64> {
ggsw.encrypt_sk(module, &pt, sk_glwe, source_xa, source_xe, sigma, scratch); 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()
}
} }

View File

@@ -1,2 +1,6 @@
// pub mod cggi; // pub mod cggi;
pub mod ccgi;
pub mod key; pub mod key;
#[cfg(test)]
pub mod test_fft64;

View File

@@ -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<FFT64> = Module::<FFT64>::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<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_glwe.fill_ternary_prob(0.5, &mut source_xs);
let sk_glwe_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_glwe);
let mut sk_lwe: LWESecret<Vec<u8>> = 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<FFT64> = 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<Vec<u8>> = LWECiphertext::alloc(n_lwe, basek, k_lwe);
let mut pt_lwe: LWEPlaintext<Vec<u8>> = 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<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_lut);
let mut res: GLWECiphertext<Vec<u8>> = 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<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_lut);
res.decrypt(&module , &mut pt, &sk_glwe_dft, scratch.borrow());
println!("{}", pt.data);
}

View File

@@ -0,0 +1 @@
pub mod cggi;

View File

@@ -1,6 +1,6 @@
use backend::{Backend, Module, ZnxInfos}; use backend::{Backend, Module, ZnxInfos};
use crate::{FourierGLWECiphertext, div_ceil}; use crate::FourierGLWECiphertext;
pub trait Infos { pub trait Infos {
type Inner: ZnxInfos; type Inner: ZnxInfos;

View File

@@ -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<C, B: Backend> {
pub data: VecZnxDft<C, B>,
pub basek: usize,
pub k: usize,
}
impl<B: Backend> FourierGLWECiphertext<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
Self {
data: module.new_vec_znx_dft(rank + 1, k.div_ceil(basek)),
basek: basek,
k: k,
}
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize {
module.bytes_of_vec_znx_dft(rank + 1, k.div_ceil(basek))
}
}
impl<T, B: Backend> Infos for FourierGLWECiphertext<T, B> {
type Inner = VecZnxDft<T, B>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<T, B: Backend> FourierGLWECiphertext<T, B> {
pub fn rank(&self) -> usize {
self.cols() - 1
}
}
impl FourierGLWECiphertext<Vec<u8>, FFT64> {
#[allow(dead_code)]
pub(crate) fn idft_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
module.bytes_of_vec_znx(1, 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<FFT64>, 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<FFT64>, 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<FFT64>,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
GLWECiphertext::bytes_of(module, basek, k_out, rank_out)
+ GLWECiphertext::keyswitch_from_fourier_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out)
}
pub fn keyswitch_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
Self::keyswitch_scratch_space(module, basek, k_out, k_out, k_ksk, digits, rank, rank)
}
// WARNING TODO: UPDATE
pub fn external_product_scratch_space(
module: &Module<FFT64>,
basek: usize,
_k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
let ggsw_size: usize = 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<FFT64>,
basek: usize,
k_out: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
Self::external_product_scratch_space(module, basek, k_out, k_out, k_ggsw, digits, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> FourierGLWECiphertext<DataSelf, FFT64> {
pub fn encrypt_zero_sk<DataSk: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
sk: &GLWESecret<DataSk, FFT64>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch,
) {
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<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
lhs: &FourierGLWECiphertext<DataLhs, FFT64>,
rhs: &GLWESwitchingKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
let (mut tmp_ct, scratch1) = scratch.tmp_glwe_ct(module, self.basek(), self.k(), self.rank());
tmp_ct.keyswitch_from_fourier(module, lhs, rhs, scratch1);
tmp_ct.dft(module, self);
}
pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &GLWESwitchingKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
unsafe {
let self_ptr: *mut FourierGLWECiphertext<DataSelf, FFT64> = self as *mut FourierGLWECiphertext<DataSelf, FFT64>;
self.keyswitch(&module, &*self_ptr, rhs, scratch);
}
}
pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
lhs: &FourierGLWECiphertext<DataLhs, FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
let basek: usize = self.basek();
#[cfg(debug_assertions)]
{
assert_eq!(rhs.rank(), lhs.rank());
assert_eq!(rhs.rank(), self.rank());
assert_eq!(self.basek(), basek);
assert_eq!(lhs.basek(), basek);
assert_eq!(rhs.n(), module.n());
assert_eq!(self.n(), module.n());
assert_eq!(lhs.n(), module.n());
assert!(
scratch.available()
>= FourierGLWECiphertext::external_product_scratch_space(
module,
self.basek(),
self.k(),
lhs.k(),
rhs.k(),
rhs.digits(),
rhs.rank(),
)
);
}
let cols: usize = rhs.rank() + 1;
let digits = rhs.digits();
// Space for VMP result in DFT domain and high precision
let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols, rhs.size());
let (mut a_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, (lhs.size() + digits - 1) / digits);
{
(0..digits).for_each(|di| {
a_dft.set_size((lhs.size() + di) / digits);
// Small optimization for digits > 2
// VMP produce some error e, and since we aggregate vmp * 2^{di * B}, then
// we also aggregate ei * 2^{di * B}, with the largest error being ei * 2^{(digits-1) * B}.
// As such we can ignore the last digits-2 limbs safely of the sum of vmp products.
// It is possible to further ignore the last digits-1 limbs, but this introduce
// ~0.5 to 1 bit of additional noise, and thus not chosen here to ensure that the same
// noise is kept with respect to the ideal functionality.
res_dft.set_size(rhs.size() - ((digits - di) as isize - 2).max(0) as usize);
(0..cols).for_each(|col_i| {
module.vec_znx_dft_copy(digits, digits - 1 - di, &mut a_dft, col_i, &lhs.data, col_i);
});
if di == 0 {
module.vmp_apply(&mut res_dft, &a_dft, &rhs.data, scratch2);
} else {
module.vmp_apply_add(&mut res_dft, &a_dft, &rhs.data, di, scratch2);
}
});
}
// VMP result in high precision
let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume::<&mut [u8]>(res_dft);
// Space for VMP result normalized
let (mut res_small, scratch2) = scratch1.tmp_vec_znx(module, cols, rhs.size());
(0..cols).for_each(|i| {
module.vec_znx_big_normalize(basek, &mut res_small, i, &res_big, i, scratch2);
module.vec_znx_dft(1, 0, &mut self.data, i, &res_small, i);
});
}
pub fn external_product_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
unsafe {
let self_ptr: *mut FourierGLWECiphertext<DataSelf, FFT64> = self as *mut FourierGLWECiphertext<DataSelf, FFT64>;
self.external_product(&module, &*self_ptr, rhs, scratch);
}
}
}
impl<DataSelf: AsRef<[u8]>> FourierGLWECiphertext<DataSelf, FFT64> {
pub fn decrypt<DataPt: AsRef<[u8]> + AsMut<[u8]>, DataSk: AsRef<[u8]>>(
&self,
module: &Module<FFT64>,
pt: &mut GLWEPlaintext<DataPt>,
sk: &GLWESecret<DataSk, FFT64>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), sk.rank());
assert_eq!(self.n(), module.n());
assert_eq!(pt.n(), module.n());
assert_eq!(sk.n(), module.n());
}
let cols = self.rank() + 1;
let (mut pt_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, self.size()); // TODO optimize size when pt << ct
pt_big.zero();
{
(1..cols).for_each(|i| {
let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct
module.svp_apply(&mut ci_dft, 0, &sk.data_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<DataRes: AsRef<[u8]> + AsMut<[u8]>>(
&self,
module: &Module<FFT64>,
res: &mut GLWECiphertext<DataRes>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), res.rank());
assert_eq!(self.basek(), res.basek())
}
let min_size: usize = self.size().min(res.size());
let (mut res_big, scratch1) = scratch.tmp_vec_znx_big(module, 1, min_size);
(0..self.rank() + 1).for_each(|i| {
module.vec_znx_idft(&mut res_big, 0, &self.data, i, scratch1);
module.vec_znx_big_normalize(self.basek(), &mut res.data, i, &res_big, 0, scratch1);
});
}
}

View File

@@ -1,6 +1,6 @@
use backend::{Backend, Module, VecZnxDft, VecZnxDftAlloc}; use backend::{Backend, Module, VecZnxDft, VecZnxDftAlloc};
use crate::{Infos, div_ceil}; use crate::Infos;
pub struct FourierGLWECiphertext<C, B: Backend> { pub struct FourierGLWECiphertext<C, B: Backend> {
pub data: VecZnxDft<C, B>, pub data: VecZnxDft<C, B>,
@@ -11,14 +11,14 @@ pub struct FourierGLWECiphertext<C, B: Backend> {
impl<B: Backend> FourierGLWECiphertext<Vec<u8>, B> { impl<B: Backend> FourierGLWECiphertext<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self { pub fn alloc(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
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, basek: basek,
k: k, k: k,
} }
} }
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize { pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize {
module.bytes_of_vec_znx_dft(rank + 1, div_ceil(k, basek)) module.bytes_of_vec_znx_dft(rank + 1, k.div_ceil(basek))
} }
} }

View File

@@ -3,11 +3,11 @@ use backend::{
VecZnxDftOps, ZnxZero, VecZnxDftOps, ZnxZero,
}; };
use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, Infos, div_ceil}; use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, Infos};
impl FourierGLWECiphertext<Vec<u8>, FFT64> { impl FourierGLWECiphertext<Vec<u8>, FFT64> {
pub fn decrypt_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize { pub fn decrypt_scratch_space(module: &Module<FFT64>, 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.vec_znx_big_normalize_tmp_bytes()
| module.bytes_of_vec_znx_dft(1, size) | 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) + module.vec_znx_idft_tmp_bytes()))

View File

@@ -1,17 +1,17 @@
use backend::{FFT64, Module, Scratch, VecZnxAlloc, VecZnxBigScratch, VecZnxDftOps}; use backend::{FFT64, Module, Scratch, VecZnxAlloc, VecZnxBigScratch, VecZnxDftOps};
use sampling::source::Source; use sampling::source::Source;
use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, Infos, ScratchCore, div_ceil}; use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, Infos, ScratchCore};
impl FourierGLWECiphertext<Vec<u8>, FFT64> { impl FourierGLWECiphertext<Vec<u8>, FFT64> {
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) fn idft_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize { pub(crate) fn idft_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
module.bytes_of_vec_znx(1, div_ceil(k, basek)) module.bytes_of_vec_znx(1, k.div_ceil(basek))
+ (module.vec_znx_big_normalize_tmp_bytes() | module.vec_znx_idft_tmp_bytes()) + (module.vec_znx_big_normalize_tmp_bytes() | module.vec_znx_idft_tmp_bytes())
} }
pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize { pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
module.bytes_of_vec_znx(rank + 1, div_ceil(k, basek)) + GLWECiphertext::encrypt_sk_scratch_space(module, basek, k) module.bytes_of_vec_znx(rank + 1, k.div_ceil(basek)) + GLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
} }
} }

View File

@@ -3,7 +3,7 @@ use backend::{
VecZnxDftAlloc, VecZnxDftOps, VecZnxDftAlloc, VecZnxDftOps,
}; };
use crate::{FourierGLWECiphertext, GGSWCiphertext, Infos, div_ceil}; use crate::{FourierGLWECiphertext, GGSWCiphertext, Infos};
impl FourierGLWECiphertext<Vec<u8>, FFT64> { impl FourierGLWECiphertext<Vec<u8>, FFT64> {
// WARNING TODO: UPDATE // WARNING TODO: UPDATE
@@ -16,10 +16,10 @@ impl FourierGLWECiphertext<Vec<u8>, FFT64> {
digits: usize, digits: usize,
rank: usize, rank: usize,
) -> 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 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 in_size: usize = k_in.div_ceil(basek).div_ceil(digits);
let ggsw_size: usize = div_ceil(k_ggsw, basek); let ggsw_size: usize = k_ggsw.div_ceil(basek);
let vmp: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size) 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); + 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 res_small: usize = module.bytes_of_vec_znx(rank + 1, ggsw_size);

View File

@@ -1,6 +1,6 @@
use crate::{ use crate::{
FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWECiphertext, GLWEOps, GLWEPlaintext, GLWESecret, Infos, 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 backend::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut};
use sampling::source::Source; use sampling::source::Source;
@@ -10,7 +10,7 @@ fn apply() {
let log_n: usize = 8; let log_n: usize = 8;
let basek: usize = 12; let basek: usize = 12;
let k_in: usize = 45; 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..4).for_each(|rank| {
(1..digits + 1).for_each(|di| { (1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_in + basek * di; let k_ggsw: usize = k_in + basek * di;
@@ -26,7 +26,7 @@ fn apply_inplace() {
let log_n: usize = 8; let log_n: usize = 8;
let basek: usize = 12; let basek: usize = 12;
let k_ct: usize = 60; 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..4).for_each(|rank| {
(1..digits + 1).for_each(|di| { (1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_ct + basek * 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) { fn test_apply(log_n: usize, basek: usize, k_out: usize, k_in: usize, k_ggsw: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); let module: Module<FFT64> = Module::<FFT64>::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<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank); let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank); let mut ct_in: GLWECiphertext<Vec<u8>> = 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) { fn test_apply_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); let module: Module<FFT64> = Module::<FFT64>::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<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank); let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank); let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);

View File

@@ -1,5 +1,5 @@
use crate::{ use crate::{
FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos, div_ceil, FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos,
noise::log2_std_noise_gglwe_product, noise::log2_std_noise_gglwe_product,
}; };
use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps}; use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps};
@@ -10,7 +10,7 @@ fn apply() {
let log_n: usize = 8; let log_n: usize = 8;
let basek: usize = 12; let basek: usize = 12;
let k_in: usize = 45; 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_in| {
(1..4).for_each(|rank_out| { (1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| { (1..digits + 1).for_each(|di| {
@@ -31,7 +31,7 @@ fn apply_inplace() {
let log_n: usize = 8; let log_n: usize = 8;
let basek: usize = 12; let basek: usize = 12;
let k_ct: usize = 45; 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..4).for_each(|rank| {
(1..digits + 1).for_each(|di| { (1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di; let k_ksk: usize = k_ct + basek * di;
@@ -54,7 +54,7 @@ fn test_apply(
) { ) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); let module: Module<FFT64> = Module::<FFT64>::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<Vec<u8>, FFT64> = let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out); 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) { fn test_apply_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); let module: Module<FFT64> = Module::<FFT64>::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<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank); let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank); let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);

View File

@@ -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<C, B: Backend> {
pub(crate) data: MatZnxDft<C, B>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) digits: usize,
}
impl<B: Backend> GGLWECiphertext<Vec<u8>, B> {
pub fn alloc(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> Self {
let size: usize = 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<FFT64>,
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<T, B: Backend> Infos for GGLWECiphertext<T, B> {
type Inner = MatZnxDft<T, B>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<T, B: Backend> GGLWECiphertext<T, B> {
pub fn rank(&self) -> usize {
self.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.digits
}
pub fn rank_in(&self) -> usize {
self.data.cols_in()
}
pub fn rank_out(&self) -> usize {
self.data.cols_out() - 1
}
}
impl GGLWECiphertext<Vec<u8>, FFT64> {
pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, 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<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize {
unimplemented!()
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGLWECiphertext<DataSelf, FFT64> {
pub fn encrypt_sk<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
pt: &ScalarZnx<DataPt>,
sk: &GLWESecret<DataSk, FFT64>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(self.rank_in(), pt.cols());
assert_eq!(self.rank_out(), sk.rank());
assert_eq!(self.n(), module.n());
assert_eq!(sk.n(), module.n());
assert_eq!(pt.n(), module.n());
assert!(
scratch.available()
>= GGLWECiphertext::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()),
"scratch.available: {} < GGLWECiphertext::generate_from_sk_scratch_space(module, self.rank()={}, \
self.size()={}): {}",
scratch.available(),
self.rank(),
self.size(),
GGLWECiphertext::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank())
);
assert!(
self.rows() * self.digits() * self.basek() <= self.k(),
"self.rows() : {} * self.digits() : {} * self.basek() : {} = {} >= self.k() = {}",
self.rows(),
self.digits(),
self.basek(),
self.rows() * self.digits() * self.basek(),
self.k()
);
}
let rows: usize = self.rows();
let digits: usize = self.digits();
let basek: usize = self.basek();
let k: usize = self.k();
let rank_in: usize = self.rank_in();
let rank_out: usize = self.rank_out();
let (mut tmp_pt, scrach_1) = scratch.tmp_glwe_pt(module, basek, k);
let (mut tmp_ct, scrach_2) = scrach_1.tmp_glwe_ct(module, basek, k, rank_out);
let (mut tmp_ct_dft, scratch_3) = scrach_2.tmp_glwe_fourier(module, basek, k, rank_out);
// For each input column (i.e. rank) produces a GGLWE ciphertext of rank_out+1 columns
//
// Example for ksk rank 2 to rank 3:
//
// (-(a0*s0 + a1*s1 + a2*s2) + s0', a0, a1, a2)
// (-(b0*s0 + b1*s1 + b2*s2) + s0', b0, b1, b2)
//
// Example ksk rank 2 to rank 1
//
// (-(a*s) + s0, a)
// (-(b*s) + s1, b)
(0..rank_in).for_each(|col_i| {
(0..rows).for_each(|row_i| {
// Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
tmp_pt.data.zero(); // zeroes for next iteration
module.vec_znx_add_scalar_inplace(
&mut tmp_pt.data,
0,
(digits - 1) + row_i * digits,
pt,
col_i,
);
module.vec_znx_normalize_inplace(basek, &mut tmp_pt.data, 0, scratch_3);
// rlwe encrypt of vec_znx_pt into vec_znx_ct
tmp_ct.encrypt_sk(module, &tmp_pt, sk, source_xa, source_xe, sigma, scratch_3);
// Switch vec_znx_ct into DFT domain
tmp_ct.dft(module, &mut tmp_ct_dft);
// Stores vec_znx_dft_ct into thw i-th row of the MatZnxDft
self.set_row(module, row_i, col_i, &tmp_ct_dft);
});
});
}
}
impl<C: AsRef<[u8]>> GetRow<FFT64> for GGLWECiphertext<C, FFT64> {
fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>(
&self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
res: &mut FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_get_row(&mut res.data, &self.data, row_i, col_j);
}
}
impl<C: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GGLWECiphertext<C, FFT64> {
fn set_row<R: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
a: &FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_set_row(&mut self.data, row_i, col_j, &a.data);
}
}

View File

@@ -62,24 +62,33 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
self.rank_out(), self.rank_out(),
rhs.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 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.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| { (0..self.rows()).for_each(|row_j| {
let (mut tmp_idft_data, scratct1) = scratch.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());
// Extracts relevant row // Extracts relevant row
lhs.get_row(module, row_j, col_i, &mut tmp_dft); lhs.get_row(module, row_j, col_i, &mut tmp_dft);
// Get a VecZnxBig from scratch space // 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 // Switches input outside of DFT
(0..cols_out).for_each(|i| { (0..cols_out).for_each(|i| {
module.vec_znx_idft(&mut tmp_idft_data, i, &tmp_dft.data, i, scratch2); module.vec_znx_idft(&mut tmp_idft_data, i, &tmp_dft.data, i, scratch2);
}); });
}
// Consumes to small vec znx // Consumes to small vec znx
let mut tmp_idft_small_data: VecZnx<&mut [u8]> = tmp_idft_data.to_vec_znx_small(); let mut tmp_idft_small_data: VecZnx<&mut [u8]> = tmp_idft_data.to_vec_znx_small();
@@ -97,7 +106,10 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
}; };
// Key-switch (-sa + pi_{k}(s), a) to (-pi^{-1}_{k'}(s)a + pi_{k}(s), a) // 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);
{
let (mut tmp_dft, _) = scratct1.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank());
// Applies back the automorphism X^{k}: (-pi^{-1}_{k'}(s)a + pi_{k}(s), a) -> (-pi^{-1}_{k'+k}(s)a + s, a) // 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 // and switches back to DFT domain
@@ -108,9 +120,11 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
// Sets back the relevant row // Sets back the relevant row
self.set_row(module, row_j, col_i, &tmp_dft); 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(); tmp_dft.data.zero();
(self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| { (self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| {

View File

@@ -1,6 +1,6 @@
use backend::{Backend, FFT64, MatZnxDft, MatZnxDftAlloc, MatZnxDftOps, Module}; 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<C, B: Backend> { pub struct GGLWECiphertext<C, B: Backend> {
pub(crate) data: MatZnxDft<C, B>, pub(crate) data: MatZnxDft<C, B>,
@@ -19,7 +19,7 @@ impl<B: Backend> GGLWECiphertext<Vec<u8>, B> {
rank_in: usize, rank_in: usize,
rank_out: usize, rank_out: usize,
) -> Self { ) -> Self {
let size: usize = div_ceil(k, basek); let size: usize = k.div_ceil(basek);
debug_assert!( debug_assert!(
size > digits, size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}", "invalid gglwe: ceil(k/basek): {} <= digits: {}",
@@ -52,7 +52,7 @@ impl<B: Backend> GGLWECiphertext<Vec<u8>, B> {
rank_in: usize, rank_in: usize,
rank_out: usize, rank_out: usize,
) -> usize { ) -> usize {
let size: usize = div_ceil(k, basek); let size: usize = k.div_ceil(basek);
debug_assert!( debug_assert!(
size > digits, size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}", "invalid gglwe: ceil(k/basek): {} <= digits: {}",

View File

@@ -5,12 +5,12 @@ use sampling::source::Source;
use crate::{ use crate::{
FourierGLWESecret, GGLWECiphertext, GLWEAutomorphismKey, GLWECiphertext, GLWESecret, GLWESwitchingKey, GLWETensorKey, Infos, FourierGLWESecret, GGLWECiphertext, GLWEAutomorphismKey, GLWECiphertext, GLWESecret, GLWESwitchingKey, GLWETensorKey, Infos,
ScratchCore, SetRow, div_ceil, ScratchCore, SetRow,
}; };
impl GGLWECiphertext<Vec<u8>, FFT64> { impl GGLWECiphertext<Vec<u8>, FFT64> {
pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize { pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, 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) GLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
+ module.bytes_of_vec_znx(rank + 1, size) + module.bytes_of_vec_znx(rank + 1, size)
+ module.bytes_of_vec_znx(1, size) + module.bytes_of_vec_znx(1, size)

View File

@@ -2,7 +2,7 @@ use backend::{FFT64, Module, ScalarZnxOps, ScratchOwned, Stats, VecZnxOps};
use sampling::source::Source; use sampling::source::Source;
use crate::{ use crate::{
FourierGLWECiphertext, FourierGLWESecret, GLWEAutomorphismKey, GLWEPlaintext, GLWESecret, GetRow, Infos, div_ceil, FourierGLWECiphertext, FourierGLWESecret, GLWEAutomorphismKey, GLWEPlaintext, GLWESecret, GetRow, Infos,
noise::log2_std_noise_gglwe_product, noise::log2_std_noise_gglwe_product,
}; };

View File

@@ -3,7 +3,6 @@ use sampling::source::Source;
use crate::{ use crate::{
FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos, FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos,
div_ceil,
noise::{log2_std_noise_gglwe_product, noise_ggsw_product}, noise::{log2_std_noise_gglwe_product, noise_ggsw_product},
}; };

View File

@@ -7,7 +7,7 @@ use sampling::source::Source;
use crate::{ use crate::{
FourierGLWECiphertext, FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWESwitchingKey, GLWETensorKey, GetRow, FourierGLWECiphertext, FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWESwitchingKey, GLWETensorKey, GetRow,
Infos, ScratchCore, SetRow, div_ceil, Infos, ScratchCore, SetRow,
}; };
pub struct GGSWCiphertext<C, B: Backend> { pub struct GGSWCiphertext<C, B: Backend> {
@@ -17,8 +17,8 @@ pub struct GGSWCiphertext<C, B: Backend> {
pub(crate) digits: usize, pub(crate) digits: usize,
} }
impl<B: Backend> GGSWCiphertext<Vec<u8>, B> { impl GGSWCiphertext<Vec<u8>, FFT64> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self { pub fn alloc(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self {
let size: usize = k.div_ceil(basek); let size: usize = k.div_ceil(basek);
debug_assert!( debug_assert!(
size > digits, size > digits,

View File

@@ -6,7 +6,7 @@ use sampling::source::Source;
use crate::{ use crate::{
FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWEAutomorphismKey, GLWEPlaintext, GLWESecret, GLWESwitchingKey, FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWEAutomorphismKey, GLWEPlaintext, GLWESecret, GLWESwitchingKey,
GLWETensorKey, GetRow, Infos, div_ceil, GLWETensorKey, GetRow, Infos,
noise::{noise_ggsw_keyswitch, noise_ggsw_product}, noise::{noise_ggsw_keyswitch, noise_ggsw_product},
}; };

View File

@@ -1,9 +1,6 @@
use backend::{ use backend::{Backend, FFT64, Module, VecZnx, VecZnxAlloc, VecZnxDftOps, VecZnxToMut, VecZnxToRef};
Backend, FFT64, Module, VecZnx, VecZnxAlloc, VecZnxBigAlloc, VecZnxBigScratch, VecZnxDftAlloc, VecZnxDftOps, VecZnxToMut,
VecZnxToRef,
};
use crate::{FourierGLWECiphertext, GLWEOps, Infos, SetMetaData, div_ceil}; use crate::{FourierGLWECiphertext, GLWEOps, Infos, SetMetaData};
pub struct GLWECiphertext<C> { pub struct GLWECiphertext<C> {
pub data: VecZnx<C>, pub data: VecZnx<C>,
@@ -14,14 +11,14 @@ pub struct GLWECiphertext<C> {
impl GLWECiphertext<Vec<u8>> { impl GLWECiphertext<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self { pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
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, basek,
k, k,
} }
} }
pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize { pub fn bytes_of(module: &Module<FFT64>, 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<C: AsRef<[u8]>> GLWECiphertext<C> {
} }
} }
impl GLWECiphertext<Vec<u8>> { impl<DataSelf: AsRef<[u8]>> GLWECiphertext<DataSelf> {
pub fn decrypt_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize { pub fn clone(&self) -> GLWECiphertext<Vec<u8>> {
let size: usize = div_ceil(k, basek); GLWECiphertext {
(module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, size)) + module.bytes_of_vec_znx_big(1, size) data: self.data.clone(),
basek: self.basek(),
k: self.k(),
}
} }
} }

View File

@@ -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}; use crate::{FourierGLWESecret, GLWECiphertext, GLWEPlaintext, Infos};
impl<DataSelf: AsRef<[u8]>> GLWECiphertext<DataSelf> { impl GLWECiphertext<Vec<u8>> {
pub fn clone(&self) -> GLWECiphertext<Vec<u8>> { pub fn decrypt_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
GLWECiphertext { let size: usize = k.div_ceil(basek);
data: self.data.clone(), (module.vec_znx_big_normalize_tmp_bytes() | module.bytes_of_vec_znx_dft(1, size)) + module.bytes_of_vec_znx_big(1, size)
basek: self.basek(),
k: self.k(),
}
} }
}
impl<DataSelf: AsRef<[u8]>> GLWECiphertext<DataSelf> {
pub fn decrypt<DataPt: AsMut<[u8]> + AsRef<[u8]>, DataSk: AsRef<[u8]>>( pub fn decrypt<DataPt: AsMut<[u8]> + AsRef<[u8]>, DataSk: AsRef<[u8]>>(
&self, &self,
module: &Module<FFT64>, module: &Module<FFT64>,

View File

@@ -4,15 +4,15 @@ use backend::{
}; };
use sampling::source::Source; 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<Vec<u8>> { impl GLWECiphertext<Vec<u8>> {
pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize { pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, 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) 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<FFT64>, basek: usize, k: usize) -> usize { pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, 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_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.bytes_of_scalar_znx_dft(1)
+ module.vec_znx_big_normalize_tmp_bytes() + module.vec_znx_big_normalize_tmp_bytes()
@@ -71,7 +71,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
sigma: f64, sigma: f64,
scratch: &mut Scratch, scratch: &mut Scratch,
) { ) {
self.encrypt_pk_private( self.encrypt_pk_private::<DataPt, DataPk>(
module, module,
Some((pt, 0)), Some((pt, 0)),
pk, pk,
@@ -91,7 +91,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
sigma: f64, sigma: f64,
scratch: &mut Scratch, scratch: &mut Scratch,
) { ) {
self.encrypt_pk_private( self.encrypt_pk_private::<Vec<u8>, DataPk>(
module, module,
None::<(&GLWEPlaintext<Vec<u8>>, usize)>, None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
pk, pk,

View File

@@ -2,7 +2,7 @@ use backend::{
FFT64, MatZnxDftOps, MatZnxDftScratch, Module, Scratch, VecZnxBig, VecZnxBigOps, VecZnxDftAlloc, VecZnxDftOps, VecZnxScratch, 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<Vec<u8>> { impl GLWECiphertext<Vec<u8>> {
pub fn external_product_scratch_space( pub fn external_product_scratch_space(
@@ -10,14 +10,14 @@ impl GLWECiphertext<Vec<u8>> {
basek: usize, basek: usize,
k_out: usize, k_out: usize,
k_in: usize, k_in: usize,
ggsw_k: usize, k_ggsw: usize,
digits: usize, digits: usize,
rank: usize, rank: usize,
) -> usize { ) -> usize {
let res_dft: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank); 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 in_size: usize = k_in.div_ceil(basek).div_ceil(digits);
let out_size: usize = div_ceil(k_out, basek); let out_size: usize = k_out.div_ceil(basek);
let ggsw_size: usize = div_ceil(ggsw_k, basek); let ggsw_size: usize = k_ggsw.div_ceil(basek);
let vmp: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size) let vmp: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size)
+ module.vmp_apply_tmp_bytes( + module.vmp_apply_tmp_bytes(
out_size, out_size,
@@ -35,11 +35,11 @@ impl GLWECiphertext<Vec<u8>> {
module: &Module<FFT64>, module: &Module<FFT64>,
basek: usize, basek: usize,
k_out: usize, k_out: usize,
ggsw_k: usize, k_ggsw: usize,
digits: usize, digits: usize,
rank: usize, rank: usize,
) -> 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)
} }
} }

View File

@@ -3,7 +3,7 @@ use backend::{
VecZnxDftOps, ZnxZero, VecZnxDftOps, ZnxZero,
}; };
use crate::{FourierGLWECiphertext, GLWECiphertext, GLWESwitchingKey, Infos, div_ceil}; use crate::{FourierGLWECiphertext, GLWECiphertext, GLWESwitchingKey, Infos};
impl GLWECiphertext<Vec<u8>> { impl GLWECiphertext<Vec<u8>> {
pub fn keyswitch_scratch_space( pub fn keyswitch_scratch_space(
@@ -17,9 +17,9 @@ impl GLWECiphertext<Vec<u8>> {
rank_out: usize, rank_out: usize,
) -> usize { ) -> usize {
let res_dft: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank_out + 1); 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 in_size: usize = k_in.div_ceil(basek).div_ceil(digits);
let out_size: usize = div_ceil(k_out, basek); let out_size: usize = k_out.div_ceil(basek);
let ksk_size: usize = div_ceil(k_ksk, 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 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) 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); + module.bytes_of_vec_znx_dft(rank_in, in_size);

View File

@@ -1,6 +1,6 @@
use backend::{Backend, FFT64, Module, VecZnx, VecZnxAlloc, VecZnxToMut, VecZnxToRef}; 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<C> { pub struct GLWEPlaintext<C> {
pub data: VecZnx<C>, pub data: VecZnx<C>,

View File

@@ -3,7 +3,7 @@ use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps};
use sampling::source::Source; use sampling::source::Source;
use crate::{ use crate::{
FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, div_ceil, FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos,
noise::log2_std_noise_gglwe_product, noise::log2_std_noise_gglwe_product,
}; };
@@ -12,7 +12,7 @@ fn apply_inplace() {
let log_n: usize = 8; let log_n: usize = 8;
let basek: usize = 12; let basek: usize = 12;
let k_ct: usize = 60; 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..4).for_each(|rank| {
(1..digits + 1).for_each(|di| { (1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di; let k_ksk: usize = k_ct + basek * di;
@@ -27,7 +27,7 @@ fn apply() {
let log_n: usize = 8; let log_n: usize = 8;
let basek: usize = 12; let basek: usize = 12;
let k_in: usize = 60; 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..4).for_each(|rank| {
(1..digits + 1).for_each(|di| { (1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di; let k_ksk: usize = k_in + basek * di;
@@ -51,7 +51,7 @@ fn test_automorphism(
) { ) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); let module: Module<FFT64> = Module::<FFT64>::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<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); let mut autokey: GLWEAutomorphismKey<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank); let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank);
@@ -149,7 +149,7 @@ fn test_automorphism_inplace(
) { ) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); let module: Module<FFT64> = Module::<FFT64>::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<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank); let mut autokey: GLWEAutomorphismKey<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank); let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);

View File

@@ -2,7 +2,7 @@ use backend::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwne
use sampling::source::Source; use sampling::source::Source;
use crate::{ 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] #[test]
@@ -10,7 +10,7 @@ fn apply() {
let log_n: usize = 8; let log_n: usize = 8;
let basek: usize = 12; let basek: usize = 12;
let k_in: usize = 45; 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..4).for_each(|rank| {
(1..digits + 1).for_each(|di| { (1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_in + basek * di; let k_ggsw: usize = k_in + basek * di;

View File

@@ -2,7 +2,7 @@ use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps};
use sampling::source::Source; use sampling::source::Source;
use crate::{ use crate::{
FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos, div_ceil, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos,
noise::log2_std_noise_gglwe_product, noise::log2_std_noise_gglwe_product,
}; };
@@ -11,7 +11,7 @@ fn apply() {
let log_n: usize = 8; let log_n: usize = 8;
let basek: usize = 12; let basek: usize = 12;
let k_in: usize = 45; 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_in| {
(1..4).for_each(|rank_out| { (1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| { (1..digits + 1).for_each(|di| {
@@ -32,7 +32,7 @@ fn apply_inplace() {
let log_n: usize = 8; let log_n: usize = 8;
let basek: usize = 12; let basek: usize = 12;
let k_ct: usize = 45; 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..4).for_each(|rank| {
(1..digits + 1).for_each(|di| { (1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di; let k_ksk: usize = k_ct + basek * di;
@@ -55,7 +55,7 @@ fn test_keyswitch(
) { ) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); let module: Module<FFT64> = Module::<FFT64>::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<Vec<u8>, FFT64> = let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out); 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) { fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); let module: Module<FFT64> = Module::<FFT64>::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<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank); let mut ct_grlwe: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank); let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);

View File

@@ -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 std::collections::HashMap;
use backend::{Encoding, FFT64, Module, ScratchOwned, Stats}; use backend::{Encoding, FFT64, Module, ScratchOwned, Stats};

View File

@@ -4,7 +4,7 @@ use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps, ZnxVie
use sampling::source::Source; use sampling::source::Source;
use crate::{ use crate::{
FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, div_ceil, FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos,
noise::var_noise_gglwe_product, noise::var_noise_gglwe_product,
}; };

View File

@@ -7,18 +7,17 @@ pub mod ggsw;
pub mod glwe; pub mod glwe;
pub mod lwe; pub mod lwe;
pub mod noise; pub mod noise;
mod utils;
use backend::Backend; use backend::Backend;
use backend::FFT64; use backend::FFT64;
use backend::Module; use backend::Module;
pub use elem::*; pub use elem::{GetRow, Infos, SetMetaData, SetRow};
pub use fourier_glwe::{FourierGLWECiphertext, FourierGLWESecret}; pub use fourier_glwe::{FourierGLWECiphertext, FourierGLWESecret};
pub use gglwe::{GGLWECiphertext, GLWEAutomorphismKey, GLWESwitchingKey, GLWETensorKey}; pub use gglwe::{GGLWECiphertext, GLWEAutomorphismKey, GLWESwitchingKey, GLWETensorKey};
pub use ggsw::*; pub use ggsw::GGSWCiphertext;
pub use glwe::{GLWECiphertext, GLWEOps, GLWEPacker, GLWEPlaintext, GLWEPublicKey, GLWESecret}; pub use glwe::{GLWECiphertext, GLWEOps, GLWEPacker, GLWEPlaintext, GLWEPublicKey, GLWESecret};
pub(crate) use glwe::{GLWECiphertextToMut, GLWECiphertextToRef}; pub(crate) use glwe::{GLWECiphertextToMut, GLWECiphertextToRef};
pub use lwe::*; pub use lwe::{LWECiphertext, LWESecret};
pub use backend::Scratch; pub use backend::Scratch;
pub use backend::ScratchOwned; pub use backend::ScratchOwned;
@@ -174,7 +173,7 @@ impl ScratchCore<FFT64> for Scratch {
k: usize, k: usize,
rank: usize, rank: usize,
) -> (FourierGLWECiphertext<&mut [u8], FFT64>, &mut Self) { ) -> (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) (FourierGLWECiphertext { data, basek, k }, scratch)
} }

View File

@@ -0,0 +1,77 @@
use backend::{VecZnx, VecZnxToMut, VecZnxToRef};
use crate::{Infos, SetMetaData};
pub struct LWECiphertext<D> {
pub(crate) data: VecZnx<D>,
pub(crate) k: usize,
pub(crate) basek: usize,
}
impl LWECiphertext<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize) -> Self {
Self {
data: VecZnx::new::<i64>(n, 1, k.div_ceil(basek)),
k: k,
basek: basek,
}
}
}
impl<T> Infos for LWECiphertext<T> {
type Inner = VecZnx<T>;
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<DataSelf: AsMut<[u8]> + AsRef<[u8]>> SetMetaData for LWECiphertext<DataSelf> {
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<D: AsRef<[u8]>> LWECiphertextToRef for LWECiphertext<D> {
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<D: AsMut<[u8]> + AsRef<[u8]>> LWECiphertextToMut for LWECiphertext<D> {
fn to_mut(&mut self) -> LWECiphertext<&mut [u8]> {
LWECiphertext {
data: self.data.to_mut(),
basek: self.basek,
k: self.k,
}
}
}

View File

@@ -0,0 +1,21 @@
use backend::{alloc_aligned, ZnxView, ZnxViewMut};
use crate::{lwe::{LWEPlaintext}, Infos, LWECiphertext, LWESecret, SetMetaData};
impl<DataSelf> LWECiphertext<DataSelf> where DataSelf: AsRef<[u8]>{
pub fn decrypt<DataPt, DataSk>(&self, pt: &mut LWEPlaintext<DataPt>, sk: &LWESecret<DataSk>) 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::<i64>();
});
let mut tmp_bytes: Vec<u8> = alloc_aligned(size_of::<i64>());
pt.data.normalize(self.basek(), 0, &mut tmp_bytes);
pt.set_basek(self.basek());
pt.set_k(self.k().min(pt.size() * self.basek()));
}
}

View File

@@ -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<DataSelf> LWECiphertext<DataSelf> where DataSelf: AsMut<[u8]> + AsRef<[u8]>{
pub fn encrypt_sk<DataPt, DataSk>(&mut self, pt: &LWEPlaintext<DataPt>, sk: &LWESecret<DataSk>, 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<Vec<u8>> = VecZnx::<Vec<u8>>::new::<i64>(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::<i64>();
});
tmp_znx.add_normal(basek, 0, self.k(), source_xe, sigma, sigma*SIX_SIGMA);
let mut tmp_bytes: Vec<u8> = alloc_aligned(size_of::<i64>());
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];
});
}
}

View File

@@ -1,3 +1,9 @@
pub mod ciphertext;
pub mod secret; pub mod secret;
pub mod encryption;
pub mod decryption;
pub mod plaintext;
pub use ciphertext::LWECiphertext;
pub use secret::LWESecret; pub use secret::LWESecret;
pub use plaintext::LWEPlaintext;

73
core/src/lwe/plaintext.rs Normal file
View File

@@ -0,0 +1,73 @@
use backend::{VecZnx, VecZnxToMut, VecZnxToRef};
use crate::{Infos, SetMetaData};
pub struct LWEPlaintext<D>{
pub(crate) data: VecZnx<D>,
pub(crate) k: usize,
pub(crate) basek: usize,
}
impl LWEPlaintext<Vec<u8>> {
pub fn alloc(basek: usize, k: usize) -> Self {
Self {
data: VecZnx::new::<i64>(1, 1, k.div_ceil(basek)),
k: k,
basek: basek,
}
}
}
impl<T> Infos for LWEPlaintext<T> {
type Inner = VecZnx<T>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> SetMetaData for LWEPlaintext<DataSelf> {
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<D: AsRef<[u8]>> LWEPlaintextToRef for LWEPlaintext<D> {
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<D: AsMut<[u8]> + AsRef<[u8]>> LWEPlaintextToMut for LWEPlaintext<D> {
fn to_mut(&mut self) -> LWEPlaintext<&mut [u8]> {
LWEPlaintext {
data: self.data.to_mut(),
basek: self.basek,
k: self.k,
}
}
}