Dev serialization (#64)

* Added compressed serialization for GLWECiphertext + Ciphertext decompression

* Added compressed serialization for GGLWECiphertext & GLWESwitchingkey

* generalized automorphism test

* Removed ops on scalar_znx, replaced by as_vec_znx/as_vec_znx_mut and then call op on vec_znx

* Added tests for automorphism key encryption

* Added tensorkey compressed

* added ggsw compressed
This commit is contained in:
Jean-Philippe Bossuat
2025-08-12 17:43:28 +02:00
committed by GitHub
parent 4c59733566
commit 9aa4b1f1e2
68 changed files with 3430 additions and 1695 deletions

47
Cargo.lock generated
View File

@@ -50,6 +50,7 @@ dependencies = [
"rug", "rug",
"sampling", "sampling",
"utils", "utils",
"zstd",
] ]
[[package]] [[package]]
@@ -82,6 +83,8 @@ version = "1.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
dependencies = [ dependencies = [
"jobserver",
"libc",
"shlex", "shlex",
] ]
@@ -160,6 +163,7 @@ dependencies = [
"byteorder", "byteorder",
"criterion", "criterion",
"itertools 0.14.0", "itertools 0.14.0",
"rand_core",
"rand_distr", "rand_distr",
"rug", "rug",
"sampling", "sampling",
@@ -297,6 +301,15 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "jobserver"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.76" version = "0.3.76"
@@ -359,6 +372,12 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]] [[package]]
name = "plotters" name = "plotters"
version = "0.3.7" version = "0.3.7"
@@ -819,3 +838,31 @@ dependencies = [
"quote", "quote",
"syn", "syn",
] ]
[[package]]
name = "zstd"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.15+zstd.1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
dependencies = [
"cc",
"pkg-config",
]

View File

@@ -11,3 +11,4 @@ rand_distr = "0.5.1"
itertools = "0.14.0" itertools = "0.14.0"
criterion = "0.7.0" criterion = "0.7.0"
byteorder = "1.5.0" byteorder = "1.5.0"
zstd = "0.13.3"

View File

@@ -1,11 +0,0 @@
{
"github.copilot.enable": {
"*": false,
"plaintext": false,
"markdown": false,
"scminput": false
},
"files.associations": {
"random": "c"
}
}

View File

@@ -15,6 +15,7 @@ sampling = { path = "../sampling" }
utils = { path = "../utils" } utils = { path = "../utils" }
paste = "1.0.15" paste = "1.0.15"
byteorder = {workspace = true} byteorder = {workspace = true}
zstd = {workspace = true}
[build-dependencies] [build-dependencies]
cmake = "0.1.54" cmake = "0.1.54"

View File

@@ -1,7 +1,9 @@
use std::path::PathBuf; use std::path::PathBuf;
pub fn build() { pub fn build() {
let dst: PathBuf = cmake::Config::new("src/implementation/cpu_spqlios/spqlios-arithmetic").define("ENABLE_TESTING", "FALSE").build(); let dst: PathBuf = cmake::Config::new("src/implementation/cpu_spqlios/spqlios-arithmetic")
.define("ENABLE_TESTING", "FALSE")
.build();
let lib_dir: PathBuf = dst.join("lib"); let lib_dir: PathBuf = dst.join("lib");

View File

@@ -1,4 +1,4 @@
use crate::hal::layouts::{ScalarZnxOwned, ScalarZnxToMut, ScalarZnxToRef}; use crate::hal::layouts::ScalarZnxOwned;
/// Allocates as [crate::hal::layouts::ScalarZnx]. /// Allocates as [crate::hal::layouts::ScalarZnx].
pub trait ScalarZnxAlloc { pub trait ScalarZnxAlloc {
@@ -15,33 +15,3 @@ pub trait ScalarZnxAllocBytes {
pub trait ScalarZnxFromBytes { pub trait ScalarZnxFromBytes {
fn scalar_znx_from_bytes(&self, cols: usize, bytes: Vec<u8>) -> ScalarZnxOwned; fn scalar_znx_from_bytes(&self, cols: usize, bytes: Vec<u8>) -> ScalarZnxOwned;
} }
/// Applies the mapping X -> X^k to a\[a_col\] and write the result on res\[res_col\].
pub trait ScalarZnxAutomorphism {
fn scalar_znx_automorphism<R, A>(&self, k: i64, res: &mut R, res_col: usize, a: &A, a_col: usize)
where
R: ScalarZnxToMut,
A: ScalarZnxToRef;
}
/// Applies the mapping X -> X^k on res\[res_col\].
pub trait ScalarZnxAutomorphismInplace {
fn scalar_znx_automorphism_inplace<R>(&self, k: i64, res: &mut R, res_col: usize)
where
R: ScalarZnxToMut;
}
/// Multiply a\[a_col\] with (X^p - 1) and write the result on res\[res_col\].
pub trait ScalarZnxMulXpMinusOne {
fn scalar_znx_mul_xp_minus_one<R, A>(&self, p: i64, r: &mut R, r_col: usize, a: &A, a_col: usize)
where
R: ScalarZnxToMut,
A: ScalarZnxToRef;
}
/// Multiply res\[res_col\] with (X^p - 1).
pub trait ScalarZnxMulXpMinusOneInplace {
fn scalar_znx_mul_xp_minus_one_inplace<R>(&self, p: i64, res: &mut R, res_col: usize)
where
R: ScalarZnxToMut;
}

View File

@@ -1,5 +1,6 @@
use crate::hal::layouts::{Data, DataMut, DataRef}; use crate::hal::layouts::{Data, DataMut, DataRef};
use rand_distr::num_traits::Zero; use rand_distr::num_traits::Zero;
use sampling::source::Source;
pub trait ZnxInfos { pub trait ZnxInfos {
/// Returns the ring degree of the polynomials. /// Returns the ring degree of the polynomials.
@@ -108,3 +109,7 @@ where
fn zero(&mut self); fn zero(&mut self);
fn zero_at(&mut self, i: usize, j: usize); fn zero_at(&mut self, i: usize, j: usize);
} }
pub trait FillUniform {
fn fill_uniform(&mut self, source: &mut Source);
}

View File

@@ -1,13 +1,7 @@
use crate::hal::{ use crate::hal::{
api::{ api::{ScalarZnxAlloc, ScalarZnxAllocBytes},
ScalarZnxAlloc, ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScalarZnxAutomorphismInplace, ScalarZnxFromBytes, layouts::{Backend, Module, ScalarZnxOwned},
ScalarZnxMulXpMinusOne, ScalarZnxMulXpMinusOneInplace, oep::{ScalarZnxAllocBytesImpl, ScalarZnxAllocImpl},
},
layouts::{Backend, Module, ScalarZnxOwned, ScalarZnxToMut, ScalarZnxToRef},
oep::{
ScalarZnxAllocBytesImpl, ScalarZnxAllocImpl, ScalarZnxAutomorphismImpl, ScalarZnxAutomorphismInplaceIml,
ScalarZnxFromBytesImpl, ScalarZnxMulXpMinusOneImpl, ScalarZnxMulXpMinusOneInplaceImpl,
},
}; };
impl<B> ScalarZnxAllocBytes for Module<B> impl<B> ScalarZnxAllocBytes for Module<B>
@@ -27,62 +21,3 @@ where
B::scalar_znx_alloc_impl(self.n(), cols) B::scalar_znx_alloc_impl(self.n(), cols)
} }
} }
impl<B> ScalarZnxFromBytes for Module<B>
where
B: Backend + ScalarZnxFromBytesImpl<B>,
{
fn scalar_znx_from_bytes(&self, cols: usize, bytes: Vec<u8>) -> ScalarZnxOwned {
B::scalar_znx_from_bytes_impl(self.n(), cols, bytes)
}
}
impl<B> ScalarZnxAutomorphism for Module<B>
where
B: Backend + ScalarZnxAutomorphismImpl<B>,
{
fn scalar_znx_automorphism<R, A>(&self, k: i64, res: &mut R, res_col: usize, a: &A, a_col: usize)
where
R: ScalarZnxToMut,
A: ScalarZnxToRef,
{
B::scalar_znx_automorphism_impl(self, k, res, res_col, a, a_col);
}
}
impl<B> ScalarZnxAutomorphismInplace for Module<B>
where
B: Backend + ScalarZnxAutomorphismInplaceIml<B>,
{
fn scalar_znx_automorphism_inplace<A>(&self, k: i64, a: &mut A, a_col: usize)
where
A: ScalarZnxToMut,
{
B::scalar_znx_automorphism_inplace_impl(self, k, a, a_col);
}
}
impl<B> ScalarZnxMulXpMinusOne for Module<B>
where
B: Backend + ScalarZnxMulXpMinusOneImpl<B>,
{
fn scalar_znx_mul_xp_minus_one<R, A>(&self, p: i64, r: &mut R, r_col: usize, a: &A, a_col: usize)
where
R: ScalarZnxToMut,
A: ScalarZnxToRef,
{
B::scalar_znx_mul_xp_minus_one_impl(self, p, r, r_col, a, a_col);
}
}
impl<B> ScalarZnxMulXpMinusOneInplace for Module<B>
where
B: Backend + ScalarZnxMulXpMinusOneInplaceImpl<B>,
{
fn scalar_znx_mul_xp_minus_one_inplace<R>(&self, p: i64, r: &mut R, r_col: usize)
where
R: ScalarZnxToMut,
{
B::scalar_znx_mul_xp_minus_one_inplace_impl(self, p, r, r_col);
}
}

View File

@@ -1,12 +1,17 @@
use crate::{ use crate::{
alloc_aligned, alloc_aligned,
hal::{ hal::{
api::{DataView, DataViewMut, ZnxInfos, ZnxSliceSize, ZnxView}, api::{DataView, DataViewMut, FillUniform, ZnxInfos, ZnxSliceSize, ZnxView, ZnxViewMut, ZnxZero},
layouts::{Data, DataMut, DataRef, ReaderFrom, VecZnx, WriterTo}, layouts::{Data, DataMut, DataRef, ReaderFrom, VecZnx, WriterTo},
}, },
}; };
use std::fmt;
#[derive(PartialEq, Eq)] use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use rand::RngCore;
use sampling::source::Source;
#[derive(PartialEq, Eq, Clone)]
pub struct MatZnx<D: Data> { pub struct MatZnx<D: Data> {
data: D, data: D,
n: usize, n: usize,
@@ -16,6 +21,12 @@ pub struct MatZnx<D: Data> {
cols_out: usize, cols_out: usize,
} }
impl<D: DataRef> fmt::Debug for MatZnx<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
impl<D: Data> ZnxInfos for MatZnx<D> { impl<D: Data> ZnxInfos for MatZnx<D> {
fn cols(&self) -> usize { fn cols(&self) -> usize {
self.cols_in self.cols_in
@@ -74,7 +85,7 @@ impl<D: DataRef> MatZnx<D> {
} }
impl<D: DataRef + From<Vec<u8>>> MatZnx<D> { impl<D: DataRef + From<Vec<u8>>> MatZnx<D> {
pub(crate) fn new(n: usize, rows: usize, cols_in: usize, cols_out: usize, size: usize) -> Self { pub(crate) fn alloc(n: usize, rows: usize, cols_in: usize, cols_out: usize, size: usize) -> Self {
let data: Vec<u8> = alloc_aligned(Self::bytes_of(n, rows, cols_in, cols_out, size)); let data: Vec<u8> = alloc_aligned(Self::bytes_of(n, rows, cols_in, cols_out, size));
Self { Self {
data: data.into(), data: data.into(),
@@ -86,7 +97,7 @@ impl<D: DataRef + From<Vec<u8>>> MatZnx<D> {
} }
} }
pub(crate) fn new_from_bytes( pub(crate) fn from_bytes(
n: usize, n: usize,
rows: usize, rows: usize,
cols_in: usize, cols_in: usize,
@@ -158,6 +169,12 @@ impl<D: DataMut> MatZnx<D> {
} }
} }
impl<D: DataMut> FillUniform for MatZnx<D> {
fn fill_uniform(&mut self, source: &mut Source) {
source.fill_bytes(self.data.as_mut());
}
}
pub type MatZnxOwned = MatZnx<Vec<u8>>; pub type MatZnxOwned = MatZnx<Vec<u8>>;
pub type MatZnxMut<'a> = MatZnx<&'a mut [u8]>; pub type MatZnxMut<'a> = MatZnx<&'a mut [u8]>;
pub type MatZnxRef<'a> = MatZnx<&'a [u8]>; pub type MatZnxRef<'a> = MatZnx<&'a [u8]>;
@@ -209,8 +226,6 @@ impl<D: Data> MatZnx<D> {
} }
} }
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
impl<D: DataMut> ReaderFrom for MatZnx<D> { impl<D: DataMut> ReaderFrom for MatZnx<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> { fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.n = reader.read_u64::<LittleEndian>()? as usize; self.n = reader.read_u64::<LittleEndian>()? as usize;
@@ -244,3 +259,32 @@ impl<D: DataRef> WriterTo for MatZnx<D> {
Ok(()) Ok(())
} }
} }
impl<D: DataRef> fmt::Display for MatZnx<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"MatZnx(n={}, rows={}, cols_in={}, cols_out={}, size={})",
self.n, self.rows, self.cols_in, self.cols_out, self.size
)?;
for row_i in 0..self.rows {
writeln!(f, "Row {}:", row_i)?;
for col_i in 0..self.cols_in {
writeln!(f, "cols_in {}:", col_i)?;
writeln!(f, "{}:", self.at(row_i, col_i))?;
}
}
Ok(())
}
}
impl<D: DataMut> ZnxZero for MatZnx<D> {
fn zero(&mut self) {
self.raw_mut().fill(0)
}
fn zero_at(&mut self, i: usize, j: usize) {
self.at_mut(i, j).zero();
}
}

View File

@@ -6,12 +6,12 @@ use sampling::source::Source;
use crate::{ use crate::{
alloc_aligned, alloc_aligned,
hal::{ hal::{
api::{DataView, DataViewMut, ZnxInfos, ZnxSliceSize, ZnxView, ZnxViewMut, ZnxZero}, api::{DataView, DataViewMut, FillUniform, ZnxInfos, ZnxSliceSize, ZnxView, ZnxViewMut, ZnxZero},
layouts::{Data, DataMut, DataRef, ReaderFrom, VecZnx, VecZnxToMut, VecZnxToRef, WriterTo}, layouts::{Data, DataMut, DataRef, ReaderFrom, VecZnx, WriterTo},
}, },
}; };
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq, Debug, Clone)]
pub struct ScalarZnx<D: Data> { pub struct ScalarZnx<D: Data> {
pub(crate) data: D, pub(crate) data: D,
pub(crate) n: usize, pub(crate) n: usize,
@@ -114,7 +114,7 @@ impl<D: DataRef> ScalarZnx<D> {
} }
impl<D: DataRef + From<Vec<u8>>> ScalarZnx<D> { impl<D: DataRef + From<Vec<u8>>> ScalarZnx<D> {
pub fn new(n: usize, cols: usize) -> Self { pub fn alloc(n: usize, cols: usize) -> Self {
let data: Vec<u8> = alloc_aligned::<u8>(Self::bytes_of(n, cols)); let data: Vec<u8> = alloc_aligned::<u8>(Self::bytes_of(n, cols));
Self { Self {
data: data.into(), data: data.into(),
@@ -123,7 +123,7 @@ impl<D: DataRef + From<Vec<u8>>> ScalarZnx<D> {
} }
} }
pub(crate) fn new_from_bytes(n: usize, cols: usize, bytes: impl Into<Vec<u8>>) -> Self { pub(crate) fn from_bytes(n: usize, cols: usize, bytes: impl Into<Vec<u8>>) -> Self {
let data: Vec<u8> = bytes.into(); let data: Vec<u8> = bytes.into();
assert!(data.len() == Self::bytes_of(n, cols)); assert!(data.len() == Self::bytes_of(n, cols));
Self { Self {
@@ -143,6 +143,12 @@ impl<D: DataMut> ZnxZero for ScalarZnx<D> {
} }
} }
impl<D: DataMut> FillUniform for ScalarZnx<D> {
fn fill_uniform(&mut self, source: &mut Source) {
source.fill_bytes(self.data.as_mut());
}
}
pub type ScalarZnxOwned = ScalarZnx<Vec<u8>>; pub type ScalarZnxOwned = ScalarZnx<Vec<u8>>;
impl<D: Data> ScalarZnx<D> { impl<D: Data> ScalarZnx<D> {
@@ -179,8 +185,8 @@ impl<D: DataMut> ScalarZnxToMut for ScalarZnx<D> {
} }
} }
impl<D: DataRef> VecZnxToRef for ScalarZnx<D> { impl<D: DataRef> ScalarZnx<D> {
fn to_ref(&self) -> VecZnx<&[u8]> { pub fn as_vec_znx(&self) -> VecZnx<&[u8]> {
VecZnx { VecZnx {
data: self.data.as_ref(), data: self.data.as_ref(),
n: self.n, n: self.n,
@@ -191,8 +197,8 @@ impl<D: DataRef> VecZnxToRef for ScalarZnx<D> {
} }
} }
impl<D: DataMut> VecZnxToMut for ScalarZnx<D> { impl<D: DataMut> ScalarZnx<D> {
fn to_mut(&mut self) -> VecZnx<&mut [u8]> { pub fn as_vec_znx_mut(&mut self) -> VecZnx<&mut [u8]> {
VecZnx { VecZnx {
data: self.data.as_mut(), data: self.data.as_mut(),
n: self.n, n: self.n,

View File

@@ -3,12 +3,12 @@ use std::fmt;
use crate::{ use crate::{
alloc_aligned, alloc_aligned,
hal::{ hal::{
api::{DataView, DataViewMut, ZnxInfos, ZnxSliceSize, ZnxView, ZnxViewMut, ZnxZero}, api::{DataView, DataViewMut, FillUniform, ZnxInfos, ZnxSliceSize, ZnxView, ZnxViewMut, ZnxZero},
layouts::{Data, DataMut, DataRef, ReaderFrom, WriterTo}, layouts::{Data, DataMut, DataRef, ReaderFrom, WriterTo},
}, },
}; };
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq, Clone)]
pub struct VecZnx<D: Data> { pub struct VecZnx<D: Data> {
pub(crate) data: D, pub(crate) data: D,
pub(crate) n: usize, pub(crate) n: usize,
@@ -86,7 +86,7 @@ impl<D: DataRef> VecZnx<D> {
} }
impl<D: DataRef + From<Vec<u8>>> VecZnx<D> { impl<D: DataRef + From<Vec<u8>>> VecZnx<D> {
pub fn new<Scalar: Sized>(n: usize, cols: usize, size: usize) -> Self { pub fn alloc<Scalar: Sized>(n: usize, cols: usize, size: usize) -> Self {
let data: Vec<u8> = alloc_aligned::<u8>(Self::alloc_bytes::<Scalar>(n, cols, size)); let data: Vec<u8> = alloc_aligned::<u8>(Self::alloc_bytes::<Scalar>(n, cols, size));
Self { Self {
data: data.into(), data: data.into(),
@@ -157,6 +157,12 @@ impl<D: DataRef> fmt::Display for VecZnx<D> {
} }
} }
impl<D: DataMut> FillUniform for VecZnx<D> {
fn fill_uniform(&mut self, source: &mut Source) {
source.fill_bytes(self.data.as_mut());
}
}
pub type VecZnxOwned = VecZnx<Vec<u8>>; pub type VecZnxOwned = VecZnx<Vec<u8>>;
pub type VecZnxMut<'a> = VecZnx<&'a mut [u8]>; pub type VecZnxMut<'a> = VecZnx<&'a mut [u8]>;
pub type VecZnxRef<'a> = VecZnx<&'a [u8]>; pub type VecZnxRef<'a> = VecZnx<&'a [u8]>;
@@ -207,6 +213,8 @@ impl<D: DataRef> VecZnx<D> {
} }
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use rand::RngCore;
use sampling::source::Source;
impl<D: DataMut> ReaderFrom for VecZnx<D> { impl<D: DataMut> ReaderFrom for VecZnx<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> { fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {

View File

@@ -1,4 +1,4 @@
use crate::hal::layouts::{Backend, Module, ScalarZnxOwned, ScalarZnxToMut, ScalarZnxToRef}; use crate::hal::layouts::{Backend, ScalarZnxOwned};
pub unsafe trait ScalarZnxFromBytesImpl<B: Backend> { pub unsafe trait ScalarZnxFromBytesImpl<B: Backend> {
fn scalar_znx_from_bytes_impl(n: usize, cols: usize, bytes: Vec<u8>) -> ScalarZnxOwned; fn scalar_znx_from_bytes_impl(n: usize, cols: usize, bytes: Vec<u8>) -> ScalarZnxOwned;
@@ -11,29 +11,3 @@ pub unsafe trait ScalarZnxAllocBytesImpl<B: Backend> {
pub unsafe trait ScalarZnxAllocImpl<B: Backend> { pub unsafe trait ScalarZnxAllocImpl<B: Backend> {
fn scalar_znx_alloc_impl(n: usize, cols: usize) -> ScalarZnxOwned; fn scalar_znx_alloc_impl(n: usize, cols: usize) -> ScalarZnxOwned;
} }
pub unsafe trait ScalarZnxAutomorphismImpl<B: Backend> {
fn scalar_znx_automorphism_impl<R, A>(module: &Module<B>, k: i64, res: &mut R, res_col: usize, a: &A, a_col: usize)
where
R: ScalarZnxToMut,
A: ScalarZnxToRef;
}
pub unsafe trait ScalarZnxAutomorphismInplaceIml<B: Backend> {
fn scalar_znx_automorphism_inplace_impl<A>(module: &Module<B>, k: i64, a: &mut A, a_col: usize)
where
A: ScalarZnxToMut;
}
pub unsafe trait ScalarZnxMulXpMinusOneImpl<B: Backend> {
fn scalar_znx_mul_xp_minus_one_impl<R, A>(module: &Module<B>, p: i64, r: &mut R, r_col: usize, a: &A, a_col: usize)
where
R: ScalarZnxToMut,
A: ScalarZnxToRef;
}
pub unsafe trait ScalarZnxMulXpMinusOneInplaceImpl<B: Backend> {
fn scalar_znx_mul_xp_minus_one_inplace_impl<R>(module: &Module<B>, p: i64, r: &mut R, r_col: usize)
where
R: ScalarZnxToMut;
}

View File

@@ -1 +1,2 @@
pub mod serialization;
pub mod vec_znx; pub mod vec_znx;

View File

@@ -0,0 +1,56 @@
use std::fmt::Debug;
use sampling::source::Source;
use crate::hal::{
api::{FillUniform, ZnxZero},
layouts::{ReaderFrom, WriterTo},
};
/// Generic test for serialization and deserialization.
///
/// - `T` must implement I/O traits, zeroing, cloning, and random filling.
pub fn test_reader_writer_interface<T>(mut original: T)
where
T: WriterTo + ReaderFrom + PartialEq + Eq + Debug + Clone + ZnxZero + FillUniform,
{
// Fill original with uniform random data
let mut source = Source::new([0u8; 32]);
original.fill_uniform(&mut source);
// Serialize into a buffer
let mut buffer = Vec::new();
original.write_to(&mut buffer).expect("write_to failed");
// Prepare receiver: same shape, but zeroed
let mut receiver = original.clone();
receiver.zero();
// Deserialize from buffer
let mut reader: &[u8] = &buffer;
receiver.read_from(&mut reader).expect("read_from failed");
// Ensure serialization round-trip correctness
assert_eq!(
&original, &receiver,
"Deserialized object does not match the original"
);
}
#[test]
fn scalar_znx_serialize() {
let original: crate::hal::layouts::ScalarZnx<Vec<u8>> = crate::hal::layouts::ScalarZnx::alloc(1024, 3);
test_reader_writer_interface(original);
}
#[test]
fn vec_znx_serialize() {
let original: crate::hal::layouts::VecZnx<Vec<u8>> = crate::hal::layouts::VecZnx::alloc::<i64>(1024, 3, 4);
test_reader_writer_interface(original);
}
#[test]
fn mat_znx_serialize() {
let original: crate::hal::layouts::MatZnx<Vec<u8>> = crate::hal::layouts::MatZnx::alloc(1024, 3, 2, 2, 4);
test_reader_writer_interface(original);
}

View File

@@ -11,7 +11,7 @@ where
B: CPUAVX, B: CPUAVX,
{ {
fn mat_znx_alloc_impl(module: &Module<B>, rows: usize, cols_in: usize, cols_out: usize, size: usize) -> MatZnxOwned { fn mat_znx_alloc_impl(module: &Module<B>, rows: usize, cols_in: usize, cols_out: usize, size: usize) -> MatZnxOwned {
MatZnxOwned::new(module.n(), rows, cols_in, cols_out, size) MatZnxOwned::alloc(module.n(), rows, cols_in, cols_out, size)
} }
} }
@@ -36,6 +36,6 @@ where
size: usize, size: usize,
bytes: Vec<u8>, bytes: Vec<u8>,
) -> MatZnxOwned { ) -> MatZnxOwned {
MatZnxOwned::new_from_bytes(module.n(), rows, cols_in, cols_out, size, bytes) MatZnxOwned::from_bytes(module.n(), rows, cols_in, cols_out, size, bytes)
} }
} }

View File

@@ -21,6 +21,9 @@ pub use module_fft64::*;
pub use module_ntt120::*; pub use module_ntt120::*;
/// For external documentation /// For external documentation
pub use vec_znx::{vec_znx_copy_ref, vec_znx_lsh_inplace_ref, vec_znx_merge_ref, vec_znx_rsh_inplace_ref, vec_znx_split_ref, vec_znx_switch_degree_ref}; pub use vec_znx::{
vec_znx_copy_ref, vec_znx_lsh_inplace_ref, vec_znx_merge_ref, vec_znx_rsh_inplace_ref, vec_znx_split_ref,
vec_znx_switch_degree_ref,
};
pub trait CPUAVX {} pub trait CPUAVX {}

View File

@@ -1,16 +1,9 @@
use crate::{ use crate::{
hal::{ hal::{
api::{ZnxInfos, ZnxSliceSize, ZnxView, ZnxViewMut}, layouts::{Backend, ScalarZnxOwned},
layouts::{Backend, Module, ScalarZnx, ScalarZnxOwned, ScalarZnxToMut, ScalarZnxToRef}, oep::{ScalarZnxAllocBytesImpl, ScalarZnxAllocImpl, ScalarZnxFromBytesImpl},
oep::{
ScalarZnxAllocBytesImpl, ScalarZnxAllocImpl, ScalarZnxAutomorphismImpl, ScalarZnxAutomorphismInplaceIml,
ScalarZnxFromBytesImpl,
},
},
implementation::cpu_spqlios::{
CPUAVX,
ffi::{module::module_info_t, vec_znx},
}, },
implementation::cpu_spqlios::CPUAVX,
}; };
unsafe impl<B: Backend> ScalarZnxAllocBytesImpl<B> for B unsafe impl<B: Backend> ScalarZnxAllocBytesImpl<B> for B
@@ -27,7 +20,7 @@ where
B: CPUAVX, B: CPUAVX,
{ {
fn scalar_znx_alloc_impl(n: usize, cols: usize) -> ScalarZnxOwned { fn scalar_znx_alloc_impl(n: usize, cols: usize) -> ScalarZnxOwned {
ScalarZnxOwned::new(n, cols) ScalarZnxOwned::alloc(n, cols)
} }
} }
@@ -36,65 +29,6 @@ where
B: CPUAVX, B: CPUAVX,
{ {
fn scalar_znx_from_bytes_impl(n: usize, cols: usize, bytes: Vec<u8>) -> ScalarZnxOwned { fn scalar_znx_from_bytes_impl(n: usize, cols: usize, bytes: Vec<u8>) -> ScalarZnxOwned {
ScalarZnxOwned::new_from_bytes(n, cols, bytes) ScalarZnxOwned::from_bytes(n, cols, bytes)
}
}
unsafe impl<B: Backend> ScalarZnxAutomorphismImpl<B> for B
where
B: CPUAVX,
{
fn scalar_znx_automorphism_impl<R, A>(module: &Module<B>, k: i64, res: &mut R, res_col: usize, a: &A, a_col: usize)
where
R: ScalarZnxToMut,
A: ScalarZnxToRef,
{
let a: ScalarZnx<&[u8]> = a.to_ref();
let mut res: ScalarZnx<&mut [u8]> = res.to_mut();
#[cfg(debug_assertions)]
{
assert_eq!(a.n(), module.n());
assert_eq!(res.n(), module.n());
}
unsafe {
vec_znx::vec_znx_automorphism(
module.ptr() as *const module_info_t,
k,
res.at_mut_ptr(res_col, 0),
res.size() as u64,
res.sl() as u64,
a.at_ptr(a_col, 0),
a.size() as u64,
a.sl() as u64,
)
}
}
}
unsafe impl<B: Backend> ScalarZnxAutomorphismInplaceIml<B> for B
where
B: CPUAVX,
{
fn scalar_znx_automorphism_inplace_impl<A>(module: &Module<B>, k: i64, a: &mut A, a_col: usize)
where
A: ScalarZnxToMut,
{
let mut a: ScalarZnx<&mut [u8]> = a.to_mut();
#[cfg(debug_assertions)]
{
assert_eq!(a.n(), module.n());
}
unsafe {
vec_znx::vec_znx_automorphism(
module.ptr() as *const module_info_t,
k,
a.at_mut_ptr(a_col, 0),
a.size() as u64,
a.sl() as u64,
a.at_ptr(a_col, 0),
a.size() as u64,
a.sl() as u64,
)
}
} }
} }

View File

@@ -37,7 +37,7 @@ where
B: CPUAVX, B: CPUAVX,
{ {
fn vec_znx_alloc_impl(n: usize, cols: usize, size: usize) -> VecZnxOwned { fn vec_znx_alloc_impl(n: usize, cols: usize, size: usize) -> VecZnxOwned {
VecZnxOwned::new::<i64>(n, cols, size) VecZnxOwned::alloc::<i64>(n, cols, size)
} }
} }

View File

@@ -12,6 +12,7 @@ sampling = {path="../sampling"}
rand_distr = {workspace = true} rand_distr = {workspace = true}
itertools = {workspace = true} itertools = {workspace = true}
byteorder = {workspace = true} byteorder = {workspace = true}
rand_core = {workspace = true}
[[bench]] [[bench]]
name = "external_product_glwe_fft64" name = "external_product_glwe_fft64"

View File

@@ -75,7 +75,7 @@ impl LookUpTable {
let size: usize = self.k.div_ceil(self.basek); let size: usize = self.k.div_ceil(self.basek);
// Equivalent to AUTO([f(0), -f(n-1), -f(n-2), ..., -f(1)], -1) // Equivalent to AUTO([f(0), -f(n-1), -f(n-2), ..., -f(1)], -1)
let mut lut_full: VecZnx<Vec<u8>> = VecZnx::new::<i64>(domain_size, 1, size); let mut lut_full: VecZnx<Vec<u8>> = VecZnx::alloc::<i64>(domain_size, 1, size);
let lut_at: &mut [i64] = lut_full.at_mut(0, limbs - 1); let lut_at: &mut [i64] = lut_full.at_mut(0, limbs - 1);

View File

@@ -1,187 +0,0 @@
use backend::hal::{
api::{MatZnxAlloc, MatZnxAllocBytes},
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, Scratch, VmpPMat, WriterTo},
};
use crate::{GGLWEExecLayoutFamily, GLWECiphertext, GLWESwitchingKey, GLWESwitchingKeyExec, Infos};
#[derive(PartialEq, Eq)]
pub struct AutomorphismKey<D: Data> {
pub(crate) key: GLWESwitchingKey<D>,
pub(crate) p: i64,
}
impl AutomorphismKey<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: MatZnxAlloc,
{
AutomorphismKey {
key: GLWESwitchingKey::alloc(module, basek, k, rows, digits, rank, rank),
p: 0,
}
}
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: MatZnxAllocBytes,
{
GLWESwitchingKey::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, rank, rank)
}
}
impl<D: Data> Infos for AutomorphismKey<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.key.inner()
}
fn basek(&self) -> usize {
self.key.basek()
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data> AutomorphismKey<D> {
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<D: DataRef> AutomorphismKey<D> {
pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
self.key.at(row, col)
}
}
impl<D: DataMut> AutomorphismKey<D> {
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertext<&mut [u8]> {
self.key.at_mut(row, col)
}
}
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
impl<D: DataMut> ReaderFrom for AutomorphismKey<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.p = reader.read_u64::<LittleEndian>()? as i64;
self.key.read_from(reader)
}
}
impl<D: DataRef> WriterTo for AutomorphismKey<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.p as u64)?;
self.key.write_to(writer)
}
}
#[derive(PartialEq, Eq)]
pub struct AutomorphismKeyExec<D: Data, B: Backend> {
pub(crate) key: GLWESwitchingKeyExec<D, B>,
pub(crate) p: i64,
}
impl<B: Backend> AutomorphismKeyExec<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
AutomorphismKeyExec::<Vec<u8>, B> {
key: GLWESwitchingKeyExec::alloc(module, basek, k, rows, digits, rank, rank),
p: 0,
}
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
GLWESwitchingKeyExec::<Vec<u8>, B>::bytes_of(module, basek, k, rows, digits, rank, rank)
}
pub fn from<DataOther: DataRef>(module: &Module<B>, other: &AutomorphismKey<DataOther>, scratch: &mut Scratch<B>) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let mut atk_exec: AutomorphismKeyExec<Vec<u8>, B> = Self::alloc(
module,
other.basek(),
other.k(),
other.rows(),
other.digits(),
other.rank(),
);
atk_exec.prepare(module, other, scratch);
atk_exec
}
}
impl<D: DataMut, B: Backend> AutomorphismKeyExec<D, B> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &AutomorphismKey<DataOther>, scratch: &mut Scratch<B>)
where
DataOther: DataRef,
Module<B>: GGLWEExecLayoutFamily<B>,
{
self.key.prepare(module, &other.key, scratch);
self.p = other.p;
}
}
impl<D: Data, B: Backend> Infos for AutomorphismKeyExec<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
&self.key.inner()
}
fn basek(&self) -> usize {
self.key.basek()
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data, B: Backend> AutomorphismKeyExec<D, 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()
}
}

View File

@@ -1,7 +1,7 @@
use backend::hal::{ use backend::hal::{
api::{ api::{
ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScratchAvailable, SvpApply, TakeScalarZnx, TakeVecZnx, TakeVecZnxBig, ScalarZnxAllocBytes, ScratchAvailable, SvpApply, TakeScalarZnx, TakeVecZnx, TakeVecZnxBig, TakeVecZnxDft,
TakeVecZnxDft, VecZnxAddScalarInplace, VecZnxAllocBytes, VecZnxBigAllocBytes, VecZnxDftToVecZnxBigTmpA, VecZnxAddScalarInplace, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxBigAllocBytes, VecZnxDftToVecZnxBigTmpA,
VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSwithcDegree, ZnxZero, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSwithcDegree, ZnxZero,
}, },
layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, Scratch}, layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, Scratch},
@@ -9,8 +9,10 @@ use backend::hal::{
use sampling::source::Source; use sampling::source::Source;
use crate::{ use crate::{
AutomorphismKey, GGLWECiphertext, GLWECiphertext, GLWEDecryptFamily, GLWEEncryptSkFamily, GLWEPlaintext, GLWESecret, AutomorphismKey, AutomorphismKeyCompressed, GGLWECiphertext, GGLWECiphertextCompressed, GLWECiphertext, GLWEDecryptFamily,
GLWESecretExec, GLWESecretFamily, GLWESwitchingKey, GLWETensorKey, Infos, TakeGLWEPt, TakeGLWESecret, TakeGLWESecretExec, GLWEEncryptSkFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, GLWESecretFamily, GLWESwitchingKey,
GLWESwitchingKeyCompressed, GLWETensorKey, GLWETensorKeyCompressed, Infos, TakeGLWEPt, TakeGLWESecret, TakeGLWESecretExec,
encrypt_sk_internal,
}; };
pub trait GGLWEEncryptSkFamily<B: Backend> = GLWEEncryptSkFamily<B> + GLWESecretFamily<B>; pub trait GGLWEEncryptSkFamily<B: Backend> = GLWEEncryptSkFamily<B> + GLWESecretFamily<B>;
@@ -122,6 +124,112 @@ impl<DataSelf: DataMut> GGLWECiphertext<DataSelf> {
} }
} }
impl GGLWECiphertextCompressed<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize) -> usize
where
Module<B>: GLWESwitchingKeyEncryptSkFamily<B> + VecZnxAllocBytes,
{
GGLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
}
}
impl<D: DataMut> GGLWECiphertextCompressed<D> {
pub fn encrypt_sk<DataPt: DataRef, DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: &ScalarZnx<DataPt>,
sk: &GLWESecretExec<DataSk, B>,
seed: [u8; 32],
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch<B>,
) where
Module<B>: GGLWEEncryptSkFamily<B> + VecZnxAllocBytes + VecZnxAddScalarInplace,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx<B>,
{
#[cfg(debug_assertions)]
{
use backend::hal::api::ZnxInfos;
assert_eq!(
self.rank_in(),
pt.cols(),
"self.rank_in(): {} != pt.cols(): {}",
self.rank_in(),
pt.cols()
);
assert_eq!(
self.rank_out(),
sk.rank(),
"self.rank_out(): {} != sk.rank(): {}",
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() >= GGLWECiphertextCompressed::encrypt_sk_scratch_space(module, self.basek(), self.k()),
"scratch.available: {} < GGLWECiphertext::encrypt_sk_scratch_space(module, self.rank()={}, self.size()={}): {}",
scratch.available(),
self.rank(),
self.size(),
GGLWECiphertextCompressed::encrypt_sk_scratch_space(module, self.basek(), self.k())
);
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 mut source_xa = Source::new(seed);
let (mut tmp_pt, scrach_1) = scratch.take_glwe_pt(module, basek, k);
(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, scrach_1);
let (seed, mut source_xa_tmp) = source_xa.branch();
self.seed[col_i * rows + row_i] = seed;
encrypt_sk_internal(
module,
self.basek(),
self.k(),
&mut self.at_mut(row_i, col_i).data,
true,
Some((&tmp_pt, 0)),
sk,
&mut source_xa_tmp,
source_xe,
sigma,
scrach_1,
);
});
});
}
}
pub trait GLWESwitchingKeyEncryptSkFamily<B: Backend> = GGLWEEncryptSkFamily<B>; pub trait GLWESwitchingKeyEncryptSkFamily<B: Backend> = GGLWEEncryptSkFamily<B>;
impl GLWESwitchingKey<Vec<u8>> { impl GLWESwitchingKey<Vec<u8>> {
@@ -197,14 +305,19 @@ impl<DataSelf: DataMut> GLWESwitchingKey<DataSelf> {
let (mut sk_in_tmp, scratch1) = scratch.take_scalar_znx(module, sk_in.rank()); let (mut sk_in_tmp, scratch1) = scratch.take_scalar_znx(module, sk_in.rank());
(0..sk_in.rank()).for_each(|i| { (0..sk_in.rank()).for_each(|i| {
module.vec_znx_switch_degree(&mut sk_in_tmp, i, &sk_in.data, i); module.vec_znx_switch_degree(
&mut sk_in_tmp.as_vec_znx_mut(),
i,
&sk_in.data.as_vec_znx(),
i,
);
}); });
let (mut sk_out_tmp, scratch2) = scratch1.take_glwe_secret_exec(module, sk_out.rank()); let (mut sk_out_tmp, scratch2) = scratch1.take_glwe_secret_exec(module, sk_out.rank());
{ {
let (mut tmp, _) = scratch2.take_scalar_znx(module, 1); let (mut tmp, _) = scratch2.take_scalar_znx(module, 1);
(0..sk_out.rank()).for_each(|i| { (0..sk_out.rank()).for_each(|i| {
module.vec_znx_switch_degree(&mut tmp, 0, &sk_out.data, i); module.vec_znx_switch_degree(&mut tmp.as_vec_znx_mut(), 0, &sk_out.data.as_vec_znx(), i);
module.svp_prepare(&mut sk_out_tmp.data, i, &tmp, 0); module.svp_prepare(&mut sk_out_tmp.data, i, &tmp, 0);
}); });
} }
@@ -223,6 +336,100 @@ impl<DataSelf: DataMut> GLWESwitchingKey<DataSelf> {
} }
} }
impl GLWESwitchingKeyCompressed<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
rank_in: usize,
rank_out: usize,
) -> usize
where
Module<B>: GLWESwitchingKeyEncryptSkFamily<B> + ScalarZnxAllocBytes + VecZnxAllocBytes,
{
(GGLWECiphertext::encrypt_sk_scratch_space(module, basek, k) | module.scalar_znx_alloc_bytes(1))
+ module.scalar_znx_alloc_bytes(rank_in)
+ GLWESecretExec::bytes_of(module, rank_out)
}
}
impl<DataSelf: DataMut> GLWESwitchingKeyCompressed<DataSelf> {
pub fn encrypt_sk<DataSkIn: DataRef, DataSkOut: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
sk_in: &GLWESecret<DataSkIn>,
sk_out: &GLWESecret<DataSkOut>,
seed_xa: [u8; 32],
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWESwitchingKeyEncryptSkFamily<B>
+ ScalarZnxAllocBytes
+ VecZnxSwithcDegree
+ VecZnxAllocBytes
+ VecZnxAddScalarInplace,
Scratch<B>:
ScratchAvailable + TakeScalarZnx<B> + TakeVecZnxDft<B> + TakeGLWESecretExec<B> + ScratchAvailable + TakeVecZnx<B>,
{
#[cfg(debug_assertions)]
{
assert!(sk_in.n() <= module.n());
assert!(sk_out.n() <= module.n());
assert!(
scratch.available()
>= GLWESwitchingKey::encrypt_sk_scratch_space(
module,
self.basek(),
self.k(),
self.rank_in(),
self.rank_out()
),
"scratch.available()={} < GLWESwitchingKey::encrypt_sk_scratch_space={}",
scratch.available(),
GLWESwitchingKey::encrypt_sk_scratch_space(
module,
self.basek(),
self.k(),
self.rank_in(),
self.rank_out()
)
)
}
let (mut sk_in_tmp, scratch1) = scratch.take_scalar_znx(module, sk_in.rank());
(0..sk_in.rank()).for_each(|i| {
module.vec_znx_switch_degree(
&mut sk_in_tmp.as_vec_znx_mut(),
i,
&sk_in.data.as_vec_znx(),
i,
);
});
let (mut sk_out_tmp, scratch2) = scratch1.take_glwe_secret_exec(module, sk_out.rank());
{
let (mut tmp, _) = scratch2.take_scalar_znx(module, 1);
(0..sk_out.rank()).for_each(|i| {
module.vec_znx_switch_degree(&mut tmp.as_vec_znx_mut(), 0, &sk_out.data.as_vec_znx(), i);
module.svp_prepare(&mut sk_out_tmp.data, i, &tmp, 0);
});
}
self.key.encrypt_sk(
module,
&sk_in_tmp,
&sk_out_tmp,
seed_xa,
source_xe,
sigma,
scratch2,
);
self.sk_in_n = sk_in.n();
self.sk_out_n = sk_out.n();
}
}
pub trait AutomorphismKeyEncryptSkFamily<B: Backend> = GGLWEEncryptSkFamily<B>; pub trait AutomorphismKeyEncryptSkFamily<B: Backend> = GGLWEEncryptSkFamily<B>;
impl AutomorphismKey<Vec<u8>> { impl AutomorphismKey<Vec<u8>> {
@@ -250,9 +457,9 @@ impl<DataSelf: DataMut> AutomorphismKey<DataSelf> {
scratch: &mut Scratch<B>, scratch: &mut Scratch<B>,
) where ) where
Module<B>: AutomorphismKeyEncryptSkFamily<B> Module<B>: AutomorphismKeyEncryptSkFamily<B>
+ ScalarZnxAutomorphism
+ ScalarZnxAllocBytes + ScalarZnxAllocBytes
+ VecZnxAllocBytes + VecZnxAllocBytes
+ VecZnxAutomorphism
+ VecZnxSwithcDegree + VecZnxSwithcDegree
+ VecZnxAddScalarInplace, + VecZnxAddScalarInplace,
Scratch<B>: ScratchAvailable + TakeScalarZnx<B> + TakeVecZnxDft<B> + TakeGLWESecretExec<B> + TakeVecZnx<B>, Scratch<B>: ScratchAvailable + TakeScalarZnx<B> + TakeVecZnxDft<B> + TakeGLWESecretExec<B> + TakeVecZnx<B>,
@@ -277,11 +484,11 @@ impl<DataSelf: DataMut> AutomorphismKey<DataSelf> {
{ {
(0..self.rank()).for_each(|i| { (0..self.rank()).for_each(|i| {
module.scalar_znx_automorphism( module.vec_znx_automorphism(
module.galois_element_inv(p), module.galois_element_inv(p),
&mut sk_out.data, &mut sk_out.data.as_vec_znx_mut(),
i, i,
&sk.data, &sk.data.as_vec_znx(),
i, i,
); );
}); });
@@ -294,6 +501,72 @@ impl<DataSelf: DataMut> AutomorphismKey<DataSelf> {
} }
} }
impl AutomorphismKeyCompressed<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize
where
Module<B>: AutomorphismKeyEncryptSkFamily<B> + ScalarZnxAllocBytes + VecZnxAllocBytes,
{
GLWESwitchingKeyCompressed::encrypt_sk_scratch_space(module, basek, k, rank, rank) + GLWESecret::bytes_of(module, rank)
}
}
impl<DataSelf: DataMut> AutomorphismKeyCompressed<DataSelf> {
pub fn encrypt_sk<DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
p: i64,
sk: &GLWESecret<DataSk>,
seed_xa: [u8; 32],
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch<B>,
) where
Module<B>: AutomorphismKeyEncryptSkFamily<B>
+ ScalarZnxAllocBytes
+ VecZnxAllocBytes
+ VecZnxSwithcDegree
+ VecZnxAutomorphism
+ VecZnxAddScalarInplace,
Scratch<B>: ScratchAvailable + TakeScalarZnx<B> + TakeVecZnxDft<B> + TakeGLWESecretExec<B> + TakeVecZnx<B>,
{
#[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()
>= AutomorphismKeyCompressed::encrypt_sk_scratch_space(module, self.basek(), self.k(), self.rank()),
"scratch.available(): {} < AutomorphismKey::encrypt_sk_scratch_space(module, self.rank()={}, self.size()={}): {}",
scratch.available(),
self.rank(),
self.size(),
AutomorphismKeyCompressed::encrypt_sk_scratch_space(module, self.basek(), self.k(), self.rank())
)
}
let (mut sk_out, scratch_1) = scratch.take_glwe_secret(module, sk.rank());
{
(0..self.rank()).for_each(|i| {
module.vec_znx_automorphism(
module.galois_element_inv(p),
&mut sk_out.data.as_vec_znx_mut(),
i,
&sk.data.as_vec_znx(),
i,
);
});
}
self.key
.encrypt_sk(module, &sk, &sk_out, seed_xa, source_xe, sigma, scratch_1);
self.p = p;
}
}
pub trait GLWETensorKeyEncryptSkFamily<B: Backend> = pub trait GLWETensorKeyEncryptSkFamily<B: Backend> =
GGLWEEncryptSkFamily<B> + VecZnxBigAllocBytes + VecZnxDftToVecZnxBigTmpA<B> + SvpApply<B>; GGLWEEncryptSkFamily<B> + VecZnxBigAllocBytes + VecZnxDftToVecZnxBigTmpA<B> + SvpApply<B>;
@@ -344,7 +617,7 @@ impl<DataSelf: DataMut> GLWETensorKey<DataSelf> {
let (mut sk_dft, scratch2) = scratch1.take_vec_znx_dft(module, rank, 1); let (mut sk_dft, scratch2) = scratch1.take_vec_znx_dft(module, rank, 1);
(0..rank).for_each(|i| { (0..rank).for_each(|i| {
module.vec_znx_dft_from_vec_znx(1, 0, &mut sk_dft, i, &sk.data, i); module.vec_znx_dft_from_vec_znx(1, 0, &mut sk_dft, i, &sk.data.as_vec_znx(), i);
}); });
let (mut sk_ij_big, scratch3) = scratch2.take_vec_znx_big(module, 1, 1); let (mut sk_ij_big, scratch3) = scratch2.take_vec_znx_big(module, 1, 1);
@@ -356,7 +629,14 @@ impl<DataSelf: DataMut> GLWETensorKey<DataSelf> {
module.svp_apply(&mut sk_ij_dft, 0, &sk_dft_prep.data, j, &sk_dft, i); module.svp_apply(&mut sk_ij_dft, 0, &sk_dft_prep.data, j, &sk_dft, i);
module.vec_znx_dft_to_vec_znx_big_tmp_a(&mut sk_ij_big, 0, &mut sk_ij_dft, 0); module.vec_znx_dft_to_vec_znx_big_tmp_a(&mut sk_ij_big, 0, &mut sk_ij_dft, 0);
module.vec_znx_big_normalize(self.basek(), &mut sk_ij.data, 0, &sk_ij_big, 0, scratch5); module.vec_znx_big_normalize(
self.basek(),
&mut sk_ij.data.as_vec_znx_mut(),
0,
&sk_ij_big,
0,
scratch5,
);
self.at_mut(i, j) self.at_mut(i, j)
.encrypt_sk(module, &sk_ij, sk, source_xa, source_xe, sigma, scratch5); .encrypt_sk(module, &sk_ij, sk, source_xa, source_xe, sigma, scratch5);
@@ -364,3 +644,77 @@ impl<DataSelf: DataMut> GLWETensorKey<DataSelf> {
}) })
} }
} }
impl GLWETensorKeyCompressed<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> usize
where
Module<B>: GLWETensorKeyEncryptSkFamily<B> + ScalarZnxAllocBytes + VecZnxAllocBytes,
{
GLWETensorKey::encrypt_sk_scratch_space(module, basek, k, rank)
}
}
impl<DataSelf: DataMut> GLWETensorKeyCompressed<DataSelf> {
pub fn encrypt_sk<DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
sk: &GLWESecret<DataSk>,
seed_xa: [u8; 32],
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWETensorKeyEncryptSkFamily<B>
+ ScalarZnxAllocBytes
+ VecZnxSwithcDegree
+ VecZnxAllocBytes
+ VecZnxAddScalarInplace,
Scratch<B>:
ScratchAvailable + TakeVecZnxDft<B> + TakeVecZnxBig<B> + TakeGLWESecretExec<B> + TakeScalarZnx<B> + TakeVecZnx<B>,
{
#[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_dft_prep, scratch1) = scratch.take_glwe_secret_exec(module, rank);
sk_dft_prep.prepare(module, &sk);
let (mut sk_dft, scratch2) = scratch1.take_vec_znx_dft(module, rank, 1);
(0..rank).for_each(|i| {
module.vec_znx_dft_from_vec_znx(1, 0, &mut sk_dft, i, &sk.data.as_vec_znx(), i);
});
let (mut sk_ij_big, scratch3) = scratch2.take_vec_znx_big(module, 1, 1);
let (mut sk_ij, scratch4) = scratch3.take_glwe_secret(module, 1);
let (mut sk_ij_dft, scratch5) = scratch4.take_vec_znx_dft(module, 1, 1);
let mut source_xa: Source = Source::new(seed_xa);
(0..rank).for_each(|i| {
(i..rank).for_each(|j| {
module.svp_apply(&mut sk_ij_dft, 0, &sk_dft_prep.data, j, &sk_dft, i);
module.vec_znx_dft_to_vec_znx_big_tmp_a(&mut sk_ij_big, 0, &mut sk_ij_dft, 0);
module.vec_znx_big_normalize(
self.basek(),
&mut sk_ij.data.as_vec_znx_mut(),
0,
&sk_ij_big,
0,
scratch5,
);
let (seed_xa_tmp, _) = source_xa.branch();
self.at_mut(i, j)
.encrypt_sk(module, &sk_ij, sk, seed_xa_tmp, source_xe, sigma, scratch5);
});
})
}
}

View File

@@ -1,227 +0,0 @@
use backend::hal::{
api::{MatZnxAlloc, MatZnxAllocBytes},
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, Scratch, VmpPMat, WriterTo},
};
use crate::{GGLWECiphertext, GGLWECiphertextExec, GGLWEExecLayoutFamily, GLWECiphertext, Infos};
#[derive(PartialEq, Eq)]
pub struct GLWESwitchingKey<D: Data> {
pub(crate) key: GGLWECiphertext<D>,
pub(crate) sk_in_n: usize, // Degree of sk_in
pub(crate) sk_out_n: usize, // Degree of sk_out
}
impl GLWESwitchingKey<Vec<u8>> {
pub fn alloc<B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> Self
where
Module<B>: MatZnxAlloc,
{
GLWESwitchingKey {
key: GGLWECiphertext::alloc(module, basek, k, rows, digits, rank_in, rank_out),
sk_in_n: 0,
sk_out_n: 0,
}
}
pub fn bytes_of<B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize
where
Module<B>: MatZnxAllocBytes,
{
GGLWECiphertext::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out)
}
}
impl<D: Data> Infos for GLWESwitchingKey<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
self.key.inner()
}
fn basek(&self) -> usize {
self.key.basek()
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data> GLWESwitchingKey<D> {
pub fn rank(&self) -> usize {
self.key.data.cols_out() - 1
}
pub fn rank_in(&self) -> usize {
self.key.data.cols_in()
}
pub fn rank_out(&self) -> usize {
self.key.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.key.digits()
}
pub fn sk_degree_in(&self) -> usize {
self.sk_in_n
}
pub fn sk_degree_out(&self) -> usize {
self.sk_out_n
}
}
impl<D: DataRef> GLWESwitchingKey<D> {
pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
self.key.at(row, col)
}
}
impl<D: DataMut> GLWESwitchingKey<D> {
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertext<&mut [u8]> {
self.key.at_mut(row, col)
}
}
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
impl<D: DataMut> ReaderFrom for GLWESwitchingKey<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.sk_in_n = reader.read_u64::<LittleEndian>()? as usize;
self.sk_out_n = reader.read_u64::<LittleEndian>()? as usize;
self.key.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GLWESwitchingKey<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.sk_in_n as u64)?;
writer.write_u64::<LittleEndian>(self.sk_out_n as u64)?;
self.key.write_to(writer)
}
}
#[derive(PartialEq, Eq)]
pub struct GLWESwitchingKeyExec<D: Data, B: Backend> {
pub(crate) key: GGLWECiphertextExec<D, B>,
pub(crate) sk_in_n: usize, // Degree of sk_in
pub(crate) sk_out_n: usize, // Degree of sk_out
}
impl<B: Backend> GLWESwitchingKeyExec<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
GLWESwitchingKeyExec::<Vec<u8>, B> {
key: GGLWECiphertextExec::alloc(module, basek, k, rows, digits, rank_in, rank_out),
sk_in_n: 0,
sk_out_n: 0,
}
}
pub fn bytes_of(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
GGLWECiphertextExec::bytes_of(module, basek, k, rows, digits, rank_in, rank_out)
}
pub fn from<DataOther: DataRef>(module: &Module<B>, other: &GLWESwitchingKey<DataOther>, scratch: &mut Scratch<B>) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let mut ksk_exec: GLWESwitchingKeyExec<Vec<u8>, B> = Self::alloc(
module,
other.basek(),
other.k(),
other.rows(),
other.digits(),
other.rank_in(),
other.rank_out(),
);
ksk_exec.prepare(module, other, scratch);
ksk_exec
}
}
impl<D: Data, B: Backend> Infos for GLWESwitchingKeyExec<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
self.key.inner()
}
fn basek(&self) -> usize {
self.key.basek()
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data, B: Backend> GLWESwitchingKeyExec<D, B> {
pub fn rank(&self) -> usize {
self.key.data.cols_out() - 1
}
pub fn rank_in(&self) -> usize {
self.key.data.cols_in()
}
pub fn rank_out(&self) -> usize {
self.key.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.key.digits()
}
pub fn sk_degree_in(&self) -> usize {
self.sk_in_n
}
pub fn sk_degree_out(&self) -> usize {
self.sk_out_n
}
}
impl<D: DataMut, B: Backend> GLWESwitchingKeyExec<D, B> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &GLWESwitchingKey<DataOther>, scratch: &mut Scratch<B>)
where
DataOther: DataRef,
Module<B>: GGLWEExecLayoutFamily<B>,
{
self.key.prepare(module, &other.key, scratch);
self.sk_in_n = other.sk_in_n;
self.sk_out_n = other.sk_out_n;
}
}

View File

@@ -1,9 +1,10 @@
use backend::hal::{ use backend::hal::{
api::{MatZnxAlloc, MatZnxAllocBytes, VmpPMatAlloc, VmpPMatAllocBytes, VmpPMatPrepare}, api::{MatZnxAlloc, MatZnxAllocBytes, VmpPMatAlloc, VmpPMatAllocBytes, VmpPMatPrepare},
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, Scratch, VmpPMat, WriterTo}, layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, WriterTo},
}; };
use crate::{GLWECiphertext, Infos}; use crate::{GLWECiphertext, Infos};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
pub trait GGLWEExecLayoutFamily<B: Backend> = VmpPMatAlloc<B> + VmpPMatAllocBytes + VmpPMatPrepare<B>; pub trait GGLWEExecLayoutFamily<B: Backend> = VmpPMatAlloc<B> + VmpPMatAllocBytes + VmpPMatPrepare<B>;
@@ -138,8 +139,6 @@ impl<D: Data> GGLWECiphertext<D> {
} }
} }
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
impl<D: DataMut> ReaderFrom for GGLWECiphertext<D> { impl<D: DataMut> ReaderFrom for GGLWECiphertext<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> { fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.k = reader.read_u64::<LittleEndian>()? as usize; self.k = reader.read_u64::<LittleEndian>()? as usize;
@@ -159,43 +158,33 @@ impl<D: DataRef> WriterTo for GGLWECiphertext<D> {
} }
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
pub struct GGLWECiphertextExec<D: Data, B: Backend> { pub struct GLWESwitchingKey<D: Data> {
pub(crate) data: VmpPMat<D, B>, pub(crate) key: GGLWECiphertext<D>,
pub(crate) basek: usize, pub(crate) sk_in_n: usize, // Degree of sk_in
pub(crate) k: usize, pub(crate) sk_out_n: usize, // Degree of sk_out
pub(crate) digits: usize,
} }
impl<B: Backend> GGLWECiphertextExec<Vec<u8>, B> { impl GLWESwitchingKey<Vec<u8>> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self pub fn alloc<B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> Self
where where
Module<B>: GGLWEExecLayoutFamily<B>, Module<B>: MatZnxAlloc,
{ {
let size: usize = k.div_ceil(basek); GLWESwitchingKey {
debug_assert!( key: GGLWECiphertext::alloc(module, basek, k, rows, digits, rank_in, rank_out),
size > digits, sk_in_n: 0,
"invalid gglwe: ceil(k/basek): {} <= digits: {}", sk_out_n: 0,
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
Self {
data: module.vmp_pmat_alloc(rows, rank_in, rank_out + 1, size),
basek: basek,
k,
digits,
} }
} }
pub fn bytes_of( pub fn bytes_of<B: Backend>(
module: &Module<B>, module: &Module<B>,
basek: usize, basek: usize,
k: usize, k: usize,
@@ -205,71 +194,278 @@ impl<B: Backend> GGLWECiphertextExec<Vec<u8>, B> {
rank_out: usize, rank_out: usize,
) -> usize ) -> usize
where where
Module<B>: GGLWEExecLayoutFamily<B>, Module<B>: MatZnxAllocBytes,
{ {
let size: usize = k.div_ceil(basek); GGLWECiphertext::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out)
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.vmp_pmat_alloc_bytes(rows, rank_in, rank_out + 1, rows)
} }
} }
impl<D: Data, B: Backend> Infos for GGLWECiphertextExec<D, B> { impl<D: Data> Infos for GLWESwitchingKey<D> {
type Inner = VmpPMat<D, B>; type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner { fn inner(&self) -> &Self::Inner {
&self.data self.key.inner()
} }
fn basek(&self) -> usize { fn basek(&self) -> usize {
self.basek self.key.basek()
} }
fn k(&self) -> usize { fn k(&self) -> usize {
self.k self.key.k()
} }
} }
impl<D: Data, B: Backend> GGLWECiphertextExec<D, B> { impl<D: Data> GLWESwitchingKey<D> {
pub fn rank(&self) -> usize { pub fn rank(&self) -> usize {
self.data.cols_out() - 1 self.key.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.digits
} }
pub fn rank_in(&self) -> usize { pub fn rank_in(&self) -> usize {
self.data.cols_in() self.key.data.cols_in()
} }
pub fn rank_out(&self) -> usize { pub fn rank_out(&self) -> usize {
self.data.cols_out() - 1 self.key.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.key.digits()
}
pub fn sk_degree_in(&self) -> usize {
self.sk_in_n
}
pub fn sk_degree_out(&self) -> usize {
self.sk_out_n
} }
} }
impl<D: DataMut, B: Backend> GGLWECiphertextExec<D, B> { impl<D: DataRef> GLWESwitchingKey<D> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &GGLWECiphertext<DataOther>, scratch: &mut Scratch<B>) pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
where self.key.at(row, col)
DataOther: DataRef, }
Module<B>: GGLWEExecLayoutFamily<B>, }
{
module.vmp_prepare(&mut self.data, &other.data, scratch); impl<D: DataMut> GLWESwitchingKey<D> {
self.basek = other.basek; pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertext<&mut [u8]> {
self.k = other.k; self.key.at_mut(row, col)
self.digits = other.digits; }
}
impl<D: DataMut> ReaderFrom for GLWESwitchingKey<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.sk_in_n = reader.read_u64::<LittleEndian>()? as usize;
self.sk_out_n = reader.read_u64::<LittleEndian>()? as usize;
self.key.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GLWESwitchingKey<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.sk_in_n as u64)?;
writer.write_u64::<LittleEndian>(self.sk_out_n as u64)?;
self.key.write_to(writer)
}
}
#[derive(PartialEq, Eq)]
pub struct AutomorphismKey<D: Data> {
pub(crate) key: GLWESwitchingKey<D>,
pub(crate) p: i64,
}
impl AutomorphismKey<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: MatZnxAlloc,
{
AutomorphismKey {
key: GLWESwitchingKey::alloc(module, basek, k, rows, digits, rank, rank),
p: 0,
}
}
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: MatZnxAllocBytes,
{
GLWESwitchingKey::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, rank, rank)
}
}
impl<D: Data> Infos for AutomorphismKey<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.key.inner()
}
fn basek(&self) -> usize {
self.key.basek()
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data> AutomorphismKey<D> {
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<D: DataRef> AutomorphismKey<D> {
pub fn at(&self, row: usize, col: usize) -> GLWECiphertext<&[u8]> {
self.key.at(row, col)
}
}
impl<D: DataMut> AutomorphismKey<D> {
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertext<&mut [u8]> {
self.key.at_mut(row, col)
}
}
impl<D: DataMut> ReaderFrom for AutomorphismKey<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.p = reader.read_u64::<LittleEndian>()? as i64;
self.key.read_from(reader)
}
}
impl<D: DataRef> WriterTo for AutomorphismKey<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.p as u64)?;
self.key.write_to(writer)
}
}
#[derive(PartialEq, Eq)]
pub struct GLWETensorKey<D: Data> {
pub(crate) keys: Vec<GLWESwitchingKey<D>>,
}
impl GLWETensorKey<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: MatZnxAlloc,
{
let mut keys: Vec<GLWESwitchingKey<Vec<u8>>> = Vec::new();
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
(0..pairs).for_each(|_| {
keys.push(GLWESwitchingKey::alloc(
module, basek, k, rows, digits, 1, rank,
));
});
Self { keys: keys }
}
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: MatZnxAllocBytes,
{
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
pairs * GLWESwitchingKey::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, 1, rank)
}
}
impl<D: Data> Infos for GLWETensorKey<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.keys[0].inner()
}
fn basek(&self) -> usize {
self.keys[0].basek()
}
fn k(&self) -> usize {
self.keys[0].k()
}
}
impl<D: Data> GLWETensorKey<D> {
pub fn rank(&self) -> usize {
self.keys[0].rank()
}
pub fn rank_in(&self) -> usize {
self.keys[0].rank_in()
}
pub fn rank_out(&self) -> usize {
self.keys[0].rank_out()
}
pub fn digits(&self) -> usize {
self.keys[0].digits()
}
}
impl<D: DataMut> GLWETensorKey<D> {
// 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<D> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
&mut self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
impl<D: DataRef> GLWETensorKey<D> {
// Returns a reference to GLWESwitchingKey_{s}(s[i] * s[j])
pub fn at(&self, mut i: usize, mut j: usize) -> &GLWESwitchingKey<D> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
&self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
impl<D: DataMut> ReaderFrom for GLWETensorKey<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
let len: usize = reader.read_u64::<LittleEndian>()? as usize;
if self.keys.len() != len {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("self.keys.len()={} != read len={}", self.keys.len(), len),
));
}
for key in &mut self.keys {
key.read_from(reader)?;
}
Ok(())
}
}
impl<D: DataRef> WriterTo for GLWETensorKey<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.keys.len() as u64)?;
for key in &self.keys {
key.write_to(writer)?;
}
Ok(())
} }
} }

View File

@@ -0,0 +1,535 @@
use backend::hal::{
api::{MatZnxAlloc, MatZnxAllocBytes, VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, WriterTo},
};
use crate::{AutomorphismKey, GGLWECiphertext, GLWECiphertextCompressed, GLWESwitchingKey, GLWETensorKey, Infos};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
#[derive(PartialEq, Eq)]
pub struct GGLWECiphertextCompressed<D: Data> {
pub(crate) data: MatZnx<D>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) rank_out: usize,
pub(crate) digits: usize,
pub(crate) seed: Vec<[u8; 32]>,
}
impl GGLWECiphertextCompressed<Vec<u8>> {
pub fn alloc<B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> Self
where
Module<B>: MatZnxAlloc,
{
let size: usize = k.div_ceil(basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
Self {
data: module.mat_znx_alloc(rows, rank_in, 1, size),
basek: basek,
k,
rank_out,
digits,
seed: vec![[0u8; 32]; rows * rank_in],
}
}
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize) -> usize
where
Module<B>: MatZnxAllocBytes,
{
let size: usize = k.div_ceil(basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
module.mat_znx_alloc_bytes(rows, rank_in, 1, rows)
}
}
impl<D: Data> Infos for GGLWECiphertextCompressed<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<D: Data> GGLWECiphertextCompressed<D> {
pub fn rank(&self) -> usize {
self.rank_out
}
pub fn digits(&self) -> usize {
self.digits
}
pub fn rank_in(&self) -> usize {
self.data.cols_in()
}
pub fn rank_out(&self) -> usize {
self.rank_out
}
}
impl<D: DataRef> GGLWECiphertextCompressed<D> {
pub(crate) fn at(&self, row: usize, col: usize) -> GLWECiphertextCompressed<&[u8]> {
GLWECiphertextCompressed {
data: self.data.at(row, col),
basek: self.basek,
k: self.k,
rank: self.rank_out,
seed: self.seed[self.rank_in() * row + col],
}
}
}
impl<D: DataMut> GGLWECiphertextCompressed<D> {
pub(crate) fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertextCompressed<&mut [u8]> {
let rank_in: usize = self.rank_in();
GLWECiphertextCompressed {
data: self.data.at_mut(row, col),
basek: self.basek,
k: self.k,
rank: self.rank_out,
seed: self.seed[rank_in * row + col], // Warning: value is copied and not borrow mut
}
}
}
impl<D: DataMut> ReaderFrom for GGLWECiphertextCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.k = reader.read_u64::<LittleEndian>()? as usize;
self.basek = reader.read_u64::<LittleEndian>()? as usize;
self.digits = reader.read_u64::<LittleEndian>()? as usize;
self.rank_out = reader.read_u64::<LittleEndian>()? as usize;
let seed_len = reader.read_u64::<LittleEndian>()? as usize;
if seed_len != self.seed.len() {
} else {
for s in &mut self.seed {
reader.read_exact(s)?;
}
}
self.data.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GGLWECiphertextCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.k as u64)?;
writer.write_u64::<LittleEndian>(self.basek as u64)?;
writer.write_u64::<LittleEndian>(self.digits as u64)?;
writer.write_u64::<LittleEndian>(self.rank_out as u64)?;
writer.write_u64::<LittleEndian>(self.seed.len() as u64)?;
for s in &self.seed {
writer.write_all(s)?;
}
self.data.write_to(writer)
}
}
impl<D: DataMut> GGLWECiphertext<D> {
pub fn decompress<DataOther: DataRef, B: Backend>(&mut self, module: &Module<B>, other: &GGLWECiphertextCompressed<DataOther>)
where
Module<B>: VecZnxFillUniform + VecZnxCopy,
{
#[cfg(debug_assertions)]
{
use backend::hal::api::ZnxInfos;
assert_eq!(
self.n(),
other.data.n(),
"invalid receiver: self.n()={} != other.n()={}",
self.n(),
other.data.n()
);
assert_eq!(
self.size(),
other.size(),
"invalid receiver: self.size()={} != other.size()={}",
self.size(),
other.size()
);
assert_eq!(
self.rank_in(),
other.rank_in(),
"invalid receiver: self.rank_in()={} != other.rank_in()={}",
self.rank_in(),
other.rank_in()
);
assert_eq!(
self.rank_out(),
other.rank_out(),
"invalid receiver: self.rank_out()={} != other.rank_out()={}",
self.rank_out(),
other.rank_out()
);
assert_eq!(
self.rows(),
other.rows(),
"invalid receiver: self.rows()={} != other.rows()={}",
self.rows(),
other.rows()
);
}
let rank_in: usize = self.rank_in();
let rows: usize = self.rows();
(0..rank_in).for_each(|col_i| {
(0..rows).for_each(|row_i| {
self.at_mut(row_i, col_i)
.decompress(module, &other.at(row_i, col_i));
});
});
}
}
#[derive(PartialEq, Eq)]
pub struct GLWESwitchingKeyCompressed<D: Data> {
pub(crate) key: GGLWECiphertextCompressed<D>,
pub(crate) sk_in_n: usize, // Degree of sk_in
pub(crate) sk_out_n: usize, // Degree of sk_out
}
impl<D: Data> Infos for GLWESwitchingKeyCompressed<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.key.inner()
}
fn basek(&self) -> usize {
self.key.basek()
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data> GLWESwitchingKeyCompressed<D> {
pub fn rank(&self) -> usize {
self.key.rank()
}
pub fn digits(&self) -> usize {
self.key.digits()
}
pub fn rank_in(&self) -> usize {
self.key.rank_in()
}
pub fn rank_out(&self) -> usize {
self.key.rank_out()
}
}
impl GLWESwitchingKeyCompressed<Vec<u8>> {
pub fn alloc<B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> Self
where
Module<B>: MatZnxAlloc,
{
GLWESwitchingKeyCompressed {
key: GGLWECiphertextCompressed::alloc(module, basek, k, rows, digits, rank_in, rank_out),
sk_in_n: 0,
sk_out_n: 0,
}
}
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize) -> usize
where
Module<B>: MatZnxAllocBytes,
{
GGLWECiphertextCompressed::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, rank_in)
}
}
impl<D: DataMut> ReaderFrom for GLWESwitchingKeyCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.sk_in_n = reader.read_u64::<LittleEndian>()? as usize;
self.sk_out_n = reader.read_u64::<LittleEndian>()? as usize;
self.key.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GLWESwitchingKeyCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.sk_in_n as u64)?;
writer.write_u64::<LittleEndian>(self.sk_out_n as u64)?;
self.key.write_to(writer)
}
}
impl<D: DataMut> GLWESwitchingKey<D> {
pub fn decompress<DataOther: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
other: &GLWESwitchingKeyCompressed<DataOther>,
) where
Module<B>: VecZnxFillUniform + VecZnxCopy,
{
self.key.decompress(module, &other.key);
self.sk_in_n = other.sk_in_n;
self.sk_out_n = other.sk_out_n;
}
}
#[derive(PartialEq, Eq)]
pub struct AutomorphismKeyCompressed<D: Data> {
pub(crate) key: GLWESwitchingKeyCompressed<D>,
pub(crate) p: i64,
}
impl AutomorphismKeyCompressed<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: MatZnxAlloc,
{
AutomorphismKeyCompressed {
key: GLWESwitchingKeyCompressed::alloc(module, basek, k, rows, digits, rank, rank),
p: 0,
}
}
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: MatZnxAllocBytes,
{
GLWESwitchingKeyCompressed::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, rank)
}
}
impl<D: Data> Infos for AutomorphismKeyCompressed<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.key.inner()
}
fn basek(&self) -> usize {
self.key.basek()
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data> AutomorphismKeyCompressed<D> {
pub fn rank(&self) -> usize {
self.key.rank()
}
pub fn digits(&self) -> usize {
self.key.digits()
}
pub fn rank_in(&self) -> usize {
self.key.rank_in()
}
pub fn rank_out(&self) -> usize {
self.key.rank_out()
}
}
impl<D: DataMut> ReaderFrom for AutomorphismKeyCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.p = reader.read_u64::<LittleEndian>()? as i64;
self.key.read_from(reader)
}
}
impl<D: DataRef> WriterTo for AutomorphismKeyCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.p as u64)?;
self.key.write_to(writer)
}
}
impl<D: DataMut> AutomorphismKey<D> {
pub fn decompress<DataOther: DataRef, B: Backend>(&mut self, module: &Module<B>, other: &AutomorphismKeyCompressed<DataOther>)
where
Module<B>: VecZnxFillUniform + VecZnxCopy,
{
self.key.decompress(module, &other.key);
self.p = other.p;
}
}
#[derive(PartialEq, Eq)]
pub struct GLWETensorKeyCompressed<D: Data> {
pub(crate) keys: Vec<GLWESwitchingKeyCompressed<D>>,
}
impl GLWETensorKeyCompressed<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: MatZnxAlloc,
{
let mut keys: Vec<GLWESwitchingKeyCompressed<Vec<u8>>> = Vec::new();
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
(0..pairs).for_each(|_| {
keys.push(GLWESwitchingKeyCompressed::alloc(
module, basek, k, rows, digits, 1, rank,
));
});
Self { keys: keys }
}
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: MatZnxAllocBytes,
{
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
pairs * GLWESwitchingKeyCompressed::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, 1)
}
}
impl<D: Data> Infos for GLWETensorKeyCompressed<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.keys[0].inner()
}
fn basek(&self) -> usize {
self.keys[0].basek()
}
fn k(&self) -> usize {
self.keys[0].k()
}
}
impl<D: Data> GLWETensorKeyCompressed<D> {
pub fn rank(&self) -> usize {
self.keys[0].rank()
}
pub fn digits(&self) -> usize {
self.keys[0].digits()
}
pub fn rank_in(&self) -> usize {
self.keys[0].rank_in()
}
pub fn rank_out(&self) -> usize {
self.keys[0].rank_out()
}
}
impl<D: DataMut> ReaderFrom for GLWETensorKeyCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
let len: usize = reader.read_u64::<LittleEndian>()? as usize;
if self.keys.len() != len {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("self.keys.len()={} != read len={}", self.keys.len(), len),
));
}
for key in &mut self.keys {
key.read_from(reader)?;
}
Ok(())
}
}
impl<D: DataRef> WriterTo for GLWETensorKeyCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.keys.len() as u64)?;
for key in &self.keys {
key.write_to(writer)?;
}
Ok(())
}
}
impl<D: DataMut> GLWETensorKeyCompressed<D> {
pub(crate) fn at_mut(&mut self, mut i: usize, mut j: usize) -> &mut GLWESwitchingKeyCompressed<D> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
&mut self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
impl<D: DataMut> GLWETensorKey<D> {
pub fn decompress<DataOther: DataRef, B: Backend>(&mut self, module: &Module<B>, other: &GLWETensorKeyCompressed<DataOther>)
where
Module<B>: VecZnxFillUniform + VecZnxCopy,
{
#[cfg(debug_assertions)]
{
assert_eq!(
self.keys.len(),
other.keys.len(),
"invalid receiver: self.keys.len()={} != other.keys.len()={}",
self.keys.len(),
other.keys.len()
);
}
self.keys
.iter_mut()
.zip(other.keys.iter())
.for_each(|(a, b)| {
a.decompress(module, b);
});
}
}

View File

@@ -0,0 +1,422 @@
use backend::hal::{
api::{VmpPMatAlloc, VmpPMatAllocBytes, VmpPMatPrepare},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat},
};
use crate::{AutomorphismKey, GGLWECiphertext, GGLWEExecLayoutFamily, GLWESwitchingKey, GLWETensorKey, Infos};
#[derive(PartialEq, Eq)]
pub struct GGLWECiphertextExec<D: Data, B: Backend> {
pub(crate) data: VmpPMat<D, B>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) digits: usize,
}
impl<B: Backend> GGLWECiphertextExec<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let size: usize = k.div_ceil(basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
Self {
data: module.vmp_pmat_alloc(rows, rank_in, rank_out + 1, size),
basek: basek,
k,
digits,
}
}
pub fn bytes_of(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let size: usize = k.div_ceil(basek);
debug_assert!(
size > digits,
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
module.vmp_pmat_alloc_bytes(rows, rank_in, rank_out + 1, rows)
}
}
impl<D: Data, B: Backend> Infos for GGLWECiphertextExec<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<D: Data, B: Backend> GGLWECiphertextExec<D, 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<D: DataMut, B: Backend> GGLWECiphertextExec<D, B> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &GGLWECiphertext<DataOther>, scratch: &mut Scratch<B>)
where
DataOther: DataRef,
Module<B>: GGLWEExecLayoutFamily<B>,
{
module.vmp_prepare(&mut self.data, &other.data, scratch);
self.basek = other.basek;
self.k = other.k;
self.digits = other.digits;
}
}
#[derive(PartialEq, Eq)]
pub struct GLWESwitchingKeyExec<D: Data, B: Backend> {
pub(crate) key: GGLWECiphertextExec<D, B>,
pub(crate) sk_in_n: usize, // Degree of sk_in
pub(crate) sk_out_n: usize, // Degree of sk_out
}
impl<B: Backend> GLWESwitchingKeyExec<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
GLWESwitchingKeyExec::<Vec<u8>, B> {
key: GGLWECiphertextExec::alloc(module, basek, k, rows, digits, rank_in, rank_out),
sk_in_n: 0,
sk_out_n: 0,
}
}
pub fn bytes_of(
module: &Module<B>,
basek: usize,
k: usize,
rows: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
) -> usize
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
GGLWECiphertextExec::bytes_of(module, basek, k, rows, digits, rank_in, rank_out)
}
pub fn from<DataOther: DataRef>(module: &Module<B>, other: &GLWESwitchingKey<DataOther>, scratch: &mut Scratch<B>) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let mut ksk_exec: GLWESwitchingKeyExec<Vec<u8>, B> = Self::alloc(
module,
other.basek(),
other.k(),
other.rows(),
other.digits(),
other.rank_in(),
other.rank_out(),
);
ksk_exec.prepare(module, other, scratch);
ksk_exec
}
}
impl<D: Data, B: Backend> Infos for GLWESwitchingKeyExec<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
self.key.inner()
}
fn basek(&self) -> usize {
self.key.basek()
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data, B: Backend> GLWESwitchingKeyExec<D, B> {
pub fn rank(&self) -> usize {
self.key.data.cols_out() - 1
}
pub fn rank_in(&self) -> usize {
self.key.data.cols_in()
}
pub fn rank_out(&self) -> usize {
self.key.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.key.digits()
}
pub fn sk_degree_in(&self) -> usize {
self.sk_in_n
}
pub fn sk_degree_out(&self) -> usize {
self.sk_out_n
}
}
impl<D: DataMut, B: Backend> GLWESwitchingKeyExec<D, B> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &GLWESwitchingKey<DataOther>, scratch: &mut Scratch<B>)
where
DataOther: DataRef,
Module<B>: GGLWEExecLayoutFamily<B>,
{
self.key.prepare(module, &other.key, scratch);
self.sk_in_n = other.sk_in_n;
self.sk_out_n = other.sk_out_n;
}
}
#[derive(PartialEq, Eq)]
pub struct AutomorphismKeyExec<D: Data, B: Backend> {
pub(crate) key: GLWESwitchingKeyExec<D, B>,
pub(crate) p: i64,
}
impl<B: Backend> AutomorphismKeyExec<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
AutomorphismKeyExec::<Vec<u8>, B> {
key: GLWESwitchingKeyExec::alloc(module, basek, k, rows, digits, rank, rank),
p: 0,
}
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
GLWESwitchingKeyExec::<Vec<u8>, B>::bytes_of(module, basek, k, rows, digits, rank, rank)
}
pub fn from<DataOther: DataRef>(module: &Module<B>, other: &AutomorphismKey<DataOther>, scratch: &mut Scratch<B>) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let mut atk_exec: AutomorphismKeyExec<Vec<u8>, B> = Self::alloc(
module,
other.basek(),
other.k(),
other.rows(),
other.digits(),
other.rank(),
);
atk_exec.prepare(module, other, scratch);
atk_exec
}
}
impl<D: DataMut, B: Backend> AutomorphismKeyExec<D, B> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &AutomorphismKey<DataOther>, scratch: &mut Scratch<B>)
where
DataOther: DataRef,
Module<B>: GGLWEExecLayoutFamily<B>,
{
self.key.prepare(module, &other.key, scratch);
self.p = other.p;
}
}
impl<D: Data, B: Backend> Infos for AutomorphismKeyExec<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
&self.key.inner()
}
fn basek(&self) -> usize {
self.key.basek()
}
fn k(&self) -> usize {
self.key.k()
}
}
impl<D: Data, B: Backend> AutomorphismKeyExec<D, 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()
}
}
#[derive(PartialEq, Eq)]
pub struct GLWETensorKeyExec<D: Data, B: Backend> {
pub(crate) keys: Vec<GLWESwitchingKeyExec<D, B>>,
}
impl<B: Backend> GLWETensorKeyExec<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let mut keys: Vec<GLWESwitchingKeyExec<Vec<u8>, B>> = Vec::new();
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
(0..pairs).for_each(|_| {
keys.push(GLWESwitchingKeyExec::alloc(
module, basek, k, rows, digits, 1, rank,
));
});
Self { keys }
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
pairs * GLWESwitchingKeyExec::<Vec<u8>, B>::bytes_of(module, basek, k, rows, digits, 1, rank)
}
}
impl<D: Data, B: Backend> Infos for GLWETensorKeyExec<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
&self.keys[0].inner()
}
fn basek(&self) -> usize {
self.keys[0].basek()
}
fn k(&self) -> usize {
self.keys[0].k()
}
}
impl<D: Data, B: Backend> GLWETensorKeyExec<D, B> {
pub fn rank(&self) -> usize {
self.keys[0].rank()
}
pub fn rank_in(&self) -> usize {
self.keys[0].rank_in()
}
pub fn rank_out(&self) -> usize {
self.keys[0].rank_out()
}
pub fn digits(&self) -> usize {
self.keys[0].digits()
}
}
impl<D: DataMut, B: Backend> GLWETensorKeyExec<D, B> {
// Returns a mutable reference to GLWESwitchingKey_{s}(s[i] * s[j])
pub fn at_mut(&mut self, mut i: usize, mut j: usize) -> &mut GLWESwitchingKeyExec<D, B> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
&mut self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
impl<D: DataRef, B: Backend> GLWETensorKeyExec<D, B> {
// Returns a reference to GLWESwitchingKey_{s}(s[i] * s[j])
pub fn at(&self, mut i: usize, mut j: usize) -> &GLWESwitchingKeyExec<D, B> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
&self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
impl<D: DataMut, B: Backend> GLWETensorKeyExec<D, B> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &GLWETensorKey<DataOther>, scratch: &mut Scratch<B>)
where
DataOther: DataRef,
Module<B>: GGLWEExecLayoutFamily<B>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.keys.len(), other.keys.len());
}
self.keys
.iter_mut()
.zip(other.keys.iter())
.for_each(|(a, b)| {
a.prepare(module, b, scratch);
});
}
}

View File

@@ -1,20 +1,16 @@
mod automorphism; mod automorphism;
mod automorphism_key;
mod encryption; mod encryption;
mod external_product; mod external_product;
mod keyswitch; mod keyswitch;
mod keyswitch_key;
mod layout; mod layout;
mod layouts_compressed;
mod layouts_exec;
mod noise; mod noise;
mod tensor_key;
pub use automorphism_key::{AutomorphismKey, AutomorphismKeyExec}; pub use encryption::*;
pub use encryption::{ pub use layout::*;
AutomorphismKeyEncryptSkFamily, GGLWEEncryptSkFamily, GLWESwitchingKeyEncryptSkFamily, GLWETensorKeyEncryptSkFamily, pub use layouts_compressed::*;
}; pub use layouts_exec::*;
pub use keyswitch_key::{GLWESwitchingKey, GLWESwitchingKeyExec};
pub use layout::{GGLWECiphertext, GGLWECiphertextExec, GGLWEExecLayoutFamily};
pub use tensor_key::{GLWETensorKey, GLWETensorKeyExec};
#[cfg(test)] #[cfg(test)]
mod test; mod tests;

View File

@@ -1,223 +0,0 @@
use backend::hal::{
api::{MatZnxAlloc, MatZnxAllocBytes},
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, Scratch, VmpPMat, WriterTo},
};
use crate::{GGLWEExecLayoutFamily, GLWESwitchingKey, GLWESwitchingKeyExec, Infos};
#[derive(PartialEq, Eq)]
pub struct GLWETensorKey<D: Data> {
pub(crate) keys: Vec<GLWESwitchingKey<D>>,
}
impl GLWETensorKey<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: MatZnxAlloc,
{
let mut keys: Vec<GLWESwitchingKey<Vec<u8>>> = Vec::new();
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
(0..pairs).for_each(|_| {
keys.push(GLWESwitchingKey::alloc(
module, basek, k, rows, digits, 1, rank,
));
});
Self { keys: keys }
}
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: MatZnxAllocBytes,
{
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
pairs * GLWESwitchingKey::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, 1, rank)
}
}
impl<D: Data> Infos for GLWETensorKey<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.keys[0].inner()
}
fn basek(&self) -> usize {
self.keys[0].basek()
}
fn k(&self) -> usize {
self.keys[0].k()
}
}
impl<D: Data> GLWETensorKey<D> {
pub fn rank(&self) -> usize {
self.keys[0].rank()
}
pub fn rank_in(&self) -> usize {
self.keys[0].rank_in()
}
pub fn rank_out(&self) -> usize {
self.keys[0].rank_out()
}
pub fn digits(&self) -> usize {
self.keys[0].digits()
}
}
impl<D: DataMut> GLWETensorKey<D> {
// 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<D> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
&mut self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
impl<D: DataRef> GLWETensorKey<D> {
// Returns a reference to GLWESwitchingKey_{s}(s[i] * s[j])
pub fn at(&self, mut i: usize, mut j: usize) -> &GLWESwitchingKey<D> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
&self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
impl<D: DataMut> ReaderFrom for GLWETensorKey<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
let len: usize = reader.read_u64::<LittleEndian>()? as usize;
if self.keys.len() != len {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("self.keys.len()={} != read len={}", self.keys.len(), len),
));
}
for key in &mut self.keys {
key.read_from(reader)?;
}
Ok(())
}
}
impl<D: DataRef> WriterTo for GLWETensorKey<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.keys.len() as u64)?;
for key in &self.keys {
key.write_to(writer)?;
}
Ok(())
}
}
#[derive(PartialEq, Eq)]
pub struct GLWETensorKeyExec<D: Data, B: Backend> {
pub(crate) keys: Vec<GLWESwitchingKeyExec<D, B>>,
}
impl<B: Backend> GLWETensorKeyExec<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let mut keys: Vec<GLWESwitchingKeyExec<Vec<u8>, B>> = Vec::new();
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
(0..pairs).for_each(|_| {
keys.push(GLWESwitchingKeyExec::alloc(
module, basek, k, rows, digits, 1, rank,
));
});
Self { keys }
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: GGLWEExecLayoutFamily<B>,
{
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
pairs * GLWESwitchingKeyExec::<Vec<u8>, B>::bytes_of(module, basek, k, rows, digits, 1, rank)
}
}
impl<D: Data, B: Backend> Infos for GLWETensorKeyExec<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
&self.keys[0].inner()
}
fn basek(&self) -> usize {
self.keys[0].basek()
}
fn k(&self) -> usize {
self.keys[0].k()
}
}
impl<D: Data, B: Backend> GLWETensorKeyExec<D, B> {
pub fn rank(&self) -> usize {
self.keys[0].rank()
}
pub fn rank_in(&self) -> usize {
self.keys[0].rank_in()
}
pub fn rank_out(&self) -> usize {
self.keys[0].rank_out()
}
pub fn digits(&self) -> usize {
self.keys[0].digits()
}
}
impl<D: DataMut, B: Backend> GLWETensorKeyExec<D, B> {
// Returns a mutable reference to GLWESwitchingKey_{s}(s[i] * s[j])
pub fn at_mut(&mut self, mut i: usize, mut j: usize) -> &mut GLWESwitchingKeyExec<D, B> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
&mut self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
impl<D: DataRef, B: Backend> GLWETensorKeyExec<D, B> {
// Returns a reference to GLWESwitchingKey_{s}(s[i] * s[j])
pub fn at(&self, mut i: usize, mut j: usize) -> &GLWESwitchingKeyExec<D, B> {
if i > j {
std::mem::swap(&mut i, &mut j);
};
let rank: usize = self.rank();
&self.keys[i * rank + j - (i * (i + 1) / 2)]
}
}
impl<D: DataMut, B: Backend> GLWETensorKeyExec<D, B> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &GLWETensorKey<DataOther>, scratch: &mut Scratch<B>)
where
DataOther: DataRef,
Module<B>: GGLWEExecLayoutFamily<B>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.keys.len(), other.keys.len());
}
self.keys
.iter_mut()
.zip(other.keys.iter())
.for_each(|(a, b)| {
a.prepare(module, b, scratch);
});
}
}

View File

@@ -1,138 +0,0 @@
use backend::{
hal::{api::ModuleNew, layouts::Module},
implementation::cpu_spqlios::FFT64,
};
use crate::gglwe::test::gglwe_generic::{
test_encrypt_sk, test_external_product, test_external_product_inplace, test_keyswitch, test_keyswitch_inplace,
};
#[test]
fn encrypt_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ksk: usize = 54;
let digits: usize = k_ksk / basek;
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
println!(
"test encrypt_sk digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_encrypt_sk(&module, basek, k_ksk, di, rank_in, rank_out, 3.2);
});
});
});
}
#[test]
fn keyswitch() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = k_in.div_ceil(basek);
(1..4).for_each(|rank_in_s0s1| {
(1..4).for_each(|rank_out_s0s1| {
(1..4).for_each(|rank_out_s1s2| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
println!(
"test key_switch digits: {} ranks: ({},{},{})",
di, rank_in_s0s1, rank_out_s0s1, rank_out_s1s2
);
let k_out: usize = k_ksk; // Better capture noise.
test_keyswitch(
&module,
basek,
k_out,
k_in,
k_ksk,
di,
rank_in_s0s1,
rank_out_s0s1,
rank_out_s1s2,
3.2,
);
})
})
});
});
}
#[test]
fn keyswitch_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = k_ct.div_ceil(basek);
(1..4).for_each(|rank_in_s0s1| {
(1..4).for_each(|rank_out_s0s1| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!(
"test key_switch_inplace digits: {} ranks: ({},{})",
di, rank_in_s0s1, rank_out_s0s1
);
test_keyswitch_inplace(
&module,
basek,
k_ct,
k_ksk,
di,
rank_in_s0s1,
rank_out_s0s1,
3.2,
);
});
});
});
}
#[test]
fn external_product() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 60;
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_ggsw: usize = k_in + basek * di;
println!(
"test external_product digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
let k_out: usize = k_in; // Better capture noise.
test_external_product(
&module, basek, k_out, k_in, k_ggsw, di, rank_in, rank_out, 3.2,
);
});
});
});
}
#[test]
fn external_product_inplace() {
let log_n: usize = 5;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = k_ct.div_ceil(basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits).for_each(|di| {
let k_ggsw: usize = k_ct + basek * di;
println!(
"test external_product_inplace digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_external_product_inplace(&module, basek, k_ct, k_ggsw, di, rank_in, rank_out, 3.2);
});
});
});
}

View File

@@ -1,5 +0,0 @@
mod automorphism_key;
mod gglwe_fft64;
mod gglwe_generic;
mod tensor_key_fft64;
mod tensor_key_generic;

View File

@@ -1,16 +0,0 @@
use backend::{
hal::{api::ModuleNew, layouts::Module},
implementation::cpu_spqlios::FFT64,
};
use crate::gglwe::test::tensor_key_generic::test_encrypt_sk;
#[test]
fn encrypt_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test encrypt_sk rank: {}", rank);
test_encrypt_sk(&module, 16, 54, 3.2, rank);
});
}

View File

@@ -0,0 +1,259 @@
use backend::{
hal::{api::ModuleNew, layouts::Module},
implementation::cpu_spqlios::FFT64,
};
use crate::gglwe::tests::{
generics_automorphism_key::{
test_automorphisk_key_encrypt_sk, test_automorphisk_key_encrypt_sk_compressed, test_gglwe_automorphism,
test_gglwe_automorphism_inplace,
},
generics_gglwe::{
test_gglwe_encrypt_sk, test_gglwe_encrypt_sk_compressed, test_gglwe_external_product,
test_gglwe_external_product_inplace, test_gglwe_keyswitch, test_gglwe_keyswitch_inplace,
},
generics_tensor_key::{test_tensor_key_encrypt_sk, test_tensor_key_encrypt_sk_compressed},
};
#[test]
fn gglwe_encrypt_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ksk: usize = 54;
let digits: usize = k_ksk / basek;
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
println!(
"test gglwe_encrypt_sk digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_gglwe_encrypt_sk(&module, basek, k_ksk, di, rank_in, rank_out, 3.2);
});
});
});
}
#[test]
fn gglwe_encrypt_sk_compressed() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ksk: usize = 54;
let digits: usize = k_ksk / basek;
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits + 1).for_each(|di| {
println!(
"test gglwe_encrypt_sk_compressed digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_gglwe_encrypt_sk_compressed(&module, basek, k_ksk, di, rank_in, rank_out, 3.2);
});
});
});
}
#[test]
fn gglwe_keyswitch() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = k_in.div_ceil(basek);
(1..4).for_each(|rank_in_s0s1| {
(1..4).for_each(|rank_out_s0s1| {
(1..4).for_each(|rank_out_s1s2| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_in + basek * di;
println!(
"test gglwe_keyswitch digits: {} ranks: ({},{},{})",
di, rank_in_s0s1, rank_out_s0s1, rank_out_s1s2
);
let k_out: usize = k_ksk; // Better capture noise.
test_gglwe_keyswitch(
&module,
basek,
k_out,
k_in,
k_ksk,
di,
rank_in_s0s1,
rank_out_s0s1,
rank_out_s1s2,
3.2,
);
})
})
});
});
}
#[test]
fn gglwe_keyswitch_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = k_ct.div_ceil(basek);
(1..4).for_each(|rank_in_s0s1| {
(1..4).for_each(|rank_out_s0s1| {
(1..digits + 1).for_each(|di| {
let k_ksk: usize = k_ct + basek * di;
println!(
"test gglwe_keyswitch_inplace digits: {} ranks: ({},{})",
di, rank_in_s0s1, rank_out_s0s1
);
test_gglwe_keyswitch_inplace(
&module,
basek,
k_ct,
k_ksk,
di,
rank_in_s0s1,
rank_out_s0s1,
3.2,
);
});
});
});
}
#[test]
fn gglwe_external_product() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 60;
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_ggsw: usize = k_in + basek * di;
println!(
"test gglwe_external_product digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
let k_out: usize = k_in; // Better capture noise.
test_gglwe_external_product(
&module, basek, k_out, k_in, k_ggsw, di, rank_in, rank_out, 3.2,
);
});
});
});
}
#[test]
fn gglwe_external_product_inplace() {
let log_n: usize = 5;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_ct: usize = 60;
let digits: usize = k_ct.div_ceil(basek);
(1..4).for_each(|rank_in| {
(1..4).for_each(|rank_out| {
(1..digits).for_each(|di| {
let k_ggsw: usize = k_ct + basek * di;
println!(
"test gglwe_external_product_inplace digits: {} ranks: ({} {})",
di, rank_in, rank_out
);
test_gglwe_external_product_inplace(&module, basek, k_ct, k_ggsw, di, rank_in, rank_out, 3.2);
});
});
});
}
#[test]
fn automorphism_key_encrypt_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k: usize = 60;
let digits: usize = k.div_ceil(basek) - 1;
let sigma: f64 = 3.2;
(1..4).for_each(|rank| {
(2..digits + 1).for_each(|di| {
println!(
"test automorphism key encrypt sk digits: {} rank: {}",
di, rank
);
test_automorphisk_key_encrypt_sk(&module, basek, k, di, rank, sigma);
});
});
}
#[test]
fn automorphism_key_encrypt_sk_compressed() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k: usize = 60;
let digits: usize = k.div_ceil(basek) - 1;
let sigma: f64 = 3.2;
(1..4).for_each(|rank| {
(2..digits + 1).for_each(|di| {
println!(
"test automorphism key encrypt sk compressed digits: {} rank: {}",
di, rank
);
test_automorphisk_key_encrypt_sk_compressed(&module, basek, k, di, rank, sigma);
});
});
}
#[test]
fn gglwe_automorphism() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 60;
let k_out: usize = 40;
let digits: usize = k_in.div_ceil(basek);
let sigma: f64 = 3.2;
(1..4).for_each(|rank| {
(2..digits + 1).for_each(|di| {
println!("test automorphism digits: {} rank: {}", di, rank);
let k_apply: usize = (digits + di) * basek;
test_gglwe_automorphism(&module, -1, 5, basek, di, k_in, k_out, k_apply, sigma, rank);
});
});
}
#[test]
fn gglwe_automorphism_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 60;
let digits: usize = k_in.div_ceil(basek);
let sigma: f64 = 3.2;
(1..4).for_each(|rank| {
(2..digits + 1).for_each(|di| {
println!("test automorphism_inplace digits: {} rank: {}", di, rank);
let k_apply: usize = (digits + di) * basek;
test_gglwe_automorphism_inplace(&module, -1, 5, basek, di, k_in, k_apply, sigma, rank);
});
});
}
#[test]
fn tensor_key_encrypt_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test encrypt_sk rank: {}", rank);
test_tensor_key_encrypt_sk(&module, 16, 54, 3.2, rank);
});
}
#[test]
fn tensor_key_encrypt_sk_compressed() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test encrypt_sk_compressed rank: {}", rank);
test_tensor_key_encrypt_sk_compressed(&module, 16, 54, 3.2, rank);
});
}

View File

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

View File

@@ -1,53 +1,168 @@
use backend::{ use backend::hal::{
hal::{ api::{
api::{ModuleNew, ScalarZnxAutomorphism, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxStd, VecZnxSubScalarInplace}, MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
layouts::{Module, ScratchOwned}, VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxAutomorphismInplace, VecZnxCopy, VecZnxStd,
VecZnxSubScalarInplace, VecZnxSwithcDegree,
},
layouts::{Backend, Module, ScratchOwned},
oep::{
ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl,
}, },
implementation::cpu_spqlios::FFT64,
}; };
use sampling::source::Source; use sampling::source::Source;
use crate::{ use crate::{
AutomorphismKey, AutomorphismKeyExec, GLWEPlaintext, GLWESecret, GLWESecretExec, Infos, noise::log2_std_noise_gglwe_product, AutomorphismKey, AutomorphismKeyCompressed, AutomorphismKeyEncryptSkFamily, AutomorphismKeyExec, GGLWEExecLayoutFamily,
GLWEDecryptFamily, GLWEKeyswitchFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, Infos,
noise::log2_std_noise_gglwe_product,
}; };
#[test] pub(crate) trait AutomorphismTestModuleFamily<B: Backend> = MatZnxAlloc
fn automorphism() { + AutomorphismKeyEncryptSkFamily<B>
let log_n: usize = 8; + ScalarZnxAllocBytes
let basek: usize = 12; + VecZnxAllocBytes
let k_in: usize = 60; + GLWEKeyswitchFamily<B>
let k_out: usize = 40; + ScalarZnxAlloc
let digits: usize = k_in.div_ceil(basek); + VecZnxAutomorphism
let sigma: f64 = 3.2; + GGLWEExecLayoutFamily<B>
(1..4).for_each(|rank| { + VecZnxSwithcDegree
(2..digits + 1).for_each(|di| { + VecZnxAddScalarInplace
println!("test automorphism digits: {} rank: {}", di, rank); + VecZnxAutomorphism
let k_apply: usize = (digits + di) * basek; + VecZnxAutomorphismInplace
test_automorphism(-1, 5, log_n, basek, di, k_in, k_out, k_apply, sigma, rank); + VecZnxAlloc
}); + GLWEDecryptFamily<B>
+ VecZnxSubScalarInplace
+ VecZnxStd
+ VecZnxCopy;
pub(crate) trait AutomorphismTestScratchFamily<B: Backend> = ScratchOwnedAllocImpl<B>
+ ScratchOwnedBorrowImpl<B>
+ ScratchAvailableImpl<B>
+ TakeScalarZnxImpl<B>
+ TakeVecZnxDftImpl<B>
+ TakeVecZnxImpl<B>
+ TakeSvpPPolImpl<B>
+ TakeVecZnxBigImpl<B>;
pub(crate) fn test_automorphisk_key_encrypt_sk<B: Backend>(
module: &Module<B>,
basek: usize,
k_ksk: usize,
digits: usize,
rank: usize,
sigma: f64,
) where
Module<B>: AutomorphismTestModuleFamily<B>,
B: AutomorphismTestScratchFamily<B>,
{
let rows: usize = (k_ksk - digits * basek) / (digits * basek);
let mut atk: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(module, basek, k_ksk, rows, 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<B> = ScratchOwned::alloc(AutomorphismKey::encrypt_sk_scratch_space(
module, basek, k_ksk, rank,
));
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let p = -5;
atk.encrypt_sk(
module,
p,
&sk,
&mut source_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut sk_out: GLWESecret<Vec<u8>> = sk.clone();
(0..atk.rank()).for_each(|i| {
module.vec_znx_automorphism(
module.galois_element_inv(p),
&mut sk_out.data.as_vec_znx_mut(),
i,
&sk.data.as_vec_znx(),
i,
);
}); });
let sk_out_exec = GLWESecretExec::from(module, &sk_out);
atk.key
.key
.assert_noise(module, &sk_out_exec, &sk.data, sigma);
} }
#[test] pub(crate) fn test_automorphisk_key_encrypt_sk_compressed<B: Backend>(
fn automorphism_inplace() { module: &Module<B>,
let log_n: usize = 8; basek: usize,
let basek: usize = 12; k_ksk: usize,
let k_in: usize = 60; digits: usize,
let digits: usize = k_in.div_ceil(basek); rank: usize,
let sigma: f64 = 3.2; sigma: f64,
(1..4).for_each(|rank| { ) where
(2..digits + 1).for_each(|di| { Module<B>: AutomorphismTestModuleFamily<B>,
println!("test automorphism digits: {} rank: {}", di, rank); B: AutomorphismTestScratchFamily<B>,
let k_apply: usize = (digits + di) * basek; {
test_automorphism_inplace(-1, 5, log_n, basek, di, k_in, k_apply, sigma, rank); let rows: usize = (k_ksk - digits * basek) / (digits * basek);
});
let mut atk_compressed: AutomorphismKeyCompressed<Vec<u8>> =
AutomorphismKeyCompressed::alloc(module, basek, k_ksk, rows, digits, rank);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(AutomorphismKey::encrypt_sk_scratch_space(
module, basek, k_ksk, rank,
));
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let p = -5;
let seed_xa: [u8; 32] = [1u8; 32];
atk_compressed.encrypt_sk(
module,
p,
&sk,
seed_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut sk_out: GLWESecret<Vec<u8>> = sk.clone();
(0..atk_compressed.rank()).for_each(|i| {
module.vec_znx_automorphism(
module.galois_element_inv(p),
&mut sk_out.data.as_vec_znx_mut(),
i,
&sk.data.as_vec_znx(),
i,
);
}); });
let sk_out_exec = GLWESecretExec::from(module, &sk_out);
let mut atk: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(module, basek, k_ksk, rows, digits, rank);
atk.decompress(module, &atk_compressed);
atk.key
.key
.assert_noise(module, &sk_out_exec, &sk.data, sigma);
} }
fn test_automorphism( pub(crate) fn test_gglwe_automorphism<B: Backend>(
module: &Module<B>,
p0: i64, p0: i64,
p1: i64, p1: i64,
log_n: usize,
basek: usize, basek: usize,
digits: usize, digits: usize,
k_in: usize, k_in: usize,
@@ -55,9 +170,10 @@ fn test_automorphism(
k_apply: usize, k_apply: usize,
sigma: f64, sigma: f64,
rank: usize, rank: usize,
) { ) where
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); Module<B>: AutomorphismTestModuleFamily<B>,
B: AutomorphismTestScratchFamily<B>,
{
let digits_in: usize = 1; let digits_in: usize = 1;
let rows_in: usize = k_in / (basek * digits); let rows_in: usize = k_in / (basek * digits);
@@ -71,7 +187,7 @@ fn test_automorphism(
let mut source_xe: 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_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<FFT64> = ScratchOwned::alloc( let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
AutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank) AutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank)
| AutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank), | AutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank),
); );
@@ -101,7 +217,7 @@ fn test_automorphism(
scratch.borrow(), scratch.borrow(),
); );
let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, FFT64> = let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, B> =
AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank); AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank);
auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow()); auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow());
@@ -119,16 +235,16 @@ fn test_automorphism(
let mut sk_auto: GLWESecret<Vec<u8>> = 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 sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
(0..rank).for_each(|i| { (0..rank).for_each(|i| {
module.scalar_znx_automorphism( module.vec_znx_automorphism(
module.galois_element_inv(p0 * p1), module.galois_element_inv(p0 * p1),
&mut sk_auto.data, &mut sk_auto.data.as_vec_znx_mut(),
i, i,
&sk.data, &sk.data.as_vec_znx(),
i, i,
); );
}); });
let sk_auto_dft: GLWESecretExec<Vec<u8>, FFT64> = GLWESecretExec::from(&module, &sk_auto); let sk_auto_dft: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(&module, &sk_auto);
(0..auto_key_out.rank_in()).for_each(|col_i| { (0..auto_key_out.rank_in()).for_each(|col_i| {
(0..auto_key_out.rows()).for_each(|row_i| { (0..auto_key_out.rows()).for_each(|row_i| {
@@ -168,19 +284,20 @@ fn test_automorphism(
}); });
} }
fn test_automorphism_inplace( pub(crate) fn test_gglwe_automorphism_inplace<B: Backend>(
module: &Module<B>,
p0: i64, p0: i64,
p1: i64, p1: i64,
log_n: usize,
basek: usize, basek: usize,
digits: usize, digits: usize,
k_in: usize, k_in: usize,
k_apply: usize, k_apply: usize,
sigma: f64, sigma: f64,
rank: usize, rank: usize,
) { ) where
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); Module<B>: AutomorphismTestModuleFamily<B>,
B: AutomorphismTestScratchFamily<B>,
{
let digits_in: usize = 1; let digits_in: usize = 1;
let rows_in: usize = k_in / (basek * digits); let rows_in: usize = k_in / (basek * digits);
@@ -193,7 +310,7 @@ fn test_automorphism_inplace(
let mut source_xe: 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_xa: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<FFT64> = ScratchOwned::alloc( let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(
AutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank) AutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank)
| AutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank), | AutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank),
); );
@@ -223,7 +340,7 @@ fn test_automorphism_inplace(
scratch.borrow(), scratch.borrow(),
); );
let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, FFT64> = let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, B> =
AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank); AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank);
auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow()); auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow());
@@ -237,16 +354,16 @@ fn test_automorphism_inplace(
sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk
(0..rank).for_each(|i| { (0..rank).for_each(|i| {
module.scalar_znx_automorphism( module.vec_znx_automorphism(
module.galois_element_inv(p0 * p1), module.galois_element_inv(p0 * p1),
&mut sk_auto.data, &mut sk_auto.data.as_vec_znx_mut(),
i, i,
&sk.data, &sk.data.as_vec_znx(),
i, i,
); );
}); });
let sk_auto_dft: GLWESecretExec<Vec<u8>, FFT64> = GLWESecretExec::from(&module, &sk_auto); let sk_auto_dft: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(&module, &sk_auto);
(0..auto_key.rank_in()).for_each(|col_i| { (0..auto_key.rank_in()).for_each(|col_i| {
(0..auto_key.rows()).for_each(|row_i| { (0..auto_key.rows()).for_each(|row_i| {

View File

@@ -1,7 +1,8 @@
use backend::hal::{ use backend::hal::{
api::{ api::{
MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
VecZnxAlloc, VecZnxAllocBytes, VecZnxRotateInplace, VecZnxStd, VecZnxSubScalarInplace, VecZnxSwithcDegree, ZnxViewMut, VecZnxAlloc, VecZnxAllocBytes, VecZnxCopy, VecZnxRotateInplace, VecZnxStd, VecZnxSubScalarInplace, VecZnxSwithcDegree,
ZnxViewMut,
}, },
layouts::{Backend, Module, ScalarZnx, ScalarZnxToMut, ScratchOwned}, layouts::{Backend, Module, ScalarZnx, ScalarZnxToMut, ScratchOwned},
oep::{ oep::{
@@ -13,7 +14,7 @@ use sampling::source::Source;
use crate::{ use crate::{
GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GGSWCiphertext, GGSWCiphertextExec, GGSWLayoutFamily, GLWEDecryptFamily, GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GGSWCiphertext, GGSWCiphertextExec, GGSWLayoutFamily, GLWEDecryptFamily,
GLWEExternalProductFamily, GLWEKeyswitchFamily, GLWESecret, GLWESecretExec, GLWESwitchingKey, GLWEExternalProductFamily, GLWEKeyswitchFamily, GLWESecret, GLWESecretExec, GLWESwitchingKey, GLWESwitchingKeyCompressed,
GLWESwitchingKeyEncryptSkFamily, GLWESwitchingKeyExec, GLWESwitchingKeyEncryptSkFamily, GLWESwitchingKeyExec,
noise::{log2_std_noise_gglwe_product, noise_ggsw_product}, noise::{log2_std_noise_gglwe_product, noise_ggsw_product},
}; };
@@ -28,7 +29,8 @@ pub(crate) trait TestModuleFamily<B: Backend> = GGLWEEncryptSkFamily<B>
+ VecZnxAddScalarInplace + VecZnxAddScalarInplace
+ VecZnxStd + VecZnxStd
+ VecZnxAlloc + VecZnxAlloc
+ VecZnxSubScalarInplace; + VecZnxSubScalarInplace
+ VecZnxCopy;
pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B> pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
+ TakeVecZnxBigImpl<B> + TakeVecZnxBigImpl<B>
@@ -42,7 +44,7 @@ pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
+ VecZnxBigAllocBytesImpl<B> + VecZnxBigAllocBytesImpl<B>
+ TakeSvpPPolImpl<B>; + TakeSvpPPolImpl<B>;
pub(crate) fn test_encrypt_sk<B: Backend>( pub(crate) fn test_gglwe_encrypt_sk<B: Backend>(
module: &Module<B>, module: &Module<B>,
basek: usize, basek: usize,
k_ksk: usize, k_ksk: usize,
@@ -87,7 +89,57 @@ pub(crate) fn test_encrypt_sk<B: Backend>(
.assert_noise(module, &sk_out_exec, &sk_in.data, sigma); .assert_noise(module, &sk_out_exec, &sk_in.data, sigma);
} }
pub(crate) fn test_keyswitch<B: Backend>( pub(crate) fn test_gglwe_encrypt_sk_compressed<B: Backend>(
module: &Module<B>,
basek: usize,
k_ksk: usize,
digits: usize,
rank_in: usize,
rank_out: usize,
sigma: f64,
) where
Module<B>: TestModuleFamily<B>,
B: TestScratchFamily<B>,
{
let rows: usize = (k_ksk - digits * basek) / (digits * basek);
let mut ksk_compressed: GLWESwitchingKeyCompressed<Vec<u8>> =
GLWESwitchingKeyCompressed::alloc(module, basek, k_ksk, rows, digits, rank_in, rank_out);
let mut source_xs: Source = Source::new([0u8; 32]);
let mut source_xe: Source = Source::new([0u8; 32]);
let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWESwitchingKeyCompressed::encrypt_sk_scratch_space(
module, basek, k_ksk, 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 mut sk_out: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank_out);
sk_out.fill_ternary_prob(0.5, &mut source_xs);
let sk_out_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk_out);
let seed_xa = [1u8; 32];
ksk_compressed.encrypt_sk(
module,
&sk_in,
&sk_out,
seed_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut ksk: GLWESwitchingKey<Vec<u8>> = GLWESwitchingKey::alloc(module, basek, k_ksk, rows, digits, rank_in, rank_out);
ksk.decompress(module, &ksk_compressed);
ksk.key
.assert_noise(module, &sk_out_exec, &sk_in.data, sigma);
}
pub(crate) fn test_gglwe_keyswitch<B: Backend>(
module: &Module<B>, module: &Module<B>,
basek: usize, basek: usize,
k_out: usize, k_out: usize,
@@ -217,7 +269,7 @@ pub(crate) fn test_keyswitch<B: Backend>(
.assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5); .assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5);
} }
pub(crate) fn test_keyswitch_inplace<B: Backend>( pub(crate) fn test_gglwe_keyswitch_inplace<B: Backend>(
module: &Module<B>, module: &Module<B>,
basek: usize, basek: usize,
k_ct: usize, k_ct: usize,
@@ -317,7 +369,7 @@ pub(crate) fn test_keyswitch_inplace<B: Backend>(
.assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5); .assert_noise(module, &sk2_exec, &sk0.data, max_noise + 0.5);
} }
pub(crate) fn test_external_product<B: Backend>( pub(crate) fn test_gglwe_external_product<B: Backend>(
module: &Module<B>, module: &Module<B>,
basek: usize, basek: usize,
k_out: usize, k_out: usize,
@@ -400,7 +452,7 @@ pub(crate) fn test_external_product<B: Backend>(
ct_gglwe_out.external_product(module, &ct_gglwe_in, &ct_rgsw_exec, scratch.borrow()); ct_gglwe_out.external_product(module, &ct_gglwe_in, &ct_rgsw_exec, scratch.borrow());
(0..rank_in).for_each(|i| { (0..rank_in).for_each(|i| {
module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data, i); // * X^{r} module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data.as_vec_znx_mut(), i); // * X^{r}
}); });
let var_gct_err_lhs: f64 = sigma * sigma; let var_gct_err_lhs: f64 = sigma * sigma;
@@ -429,7 +481,7 @@ pub(crate) fn test_external_product<B: Backend>(
.assert_noise(module, &sk_out_exec, &sk_in.data, max_noise + 0.5); .assert_noise(module, &sk_out_exec, &sk_in.data, max_noise + 0.5);
} }
pub(crate) fn test_external_product_inplace<B: Backend>( pub(crate) fn test_gglwe_external_product_inplace<B: Backend>(
module: &Module<B>, module: &Module<B>,
basek: usize, basek: usize,
k_ct: usize, k_ct: usize,
@@ -510,7 +562,7 @@ pub(crate) fn test_external_product_inplace<B: Backend>(
ct_gglwe.external_product_inplace(module, &ct_rgsw_exec, scratch.borrow()); ct_gglwe.external_product_inplace(module, &ct_rgsw_exec, scratch.borrow());
(0..rank_in).for_each(|i| { (0..rank_in).for_each(|i| {
module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data, i); // * X^{r} module.vec_znx_rotate_inplace(r as i64, &mut sk_in.data.as_vec_znx_mut(), i); // * X^{r}
}); });
let var_gct_err_lhs: f64 = sigma * sigma; let var_gct_err_lhs: f64 = sigma * sigma;

View File

@@ -1,7 +1,8 @@
use backend::hal::{ use backend::hal::{
api::{ api::{
MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
VecZnxAlloc, VecZnxAllocBytes, VecZnxBigAlloc, VecZnxDftAlloc, VecZnxStd, VecZnxSubScalarInplace, VecZnxSwithcDegree, VecZnxAlloc, VecZnxAllocBytes, VecZnxBigAlloc, VecZnxCopy, VecZnxDftAlloc, VecZnxStd, VecZnxSubScalarInplace,
VecZnxSwithcDegree,
}, },
layouts::{Backend, Module, ScratchOwned, VecZnxDft}, layouts::{Backend, Module, ScratchOwned, VecZnxDft},
oep::{ oep::{
@@ -13,7 +14,7 @@ use sampling::source::Source;
use crate::{ use crate::{
GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GLWEDecryptFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, GLWETensorKey, GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GLWEDecryptFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, GLWETensorKey,
GLWETensorKeyEncryptSkFamily, Infos, GLWETensorKeyCompressed, GLWETensorKeyEncryptSkFamily, Infos,
}; };
pub(crate) trait TestModuleFamily<B: Backend> = GGLWEEncryptSkFamily<B> pub(crate) trait TestModuleFamily<B: Backend> = GGLWEEncryptSkFamily<B>
@@ -40,7 +41,7 @@ pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
+ VecZnxBigAllocBytesImpl<B> + VecZnxBigAllocBytesImpl<B>
+ TakeSvpPPolImpl<B>; + TakeSvpPPolImpl<B>;
pub(crate) fn test_encrypt_sk<B: Backend>(module: &Module<B>, basek: usize, k: usize, sigma: f64, rank: usize) pub(crate) fn test_tensor_key_encrypt_sk<B: Backend>(module: &Module<B>, basek: usize, k: usize, sigma: f64, rank: usize)
where where
Module<B>: TestModuleFamily<B> Module<B>: TestModuleFamily<B>
+ GGLWEExecLayoutFamily<B> + GGLWEExecLayoutFamily<B>
@@ -87,14 +88,111 @@ where
let mut sk_dft: VecZnxDft<Vec<u8>, B> = module.vec_znx_dft_alloc(rank, 1); let mut sk_dft: VecZnxDft<Vec<u8>, B> = module.vec_znx_dft_alloc(rank, 1);
(0..rank).for_each(|i| { (0..rank).for_each(|i| {
module.vec_znx_dft_from_vec_znx(1, 0, &mut sk_dft, i, &sk.data, i); module.vec_znx_dft_from_vec_znx(1, 0, &mut sk_dft, i, &sk.data.as_vec_znx(), i);
}); });
(0..rank).for_each(|i| { (0..rank).for_each(|i| {
(0..rank).for_each(|j| { (0..rank).for_each(|j| {
module.svp_apply(&mut sk_ij_dft, 0, &sk_exec.data, j, &sk_dft, i); module.svp_apply(&mut sk_ij_dft, 0, &sk_exec.data, j, &sk_dft, i);
module.vec_znx_dft_to_vec_znx_big_tmp_a(&mut sk_ij_big, 0, &mut sk_ij_dft, 0); module.vec_znx_dft_to_vec_znx_big_tmp_a(&mut sk_ij_big, 0, &mut sk_ij_dft, 0);
module.vec_znx_big_normalize(basek, &mut sk_ij.data, 0, &sk_ij_big, 0, scratch.borrow()); module.vec_znx_big_normalize(
basek,
&mut sk_ij.data.as_vec_znx_mut(),
0,
&sk_ij_big,
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)
.at(row_i, col_i)
.decrypt(&module, &mut pt, &sk_exec, scratch.borrow());
module.vec_znx_sub_scalar_inplace(&mut pt.data, 0, row_i, &sk_ij.data, col_i);
let std_pt: f64 = module.vec_znx_std(basek, &pt.data, 0) * (k as f64).exp2();
assert!((sigma - std_pt).abs() <= 0.5, "{} {}", sigma, std_pt);
});
});
})
})
}
pub(crate) fn test_tensor_key_encrypt_sk_compressed<B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
sigma: f64,
rank: usize,
) where
Module<B>: TestModuleFamily<B>
+ GGLWEExecLayoutFamily<B>
+ GLWETensorKeyEncryptSkFamily<B>
+ GLWEDecryptFamily<B>
+ VecZnxDftAlloc<B>
+ VecZnxBigAlloc<B>
+ VecZnxCopy,
B: TestScratchFamily<B>,
{
let rows: usize = k / basek;
let mut tensor_key_compressed: GLWETensorKeyCompressed<Vec<u8>> =
GLWETensorKeyCompressed::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 scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWETensorKeyCompressed::encrypt_sk_scratch_space(
module,
basek,
tensor_key_compressed.k(),
rank,
));
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let mut sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(&module, &sk);
sk_exec.prepare(module, &sk);
let seed_xa: [u8; 32] = [1u8; 32];
tensor_key_compressed.encrypt_sk(
module,
&sk,
seed_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut tensor_key: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(&module, basek, k, rows, 1, rank);
tensor_key.decompress(module, &tensor_key_compressed);
let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k);
let mut sk_ij_dft = module.vec_znx_dft_alloc(1, 1);
let mut sk_ij_big = module.vec_znx_big_alloc(1, 1);
let mut sk_ij: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, 1);
let mut sk_dft: VecZnxDft<Vec<u8>, B> = module.vec_znx_dft_alloc(rank, 1);
(0..rank).for_each(|i| {
module.vec_znx_dft_from_vec_znx(1, 0, &mut sk_dft, i, &sk.data.as_vec_znx(), i);
});
(0..rank).for_each(|i| {
(0..rank).for_each(|j| {
module.svp_apply(&mut sk_ij_dft, 0, &sk_exec.data, j, &sk_dft, i);
module.vec_znx_dft_to_vec_znx_big_tmp_a(&mut sk_ij_big, 0, &mut sk_ij_dft, 0);
module.vec_znx_big_normalize(
basek,
&mut sk_ij.data.as_vec_znx_mut(),
0,
&sk_ij_big,
0,
scratch.borrow(),
);
(0..tensor_key.rank_in()).for_each(|col_i| { (0..tensor_key.rank_in()).for_each(|col_i| {
(0..tensor_key.rows()).for_each(|row_i| { (0..tensor_key.rows()).for_each(|row_i| {
tensor_key tensor_key

View File

@@ -0,0 +1,4 @@
mod cpu_spqlios;
mod generics_automorphism_key;
mod generics_gglwe;
mod generics_tensor_key;

View File

@@ -64,7 +64,7 @@ impl<DataSelf: DataMut> GGSWCiphertext<DataSelf> {
(0..rank + 1).for_each(|col_j| { (0..rank + 1).for_each(|col_j| {
// rlwe encrypt of vec_znx_pt into vec_znx_ct // rlwe encrypt of vec_znx_pt into vec_znx_ct
self.at_mut(row_i, col_j).encrypt_sk_private( self.at_mut(row_i, col_j).encrypt_sk_internal(
module, module,
Some((&tmp_pt, col_j)), Some((&tmp_pt, col_j)),
sk, sk,

View File

@@ -1,6 +1,6 @@
use backend::hal::{ use backend::hal::{
api::{MatZnxAlloc, MatZnxAllocBytes, VmpPMatAlloc, VmpPMatAllocBytes, VmpPMatPrepare}, api::{MatZnxAlloc, MatZnxAllocBytes, VmpPMatAlloc, VmpPMatAllocBytes, VmpPMatPrepare},
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, Scratch, VmpPMat, WriterTo}, layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, WriterTo},
}; };
use crate::{GLWECiphertext, Infos}; use crate::{GLWECiphertext, Infos};
@@ -135,125 +135,3 @@ impl<D: DataRef> WriterTo for GGSWCiphertext<D> {
self.data.write_to(writer) self.data.write_to(writer)
} }
} }
#[derive(PartialEq, Eq)]
pub struct GGSWCiphertextExec<D: Data, B: Backend> {
pub(crate) data: VmpPMat<D, B>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) digits: usize,
}
impl<B: Backend> GGSWCiphertextExec<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: GGSWLayoutFamily<B>,
{
let size: usize = k.div_ceil(basek);
debug_assert!(digits > 0, "invalid ggsw: `digits` == 0");
debug_assert!(
size > digits,
"invalid ggsw: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid ggsw: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
Self {
data: module.vmp_pmat_alloc(rows, rank + 1, rank + 1, k.div_ceil(basek)),
basek,
k: k,
digits,
}
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: GGSWLayoutFamily<B>,
{
let size: usize = k.div_ceil(basek);
debug_assert!(
size > digits,
"invalid ggsw: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid ggsw: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
module.vmp_pmat_alloc_bytes(rows, rank + 1, rank + 1, size)
}
pub fn from<DataOther: DataRef>(
module: &Module<B>,
other: &GGSWCiphertext<DataOther>,
scratch: &mut Scratch<B>,
) -> GGSWCiphertextExec<Vec<u8>, B>
where
Module<B>: GGSWLayoutFamily<B>,
{
let mut ggsw_exec: GGSWCiphertextExec<Vec<u8>, B> = Self::alloc(
module,
other.basek(),
other.k(),
other.rows(),
other.digits(),
other.rank(),
);
ggsw_exec.prepare(module, other, scratch);
ggsw_exec
}
}
impl<D: Data, B: Backend> Infos for GGSWCiphertextExec<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<D: Data, B: Backend> GGSWCiphertextExec<D, B> {
pub fn rank(&self) -> usize {
self.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.digits
}
}
impl<DataSelf: DataMut, B: Backend> GGSWCiphertextExec<DataSelf, B> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &GGSWCiphertext<DataOther>, scratch: &mut Scratch<B>)
where
DataOther: DataRef,
Module<B>: GGSWLayoutFamily<B>,
{
module.vmp_prepare(&mut self.data, &other.data, scratch);
self.k = other.k;
self.basek = other.basek;
self.digits = other.digits;
}
}

View File

@@ -0,0 +1,80 @@
use backend::hal::{
api::{MatZnxAlloc, MatZnxAllocBytes, VecZnxCopy, VecZnxFillUniform},
layouts::{Backend, Data, DataMut, DataRef, MatZnx, Module, ReaderFrom, WriterTo},
};
use crate::{GGLWECiphertextCompressed, GGSWCiphertext, Infos};
#[derive(PartialEq, Eq)]
pub struct GGSWCiphertextCompressed<D: Data> {
pub(crate) data: GGLWECiphertextCompressed<D>,
}
impl GGSWCiphertextCompressed<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: MatZnxAlloc,
{
GGSWCiphertextCompressed {
data: GGLWECiphertextCompressed::alloc(module, basek, k, rows, digits, rank, rank),
}
}
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: MatZnxAllocBytes,
{
GGLWECiphertextCompressed::bytes_of(module, basek, k, rows, digits, rank)
}
}
impl<D: Data> Infos for GGSWCiphertextCompressed<D> {
type Inner = MatZnx<D>;
fn inner(&self) -> &Self::Inner {
self.data.inner()
}
fn basek(&self) -> usize {
self.data.basek()
}
fn k(&self) -> usize {
self.data.k()
}
}
impl<D: Data> GGSWCiphertextCompressed<D> {
pub fn rank(&self) -> usize {
self.data.rank()
}
pub fn digits(&self) -> usize {
self.data.digits()
}
}
impl<D: DataMut> ReaderFrom for GGSWCiphertextCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.data.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GGSWCiphertextCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
self.data.write_to(writer)
}
}
impl<D: DataMut> GGSWCiphertext<D> {
pub fn decompress<DataOther: DataRef, B: Backend>(&mut self, module: &Module<B>, other: &GGSWCiphertextCompressed<DataOther>)
where
Module<B>: VecZnxFillUniform + VecZnxCopy,
{
let rows = self.rows();
(0..rows).for_each(|row_i| {
self.at_mut(row_i, 0)
.decompress(module, &other.data.at(row_i, 0));
});
}
}

View File

@@ -0,0 +1,128 @@
use backend::hal::{
api::{VmpPMatAlloc, VmpPMatAllocBytes, VmpPMatPrepare},
layouts::{Backend, Data, DataMut, DataRef, Module, Scratch, VmpPMat},
};
use crate::{GGSWCiphertext, GGSWLayoutFamily, Infos};
#[derive(PartialEq, Eq)]
pub struct GGSWCiphertextExec<D: Data, B: Backend> {
pub(crate) data: VmpPMat<D, B>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) digits: usize,
}
impl<B: Backend> GGSWCiphertextExec<Vec<u8>, B> {
pub fn alloc(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self
where
Module<B>: GGSWLayoutFamily<B>,
{
let size: usize = k.div_ceil(basek);
debug_assert!(digits > 0, "invalid ggsw: `digits` == 0");
debug_assert!(
size > digits,
"invalid ggsw: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid ggsw: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
Self {
data: module.vmp_pmat_alloc(rows, rank + 1, rank + 1, k.div_ceil(basek)),
basek,
k: k,
digits,
}
}
pub fn bytes_of(module: &Module<B>, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize
where
Module<B>: GGSWLayoutFamily<B>,
{
let size: usize = k.div_ceil(basek);
debug_assert!(
size > digits,
"invalid ggsw: ceil(k/basek): {} <= digits: {}",
size,
digits
);
assert!(
rows * digits <= size,
"invalid ggsw: rows: {} * digits:{} > ceil(k/basek): {}",
rows,
digits,
size
);
module.vmp_pmat_alloc_bytes(rows, rank + 1, rank + 1, size)
}
pub fn from<DataOther: DataRef>(
module: &Module<B>,
other: &GGSWCiphertext<DataOther>,
scratch: &mut Scratch<B>,
) -> GGSWCiphertextExec<Vec<u8>, B>
where
Module<B>: GGSWLayoutFamily<B>,
{
let mut ggsw_exec: GGSWCiphertextExec<Vec<u8>, B> = Self::alloc(
module,
other.basek(),
other.k(),
other.rows(),
other.digits(),
other.rank(),
);
ggsw_exec.prepare(module, other, scratch);
ggsw_exec
}
}
impl<D: Data, B: Backend> Infos for GGSWCiphertextExec<D, B> {
type Inner = VmpPMat<D, B>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<D: Data, B: Backend> GGSWCiphertextExec<D, B> {
pub fn rank(&self) -> usize {
self.data.cols_out() - 1
}
pub fn digits(&self) -> usize {
self.digits
}
}
impl<DataSelf: DataMut, B: Backend> GGSWCiphertextExec<DataSelf, B> {
pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &GGSWCiphertext<DataOther>, scratch: &mut Scratch<B>)
where
DataOther: DataRef,
Module<B>: GGSWLayoutFamily<B>,
{
module.vmp_prepare(&mut self.data, &other.data, scratch);
self.k = other.k;
self.basek = other.basek;
self.digits = other.digits;
}
}

View File

@@ -3,12 +3,16 @@ mod encryption;
mod external_product; mod external_product;
mod keyswitch; mod keyswitch;
mod layout; mod layout;
mod layout_compressed;
mod layout_exec;
mod noise; mod noise;
pub use encryption::GGSWEncryptSkFamily; pub use encryption::*;
pub use keyswitch::GGSWKeySwitchFamily; pub use keyswitch::*;
pub use layout::{GGSWCiphertext, GGSWCiphertextExec, GGSWLayoutFamily}; pub use layout::*;
pub use noise::GGSWAssertNoiseFamily; pub use layout_compressed::*;
pub use layout_exec::*;
pub use noise::*;
#[cfg(test)] #[cfg(test)]
mod test; mod test;

View File

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

View File

@@ -1,8 +1,8 @@
use backend::hal::{ use backend::hal::{
api::{ api::{
MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScalarZnxAutomorphismInplace, ScratchOwnedAlloc, MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
ScratchOwnedBorrow, VecZnxAddScalarInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphismInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxAutomorphismInplace, VecZnxRotateInplace, VecZnxStd,
VecZnxRotateInplace, VecZnxStd, VecZnxSubABInplace, VecZnxSwithcDegree, ZnxViewMut, VecZnxSubABInplace, VecZnxSwithcDegree, ZnxViewMut,
}, },
layouts::{Backend, Module, ScalarZnx, ScalarZnxToMut, ScratchOwned}, layouts::{Backend, Module, ScalarZnx, ScalarZnxToMut, ScratchOwned},
oep::{ oep::{
@@ -321,8 +321,8 @@ pub(crate) fn test_automorphism<B: Backend>(
+ GGLWEExecLayoutFamily<B> + GGLWEExecLayoutFamily<B>
+ VecZnxSwithcDegree + VecZnxSwithcDegree
+ VecZnxAutomorphismInplace + VecZnxAutomorphismInplace
+ ScalarZnxAutomorphismInplace + VecZnxAutomorphismInplace
+ ScalarZnxAutomorphism, + VecZnxAutomorphism,
B: TestScratchFamily<B>, B: TestScratchFamily<B>,
{ {
let rows: usize = k_in.div_ceil(basek * digits); let rows: usize = k_in.div_ceil(basek * digits);
@@ -393,7 +393,7 @@ pub(crate) fn test_automorphism<B: Backend>(
ct_out.automorphism(module, &ct_in, &auto_key_exec, &tsk_exec, scratch.borrow()); ct_out.automorphism(module, &ct_in, &auto_key_exec, &tsk_exec, scratch.borrow());
module.scalar_znx_automorphism_inplace(p, &mut pt_scalar, 0); module.vec_znx_automorphism_inplace(p, &mut pt_scalar.as_vec_znx_mut(), 0);
let max_noise = |col_j: usize| -> f64 { let max_noise = |col_j: usize| -> f64 {
noise_ggsw_keyswitch( noise_ggsw_keyswitch(
@@ -433,8 +433,8 @@ pub(crate) fn test_automorphism_inplace<B: Backend>(
+ GGLWEExecLayoutFamily<B> + GGLWEExecLayoutFamily<B>
+ VecZnxSwithcDegree + VecZnxSwithcDegree
+ VecZnxAutomorphismInplace + VecZnxAutomorphismInplace
+ ScalarZnxAutomorphismInplace + VecZnxAutomorphism
+ ScalarZnxAutomorphism, + VecZnxAutomorphismInplace,
B: TestScratchFamily<B>, B: TestScratchFamily<B>,
{ {
let rows: usize = k_ct.div_ceil(digits * basek); let rows: usize = k_ct.div_ceil(digits * basek);
@@ -501,7 +501,7 @@ pub(crate) fn test_automorphism_inplace<B: Backend>(
ct.automorphism_inplace(module, &auto_key_exec, &tsk_exec, scratch.borrow()); ct.automorphism_inplace(module, &auto_key_exec, &tsk_exec, scratch.borrow());
module.scalar_znx_automorphism_inplace(p, &mut pt_scalar, 0); module.vec_znx_automorphism_inplace(p, &mut pt_scalar.as_vec_znx_mut(), 0);
let max_noise = |col_j: usize| -> f64 { let max_noise = |col_j: usize| -> f64 {
noise_ggsw_keyswitch( noise_ggsw_keyswitch(
@@ -595,7 +595,7 @@ pub(crate) fn test_external_product<B: Backend>(
ct_ggsw_lhs_out.external_product(module, &ct_ggsw_lhs_in, &ct_rhs_exec, scratch.borrow()); ct_ggsw_lhs_out.external_product(module, &ct_ggsw_lhs_in, &ct_rhs_exec, scratch.borrow());
module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs, 0); module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs.as_vec_znx_mut(), 0);
let var_gct_err_lhs: f64 = sigma * sigma; let var_gct_err_lhs: f64 = sigma * sigma;
let var_gct_err_rhs: f64 = 0f64; let var_gct_err_rhs: f64 = 0f64;
@@ -695,7 +695,7 @@ pub(crate) fn test_external_product_inplace<B: Backend>(
ct_ggsw_lhs.external_product_inplace(module, &ct_rhs_exec, scratch.borrow()); ct_ggsw_lhs.external_product_inplace(module, &ct_rhs_exec, scratch.borrow());
module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs, 0); module.vec_znx_rotate_inplace(k as i64, &mut pt_ggsw_lhs.as_vec_znx_mut(), 0);
let var_gct_err_lhs: f64 = sigma * sigma; let var_gct_err_lhs: f64 = sigma * sigma;
let var_gct_err_rhs: f64 = 0f64; let var_gct_err_rhs: f64 = 0f64;

View File

@@ -1,2 +1,2 @@
mod cpu_spqlios;
mod generic_tests; mod generic_tests;
mod test_fft64;

View File

@@ -3,13 +3,17 @@ use backend::hal::{
ScalarZnxAllocBytes, ScratchAvailable, SvpApply, SvpApplyInplace, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx, ScalarZnxAllocBytes, ScratchAvailable, SvpApply, SvpApplyInplace, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx,
TakeSvpPPol, TakeVecZnx, TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal, VecZnxBigAddNormal, VecZnxBigAddSmallInplace, TakeSvpPPol, TakeVecZnx, TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal, VecZnxBigAddNormal, VecZnxBigAddSmallInplace,
VecZnxBigAllocBytes, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftFromVecZnx, VecZnxDftToVecZnxBigConsume, VecZnxBigAllocBytes, VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftFromVecZnx, VecZnxDftToVecZnxBigConsume,
VecZnxFillUniform, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSubABInplace, ZnxZero, VecZnxFillUniform, VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSubABInplace, ZnxInfos,
ZnxZero,
}, },
layouts::{Backend, DataMut, DataRef, Module, Scratch, VecZnxBig}, layouts::{Backend, DataMut, DataRef, Module, Scratch, VecZnx, VecZnxBig},
}; };
use sampling::source::Source; use sampling::source::Source;
use crate::{GLWECiphertext, GLWEPlaintext, GLWEPublicKeyExec, GLWESecretExec, Infos, SIX_SIGMA, dist::Distribution}; use crate::{
GLWECiphertext, GLWECiphertextCompressed, GLWEPlaintext, GLWEPublicKeyExec, GLWESecretExec, Infos, SIX_SIGMA,
dist::Distribution,
};
pub trait GLWEEncryptSkFamily<B: Backend> = VecZnxDftAllocBytes pub trait GLWEEncryptSkFamily<B: Backend> = VecZnxDftAllocBytes
+ VecZnxBigNormalize<B> + VecZnxBigNormalize<B>
@@ -71,7 +75,20 @@ impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
Module<B>: GLWEEncryptSkFamily<B>, Module<B>: GLWEEncryptSkFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx<B>, Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx<B>,
{ {
self.encrypt_sk_private( #[cfg(debug_assertions)]
{
assert_eq!(self.rank(), sk.rank());
assert_eq!(sk.n(), self.n());
assert_eq!(pt.n(), self.n());
assert!(
scratch.available() >= GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k()),
"scratch.available(): {} < GLWECiphertext::encrypt_sk_scratch_space: {}",
scratch.available(),
GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k())
)
}
self.encrypt_sk_internal(
module, module,
Some((pt, 0)), Some((pt, 0)),
sk, sk,
@@ -94,7 +111,18 @@ impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
Module<B>: GLWEEncryptSkFamily<B>, Module<B>: GLWEEncryptSkFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx<B>, Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx<B>,
{ {
self.encrypt_sk_private( #[cfg(debug_assertions)]
{
assert_eq!(self.rank(), sk.rank());
assert_eq!(sk.n(), self.n());
assert!(
scratch.available() >= GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k()),
"scratch.available(): {} < GLWECiphertext::encrypt_sk_scratch_space: {}",
scratch.available(),
GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k())
)
}
self.encrypt_sk_internal(
module, module,
None::<(&GLWEPlaintext<Vec<u8>>, usize)>, None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
sk, sk,
@@ -105,6 +133,34 @@ impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
); );
} }
pub(crate) fn encrypt_sk_internal<DataPt: DataRef, DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
sk: &GLWESecretExec<DataSk, B>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEEncryptSkFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx<B>,
{
encrypt_sk_internal(
module,
self.basek(),
self.k(),
&mut self.data,
false,
pt,
sk,
source_xa,
source_xe,
sigma,
scratch,
);
}
pub fn encrypt_pk<DataPt: DataRef, DataPk: DataRef, B: Backend>( pub fn encrypt_pk<DataPt: DataRef, DataPk: DataRef, B: Backend>(
&mut self, &mut self,
module: &Module<B>, module: &Module<B>,
@@ -118,7 +174,7 @@ impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
Module<B>: GLWEEncryptPkFamily<B>, Module<B>: GLWEEncryptPkFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + TakeSvpPPol<B> + TakeScalarZnx<B>, Scratch<B>: TakeVecZnxDft<B> + TakeSvpPPol<B> + TakeScalarZnx<B>,
{ {
self.encrypt_pk_private::<DataPt, DataPk, B>( self.encrypt_pk_internal::<DataPt, DataPk, B>(
module, module,
Some((pt, 0)), Some((pt, 0)),
pk, pk,
@@ -141,7 +197,7 @@ impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
Module<B>: GLWEEncryptPkFamily<B>, Module<B>: GLWEEncryptPkFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + TakeSvpPPol<B> + TakeScalarZnx<B>, Scratch<B>: TakeVecZnxDft<B> + TakeSvpPPol<B> + TakeScalarZnx<B>,
{ {
self.encrypt_pk_private::<Vec<u8>, DataPk, B>( self.encrypt_pk_internal::<Vec<u8>, DataPk, B>(
module, module,
None::<(&GLWEPlaintext<Vec<u8>>, usize)>, None::<(&GLWEPlaintext<Vec<u8>>, usize)>,
pk, pk,
@@ -152,89 +208,7 @@ impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
); );
} }
pub(crate) fn encrypt_sk_private<DataPt: DataRef, DataSk: DataRef, B: Backend>( pub(crate) fn encrypt_pk_internal<DataPt: DataRef, DataPk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
sk: &GLWESecretExec<DataSk, B>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEEncryptSkFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx<B>,
{
#[cfg(debug_assertions)]
{
assert_eq!(self.rank(), sk.rank());
assert_eq!(sk.n(), module.n());
assert_eq!(self.n(), module.n());
if let Some((pt, col)) = pt {
assert_eq!(pt.n(), module.n());
assert!(col < self.rank() + 1);
}
assert!(
scratch.available() >= GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k()),
"scratch.available(): {} < GLWECiphertext::encrypt_sk_scratch_space: {}",
scratch.available(),
GLWECiphertext::encrypt_sk_scratch_space(module, self.basek(), self.k())
)
}
let basek: usize = self.basek();
let k: usize = self.k();
let size: usize = self.size();
let cols: usize = self.rank() + 1;
let (mut c0, scratch_1) = scratch.take_vec_znx(module, 1, size);
c0.zero();
{
// c[i] = uniform
// c[0] -= c[i] * s[i],
(1..cols).for_each(|i| {
let (mut ci_dft, scratch_2) = scratch_1.take_vec_znx_dft(module, 1, size);
// c[i] = uniform
module.vec_znx_fill_uniform(basek, &mut self.data, i, k, source_xa);
// c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i])))
module.vec_znx_dft_from_vec_znx(1, 0, &mut ci_dft, 0, &self.data, i);
module.svp_apply_inplace(&mut ci_dft, 0, &sk.data, i - 1);
let ci_big: VecZnxBig<&mut [u8], B> = module.vec_znx_dft_to_vec_znx_big_consume(ci_dft);
// use c[0] as buffer, which is overwritten later by the normalization step
module.vec_znx_big_normalize(basek, &mut self.data, 0, &ci_big, 0, scratch_2);
// c0_tmp = -c[i] * s[i] (use c[0] as buffer)
module.vec_znx_sub_ab_inplace(&mut c0, 0, &self.data, 0);
// c[i] += m if col = i
if let Some((pt, col)) = pt {
if i == col {
module.vec_znx_add_inplace(&mut self.data, i, &pt.data, 0);
module.vec_znx_normalize_inplace(basek, &mut self.data, i, scratch_2);
}
}
});
}
// c[0] += e
module.vec_znx_add_normal(basek, &mut c0, 0, k, source_xe, sigma, sigma * SIX_SIGMA);
// c[0] += m if col = 0
if let Some((pt, col)) = pt {
if col == 0 {
module.vec_znx_add_inplace(&mut c0, 0, &pt.data, 0);
}
}
// c[0] = norm(c[0])
module.vec_znx_normalize(basek, &mut self.data, 0, &c0, 0, scratch_1);
}
pub(crate) fn encrypt_pk_private<DataPt: DataRef, DataPk: DataRef, B: Backend>(
&mut self, &mut self,
module: &Module<B>, module: &Module<B>,
pt: Option<(&GLWEPlaintext<DataPt>, usize)>, pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
@@ -323,3 +297,158 @@ impl<DataSelf: DataMut> GLWECiphertext<DataSelf> {
}); });
} }
} }
impl GLWECiphertextCompressed<Vec<u8>> {
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize) -> usize
where
Module<B>: GLWEEncryptSkFamily<B>,
{
GLWECiphertext::encrypt_sk_scratch_space(module, basek, k)
}
}
impl<D: DataMut> GLWECiphertextCompressed<D> {
pub fn encrypt_sk<DataPt: DataRef, DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: &GLWEPlaintext<DataPt>,
sk: &GLWESecretExec<DataSk, B>,
seed_xa: [u8; 32],
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEEncryptSkFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx<B>,
{
self.encrypt_sk_internal(
module,
Some((pt, 0)),
sk,
seed_xa,
source_xe,
sigma,
scratch,
);
}
pub(crate) fn encrypt_sk_internal<DataPt: DataRef, DataSk: DataRef, B: Backend>(
&mut self,
module: &Module<B>,
pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
sk: &GLWESecretExec<DataSk, B>,
seed_xa: [u8; 32],
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEEncryptSkFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx<B>,
{
let mut source_xa = Source::new(seed_xa);
encrypt_sk_internal(
module,
self.basek(),
self.k(),
&mut self.data,
true,
pt,
sk,
&mut source_xa,
source_xe,
sigma,
scratch,
);
self.seed = seed_xa;
}
}
pub(crate) fn encrypt_sk_internal<DataCt: DataMut, DataPt: DataRef, DataSk: DataRef, B: Backend>(
module: &Module<B>,
basek: usize,
k: usize,
ct: &mut VecZnx<DataCt>,
compressed: bool,
pt: Option<(&GLWEPlaintext<DataPt>, usize)>,
sk: &GLWESecretExec<DataSk, B>,
source_xa: &mut Source,
source_xe: &mut Source,
sigma: f64,
scratch: &mut Scratch<B>,
) where
Module<B>: GLWEEncryptSkFamily<B>,
Scratch<B>: TakeVecZnxDft<B> + ScratchAvailable + TakeVecZnx<B>,
{
#[cfg(debug_assertions)]
{
if compressed {
if let Some((_, col)) = pt {
assert_eq!(
col, 0,
"invalid plaintext: cannot put pt in col>0 if compressed encryption"
)
}
assert_eq!(
ct.cols(),
1,
"invalid ciphertext: compressed tag=true but #cols={} != 1",
ct.cols()
)
}
}
let size: usize = ct.size();
let cols: usize = ct.cols();
let (mut c0, scratch_1) = scratch.take_vec_znx(module, 1, size);
c0.zero();
{
// c[i] = uniform
// c[0] -= c[i] * s[i],
(1..cols).for_each(|i| {
let col_ct: usize;
if compressed {
col_ct = 0;
} else {
col_ct = i;
}
let (mut ci_dft, scratch_2) = scratch_1.take_vec_znx_dft(module, 1, size);
// c[i] = uniform
module.vec_znx_fill_uniform(basek, ct, col_ct, k, source_xa);
// c[i] = norm(IDFT(DFT(c[i]) * DFT(s[i])))
module.vec_znx_dft_from_vec_znx(1, 0, &mut ci_dft, 0, ct, col_ct);
module.svp_apply_inplace(&mut ci_dft, 0, &sk.data, i - 1);
let ci_big: VecZnxBig<&mut [u8], B> = module.vec_znx_dft_to_vec_znx_big_consume(ci_dft);
// use c[0] as buffer, which is overwritten later by the normalization step
module.vec_znx_big_normalize(basek, ct, 0, &ci_big, 0, scratch_2);
// c0_tmp = -c[i] * s[i] (use c[0] as buffer)
module.vec_znx_sub_ab_inplace(&mut c0, 0, ct, 0);
// c[i] += m if col = i
// note: case cannot happen if compressed = true
if let Some((pt, col)) = pt {
if i == col {
module.vec_znx_add_inplace(ct, i, &pt.data, 0);
}
}
});
}
// c[0] += e
module.vec_znx_add_normal(basek, &mut c0, 0, k, source_xe, sigma, sigma * SIX_SIGMA);
// c[0] += m if col = 0
if let Some((pt, col)) = pt {
if col == 0 {
module.vec_znx_add_inplace(&mut c0, 0, &pt.data, 0);
}
}
// c[0] = norm(c[0])
module.vec_znx_normalize(basek, ct, 0, &c0, 0, scratch_1);
}

View File

@@ -1,17 +1,54 @@
use std::fmt::Debug;
use backend::hal::{ use backend::hal::{
api::{VecZnxAlloc, VecZnxAllocBytes}, api::{FillUniform, VecZnxAlloc, VecZnxAllocBytes, VecZnxCopy, VecZnxFillUniform, ZnxInfos, ZnxZero},
layouts::{Backend, Data, DataMut, DataRef, Module, ReaderFrom, VecZnx, VecZnxToMut, VecZnxToRef, WriterTo}, layouts::{Backend, Data, DataMut, DataRef, Module, ReaderFrom, VecZnx, VecZnxToMut, VecZnxToRef, WriterTo},
}; };
use sampling::source::Source;
use crate::{GLWEOps, Infos, SetMetaData}; use crate::{GLWEOps, Infos, SetMetaData};
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq, Clone)]
pub struct GLWECiphertext<D: Data> { pub struct GLWECiphertext<D: Data> {
pub data: VecZnx<D>, pub data: VecZnx<D>,
pub basek: usize, pub basek: usize,
pub k: usize, pub k: usize,
} }
impl<D: DataRef> Debug for GLWECiphertext<D> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"GLWECiphertext: basek={} k={}: {}",
self.basek(),
self.k(),
self.data
)
}
}
impl<D: DataMut> ZnxZero for GLWECiphertext<D>
where
VecZnx<D>: ZnxZero,
{
fn zero(&mut self) {
self.data.zero()
}
fn zero_at(&mut self, i: usize, j: usize) {
self.data.zero_at(i, j);
}
}
impl<D: DataMut> FillUniform for GLWECiphertext<D>
where
VecZnx<D>: FillUniform,
{
fn fill_uniform(&mut self, source: &mut Source) {
self.data.fill_uniform(source);
}
}
impl GLWECiphertext<Vec<u8>> { impl GLWECiphertext<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self
where where
@@ -121,3 +158,165 @@ impl<D: DataRef> WriterTo for GLWECiphertext<D> {
self.data.write_to(writer) self.data.write_to(writer)
} }
} }
#[derive(PartialEq, Eq, Clone)]
pub struct GLWECiphertextCompressed<D: Data> {
pub(crate) data: VecZnx<D>,
pub(crate) basek: usize,
pub(crate) k: usize,
pub(crate) rank: usize,
pub(crate) seed: [u8; 32],
}
impl<D: DataRef> Debug for GLWECiphertextCompressed<D> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"GLWECiphertext: basek={} k={}: {}",
self.basek(),
self.k(),
self.data
)
}
}
impl<D: DataMut> ZnxZero for GLWECiphertextCompressed<D>
where
VecZnx<D>: ZnxZero,
{
fn zero(&mut self) {
self.data.zero()
}
fn zero_at(&mut self, i: usize, j: usize) {
self.data.zero_at(i, j);
}
}
impl<D: DataMut> FillUniform for GLWECiphertextCompressed<D>
where
VecZnx<D>: FillUniform,
{
fn fill_uniform(&mut self, source: &mut Source) {
self.data.fill_uniform(source);
}
}
impl<D: Data> Infos for GLWECiphertextCompressed<D> {
type Inner = VecZnx<D>;
fn inner(&self) -> &Self::Inner {
&self.data
}
fn basek(&self) -> usize {
self.basek
}
fn k(&self) -> usize {
self.k
}
}
impl<D: Data> GLWECiphertextCompressed<D> {
pub fn rank(&self) -> usize {
self.rank
}
}
impl GLWECiphertextCompressed<Vec<u8>> {
pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank: usize) -> Self
where
Module<B>: VecZnxAlloc,
{
Self {
data: module.vec_znx_alloc(1, k.div_ceil(basek)),
basek,
k,
rank,
seed: [0u8; 32],
}
}
pub fn bytes_of<B: Backend>(module: &Module<B>, basek: usize, k: usize) -> usize
where
Module<B>: VecZnxAllocBytes,
{
GLWECiphertext::bytes_of(module, basek, k, 1)
}
}
impl<D: DataMut> ReaderFrom for GLWECiphertextCompressed<D> {
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
self.k = reader.read_u64::<LittleEndian>()? as usize;
self.basek = reader.read_u64::<LittleEndian>()? as usize;
self.rank = reader.read_u64::<LittleEndian>()? as usize;
reader.read(&mut self.seed)?;
self.data.read_from(reader)
}
}
impl<D: DataRef> WriterTo for GLWECiphertextCompressed<D> {
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_u64::<LittleEndian>(self.k as u64)?;
writer.write_u64::<LittleEndian>(self.basek as u64)?;
writer.write_u64::<LittleEndian>(self.rank as u64)?;
writer.write_all(&self.seed)?;
self.data.write_to(writer)
}
}
impl<D: DataMut> GLWECiphertext<D> {
pub fn decompress<DataOther, B: Backend>(&mut self, module: &Module<B>, other: &GLWECiphertextCompressed<DataOther>)
where
DataOther: DataRef,
Module<B>: VecZnxFillUniform + VecZnxCopy,
{
#[cfg(debug_assertions)]
{
assert_eq!(
self.n(),
other.data.n(),
"invalid receiver: self.n()={} != other.n()={}",
self.n(),
other.data.n()
);
assert_eq!(
self.size(),
other.size(),
"invalid receiver: self.size()={} != other.size()={}",
self.size(),
other.size()
);
assert_eq!(
self.rank(),
other.rank(),
"invalid receiver: self.rank()={} != other.rank()={}",
self.rank(),
other.rank()
);
let mut source: Source = Source::new(other.seed);
self.decompress_internal(module, other, &mut source);
}
}
pub(crate) fn decompress_internal<DataOther, B: Backend>(
&mut self,
module: &Module<B>,
other: &GLWECiphertextCompressed<DataOther>,
source: &mut Source,
) where
DataOther: DataRef,
Module<B>: VecZnxFillUniform + VecZnxCopy,
{
let k: usize = other.k;
let basek: usize = other.basek;
let cols: usize = other.cols();
module.vec_znx_copy(&mut self.data, 0, &other.data, 0);
(1..cols).for_each(|i| {
module.vec_znx_fill_uniform(basek, &mut self.data, i, k, source);
});
self.basek = basek;
self.k = k;
}
}

View File

@@ -12,6 +12,9 @@ mod public_key;
mod secret; mod secret;
mod trace; mod trace;
#[cfg(test)]
mod tests;
pub use decryption::*; pub use decryption::*;
pub use encryption::*; pub use encryption::*;
pub use external_product::*; pub use external_product::*;
@@ -22,6 +25,3 @@ pub use packing::*;
pub use plaintext::*; pub use plaintext::*;
pub use public_key::*; pub use public_key::*;
pub use secret::*; pub use secret::*;
#[cfg(test)]
mod test_fft64;

View File

@@ -8,7 +8,7 @@ use crate::dist::Distribution;
pub trait GLWESecretFamily<B: Backend> = SvpPrepare<B> + SvpPPolAllocBytes + SvpPPolAlloc<B>; pub trait GLWESecretFamily<B: Backend> = SvpPrepare<B> + SvpPPolAllocBytes + SvpPPolAlloc<B>;
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq, Clone)]
pub struct GLWESecret<D: Data> { pub struct GLWESecret<D: Data> {
pub(crate) data: ScalarZnx<D>, pub(crate) data: ScalarZnx<D>,
pub(crate) dist: Distribution, pub(crate) dist: Distribution,

View File

@@ -1,6 +0,0 @@
pub mod automorphism;
pub mod encryption;
pub mod external_product;
pub mod keyswitch;
pub mod packing;
pub mod trace;

View File

@@ -0,0 +1,191 @@
use backend::{
hal::{api::ModuleNew, layouts::Module},
implementation::cpu_spqlios::FFT64,
};
use crate::glwe::tests::{
generic_automorphism::{test_automorphism, test_automorphism_inplace},
generic_encryption::{test_encrypt_pk, test_encrypt_sk, test_encrypt_sk_compressed, test_encrypt_zero_sk},
generic_external_product::{test_external_product, test_external_product_inplace},
generic_keyswitch::{test_keyswitch, test_keyswitch_inplace},
generic_serialization::{test_serialization, test_serialization_compressed},
packing::test_packing,
trace::test_trace_inplace,
};
#[test]
fn encrypt_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test encrypt_sk rank: {}", rank);
test_encrypt_sk(&module, 8, 54, 30, 3.2, rank);
});
}
#[test]
fn encrypt_sk_compressed() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test encrypt_sk rank: {}", rank);
test_encrypt_sk_compressed(&module, 8, 54, 30, 3.2, rank);
});
}
#[test]
fn encrypt_zero_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test encrypt_zero_sk rank: {}", rank);
test_encrypt_zero_sk(&module, 8, 64, 3.2, rank);
});
}
#[test]
fn encrypt_pk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test encrypt_pk rank: {}", rank);
test_encrypt_pk(&module, 8, 64, 64, 3.2, rank)
});
}
#[test]
fn apply() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
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(
&module, basek, k_out, k_in, k_ksk, di, rank_in, rank_out, 3.2,
);
})
});
});
}
#[test]
fn apply_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
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(&module, basek, k_ct, k_ksk, di, rank, 3.2);
});
});
}
#[test]
fn automorphism_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
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(&module, basek, -5, k_ct, k_ksk, di, rank, 3.2);
});
});
}
#[test]
fn automorphism() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
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(&module, basek, -5, k_out, k_in, k_ksk, di, rank, 3.2);
})
});
}
#[test]
fn external_product() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 45;
let digits: usize = k_in.div_ceil(basek);
(1..4).for_each(|rank| {
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_in + basek * di;
let k_out: usize = k_ggsw; // Better capture noise
println!("test external_product digits: {} rank: {}", di, rank);
test_external_product(&module, basek, k_out, k_in, k_ggsw, di, rank, 3.2);
});
});
}
#[test]
fn external_product_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
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_ggsw: usize = k_ct + basek * di;
println!("test external_product digits: {} rank: {}", di, rank);
test_external_product_inplace(&module, basek, k_ct, k_ggsw, di, rank, 3.2);
});
});
}
#[test]
fn trace_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test trace_inplace rank: {}", rank);
test_trace_inplace(&module, 8, 54, 3.2, rank);
});
}
#[test]
fn packing() {
let log_n: usize = 5;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
test_packing(&module);
}
#[test]
fn serialization() {
let log_n: usize = 5;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
test_serialization(&module);
}
#[test]
fn serialization_compressed() {
let log_n: usize = 5;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
test_serialization_compressed(&module);
}

View File

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

View File

@@ -1,17 +1,14 @@
use backend::{ use backend::hal::{
hal::{
api::{ api::{
MatZnxAlloc, ModuleNew, ScalarZnxAlloc, ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScratchOwnedAlloc, MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
ScratchOwnedBorrow, VecZnxAddScalarInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphismInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxAutomorphismInplace, VecZnxFillUniform, VecZnxStd,
VecZnxFillUniform, VecZnxStd, VecZnxSwithcDegree, VecZnxSwithcDegree,
}, },
layouts::{Backend, Module, ScratchOwned}, layouts::{Backend, Module, ScratchOwned},
oep::{ oep::{
ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl,
}, },
},
implementation::cpu_spqlios::FFT64,
}; };
use sampling::source::Source; use sampling::source::Source;
@@ -21,39 +18,6 @@ use crate::{
noise::log2_std_noise_gglwe_product, noise::log2_std_noise_gglwe_product,
}; };
#[test]
fn apply_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
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(&module, basek, -5, k_ct, k_ksk, di, rank, 3.2);
});
});
}
#[test]
fn apply() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
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(&module, basek, -5, k_out, k_in, k_ksk, di, rank, 3.2);
})
});
}
pub(crate) trait AutomorphismTestModuleFamily<B: Backend> = AutomorphismKeyEncryptSkFamily<B> pub(crate) trait AutomorphismTestModuleFamily<B: Backend> = AutomorphismKeyEncryptSkFamily<B>
+ GLWEDecryptFamily<B> + GLWEDecryptFamily<B>
+ GGLWEExecLayoutFamily<B> + GGLWEExecLayoutFamily<B>
@@ -62,7 +26,7 @@ pub(crate) trait AutomorphismTestModuleFamily<B: Backend> = AutomorphismKeyEncry
+ VecZnxAlloc + VecZnxAlloc
+ ScalarZnxAllocBytes + ScalarZnxAllocBytes
+ VecZnxAllocBytes + VecZnxAllocBytes
+ ScalarZnxAutomorphism + VecZnxAutomorphism
+ VecZnxSwithcDegree + VecZnxSwithcDegree
+ ScalarZnxAlloc + ScalarZnxAlloc
+ VecZnxAddScalarInplace + VecZnxAddScalarInplace
@@ -77,7 +41,7 @@ pub(crate) trait AutomorphismTestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B
+ TakeScalarZnxImpl<B> + TakeScalarZnxImpl<B>
+ TakeVecZnxImpl<B>; + TakeVecZnxImpl<B>;
fn test_automorphism<B: Backend>( pub(crate) fn test_automorphism<B: Backend>(
module: &Module<B>, module: &Module<B>,
basek: usize, basek: usize,
p: i64, p: i64,
@@ -166,7 +130,7 @@ fn test_automorphism<B: Backend>(
ct_out.assert_noise(module, &sk_exec, &pt_want, max_noise + 1.0); ct_out.assert_noise(module, &sk_exec, &pt_want, max_noise + 1.0);
} }
fn test_automorphism_inplace<B: Backend>( pub(crate) fn test_automorphism_inplace<B: Backend>(
module: &Module<B>, module: &Module<B>,
basek: usize, basek: usize,
p: i64, p: i64,

View File

@@ -1,7 +1,6 @@
use backend::{ use backend::hal::{
hal::{
api::{ api::{
ModuleNew, ScalarZnxAlloc, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAlloc, VecZnxDftAlloc, VecZnxFillUniform, ScalarZnxAlloc, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAlloc, VecZnxCopy, VecZnxDftAlloc, VecZnxFillUniform,
VecZnxStd, VecZnxSubABInplace, VecZnxStd, VecZnxSubABInplace,
}, },
layouts::{Backend, Module, ScratchOwned}, layouts::{Backend, Module, ScratchOwned},
@@ -9,46 +8,14 @@ use backend::{
ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl,
}, },
},
implementation::cpu_spqlios::FFT64,
}; };
use sampling::source::Source; use sampling::source::Source;
use crate::{ use crate::{
GLWECiphertext, GLWEDecryptFamily, GLWEEncryptPkFamily, GLWEEncryptSkFamily, GLWEOps, GLWEPlaintext, GLWEPublicKey, GLWECiphertext, GLWECiphertextCompressed, GLWEDecryptFamily, GLWEEncryptPkFamily, GLWEEncryptSkFamily, GLWEOps,
GLWEPublicKeyExec, GLWESecret, GLWESecretExec, GLWESecretFamily, Infos, GLWEPlaintext, GLWEPublicKey, GLWEPublicKeyExec, GLWESecret, GLWESecretExec, GLWESecretFamily, Infos,
}; };
#[test]
fn encrypt_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test encrypt_sk rank: {}", rank);
test_encrypt_sk(&module, 8, 54, 30, 3.2, rank);
});
}
#[test]
fn encrypt_zero_sk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test encrypt_zero_sk rank: {}", rank);
test_encrypt_zero_sk(&module, 8, 64, 3.2, rank);
});
}
#[test]
fn encrypt_pk() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test encrypt_pk rank: {}", rank);
test_encrypt_pk(&module, 8, 64, 64, 3.2, rank)
});
}
pub(crate) trait EncryptionTestModuleFamily<B: Backend> = pub(crate) trait EncryptionTestModuleFamily<B: Backend> =
GLWEDecryptFamily<B> + GLWESecretFamily<B> + VecZnxAlloc + ScalarZnxAlloc + VecZnxStd; GLWEDecryptFamily<B> + GLWESecretFamily<B> + VecZnxAlloc + ScalarZnxAlloc + VecZnxStd;
@@ -61,7 +28,7 @@ pub(crate) trait EncryptionTestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
+ TakeScalarZnxImpl<B> + TakeScalarZnxImpl<B>
+ TakeVecZnxImpl<B>; + TakeVecZnxImpl<B>;
fn test_encrypt_sk<B: Backend>(module: &Module<B>, basek: usize, k_ct: usize, k_pt: usize, sigma: f64, rank: usize) pub(crate) fn test_encrypt_sk<B: Backend>(module: &Module<B>, basek: usize, k_ct: usize, k_pt: usize, sigma: f64, rank: usize)
where where
Module<B>: EncryptionTestModuleFamily<B> + GLWEEncryptSkFamily<B>, Module<B>: EncryptionTestModuleFamily<B> + GLWEEncryptSkFamily<B>,
B: EncryptionTestScratchFamily<B>, B: EncryptionTestScratchFamily<B>,
@@ -105,7 +72,63 @@ where
assert!(noise_have <= noise_want + 0.2); assert!(noise_have <= noise_want + 0.2);
} }
fn test_encrypt_zero_sk<B: Backend>(module: &Module<B>, basek: usize, k_ct: usize, sigma: f64, rank: usize) pub(crate) fn test_encrypt_sk_compressed<B: Backend>(
module: &Module<B>,
basek: usize,
k_ct: usize,
k_pt: usize,
sigma: f64,
rank: usize,
) where
Module<B>: EncryptionTestModuleFamily<B> + GLWEEncryptSkFamily<B> + VecZnxCopy,
B: EncryptionTestScratchFamily<B>,
{
let mut ct_compressed: GLWECiphertextCompressed<Vec<u8>> = GLWECiphertextCompressed::alloc(module, basek, k_ct, rank);
let mut pt_want: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(module, basek, k_pt);
let mut pt_have: 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<B> = ScratchOwned::alloc(
GLWECiphertextCompressed::encrypt_sk_scratch_space(module, basek, k_ct)
| GLWECiphertext::decrypt_scratch_space(module, basek, k_ct),
);
let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(module, rank);
sk.fill_ternary_prob(0.5, &mut source_xs);
let sk_exec: GLWESecretExec<Vec<u8>, B> = GLWESecretExec::from(module, &sk);
module.vec_znx_fill_uniform(basek, &mut pt_want.data, 0, k_pt, &mut source_xa);
let seed_xa: [u8; 32] = [1u8; 32];
ct_compressed.encrypt_sk(
module,
&pt_want,
&sk_exec,
seed_xa,
&mut source_xe,
sigma,
scratch.borrow(),
);
let mut ct: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(module, basek, k_ct, rank);
ct.decompress(module, &ct_compressed);
ct.decrypt(module, &mut pt_have, &sk_exec, scratch.borrow());
pt_want.sub_inplace_ab(module, &pt_have);
let noise_have: f64 = module.vec_znx_std(basek, &pt_want.data, 0) * (ct.k() as f64).exp2();
let noise_want: f64 = sigma;
assert!(noise_have <= noise_want + 0.2);
}
pub(crate) fn test_encrypt_zero_sk<B: Backend>(module: &Module<B>, basek: usize, k_ct: usize, sigma: f64, rank: usize)
where where
Module<B>: EncryptionTestModuleFamily<B> + GLWEEncryptSkFamily<B>, Module<B>: EncryptionTestModuleFamily<B> + GLWEEncryptSkFamily<B>,
B: EncryptionTestScratchFamily<B>, B: EncryptionTestScratchFamily<B>,
@@ -140,7 +163,7 @@ where
assert!((sigma - module.vec_znx_std(basek, &pt.data, 0) * (k_ct as f64).exp2()) <= 0.2); assert!((sigma - module.vec_znx_std(basek, &pt.data, 0) * (k_ct as f64).exp2()) <= 0.2);
} }
fn test_encrypt_pk<B: Backend>(module: &Module<B>, basek: usize, k_ct: usize, k_pk: usize, sigma: f64, rank: usize) pub(crate) fn test_encrypt_pk<B: Backend>(module: &Module<B>, basek: usize, k_ct: usize, k_pk: usize, sigma: f64, rank: usize)
where where
Module<B>: EncryptionTestModuleFamily<B> Module<B>: EncryptionTestModuleFamily<B>
+ GLWEEncryptPkFamily<B> + GLWEEncryptPkFamily<B>

View File

@@ -1,7 +1,6 @@
use backend::{ use backend::hal::{
hal::{
api::{ api::{
MatZnxAlloc, ModuleNew, ScalarZnxAlloc, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, VecZnxAlloc, MatZnxAlloc, ScalarZnxAlloc, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, VecZnxAlloc,
VecZnxAllocBytes, VecZnxFillUniform, VecZnxRotateInplace, VecZnxStd, ZnxViewMut, VecZnxAllocBytes, VecZnxFillUniform, VecZnxRotateInplace, VecZnxStd, ZnxViewMut,
}, },
layouts::{Backend, Module, ScalarZnx, ScratchOwned}, layouts::{Backend, Module, ScalarZnx, ScratchOwned},
@@ -9,8 +8,6 @@ use backend::{
ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl,
}, },
},
implementation::cpu_spqlios::FFT64,
}; };
use sampling::source::Source; use sampling::source::Source;
@@ -19,39 +16,6 @@ use crate::{
GLWEExternalProductFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, GLWESecretFamily, Infos, noise::noise_ggsw_product, GLWEExternalProductFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, GLWESecretFamily, Infos, noise::noise_ggsw_product,
}; };
#[test]
fn apply() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
let basek: usize = 12;
let k_in: usize = 45;
let digits: usize = k_in.div_ceil(basek);
(1..4).for_each(|rank| {
(1..digits + 1).for_each(|di| {
let k_ggsw: usize = k_in + basek * di;
let k_out: usize = k_ggsw; // Better capture noise
println!("test external_product digits: {} rank: {}", di, rank);
test_external_product(&module, basek, k_out, k_in, k_ggsw, di, rank, 3.2);
});
});
}
#[test]
fn apply_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
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_ggsw: usize = k_ct + basek * di;
println!("test external_product digits: {} rank: {}", di, rank);
test_external_product_inplace(&module, basek, k_ct, k_ggsw, di, rank, 3.2);
});
});
}
pub(crate) trait ExternalProductTestModuleFamily<B: Backend> = GLWEEncryptSkFamily<B> pub(crate) trait ExternalProductTestModuleFamily<B: Backend> = GLWEEncryptSkFamily<B>
+ GLWEDecryptFamily<B> + GLWEDecryptFamily<B>
+ GLWESecretFamily<B> + GLWESecretFamily<B>
@@ -74,7 +38,7 @@ pub(crate) trait ExternalProductTestScratchFamily<B: Backend> = TakeVecZnxDftImp
+ TakeScalarZnxImpl<B> + TakeScalarZnxImpl<B>
+ TakeVecZnxImpl<B>; + TakeVecZnxImpl<B>;
fn test_external_product<B: Backend>( pub(crate) fn test_external_product<B: Backend>(
module: &Module<B>, module: &Module<B>,
basek: usize, basek: usize,
k_out: usize, k_out: usize,
@@ -176,7 +140,7 @@ fn test_external_product<B: Backend>(
ct_glwe_out.assert_noise(module, &sk_exec, &pt_want, max_noise + 0.5); ct_glwe_out.assert_noise(module, &sk_exec, &pt_want, max_noise + 0.5);
} }
fn test_external_product_inplace<B: Backend>( pub(crate) fn test_external_product_inplace<B: Backend>(
module: &Module<B>, module: &Module<B>,
basek: usize, basek: usize,
k_ct: usize, k_ct: usize,

View File

@@ -1,16 +1,13 @@
use backend::{ use backend::hal::{
hal::{
api::{ api::{
MatZnxAlloc, ModuleNew, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
VecZnxAddScalarInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxFillUniform, VecZnxStd, VecZnxSwithcDegree, VecZnxAlloc, VecZnxAllocBytes, VecZnxFillUniform, VecZnxStd, VecZnxSwithcDegree,
}, },
layouts::{Backend, Module, ScratchOwned}, layouts::{Backend, Module, ScratchOwned},
oep::{ oep::{
ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl,
}, },
},
implementation::cpu_spqlios::FFT64,
}; };
use sampling::source::Source; use sampling::source::Source;
@@ -20,46 +17,6 @@ use crate::{
noise::log2_std_noise_gglwe_product, noise::log2_std_noise_gglwe_product,
}; };
#[test]
fn apply() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
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(
&module, basek, k_out, k_in, k_ksk, di, rank_in, rank_out, 3.2,
);
})
});
});
}
#[test]
fn apply_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
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(&module, basek, k_ct, k_ksk, di, rank, 3.2);
});
});
}
pub(crate) trait KeySwitchTestModuleFamily<B: Backend> = GLWESecretFamily<B> pub(crate) trait KeySwitchTestModuleFamily<B: Backend> = GLWESecretFamily<B>
+ GLWESwitchingKeyEncryptSkFamily<B> + GLWESwitchingKeyEncryptSkFamily<B>
+ GLWEKeyswitchFamily<B> + GLWEKeyswitchFamily<B>
@@ -83,7 +40,7 @@ pub(crate) trait KeySwitchTestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
+ TakeScalarZnxImpl<B> + TakeScalarZnxImpl<B>
+ TakeVecZnxImpl<B>; + TakeVecZnxImpl<B>;
fn test_keyswitch<B: Backend>( pub(crate) fn test_keyswitch<B: Backend>(
module: &Module<B>, module: &Module<B>,
basek: usize, basek: usize,
k_out: usize, k_out: usize,
@@ -173,7 +130,7 @@ fn test_keyswitch<B: Backend>(
ct_out.assert_noise(module, &sk_out_exec, &pt_want, max_noise + 0.5); ct_out.assert_noise(module, &sk_out_exec, &pt_want, max_noise + 0.5);
} }
fn test_keyswitch_inplace<B: Backend>( pub(crate) fn test_keyswitch_inplace<B: Backend>(
module: &Module<B>, module: &Module<B>,
basek: usize, basek: usize,
k_ct: usize, k_ct: usize,

View File

@@ -0,0 +1,23 @@
use backend::hal::{
api::VecZnxAlloc,
layouts::{Backend, Module},
tests::serialization::test_reader_writer_interface,
};
use crate::{GLWECiphertext, GLWECiphertextCompressed};
pub(crate) fn test_serialization<B: Backend>(module: &Module<B>)
where
Module<B>: VecZnxAlloc,
{
let original: GLWECiphertext<Vec<u8>> = GLWECiphertext::alloc(module, 12, 54, 3);
test_reader_writer_interface(original);
}
pub(crate) fn test_serialization_compressed<B: Backend>(module: &Module<B>)
where
Module<B>: VecZnxAlloc,
{
let original: GLWECiphertextCompressed<Vec<u8>> = GLWECiphertextCompressed::alloc(module, 12, 54, 3);
test_reader_writer_interface(original);
}

View File

@@ -0,0 +1,8 @@
mod cpu_spqlios;
mod generic_automorphism;
mod generic_encryption;
mod generic_external_product;
mod generic_keyswitch;
mod generic_serialization;
mod packing;
mod trace;

View File

@@ -1,19 +1,16 @@
use std::collections::HashMap; use std::collections::HashMap;
use backend::{ use backend::hal::{
hal::{
api::{ api::{
MatZnxAlloc, ModuleNew, ScalarZnxAlloc, ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScratchOwnedAlloc, MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
ScratchOwnedBorrow, VecZnxAddScalarInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxBigSubSmallBInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxBigSubSmallBInplace, VecZnxEncodeVeci64, VecZnxRotateInplace,
VecZnxEncodeVeci64, VecZnxRotateInplace, VecZnxStd, VecZnxSwithcDegree, VecZnxStd, VecZnxSwithcDegree,
}, },
layouts::{Backend, Module, ScratchOwned}, layouts::{Backend, Module, ScratchOwned},
oep::{ oep::{
ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl,
}, },
},
implementation::cpu_spqlios::FFT64,
}; };
use sampling::source::Source; use sampling::source::Source;
@@ -22,13 +19,6 @@ use crate::{
GLWEPacker, GLWEPackingFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, GLWESecretFamily, GLWESwitchingKeyEncryptSkFamily, GLWEPacker, GLWEPackingFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, GLWESecretFamily, GLWESwitchingKeyEncryptSkFamily,
}; };
#[test]
fn trace() {
let log_n: usize = 5;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
test_packing(&module);
}
pub(crate) trait PackingTestModuleFamily<B: Backend> = GLWEPackingFamily<B> pub(crate) trait PackingTestModuleFamily<B: Backend> = GLWEPackingFamily<B>
+ GLWESecretFamily<B> + GLWESecretFamily<B>
+ GLWESwitchingKeyEncryptSkFamily<B> + GLWESwitchingKeyEncryptSkFamily<B>
@@ -44,8 +34,8 @@ pub(crate) trait PackingTestModuleFamily<B: Backend> = GLWEPackingFamily<B>
+ VecZnxSwithcDegree + VecZnxSwithcDegree
+ VecZnxAddScalarInplace + VecZnxAddScalarInplace
+ VecZnxEncodeVeci64 + VecZnxEncodeVeci64
+ ScalarZnxAutomorphism
+ VecZnxRotateInplace + VecZnxRotateInplace
+ VecZnxAutomorphism
+ VecZnxBigSubSmallBInplace<B>; + VecZnxBigSubSmallBInplace<B>;
pub(crate) trait PackingTestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B> pub(crate) trait PackingTestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>

View File

@@ -1,20 +1,17 @@
use std::collections::HashMap; use std::collections::HashMap;
use backend::{ use backend::hal::{
hal::{
api::{ api::{
MatZnxAlloc, ModuleNew, ScalarZnxAlloc, ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScratchOwnedAlloc, MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace,
ScratchOwnedBorrow, VecZnxAddScalarInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxBigAutomorphismInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxBigAutomorphismInplace, VecZnxBigSubSmallBInplace, VecZnxCopy,
VecZnxBigSubSmallBInplace, VecZnxCopy, VecZnxEncodeVeci64, VecZnxFillUniform, VecZnxNormalizeInplace, VecZnxEncodeVeci64, VecZnxFillUniform, VecZnxNormalizeInplace, VecZnxRotateInplace, VecZnxRshInplace, VecZnxStd,
VecZnxRotateInplace, VecZnxRshInplace, VecZnxStd, VecZnxSubABInplace, VecZnxSwithcDegree, ZnxView, ZnxViewMut, VecZnxSubABInplace, VecZnxSwithcDegree, ZnxView, ZnxViewMut,
}, },
layouts::{Backend, Module, ScratchOwned}, layouts::{Backend, Module, ScratchOwned},
oep::{ oep::{
ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl,
TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl,
}, },
},
implementation::cpu_spqlios::FFT64,
}; };
use sampling::source::Source; use sampling::source::Source;
@@ -24,16 +21,6 @@ use crate::{
noise::var_noise_gglwe_product, noise::var_noise_gglwe_product,
}; };
#[test]
fn apply_inplace() {
let log_n: usize = 8;
let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n);
(1..4).for_each(|rank| {
println!("test trace_inplace rank: {}", rank);
test_trace_inplace(&module, 8, 54, 3.2, rank);
});
}
pub(crate) trait TraceTestModuleFamily<B: Backend> = GLWESecretFamily<B> pub(crate) trait TraceTestModuleFamily<B: Backend> = GLWESecretFamily<B>
+ GLWESwitchingKeyEncryptSkFamily<B> + GLWESwitchingKeyEncryptSkFamily<B>
+ GLWEKeyswitchFamily<B> + GLWEKeyswitchFamily<B>
@@ -48,11 +35,11 @@ pub(crate) trait TraceTestModuleFamily<B: Backend> = GLWESecretFamily<B>
+ VecZnxSwithcDegree + VecZnxSwithcDegree
+ VecZnxAddScalarInplace + VecZnxAddScalarInplace
+ VecZnxEncodeVeci64 + VecZnxEncodeVeci64
+ ScalarZnxAutomorphism
+ VecZnxRotateInplace + VecZnxRotateInplace
+ VecZnxBigSubSmallBInplace<B> + VecZnxBigSubSmallBInplace<B>
+ VecZnxBigAutomorphismInplace<B> + VecZnxBigAutomorphismInplace<B>
+ VecZnxCopy + VecZnxCopy
+ VecZnxAutomorphism
+ VecZnxRshInplace; + VecZnxRshInplace;
pub(crate) trait TraceTestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B> pub(crate) trait TraceTestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
@@ -64,7 +51,7 @@ pub(crate) trait TraceTestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
+ TakeScalarZnxImpl<B> + TakeScalarZnxImpl<B>
+ TakeVecZnxImpl<B>; + TakeVecZnxImpl<B>;
fn test_trace_inplace<B: Backend>(module: &Module<B>, basek: usize, k: usize, sigma: f64, rank: usize) pub(crate) fn test_trace_inplace<B: Backend>(module: &Module<B>, basek: usize, k: usize, sigma: f64, rank: usize)
where where
Module<B>: TraceTestModuleFamily<B>, Module<B>: TraceTestModuleFamily<B>,
B: TraceTestScratchFamily<B>, B: TraceTestScratchFamily<B>,

View File

@@ -14,7 +14,7 @@ pub struct LWECiphertext<D: Data> {
impl LWECiphertext<Vec<u8>> { impl LWECiphertext<Vec<u8>> {
pub fn alloc(n: usize, basek: usize, k: usize) -> Self { pub fn alloc(n: usize, basek: usize, k: usize) -> Self {
Self { Self {
data: VecZnx::new::<i64>(n + 1, 1, k.div_ceil(basek)), data: VecZnx::alloc::<i64>(n + 1, 1, k.div_ceil(basek)),
k: k, k: k,
basek: basek, basek: basek,
} }

View File

@@ -34,7 +34,7 @@ impl<DataSelf: DataMut> LWECiphertext<DataSelf> {
module.vec_znx_fill_uniform(basek, &mut self.data, 0, k, source_xa); module.vec_znx_fill_uniform(basek, &mut self.data, 0, k, source_xa);
let mut tmp_znx: VecZnx<Vec<u8>> = VecZnx::<Vec<u8>>::new::<i64>(1, 1, self.size()); let mut tmp_znx: VecZnx<Vec<u8>> = VecZnx::<Vec<u8>>::alloc::<i64>(1, 1, self.size());
let min_size = self.size().min(pt.size()); let min_size = self.size().min(pt.size());

View File

@@ -126,7 +126,7 @@ impl<D: DataMut> GLWEToLWESwitchingKey<D> {
let (mut sk_lwe_as_glwe, scratch1) = scratch.take_glwe_secret(module, 1); let (mut sk_lwe_as_glwe, scratch1) = scratch.take_glwe_secret(module, 1);
sk_lwe_as_glwe.data.zero(); sk_lwe_as_glwe.data.zero();
sk_lwe_as_glwe.data.at_mut(0, 0)[..sk_lwe.n()].copy_from_slice(sk_lwe.data.at(0, 0)); sk_lwe_as_glwe.data.at_mut(0, 0)[..sk_lwe.n()].copy_from_slice(sk_lwe.data.at(0, 0));
module.vec_znx_automorphism_inplace(-1, &mut sk_lwe_as_glwe.data, 0); module.vec_znx_automorphism_inplace(-1, &mut sk_lwe_as_glwe.data.as_vec_znx_mut(), 0);
self.0.encrypt_sk( self.0.encrypt_sk(
module, module,
@@ -252,7 +252,7 @@ impl<D: DataMut> LWEToGLWESwitchingKey<D> {
let (mut sk_lwe_as_glwe, scratch1) = scratch.take_glwe_secret(module, 1); let (mut sk_lwe_as_glwe, scratch1) = scratch.take_glwe_secret(module, 1);
sk_lwe_as_glwe.data.at_mut(0, 0)[..sk_lwe.n()].copy_from_slice(sk_lwe.data.at(0, 0)); sk_lwe_as_glwe.data.at_mut(0, 0)[..sk_lwe.n()].copy_from_slice(sk_lwe.data.at(0, 0));
sk_lwe_as_glwe.data.at_mut(0, 0)[sk_lwe.n()..].fill(0); sk_lwe_as_glwe.data.at_mut(0, 0)[sk_lwe.n()..].fill(0);
module.vec_znx_automorphism_inplace(-1, &mut sk_lwe_as_glwe.data, 0); module.vec_znx_automorphism_inplace(-1, &mut sk_lwe_as_glwe.data.as_vec_znx_mut(), 0);
self.0.encrypt_sk( self.0.encrypt_sk(
module, module,
@@ -368,11 +368,11 @@ impl<D: DataMut> LWESwitchingKey<D> {
sk_out_glwe.data.at_mut(0, 0)[..sk_lwe_out.n()].copy_from_slice(sk_lwe_out.data.at(0, 0)); sk_out_glwe.data.at_mut(0, 0)[..sk_lwe_out.n()].copy_from_slice(sk_lwe_out.data.at(0, 0));
sk_out_glwe.data.at_mut(0, 0)[sk_lwe_out.n()..].fill(0); sk_out_glwe.data.at_mut(0, 0)[sk_lwe_out.n()..].fill(0);
module.vec_znx_automorphism_inplace(-1, &mut sk_out_glwe.data, 0); module.vec_znx_automorphism_inplace(-1, &mut sk_out_glwe.data.as_vec_znx_mut(), 0);
sk_in_glwe.data.at_mut(0, 0)[..sk_lwe_in.n()].copy_from_slice(sk_lwe_in.data.at(0, 0)); sk_in_glwe.data.at_mut(0, 0)[..sk_lwe_in.n()].copy_from_slice(sk_lwe_in.data.at(0, 0));
sk_in_glwe.data.at_mut(0, 0)[sk_lwe_in.n()..].fill(0); sk_in_glwe.data.at_mut(0, 0)[sk_lwe_in.n()..].fill(0);
module.vec_znx_automorphism_inplace(-1, &mut sk_in_glwe.data, 0); module.vec_znx_automorphism_inplace(-1, &mut sk_in_glwe.data.as_vec_znx_mut(), 0);
self.0.encrypt_sk( self.0.encrypt_sk(
module, module,

View File

@@ -11,7 +11,7 @@ pub struct LWEPlaintext<D: Data> {
impl LWEPlaintext<Vec<u8>> { impl LWEPlaintext<Vec<u8>> {
pub fn alloc(basek: usize, k: usize) -> Self { pub fn alloc(basek: usize, k: usize) -> Self {
Self { Self {
data: VecZnx::new::<i64>(1, 1, k.div_ceil(basek)), data: VecZnx::alloc::<i64>(1, 1, k.div_ceil(basek)),
k: k, k: k,
basek: basek, basek: basek,
} }

View File

@@ -14,7 +14,7 @@ pub struct LWESecret<D: Data> {
impl LWESecret<Vec<u8>> { impl LWESecret<Vec<u8>> {
pub fn alloc(n: usize) -> Self { pub fn alloc(n: usize) -> Self {
Self { Self {
data: ScalarZnx::new(n, 1), data: ScalarZnx::alloc(n, 1),
dist: Distribution::NONE, dist: Distribution::NONE,
} }
} }

View File

@@ -14,11 +14,10 @@ impl Source {
} }
} }
pub fn branch(&mut self) -> Self { pub fn branch(&mut self) -> ([u8; 32], Self) {
let mut seed = [0; 32]; let mut seed = [0; 32];
self.source.fill_bytes(&mut seed); self.source.fill_bytes(&mut seed);
(seed, Source::new(seed))
Source::new(seed)
} }
#[inline(always)] #[inline(always)]