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
jump to
@@ -50,6 +50,7 @@ "rand_distr",
"rug", "sampling", "utils", + "zstd", ] [[package]]@@ -82,6 +83,8 @@ version = "1.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" dependencies = [ + "jobserver", + "libc", "shlex", ]@@ -160,6 +163,7 @@ "backend",
"byteorder", "criterion", "itertools 0.14.0", + "rand_core", "rand_distr", "rug", "sampling",@@ -298,6 +302,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] name = "js-sys" version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index"@@ -358,6 +371,12 @@ name = "paste"
version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plotters"@@ -819,3 +838,31 @@ "proc-macro2",
"quote", "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", +]
@@ -11,3 +11,4 @@ rand_distr = "0.5.1"
itertools = "0.14.0" criterion = "0.7.0" byteorder = "1.5.0" +zstd = "0.13.3"
@@ -1,11 +0,0 @@
-{ - "github.copilot.enable": { - "*": false, - "plaintext": false, - "markdown": false, - "scminput": false - }, - "files.associations": { - "random": "c" - } -}
@@ -14,7 +14,8 @@ rand_core = {workspace = true}
sampling = { path = "../sampling" } utils = { path = "../utils" } paste = "1.0.15" -byteorder = {workspace = true} +byteorder = {workspace = true} +zstd = {workspace = true} [build-dependencies] cmake = "0.1.54"
@@ -1,7 +1,9 @@
use std::path::PathBuf; 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");
@@ -1,4 +1,4 @@
-use crate::hal::layouts::{ScalarZnxOwned, ScalarZnxToMut, ScalarZnxToRef}; +use crate::hal::layouts::ScalarZnxOwned; /// Allocates as [crate::hal::layouts::ScalarZnx]. pub trait ScalarZnxAlloc {@@ -15,33 +15,3 @@ /// User must ensure that bytes is memory aligned and that it length is equal to [ScalarZnxAllocBytes].
pub trait ScalarZnxFromBytes { 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; -}
@@ -20,12 +20,12 @@ fn vec_znx_big_from_bytes(&self, cols: usize, size: usize, bytes: Vec<u8>) -> VecZnxBigOwned<B>;
} /// Add a discrete normal distribution on res. -/// +/// /// # Arguments /// * `basek`: base two logarithm of the bivariate representation /// * `res`: receiver. /// * `res_col`: column of the receiver on which the operation is performed/stored. -/// * `k`: +/// * `k`: /// * `source`: random coin source. /// * `sigma`: standard deviation of the discrete normal distribution. /// * `bound`: rejection sampling bound.
@@ -1,5 +1,6 @@
use crate::hal::layouts::{Data, DataMut, DataRef}; use rand_distr::num_traits::Zero; +use sampling::source::Source; pub trait ZnxInfos { /// Returns the ring degree of the polynomials.@@ -108,3 +109,7 @@ {
fn zero(&mut self); fn zero_at(&mut self, i: usize, j: usize); } + +pub trait FillUniform { + fn fill_uniform(&mut self, source: &mut Source); +}
@@ -1,13 +1,7 @@
use crate::hal::{ - api::{ - ScalarZnxAlloc, ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScalarZnxAutomorphismInplace, ScalarZnxFromBytes, - ScalarZnxMulXpMinusOne, ScalarZnxMulXpMinusOneInplace, - }, - layouts::{Backend, Module, ScalarZnxOwned, ScalarZnxToMut, ScalarZnxToRef}, - oep::{ - ScalarZnxAllocBytesImpl, ScalarZnxAllocImpl, ScalarZnxAutomorphismImpl, ScalarZnxAutomorphismInplaceIml, - ScalarZnxFromBytesImpl, ScalarZnxMulXpMinusOneImpl, ScalarZnxMulXpMinusOneInplaceImpl, - }, + api::{ScalarZnxAlloc, ScalarZnxAllocBytes}, + layouts::{Backend, Module, ScalarZnxOwned}, + oep::{ScalarZnxAllocBytesImpl, ScalarZnxAllocImpl}, }; impl<B> ScalarZnxAllocBytes for Module<B>@@ -27,62 +21,3 @@ fn scalar_znx_alloc(&self, cols: usize) -> ScalarZnxOwned {
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); - } -}
@@ -1,12 +1,17 @@
use crate::{ alloc_aligned, hal::{ - api::{DataView, DataViewMut, ZnxInfos, ZnxSliceSize, ZnxView}, + api::{DataView, DataViewMut, FillUniform, ZnxInfos, ZnxSliceSize, ZnxView, ZnxViewMut, ZnxZero}, 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> { data: D, n: usize,@@ -16,6 +21,12 @@ cols_in: 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> { fn cols(&self) -> usize { self.cols_in@@ -74,7 +85,7 @@ }
} 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)); Self { data: data.into(),@@ -86,7 +97,7 @@ cols_out,
} } - pub(crate) fn new_from_bytes( + pub(crate) fn from_bytes( n: usize, rows: usize, cols_in: usize,@@ -158,6 +169,12 @@ }
} } +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 MatZnxMut<'a> = MatZnx<&'a mut [u8]>; pub type MatZnxRef<'a> = MatZnx<&'a [u8]>;@@ -209,8 +226,6 @@ }
} } -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; - impl<D: DataMut> ReaderFrom for MatZnx<D> { fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> { self.n = reader.read_u64::<LittleEndian>()? as usize;@@ -244,3 +259,32 @@ writer.write_all(buf)?;
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(); + } +}
@@ -6,12 +6,12 @@
use crate::{ alloc_aligned, hal::{ - api::{DataView, DataViewMut, ZnxInfos, ZnxSliceSize, ZnxView, ZnxViewMut, ZnxZero}, - layouts::{Data, DataMut, DataRef, ReaderFrom, VecZnx, VecZnxToMut, VecZnxToRef, WriterTo}, + api::{DataView, DataViewMut, FillUniform, ZnxInfos, ZnxSliceSize, ZnxView, ZnxViewMut, ZnxZero}, + layouts::{Data, DataMut, DataRef, ReaderFrom, VecZnx, WriterTo}, }, }; -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Debug, Clone)] pub struct ScalarZnx<D: Data> { pub(crate) data: D, pub(crate) n: usize,@@ -114,7 +114,7 @@ }
} 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)); Self { data: data.into(),@@ -123,7 +123,7 @@ cols,
} } - 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(); assert!(data.len() == Self::bytes_of(n, cols)); Self {@@ -143,6 +143,12 @@ self.at_mut(i, j).fill(0);
} } +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>>; impl<D: Data> ScalarZnx<D> {@@ -179,8 +185,8 @@ }
} } -impl<D: DataRef> VecZnxToRef for ScalarZnx<D> { - fn to_ref(&self) -> VecZnx<&[u8]> { +impl<D: DataRef> ScalarZnx<D> { + pub fn as_vec_znx(&self) -> VecZnx<&[u8]> { VecZnx { data: self.data.as_ref(), n: self.n,@@ -191,8 +197,8 @@ }
} } -impl<D: DataMut> VecZnxToMut for ScalarZnx<D> { - fn to_mut(&mut self) -> VecZnx<&mut [u8]> { +impl<D: DataMut> ScalarZnx<D> { + pub fn as_vec_znx_mut(&mut self) -> VecZnx<&mut [u8]> { VecZnx { data: self.data.as_mut(), n: self.n,
@@ -3,12 +3,12 @@
use crate::{ alloc_aligned, hal::{ - api::{DataView, DataViewMut, ZnxInfos, ZnxSliceSize, ZnxView, ZnxViewMut, ZnxZero}, + api::{DataView, DataViewMut, FillUniform, ZnxInfos, ZnxSliceSize, ZnxView, ZnxViewMut, ZnxZero}, layouts::{Data, DataMut, DataRef, ReaderFrom, WriterTo}, }, }; -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Clone)] pub struct VecZnx<D: Data> { pub(crate) data: D, pub(crate) n: usize,@@ -86,7 +86,7 @@ }
} 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)); Self { data: data.into(),@@ -157,6 +157,12 @@ Ok(())
} } +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 VecZnxMut<'a> = VecZnx<&'a mut [u8]>; pub type VecZnxRef<'a> = VecZnx<&'a [u8]>;@@ -207,6 +213,8 @@ }
} use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use rand::RngCore; +use sampling::source::Source; impl<D: DataMut> ReaderFrom for VecZnx<D> { fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
@@ -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> { fn scalar_znx_from_bytes_impl(n: usize, cols: usize, bytes: Vec<u8>) -> ScalarZnxOwned;@@ -11,29 +11,3 @@
pub unsafe trait ScalarZnxAllocImpl<B: Backend> { 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; -}
@@ -1,1 +1,2 @@
+pub mod serialization; pub mod vec_znx;
@@ -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); +}
@@ -11,7 +11,7 @@ where
B: CPUAVX, { 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 @@ cols_out: usize,
size: usize, bytes: Vec<u8>, ) -> 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) } }
@@ -21,6 +21,9 @@ pub use module_fft64::*;
pub use module_ntt120::*; /// 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 {}
@@ -1,16 +1,9 @@
use crate::{ hal::{ - api::{ZnxInfos, ZnxSliceSize, ZnxView, ZnxViewMut}, - layouts::{Backend, Module, ScalarZnx, ScalarZnxOwned, ScalarZnxToMut, ScalarZnxToRef}, - oep::{ - ScalarZnxAllocBytesImpl, ScalarZnxAllocImpl, ScalarZnxAutomorphismImpl, ScalarZnxAutomorphismInplaceIml, - ScalarZnxFromBytesImpl, - }, + layouts::{Backend, ScalarZnxOwned}, + oep::{ScalarZnxAllocBytesImpl, ScalarZnxAllocImpl, ScalarZnxFromBytesImpl}, }, - implementation::cpu_spqlios::{ - CPUAVX, - ffi::{module::module_info_t, vec_znx}, - }, + implementation::cpu_spqlios::CPUAVX, }; unsafe impl<B: Backend> ScalarZnxAllocBytesImpl<B> for B@@ -27,7 +20,7 @@ where
B: CPUAVX, { 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, { fn scalar_znx_from_bytes_impl(n: usize, cols: usize, bytes: Vec<u8>) -> ScalarZnxOwned { - ScalarZnxOwned::new_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, - ) - } + ScalarZnxOwned::from_bytes(n, cols, bytes) } }
@@ -37,7 +37,7 @@ where
B: CPUAVX, { fn vec_znx_alloc_impl(n: usize, cols: usize, size: usize) -> VecZnxOwned { - VecZnxOwned::new::<i64>(n, cols, size) + VecZnxOwned::alloc::<i64>(n, cols, size) } }
@@ -12,6 +12,7 @@ sampling = {path="../sampling"}
rand_distr = {workspace = true} itertools = {workspace = true} byteorder = {workspace = true} +rand_core = {workspace = true} [[bench]] name = "external_product_glwe_fft64"
@@ -75,7 +75,7 @@
let size: usize = self.k.div_ceil(self.basek); // 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);
@@ -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() - } -}
@@ -1,7 +1,7 @@
use backend::hal::{ api::{ - ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScratchAvailable, SvpApply, TakeScalarZnx, TakeVecZnx, TakeVecZnxBig, - TakeVecZnxDft, VecZnxAddScalarInplace, VecZnxAllocBytes, VecZnxBigAllocBytes, VecZnxDftToVecZnxBigTmpA, + ScalarZnxAllocBytes, ScratchAvailable, SvpApply, TakeScalarZnx, TakeVecZnx, TakeVecZnxBig, TakeVecZnxDft, + VecZnxAddScalarInplace, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxBigAllocBytes, VecZnxDftToVecZnxBigTmpA, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSwithcDegree, ZnxZero, }, layouts::{Backend, DataMut, DataRef, Module, ScalarZnx, Scratch},@@ -9,8 +9,10 @@ };
use sampling::source::Source; use crate::{ - AutomorphismKey, GGLWECiphertext, GLWECiphertext, GLWEDecryptFamily, GLWEEncryptSkFamily, GLWEPlaintext, GLWESecret, - GLWESecretExec, GLWESecretFamily, GLWESwitchingKey, GLWETensorKey, Infos, TakeGLWEPt, TakeGLWESecret, TakeGLWESecretExec, + AutomorphismKey, AutomorphismKeyCompressed, GGLWECiphertext, GGLWECiphertextCompressed, GLWECiphertext, GLWEDecryptFamily, + 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>;@@ -122,6 +124,112 @@ });
} } +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>; impl GLWESwitchingKey<Vec<u8>> {@@ -197,14 +305,19 @@ }
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, 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 tmp, _) = scratch2.take_scalar_znx(module, 1); (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); }); }@@ -223,6 +336,100 @@ self.sk_out_n = sk_out.n();
} } +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>; impl AutomorphismKey<Vec<u8>> {@@ -250,9 +457,9 @@ sigma: f64,
scratch: &mut Scratch<B>, ) where Module<B>: AutomorphismKeyEncryptSkFamily<B> - + ScalarZnxAutomorphism + ScalarZnxAllocBytes + VecZnxAllocBytes + + VecZnxAutomorphism + VecZnxSwithcDegree + VecZnxAddScalarInplace, Scratch<B>: ScratchAvailable + TakeScalarZnx<B> + TakeVecZnxDft<B> + TakeGLWESecretExec<B> + TakeVecZnx<B>,@@ -277,11 +484,11 @@ let (mut sk_out, scratch_1) = scratch.take_glwe_secret(module, sk.rank());
{ (0..self.rank()).for_each(|i| { - module.scalar_znx_automorphism( + module.vec_znx_automorphism( module.galois_element_inv(p), - &mut sk_out.data, + &mut sk_out.data.as_vec_znx_mut(), i, - &sk.data, + &sk.data.as_vec_znx(), i, ); });@@ -294,6 +501,72 @@ self.p = p;
} } +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> = GGLWEEncryptSkFamily<B> + VecZnxBigAllocBytes + VecZnxDftToVecZnxBigTmpA<B> + SvpApply<B>;@@ -344,7 +617,7 @@
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, 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);@@ -356,7 +629,14 @@ (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, 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) .encrypt_sk(module, &sk_ij, sk, source_xa, source_xe, sigma, scratch5);@@ -364,3 +644,77 @@ });
}) } } + +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); + }); + }) + } +}
@@ -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; - } -}
@@ -1,9 +1,10 @@
use backend::hal::{ 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 byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; pub trait GGLWEExecLayoutFamily<B: Backend> = VmpPMatAlloc<B> + VmpPMatAllocBytes + VmpPMatPrepare<B>;@@ -138,8 +139,6 @@ self.data.cols_out() - 1
} } -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; - impl<D: DataMut> ReaderFrom for GGLWECiphertext<D> { fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> { self.k = reader.read_u64::<LittleEndian>()? as usize;@@ -159,43 +158,33 @@ }
} #[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, +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<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 +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>: GGLWEExecLayoutFamily<B>, + 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.vmp_pmat_alloc(rows, rank_in, rank_out + 1, size), - basek: basek, - k, - digits, + 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( + pub fn bytes_of<B: Backend>( module: &Module<B>, basek: usize, k: usize,@@ -205,71 +194,278 @@ rank_in: usize,
rank_out: usize, ) -> usize where - Module<B>: GGLWEExecLayoutFamily<B>, + Module<B>: MatZnxAllocBytes, { - let size: usize = k.div_ceil(basek); - debug_assert!( - size > digits, - "invalid gglwe: ceil(k/basek): {} <= digits: {}", - size, - digits - ); + GGLWECiphertext::<Vec<u8>>::bytes_of(module, basek, k, rows, digits, rank_in, rank_out) + } +} - assert!( - rows * digits <= size, - "invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}", - rows, - digits, - size - ); +impl<D: Data> Infos for GLWESwitchingKey<D> { + type Inner = MatZnx<D>; - module.vmp_pmat_alloc_bytes(rows, rank_in, rank_out + 1, rows) + 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) } } -impl<D: Data, B: Backend> Infos for GGLWECiphertextExec<D, B> { - type Inner = VmpPMat<D, B>; +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.data + &self.key.inner() } fn basek(&self) -> usize { - self.basek + self.key.basek() } fn k(&self) -> usize { - self.k + self.key.k() } } -impl<D: Data, B: Backend> GGLWECiphertextExec<D, B> { - pub fn rank(&self) -> usize { - self.data.cols_out() - 1 +impl<D: Data> AutomorphismKey<D> { + pub fn p(&self) -> i64 { + self.p } pub fn digits(&self) -> usize { - self.digits + self.key.digits() + } + + pub fn rank(&self) -> usize { + self.key.rank() } pub fn rank_in(&self) -> usize { - self.data.cols_in() + self.key.rank_in() } pub fn rank_out(&self) -> usize { - self.data.cols_out() - 1 + 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) } } -impl<D: DataMut, B: Backend> GGLWECiphertextExec<D, B> { - pub fn prepare<DataOther>(&mut self, module: &Module<B>, other: &GGLWECiphertext<DataOther>, scratch: &mut Scratch<B>) +#[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 - DataOther: DataRef, - Module<B>: GGLWEExecLayoutFamily<B>, + Module<B>: MatZnxAllocBytes, { - module.vmp_prepare(&mut self.data, &other.data, scratch); - self.basek = other.basek; - self.k = other.k; - self.digits = other.digits; + 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(()) } }
@@ -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); + }); + } +}
@@ -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); + }); + } +}
@@ -1,20 +1,16 @@
mod automorphism; -mod automorphism_key; mod encryption; mod external_product; mod keyswitch; -mod keyswitch_key; mod layout; +mod layouts_compressed; +mod layouts_exec; mod noise; -mod tensor_key; -pub use automorphism_key::{AutomorphismKey, AutomorphismKeyExec}; -pub use encryption::{ - AutomorphismKeyEncryptSkFamily, GGLWEEncryptSkFamily, GLWESwitchingKeyEncryptSkFamily, GLWETensorKeyEncryptSkFamily, -}; -pub use keyswitch_key::{GLWESwitchingKey, GLWESwitchingKeyExec}; -pub use layout::{GGLWECiphertext, GGLWECiphertextExec, GGLWEExecLayoutFamily}; -pub use tensor_key::{GLWETensorKey, GLWETensorKeyExec}; +pub use encryption::*; +pub use layout::*; +pub use layouts_compressed::*; +pub use layouts_exec::*; #[cfg(test)] -mod test; +mod tests;
@@ -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); - }); - } -}
@@ -1,286 +0,0 @@
-use backend::{ - hal::{ - api::{ModuleNew, ScalarZnxAutomorphism, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxStd, VecZnxSubScalarInplace}, - layouts::{Module, ScratchOwned}, - }, - implementation::cpu_spqlios::FFT64, -}; -use sampling::source::Source; - -use crate::{ - AutomorphismKey, AutomorphismKeyExec, GLWEPlaintext, GLWESecret, GLWESecretExec, Infos, noise::log2_std_noise_gglwe_product, -}; - -#[test] -fn automorphism() { - let log_n: usize = 8; - 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_automorphism(-1, 5, log_n, basek, di, k_in, k_out, k_apply, sigma, rank); - }); - }); -} - -#[test] -fn automorphism_inplace() { - let log_n: usize = 8; - 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 digits: {} rank: {}", di, rank); - let k_apply: usize = (digits + di) * basek; - test_automorphism_inplace(-1, 5, log_n, basek, di, k_in, k_apply, sigma, rank); - }); - }); -} - -fn test_automorphism( - p0: i64, - p1: i64, - log_n: usize, - basek: usize, - digits: usize, - k_in: usize, - k_out: usize, - k_apply: usize, - sigma: f64, - rank: usize, -) { - let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); - - let digits_in: usize = 1; - - let rows_in: usize = k_in / (basek * digits); - let rows_apply: usize = k_in.div_ceil(basek * digits); - - let mut auto_key_in: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank); - let mut auto_key_out: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank); - let mut auto_key_apply: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([0u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - - let mut scratch: ScratchOwned<FFT64> = ScratchOwned::alloc( - AutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank) - | AutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank), - ); - - let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(0.5, &mut source_xs); - - // gglwe_{s1}(s0) = s0 -> s1 - auto_key_in.encrypt_sk( - &module, - p0, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - // gglwe_{s2}(s1) -> s1 -> s2 - auto_key_apply.encrypt_sk( - &module, - p1, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, FFT64> = - AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank); - - auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow()); - - // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0) - auto_key_out.automorphism( - &module, - &auto_key_in, - &auto_key_apply_exec, - scratch.borrow(), - ); - - let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out); - - let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); - sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk - (0..rank).for_each(|i| { - module.scalar_znx_automorphism( - module.galois_element_inv(p0 * p1), - &mut sk_auto.data, - i, - &sk.data, - i, - ); - }); - - let sk_auto_dft: GLWESecretExec<Vec<u8>, FFT64> = GLWESecretExec::from(&module, &sk_auto); - - (0..auto_key_out.rank_in()).for_each(|col_i| { - (0..auto_key_out.rows()).for_each(|row_i| { - auto_key_out - .at(row_i, col_i) - .decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow()); - - module.vec_znx_sub_scalar_inplace( - &mut pt.data, - 0, - (digits_in - 1) + row_i * digits_in, - &sk.data, - col_i, - ); - - let noise_have: f64 = module.vec_znx_std(basek, &pt.data, 0).log2(); - let noise_want: f64 = log2_std_noise_gglwe_product( - module.n() as f64, - basek * digits, - 0.5, - 0.5, - 0f64, - sigma * sigma, - 0f64, - rank as f64, - k_out, - k_apply, - ); - - assert!( - noise_have < noise_want + 0.5, - "{} {}", - noise_have, - noise_want - ); - }); - }); -} - -fn test_automorphism_inplace( - p0: i64, - p1: i64, - log_n: usize, - basek: usize, - digits: usize, - k_in: usize, - k_apply: usize, - sigma: f64, - rank: usize, -) { - let module: Module<FFT64> = Module::<FFT64>::new(1 << log_n); - - let digits_in: usize = 1; - - let rows_in: usize = k_in / (basek * digits); - let rows_apply: usize = k_in.div_ceil(basek * digits); - - let mut auto_key: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank); - let mut auto_key_apply: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([0u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - - let mut scratch: ScratchOwned<FFT64> = ScratchOwned::alloc( - AutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank) - | AutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank), - ); - - let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); - sk.fill_ternary_prob(0.5, &mut source_xs); - - // gglwe_{s1}(s0) = s0 -> s1 - auto_key.encrypt_sk( - &module, - p0, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - // gglwe_{s2}(s1) -> s1 -> s2 - auto_key_apply.encrypt_sk( - &module, - p1, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, FFT64> = - AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank); - - auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow()); - - // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0) - auto_key.automorphism_inplace(&module, &auto_key_apply_exec, scratch.borrow()); - - let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in); - - let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); - sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk - - (0..rank).for_each(|i| { - module.scalar_znx_automorphism( - module.galois_element_inv(p0 * p1), - &mut sk_auto.data, - i, - &sk.data, - i, - ); - }); - - let sk_auto_dft: GLWESecretExec<Vec<u8>, FFT64> = GLWESecretExec::from(&module, &sk_auto); - - (0..auto_key.rank_in()).for_each(|col_i| { - (0..auto_key.rows()).for_each(|row_i| { - auto_key - .at(row_i, col_i) - .decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow()); - module.vec_znx_sub_scalar_inplace( - &mut pt.data, - 0, - (digits_in - 1) + row_i * digits_in, - &sk.data, - col_i, - ); - - let noise_have: f64 = module.vec_znx_std(basek, &pt.data, 0).log2(); - let noise_want: f64 = log2_std_noise_gglwe_product( - module.n() as f64, - basek * digits, - 0.5, - 0.5, - 0f64, - sigma * sigma, - 0f64, - rank as f64, - k_in, - k_apply, - ); - - assert!( - noise_have < noise_want + 0.5, - "{} {}", - noise_have, - noise_want - ); - }); - }); -}
@@ -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); - }); - }); - }); -}
@@ -1,7 +1,8 @@
use backend::hal::{ api::{ 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}, oep::{@@ -13,7 +14,7 @@ use sampling::source::Source;
use crate::{ GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GGSWCiphertext, GGSWCiphertextExec, GGSWLayoutFamily, GLWEDecryptFamily, - GLWEExternalProductFamily, GLWEKeyswitchFamily, GLWESecret, GLWESecretExec, GLWESwitchingKey, + GLWEExternalProductFamily, GLWEKeyswitchFamily, GLWESecret, GLWESecretExec, GLWESwitchingKey, GLWESwitchingKeyCompressed, GLWESwitchingKeyEncryptSkFamily, GLWESwitchingKeyExec, noise::{log2_std_noise_gglwe_product, noise_ggsw_product}, };@@ -28,7 +29,8 @@ + VecZnxSwithcDegree
+ VecZnxAddScalarInplace + VecZnxStd + VecZnxAlloc - + VecZnxSubScalarInplace; + + VecZnxSubScalarInplace + + VecZnxCopy; pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B> + TakeVecZnxBigImpl<B>@@ -42,7 +44,7 @@ + VecZnxDftAllocBytesImpl<B>
+ VecZnxBigAllocBytesImpl<B> + TakeSvpPPolImpl<B>; -pub(crate) fn test_encrypt_sk<B: Backend>( +pub(crate) fn test_gglwe_encrypt_sk<B: Backend>( module: &Module<B>, basek: usize, k_ksk: usize,@@ -87,7 +89,57 @@ ksk.key
.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>, basek: usize, k_out: usize,@@ -217,7 +269,7 @@ .key
.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>, basek: usize, k_ct: usize,@@ -317,7 +369,7 @@ .key
.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>, basek: usize, k_out: usize,@@ -400,7 +452,7 @@ // gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
ct_gglwe_out.external_product(module, &ct_gglwe_in, &ct_rgsw_exec, scratch.borrow()); (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;@@ -429,7 +481,7 @@ .key
.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>, basek: usize, k_ct: usize,@@ -510,7 +562,7 @@ // gglwe_(m) (x) RGSW_(X^k) = gglwe_(m * X^k)
ct_gglwe.external_product_inplace(module, &ct_rgsw_exec, scratch.borrow()); (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;
@@ -1,5 +0,0 @@
-mod automorphism_key; -mod gglwe_fft64; -mod gglwe_generic; -mod tensor_key_fft64; -mod tensor_key_generic;
@@ -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); - }); -}
@@ -1,113 +0,0 @@
-use backend::hal::{ - api::{ - MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, - VecZnxAlloc, VecZnxAllocBytes, VecZnxBigAlloc, VecZnxDftAlloc, VecZnxStd, VecZnxSubScalarInplace, VecZnxSwithcDegree, - }, - layouts::{Backend, Module, ScratchOwned, VecZnxDft}, - oep::{ - ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, - TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, VecZnxBigAllocBytesImpl, VecZnxDftAllocBytesImpl, - }, -}; -use sampling::source::Source; - -use crate::{ - GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GLWEDecryptFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, GLWETensorKey, - GLWETensorKeyEncryptSkFamily, Infos, -}; - -pub(crate) trait TestModuleFamily<B: Backend> = GGLWEEncryptSkFamily<B> - + GLWEDecryptFamily<B> - + MatZnxAlloc - + ScalarZnxAlloc - + ScalarZnxAllocBytes - + VecZnxAllocBytes - + VecZnxSwithcDegree - + VecZnxAddScalarInplace - + VecZnxStd - + VecZnxAlloc - + VecZnxSubScalarInplace; - -pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B> - + TakeVecZnxBigImpl<B> - + TakeSvpPPolImpl<B> - + ScratchOwnedAllocImpl<B> - + ScratchOwnedBorrowImpl<B> - + ScratchAvailableImpl<B> - + TakeScalarZnxImpl<B> - + TakeVecZnxImpl<B> - + VecZnxDftAllocBytesImpl<B> - + VecZnxBigAllocBytesImpl<B> - + TakeSvpPPolImpl<B>; - -pub(crate) fn test_encrypt_sk<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>, - B: TestScratchFamily<B>, -{ - let rows: usize = k / basek; - - let mut tensor_key: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(&module, basek, k, rows, 1, rank); - - let mut source_xs: Source = Source::new([0u8; 32]); - let mut source_xe: Source = Source::new([0u8; 32]); - let mut source_xa: Source = Source::new([0u8; 32]); - - let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWETensorKey::encrypt_sk_scratch_space( - module, - basek, - tensor_key.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); - - tensor_key.encrypt_sk( - module, - &sk, - &mut source_xa, - &mut source_xe, - sigma, - scratch.borrow(), - ); - - 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, 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, 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); - }); - }); - }) - }) -}
@@ -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); + }); +}
@@ -0,0 +1,403 @@
+use backend::hal::{ + api::{ + MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, + VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxAutomorphismInplace, VecZnxCopy, VecZnxStd, + VecZnxSubScalarInplace, VecZnxSwithcDegree, + }, + layouts::{Backend, Module, ScratchOwned}, + oep::{ + ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, + TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, + }, +}; +use sampling::source::Source; + +use crate::{ + AutomorphismKey, AutomorphismKeyCompressed, AutomorphismKeyEncryptSkFamily, AutomorphismKeyExec, GGLWEExecLayoutFamily, + GLWEDecryptFamily, GLWEKeyswitchFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, Infos, + noise::log2_std_noise_gglwe_product, +}; + +pub(crate) trait AutomorphismTestModuleFamily<B: Backend> = MatZnxAlloc + + AutomorphismKeyEncryptSkFamily<B> + + ScalarZnxAllocBytes + + VecZnxAllocBytes + + GLWEKeyswitchFamily<B> + + ScalarZnxAlloc + + VecZnxAutomorphism + + GGLWEExecLayoutFamily<B> + + VecZnxSwithcDegree + + VecZnxAddScalarInplace + + VecZnxAutomorphism + + VecZnxAutomorphismInplace + + 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); +} + +pub(crate) fn test_automorphisk_key_encrypt_sk_compressed<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_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); +} + +pub(crate) fn test_gglwe_automorphism<B: Backend>( + module: &Module<B>, + p0: i64, + p1: i64, + basek: usize, + digits: usize, + k_in: usize, + k_out: usize, + k_apply: usize, + sigma: f64, + rank: usize, +) where + Module<B>: AutomorphismTestModuleFamily<B>, + B: AutomorphismTestScratchFamily<B>, +{ + let digits_in: usize = 1; + + let rows_in: usize = k_in / (basek * digits); + let rows_apply: usize = k_in.div_ceil(basek * digits); + + let mut auto_key_in: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank); + let mut auto_key_out: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_out, rows_in, digits_in, rank); + let mut auto_key_apply: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank); + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + + let mut scratch: ScratchOwned<B> = ScratchOwned::alloc( + AutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank) + | AutomorphismKey::automorphism_scratch_space(&module, basek, k_out, k_in, k_apply, digits, rank), + ); + + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + + // gglwe_{s1}(s0) = s0 -> s1 + auto_key_in.encrypt_sk( + &module, + p0, + &sk, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + // gglwe_{s2}(s1) -> s1 -> s2 + auto_key_apply.encrypt_sk( + &module, + p1, + &sk, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, B> = + AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank); + + auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow()); + + // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0) + auto_key_out.automorphism( + &module, + &auto_key_in, + &auto_key_apply_exec, + scratch.borrow(), + ); + + let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_out); + + let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk + (0..rank).for_each(|i| { + module.vec_znx_automorphism( + module.galois_element_inv(p0 * p1), + &mut sk_auto.data.as_vec_znx_mut(), + i, + &sk.data.as_vec_znx(), + i, + ); + }); + + 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.rows()).for_each(|row_i| { + auto_key_out + .at(row_i, col_i) + .decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow()); + + module.vec_znx_sub_scalar_inplace( + &mut pt.data, + 0, + (digits_in - 1) + row_i * digits_in, + &sk.data, + col_i, + ); + + let noise_have: f64 = module.vec_znx_std(basek, &pt.data, 0).log2(); + let noise_want: f64 = log2_std_noise_gglwe_product( + module.n() as f64, + basek * digits, + 0.5, + 0.5, + 0f64, + sigma * sigma, + 0f64, + rank as f64, + k_out, + k_apply, + ); + + assert!( + noise_have < noise_want + 0.5, + "{} {}", + noise_have, + noise_want + ); + }); + }); +} + +pub(crate) fn test_gglwe_automorphism_inplace<B: Backend>( + module: &Module<B>, + p0: i64, + p1: i64, + basek: usize, + digits: usize, + k_in: usize, + k_apply: usize, + sigma: f64, + rank: usize, +) where + Module<B>: AutomorphismTestModuleFamily<B>, + B: AutomorphismTestScratchFamily<B>, +{ + let digits_in: usize = 1; + + let rows_in: usize = k_in / (basek * digits); + let rows_apply: usize = k_in.div_ceil(basek * digits); + + let mut auto_key: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_in, rows_in, digits_in, rank); + let mut auto_key_apply: AutomorphismKey<Vec<u8>> = AutomorphismKey::alloc(&module, basek, k_apply, rows_apply, digits, rank); + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + + let mut scratch: ScratchOwned<B> = ScratchOwned::alloc( + AutomorphismKey::encrypt_sk_scratch_space(&module, basek, k_apply, rank) + | AutomorphismKey::automorphism_inplace_scratch_space(&module, basek, k_in, k_apply, digits, rank), + ); + + let mut sk: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk.fill_ternary_prob(0.5, &mut source_xs); + + // gglwe_{s1}(s0) = s0 -> s1 + auto_key.encrypt_sk( + &module, + p0, + &sk, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + // gglwe_{s2}(s1) -> s1 -> s2 + auto_key_apply.encrypt_sk( + &module, + p1, + &sk, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + let mut auto_key_apply_exec: AutomorphismKeyExec<Vec<u8>, B> = + AutomorphismKeyExec::alloc(&module, basek, k_apply, rows_apply, digits, rank); + + auto_key_apply_exec.prepare(&module, &auto_key_apply, scratch.borrow()); + + // gglwe_{s1}(s0) (x) gglwe_{s2}(s1) = gglwe_{s2}(s0) + auto_key.automorphism_inplace(&module, &auto_key_apply_exec, scratch.borrow()); + + let mut pt: GLWEPlaintext<Vec<u8>> = GLWEPlaintext::alloc(&module, basek, k_in); + + let mut sk_auto: GLWESecret<Vec<u8>> = GLWESecret::alloc(&module, rank); + sk_auto.fill_zero(); // Necessary to avoid panic of unfilled sk + + (0..rank).for_each(|i| { + module.vec_znx_automorphism( + module.galois_element_inv(p0 * p1), + &mut sk_auto.data.as_vec_znx_mut(), + i, + &sk.data.as_vec_znx(), + i, + ); + }); + + 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.rows()).for_each(|row_i| { + auto_key + .at(row_i, col_i) + .decrypt(&module, &mut pt, &sk_auto_dft, scratch.borrow()); + module.vec_znx_sub_scalar_inplace( + &mut pt.data, + 0, + (digits_in - 1) + row_i * digits_in, + &sk.data, + col_i, + ); + + let noise_have: f64 = module.vec_znx_std(basek, &pt.data, 0).log2(); + let noise_want: f64 = log2_std_noise_gglwe_product( + module.n() as f64, + basek * digits, + 0.5, + 0.5, + 0f64, + sigma * sigma, + 0f64, + rank as f64, + k_in, + k_apply, + ); + + assert!( + noise_have < noise_want + 0.5, + "{} {}", + noise_have, + noise_want + ); + }); + }); +}
@@ -0,0 +1,211 @@
+use backend::hal::{ + api::{ + MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, + VecZnxAlloc, VecZnxAllocBytes, VecZnxBigAlloc, VecZnxCopy, VecZnxDftAlloc, VecZnxStd, VecZnxSubScalarInplace, + VecZnxSwithcDegree, + }, + layouts::{Backend, Module, ScratchOwned, VecZnxDft}, + oep::{ + ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, + TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, VecZnxBigAllocBytesImpl, VecZnxDftAllocBytesImpl, + }, +}; +use sampling::source::Source; + +use crate::{ + GGLWEEncryptSkFamily, GGLWEExecLayoutFamily, GLWEDecryptFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, GLWETensorKey, + GLWETensorKeyCompressed, GLWETensorKeyEncryptSkFamily, Infos, +}; + +pub(crate) trait TestModuleFamily<B: Backend> = GGLWEEncryptSkFamily<B> + + GLWEDecryptFamily<B> + + MatZnxAlloc + + ScalarZnxAlloc + + ScalarZnxAllocBytes + + VecZnxAllocBytes + + VecZnxSwithcDegree + + VecZnxAddScalarInplace + + VecZnxStd + + VecZnxAlloc + + VecZnxSubScalarInplace; + +pub(crate) trait TestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B> + + TakeVecZnxBigImpl<B> + + TakeSvpPPolImpl<B> + + ScratchOwnedAllocImpl<B> + + ScratchOwnedBorrowImpl<B> + + ScratchAvailableImpl<B> + + TakeScalarZnxImpl<B> + + TakeVecZnxImpl<B> + + VecZnxDftAllocBytesImpl<B> + + VecZnxBigAllocBytesImpl<B> + + TakeSvpPPolImpl<B>; + +pub(crate) fn test_tensor_key_encrypt_sk<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>, + B: TestScratchFamily<B>, +{ + let rows: usize = k / basek; + + let mut tensor_key: GLWETensorKey<Vec<u8>> = GLWETensorKey::alloc(&module, basek, k, rows, 1, rank); + + let mut source_xs: Source = Source::new([0u8; 32]); + let mut source_xe: Source = Source::new([0u8; 32]); + let mut source_xa: Source = Source::new([0u8; 32]); + + let mut scratch: ScratchOwned<B> = ScratchOwned::alloc(GLWETensorKey::encrypt_sk_scratch_space( + module, + basek, + tensor_key.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); + + tensor_key.encrypt_sk( + module, + &sk, + &mut source_xa, + &mut source_xe, + sigma, + scratch.borrow(), + ); + + 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.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.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); + }); + }); + }) + }) +}
@@ -0,0 +1,4 @@
+mod cpu_spqlios; +mod generics_automorphism_key; +mod generics_gglwe; +mod generics_tensor_key;
@@ -64,7 +64,7 @@
(0..rank + 1).for_each(|col_j| { // 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, Some((&tmp_pt, col_j)), sk,
@@ -1,6 +1,6 @@
use backend::hal::{ 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};@@ -135,125 +135,3 @@ writer.write_u64::<LittleEndian>(self.digits as u64)?;
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; - } -}
@@ -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)); + }); + } +}
@@ -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; + } +}
@@ -3,12 +3,16 @@ mod encryption;
mod external_product; mod keyswitch; mod layout; +mod layout_compressed; +mod layout_exec; mod noise; -pub use encryption::GGSWEncryptSkFamily; -pub use keyswitch::GGSWKeySwitchFamily; -pub use layout::{GGSWCiphertext, GGSWCiphertextExec, GGSWLayoutFamily}; -pub use noise::GGSWAssertNoiseFamily; +pub use encryption::*; +pub use keyswitch::*; +pub use layout::*; +pub use layout_compressed::*; +pub use layout_exec::*; +pub use noise::*; #[cfg(test)] mod test;
@@ -1,8 +1,8 @@
use backend::hal::{ api::{ - MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScalarZnxAutomorphismInplace, ScratchOwnedAlloc, - ScratchOwnedBorrow, VecZnxAddScalarInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphismInplace, - VecZnxRotateInplace, VecZnxStd, VecZnxSubABInplace, VecZnxSwithcDegree, ZnxViewMut, + MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, + VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxAutomorphismInplace, VecZnxRotateInplace, VecZnxStd, + VecZnxSubABInplace, VecZnxSwithcDegree, ZnxViewMut, }, layouts::{Backend, Module, ScalarZnx, ScalarZnxToMut, ScratchOwned}, oep::{@@ -321,8 +321,8 @@ + GLWETensorKeyEncryptSkFamily<B>
+ GGLWEExecLayoutFamily<B> + VecZnxSwithcDegree + VecZnxAutomorphismInplace - + ScalarZnxAutomorphismInplace - + ScalarZnxAutomorphism, + + VecZnxAutomorphismInplace + + VecZnxAutomorphism, B: TestScratchFamily<B>, { let rows: usize = k_in.div_ceil(basek * digits);@@ -393,7 +393,7 @@ tsk_exec.prepare(module, &tensor_key, 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 { noise_ggsw_keyswitch(@@ -433,8 +433,8 @@ + GLWETensorKeyEncryptSkFamily<B>
+ GGLWEExecLayoutFamily<B> + VecZnxSwithcDegree + VecZnxAutomorphismInplace - + ScalarZnxAutomorphismInplace - + ScalarZnxAutomorphism, + + VecZnxAutomorphism + + VecZnxAutomorphismInplace, B: TestScratchFamily<B>, { let rows: usize = k_ct.div_ceil(digits * basek);@@ -501,7 +501,7 @@ tsk_exec.prepare(module, &tensor_key, 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 { noise_ggsw_keyswitch(@@ -595,7 +595,7 @@ ct_rhs_exec.prepare(module, &ct_ggsw_rhs, 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_rhs: f64 = 0f64;@@ -695,7 +695,7 @@ ct_rhs_exec.prepare(module, &ct_ggsw_rhs, 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_rhs: f64 = 0f64;
@@ -1,2 +1,2 @@
+mod cpu_spqlios; mod generic_tests; -mod test_fft64;
@@ -3,13 +3,17 @@ api::{
ScalarZnxAllocBytes, ScratchAvailable, SvpApply, SvpApplyInplace, SvpPPolAllocBytes, SvpPrepare, TakeScalarZnx, TakeSvpPPol, TakeVecZnx, TakeVecZnxDft, VecZnxAddInplace, VecZnxAddNormal, VecZnxBigAddNormal, VecZnxBigAddSmallInplace, 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 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 + VecZnxBigNormalize<B>@@ -71,7 +75,20 @@ ) where
Module<B>: GLWEEncryptSkFamily<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, Some((pt, 0)), sk,@@ -94,7 +111,18 @@ ) where
Module<B>: GLWEEncryptSkFamily<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, None::<(&GLWEPlaintext<Vec<u8>>, usize)>, sk,@@ -105,6 +133,34 @@ 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>, + 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>( &mut self, module: &Module<B>,@@ -118,7 +174,7 @@ ) where
Module<B>: GLWEEncryptPkFamily<B>, Scratch<B>: TakeVecZnxDft<B> + TakeSvpPPol<B> + TakeScalarZnx<B>, { - self.encrypt_pk_private::<DataPt, DataPk, B>( + self.encrypt_pk_internal::<DataPt, DataPk, B>( module, Some((pt, 0)), pk,@@ -141,7 +197,7 @@ ) where
Module<B>: GLWEEncryptPkFamily<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, None::<(&GLWEPlaintext<Vec<u8>>, usize)>, pk,@@ -152,89 +208,7 @@ scratch,
); } - pub(crate) fn encrypt_sk_private<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>, - { - #[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>( + pub(crate) fn encrypt_pk_internal<DataPt: DataRef, DataPk: DataRef, B: Backend>( &mut self, module: &Module<B>, pt: Option<(&GLWEPlaintext<DataPt>, usize)>,@@ -323,3 +297,158 @@ module.vec_znx_big_normalize(basek, &mut self.data, i, &ci_big, 0, scratch_2);
}); } } + +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); +}
@@ -1,15 +1,52 @@
+use std::fmt::Debug; + 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}, }; +use sampling::source::Source; use crate::{GLWEOps, Infos, SetMetaData}; -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Clone)] pub struct GLWECiphertext<D: Data> { pub data: VecZnx<D>, pub basek: 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>> {@@ -121,3 +158,165 @@ writer.write_u64::<LittleEndian>(self.basek as u64)?;
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; + } +}
@@ -12,6 +12,9 @@ mod public_key;
mod secret; mod trace; +#[cfg(test)] +mod tests; + pub use decryption::*; pub use encryption::*; pub use external_product::*;@@ -22,6 +25,3 @@ pub use packing::*;
pub use plaintext::*; pub use public_key::*; pub use secret::*; - -#[cfg(test)] -mod test_fft64;
@@ -8,7 +8,7 @@ use crate::dist::Distribution;
pub trait GLWESecretFamily<B: Backend> = SvpPrepare<B> + SvpPPolAllocBytes + SvpPPolAlloc<B>; -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Clone)] pub struct GLWESecret<D: Data> { pub(crate) data: ScalarZnx<D>, pub(crate) dist: Distribution,
@@ -1,17 +1,14 @@
-use backend::{ - hal::{ - api::{ - MatZnxAlloc, ModuleNew, ScalarZnxAlloc, ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScratchOwnedAlloc, - ScratchOwnedBorrow, VecZnxAddScalarInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphismInplace, - VecZnxFillUniform, VecZnxStd, VecZnxSwithcDegree, - }, - layouts::{Backend, Module, ScratchOwned}, - oep::{ - ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, - TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, - }, +use backend::hal::{ + api::{ + MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, + VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxAutomorphismInplace, VecZnxFillUniform, VecZnxStd, + VecZnxSwithcDegree, }, - implementation::cpu_spqlios::FFT64, + layouts::{Backend, Module, ScratchOwned}, + oep::{ + ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, + TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, + }, }; use sampling::source::Source;@@ -21,39 +18,6 @@ GLWEDecryptFamily, GLWEKeyswitchFamily, GLWEPlaintext, GLWESecret, GLWESecretExec, Infos,
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> + GLWEDecryptFamily<B> + GGLWEExecLayoutFamily<B>@@ -62,7 +26,7 @@ + MatZnxAlloc
+ VecZnxAlloc + ScalarZnxAllocBytes + VecZnxAllocBytes - + ScalarZnxAutomorphism + + VecZnxAutomorphism + VecZnxSwithcDegree + ScalarZnxAlloc + VecZnxAddScalarInplace@@ -77,7 +41,7 @@ + ScratchAvailableImpl<B>
+ TakeScalarZnxImpl<B> + TakeVecZnxImpl<B>; -fn test_automorphism<B: Backend>( +pub(crate) fn test_automorphism<B: Backend>( module: &Module<B>, basek: usize, p: i64,@@ -166,7 +130,7 @@
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>, basek: usize, p: i64,
@@ -1,54 +1,21 @@
-use backend::{ - hal::{ - api::{ - ModuleNew, ScalarZnxAlloc, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAlloc, VecZnxDftAlloc, VecZnxFillUniform, - VecZnxStd, VecZnxSubABInplace, - }, - layouts::{Backend, Module, ScratchOwned}, - oep::{ - ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, - TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, - }, +use backend::hal::{ + api::{ + ScalarZnxAlloc, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAlloc, VecZnxCopy, VecZnxDftAlloc, VecZnxFillUniform, + VecZnxStd, VecZnxSubABInplace, }, - implementation::cpu_spqlios::FFT64, + layouts::{Backend, Module, ScratchOwned}, + oep::{ + ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, + TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, + }, }; use sampling::source::Source; use crate::{ - GLWECiphertext, GLWEDecryptFamily, GLWEEncryptPkFamily, GLWEEncryptSkFamily, GLWEOps, GLWEPlaintext, GLWEPublicKey, - GLWEPublicKeyExec, GLWESecret, GLWESecretExec, GLWESecretFamily, Infos, + GLWECiphertext, GLWECiphertextCompressed, GLWEDecryptFamily, GLWEEncryptPkFamily, GLWEEncryptSkFamily, GLWEOps, + 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> = GLWEDecryptFamily<B> + GLWESecretFamily<B> + VecZnxAlloc + ScalarZnxAlloc + VecZnxStd;@@ -61,7 +28,7 @@ + ScratchAvailableImpl<B>
+ TakeScalarZnxImpl<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 Module<B>: EncryptionTestModuleFamily<B> + GLWEEncryptSkFamily<B>, B: EncryptionTestScratchFamily<B>,@@ -105,7 +72,63 @@
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 Module<B>: EncryptionTestModuleFamily<B> + GLWEEncryptSkFamily<B>, B: EncryptionTestScratchFamily<B>,@@ -140,7 +163,7 @@
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 Module<B>: EncryptionTestModuleFamily<B> + GLWEEncryptPkFamily<B>
@@ -1,16 +1,13 @@
-use backend::{ - hal::{ - api::{ - MatZnxAlloc, ModuleNew, ScalarZnxAlloc, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, VecZnxAlloc, - VecZnxAllocBytes, VecZnxFillUniform, VecZnxRotateInplace, VecZnxStd, ZnxViewMut, - }, - layouts::{Backend, Module, ScalarZnx, ScratchOwned}, - oep::{ - ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, - TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, - }, +use backend::hal::{ + api::{ + MatZnxAlloc, ScalarZnxAlloc, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, VecZnxAlloc, + VecZnxAllocBytes, VecZnxFillUniform, VecZnxRotateInplace, VecZnxStd, ZnxViewMut, }, - implementation::cpu_spqlios::FFT64, + layouts::{Backend, Module, ScalarZnx, ScratchOwned}, + oep::{ + ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, + TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, + }, }; use sampling::source::Source;@@ -19,39 +16,6 @@ GGSWCiphertext, GGSWCiphertextExec, GGSWLayoutFamily, GLWECiphertext, GLWEDecryptFamily, GLWEEncryptSkFamily,
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> + GLWEDecryptFamily<B> + GLWESecretFamily<B>@@ -74,7 +38,7 @@ + ScratchAvailableImpl<B>
+ TakeScalarZnxImpl<B> + TakeVecZnxImpl<B>; -fn test_external_product<B: Backend>( +pub(crate) fn test_external_product<B: Backend>( module: &Module<B>, basek: usize, k_out: usize,@@ -176,7 +140,7 @@
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>, basek: usize, k_ct: usize,
@@ -1,16 +1,13 @@
-use backend::{ - hal::{ - api::{ - MatZnxAlloc, ModuleNew, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, - VecZnxAddScalarInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxFillUniform, VecZnxStd, VecZnxSwithcDegree, - }, - layouts::{Backend, Module, ScratchOwned}, - oep::{ - ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, - TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, - }, +use backend::hal::{ + api::{ + MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, + VecZnxAlloc, VecZnxAllocBytes, VecZnxFillUniform, VecZnxStd, VecZnxSwithcDegree, }, - implementation::cpu_spqlios::FFT64, + layouts::{Backend, Module, ScratchOwned}, + oep::{ + ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, + TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, + }, }; use sampling::source::Source;@@ -20,46 +17,6 @@ GLWESecretFamily, GLWESwitchingKey, GLWESwitchingKeyEncryptSkFamily, GLWESwitchingKeyExec, Infos,
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> + GLWESwitchingKeyEncryptSkFamily<B> + GLWEKeyswitchFamily<B>@@ -83,7 +40,7 @@ + ScratchAvailableImpl<B>
+ TakeScalarZnxImpl<B> + TakeVecZnxImpl<B>; -fn test_keyswitch<B: Backend>( +pub(crate) fn test_keyswitch<B: Backend>( module: &Module<B>, basek: usize, k_out: usize,@@ -173,7 +130,7 @@
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>, basek: usize, k_ct: usize,
@@ -1,6 +0,0 @@
-pub mod automorphism; -pub mod encryption; -pub mod external_product; -pub mod keyswitch; -pub mod packing; -pub mod trace;
@@ -1,19 +1,16 @@
use std::collections::HashMap; -use backend::{ - hal::{ - api::{ - MatZnxAlloc, ModuleNew, ScalarZnxAlloc, ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScratchOwnedAlloc, - ScratchOwnedBorrow, VecZnxAddScalarInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxBigSubSmallBInplace, - VecZnxEncodeVeci64, VecZnxRotateInplace, VecZnxStd, VecZnxSwithcDegree, - }, - layouts::{Backend, Module, ScratchOwned}, - oep::{ - ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, - TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, - }, +use backend::hal::{ + api::{ + MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, + VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxBigSubSmallBInplace, VecZnxEncodeVeci64, VecZnxRotateInplace, + VecZnxStd, VecZnxSwithcDegree, }, - implementation::cpu_spqlios::FFT64, + layouts::{Backend, Module, ScratchOwned}, + oep::{ + ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, + TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, + }, }; use sampling::source::Source;@@ -22,13 +19,6 @@ AutomorphismKey, AutomorphismKeyExec, GGLWEExecLayoutFamily, GLWECiphertext, GLWEDecryptFamily, GLWEKeyswitchFamily, GLWEOps,
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> + GLWESecretFamily<B> + GLWESwitchingKeyEncryptSkFamily<B>@@ -44,8 +34,8 @@ + VecZnxStd
+ VecZnxSwithcDegree + VecZnxAddScalarInplace + VecZnxEncodeVeci64 - + ScalarZnxAutomorphism + VecZnxRotateInplace + + VecZnxAutomorphism + VecZnxBigSubSmallBInplace<B>; pub(crate) trait PackingTestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>
@@ -1,20 +1,17 @@
use std::collections::HashMap; -use backend::{ - hal::{ - api::{ - MatZnxAlloc, ModuleNew, ScalarZnxAlloc, ScalarZnxAllocBytes, ScalarZnxAutomorphism, ScratchOwnedAlloc, - ScratchOwnedBorrow, VecZnxAddScalarInplace, VecZnxAlloc, VecZnxAllocBytes, VecZnxBigAutomorphismInplace, - VecZnxBigSubSmallBInplace, VecZnxCopy, VecZnxEncodeVeci64, VecZnxFillUniform, VecZnxNormalizeInplace, - VecZnxRotateInplace, VecZnxRshInplace, VecZnxStd, VecZnxSubABInplace, VecZnxSwithcDegree, ZnxView, ZnxViewMut, - }, - layouts::{Backend, Module, ScratchOwned}, - oep::{ - ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, - TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, - }, +use backend::hal::{ + api::{ + MatZnxAlloc, ScalarZnxAlloc, ScalarZnxAllocBytes, ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAddScalarInplace, + VecZnxAlloc, VecZnxAllocBytes, VecZnxAutomorphism, VecZnxBigAutomorphismInplace, VecZnxBigSubSmallBInplace, VecZnxCopy, + VecZnxEncodeVeci64, VecZnxFillUniform, VecZnxNormalizeInplace, VecZnxRotateInplace, VecZnxRshInplace, VecZnxStd, + VecZnxSubABInplace, VecZnxSwithcDegree, ZnxView, ZnxViewMut, + }, + layouts::{Backend, Module, ScratchOwned}, + oep::{ + ScratchAvailableImpl, ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl, TakeScalarZnxImpl, TakeSvpPPolImpl, + TakeVecZnxBigImpl, TakeVecZnxDftImpl, TakeVecZnxImpl, }, - implementation::cpu_spqlios::FFT64, }; use sampling::source::Source;@@ -24,16 +21,6 @@ GLWEPlaintext, GLWESecret, GLWESecretExec, GLWESecretFamily, GLWESwitchingKeyEncryptSkFamily, Infos,
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> + GLWESwitchingKeyEncryptSkFamily<B> + GLWEKeyswitchFamily<B>@@ -48,11 +35,11 @@ + VecZnxStd
+ VecZnxSwithcDegree + VecZnxAddScalarInplace + VecZnxEncodeVeci64 - + ScalarZnxAutomorphism + VecZnxRotateInplace + VecZnxBigSubSmallBInplace<B> + VecZnxBigAutomorphismInplace<B> + VecZnxCopy + + VecZnxAutomorphism + VecZnxRshInplace; pub(crate) trait TraceTestScratchFamily<B: Backend> = TakeVecZnxDftImpl<B>@@ -64,7 +51,7 @@ + ScratchAvailableImpl<B>
+ TakeScalarZnxImpl<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 Module<B>: TraceTestModuleFamily<B>, B: TraceTestScratchFamily<B>,
@@ -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); +}
@@ -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); +}
@@ -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;
@@ -14,7 +14,7 @@
impl LWECiphertext<Vec<u8>> { pub fn alloc(n: usize, basek: usize, k: usize) -> 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, basek: basek, }
@@ -34,7 +34,7 @@ let k: usize = self.k();
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());
@@ -126,7 +126,7 @@
let (mut sk_lwe_as_glwe, scratch1) = scratch.take_glwe_secret(module, 1); 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)); - 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( module,@@ -252,7 +252,7 @@
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()..].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( module,@@ -368,11 +368,11 @@ let (mut sk_out_glwe, scratch2) = scratch1.take_glwe_secret(module, 1);
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); - 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()..].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( module,
@@ -11,7 +11,7 @@
impl LWEPlaintext<Vec<u8>> { pub fn alloc(basek: usize, k: usize) -> Self { Self { - data: VecZnx::new::<i64>(1, 1, k.div_ceil(basek)), + data: VecZnx::alloc::<i64>(1, 1, k.div_ceil(basek)), k: k, basek: basek, }
@@ -14,7 +14,7 @@
impl LWESecret<Vec<u8>> { pub fn alloc(n: usize) -> Self { Self { - data: ScalarZnx::new(n, 1), + data: ScalarZnx::alloc(n, 1), dist: Distribution::NONE, } }
@@ -14,11 +14,10 @@ source: ChaCha8Rng::from_seed(seed),
} } - pub fn branch(&mut self) -> Self { + pub fn branch(&mut self) -> ([u8; 32], Self) { let mut seed = [0; 32]; self.source.fill_bytes(&mut seed); - - Source::new(seed) + (seed, Source::new(seed)) } #[inline(always)]