Reorganized other crates

This commit is contained in:
Jean-Philippe Bossuat
2025-06-13 18:57:28 +02:00
parent 4d4b43a4e5
commit e8cfb5e2ab
52 changed files with 2787 additions and 1380 deletions

View File

@@ -29,7 +29,7 @@ pub trait ScalarZnxDftOps<BACKEND: Backend> {
R: VecZnxDftToMut<BACKEND>,
A: ScalarZnxDftToRef<BACKEND>;
fn svp_idft<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch)
fn scalar_znx_idft<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch)
where
R: ScalarZnxToMut,
A: ScalarZnxDftToRef<BACKEND>;
@@ -50,7 +50,7 @@ impl<B: Backend> ScalarZnxDftAlloc<B> for Module<B> {
}
impl ScalarZnxDftOps<FFT64> for Module<FFT64> {
fn svp_idft<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch)
fn scalar_znx_idft<R, A>(&self, res: &mut R, res_col: usize, a: &A, a_col: usize, scratch: &mut Scratch)
where
R: ScalarZnxToMut,
A: ScalarZnxDftToRef<FFT64>,

View File

@@ -1,5 +1,5 @@
use backend::{FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned};
use core::{GGSWCiphertext, GLWECiphertext, GLWESecret, Infos};
use core::{FourierGLWESecret, GGSWCiphertext, GLWECiphertext, GLWESecret, Infos};
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use sampling::source::Source;
use std::hint::black_box;
@@ -52,13 +52,14 @@ fn bench_external_product_glwe_fft64(c: &mut Criterion) {
let mut source_xe = Source::new([0u8; 32]);
let mut source_xa = Source::new([0u8; 32]);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
ct_ggsw.encrypt_sk(
&module,
&pt_rgsw,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -67,7 +68,7 @@ fn bench_external_product_glwe_fft64(c: &mut Criterion) {
ct_glwe_in.encrypt_zero_sk(
&module,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -134,13 +135,14 @@ fn bench_external_product_glwe_inplace_fft64(c: &mut Criterion) {
let mut source_xe = Source::new([0u8; 32]);
let mut source_xa = Source::new([0u8; 32]);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
ct_ggsw.encrypt_sk(
&module,
&pt_rgsw,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -149,7 +151,7 @@ fn bench_external_product_glwe_inplace_fft64(c: &mut Criterion) {
ct_glwe.encrypt_zero_sk(
&module,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,

View File

@@ -1,5 +1,5 @@
use backend::{FFT64, Module, ScratchOwned};
use core::{AutomorphismKey, GLWECiphertext, GLWESecret, GLWESwitchingKey, Infos};
use core::{FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWESecret, GLWESwitchingKey, Infos};
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use sampling::source::Source;
use std::{hint::black_box, time::Duration};
@@ -32,7 +32,8 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) {
let rows: usize = (p.k_ct_in + (p.basek * digits) - 1) / (p.basek * digits);
let sigma: f64 = 3.2;
let mut ksk: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_grlwe, rows, digits, rank_out);
let mut ksk: GLWEAutomorphismKey<Vec<u8>, FFT64> =
GLWEAutomorphismKey::alloc(&module, basek, k_grlwe, rows, digits, rank_out);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_rlwe_in, rank_in);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_rlwe_out, rank_out);
@@ -55,11 +56,12 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) {
let mut source_xe = Source::new([0u8; 32]);
let mut source_xa = Source::new([0u8; 32]);
let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in);
let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
ksk.generate_from_sk(
&module,
@@ -73,7 +75,7 @@ fn bench_keyswitch_glwe_fft64(c: &mut Criterion) {
ct_in.encrypt_zero_sk(
&module,
&sk_in,
&sk_in_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -146,16 +148,18 @@ fn bench_keyswitch_glwe_inplace_fft64(c: &mut Criterion) {
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk_in.fill_ternary_prob(&&module, 0.5, &mut source_xs);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in);
let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk_out.fill_ternary_prob(&&module, 0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out);
ksk.generate_from_sk(
&module,
&sk_in,
&sk_out,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -164,7 +168,7 @@ fn bench_keyswitch_glwe_inplace_fft64(c: &mut Criterion) {
ct.encrypt_zero_sk(
&module,
&sk_in,
&sk_in_dft,
&mut source_xa,
&mut source_xe,
sigma,

View File

@@ -1,16 +1,16 @@
use backend::{Backend, FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxToRef, Scratch, ZnxView, ZnxViewMut};
use sampling::source::Source;
use crate::{AutomorphismKey, GGSWCiphertext, GLWESecret, LWESecret, SecretDistribution};
use crate::{Distribution, FourierGLWESecret, GGSWCiphertext, GLWEAutomorphismKey, LWESecret};
pub struct BlindRotationKeyCGGI<B: Backend> {
pub(crate) data: Vec<GGSWCiphertext<Vec<u8>, B>>,
pub(crate) dist: SecretDistribution,
pub(crate) dist: Distribution,
}
pub struct BlindRotationKeyFHEW<B: Backend> {
pub(crate) data: Vec<GGSWCiphertext<Vec<u8>, B>>,
pub(crate) auto: Vec<AutomorphismKey<Vec<u8>, B>>,
pub(crate) auto: Vec<GLWEAutomorphismKey<Vec<u8>, B>>,
}
impl BlindRotationKeyCGGI<FFT64> {
@@ -19,7 +19,7 @@ impl BlindRotationKeyCGGI<FFT64> {
(0..lwe_degree).for_each(|_| data.push(GGSWCiphertext::alloc(module, basek, k, rows, 1, rank)));
Self {
data,
dist: SecretDistribution::NONE,
dist: Distribution::NONE,
}
}
@@ -30,7 +30,7 @@ impl BlindRotationKeyCGGI<FFT64> {
pub fn generate_from_sk<DataSkGLWE, DataSkLWE>(
&mut self,
module: &Module<FFT64>,
sk_glwe: &GLWESecret<DataSkGLWE, FFT64>,
sk_glwe: &FourierGLWESecret<DataSkGLWE, FFT64>,
sk_lwe: &LWESecret<DataSkLWE>,
source_xa: &mut Source,
source_xe: &mut Source,
@@ -46,7 +46,7 @@ impl BlindRotationKeyCGGI<FFT64> {
assert_eq!(sk_glwe.n(), module.n());
assert_eq!(sk_glwe.rank(), self.data[0].rank());
match sk_lwe.dist {
SecretDistribution::BinaryBlock(_) | SecretDistribution::BinaryFixed(_) | SecretDistribution::BinaryProb(_) => {}
Distribution::BinaryBlock(_) | Distribution::BinaryFixed(_) | Distribution::BinaryProb(_) => {}
_ => panic!("invalid GLWESecret distribution: must be BinaryBlock, BinaryFixed or BinaryProb"),
}
}

View File

@@ -1,5 +1,5 @@
#[derive(Clone, Copy, Debug)]
pub(crate) enum SecretDistribution {
pub(crate) enum Distribution {
TernaryFixed(usize), // Ternary with fixed Hamming weight
TernaryProb(f64), // Ternary with probabilistic Hamming weight
BinaryFixed(usize), // Binary with fixed Hamming weight

View File

@@ -0,0 +1,45 @@
use backend::{Backend, Module, VecZnxDft, VecZnxDftAlloc};
use crate::{Infos, div_ceil};
pub struct FourierGLWECiphertext<C, B: Backend> {
pub data: VecZnxDft<C, B>,
pub basek: usize,
pub k: usize,
}
impl<B: Backend> FourierGLWECiphertext<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
Self {
data: module.new_vec_znx_dft(rank + 1, div_ceil(k, basek)),
basek: basek,
k: k,
}
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize {
module.bytes_of_vec_znx_dft(rank + 1, div_ceil(k, basek))
}
}
impl<T, B: Backend> Infos for FourierGLWECiphertext<T, B> {
type Inner = VecZnxDft<T, B>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<T, B: Backend> FourierGLWECiphertext<T, B> {
pub fn rank(&self) -> usize {
self.cols() - 1
}
}

View File

@@ -0,0 +1,84 @@
use backend::{
FFT64, Module, ScalarZnxDftOps, Scratch, VecZnxBig, VecZnxBigAlloc, VecZnxBigOps, VecZnxBigScratch, VecZnxDftAlloc,
VecZnxDftOps, ZnxZero,
};
use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, Infos, div_ceil};
impl FourierGLWECiphertext<Vec<u8>, FFT64> {
pub fn decrypt_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
let size: usize = div_ceil(k, basek);
(module.vec_znx_big_normalize_tmp_bytes()
| module.bytes_of_vec_znx_dft(1, size)
| (module.bytes_of_vec_znx_big(1, size) + module.vec_znx_idft_tmp_bytes()))
+ module.bytes_of_vec_znx_big(1, size)
}
}
impl<DataSelf: AsRef<[u8]>> FourierGLWECiphertext<DataSelf, FFT64> {
pub fn decrypt<DataPt: AsRef<[u8]> + AsMut<[u8]>, DataSk: AsRef<[u8]>>(
&self,
module: &Module<FFT64>,
pt: &mut GLWEPlaintext<DataPt>,
sk: &FourierGLWESecret<DataSk, FFT64>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), sk.rank());
assert_eq!(self.n(), module.n());
assert_eq!(pt.n(), module.n());
assert_eq!(sk.n(), module.n());
}
let cols = self.rank() + 1;
let (mut pt_big, scratch_1) = scratch.tmp_vec_znx_big(module, 1, self.size()); // TODO optimize size when pt << ct
pt_big.zero();
{
(1..cols).for_each(|i| {
let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct
module.svp_apply(&mut ci_dft, 0, &sk.data, i - 1, &self.data, i);
let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft);
module.vec_znx_big_add_inplace(&mut pt_big, 0, &ci_big, 0);
});
}
{
let (mut c0_big, scratch_2) = scratch_1.tmp_vec_znx_big(module, 1, self.size());
// c0_big = (a * s) + (-a * s + m + e) = BIG(m + e)
module.vec_znx_idft(&mut c0_big, 0, &self.data, 0, scratch_2);
module.vec_znx_big_add_inplace(&mut pt_big, 0, &c0_big, 0);
}
// pt = norm(BIG(m + e))
module.vec_znx_big_normalize(self.basek(), &mut pt.data, 0, &mut pt_big, 0, scratch_1);
pt.basek = self.basek();
pt.k = pt.k().min(self.k());
}
#[allow(dead_code)]
pub(crate) fn idft<DataRes: AsRef<[u8]> + AsMut<[u8]>>(
&self,
module: &Module<FFT64>,
res: &mut GLWECiphertext<DataRes>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), res.rank());
assert_eq!(self.basek(), res.basek())
}
let min_size: usize = self.size().min(res.size());
let (mut res_big, scratch1) = scratch.tmp_vec_znx_big(module, 1, min_size);
(0..self.rank() + 1).for_each(|i| {
module.vec_znx_idft(&mut res_big, 0, &self.data, i, scratch1);
module.vec_znx_big_normalize(self.basek(), &mut res.data, i, &res_big, 0, scratch1);
});
}
}

View File

@@ -0,0 +1,32 @@
use backend::{FFT64, Module, Scratch, VecZnxAlloc, VecZnxBigScratch, VecZnxDftOps};
use sampling::source::Source;
use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, Infos, ScratchCore, div_ceil};
impl FourierGLWECiphertext<Vec<u8>, FFT64> {
#[allow(dead_code)]
pub(crate) fn idft_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
module.bytes_of_vec_znx(1, div_ceil(k, basek))
+ (module.vec_znx_big_normalize_tmp_bytes() | module.vec_znx_idft_tmp_bytes())
}
pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
module.bytes_of_vec_znx(rank + 1, div_ceil(k, basek)) + GLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> FourierGLWECiphertext<DataSelf, FFT64> {
pub fn encrypt_zero_sk<DataSk: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
sk: &FourierGLWESecret<DataSk, FFT64>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch,
) {
let (mut tmp_ct, scratch1) = scratch.tmp_glwe_ct(module, self.basek(), self.k(), self.rank());
tmp_ct.encrypt_zero_sk(module, sk, source_xa, source_xe, sigma, scratch1);
tmp_ct.dft(module, self);
}
}

View File

@@ -0,0 +1,129 @@
use backend::{
FFT64, MatZnxDftOps, MatZnxDftScratch, Module, Scratch, VecZnxAlloc, VecZnxBig, VecZnxBigOps, VecZnxBigScratch,
VecZnxDftAlloc, VecZnxDftOps,
};
use crate::{FourierGLWECiphertext, GGSWCiphertext, Infos, div_ceil};
impl FourierGLWECiphertext<Vec<u8>, FFT64> {
// WARNING TODO: UPDATE
pub fn external_product_scratch_space(
module: &Module<FFT64>,
basek: usize,
_k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
let ggsw_size: usize = div_ceil(k_ggsw, basek);
let res_dft: usize = module.bytes_of_vec_znx_dft(rank + 1, ggsw_size);
let in_size: usize = div_ceil(div_ceil(k_in, basek), digits);
let ggsw_size: usize = div_ceil(k_ggsw, basek);
let vmp: usize = module.bytes_of_vec_znx_dft(rank + 1, in_size)
+ module.vmp_apply_tmp_bytes(ggsw_size, in_size, in_size, rank + 1, rank + 1, ggsw_size);
let res_small: usize = module.bytes_of_vec_znx(rank + 1, ggsw_size);
let normalize: usize = module.vec_znx_big_normalize_tmp_bytes();
res_dft + (vmp | (res_small + normalize))
}
pub fn external_product_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
Self::external_product_scratch_space(module, basek, k_out, k_out, k_ggsw, digits, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> FourierGLWECiphertext<DataSelf, FFT64> {
pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
lhs: &FourierGLWECiphertext<DataLhs, FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
let basek: usize = self.basek();
#[cfg(debug_assertions)]
{
assert_eq!(rhs.rank(), lhs.rank());
assert_eq!(rhs.rank(), self.rank());
assert_eq!(self.basek(), basek);
assert_eq!(lhs.basek(), basek);
assert_eq!(rhs.n(), module.n());
assert_eq!(self.n(), module.n());
assert_eq!(lhs.n(), module.n());
assert!(
scratch.available()
>= FourierGLWECiphertext::external_product_scratch_space(
module,
self.basek(),
self.k(),
lhs.k(),
rhs.k(),
rhs.digits(),
rhs.rank(),
)
);
}
let cols: usize = rhs.rank() + 1;
let digits = rhs.digits();
// Space for VMP result in DFT domain and high precision
let (mut res_dft, scratch1) = scratch.tmp_vec_znx_dft(module, cols, rhs.size());
let (mut a_dft, scratch2) = scratch1.tmp_vec_znx_dft(module, cols, (lhs.size() + digits - 1) / digits);
{
(0..digits).for_each(|di| {
a_dft.set_size((lhs.size() + di) / digits);
// Small optimization for digits > 2
// VMP produce some error e, and since we aggregate vmp * 2^{di * B}, then
// we also aggregate ei * 2^{di * B}, with the largest error being ei * 2^{(digits-1) * B}.
// As such we can ignore the last digits-2 limbs safely of the sum of vmp products.
// It is possible to further ignore the last digits-1 limbs, but this introduce
// ~0.5 to 1 bit of additional noise, and thus not chosen here to ensure that the same
// noise is kept with respect to the ideal functionality.
res_dft.set_size(rhs.size() - ((digits - di) as isize - 2).max(0) as usize);
(0..cols).for_each(|col_i| {
module.vec_znx_dft_copy(digits, digits - 1 - di, &mut a_dft, col_i, &lhs.data, col_i);
});
if di == 0 {
module.vmp_apply(&mut res_dft, &a_dft, &rhs.data, scratch2);
} else {
module.vmp_apply_add(&mut res_dft, &a_dft, &rhs.data, di, scratch2);
}
});
}
// VMP result in high precision
let res_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume::<&mut [u8]>(res_dft);
// Space for VMP result normalized
let (mut res_small, scratch2) = scratch1.tmp_vec_znx(module, cols, rhs.size());
(0..cols).for_each(|i| {
module.vec_znx_big_normalize(basek, &mut res_small, i, &res_big, i, scratch2);
module.vec_znx_dft(1, 0, &mut self.data, i, &res_small, i);
});
}
pub fn external_product_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
unsafe {
let self_ptr: *mut FourierGLWECiphertext<DataSelf, FFT64> = self as *mut FourierGLWECiphertext<DataSelf, FFT64>;
self.external_product(&module, &*self_ptr, rhs, scratch);
}
}
}

View File

@@ -0,0 +1,56 @@
use backend::{FFT64, Module, Scratch};
use crate::{FourierGLWECiphertext, GLWECiphertext, GLWESwitchingKey, Infos, ScratchCore};
impl FourierGLWECiphertext<Vec<u8>, FFT64> {
pub fn keyswitch_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
GLWECiphertext::bytes_of(module, basek, k_out, rank_out)
+ GLWECiphertext::keyswitch_from_fourier_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out)
}
pub fn keyswitch_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
Self::keyswitch_scratch_space(module, basek, k_out, k_out, k_ksk, digits, rank, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> FourierGLWECiphertext<DataSelf, FFT64> {
pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
lhs: &FourierGLWECiphertext<DataLhs, FFT64>,
rhs: &GLWESwitchingKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
let (mut tmp_ct, scratch1) = scratch.tmp_glwe_ct(module, self.basek(), self.k(), self.rank());
tmp_ct.keyswitch_from_fourier(module, lhs, rhs, scratch1);
tmp_ct.dft(module, self);
}
pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &GLWESwitchingKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
unsafe {
let self_ptr: *mut FourierGLWECiphertext<DataSelf, FFT64> = self as *mut FourierGLWECiphertext<DataSelf, FFT64>;
self.keyswitch(&module, &*self_ptr, rhs, scratch);
}
}
}

View File

@@ -0,0 +1,12 @@
pub mod ciphertext;
pub mod decryption;
pub mod encryption;
pub mod external_product;
pub mod keyswitch;
pub mod secret;
pub use ciphertext::FourierGLWECiphertext;
pub use secret::FourierGLWESecret;
#[cfg(test)]
pub mod test_fft64;

View File

@@ -0,0 +1,58 @@
use backend::{Backend, FFT64, Module, ScalarZnxDft, ScalarZnxDftAlloc, ScalarZnxDftOps, ZnxInfos};
use crate::{GLWESecret, dist::Distribution};
pub struct FourierGLWESecret<T, B: Backend> {
pub(crate) data: ScalarZnxDft<T, B>,
pub(crate) dist: Distribution,
}
impl<B: Backend> FourierGLWESecret<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, rank: usize) -> Self {
Self {
data: module.new_scalar_znx_dft(rank),
dist: Distribution::NONE,
}
}
pub fn bytes_of(module: &Module<B>, rank: usize) -> usize {
module.bytes_of_scalar_znx_dft(rank)
}
}
impl FourierGLWESecret<Vec<u8>, FFT64> {
pub fn from<D>(module: &Module<FFT64>, sk: &GLWESecret<D>) -> Self
where
D: AsRef<[u8]>,
{
let mut sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = Self::alloc(module, sk.rank());
sk_dft.set(module, sk);
sk_dft
}
}
impl<DataSelf, B: Backend> FourierGLWESecret<DataSelf, B> {
pub fn n(&self) -> usize {
self.data.n()
}
pub fn log_n(&self) -> usize {
self.data.log_n()
}
pub fn rank(&self) -> usize {
self.data.cols()
}
}
impl<S: AsMut<[u8]> + AsRef<[u8]>> FourierGLWESecret<S, FFT64> {
pub(crate) fn set<D>(&mut self, module: &Module<FFT64>, sk: &GLWESecret<D>)
where
D: AsRef<[u8]>,
{
(0..self.rank()).for_each(|i| {
module.svp_prepare(&mut self.data, i, &sk.data, i);
});
self.dist = sk.dist
}
}

View File

@@ -0,0 +1,246 @@
use crate::{
FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWECiphertext, GLWEOps, GLWEPlaintext, GLWESecret, Infos,
div_ceil, noise::noise_ggsw_product,
};
use backend::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut};
use sampling::source::Source;
#[test]
fn apply() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 45;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank| {
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_in + basek * di;
println!("test external_product digits: {} rank: {}", di, rank);
let k_out: usize = k_ggsw; // Better capture noise.
test_apply(log_n, basek, k_out, k_in, k_ggsw, di, rank, 3.2);
});
});
}
#[test]
fn apply_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank| {
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_ct + basek * di;
println!("test external_product digits: {} rank: {}", di, rank);
test_apply_inplace(log_n, basek, k_ct, k_ggsw, di, rank, 3.2);
});
});
}
fn test_apply(log_n: usize, basek: usize, k_out: usize, k_in: usize, k_ggsw: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = div_ceil(k_in, digits * basek);
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank);
let mut ct_in_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank);
let mut ct_out_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_out, rank);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
// Random input plaintext
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
pt_want.data.at_mut(0, 0)[1] = 1;
let k: i64 = 1;
pt_rgsw.raw_mut()[0] = 1; // X^{0}
module.vec_znx_rotate_inplace(k, &mut pt_rgsw, 0); // X^{k}
let mut scratch: ScratchOwned = ScratchOwned::new(
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k())
| FourierGLWECiphertext::external_product_scratch_space(
&module,
basek,
ct_out.k(),
ct_in.k(),
ct_ggsw.k(),
digits,
rank,
),
);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
ct_ggsw.encrypt_sk(
&module,
&pt_rgsw,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_in.encrypt_sk(
&module,
&pt_want,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_in.dft(&module, &mut ct_in_dft);
ct_out_dft.external_product(&module, &ct_in_dft, &ct_ggsw, scratch.borrow());
ct_out_dft.idft(&module, &mut ct_out, scratch.borrow());
ct_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
pt_want.rotate_inplace(&module, k);
pt_have.sub_inplace_ab(&module, &pt_want);
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let var_gct_err_lhs: f64 = sigma * sigma;
let var_gct_err_rhs: f64 = 0f64;
let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
let var_a0_err: f64 = sigma * sigma;
let var_a1_err: f64 = 1f64 / 12f64;
let noise_want: f64 = noise_ggsw_product(
module.n() as f64,
basek * digits,
0.5,
var_msg,
var_a0_err,
var_a1_err,
var_gct_err_lhs,
var_gct_err_rhs,
rank as f64,
k_in,
k_ggsw,
);
assert!(
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}
fn test_apply_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = div_ceil(k_ct, digits * basek);
let mut ct_ggsw: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ggsw, rows, digits, rank);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut ct_rlwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut pt_rgsw: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
// Random input plaintext
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
pt_want.data.at_mut(0, 0)[1] = 1;
let k: i64 = 1;
pt_rgsw.raw_mut()[0] = 1; // X^{0}
module.vec_znx_rotate_inplace(k, &mut pt_rgsw, 0); // X^{k}
let mut scratch: ScratchOwned = ScratchOwned::new(
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, ct_ggsw.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
| FourierGLWECiphertext::external_product_inplace_scratch_space(&module, basek, ct.k(), ct_ggsw.k(), digits, rank),
);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
ct_ggsw.encrypt_sk(
&module,
&pt_rgsw,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct.encrypt_sk(
&module,
&pt_want,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct.dft(&module, &mut ct_rlwe_dft);
ct_rlwe_dft.external_product_inplace(&module, &ct_ggsw, scratch.borrow());
ct_rlwe_dft.idft(&module, &mut ct, scratch.borrow());
ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
pt_want.rotate_inplace(&module, k);
pt_have.sub_inplace_ab(&module, &pt_want);
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let var_gct_err_lhs: f64 = sigma * sigma;
let var_gct_err_rhs: f64 = 0f64;
let var_msg: f64 = 1f64 / module.n() as f64; // X^{k}
let var_a0_err: f64 = sigma * sigma;
let var_a1_err: f64 = 1f64 / 12f64;
let noise_want: f64 = noise_ggsw_product(
module.n() as f64,
basek * digits,
0.5,
var_msg,
var_a0_err,
var_a1_err,
var_gct_err_lhs,
var_gct_err_rhs,
rank as f64,
k_ct,
k_ggsw,
);
assert!(
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
println!("{} {}", noise_have, noise_want);
}

View File

@@ -0,0 +1,235 @@
use crate::{
FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos, div_ceil,
noise::log2_std_noise_gglwe_product,
};
use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps};
use sampling::source::Source;
#[test]
fn apply() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 45;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
println!(
"test keyswitch digits: {} rank_in: {} rank_out: {}",
di, rank_in, rank_out
);
let k_out: usize = k_ksk; // Better capture noise.
test_apply(log_n, basek, k_in, k_out, k_ksk, di, rank_in, rank_out, 3.2);
})
});
});
}
#[test]
fn apply_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 45;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!("test keyswitch_inplace digits: {} rank: {}", di, rank);
test_apply_inplace(log_n, basek, k_ct, k_ksk, di, rank, 3.2);
});
});
}
fn test_apply(
log_n: usize,
basek: usize,
k_in: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = div_ceil(k_in, basek * digits);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out);
let mut ct_glwe_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank_in);
let mut ct_glwe_dft_in: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank_in);
let mut ct_glwe_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank_out);
let mut ct_glwe_dft_out: FourierGLWECiphertext<Vec<u8>, FFT64> =
FourierGLWECiphertext::alloc(&module, basek, k_out, rank_out);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
// Random input plaintext
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank_out)
| GLWECiphertext::decrypt_scratch_space(&module, basek, k_out)
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_in)
| FourierGLWECiphertext::keyswitch_scratch_space(
&module,
basek,
ct_glwe_out.k(),
ksk.k(),
ct_glwe_in.k(),
digits,
rank_in,
rank_out,
),
);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out);
ksk.generate_from_sk(
&module,
&sk_in,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_glwe_in.encrypt_sk(
&module,
&pt_want,
&sk_in_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_glwe_in.dft(&module, &mut ct_glwe_dft_in);
ct_glwe_dft_out.keyswitch(&module, &ct_glwe_dft_in, &ksk, scratch.borrow());
ct_glwe_dft_out.idft(&module, &mut ct_glwe_out, scratch.borrow());
ct_glwe_out.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank_in as f64,
k_in,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}
fn test_apply_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = div_ceil(k_ct, basek * digits);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut ct_rlwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
// Random input plaintext
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k())
| FourierGLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct_rlwe_dft.k(), ksk.k(), digits, rank),
);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out);
ksk.generate_from_sk(
&module,
&sk_in,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_glwe.encrypt_sk(
&module,
&pt_want,
&sk_in_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_glwe.dft(&module, &mut ct_rlwe_dft);
ct_rlwe_dft.keyswitch_inplace(&module, &ksk, scratch.borrow());
ct_rlwe_dft.idft(&module, &mut ct_glwe, scratch.borrow());
ct_glwe.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
k_ct,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}

View File

@@ -0,0 +1,2 @@
pub mod external_product;
pub mod keyswitch;

View File

@@ -0,0 +1,136 @@
use backend::{FFT64, Module, Scratch, VecZnx, VecZnxDftOps, VecZnxOps, ZnxZero};
use crate::{FourierGLWECiphertext, GLWEAutomorphismKey, GLWECiphertext, GetRow, Infos, ScratchCore, SetRow};
impl GLWEAutomorphismKey<Vec<u8>, FFT64> {
pub fn automorphism_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp_dft: usize = FourierGLWECiphertext::bytes_of(module, basek, k_in, rank);
let tmp_idft: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank);
let idft: usize = module.vec_znx_idft_tmp_bytes();
let keyswitch: usize = GLWECiphertext::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank);
tmp_dft + tmp_idft + idft + keyswitch
}
pub fn automorphism_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
GLWEAutomorphismKey::automorphism_scratch_space(module, basek, k_out, k_out, k_ksk, digits, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
pub fn automorphism<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
lhs: &GLWEAutomorphismKey<DataLhs, FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(
self.rank_in(),
lhs.rank_in(),
"ksk_out input rank: {} != ksk_in input rank: {}",
self.rank_in(),
lhs.rank_in()
);
assert_eq!(
lhs.rank_out(),
rhs.rank_in(),
"ksk_in output rank: {} != ksk_apply input rank: {}",
self.rank_out(),
rhs.rank_in()
);
assert_eq!(
self.rank_out(),
rhs.rank_out(),
"ksk_out output rank: {} != ksk_apply output rank: {}",
self.rank_out(),
rhs.rank_out()
);
}
let cols_out: usize = rhs.rank_out() + 1;
let (mut tmp_dft, scratch1) = scratch.tmp_glwe_fourier(module, lhs.basek(), lhs.k(), lhs.rank());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
// Extracts relevant row
lhs.get_row(module, row_j, col_i, &mut tmp_dft);
// Get a VecZnxBig from scratch space
let (mut tmp_idft_data, scratch2) = scratch1.tmp_vec_znx_big(module, cols_out, self.size());
// Switches input outside of DFT
(0..cols_out).for_each(|i| {
module.vec_znx_idft(&mut tmp_idft_data, i, &tmp_dft.data, i, scratch2);
});
// Consumes to small vec znx
let mut tmp_idft_small_data: VecZnx<&mut [u8]> = tmp_idft_data.to_vec_znx_small();
// Reverts the automorphis key from (-pi^{-1}_{k}(s)a + s, a) to (-sa + pi_{k}(s), a)
(0..cols_out).for_each(|i| {
module.vec_znx_automorphism_inplace(lhs.p(), &mut tmp_idft_small_data, i);
});
// Wraps into ciphertext
let mut tmp_idft: GLWECiphertext<&mut [u8]> = GLWECiphertext::<&mut [u8]> {
data: tmp_idft_small_data,
basek: self.basek(),
k: self.k(),
};
// Key-switch (-sa + pi_{k}(s), a) to (-pi^{-1}_{k'}(s)a + pi_{k}(s), a)
tmp_idft.keyswitch_inplace(module, &rhs.key, scratch2);
// Applies back the automorphism X^{k}: (-pi^{-1}_{k'}(s)a + pi_{k}(s), a) -> (-pi^{-1}_{k'+k}(s)a + s, a)
// and switches back to DFT domain
(0..self.rank_out() + 1).for_each(|i| {
module.vec_znx_automorphism_inplace(lhs.p(), &mut tmp_idft.data, i);
module.vec_znx_dft(1, 0, &mut tmp_dft.data, i, &tmp_idft.data, i);
});
// Sets back the relevant row
self.set_row(module, row_j, col_i, &tmp_dft);
});
});
tmp_dft.data.zero();
(self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| {
(0..self.rank_in()).for_each(|col_j| {
self.set_row(module, row_i, col_j, &tmp_dft);
});
});
self.p = (lhs.p * rhs.p) % (module.cyclotomic_order() as i64);
}
pub fn automorphism_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
unsafe {
let self_ptr: *mut GLWEAutomorphismKey<DataSelf, FFT64> = self as *mut GLWEAutomorphismKey<DataSelf, FFT64>;
self.automorphism(&module, &*self_ptr, rhs, scratch);
}
}
}

View File

@@ -0,0 +1,83 @@
use backend::{Backend, FFT64, MatZnxDft, MatZnxDftOps, Module};
use crate::{FourierGLWECiphertext, GLWESwitchingKey, GetRow, Infos, SetRow};
pub struct GLWEAutomorphismKey<Data, B: Backend> {
pub(crate) key: GLWESwitchingKey<Data, B>,
pub(crate) p: i64,
}
impl GLWEAutomorphismKey<Vec<u8>, FFT64> {
pub fn alloc(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self {
GLWEAutomorphismKey {
key: GLWESwitchingKey::alloc(module, basek, k, rows, digits, rank, rank),
p: 0,
}
}
pub fn bytes_of(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize {
GLWESwitchingKey::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, rank, rank)
}
}
impl<T, B: Backend> Infos for GLWEAutomorphismKey<T, B> {
type Inner = MatZnxDft<T, B>;
fn inner(&self) -> &Self::Inner {
&self.key.inner()
}
fn basek(&self) -> usize {
self.key.basek()
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<T, B: Backend> GLWEAutomorphismKey<T, B> {
pub fn p(&self) -> i64 {
self.p
}
pub fn digits(&self) -> usize {
self.key.digits()
}
pub fn rank(&self) -> usize {
self.key.rank()
}
pub fn rank_in(&self) -> usize {
self.key.rank_in()
}
pub fn rank_out(&self) -> usize {
self.key.rank_out()
}
}
impl<C: AsRef<[u8]>> GetRow<FFT64> for GLWEAutomorphismKey<C, FFT64> {
fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>(
&self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
res: &mut FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_get_row(&mut res.data, &self.key.0.data, row_i, col_j);
}
}
impl<C: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GLWEAutomorphismKey<C, FFT64> {
fn set_row<R: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
a: &FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_set_row(&mut self.key.0.data, row_i, col_j, &a.data);
}
}

View File

@@ -0,0 +1,131 @@
use backend::{Backend, FFT64, MatZnxDft, MatZnxDftAlloc, MatZnxDftOps, Module};
use crate::{FourierGLWECiphertext, GetRow, Infos, SetRow, div_ceil};
pub struct GGLWECiphertext<C, B: Backend> {
pub(crate) data: MatZnxDft<C, B>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) digits: usize,
}
impl<B: Backend> GGLWECiphertext<Vec<u8>, B> {
pub fn alloc(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> Self {
let size: usize = div_ceil(k, basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
Self {
data: module.new_mat_znx_dft(rows, rank_in, rank_out + 1, size),
basek: basek,
k,
digits,
}
}
pub fn bytes_of(
module: &Module<FFT64>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
let size: usize = div_ceil(k, basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
module.bytes_of_mat_znx_dft(rows, rank_in, rank_out + 1, rows)
}
}
impl<T, B: Backend> Infos for GGLWECiphertext<T, B> {
type Inner = MatZnxDft<T, B>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<T, B: Backend> GGLWECiphertext<T, B> {
pub fn rank(&self) -> usize {
self.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.digits
}
pub fn rank_in(&self) -> usize {
self.data.cols_in()
}
pub fn rank_out(&self) -> usize {
self.data.cols_out() - 1
}
}
impl<C: AsRef<[u8]>> GetRow<FFT64> for GGLWECiphertext<C, FFT64> {
fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>(
&self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
res: &mut FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_get_row(&mut res.data, &self.data, row_i, col_j);
}
}
impl<C: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GGLWECiphertext<C, FFT64> {
fn set_row<R: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
a: &FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_set_row(&mut self.data, row_i, col_j, &a.data);
}
}

View File

@@ -0,0 +1,253 @@
use backend::{
FFT64, Module, ScalarZnx, ScalarZnxDftOps, ScalarZnxOps, Scratch, VecZnxAlloc, VecZnxDftAlloc, VecZnxOps, ZnxInfos, ZnxZero,
};
use sampling::source::Source;
use crate::{
FourierGLWESecret, GGLWECiphertext, GLWEAutomorphismKey, GLWECiphertext, GLWESecret, GLWESwitchingKey, GLWETensorKey, Infos,
ScratchCore, SetRow, div_ceil,
};
impl GGLWECiphertext<Vec<u8>, FFT64> {
pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
let size = div_ceil(k, basek);
GLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
+ module.bytes_of_vec_znx(rank + 1, size)
+ module.bytes_of_vec_znx(1, size)
+ module.bytes_of_vec_znx_dft(rank + 1, size)
}
pub fn generate_from_pk_scratch_space(_module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize {
unimplemented!()
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGLWECiphertext<DataSelf, FFT64> {
pub fn encrypt_sk<DataPt: AsRef<[u8]>, DataSk: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
pt: &ScalarZnx<DataPt>,
sk: &FourierGLWESecret<DataSk, FFT64>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(self.rank_in(), pt.cols());
assert_eq!(self.rank_out(), sk.rank());
assert_eq!(self.n(), module.n());
assert_eq!(sk.n(), module.n());
assert_eq!(pt.n(), module.n());
assert!(
scratch.available()
>= GGLWECiphertext::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()),
"scratch.available: {} < GGLWECiphertext::generate_from_sk_scratch_space(module, self.rank()={}, \
self.size()={}): {}",
scratch.available(),
self.rank(),
self.size(),
GGLWECiphertext::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank())
);
assert!(
self.rows() * self.digits() * self.basek() <= self.k(),
"self.rows() : {} * self.digits() : {} * self.basek() : {} = {} >= self.k() = {}",
self.rows(),
self.digits(),
self.basek(),
self.rows() * self.digits() * self.basek(),
self.k()
);
}
let rows: usize = self.rows();
let digits: usize = self.digits();
let basek: usize = self.basek();
let k: usize = self.k();
let rank_in: usize = self.rank_in();
let rank_out: usize = self.rank_out();
let (mut tmp_pt, scrach_1) = scratch.tmp_glwe_pt(module, basek, k);
let (mut tmp_ct, scrach_2) = scrach_1.tmp_glwe_ct(module, basek, k, rank_out);
let (mut tmp_ct_dft, scratch_3) = scrach_2.tmp_glwe_fourier(module, basek, k, rank_out);
// For each input column (i.e. rank) produces a GGLWE ciphertext of rank_out+1 columns
//
// Example for ksk rank 2 to rank 3:
//
// (-(a0*s0 + a1*s1 + a2*s2) + s0', a0, a1, a2)
// (-(b0*s0 + b1*s1 + b2*s2) + s0', b0, b1, b2)
//
// Example ksk rank 2 to rank 1
//
// (-(a*s) + s0, a)
// (-(b*s) + s1, b)
(0..rank_in).for_each(|col_i| {
(0..rows).for_each(|row_i| {
// Adds the scalar_znx_pt to the i-th limb of the vec_znx_pt
tmp_pt.data.zero(); // zeroes for next iteration
module.vec_znx_add_scalar_inplace(
&mut tmp_pt.data,
0,
(digits - 1) + row_i * digits,
pt,
col_i,
);
module.vec_znx_normalize_inplace(basek, &mut tmp_pt.data, 0, scratch_3);
// rlwe encrypt of vec_znx_pt into vec_znx_ct
tmp_ct.encrypt_sk(module, &tmp_pt, sk, source_xa, source_xe, sigma, scratch_3);
// Switch vec_znx_ct into DFT domain
tmp_ct.dft(module, &mut tmp_ct_dft);
// Stores vec_znx_dft_ct into thw i-th row of the MatZnxDft
self.set_row(module, row_i, col_i, &tmp_ct_dft);
});
});
}
}
impl GLWESwitchingKey<Vec<u8>, FFT64> {
pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
GGLWECiphertext::generate_from_sk_scratch_space(module, basek, k, rank)
}
pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize {
GGLWECiphertext::generate_from_pk_scratch_space(module, _basek, _k, _rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
pub fn generate_from_sk<DataSkIn: AsRef<[u8]>, DataSkOut: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
sk_in: &GLWESecret<DataSkIn>,
sk_out: &FourierGLWESecret<DataSkOut, FFT64>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch,
) {
self.0.encrypt_sk(
module,
&sk_in.data,
sk_out,
source_xa,
source_xe,
sigma,
scratch,
);
}
}
impl GLWEAutomorphismKey<Vec<u8>, FFT64> {
pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
GGLWECiphertext::generate_from_sk_scratch_space(module, basek, k, rank) + GLWESecret::bytes_of(module, rank)
}
pub fn generate_from_pk_scratch_space(module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize {
GGLWECiphertext::generate_from_pk_scratch_space(module, _basek, _k, _rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
pub fn generate_from_sk<DataSk: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
p: i64,
sk: &GLWESecret<DataSk>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(self.n(), module.n());
assert_eq!(sk.n(), module.n());
assert_eq!(self.rank_out(), self.rank_in());
assert_eq!(sk.rank(), self.rank());
assert!(
scratch.available()
>= GLWEAutomorphismKey::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank()),
"scratch.available(): {} < AutomorphismKey::generate_from_sk_scratch_space(module, self.rank()={}, \
self.size()={}): {}",
scratch.available(),
self.rank(),
self.size(),
GLWEAutomorphismKey::generate_from_sk_scratch_space(module, self.basek(), self.k(), self.rank())
)
}
let (mut sk_out_dft, scratch_1) = scratch.tmp_fourier_sk(module, sk.rank());
{
let (mut sk_out, _) = scratch_1.tmp_sk(module, sk.rank());
(0..self.rank()).for_each(|i| {
module.scalar_znx_automorphism(
module.galois_element_inv(p),
&mut sk_out.data,
i,
&sk.data,
i,
);
});
sk_out_dft.set(module, &sk_out);
}
self.key.generate_from_sk(
module,
&sk,
&sk_out_dft,
source_xa,
source_xe,
sigma,
scratch_1,
);
self.p = p;
}
}
impl GLWETensorKey<Vec<u8>, FFT64> {
pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
GLWESecret::bytes_of(module, 1)
+ FourierGLWESecret::bytes_of(module, 1)
+ GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWETensorKey<DataSelf, FFT64> {
pub fn generate_from_sk<DataSk: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
sk: &FourierGLWESecret<DataSk, FFT64>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), sk.rank());
assert_eq!(self.n(), module.n());
assert_eq!(sk.n(), module.n());
}
let rank: usize = self.rank();
let (mut sk_ij, scratch1) = scratch.tmp_sk(module, 1);
let (mut sk_ij_dft, scratch2) = scratch1.tmp_fourier_sk(module, 1);
(0..rank).for_each(|i| {
(i..rank).for_each(|j| {
module.svp_apply(&mut sk_ij_dft.data, 0, &sk.data, i, &sk.data, j);
module.scalar_znx_idft(&mut sk_ij.data, 0, &sk_ij_dft.data, 0, scratch2);
self.at_mut(i, j)
.generate_from_sk(module, &sk_ij, sk, source_xa, source_xe, sigma, scratch2);
});
})
}
}

View File

@@ -0,0 +1,162 @@
use backend::{FFT64, Module, Scratch, ZnxZero};
use crate::{FourierGLWECiphertext, GGSWCiphertext, GLWEAutomorphismKey, GLWESwitchingKey, GetRow, Infos, ScratchCore, SetRow};
impl GLWESwitchingKey<Vec<u8>, FFT64> {
pub fn external_product_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp_in: usize = FourierGLWECiphertext::bytes_of(module, basek, k_in, rank);
let tmp_out: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank);
let ggsw: usize = FourierGLWECiphertext::external_product_scratch_space(module, basek, k_out, k_in, k_ggsw, digits, rank);
tmp_in + tmp_out + ggsw
}
pub fn external_product_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank);
let ggsw: usize =
FourierGLWECiphertext::external_product_inplace_scratch_space(module, basek, k_out, k_ggsw, digits, rank);
tmp + ggsw
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
lhs: &GLWESwitchingKey<DataLhs, FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(
self.rank_in(),
lhs.rank_in(),
"ksk_out input rank: {} != ksk_in input rank: {}",
self.rank_in(),
lhs.rank_in()
);
assert_eq!(
lhs.rank_out(),
rhs.rank(),
"ksk_in output rank: {} != ggsw rank: {}",
self.rank_out(),
rhs.rank()
);
assert_eq!(
self.rank_out(),
rhs.rank(),
"ksk_out output rank: {} != ggsw rank: {}",
self.rank_out(),
rhs.rank()
);
}
let (mut tmp_in, scratch1) = scratch.tmp_glwe_fourier(module, lhs.basek(), lhs.k(), lhs.rank());
let (mut tmp_out, scratch2) = scratch1.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
lhs.get_row(module, row_j, col_i, &mut tmp_in);
tmp_out.external_product(module, &tmp_in, rhs, scratch2);
self.set_row(module, row_j, col_i, &tmp_out);
});
});
tmp_out.data.zero();
(self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| {
(0..self.rank_in()).for_each(|col_j| {
self.set_row(module, row_i, col_j, &tmp_out);
});
});
}
pub fn external_product_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(
self.rank_out(),
rhs.rank(),
"ksk_out output rank: {} != ggsw rank: {}",
self.rank_out(),
rhs.rank()
);
}
let (mut tmp, scratch1) = scratch.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank());
println!("tmp: {}", tmp.size());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
self.get_row(module, row_j, col_i, &mut tmp);
tmp.external_product_inplace(module, rhs, scratch1);
self.set_row(module, row_j, col_i, &tmp);
});
});
}
}
impl GLWEAutomorphismKey<Vec<u8>, FFT64> {
pub fn external_product_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_in: usize,
ggsw_k: usize,
digits: usize,
rank: usize,
) -> usize {
GLWESwitchingKey::external_product_scratch_space(module, basek, k_out, k_in, ggsw_k, digits, rank)
}
pub fn external_product_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
ggsw_k: usize,
digits: usize,
rank: usize,
) -> usize {
GLWESwitchingKey::external_product_inplace_scratch_space(module, basek, k_out, ggsw_k, digits, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
lhs: &GLWEAutomorphismKey<DataLhs, FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
self.key.external_product(module, &lhs.key, rhs, scratch);
}
pub fn external_product_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
self.key.external_product_inplace(module, rhs, scratch);
}
}

163
core/src/gglwe/keyswitch.rs Normal file
View File

@@ -0,0 +1,163 @@
use backend::{FFT64, Module, Scratch, ZnxZero};
use crate::{FourierGLWECiphertext, GLWEAutomorphismKey, GLWESwitchingKey, GetRow, Infos, ScratchCore, SetRow};
impl GLWEAutomorphismKey<Vec<u8>, FFT64> {
pub fn keyswitch_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
GLWESwitchingKey::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank, rank)
}
pub fn keyswitch_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
GLWESwitchingKey::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWEAutomorphismKey<DataSelf, FFT64> {
pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
lhs: &GLWEAutomorphismKey<DataLhs, FFT64>,
rhs: &GLWESwitchingKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
self.key.keyswitch(module, &lhs.key, rhs, scratch);
}
pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
self.key.keyswitch_inplace(module, &rhs.key, scratch);
}
}
impl GLWESwitchingKey<Vec<u8>, FFT64> {
pub fn keyswitch_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
let tmp_in: usize = FourierGLWECiphertext::bytes_of(module, basek, k_in, rank_in);
let tmp_out: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank_out);
let ksk: usize =
FourierGLWECiphertext::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out);
tmp_in + tmp_out + ksk
}
pub fn keyswitch_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank);
let ksk: usize = FourierGLWECiphertext::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank);
tmp + ksk
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
lhs: &GLWESwitchingKey<DataLhs, FFT64>,
rhs: &GLWESwitchingKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(
self.rank_in(),
lhs.rank_in(),
"ksk_out input rank: {} != ksk_in input rank: {}",
self.rank_in(),
lhs.rank_in()
);
assert_eq!(
lhs.rank_out(),
rhs.rank_in(),
"ksk_in output rank: {} != ksk_apply input rank: {}",
self.rank_out(),
rhs.rank_in()
);
assert_eq!(
self.rank_out(),
rhs.rank_out(),
"ksk_out output rank: {} != ksk_apply output rank: {}",
self.rank_out(),
rhs.rank_out()
);
}
let (mut tmp_in, scratch1) = scratch.tmp_glwe_fourier(module, lhs.basek(), lhs.k(), lhs.rank());
let (mut tmp_out, scratch2) = scratch1.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
lhs.get_row(module, row_j, col_i, &mut tmp_in);
tmp_out.keyswitch(module, &tmp_in, rhs, scratch2);
self.set_row(module, row_j, col_i, &tmp_out);
});
});
tmp_out.data.zero();
(self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| {
(0..self.rank_in()).for_each(|col_j| {
self.set_row(module, row_i, col_j, &tmp_out);
});
});
}
pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &GLWESwitchingKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(
self.rank_out(),
rhs.rank_out(),
"ksk_out output rank: {} != ksk_apply output rank: {}",
self.rank_out(),
rhs.rank_out()
);
}
let (mut tmp, scratch1) = scratch.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
self.get_row(module, row_j, col_i, &mut tmp);
tmp.keyswitch_inplace(module, rhs, scratch1);
self.set_row(module, row_j, col_i, &tmp);
});
});
}
}

View File

@@ -0,0 +1,91 @@
use backend::{Backend, FFT64, MatZnxDft, MatZnxDftOps, Module};
use crate::{FourierGLWECiphertext, GGLWECiphertext, GetRow, Infos, SetRow};
pub struct GLWESwitchingKey<Data, B: Backend>(pub(crate) GGLWECiphertext<Data, B>);
impl GLWESwitchingKey<Vec<u8>, FFT64> {
pub fn alloc(
module: &Module<FFT64>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> Self {
GLWESwitchingKey(GGLWECiphertext::alloc(
module, basek, k, rows, digits, rank_in, rank_out,
))
}
pub fn bytes_of(
module: &Module<FFT64>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
GGLWECiphertext::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out)
}
}
impl<T, B: Backend> Infos for GLWESwitchingKey<T, B> {
type Inner = MatZnxDft<T, B>;
fn inner(&self) -> &Self::Inner {
self.0.inner()
}
fn basek(&self) -> usize {
self.0.basek()
}
fn k(&self) -> usize {
self.0.k()
}
}
impl<T, B: Backend> GLWESwitchingKey<T, B> {
pub fn rank(&self) -> usize {
self.0.data.cols_out() - 1
}
pub fn rank_in(&self) -> usize {
self.0.data.cols_in()
}
pub fn rank_out(&self) -> usize {
self.0.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.0.digits()
}
}
impl<C: AsRef<[u8]>> GetRow<FFT64> for GLWESwitchingKey<C, FFT64> {
fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>(
&self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
res: &mut FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_get_row(&mut res.data, &self.0.data, row_i, col_j);
}
}
impl<C: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GLWESwitchingKey<C, FFT64> {
fn set_row<R: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
a: &FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_set_row(&mut self.0.data, row_i, col_j, &a.data);
}
}

16
core/src/gglwe/mod.rs Normal file
View File

@@ -0,0 +1,16 @@
pub mod automorphism;
pub mod automorphism_key;
pub mod ciphertext;
pub mod encryption;
pub mod external_product;
pub mod keyswitch;
pub mod keyswitch_key;
pub mod tensor_key;
pub use automorphism_key::GLWEAutomorphismKey;
pub use ciphertext::GGLWECiphertext;
pub use keyswitch_key::GLWESwitchingKey;
pub use tensor_key::GLWETensorKey;
#[cfg(test)]
mod test_fft64;

View File

@@ -1,13 +1,12 @@
use backend::{Backend, FFT64, MatZnxDft, Module, ScalarZnxDftOps, Scratch};
use sampling::source::Source;
use backend::{Backend, FFT64, MatZnxDft, Module};
use crate::{GLWESecret, GLWESwitchingKey, Infos, ScratchCore};
use crate::{GLWESwitchingKey, Infos};
pub struct TensorKey<C, B: Backend> {
pub struct GLWETensorKey<C, B: Backend> {
pub(crate) keys: Vec<GLWESwitchingKey<C, B>>,
}
impl TensorKey<Vec<u8>, FFT64> {
impl GLWETensorKey<Vec<u8>, FFT64> {
pub fn alloc(module: &Module<FFT64>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self {
let mut keys: Vec<GLWESwitchingKey<Vec<u8>, FFT64>> = Vec::new();
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
@@ -25,7 +24,7 @@ impl TensorKey<Vec<u8>, FFT64> {
}
}
impl<T, B: Backend> Infos for TensorKey<T, B> {
impl<T, B: Backend> Infos for GLWETensorKey<T, B> {
type Inner = MatZnxDft<T, B>;
fn inner(&self) -> &Self::Inner {
@@ -41,7 +40,7 @@ impl<T, B: Backend> Infos for TensorKey<T, B> {
}
}
impl<T, B: Backend> TensorKey<T, B> {
impl<T, B: Backend> GLWETensorKey<T, B> {
pub fn rank(&self) -> usize {
self.keys[0].rank()
}
@@ -59,50 +58,7 @@ impl<T, B: Backend> TensorKey<T, B> {
}
}
impl TensorKey<Vec<u8>, FFT64> {
pub fn generate_from_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
GLWESecret::bytes_of(module, 1) + GLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank)
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> TensorKey<DataSelf, FFT64> {
pub fn generate_from_sk<DataSk: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
sk: &GLWESecret<DataSk, FFT64>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), sk.rank());
assert_eq!(self.n(), module.n());
assert_eq!(sk.n(), module.n());
}
let rank: usize = self.rank();
let (mut sk_ij, scratch1) = scratch.tmp_sk(module, 1);
(0..rank).for_each(|i| {
(i..rank).for_each(|j| {
module.svp_apply(
&mut sk_ij.data_fourier,
0,
&sk.data_fourier,
i,
&sk.data_fourier,
j,
);
module.svp_idft(&mut sk_ij.data, 0, &sk_ij.data_fourier, 0, scratch1);
self.at_mut(i, j)
.generate_from_sk(module, &sk_ij, sk, source_xa, source_xe, sigma, scratch1);
});
})
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWETensorKey<DataSelf, FFT64> {
// Returns a mutable reference to GLWESwitchingKey_{s}(s[i] * s[j])
pub fn at_mut(&mut self, mut i: usize, mut j: usize) -> &mut GLWESwitchingKey<DataSelf, FFT64> {
if i > j {
@@ -113,7 +69,7 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> TensorKey<DataSelf, FFT64> {
}
}
impl<DataSelf: AsRef<[u8]>> TensorKey<DataSelf, FFT64> {
impl<DataSelf: AsRef<[u8]>> GLWETensorKey<DataSelf, FFT64> {
// Returns a reference to GLWESwitchingKey_{s}(s[i] * s[j])
pub fn at(&self, mut i: usize, mut j: usize) -> &GLWESwitchingKey<DataSelf, FFT64> {
if i > j {

View File

@@ -2,8 +2,8 @@ use backend::{FFT64, Module, ScalarZnxOps, ScratchOwned, Stats, VecZnxOps};
use sampling::source::Source;
use crate::{
AutomorphismKey, FourierGLWECiphertext, GLWEPlaintext, GLWESecret, GetRow, Infos, div_ceil,
test_fft64::log2_std_noise_gglwe_product,
FourierGLWECiphertext, FourierGLWESecret, GLWEAutomorphismKey, GLWEPlaintext, GLWESecret, GetRow, Infos, div_ceil,
noise::log2_std_noise_gglwe_product,
};
#[test]
@@ -58,24 +58,25 @@ fn test_automorphism(
let rows_in: usize = k_in / (basek * digits);
let rows_apply: usize = k_in.div_ceil(basek * digits);
let mut auto_key_in: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut auto_key_out: AutomorphismKey<Vec<u8>, FFT64> =
AutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank);
let mut auto_key_apply: AutomorphismKey<Vec<u8>, FFT64> =
AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
let mut auto_key_in: GLWEAutomorphismKey<Vec<u8>, FFT64> =
GLWEAutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut auto_key_out: GLWEAutomorphismKey<Vec<u8>, FFT64> =
GLWEAutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank);
let mut auto_key_apply: GLWEAutomorphismKey<Vec<u8>, FFT64> =
GLWEAutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned = ScratchOwned::new(
AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank)
GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out)
| AutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank),
| GLWEAutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
// gglwe_{s1}(s0) = s0 -> s1
auto_key_in.generate_from_sk(
@@ -105,7 +106,7 @@ fn test_automorphism(
let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_out, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut sk_auto: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
(0..rank).for_each(|i| {
module.scalar_znx_automorphism(
@@ -117,12 +118,12 @@ fn test_automorphism(
);
});
sk_auto.prep_fourier(&module);
let sk_auto_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_auto);
(0..auto_key_out.rank_in()).for_each(|col_i| {
(0..auto_key_out.rows()).for_each(|row_i| {
auto_key_out.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto, scratch.borrow());
ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
@@ -174,22 +175,23 @@ fn test_automorphism_inplace(
let rows_in: usize = k_in / (basek * digits);
let rows_apply: usize = k_in.div_ceil(basek * digits);
let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut auto_key_apply: AutomorphismKey<Vec<u8>, FFT64> =
AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
let mut auto_key: GLWEAutomorphismKey<Vec<u8>, FFT64> =
GLWEAutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut auto_key_apply: GLWEAutomorphismKey<Vec<u8>, FFT64> =
GLWEAutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned = ScratchOwned::new(
AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank)
GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_apply, rank)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_in)
| AutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank),
| GLWEAutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
// gglwe_{s1}(s0) = s0 -> s1
auto_key.generate_from_sk(
@@ -219,8 +221,9 @@ fn test_automorphism_inplace(
let mut ct_glwe_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_in, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut sk_auto: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
(0..rank).for_each(|i| {
module.scalar_znx_automorphism(
module.galois_element_inv(p0 * p1),
@@ -231,13 +234,13 @@ fn test_automorphism_inplace(
);
});
sk_auto.prep_fourier(&module);
let sk_auto_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_auto);
(0..auto_key.rank_in()).for_each(|col_i| {
(0..auto_key.rows()).for_each(|row_i| {
auto_key.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto, scratch.borrow());
ct_glwe_dft.decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,

View File

@@ -2,8 +2,9 @@ use backend::{FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxToMut, ScratchO
use sampling::source::Source;
use crate::{
FourierGLWECiphertext, GGSWCiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos, div_ceil,
test_fft64::{log2_std_noise_gglwe_product, noise_ggsw_product},
FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos,
div_ceil,
noise::{log2_std_noise_gglwe_product, noise_ggsw_product},
};
#[test]
@@ -148,16 +149,17 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ksk: usize, digits: usize, rank
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ksk),
);
let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out);
ksk.generate_from_sk(
&module,
&sk_in,
&sk_out,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -170,7 +172,7 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ksk: usize, digits: usize, rank
(0..ksk.rank_in()).for_each(|col_i| {
(0..ksk.rows()).for_each(|row_i| {
ksk.get_row(&module, row_i, col_i, &mut ct_glwe_fourier);
ct_glwe_fourier.decrypt(&module, &mut pt, &sk_out, scratch.borrow());
ct_glwe_fourier.decrypt(&module, &mut pt, &sk_out_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
@@ -247,20 +249,22 @@ fn test_key_switch(
),
);
let mut sk0: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in_s0s1);
sk0.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in_s0s1);
sk0.fill_ternary_prob(0.5, &mut source_xs);
let mut sk1: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out_s0s1);
sk1.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out_s0s1);
sk1.fill_ternary_prob(0.5, &mut source_xs);
let sk1_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk1);
let mut sk2: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out_s1s2);
sk2.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out_s1s2);
sk2.fill_ternary_prob(0.5, &mut source_xs);
let sk2_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk2);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe_s0s1.generate_from_sk(
&module,
&sk0,
&sk1,
&sk1_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -271,7 +275,7 @@ fn test_key_switch(
ct_gglwe_s1s2.generate_from_sk(
&module,
&sk1,
&sk2,
&sk2_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -288,7 +292,7 @@ fn test_key_switch(
(0..ct_gglwe_s0s2.rank_in()).for_each(|col_i| {
(0..ct_gglwe_s0s2.rows()).for_each(|row_i| {
ct_gglwe_s0s2.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk2, scratch.borrow());
ct_glwe_dft.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
@@ -350,20 +354,22 @@ fn test_key_switch_inplace(
| GLWESwitchingKey::keyswitch_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, rank_out),
);
let mut sk0: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in);
sk0.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk0: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in);
sk0.fill_ternary_prob(0.5, &mut source_xs);
let mut sk1: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out);
sk1.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk1: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out);
sk1.fill_ternary_prob(0.5, &mut source_xs);
let sk1_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk1);
let mut sk2: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out);
sk2.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk2: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out);
sk2.fill_ternary_prob(0.5, &mut source_xs);
let sk2_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk2);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe_s0s1.generate_from_sk(
&module,
&sk0,
&sk1,
&sk1_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -374,7 +380,7 @@ fn test_key_switch_inplace(
ct_gglwe_s1s2.generate_from_sk(
&module,
&sk1,
&sk2,
&sk2_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -392,7 +398,7 @@ fn test_key_switch_inplace(
(0..ct_gglwe_s0s2.rank_in()).for_each(|col_i| {
(0..ct_gglwe_s0s2.rows()).for_each(|row_i| {
ct_gglwe_s0s2.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk2, scratch.borrow());
ct_glwe_dft.decrypt(&module, &mut pt, &sk2_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
0,
@@ -464,17 +470,18 @@ fn test_external_product(
pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r}
let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe_in.generate_from_sk(
&module,
&sk_in,
&sk_out,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -484,7 +491,7 @@ fn test_external_product(
ct_rgsw.encrypt_sk(
&module,
&pt_rgsw,
&sk_out,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -504,7 +511,7 @@ fn test_external_product(
(0..rank_in).for_each(|col_i| {
(0..ct_gglwe_out.rows()).for_each(|row_i| {
ct_gglwe_out.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk_out, scratch.borrow());
ct_glwe_dft.decrypt(&module, &mut pt, &sk_out_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,
@@ -584,17 +591,18 @@ fn test_external_product_inplace(
pt_rgsw.to_mut().raw_mut()[r] = 1; // X^{r}
let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out);
// gglwe_{s1}(s0) = s0 -> s1
ct_gglwe.generate_from_sk(
&module,
&sk_in,
&sk_out,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -604,7 +612,7 @@ fn test_external_product_inplace(
ct_rgsw.encrypt_sk(
&module,
&pt_rgsw,
&sk_out,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -624,7 +632,7 @@ fn test_external_product_inplace(
(0..rank_in).for_each(|col_i| {
(0..ct_gglwe.rows()).for_each(|row_i| {
ct_gglwe.get_row(&module, row_i, col_i, &mut ct_glwe_dft);
ct_glwe_dft.decrypt(&module, &mut pt, &sk_out, scratch.borrow());
ct_glwe_dft.decrypt(&module, &mut pt, &sk_out_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(
&mut pt.data,

View File

@@ -0,0 +1,3 @@
pub mod automorphism_key;
pub mod gglwe;
pub mod tensor_key;

View File

@@ -1,7 +1,7 @@
use backend::{FFT64, Module, ScalarZnxDftOps, ScratchOwned, Stats, VecZnxOps};
use sampling::source::Source;
use crate::{FourierGLWECiphertext, GLWEPlaintext, GLWESecret, GetRow, Infos, TensorKey};
use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWEPlaintext, GLWESecret, GLWETensorKey, GetRow, Infos};
#[test]
fn encrypt_sk() {
@@ -17,25 +17,26 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, sigma: f64, rank: usize
let rows: usize = k / basek;
let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k, rows, 1, rank);
let mut tensor_key: GLWETensorKey<Vec<u8>, FFT64> = GLWETensorKey::alloc(&module, basek, k, rows, 1, rank);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned = ScratchOwned::new(TensorKey::generate_from_sk_scratch_space(
let mut scratch: ScratchOwned = ScratchOwned::new(GLWETensorKey::generate_from_sk_scratch_space(
&module,
basek,
tensor_key.k(),
rank,
));
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
tensor_key.generate_from_sk(
&module,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -45,25 +46,19 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, sigma: f64, rank: usize
let mut ct_glwe_fourier: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut sk_ij = GLWESecret::alloc(&module, 1);
let mut sk_ij_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::alloc(&module, 1);
let mut sk_ij: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, 1);
(0..rank).for_each(|i| {
(0..rank).for_each(|j| {
module.svp_apply(
&mut sk_ij.data_fourier,
0,
&sk.data_fourier,
i,
&sk.data_fourier,
j,
);
module.svp_idft(&mut sk_ij.data, 0, &sk_ij.data_fourier, 0, scratch.borrow());
module.svp_apply(&mut sk_ij_dft.data, 0, &sk_dft.data, i, &sk_dft.data, j);
module.scalar_znx_idft(&mut sk_ij.data, 0, &sk_ij_dft.data, 0, scratch.borrow());
(0..tensor_key.rank_in()).for_each(|col_i| {
(0..tensor_key.rows()).for_each(|row_i| {
tensor_key
.at(i, j)
.get_row(&module, row_i, col_i, &mut ct_glwe_fourier);
ct_glwe_fourier.decrypt(&module, &mut pt, &sk, scratch.borrow());
ct_glwe_fourier.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk_ij.data, col_i);
let std_pt: f64 = pt.data.std(0, basek) * (k as f64).exp2();
assert!((sigma - std_pt).abs() <= 0.5, "{} {}", sigma, std_pt);

View File

@@ -6,8 +6,8 @@ use backend::{
use sampling::source::Source;
use crate::{
AutomorphismKey, FourierGLWECiphertext, GLWECiphertext, GLWESecret, GLWESwitchingKey, GetRow, Infos, ScratchCore, SetRow,
TensorKey, div_ceil,
FourierGLWECiphertext, FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWESwitchingKey, GLWETensorKey, GetRow,
Infos, ScratchCore, SetRow, div_ceil,
};
pub struct GGSWCiphertext<C, B: Backend> {
@@ -246,7 +246,7 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
&mut self,
module: &Module<FFT64>,
pt: &ScalarZnx<DataPt>,
sk: &GLWESecret<DataSk, FFT64>,
sk: &FourierGLWESecret<DataSk, FFT64>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
@@ -304,7 +304,7 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
col_j: usize,
res: &mut R,
ci_dft: &VecZnxDft<DataCi, FFT64>,
tsk: &TensorKey<DataTsk, FFT64>,
tsk: &GLWETensorKey<DataTsk, FFT64>,
scratch: &mut Scratch,
) where
R: VecZnxToMut,
@@ -408,7 +408,7 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
module: &Module<FFT64>,
lhs: &GGSWCiphertext<DataLhs, FFT64>,
ksk: &GLWESwitchingKey<DataKsk, FFT64>,
tsk: &TensorKey<DataTsk, FFT64>,
tsk: &GLWETensorKey<DataTsk, FFT64>,
scratch: &mut Scratch,
) {
let rank: usize = self.rank();
@@ -449,7 +449,7 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
&mut self,
module: &Module<FFT64>,
ksk: &GLWESwitchingKey<DataKsk, FFT64>,
tsk: &TensorKey<DataTsk, FFT64>,
tsk: &GLWETensorKey<DataTsk, FFT64>,
scratch: &mut Scratch,
) {
unsafe {
@@ -462,8 +462,8 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
&mut self,
module: &Module<FFT64>,
lhs: &GGSWCiphertext<DataLhs, FFT64>,
auto_key: &AutomorphismKey<DataAk, FFT64>,
tensor_key: &TensorKey<DataTsk, FFT64>,
auto_key: &GLWEAutomorphismKey<DataAk, FFT64>,
tensor_key: &GLWETensorKey<DataTsk, FFT64>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
@@ -551,8 +551,8 @@ impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GGSWCiphertext<DataSelf, FFT64> {
pub fn automorphism_inplace<DataKsk: AsRef<[u8]>, DataTsk: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
auto_key: &AutomorphismKey<DataKsk, FFT64>,
tensor_key: &TensorKey<DataTsk, FFT64>,
auto_key: &GLWEAutomorphismKey<DataKsk, FFT64>,
tensor_key: &GLWETensorKey<DataTsk, FFT64>,
scratch: &mut Scratch,
) {
unsafe {

6
core/src/ggsw/mod.rs Normal file
View File

@@ -0,0 +1,6 @@
pub mod ciphertext;
pub use ciphertext::GGSWCiphertext;
#[cfg(test)]
mod test_fft64;

View File

@@ -5,9 +5,9 @@ use backend::{
use sampling::source::Source;
use crate::{
FourierGLWECiphertext, GGSWCiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, GetRow, Infos, TensorKey,
automorphism::AutomorphismKey,
test_fft64::{noise_ggsw_keyswitch, noise_ggsw_product},
FourierGLWECiphertext, FourierGLWESecret, GGSWCiphertext, GLWEAutomorphismKey, GLWEPlaintext, GLWESecret, GLWESwitchingKey,
GLWETensorKey, GetRow, Infos, div_ceil,
noise::{noise_ggsw_keyswitch, noise_ggsw_product},
};
#[test]
@@ -142,13 +142,14 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, digits: usize, rank: us
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
ct.encrypt_sk(
&module,
&pt_scalar,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -172,14 +173,14 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k: usize, digits: usize, rank: us
// mul with sk[col_j-1]
if col_j > 0 {
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft.data, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
}
ct.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
@@ -209,7 +210,7 @@ fn test_keyswitch(
let mut ct_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_in, rows, digits_in, rank);
let mut ct_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_out, rows, digits_in, rank);
let mut tsk: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut tsk: GLWETensorKey<Vec<u8>, FFT64> = GLWETensorKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
@@ -223,7 +224,7 @@ fn test_keyswitch(
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_in, rank)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out)
| GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank)
| TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
| GLWETensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
| GGSWCiphertext::keyswitch_scratch_space(
&module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank,
),
@@ -231,16 +232,18 @@ fn test_keyswitch(
let var_xs: f64 = 0.5;
let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk_in.fill_ternary_prob(&module, var_xs, &mut source_xs);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_in.fill_ternary_prob(var_xs, &mut source_xs);
let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in);
let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk_out.fill_ternary_prob(&module, var_xs, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_out.fill_ternary_prob(var_xs, &mut source_xs);
let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out);
ksk.generate_from_sk(
&module,
&sk_in,
&sk_out,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -248,7 +251,7 @@ fn test_keyswitch(
);
tsk.generate_from_sk(
&module,
&sk_out,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -260,7 +263,7 @@ fn test_keyswitch(
ct_in.encrypt_sk(
&module,
&pt_scalar,
&sk_in,
&sk_in_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -280,14 +283,14 @@ fn test_keyswitch(
// mul with sk[col_j-1]
if col_j > 0 {
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk_out.data_fourier, col_j - 1);
module.svp_apply_inplace(&mut pt_dft, 0, &sk_out_dft.data, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
}
ct_out.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_out, scratch.borrow());
ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
@@ -336,7 +339,7 @@ fn test_keyswitch_inplace(
let digits_in: usize = 1;
let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ct, rows, digits_in, rank);
let mut tsk: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank);
let mut tsk: GLWETensorKey<Vec<u8>, FFT64> = GLWETensorKey::alloc(&module, basek, k_tsk, rows, digits, rank);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
@@ -350,22 +353,24 @@ fn test_keyswitch_inplace(
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ct, rank)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ct)
| GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, k_ksk, rank)
| TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
| GLWETensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
| GGSWCiphertext::keyswitch_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank),
);
let var_xs: f64 = 0.5;
let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk_in.fill_ternary_prob(&module, var_xs, &mut source_xs);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_in.fill_ternary_prob(var_xs, &mut source_xs);
let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in);
let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk_out.fill_ternary_prob(&module, var_xs, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_out.fill_ternary_prob(var_xs, &mut source_xs);
let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out);
ksk.generate_from_sk(
&module,
&sk_in,
&sk_out,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -373,7 +378,7 @@ fn test_keyswitch_inplace(
);
tsk.generate_from_sk(
&module,
&sk_out,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -385,7 +390,7 @@ fn test_keyswitch_inplace(
ct.encrypt_sk(
&module,
&pt_scalar,
&sk_in,
&sk_in_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -411,14 +416,14 @@ fn test_keyswitch_inplace(
// mul with sk[col_j-1]
if col_j > 0 {
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk_out.data_fourier, col_j - 1);
module.svp_apply_inplace(&mut pt_dft, 0, &sk_out_dft.data, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
}
ct.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_out, scratch.borrow());
ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
@@ -471,8 +476,8 @@ fn test_automorphism(
let mut ct_in: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_in, rows_in, digits_in, rank);
let mut ct_out: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_out, rows_in, digits_in, rank);
let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank);
let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut tensor_key: GLWETensorKey<Vec<u8>, FFT64> = GLWETensorKey::alloc(&module, basek, k_tsk, rows, digits, rank);
let mut auto_key: GLWEAutomorphismKey<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
@@ -484,8 +489,8 @@ fn test_automorphism(
let mut scratch: ScratchOwned = ScratchOwned::new(
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_in, rank)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_out)
| AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
| TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
| GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
| GLWETensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
| GGSWCiphertext::automorphism_scratch_space(
&module, basek, k_out, k_in, k_ksk, digits, k_tsk, digits, rank,
),
@@ -493,8 +498,9 @@ fn test_automorphism(
let var_xs: f64 = 0.5;
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, var_xs, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(var_xs, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
auto_key.generate_from_sk(
&module,
@@ -507,7 +513,7 @@ fn test_automorphism(
);
tensor_key.generate_from_sk(
&module,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -519,7 +525,7 @@ fn test_automorphism(
ct_in.encrypt_sk(
&module,
&pt_scalar,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -541,14 +547,14 @@ fn test_automorphism(
// mul with sk[col_j-1]
if col_j > 0 {
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft.data, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
}
ct_out.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
@@ -596,8 +602,8 @@ fn test_automorphism_inplace(
let digits_in: usize = 1;
let mut ct: GGSWCiphertext<Vec<u8>, FFT64> = GGSWCiphertext::alloc(&module, basek, k_ct, rows_in, digits_in, rank);
let mut tensor_key: TensorKey<Vec<u8>, FFT64> = TensorKey::alloc(&module, basek, k_tsk, rows, digits, rank);
let mut auto_key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut tensor_key: GLWETensorKey<Vec<u8>, FFT64> = GLWETensorKey::alloc(&module, basek, k_tsk, rows, digits, rank);
let mut auto_key: GLWEAutomorphismKey<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_scalar: ScalarZnx<Vec<u8>> = module.new_scalar_znx(1);
@@ -609,15 +615,16 @@ fn test_automorphism_inplace(
let mut scratch: ScratchOwned = ScratchOwned::new(
GGSWCiphertext::encrypt_sk_scratch_space(&module, basek, k_ct, rank)
| FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ct)
| AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
| TensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
| GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
| GLWETensorKey::generate_from_sk_scratch_space(&module, basek, k_tsk, rank)
| GGSWCiphertext::automorphism_inplace_scratch_space(&module, basek, k_ct, k_ksk, digits, k_tsk, digits, rank),
);
let var_xs: f64 = 0.5;
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, var_xs, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(var_xs, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
auto_key.generate_from_sk(
&module,
@@ -630,7 +637,7 @@ fn test_automorphism_inplace(
);
tensor_key.generate_from_sk(
&module,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -642,7 +649,7 @@ fn test_automorphism_inplace(
ct.encrypt_sk(
&module,
&pt_scalar,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -664,14 +671,14 @@ fn test_automorphism_inplace(
// mul with sk[col_j-1]
if col_j > 0 {
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft.data, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
}
ct.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
ct_glwe_fourier.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
@@ -742,13 +749,14 @@ fn test_external_product(
| GGSWCiphertext::external_product_scratch_space(&module, basek, k_out, k_in, k_ggsw, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
ct_ggsw_rhs.encrypt_sk(
&module,
&pt_ggsw_rhs,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -758,7 +766,7 @@ fn test_external_product(
ct_ggsw_lhs_in.encrypt_sk(
&module,
&pt_ggsw_lhs,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -787,13 +795,13 @@ fn test_external_product(
if col_j > 0 {
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft.data, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
}
ct_ggsw_lhs_out.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
ct_glwe_fourier.decrypt(&module, &mut pt, &sk, scratch.borrow());
ct_glwe_fourier.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt.data, 0, &pt_want.data, 0);
@@ -862,13 +870,14 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw
| GGSWCiphertext::external_product_inplace_scratch_space(&module, basek, k_ct, k_ggsw, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
ct_ggsw_rhs.encrypt_sk(
&module,
&pt_ggsw_rhs,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -878,7 +887,7 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw
ct_ggsw_lhs.encrypt_sk(
&module,
&pt_ggsw_lhs,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -907,13 +916,13 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw
if col_j > 0 {
module.vec_znx_dft(1, 0, &mut pt_dft, 0, &pt_want.data, 0);
module.svp_apply_inplace(&mut pt_dft, 0, &sk.data_fourier, col_j - 1);
module.svp_apply_inplace(&mut pt_dft, 0, &sk_dft.data, col_j - 1);
module.vec_znx_idft_tmp_a(&mut pt_big, 0, &mut pt_dft, 0);
module.vec_znx_big_normalize(basek, &mut pt_want.data, 0, &pt_big, 0, scratch.borrow());
}
ct_ggsw_lhs.get_row(&module, row_i, col_j, &mut ct_glwe_fourier);
ct_glwe_fourier.decrypt(&module, &mut pt, &sk, scratch.borrow());
ct_glwe_fourier.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt.data, 0, &pt_want.data, 0);

View File

@@ -0,0 +1 @@
mod ggsw;

View File

@@ -1,6 +1,6 @@
use backend::{FFT64, Module, Scratch, VecZnxOps};
use crate::{AutomorphismKey, GLWECiphertext};
use crate::{GLWEAutomorphismKey, GLWECiphertext};
impl GLWECiphertext<Vec<u8>> {
pub fn automorphism_scratch_space(
@@ -32,7 +32,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
&mut self,
module: &Module<FFT64>,
lhs: &GLWECiphertext<DataLhs>,
rhs: &AutomorphismKey<DataRhs, FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
self.keyswitch(module, lhs, &rhs.key, scratch);
@@ -44,7 +44,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
pub fn automorphism_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &AutomorphismKey<DataRhs, FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
self.keyswitch_inplace(module, &rhs.key, scratch);
@@ -57,7 +57,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
&mut self,
module: &Module<FFT64>,
lhs: &GLWECiphertext<DataLhs>,
rhs: &AutomorphismKey<DataRhs, FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
Self::keyswitch_private::<_, _, 1>(self, rhs.p(), module, lhs, &rhs.key, scratch);
@@ -66,7 +66,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
pub fn automorphism_add_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &AutomorphismKey<DataRhs, FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
unsafe {
@@ -79,7 +79,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
&mut self,
module: &Module<FFT64>,
lhs: &GLWECiphertext<DataLhs>,
rhs: &AutomorphismKey<DataRhs, FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
Self::keyswitch_private::<_, _, 2>(self, rhs.p(), module, lhs, &rhs.key, scratch);
@@ -88,7 +88,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
pub fn automorphism_sub_ab_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &AutomorphismKey<DataRhs, FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
unsafe {
@@ -101,7 +101,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
&mut self,
module: &Module<FFT64>,
lhs: &GLWECiphertext<DataLhs>,
rhs: &AutomorphismKey<DataRhs, FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
Self::keyswitch_private::<_, _, 3>(self, rhs.p(), module, lhs, &rhs.key, scratch);
@@ -110,7 +110,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
pub fn automorphism_sub_ba_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &AutomorphismKey<DataRhs, FFT64>,
rhs: &GLWEAutomorphismKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
unsafe {

View File

@@ -1,6 +1,6 @@
use backend::{FFT64, Module, ScalarZnxDftOps, Scratch, VecZnxBigOps, VecZnxDftOps, ZnxZero};
use crate::{GLWECiphertext, GLWEPlaintext, GLWESecret, Infos};
use crate::{FourierGLWESecret, GLWECiphertext, GLWEPlaintext, Infos};
impl<DataSelf: AsRef<[u8]>> GLWECiphertext<DataSelf> {
pub fn clone(&self) -> GLWECiphertext<Vec<u8>> {
@@ -15,7 +15,7 @@ impl<DataSelf: AsRef<[u8]>> GLWECiphertext<DataSelf> {
&self,
module: &Module<FFT64>,
pt: &mut GLWEPlaintext<DataPt>,
sk: &GLWESecret<DataSk, FFT64>,
sk: &FourierGLWESecret<DataSk, FFT64>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
@@ -36,7 +36,7 @@ impl<DataSelf: AsRef<[u8]>> GLWECiphertext<DataSelf> {
// ci_dft = DFT(a[i]) * DFT(s[i])
let (mut ci_dft, _) = scratch_1.tmp_vec_znx_dft(module, 1, self.size()); // TODO optimize size when pt << ct
module.vec_znx_dft(1, 0, &mut ci_dft, 0, &self.data, i);
module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1);
module.svp_apply_inplace(&mut ci_dft, 0, &sk.data, i - 1);
let ci_big = module.vec_znx_idft_consume(ci_dft);
// c0_big += a[i] * s[i]

View File

@@ -4,7 +4,7 @@ use backend::{
};
use sampling::source::Source;
use crate::{GLWECiphertext, GLWEPlaintext, GLWEPublicKey, GLWESecret, Infos, SIX_SIGMA, div_ceil, keys::SecretDistribution};
use crate::{FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWEPublicKey, Infos, SIX_SIGMA, dist::Distribution, div_ceil};
impl GLWECiphertext<Vec<u8>> {
pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize) -> usize {
@@ -24,7 +24,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
&mut self,
module: &Module<FFT64>,
pt: &GLWEPlaintext<DataPt>,
sk: &GLWESecret<DataSk, FFT64>,
sk: &FourierGLWESecret<DataSk, FFT64>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
@@ -44,7 +44,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
pub fn encrypt_zero_sk<DataSk: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
sk: &GLWESecret<DataSk, FFT64>,
sk: &FourierGLWESecret<DataSk, FFT64>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
@@ -106,7 +106,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
&mut self,
module: &Module<FFT64>,
pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
sk: &GLWESecret<DataSk, FFT64>,
sk: &FourierGLWESecret<DataSk, FFT64>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
@@ -148,7 +148,7 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
// c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i])))
module.vec_znx_dft(1, 0, &mut ci_dft, 0, &self.data, i);
module.svp_apply_inplace(&mut ci_dft, 0, &sk.data_fourier, i - 1);
module.svp_apply_inplace(&mut ci_dft, 0, &sk.data, i - 1);
let ci_big: VecZnxBig<&mut [u8], FFT64> = module.vec_znx_idft_consume(ci_dft);
// use c[0] as buffer, which is overwritten later by the normalization step
@@ -213,16 +213,16 @@ impl<DataSelf: AsRef<[u8]> + AsMut<[u8]>> GLWECiphertext<DataSelf> {
{
let (mut u, _) = scratch_1.tmp_scalar_znx(module, 1);
match pk.dist {
SecretDistribution::NONE => panic!(
Distribution::NONE => panic!(
"invalid public key: SecretDistribution::NONE, ensure it has been correctly intialized through \
Self::generate"
),
SecretDistribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu),
SecretDistribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu),
SecretDistribution::BinaryFixed(hw) => u.fill_binary_hw(0, hw, source_xu),
SecretDistribution::BinaryProb(prob) => u.fill_binary_prob(0, prob, source_xu),
SecretDistribution::BinaryBlock(block_size) => u.fill_binary_block(0, block_size, source_xu),
SecretDistribution::ZERO => {}
Distribution::TernaryFixed(hw) => u.fill_ternary_hw(0, hw, source_xu),
Distribution::TernaryProb(prob) => u.fill_ternary_prob(0, prob, source_xu),
Distribution::BinaryFixed(hw) => u.fill_binary_hw(0, hw, source_xu),
Distribution::BinaryProb(prob) => u.fill_binary_prob(0, prob, source_xu),
Distribution::BinaryBlock(block_size) => u.fill_binary_block(0, block_size, source_xu),
Distribution::ZERO => {}
}
module.svp_prepare(&mut u_dft, 0, &u, 0);

View File

@@ -11,24 +11,13 @@ pub mod public_key;
pub mod secret;
pub mod trace;
#[allow(unused_imports)]
pub use automorphism::*;
pub use ciphertext::*;
#[allow(unused_imports)]
pub use decryption::*;
#[allow(unused_imports)]
pub use encryption::*;
#[allow(unused_imports)]
pub use external_product::*;
#[allow(unused_imports)]
pub use keyswitch::*;
pub use ops::*;
pub use packing::*;
pub use plaintext::*;
pub use public_key::*;
pub use secret::*;
#[allow(unused_imports)]
pub use trace::*;
pub use ciphertext::GLWECiphertext;
pub(crate) use ciphertext::{GLWECiphertextToMut, GLWECiphertextToRef};
pub use ops::GLWEOps;
pub use packing::GLWEPacker;
pub use plaintext::GLWEPlaintext;
pub use public_key::GLWEPublicKey;
pub use secret::GLWESecret;
#[cfg(test)]
mod test_fft64;

View File

@@ -1,4 +1,4 @@
use crate::{AutomorphismKey, GLWECiphertext, GLWEOps, Infos, ScratchCore};
use crate::{GLWEAutomorphismKey, GLWECiphertext, GLWEOps, Infos, ScratchCore};
use std::collections::HashMap;
use backend::{FFT64, Module, Scratch};
@@ -7,7 +7,7 @@ use backend::{FFT64, Module, Scratch};
/// with constant memory of Log(N) ciphertexts.
/// Main difference with usual GLWE packing is that
/// the output is bit-reversed.
pub struct StreamPacker {
pub struct GLWEPacker {
accumulators: Vec<Accumulator>,
log_batch: usize,
counter: usize,
@@ -39,7 +39,7 @@ impl Accumulator {
}
}
impl StreamPacker {
impl GLWEPacker {
/// Instantiates a new [StreamPacker].
///
/// #Arguments
@@ -98,7 +98,7 @@ impl StreamPacker {
module: &Module<FFT64>,
res: &mut Vec<GLWECiphertext<Vec<u8>>>,
a: Option<&GLWECiphertext<DataA>>,
auto_keys: &HashMap<i64, AutomorphismKey<DataAK, FFT64>>,
auto_keys: &HashMap<i64, GLWEAutomorphismKey<DataAK, FFT64>>,
scratch: &mut Scratch,
) {
pack_core(
@@ -125,7 +125,7 @@ impl StreamPacker {
&mut self,
module: &Module<FFT64>,
res: &mut Vec<GLWECiphertext<Vec<u8>>>,
auto_keys: &HashMap<i64, AutomorphismKey<DataAK, FFT64>>,
auto_keys: &HashMap<i64, GLWEAutomorphismKey<DataAK, FFT64>>,
scratch: &mut Scratch,
) {
if self.counter != 0 {
@@ -151,7 +151,7 @@ fn pack_core<D: AsRef<[u8]>, DataAK: AsRef<[u8]>>(
a: Option<&GLWECiphertext<D>>,
accumulators: &mut [Accumulator],
i: usize,
auto_keys: &HashMap<i64, AutomorphismKey<DataAK, FFT64>>,
auto_keys: &HashMap<i64, GLWEAutomorphismKey<DataAK, FFT64>>,
scratch: &mut Scratch,
) {
let log_n: usize = module.log_n();
@@ -215,7 +215,7 @@ fn combine<D: AsRef<[u8]>, DataAK: AsRef<[u8]>>(
acc: &mut Accumulator,
b: Option<&GLWECiphertext<D>>,
i: usize,
auto_keys: &HashMap<i64, AutomorphismKey<DataAK, FFT64>>,
auto_keys: &HashMap<i64, GLWEAutomorphismKey<DataAK, FFT64>>,
scratch: &mut Scratch,
) {
let log_n: usize = module.log_n();

View File

@@ -1,10 +1,6 @@
use backend::{Backend, FFT64, Module, VecZnx, VecZnxAlloc, VecZnxToMut, VecZnxToRef};
use crate::{
GLWEOps, Infos, SetMetaData,
ciphertext::{GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef},
div_ceil,
};
use crate::{GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef, GLWEOps, Infos, SetMetaData, div_ceil};
pub struct GLWEPlaintext<C> {
pub data: VecZnx<C>,

View File

@@ -1,18 +1,18 @@
use backend::{Backend, FFT64, Module, ScratchOwned, VecZnxDft};
use sampling::source::Source;
use crate::{FourierGLWECiphertext, GLWESecret, Infos, keys::SecretDistribution};
use crate::{FourierGLWECiphertext, FourierGLWESecret, Infos, dist::Distribution};
pub struct GLWEPublicKey<D, B: Backend> {
pub(crate) data: FourierGLWECiphertext<D, B>,
pub(crate) dist: SecretDistribution,
pub(crate) dist: Distribution,
}
impl<B: Backend> GLWEPublicKey<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self {
Self {
data: FourierGLWECiphertext::alloc(module, basek, k, rank),
dist: SecretDistribution::NONE,
dist: Distribution::NONE,
}
}
@@ -47,7 +47,7 @@ impl<C: AsRef<[u8]> + AsMut<[u8]>> GLWEPublicKey<C, FFT64> {
pub fn generate_from_sk<S: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
sk: &GLWESecret<S, FFT64>,
sk: &FourierGLWESecret<S, FFT64>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
@@ -55,7 +55,7 @@ impl<C: AsRef<[u8]> + AsMut<[u8]>> GLWEPublicKey<C, FFT64> {
#[cfg(debug_assertions)]
{
match sk.dist {
SecretDistribution::NONE => panic!("invalid sk: SecretDistribution::NONE"),
Distribution::NONE => panic!("invalid sk: SecretDistribution::NONE"),
_ => {}
}
}

View File

@@ -1,31 +1,27 @@
use backend::{
Backend, FFT64, Module, ScalarZnx, ScalarZnxAlloc, ScalarZnxDft, ScalarZnxDftAlloc, ScalarZnxDftOps, ZnxInfos, ZnxZero,
};
use backend::{Backend, Module, ScalarZnx, ScalarZnxAlloc, ZnxInfos, ZnxZero};
use sampling::source::Source;
use crate::keys::SecretDistribution;
use crate::dist::Distribution;
pub struct GLWESecret<T, B: Backend> {
pub struct GLWESecret<T> {
pub(crate) data: ScalarZnx<T>,
pub(crate) data_fourier: ScalarZnxDft<T, B>,
pub(crate) dist: SecretDistribution,
pub(crate) dist: Distribution,
}
impl<B: Backend> GLWESecret<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, rank: usize) -> Self {
impl GLWESecret<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, rank: usize) -> Self {
Self {
data: module.new_scalar_znx(rank),
data_fourier: module.new_scalar_znx_dft(rank),
dist: SecretDistribution::NONE,
dist: Distribution::NONE,
}
}
pub fn bytes_of(module: &Module<B>, rank: usize) -> usize {
module.bytes_of_scalar_znx(rank) + module.bytes_of_scalar_znx_dft(rank)
pub fn bytes_of<B: Backend>(module: &Module<B>, rank: usize) -> usize {
module.bytes_of_scalar_znx(rank)
}
}
impl<DataSelf, B: Backend> GLWESecret<DataSelf, B> {
impl<DataSelf> GLWESecret<DataSelf> {
pub fn n(&self) -> usize {
self.data.n()
}
@@ -39,55 +35,50 @@ impl<DataSelf, B: Backend> GLWESecret<DataSelf, B> {
}
}
impl<S: AsMut<[u8]> + AsRef<[u8]>> GLWESecret<S, FFT64> {
pub fn fill_ternary_prob(&mut self, module: &Module<FFT64>, prob: f64, source: &mut Source) {
impl<S: AsMut<[u8]> + AsRef<[u8]>> GLWESecret<S> {
pub fn fill_ternary_prob(&mut self, prob: f64, source: &mut Source) {
(0..self.rank()).for_each(|i| {
self.data.fill_ternary_prob(i, prob, source);
});
self.prep_fourier(module);
self.dist = SecretDistribution::TernaryProb(prob);
self.dist = Distribution::TernaryProb(prob);
}
pub fn fill_ternary_hw(&mut self, module: &Module<FFT64>, hw: usize, source: &mut Source) {
pub fn fill_ternary_hw(&mut self, hw: usize, source: &mut Source) {
(0..self.rank()).for_each(|i| {
self.data.fill_ternary_hw(i, hw, source);
});
self.prep_fourier(module);
self.dist = SecretDistribution::TernaryFixed(hw);
self.dist = Distribution::TernaryFixed(hw);
}
pub fn fill_binary_prob(&mut self, module: &Module<FFT64>, prob: f64, source: &mut Source) {
pub fn fill_binary_prob(&mut self, prob: f64, source: &mut Source) {
(0..self.rank()).for_each(|i| {
self.data.fill_binary_prob(i, prob, source);
});
self.prep_fourier(module);
self.dist = SecretDistribution::BinaryProb(prob);
self.dist = Distribution::BinaryProb(prob);
}
pub fn fill_binary_hw(&mut self, module: &Module<FFT64>, hw: usize, source: &mut Source) {
pub fn fill_binary_hw(&mut self, hw: usize, source: &mut Source) {
(0..self.rank()).for_each(|i| {
self.data.fill_binary_hw(i, hw, source);
});
self.prep_fourier(module);
self.dist = SecretDistribution::BinaryFixed(hw);
self.dist = Distribution::BinaryFixed(hw);
}
pub fn fill_binary_block(&mut self, module: &Module<FFT64>, block_size: usize, source: &mut Source) {
pub fn fill_binary_block(&mut self, block_size: usize, source: &mut Source) {
(0..self.rank()).for_each(|i| {
self.data.fill_binary_block(i, block_size, source);
});
self.prep_fourier(module);
self.dist = SecretDistribution::BinaryBlock(block_size);
self.dist = Distribution::BinaryBlock(block_size);
}
pub fn fill_zero(&mut self) {
self.data.zero();
self.dist = SecretDistribution::ZERO;
self.dist = Distribution::ZERO;
}
pub(crate) fn prep_fourier(&mut self, module: &Module<FFT64>) {
(0..self.rank()).for_each(|i| {
module.svp_prepare(&mut self.data_fourier, i, &self.data, i);
});
}
// pub(crate) fn prep_fourier(&mut self, module: &Module<FFT64>) {
// (0..self.rank()).for_each(|i| {
// module.svp_prepare(&mut self.data_fourier, i, &self.data, i);
// });
// }
}

View File

@@ -1 +1,224 @@
use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps};
use sampling::source::Source;
use crate::{
FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, div_ceil,
noise::log2_std_noise_gglwe_product,
};
#[test]
fn apply_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!("test automorphism_inplace digits: {} rank: {}", di, rank);
test_automorphism_inplace(log_n, basek, -5, k_ct, k_ksk, di, rank, 3.2);
});
});
}
#[test]
fn apply() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
let k_out: usize = k_ksk; // Better capture noise.
println!("test automorphism digits: {} rank: {}", di, rank);
test_automorphism(log_n, basek, -5, k_out, k_in, k_ksk, di, rank, 3.2);
})
});
}
fn test_automorphism(
log_n: usize,
basek: usize,
p: i64,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = div_ceil(k_in, basek * digits);
let mut autokey: GLWEAutomorphismKey<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, autokey.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k())
| GLWECiphertext::automorphism_scratch_space(
&module,
basek,
ct_out.k(),
ct_in.k(),
autokey.k(),
digits,
rank,
),
);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
autokey.generate_from_sk(
&module,
p,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_in.encrypt_sk(
&module,
&pt_want,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_out.automorphism(&module, &ct_in, &autokey, scratch.borrow());
ct_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
module.vec_znx_automorphism_inplace(p, &mut pt_want.data, 0);
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
module.vec_znx_normalize_inplace(basek, &mut pt_have.data, 0, scratch.borrow());
let noise_have: f64 = pt_have.data.std(0, basek).log2();
println!("{}", noise_have);
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
k_in,
k_ksk,
);
assert!(
noise_have <= noise_want + 1.0,
"{} {}",
noise_have,
noise_want
);
}
fn test_automorphism_inplace(
log_n: usize,
basek: usize,
p: i64,
k_ct: usize,
k_ksk: usize,
digits: usize,
rank: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = div_ceil(k_ct, basek * digits);
let mut autokey: GLWEAutomorphismKey<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, autokey.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
| GLWECiphertext::automorphism_inplace_scratch_space(&module, basek, ct.k(), autokey.k(), digits, rank),
);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
autokey.generate_from_sk(
&module,
p,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct.encrypt_sk(
&module,
&pt_want,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct.automorphism_inplace(&module, &autokey, scratch.borrow());
ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
module.vec_znx_automorphism_inplace(p, &mut pt_want.data, 0);
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
module.vec_znx_normalize_inplace(basek, &mut pt_have.data, 0, scratch.borrow());
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
k_ct,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}

View File

@@ -2,7 +2,7 @@ use backend::{Decoding, Encoding, FFT64, Module, ScratchOwned, Stats, VecZnxOps,
use itertools::izip;
use sampling::source::Source;
use crate::{FourierGLWECiphertext, GLWECiphertext, GLWEPlaintext, GLWEPublicKey, GLWESecret, Infos};
use crate::{FourierGLWECiphertext, FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWEPublicKey, GLWESecret, Infos};
#[test]
fn encrypt_sk() {
@@ -46,8 +46,9 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma:
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
let mut data_want: Vec<i64> = vec![0i64; module.n()];
@@ -60,7 +61,7 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma:
ct.encrypt_sk(
&module,
&pt,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -69,7 +70,7 @@ fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma:
pt.data.zero();
ct.decrypt(&module, &mut pt, &sk, scratch.borrow());
ct.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
let mut data_have: Vec<i64> = vec![0i64; module.n()];
@@ -98,8 +99,9 @@ fn test_encrypt_zero_sk(log_n: usize, basek: usize, k_ct: usize, sigma: f64, ran
let mut source_xe: Source = Source::new([1u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
let mut ct_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank);
@@ -110,13 +112,13 @@ fn test_encrypt_zero_sk(log_n: usize, basek: usize, k_ct: usize, sigma: f64, ran
ct_dft.encrypt_zero_sk(
&module,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_dft.decrypt(&module, &mut pt, &sk, scratch.borrow());
ct_dft.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
assert!((sigma - pt.data.std(0, basek) * (k_ct as f64).exp2()) <= 0.2);
}
@@ -132,11 +134,12 @@ fn test_encrypt_pk(log_n: usize, basek: usize, k_ct: usize, k_pk: usize, sigma:
let mut source_xa: Source = Source::new([0u8; 32]);
let mut source_xu: Source = Source::new([0u8; 32]);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
let mut pk: GLWEPublicKey<Vec<u8>, FFT64> = GLWEPublicKey::alloc(&module, basek, k_pk, rank);
pk.generate_from_sk(&module, &sk, &mut source_xa, &mut source_xe, sigma);
pk.generate_from_sk(&module, &sk_dft, &mut source_xa, &mut source_xe, sigma);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
@@ -164,7 +167,7 @@ fn test_encrypt_pk(log_n: usize, basek: usize, k_ct: usize, k_pk: usize, sigma:
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
ct.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_want.data, 0, &pt_have.data, 0);

View File

@@ -1,86 +1,16 @@
use backend::{
Decoding, Encoding, FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut,
ZnxZero,
};
use itertools::izip;
use backend::{FFT64, FillUniform, Module, ScalarZnx, ScalarZnxAlloc, ScratchOwned, Stats, VecZnxOps, ZnxViewMut};
use sampling::source::Source;
use crate::{
FourierGLWECiphertext, GGSWCiphertext, GLWECiphertext, GLWEPlaintext, GLWEPublicKey, GLWESecret, Infos,
automorphism::AutomorphismKey,
keyswitch_key::GLWESwitchingKey,
test_fft64::{log2_std_noise_gglwe_product, noise_ggsw_product},
FourierGLWESecret, GGSWCiphertext, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, div_ceil, noise::noise_ggsw_product,
};
#[test]
fn encrypt_sk() {
let log_n: usize = 8;
(1..4).for_each(|rank| {
println!("test encrypt_sk rank: {}", rank);
test_encrypt_sk(log_n, 8, 54, 30, 3.2, rank);
});
}
#[test]
fn encrypt_zero_sk() {
let log_n: usize = 8;
(1..4).for_each(|rank| {
println!("test encrypt_zero_sk rank: {}", rank);
test_encrypt_zero_sk(log_n, 8, 64, 3.2, rank);
});
}
#[test]
fn encrypt_pk() {
let log_n: usize = 8;
(1..4).for_each(|rank| {
println!("test encrypt_pk rank: {}", rank);
test_encrypt_pk(log_n, 8, 64, 64, 3.2, rank)
});
}
#[test]
fn keyswitch() {
fn apply() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 45;
let digits: usize = k_in.div_ceil(basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
let k_out: usize = k_ksk; // better capture noise
println!(
"test keyswitch digits: {} rank_in: {} rank_out: {}",
di, rank_in, rank_out
);
test_keyswitch(log_n, basek, k_out, k_in, k_ksk, di, rank_in, rank_out, 3.2);
})
});
});
}
#[test]
fn keyswitch_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 45;
let digits: usize = k_ct.div_ceil(basek);
(1..4).for_each(|rank| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!("test keyswitch_inplace digits: {} rank: {}", di, rank);
test_keyswitch_inplace(log_n, basek, k_ct, k_ksk, di, rank, 3.2);
});
});
}
#[test]
fn external_product() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 45;
let digits: usize = k_in.div_ceil(basek);
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank| {
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_in + basek * di;
@@ -92,7 +22,7 @@ fn external_product() {
}
#[test]
fn external_product_inplace() {
fn apply_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 60;
@@ -106,548 +36,6 @@ fn external_product_inplace() {
});
}
#[test]
fn automorphism_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = k_ct.div_ceil(basek);
(1..4).for_each(|rank| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!("test automorphism_inplace digits: {} rank: {}", di, rank);
test_automorphism_inplace(log_n, basek, -5, k_ct, k_ksk, di, rank, 3.2);
});
});
}
#[test]
fn automorphism() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = k_in.div_ceil(basek);
(1..4).for_each(|rank| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
let k_out: usize = k_ksk; // Better capture noise.
println!("test automorphism digits: {} rank: {}", di, rank);
test_automorphism(log_n, basek, -5, k_out, k_in, k_ksk, di, rank, 3.2);
})
});
}
fn test_encrypt_sk(log_n: usize, basek: usize, k_ct: usize, k_pt: usize, sigma: f64, rank: usize) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_pt);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k()),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut data_want: Vec<i64> = vec![0i64; module.n()];
data_want
.iter_mut()
.for_each(|x| *x = source_xa.next_i64() & 0xFF);
pt.data.encode_vec_i64(0, basek, k_pt, &data_want, 10);
ct.encrypt_sk(
&module,
&pt,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
pt.data.zero();
ct.decrypt(&module, &mut pt, &sk, scratch.borrow());
let mut data_have: Vec<i64> = vec![0i64; module.n()];
pt.data
.decode_vec_i64(0, basek, pt.size() * basek, &mut data_have);
// TODO: properly assert the decryption noise through std(dec(ct) - pt)
let scale: f64 = (1 << (pt.size() * basek - k_pt)) as f64;
izip!(data_want.iter(), data_have.iter()).for_each(|(a, b)| {
let b_scaled = (*b as f64) / scale;
assert!(
(*a as f64 - b_scaled).abs() < 0.1,
"{} {}",
*a as f64,
b_scaled
)
});
}
fn test_encrypt_zero_sk(log_n: usize, basek: usize, k_ct: usize, sigma: f64, rank: usize) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([1u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut ct_dft: FourierGLWECiphertext<Vec<u8>, FFT64> = FourierGLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut scratch: ScratchOwned = ScratchOwned::new(
FourierGLWECiphertext::decrypt_scratch_space(&module, basek, k_ct)
| FourierGLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_ct, rank),
);
ct_dft.encrypt_zero_sk(
&module,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_dft.decrypt(&module, &mut pt, &sk, scratch.borrow());
assert!((sigma - pt.data.std(0, basek) * (k_ct as f64).exp2()) <= 0.2);
}
fn test_encrypt_pk(log_n: usize, basek: usize, k_ct: usize, k_pk: usize, sigma: f64, rank: usize) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
let mut source_xu: Source = Source::new([0u8; 32]);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut pk: GLWEPublicKey<Vec<u8>, FFT64> = GLWEPublicKey::alloc(&module, basek, k_pk, rank);
pk.generate_from_sk(&module, &sk, &mut source_xa, &mut source_xe, sigma);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k())
| GLWECiphertext::encrypt_pk_scratch_space(&module, basek, pk.k()),
);
let mut data_want: Vec<i64> = vec![0i64; module.n()];
data_want
.iter_mut()
.for_each(|x| *x = source_xa.next_i64() & 0);
pt_want.data.encode_vec_i64(0, basek, k_ct, &data_want, 10);
ct.encrypt_pk(
&module,
&pt_want,
&pk,
&mut source_xu,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
ct.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_want.data, 0, &pt_have.data, 0);
let noise_have: f64 = pt_want.data.std(0, basek).log2();
let noise_want: f64 = ((((rank as f64) + 1.0) * module.n() as f64 * 0.5 * sigma * sigma).sqrt()).log2() - (k_ct as f64);
assert!(
(noise_have - noise_want).abs() < 0.2,
"{} {}",
noise_have,
noise_want
);
}
fn test_keyswitch(
log_n: usize,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = k_in.div_ceil(basek * digits);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank_in);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank_out);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank_out)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k())
| GLWECiphertext::keyswitch_scratch_space(
&module,
basek,
ct_out.k(),
ct_in.k(),
ksk.k(),
digits,
rank_in,
rank_out,
),
);
let mut sk_in: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk_out: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(&module, 0.5, &mut source_xs);
ksk.generate_from_sk(
&module,
&sk_in,
&sk_out,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_in.encrypt_sk(
&module,
&pt_want,
&sk_in,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_out.keyswitch(&module, &ct_in, &ksk, scratch.borrow());
ct_out.decrypt(&module, &mut pt_have, &sk_out, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank_in as f64,
k_in,
k_ksk,
);
println!("{} vs. {}", noise_have, noise_want);
assert!(
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}
fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = k_ct.div_ceil(basek * digits);
let mut ct_grlwe: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ct_grlwe.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k())
| GLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_grlwe.k(), digits, rank),
);
let mut sk0: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk0.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk1: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk1.fill_ternary_prob(&module, 0.5, &mut source_xs);
ct_grlwe.generate_from_sk(
&module,
&sk0,
&sk1,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_glwe.encrypt_sk(
&module,
&pt_want,
&sk0,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_glwe.keyswitch_inplace(&module, &ct_grlwe, scratch.borrow());
ct_glwe.decrypt(&module, &mut pt_have, &sk1, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
k_ct,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}
fn test_automorphism(
log_n: usize,
basek: usize,
p: i64,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = k_in.div_ceil(basek * digits);
let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
let mut scratch: ScratchOwned = ScratchOwned::new(
AutomorphismKey::generate_from_sk_scratch_space(&module, basek, autokey.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k())
| GLWECiphertext::automorphism_scratch_space(
&module,
basek,
ct_out.k(),
ct_in.k(),
autokey.k(),
digits,
rank,
),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
autokey.generate_from_sk(
&module,
p,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_in.encrypt_sk(
&module,
&pt_want,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_out.automorphism(&module, &ct_in, &autokey, scratch.borrow());
ct_out.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
module.vec_znx_automorphism_inplace(p, &mut pt_want.data, 0);
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
module.vec_znx_normalize_inplace(basek, &mut pt_have.data, 0, scratch.borrow());
let noise_have: f64 = pt_have.data.std(0, basek).log2();
println!("{}", noise_have);
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
k_in,
k_ksk,
);
assert!(
noise_have <= noise_want + 1.0,
"{} {}",
noise_have,
noise_want
);
}
fn test_automorphism_inplace(
log_n: usize,
basek: usize,
p: i64,
k_ct: usize,
k_ksk: usize,
digits: usize,
rank: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = k_ct.div_ceil(basek * digits);
let mut autokey: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
let mut scratch: ScratchOwned = ScratchOwned::new(
AutomorphismKey::generate_from_sk_scratch_space(&module, basek, autokey.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
| GLWECiphertext::automorphism_inplace_scratch_space(&module, basek, ct.k(), autokey.k(), digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
autokey.generate_from_sk(
&module,
p,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct.encrypt_sk(
&module,
&pt_want,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct.automorphism_inplace(&module, &autokey, scratch.borrow());
ct.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
module.vec_znx_automorphism_inplace(p, &mut pt_want.data, 0);
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
module.vec_znx_normalize_inplace(basek, &mut pt_have.data, 0, scratch.borrow());
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
k_ct,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}
fn test_external_product(
log_n: usize,
basek: usize,
@@ -699,13 +87,14 @@ fn test_external_product(
),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
ct_ggsw.encrypt_sk(
&module,
&pt_rgsw,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -715,7 +104,7 @@ fn test_external_product(
ct_glwe_in.encrypt_sk(
&module,
&pt_want,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -724,7 +113,7 @@ fn test_external_product(
ct_glwe_out.external_product(&module, &ct_glwe_in, &ct_ggsw, scratch.borrow());
ct_glwe_out.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
ct_glwe_out.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
module.vec_znx_rotate_inplace(k as i64, &mut pt_want.data, 0);
@@ -793,13 +182,14 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw
| GLWECiphertext::external_product_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_ggsw.k(), digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
ct_ggsw.encrypt_sk(
&module,
&pt_rgsw,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -809,7 +199,7 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw
ct_glwe.encrypt_sk(
&module,
&pt_want,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -818,7 +208,7 @@ fn test_external_product_inplace(log_n: usize, basek: usize, k_ct: usize, k_ggsw
ct_glwe.external_product_inplace(&module, &ct_ggsw, scratch.borrow());
ct_glwe.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
ct_glwe.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
module.vec_znx_rotate_inplace(k as i64, &mut pt_want.data, 0);

View File

@@ -1 +1,227 @@
use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps};
use sampling::source::Source;
use crate::{
FourierGLWESecret, GLWECiphertext, GLWEPlaintext, GLWESecret, GLWESwitchingKey, Infos, div_ceil,
noise::log2_std_noise_gglwe_product,
};
#[test]
fn apply() {
let log_n: usize = 8;
let basek: usize = 12;
let k_in: usize = 45;
let digits: usize = div_ceil(k_in, basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
let k_out: usize = k_ksk; // better capture noise
println!(
"test keyswitch digits: {} rank_in: {} rank_out: {}",
di, rank_in, rank_out
);
test_keyswitch(log_n, basek, k_out, k_in, k_ksk, di, rank_in, rank_out, 3.2);
})
});
});
}
#[test]
fn apply_inplace() {
let log_n: usize = 8;
let basek: usize = 12;
let k_ct: usize = 45;
let digits: usize = div_ceil(k_ct, basek);
(1..4).for_each(|rank| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!("test keyswitch_inplace digits: {} rank: {}", di, rank);
test_keyswitch_inplace(log_n, basek, k_ct, k_ksk, di, rank, 3.2);
});
});
}
fn test_keyswitch(
log_n: usize,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = div_ceil(k_in, basek * digits);
let mut ksk: GLWESwitchingKey<Vec<u8>, FFT64> =
GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank_in, rank_out);
let mut ct_in: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_in, rank_in);
let mut ct_out: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_out, rank_out);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ksk.k(), rank_out)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_out.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_in.k())
| GLWECiphertext::keyswitch_scratch_space(
&module,
basek,
ct_out.k(),
ct_in.k(),
ksk.k(),
digits,
rank_in,
rank_out,
),
);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_in);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank_out);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out);
ksk.generate_from_sk(
&module,
&sk_in,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_in.encrypt_sk(
&module,
&pt_want,
&sk_in_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_out.keyswitch(&module, &ct_in, &ksk, scratch.borrow());
ct_out.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank_in as f64,
k_in,
k_ksk,
);
println!("{} vs. {}", noise_have, noise_want);
assert!(
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}
fn test_keyswitch_inplace(log_n: usize, basek: usize, k_ct: usize, k_ksk: usize, digits: usize, rank: usize, sigma: f64) {
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let rows: usize = div_ceil(k_ct, basek * digits);
let mut ct_grlwe: GLWESwitchingKey<Vec<u8>, FFT64> = GLWESwitchingKey::alloc(&module, basek, k_ksk, rows, digits, rank, rank);
let mut ct_glwe: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut pt_have: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut source_xa: Source = Source::new([0u8; 32]);
pt_want
.data
.fill_uniform(basek, 0, pt_want.size(), &mut source_xa);
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWESwitchingKey::encrypt_sk_scratch_space(&module, basek, ct_grlwe.k(), rank)
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct_glwe.k())
| GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct_glwe.k())
| GLWECiphertext::keyswitch_inplace_scratch_space(&module, basek, ct_glwe.k(), ct_grlwe.k(), digits, rank),
);
let mut sk_in: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_in.fill_ternary_prob(0.5, &mut source_xs);
let sk_in_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_in);
let mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk_out);
ct_grlwe.generate_from_sk(
&module,
&sk_in,
&sk_out_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_glwe.encrypt_sk(
&module,
&pt_want,
&sk_in_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
ct_glwe.keyswitch_inplace(&module, &ct_grlwe, scratch.borrow());
ct_glwe.decrypt(&module, &mut pt_have, &sk_out_dft, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_have.data, 0, &pt_want.data, 0);
let noise_have: f64 = pt_have.data.std(0, basek).log2();
let noise_want: f64 = log2_std_noise_gglwe_product(
module.n() as f64,
basek * digits,
0.5,
0.5,
0f64,
sigma * sigma,
0f64,
rank as f64,
k_ct,
k_ksk,
);
assert!(
(noise_have - noise_want).abs() <= 0.5,
"{} {}",
noise_have,
noise_want
);
}

View File

@@ -1,11 +1,11 @@
use crate::{AutomorphismKey, GLWECiphertext, GLWEOps, GLWEPlaintext, GLWESecret, StreamPacker};
use crate::{FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEOps, GLWEPacker, GLWEPlaintext, GLWESecret, div_ceil};
use std::collections::HashMap;
use backend::{Encoding, FFT64, Module, ScratchOwned, Stats};
use sampling::source::Source;
#[test]
fn packing() {
fn apply() {
let log_n: usize = 5;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
@@ -26,12 +26,13 @@ fn packing() {
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWECiphertext::encrypt_sk_scratch_space(&module, basek, k_ct)
| GLWECiphertext::decrypt_scratch_space(&module, basek, k_ct)
| AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
| StreamPacker::scratch_space(&module, basek, k_ct, k_ksk, digits, rank),
| GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_ksk, rank)
| GLWEPacker::scratch_space(&module, basek, k_ct, k_ksk, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_ct);
let mut data: Vec<i64> = vec![0i64; module.n()];
@@ -40,11 +41,11 @@ fn packing() {
});
pt.data.encode_vec_i64(0, basek, pt_k, &data, 32);
let gal_els: Vec<i64> = StreamPacker::galois_elements(&module);
let gal_els: Vec<i64> = GLWEPacker::galois_elements(&module);
let mut auto_keys: HashMap<i64, AutomorphismKey<Vec<u8>, FFT64>> = HashMap::new();
let mut auto_keys: HashMap<i64, GLWEAutomorphismKey<Vec<u8>, FFT64>> = HashMap::new();
gal_els.iter().for_each(|gal_el| {
let mut key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
let mut key: GLWEAutomorphismKey<Vec<u8>, FFT64> = GLWEAutomorphismKey::alloc(&module, basek, k_ksk, rows, digits, rank);
key.generate_from_sk(
&module,
*gal_el,
@@ -59,14 +60,14 @@ fn packing() {
let log_batch: usize = 0;
let mut packer: StreamPacker = StreamPacker::new(&module, log_batch, basek, k_ct, rank);
let mut packer: GLWEPacker = GLWEPacker::new(&module, log_batch, basek, k_ct, rank);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(&module, basek, k_ct, rank);
ct.encrypt_sk(
&module,
&pt,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -79,7 +80,7 @@ fn packing() {
ct.encrypt_sk(
&module,
&pt,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
@@ -115,7 +116,7 @@ fn packing() {
});
pt_want.data.encode_vec_i64(0, basek, pt_k, &data, 32);
res_i.decrypt(&module, &mut pt, &sk, scratch.borrow());
res_i.decrypt(&module, &mut pt, &sk_dft, scratch.borrow());
if i & 1 == 0 {
pt.sub_inplace_ab(&module, &pt_want);

View File

@@ -3,10 +3,13 @@ use std::collections::HashMap;
use backend::{FFT64, FillUniform, Module, ScratchOwned, Stats, VecZnxOps, ZnxView, ZnxViewMut};
use sampling::source::Source;
use crate::{AutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, test_fft64::var_noise_gglwe_product};
use crate::{
FourierGLWESecret, GLWEAutomorphismKey, GLWECiphertext, GLWEPlaintext, GLWESecret, Infos, div_ceil,
noise::var_noise_gglwe_product,
};
#[test]
fn trace_inplace() {
fn apply_inplace() {
let log_n: usize = 8;
(1..4).for_each(|rank| {
println!("test trace_inplace rank: {}", rank);
@@ -33,12 +36,13 @@ fn test_trace_inplace(log_n: usize, basek: usize, k: usize, sigma: f64, rank: us
let mut scratch: ScratchOwned = ScratchOwned::new(
GLWECiphertext::encrypt_sk_scratch_space(&module, basek, ct.k())
| GLWECiphertext::decrypt_scratch_space(&module, basek, ct.k())
| AutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_autokey, rank)
| GLWEAutomorphismKey::generate_from_sk_scratch_space(&module, basek, k_autokey, rank)
| GLWECiphertext::trace_inplace_scratch_space(&module, basek, ct.k(), k_autokey, digits, rank),
);
let mut sk: GLWESecret<Vec<u8>, FFT64> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(&&module, 0.5, &mut source_xs);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_dft: FourierGLWESecret<Vec<u8>, FFT64> = FourierGLWESecret::from(&module, &sk);
let mut data_want: Vec<i64> = vec![0i64; module.n()];
@@ -53,17 +57,18 @@ fn test_trace_inplace(log_n: usize, basek: usize, k: usize, sigma: f64, rank: us
ct.encrypt_sk(
&module,
&pt_have,
&sk,
&sk_dft,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut auto_keys: HashMap<i64, AutomorphismKey<Vec<u8>, FFT64>> = HashMap::new();
let mut auto_keys: HashMap<i64, GLWEAutomorphismKey<Vec<u8>, FFT64>> = HashMap::new();
let gal_els: Vec<i64> = GLWECiphertext::trace_galois_elements(&module);
gal_els.iter().for_each(|gal_el| {
let mut key: AutomorphismKey<Vec<u8>, FFT64> = AutomorphismKey::alloc(&module, basek, k_autokey, rows, digits, rank);
let mut key: GLWEAutomorphismKey<Vec<u8>, FFT64> =
GLWEAutomorphismKey::alloc(&module, basek, k_autokey, rows, digits, rank);
key.generate_from_sk(
&module,
*gal_el,
@@ -81,7 +86,7 @@ fn test_trace_inplace(log_n: usize, basek: usize, k: usize, sigma: f64, rank: us
(0..pt_want.size()).for_each(|i| pt_want.data.at_mut(0, i)[0] = pt_have.data.at(0, i)[0]);
ct.decrypt(&module, &mut pt_have, &sk, scratch.borrow());
ct.decrypt(&module, &mut pt_have, &sk_dft, scratch.borrow());
module.vec_znx_sub_ab_inplace(&mut pt_want.data, 0, &pt_have.data, 0);
module.vec_znx_normalize_inplace(basek, &mut pt_want.data, 0, scratch.borrow());

View File

@@ -2,7 +2,7 @@ use std::collections::HashMap;
use backend::{FFT64, Module, Scratch};
use crate::{AutomorphismKey, GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef, GLWEOps, Infos, SetMetaData};
use crate::{GLWEAutomorphismKey, GLWECiphertext, GLWECiphertextToMut, GLWECiphertextToRef, GLWEOps, Infos, SetMetaData};
impl GLWECiphertext<Vec<u8>> {
pub fn trace_galois_elements(module: &Module<FFT64>) -> Vec<i64> {
@@ -51,7 +51,7 @@ where
start: usize,
end: usize,
lhs: &GLWECiphertext<DataLhs>,
auto_keys: &HashMap<i64, AutomorphismKey<DataAK, FFT64>>,
auto_keys: &HashMap<i64, GLWEAutomorphismKey<DataAK, FFT64>>,
scratch: &mut Scratch,
) where
GLWECiphertext<DataLhs>: GLWECiphertextToRef + Infos,
@@ -65,7 +65,7 @@ where
module: &Module<FFT64>,
start: usize,
end: usize,
auto_keys: &HashMap<i64, AutomorphismKey<DataAK, FFT64>>,
auto_keys: &HashMap<i64, GLWEAutomorphismKey<DataAK, FFT64>>,
scratch: &mut Scratch,
) {
(start..end).for_each(|i| {

View File

@@ -1,343 +0,0 @@
use backend::{Backend, FFT64, MatZnxDft, MatZnxDftOps, Module, Scratch, ZnxZero};
use sampling::source::Source;
use crate::{FourierGLWECiphertext, GGLWECiphertext, GGSWCiphertext, GLWESecret, GetRow, Infos, ScratchCore, SetRow};
pub struct GLWESwitchingKey<Data, B: Backend>(pub(crate) GGLWECiphertext<Data, B>);
impl GLWESwitchingKey<Vec<u8>, FFT64> {
pub fn alloc(
module: &Module<FFT64>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> Self {
GLWESwitchingKey(GGLWECiphertext::alloc(
module, basek, k, rows, digits, rank_in, rank_out,
))
}
pub fn bytes_of(
module: &Module<FFT64>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
GGLWECiphertext::<Vec<u8>, FFT64>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out)
}
}
impl<T, B: Backend> Infos for GLWESwitchingKey<T, B> {
type Inner = MatZnxDft<T, B>;
fn inner(&self) -> &Self::Inner {
self.0.inner()
}
fn basek(&self) -> usize {
self.0.basek()
}
fn k(&self) -> usize {
self.0.k()
}
}
impl<T, B: Backend> GLWESwitchingKey<T, B> {
pub fn rank(&self) -> usize {
self.0.data.cols_out() - 1
}
pub fn rank_in(&self) -> usize {
self.0.data.cols_in()
}
pub fn rank_out(&self) -> usize {
self.0.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.0.digits()
}
}
impl<C: AsRef<[u8]>> GetRow<FFT64> for GLWESwitchingKey<C, FFT64> {
fn get_row<R: AsMut<[u8]> + AsRef<[u8]>>(
&self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
res: &mut FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_get_row(&mut res.data, &self.0.data, row_i, col_j);
}
}
impl<C: AsMut<[u8]> + AsRef<[u8]>> SetRow<FFT64> for GLWESwitchingKey<C, FFT64> {
fn set_row<R: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
row_i: usize,
col_j: usize,
a: &FourierGLWECiphertext<R, FFT64>,
) {
module.mat_znx_dft_set_row(&mut self.0.data, row_i, col_j, &a.data);
}
}
impl GLWESwitchingKey<Vec<u8>, FFT64> {
pub fn encrypt_sk_scratch_space(module: &Module<FFT64>, basek: usize, k: usize, rank: usize) -> usize {
GGLWECiphertext::generate_from_sk_scratch_space(module, basek, k, rank)
}
pub fn encrypt_pk_scratch_space(module: &Module<FFT64>, _basek: usize, _k: usize, _rank: usize) -> usize {
GGLWECiphertext::generate_from_pk_scratch_space(module, _basek, _k, _rank)
}
pub fn keyswitch_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_in: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize {
let tmp_in: usize = FourierGLWECiphertext::bytes_of(module, basek, k_in, rank_in);
let tmp_out: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank_out);
let ksk: usize =
FourierGLWECiphertext::keyswitch_scratch_space(module, basek, k_out, k_in, k_ksk, digits, rank_in, rank_out);
tmp_in + tmp_out + ksk
}
pub fn keyswitch_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_ksk: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank);
let ksk: usize = FourierGLWECiphertext::keyswitch_inplace_scratch_space(module, basek, k_out, k_ksk, digits, rank);
tmp + ksk
}
pub fn external_product_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_in: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp_in: usize = FourierGLWECiphertext::bytes_of(module, basek, k_in, rank);
let tmp_out: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank);
let ggsw: usize = FourierGLWECiphertext::external_product_scratch_space(module, basek, k_out, k_in, k_ggsw, digits, rank);
tmp_in + tmp_out + ggsw
}
pub fn external_product_inplace_scratch_space(
module: &Module<FFT64>,
basek: usize,
k_out: usize,
k_ggsw: usize,
digits: usize,
rank: usize,
) -> usize {
let tmp: usize = FourierGLWECiphertext::bytes_of(module, basek, k_out, rank);
let ggsw: usize =
FourierGLWECiphertext::external_product_inplace_scratch_space(module, basek, k_out, k_ggsw, digits, rank);
tmp + ggsw
}
}
impl<DataSelf: AsMut<[u8]> + AsRef<[u8]>> GLWESwitchingKey<DataSelf, FFT64> {
pub fn generate_from_sk<DataSkIn: AsRef<[u8]>, DataSkOut: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
sk_in: &GLWESecret<DataSkIn, FFT64>,
sk_out: &GLWESecret<DataSkOut, FFT64>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch,
) {
self.0.encrypt_sk(
module,
&sk_in.data,
sk_out,
source_xa,
source_xe,
sigma,
scratch,
);
}
pub fn keyswitch<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
lhs: &GLWESwitchingKey<DataLhs, FFT64>,
rhs: &GLWESwitchingKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(
self.rank_in(),
lhs.rank_in(),
"ksk_out input rank: {} != ksk_in input rank: {}",
self.rank_in(),
lhs.rank_in()
);
assert_eq!(
lhs.rank_out(),
rhs.rank_in(),
"ksk_in output rank: {} != ksk_apply input rank: {}",
self.rank_out(),
rhs.rank_in()
);
assert_eq!(
self.rank_out(),
rhs.rank_out(),
"ksk_out output rank: {} != ksk_apply output rank: {}",
self.rank_out(),
rhs.rank_out()
);
}
let (mut tmp_in, scratch1) = scratch.tmp_glwe_fourier(module, lhs.basek(), lhs.k(), lhs.rank());
let (mut tmp_out, scratch2) = scratch1.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
lhs.get_row(module, row_j, col_i, &mut tmp_in);
tmp_out.keyswitch(module, &tmp_in, rhs, scratch2);
self.set_row(module, row_j, col_i, &tmp_out);
});
});
tmp_out.data.zero();
(self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| {
(0..self.rank_in()).for_each(|col_j| {
self.set_row(module, row_i, col_j, &tmp_out);
});
});
}
pub fn keyswitch_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &GLWESwitchingKey<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(
self.rank_out(),
rhs.rank_out(),
"ksk_out output rank: {} != ksk_apply output rank: {}",
self.rank_out(),
rhs.rank_out()
);
}
let (mut tmp, scratch1) = scratch.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
self.get_row(module, row_j, col_i, &mut tmp);
tmp.keyswitch_inplace(module, rhs, scratch1);
self.set_row(module, row_j, col_i, &tmp);
});
});
}
pub fn external_product<DataLhs: AsRef<[u8]>, DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
lhs: &GLWESwitchingKey<DataLhs, FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(
self.rank_in(),
lhs.rank_in(),
"ksk_out input rank: {} != ksk_in input rank: {}",
self.rank_in(),
lhs.rank_in()
);
assert_eq!(
lhs.rank_out(),
rhs.rank(),
"ksk_in output rank: {} != ggsw rank: {}",
self.rank_out(),
rhs.rank()
);
assert_eq!(
self.rank_out(),
rhs.rank(),
"ksk_out output rank: {} != ggsw rank: {}",
self.rank_out(),
rhs.rank()
);
}
let (mut tmp_in, scratch1) = scratch.tmp_glwe_fourier(module, lhs.basek(), lhs.k(), lhs.rank());
let (mut tmp_out, scratch2) = scratch1.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
lhs.get_row(module, row_j, col_i, &mut tmp_in);
tmp_out.external_product(module, &tmp_in, rhs, scratch2);
self.set_row(module, row_j, col_i, &tmp_out);
});
});
tmp_out.data.zero();
(self.rows().min(lhs.rows())..self.rows()).for_each(|row_i| {
(0..self.rank_in()).for_each(|col_j| {
self.set_row(module, row_i, col_j, &tmp_out);
});
});
}
pub fn external_product_inplace<DataRhs: AsRef<[u8]>>(
&mut self,
module: &Module<FFT64>,
rhs: &GGSWCiphertext<DataRhs, FFT64>,
scratch: &mut Scratch,
) {
#[cfg(debug_assertions)]
{
assert_eq!(
self.rank_out(),
rhs.rank(),
"ksk_out output rank: {} != ggsw rank: {}",
self.rank_out(),
rhs.rank()
);
}
let (mut tmp, scratch1) = scratch.tmp_glwe_fourier(module, self.basek(), self.k(), self.rank());
println!("tmp: {}", tmp.size());
(0..self.rank_in()).for_each(|col_i| {
(0..self.rows()).for_each(|row_j| {
self.get_row(module, row_j, col_i, &mut tmp);
tmp.external_product_inplace(module, rhs, scratch1);
self.set_row(module, row_j, col_i, &tmp);
});
});
}
}

View File

@@ -1,35 +1,29 @@
pub mod automorphism;
pub mod blind_rotation;
pub mod dist;
pub mod elem;
pub mod fourier_glwe;
pub mod gglwe;
pub mod ggsw;
pub mod glwe;
pub mod keys;
pub mod keyswitch_key;
pub mod lwe;
pub mod tensor_key;
#[cfg(test)]
mod test_fft64;
pub mod noise;
mod utils;
pub use automorphism::*;
use backend::Backend;
use backend::FFT64;
use backend::Module;
pub use elem::*;
pub use fourier_glwe::*;
pub use gglwe::*;
pub use fourier_glwe::{FourierGLWECiphertext, FourierGLWESecret};
pub use gglwe::{GGLWECiphertext, GLWEAutomorphismKey, GLWESwitchingKey, GLWETensorKey};
pub use ggsw::*;
pub use glwe::*;
pub use keyswitch_key::*;
pub use glwe::{GLWECiphertext, GLWEOps, GLWEPacker, GLWEPlaintext, GLWEPublicKey, GLWESecret};
pub(crate) use glwe::{GLWECiphertextToMut, GLWECiphertextToRef};
pub use lwe::*;
pub use tensor_key::*;
pub use backend::Scratch;
pub use backend::ScratchOwned;
use crate::keys::SecretDistribution;
use crate::dist::Distribution;
pub(crate) const SIX_SIGMA: f64 = 6.0;
@@ -62,7 +56,8 @@ pub trait ScratchCore<B: Backend> {
k: usize,
rank: usize,
) -> (FourierGLWECiphertext<&mut [u8], B>, &mut Self);
fn tmp_sk(&mut self, module: &Module<B>, rank: usize) -> (GLWESecret<&mut [u8], B>, &mut Self);
fn tmp_sk(&mut self, module: &Module<B>, rank: usize) -> (GLWESecret<&mut [u8]>, &mut Self);
fn tmp_fourier_sk(&mut self, module: &Module<B>, rank: usize) -> (FourierGLWESecret<&mut [u8], B>, &mut Self);
fn tmp_glwe_pk(
&mut self,
module: &Module<B>,
@@ -88,7 +83,7 @@ pub trait ScratchCore<B: Backend> {
rows: usize,
digits: usize,
rank: usize,
) -> (TensorKey<&mut [u8], B>, &mut Self);
) -> (GLWETensorKey<&mut [u8], B>, &mut Self);
fn tmp_autokey(
&mut self,
module: &Module<B>,
@@ -97,7 +92,7 @@ pub trait ScratchCore<B: Backend> {
rows: usize,
digits: usize,
rank: usize,
) -> (AutomorphismKey<&mut [u8], B>, &mut Self);
) -> (GLWEAutomorphismKey<&mut [u8], B>, &mut Self);
}
impl ScratchCore<FFT64> for Scratch {
@@ -194,22 +189,31 @@ impl ScratchCore<FFT64> for Scratch {
(
GLWEPublicKey {
data,
dist: SecretDistribution::NONE,
dist: Distribution::NONE,
},
scratch,
)
}
fn tmp_sk(&mut self, module: &Module<FFT64>, rank: usize) -> (GLWESecret<&mut [u8], FFT64>, &mut Self) {
fn tmp_sk(&mut self, module: &Module<FFT64>, rank: usize) -> (GLWESecret<&mut [u8]>, &mut Self) {
let (data, scratch) = self.tmp_scalar_znx(module, rank);
let (data_fourier, scratch1) = scratch.tmp_scalar_znx_dft(module, rank);
(
GLWESecret {
data,
data_fourier,
dist: SecretDistribution::NONE,
dist: Distribution::NONE,
},
scratch1,
scratch,
)
}
fn tmp_fourier_sk(&mut self, module: &Module<FFT64>, rank: usize) -> (FourierGLWESecret<&mut [u8], FFT64>, &mut Self) {
let (data, scratch) = self.tmp_scalar_znx_dft(module, rank);
(
FourierGLWESecret {
data,
dist: Distribution::NONE,
},
scratch,
)
}
@@ -235,9 +239,9 @@ impl ScratchCore<FFT64> for Scratch {
rows: usize,
digits: usize,
rank: usize,
) -> (AutomorphismKey<&mut [u8], FFT64>, &mut Self) {
) -> (GLWEAutomorphismKey<&mut [u8], FFT64>, &mut Self) {
let (data, scratch) = self.tmp_glwe_ksk(module, basek, k, rows, digits, rank, rank);
(AutomorphismKey { key: data, p: 0 }, scratch)
(GLWEAutomorphismKey { key: data, p: 0 }, scratch)
}
fn tmp_tsk(
@@ -248,7 +252,7 @@ impl ScratchCore<FFT64> for Scratch {
rows: usize,
digits: usize,
rank: usize,
) -> (TensorKey<&mut [u8], FFT64>, &mut Self) {
) -> (GLWETensorKey<&mut [u8], FFT64>, &mut Self) {
let mut keys: Vec<GLWESwitchingKey<&mut [u8], FFT64>> = Vec::new();
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
@@ -264,6 +268,6 @@ impl ScratchCore<FFT64> for Scratch {
scratch = s;
keys.push(gglwe);
}
(TensorKey { keys }, scratch)
(GLWETensorKey { keys }, scratch)
}
}

3
core/src/lwe/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod secret;
pub use secret::LWESecret;

View File

@@ -1,18 +1,18 @@
use backend::{ScalarZnx, ZnxInfos, ZnxZero};
use sampling::source::Source;
use crate::SecretDistribution;
use crate::Distribution;
pub struct LWESecret<T> {
pub(crate) data: ScalarZnx<T>,
pub(crate) dist: SecretDistribution,
pub(crate) dist: Distribution,
}
impl LWESecret<Vec<u8>> {
pub fn alloc(n: usize) -> Self {
Self {
data: ScalarZnx::new(n, 1),
dist: SecretDistribution::NONE,
dist: Distribution::NONE,
}
}
}
@@ -34,31 +34,31 @@ impl<DataSelf> LWESecret<DataSelf> {
impl<D: AsRef<[u8]> + AsMut<[u8]>> LWESecret<D> {
pub fn fill_ternary_prob(&mut self, prob: f64, source: &mut Source) {
self.data.fill_ternary_prob(0, prob, source);
self.dist = SecretDistribution::TernaryProb(prob);
self.dist = Distribution::TernaryProb(prob);
}
pub fn fill_ternary_hw(&mut self, hw: usize, source: &mut Source) {
self.data.fill_ternary_hw(0, hw, source);
self.dist = SecretDistribution::TernaryFixed(hw);
self.dist = Distribution::TernaryFixed(hw);
}
pub fn fill_binary_prob(&mut self, prob: f64, source: &mut Source) {
self.data.fill_binary_prob(0, prob, source);
self.dist = SecretDistribution::BinaryProb(prob);
self.dist = Distribution::BinaryProb(prob);
}
pub fn fill_binary_hw(&mut self, hw: usize, source: &mut Source) {
self.data.fill_binary_hw(0, hw, source);
self.dist = SecretDistribution::BinaryFixed(hw);
self.dist = Distribution::BinaryFixed(hw);
}
pub fn fill_binary_block(&mut self, block_size: usize, source: &mut Source) {
self.data.fill_binary_block(0, block_size, source);
self.dist = SecretDistribution::BinaryBlock(block_size);
self.dist = Distribution::BinaryBlock(block_size);
}
pub fn fill_zero(&mut self) {
self.data.zero();
self.dist = SecretDistribution::ZERO;
self.dist = Distribution::ZERO;
}
}

View File

@@ -1,9 +1,4 @@
mod automorphism_key;
mod gglwe;
mod ggsw;
mod glwe_fourier;
mod tensor_key;
#[allow(dead_code)]
pub(crate) fn var_noise_gglwe_product(
n: f64,
basek: usize,
@@ -34,6 +29,7 @@ pub(crate) fn var_noise_gglwe_product(
noise
}
#[allow(dead_code)]
pub(crate) fn log2_std_noise_gglwe_product(
n: f64,
basek: usize,
@@ -62,6 +58,7 @@ pub(crate) fn log2_std_noise_gglwe_product(
noise.log2().min(-1.0).max(-(a_logq as f64)) // max noise is [-2^{-1}, 2^{-1}]
}
#[allow(dead_code)]
pub(crate) fn noise_ggsw_product(
n: f64,
basek: usize,
@@ -94,6 +91,7 @@ pub(crate) fn noise_ggsw_product(
noise.log2().min(-1.0) // max noise is [-2^{-1}, 2^{-1}]
}
#[allow(dead_code)]
pub(crate) fn noise_ggsw_keyswitch(
n: f64,
basek: usize,