Rweber/overlay size (#394)

Add methods to check if entities are correct size. Update aligned-vec version to incorporate upstreamed security fix.
This commit is contained in:
rickwebiii
2025-03-04 13:27:04 -08:00
committed by GitHub
parent f741fa1743
commit abfe2fed57
43 changed files with 235 additions and 312 deletions

71
Cargo.lock generated
View File

@@ -40,10 +40,11 @@ dependencies = [
[[package]]
name = "aligned-vec"
version = "0.5.0"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
checksum = "af15ccceeacb9304119d97925de463bc97ae3555ee8dc8056f67b119f66e5934"
dependencies = [
"equator",
"serde",
]
@@ -293,7 +294,7 @@ dependencies = [
"bitflags 2.4.2",
"cexpr",
"clang-sys",
"itertools 0.10.5",
"itertools 0.11.0",
"log",
"prettyplease",
"proc-macro2",
@@ -301,7 +302,7 @@ dependencies = [
"regex",
"rustc-hash 2.1.0",
"shlex",
"syn 2.0.49",
"syn 2.0.98",
]
[[package]]
@@ -845,7 +846,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16e44ab292b1dddfdaf7be62cfd8877df52f2f3fde5858d95bab606be259f20"
dependencies = [
"bitflags 2.4.2",
"libloading 0.7.4",
"libloading 0.8.1",
"winapi",
]
@@ -870,7 +871,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn 2.0.49",
"syn 2.0.98",
]
[[package]]
@@ -881,7 +882,7 @@ checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be"
dependencies = [
"darling_core",
"quote",
"syn 2.0.49",
"syn 2.0.98",
]
[[package]]
@@ -966,6 +967,26 @@ dependencies = [
"termcolor",
]
[[package]]
name = "equator"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
dependencies = [
"equator-macro",
]
[[package]]
name = "equator-macro"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.98",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@@ -1045,7 +1066,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.98",
]
[[package]]
@@ -1143,7 +1164,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.98",
]
[[package]]
@@ -2048,7 +2069,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.98",
]
[[package]]
@@ -2206,7 +2227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
dependencies = [
"proc-macro2",
"syn 2.0.49",
"syn 2.0.98",
]
[[package]]
@@ -2269,9 +2290,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.35"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
@@ -2688,7 +2709,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.98",
]
[[package]]
@@ -2960,7 +2981,7 @@ dependencies = [
"semver",
"serde",
"static_assertions",
"syn 2.0.49",
"syn 2.0.98",
"thiserror",
]
@@ -2972,7 +2993,7 @@ dependencies = [
"quote",
"serde_json",
"sunscreen_compiler_common",
"syn 2.0.49",
"syn 2.0.98",
"thiserror",
]
@@ -3044,7 +3065,7 @@ dependencies = [
"num",
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.98",
]
[[package]]
@@ -3129,9 +3150,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.49"
version = "2.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496"
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
dependencies = [
"proc-macro2",
"quote",
@@ -3218,7 +3239,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.98",
]
[[package]]
@@ -3462,7 +3483,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.98",
"wasm-bindgen-shared",
]
@@ -3496,7 +3517,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.98",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -3586,7 +3607,7 @@ dependencies = [
"js-sys",
"khronos-egl",
"libc",
"libloading 0.7.4",
"libloading 0.8.1",
"log",
"metal",
"naga",
@@ -3897,7 +3918,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.98",
]
[[package]]
@@ -3917,5 +3938,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.49",
"syn 2.0.98",
]

View File

@@ -38,7 +38,8 @@ lto = false
codegen-units = 16
[workspace.dependencies]
aligned-vec = { version = "0.5.0", features = ["serde"] }
# We must have our upstreamed security fix that addresses a panic during bincode deserialization when the length is malformed or malicious.
aligned-vec = { version = ">=0.6.2", features = ["serde"] }
bytemuck = "1.13.0"
raw-cpuid = "11.0.1"
lazy_static = "1.4.0"
@@ -60,8 +61,8 @@ find_cuda_helper = "0.2.0"
criterion = { version = "0.5.1", default-features = false }
darling = "0.20.3"
proc-macro2 = "1.0"
quote = "1.0.32"
syn = { version = "2.0.28", features = ["full"] }
quote = "1"
syn = { version = "2", features = ["full"] }
petgraph = { version = "0.6.0", features = ["serde-1"] }
serde = { version = "1.0.147", features = ["derive"] }
static_assertions = "1.1.0"

View File

@@ -1,3 +1,4 @@
use crate::error::*;
use crate::scratch::Pod;
macro_rules! avec {
@@ -42,36 +43,52 @@ macro_rules! dst {
*l = r.clone();
}
}
}
impl<T> crate::dst::AsSlice<$wrapper<T>> for $ref_t<T> where T: Clone $(+ $t_bounds)* {
#[allow(unused)]
/// Returns a slice view of the data representing a $t.
pub fn as_slice(&self) -> &[$wrapper<T>] {
fn as_slice(&self) -> &[$wrapper<T>] {
&self.data
}
}
#[allow(unused)]
impl<T> crate::dst::AsMutSlice<$wrapper<T>> for $ref_t<T> where T: Clone $(+ $t_bounds)* {
#[inline(always)]
/// Returns a mutable slice view of the data representing a $t.
pub fn as_mut_slice(&mut self) -> &mut [$wrapper<T>] {
fn as_mut_slice(&mut self) -> &mut [$wrapper<T>] {
&mut self.data
}
}
impl<T> crate::dst::FromSlice<$wrapper<T>> for $ref_t<T> where T: Clone $(+ $t_bounds)* {
fn from_slice(s: &[$wrapper<T>]) -> &$ref_t<T> {
// Casting the slice to the ref type is sound because it is #[repr(transparent)]
unsafe { &*(s as *const [$wrapper<T>] as *const $ref_t<T>) }
}
}
impl<T> crate::dst::FromMutSlice<$wrapper<T>> for $ref_t<T> where T: Clone $(+ $t_bounds)* {
fn from_mut_slice(s: &mut [$wrapper<T>]) -> &mut $ref_t<T> {
// Casting the mut slice to the mut ref type is sound because it is #[repr(transparent)]
unsafe { &mut *(s as *mut [$wrapper<T>] as *mut $ref_t<T>) }
}
}
impl<T> crate::dst::Len for $ref_t<T> where T: Clone $(+ $t_bounds)* {
#[inline(always)]
fn len(&self) -> usize {
use crate::dst::AsSlice;
self.as_slice().len()
}
}
impl<T> $ref_t<T> where T: Clone $(+ $t_bounds)*, $wrapper<T>: num::Zero {
#[allow(unused)]
/// Clears the contents of self to contain zero
pub fn clear(&mut self) {
use crate::dst::AsMutSlice;
for x in self.as_mut_slice() {
*x = <$wrapper<T> as num::Zero>::zero();
@@ -462,13 +479,50 @@ macro_rules! dst_iter {
pub type NoWrapper<T> = T;
pub(crate) trait AsSlice<T> {
fn as_slice(&self) -> &[T];
}
pub(crate) trait AsMutSlice<T> {
fn as_mut_slice(&mut self) -> &mut [T];
}
/// The length of an entity in fundamental elements (i.e. the type of polynomial coefficients in the underlying scheme).
pub trait Len {
/// Gets the length of this entity in fundamental elements.
fn len(&self) -> usize;
}
/// Describes how large an entity will be for the given parameters.
pub trait OverlaySize {
pub trait OverlaySize: Len {
/// The inputs that determine this entity's size
type Inputs: Copy + Clone;
/// Get the size of the entity.
fn size(t: Self::Inputs) -> usize;
#[inline(always)]
/// Returns if this entity is the correct length for the given input parameters
fn check_is_valid(&self, t: Self::Inputs) -> Result<()> {
if self.len() == Self::size(t) {
Ok(())
} else {
Err(Error::InvalidSize)
}
}
#[inline(always)]
/// Panics if this entity is not of the correct length.
fn assert_is_valid(&self, t: Self::Inputs) {
self.check_is_valid(t)
.expect("Entity was not the correct length.");
}
}
impl<S: Pod> Len for [S] {
fn len(&self) -> usize {
self.len()
}
}
impl<S: Pod> OverlaySize for [S] {

View File

@@ -2,7 +2,7 @@ use num::{Complex, Zero};
use serde::{Deserialize, Serialize};
use crate::{
dst::{NoWrapper, OverlaySize},
dst::{AsMutSlice, AsSlice, NoWrapper, OverlaySize},
entities::{
GgswCiphertextFftIterator, GgswCiphertextFftIteratorMut, GgswCiphertextFftRef,
GgswCiphertextIterator, GgswCiphertextIteratorMut, GgswCiphertextRef,

View File

@@ -2,7 +2,7 @@ use num::{Complex, Zero};
use serde::{Deserialize, Serialize};
use crate::{
dst::{NoWrapper, OverlaySize},
dst::{AsMutSlice, AsSlice, NoWrapper, OverlaySize},
entities::{
GgswCiphertextFftIterator, GgswCiphertextFftIteratorMut, GgswCiphertextFftRef,
GgswCiphertextIterator, GgswCiphertextIteratorMut, GgswCiphertextRef,
@@ -96,22 +96,13 @@ impl<S: TorusOps> BootstrapKeyRef<S> {
glwe: &GlweDef,
radix: &RadixDecomposition,
) {
self.assert_valid(lwe, glwe, radix);
result.assert_valid(lwe, glwe, radix);
self.assert_is_valid((lwe.dim, glwe.dim, radix.count));
result.assert_is_valid((lwe.dim, glwe.dim, radix.count));
for (s, r) in self.rows(glwe, radix).zip(result.rows_mut(glwe, radix)) {
s.fft(r, glwe, radix);
}
}
#[inline(always)]
/// Asserts that this entity is valid under the passed parameters.
pub fn assert_valid(&self, lwe: &LweDef, glwe: &GlweDef, radix: &RadixDecomposition) {
assert_eq!(
Self::size((lwe.dim, glwe.dim, radix.count)),
self.data.len()
);
}
}
dst! {
@@ -184,13 +175,4 @@ impl BootstrapKeyFftRef<Complex<f64>> {
s.ifft(r, params, radix);
}
}
/// Asserts that the [BootstrapKeyFft] is valid for the given parameters.
#[inline(always)]
pub fn assert_valid(&self, lwe: &LweDef, glwe: &GlweDef, radix: &RadixDecomposition) {
assert_eq!(
self.as_slice().len(),
BootstrapKeyFftRef::size((lwe.dim, glwe.dim, radix.count))
);
}
}

View File

@@ -2,8 +2,9 @@ use serde::{Deserialize, Serialize};
use sunscreen_math::Zero;
use crate::{
dst::OverlaySize, GlweDef, GlweDimension, LweDef, LweDimension,
PrivateFunctionalKeyswitchLweCount, RadixCount, RadixDecomposition, Torus, TorusOps,
dst::{AsMutSlice, AsSlice, OverlaySize},
GlweDef, GlweDimension, LweDef, LweDimension, PrivateFunctionalKeyswitchLweCount, RadixCount,
RadixDecomposition, Torus, TorusOps,
};
use super::{
@@ -117,17 +118,4 @@ impl<S: TorusOps> CircuitBootstrappingKeyswitchKeysRef<S> {
ParallelPrivateFunctionalKeyswitchKeyIterMut::new(self.as_mut_slice(), stride)
}
#[inline(always)]
/// Assert these keys are valid under the given parameters.
pub fn assert_valid(&self, from_lwe: &LweDef, to_glwe: &GlweDef, radix: &RadixDecomposition) {
assert_eq!(
self.as_slice().len(),
CircuitBootstrappingKeyswitchKeysRef::<S>::size((
from_lwe.dim,
to_glwe.dim,
radix.count,
))
);
}
}

View File

@@ -101,17 +101,11 @@ where
params: &GlweDef,
radix: &RadixDecomposition,
) {
self.assert_valid(params, radix);
result.assert_valid(params, radix);
self.assert_is_valid((params.dim, radix.count));
result.assert_is_valid((params.dim, radix.count));
for (s, r) in self.rows(params, radix).zip(result.rows_mut(params, radix)) {
s.fft(r, params);
}
}
#[inline(always)]
/// Assert that the GGSW ciphertext is valid for the given parameters.
pub fn assert_valid(&self, glwe: &GlweDef, radix: &RadixDecomposition) {
assert_eq!(self.as_slice().len(), Self::size((glwe.dim, radix.count)));
}
}

View File

@@ -2,7 +2,7 @@ use num::{Complex, Zero};
use serde::{Deserialize, Serialize};
use crate::{
dst::{NoWrapper, OverlaySize},
dst::{AsMutSlice, AsSlice, NoWrapper, OverlaySize},
entities::GgswCiphertextRef,
GlweDef, GlweDimension, RadixCount, RadixDecomposition, TorusOps,
};
@@ -76,10 +76,4 @@ impl GgswCiphertextFftRef<Complex<f64>> {
s.ifft(r, params);
}
}
#[inline(always)]
/// Asserts that this entity is valid under the passed parameters.
pub fn assert_valid(&self, glwe: &GlweDef, radix: &RadixDecomposition) {
assert_eq!(Self::size((glwe.dim, radix.count)), self.data.len());
}
}

View File

@@ -71,12 +71,4 @@ where
i.fft(fft, params);
}
}
/// Assert that this entityt is valid.
pub fn assert_valid(&self, params: &GlweDef, radix: &RadixDecomposition) {
assert_eq!(
self.data.len(),
GlevCiphertextRef::<S>::size((params.dim, radix.count))
);
}
}

View File

@@ -139,8 +139,8 @@ where
/// Create an FFT transformed version of `self` stored to result.
pub fn fft(&self, result: &mut GlweCiphertextFftRef<Complex<f64>>, params: &GlweDef) {
self.assert_valid(params);
result.assert_valid(params);
self.assert_is_valid(params.dim);
result.assert_is_valid(params.dim);
for (a, fft) in self.a(params).zip(result.a_mut(params)) {
a.fft(fft);
@@ -149,15 +149,6 @@ where
self.b(params).fft(result.b_mut(params));
}
#[inline(always)]
/// Asserts that this entity is valid for the given `params`
pub fn assert_valid(&self, params: &GlweDef) {
assert_eq!(
self.as_slice().len(),
GlweCiphertextRef::<S>::size(params.dim)
)
}
/// Sets all coefficients of the polynomial at the specified index in the
/// GLWE ciphertext's mask (a) to zero.
///

View File

@@ -2,7 +2,7 @@ use num::{complex::Complex64, Complex, Zero};
use serde::{Deserialize, Serialize};
use crate::{
dst::{FromMutSlice, FromSlice, NoWrapper, OverlaySize},
dst::{AsMutSlice, AsSlice, FromMutSlice, FromSlice, NoWrapper, OverlaySize},
GlweDef, GlweDimension, TorusOps,
};
@@ -109,12 +109,6 @@ impl GlweCiphertextFftRef<Complex<f64>> {
self.b(params).ifft(result.b_mut(params));
}
#[inline(always)]
/// Asserts this entity is valid for the given `params`.
pub fn assert_valid(&self, params: &GlweDef) {
assert_eq!(Self::size(params.dim), self.data.len());
}
}
#[cfg(test)]

View File

@@ -100,7 +100,7 @@ where
{
params.assert_valid();
assert!(plaintext_bits.0 < S::BITS);
ct.assert_valid(params);
ct.assert_is_valid(params.dim);
let mut result = Polynomial::zero(ct.a_b(params).1.len());
@@ -156,15 +156,6 @@ where
pub fn to_lwe_secret_key(&self) -> &LweSecretKeyRef<S> {
LweSecretKeyRef::from_slice(&self.data)
}
#[inline(always)]
/// Asserts that this entity is valid for the given `params`
pub fn assert_valid(&self, params: &GlweDef) {
assert_eq!(
self.as_slice().len(),
GlweSecretKeyRef::<S>::size(params.dim)
);
}
}
impl<S> GlweSecretKeyRef<S>

View File

@@ -92,15 +92,6 @@ impl<S: TorusOps> LweCiphertextRef<S> {
b
}
/// Asserts that the LWE ciphertext is valid for a given LWE dimension.
#[inline(always)]
pub fn assert_valid(&self, params: &LweDef) {
assert_eq!(
self.as_slice().len(),
LweCiphertextRef::<S>::size(params.dim)
);
}
}
#[cfg(test)]

View File

@@ -1,7 +1,10 @@
use serde::{Deserialize, Serialize};
use sunscreen_math::Zero;
use crate::{dst::OverlaySize, LweDef, LweDimension, Torus, TorusOps};
use crate::{
dst::{AsMutSlice, AsSlice, OverlaySize},
LweDef, LweDimension, Torus, TorusOps,
};
use super::{LweCiphertextIterator, LweCiphertextIteratorMut, LweCiphertextRef};

View File

@@ -80,20 +80,6 @@ where
LevCiphertextIteratorMut::new(&mut self.data, stride)
}
/// Asserts that the keyswitch key is valid for the given parameters.
#[inline(always)]
pub fn assert_valid(
&self,
original_params: &LweDef,
new_params: &LweDef,
radix: &RadixDecomposition,
) {
assert_eq!(
self.as_slice().len(),
LweKeyswitchKeyRef::<S>::size((original_params.dim, new_params.dim, radix.count))
);
}
}
#[cfg(test)]

View File

@@ -52,7 +52,7 @@ where
/// then using the resulting ciphertext as the public key.
pub fn generate(sk: &LweSecretKeyRef<S>, params: &LweDef) -> Self {
params.assert_valid();
sk.assert_valid(params);
sk.assert_is_valid(params.dim);
let mut pk = LwePublicKey {
data: avec![Torus::zero(); LwePublicKeyRef::<S>::size(params.dim)],
@@ -90,7 +90,7 @@ where
plaintext_bits: PlaintextBits,
) -> (LweCiphertext<S>, TlwePublicEncRandomness<S>) {
params.assert_valid();
self.assert_valid(params);
self.assert_is_valid(params.dim);
assert!(plaintext_bits.0 < S::BITS);
let msg = Torus::<S>::encode(msg, plaintext_bits);
@@ -129,12 +129,6 @@ where
(acc, noise)
}
#[inline(always)]
/// Assert this entity is valid under the given `lwe`.
pub fn assert_valid(&self, lwe: &LweDef) {
assert_eq!(Self::size(lwe.dim), self.data.len());
}
}
#[cfg(test)]

View File

@@ -91,7 +91,7 @@ where
/// [Self::decrypt] for a function that performs the decoding automatically.
pub fn decrypt_without_decode(&self, ct: &LweCiphertextRef<S>, params: &LweDef) -> Torus<S> {
params.assert_valid();
ct.assert_valid(params);
ct.assert_is_valid(params.dim);
let (a, b) = ct.a_b(params);
@@ -116,20 +116,12 @@ where
) -> S {
params.assert_valid();
assert!(plaintext_bits.0 < S::BITS);
ct.assert_valid(params);
ct.assert_is_valid(params.dim);
let msg = self.decrypt_without_decode(ct, params);
msg.decode(plaintext_bits)
}
/// Asserts that a given secret key is valid for a given LWE dimension.
pub fn assert_valid(&self, params: &LweDef) {
assert_eq!(
self.as_slice().len(),
LweSecretKeyRef::<S>::size(params.dim)
);
}
}
impl<S> LweSecretKeyRef<S>

View File

@@ -7,7 +7,7 @@ use num::{Complex, Zero};
use serde::{Deserialize, Serialize};
use crate::{
dst::{FromMutSlice, FromSlice, NoWrapper, OverlaySize},
dst::{AsMutSlice, FromMutSlice, FromSlice, NoWrapper, OverlaySize},
fft::negacyclic::get_fft,
polynomial::{polynomial_add_assign, polynomial_external_mad, polynomial_sub_assign},
scratch::allocate_scratch,
@@ -133,12 +133,6 @@ where
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
/// Asserts that this polynomial is valid for the given degree.
#[inline]
pub fn assert_valid(&self, degree: PolynomialDegree) {
assert_eq!(self.degree(), degree);
}
}
impl<T> PolynomialRef<T>

View File

@@ -1,7 +1,7 @@
use num::Complex;
use crate::{
dst::{NoWrapper, OverlaySize},
dst::{AsMutSlice, AsSlice, NoWrapper, OverlaySize},
fft::negacyclic::get_fft,
scratch::allocate_scratch,
simd, FrequencyTransform, FromF64, NumBits, PolynomialDegree,

View File

@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use sunscreen_math::Zero;
use crate::{
dst::OverlaySize,
dst::{AsMutSlice, AsSlice, OverlaySize},
entities::{GlevCiphertextIterator, GlevCiphertextIteratorMut, GlevCiphertextRef},
GlweDef, GlweDimension, LweDef, LweDimension, PrivateFunctionalKeyswitchLweCount, RadixCount,
RadixDecomposition, Torus, TorusOps,
@@ -113,24 +113,4 @@ impl<S: TorusOps> PrivateFunctionalKeyswitchKeyRef<S> {
GlevCiphertextRef::<S>::size((to_glwe.dim, radix.count)),
)
}
#[inline(always)]
/// Assert this value is correct for the given parameters.
pub fn assert_valid(
&self,
from_lwe: &LweDef,
to_glwe: &GlweDef,
radix: &RadixDecomposition,
lwe_count: &PrivateFunctionalKeyswitchLweCount,
) {
assert_eq!(
self.as_slice().len(),
PrivateFunctionalKeyswitchKeyRef::<S>::size((
from_lwe.dim,
to_glwe.dim,
radix.count,
*lwe_count
))
)
}
}

View File

@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use sunscreen_math::Zero;
use crate::{
dst::OverlaySize,
dst::{AsMutSlice, AsSlice, OverlaySize},
entities::{GlevCiphertextIterator, GlevCiphertextIteratorMut, GlevCiphertextRef},
GlweDef, GlweDimension, LweDef, LweDimension, RadixCount, RadixDecomposition, Torus, TorusOps,
};
@@ -63,12 +63,4 @@ impl<S: TorusOps> PublicFunctionalKeyswitchKeyRef<S> {
GlevCiphertextIteratorMut::new(self.as_mut_slice(), stride)
}
/// Asserts that the key is valid for the given parameters.
pub fn assert_valid(&self, from_lwe: &LweDef, to_glwe: &GlweDef, radix: &RadixDecomposition) {
assert_eq!(
self.as_slice().len(),
PublicFunctionalKeyswitchKeyRef::<S>::size((from_lwe.dim, to_glwe.dim, radix.count))
);
}
}

View File

@@ -63,12 +63,4 @@ where
(p0.next().unwrap(), p1)
}
/// Asserts this (`RlwePublicKey`)[RlwePublicKey] matches the given glwe parameters.
pub fn assert_valid(&self, glwe: &GlweDef) {
assert_eq!(
self.as_slice().len(),
GlweCiphertextRef::<S>::size(glwe.dim)
)
}
}

View File

@@ -177,8 +177,8 @@ where
params: &GlweDef,
radix: &RadixDecomposition,
) {
self.assert_valid(params, radix);
result.assert_valid(params, radix);
self.assert_is_valid((params.dim, radix.count));
result.assert_is_valid((params.dim, radix.count));
for (s, r) in self
.glev_ciphertexts(params, radix)
@@ -187,12 +187,6 @@ where
s.fft(r, params);
}
}
#[inline(always)]
/// Asserts that this entity is valid under the passed parameters.
pub fn assert_valid(&self, params: &GlweDef, radix: &RadixDecomposition) {
assert_eq!(Self::size((params.dim, radix.count)), self.data.len());
}
}
#[cfg(test)]

View File

@@ -2,7 +2,7 @@ use num::{Complex, Zero};
use serde::{Deserialize, Serialize};
use crate::{
dst::{NoWrapper, OverlaySize},
dst::{AsMutSlice, AsSlice, NoWrapper, OverlaySize},
entities::SchemeSwitchKeyRef,
GlweDef, GlweDimension, RadixCount, RadixDecomposition, TorusOps,
};
@@ -157,10 +157,4 @@ impl SchemeSwitchKeyFftRef<Complex<f64>> {
s.ifft(r, params);
}
}
#[inline(always)]
/// Asserts that this entity is valid under the passed parameters.
pub fn assert_valid(&self, glwe: &GlweDef, radix: &RadixDecomposition) {
assert_eq!(Self::size((glwe.dim, radix.count)), self.data.len());
}
}

View File

@@ -109,10 +109,4 @@ impl<S: TorusOps> UnivariateLookupTableRef<S> {
*o = Torus::encode(val, plaintext_bits);
}
}
#[inline(always)]
/// Asserts this LUT is valid under the given [`GlweDef`] parameters.
pub fn assert_valid(&self, glwe: &GlweDef) {
assert_eq!(Self::size(glwe.dim), self.data.len());
}
}

View File

@@ -1,4 +1,10 @@
#[derive(thiserror::Error)]
/// Errors that can occur using this crate.
#[derive(Debug, thiserror::Error)]
pub enum Error {
OutOfRange
}
/// The size of the given entity is invalid under the given scheme parameters.
#[error("The given entity is the incorrect size for the requested parameters.")]
InvalidSize,
}
/// A result that can occur in this crate.
pub type Result<T> = std::result::Result<T, Error>;

View File

@@ -37,3 +37,7 @@ pub mod high_level;
/// Zero Knowledge proofs for TFHE.
#[cfg(feature = "logproof")]
pub mod zkp;
/// Container [`Error`] and [`Result`] types for this crate.
mod error;
pub use error::*;

View File

@@ -6,7 +6,10 @@ use std::{
use num::traits::MulAdd;
use crate::{
dst::FromMutSlice, entities::PolynomialRef, scratch::allocate_scratch, ToF64, Torus, TorusOps,
dst::{AsMutSlice, AsSlice, FromMutSlice},
entities::PolynomialRef,
scratch::allocate_scratch,
ToF64, Torus, TorusOps,
};
/// Polynomial subtraction in place. This is equivalent to `a -= b` for each

View File

@@ -13,8 +13,8 @@ use crate::{
keyswitch::private_functional_keyswitch::private_functional_keyswitch,
},
scratch::allocate_scratch_ref,
GlweDef, LweDef, PlaintextBits, PrivateFunctionalKeyswitchLweCount, RadixDecomposition, Torus,
TorusOps,
GlweDef, LweDef, OverlaySize, PlaintextBits, PrivateFunctionalKeyswitchLweCount,
RadixDecomposition, Torus, TorusOps,
};
/// Bootstraps a LWE ciphertext to a GGSW ciphertext.
@@ -163,10 +163,10 @@ pub fn circuit_bootstrap<S: TorusOps>(
pbs_radix.assert_valid::<S>();
cbs_radix.assert_valid::<S>();
pfks_radix.assert_valid::<S>();
cbsksk.assert_valid(&glwe_2.as_lwe_def(), glwe_1, pfks_radix);
bsk.assert_valid(lwe_0, glwe_2, pbs_radix);
output.assert_valid(glwe_1, cbs_radix);
input.assert_valid(lwe_0);
cbsksk.assert_is_valid((glwe_2.as_lwe_def().dim, glwe_1.dim, pfks_radix.count));
bsk.assert_is_valid((lwe_0.dim, glwe_2.dim, pbs_radix.count));
output.assert_is_valid((glwe_1.dim, cbs_radix.count));
input.assert_is_valid(lwe_0.dim);
// Step 1, for each l in cbs_radix.count, use bootstrapping to base decompose the
// plaintext in input. We bootstrap from level 0 -> level 2.

View File

@@ -18,7 +18,7 @@ use crate::{
fft_ops::cmux,
},
scratch::allocate_scratch_ref,
CarryBits, GlweDef, LweDef, PlaintextBits, RadixDecomposition, Torus, TorusOps,
CarryBits, GlweDef, LweDef, OverlaySize, PlaintextBits, RadixDecomposition, Torus, TorusOps,
};
use super::rotate_glwe_negative_monomial_negacyclic;
@@ -44,9 +44,9 @@ pub fn generate_bootstrap_key<S>(
lwe.assert_valid();
glwe.assert_valid();
radix.assert_valid::<S>();
bootstrap_key.assert_valid(lwe, glwe, radix);
sk.assert_valid(glwe);
sk_to_encrypt.assert_valid(lwe);
bootstrap_key.assert_is_valid((lwe.dim, glwe.dim, radix.count));
sk.assert_is_valid(glwe.dim);
sk_to_encrypt.assert_is_valid(lwe.dim);
sk_to_encrypt
.s()
@@ -352,10 +352,10 @@ pub fn generalized_programmable_bootstrap<S>(
lwe_params.assert_valid();
glwe_params.assert_valid();
radix.assert_valid::<S>();
bootstrap_key.assert_valid(lwe_params, glwe_params, radix);
lut.assert_valid(glwe_params);
input.assert_valid(lwe_params);
output.assert_valid(glwe_params);
bootstrap_key.assert_is_valid((lwe_params.dim, glwe_params.dim, radix.count));
lut.assert_is_valid(glwe_params.dim);
input.assert_is_valid(lwe_params.dim);
output.assert_is_valid(glwe_params.dim);
// Steps:
// 1. Modulus switch the ciphertext to 2N.

View File

@@ -4,7 +4,7 @@ use std::{
};
use crate::{
dst::FromMutSlice,
dst::{AsSlice, FromMutSlice},
entities::{
GgswCiphertextRef, GlevCiphertextRef, GlweCiphertextRef, GlweSecretKeyRef, Polynomial,
PolynomialFft, PolynomialRef, SchemeSwitchKeyRef,
@@ -13,7 +13,7 @@ use crate::{
ops::{ciphertext::decomposed_polynomial_glev_mad, encryption::encrypt_secret_glev_ciphertext},
radix::PolynomialRadixIterator,
scratch::allocate_scratch_ref,
GlweDef, RadixDecomposition, Torus, TorusOps,
GlweDef, OverlaySize, RadixDecomposition, Torus, TorusOps,
};
use num::{Complex, Zero};
@@ -33,8 +33,8 @@ pub fn generate_scheme_switch_key<S>(
params.assert_valid();
radix.assert_valid::<S>();
scheme_switch_key.assert_valid(params, radix);
sk.assert_valid(params);
scheme_switch_key.assert_is_valid((params.dim, radix.count));
sk.assert_is_valid(params.dim);
let polynomial_size = params.dim.polynomial_degree.0;
@@ -261,9 +261,9 @@ pub fn scheme_switch<S>(
) where
S: TorusOps,
{
ssk.assert_valid(params, radix_ss);
output.assert_valid(params, radix_ggsw);
glev_ciphertext.assert_valid(params, radix_ggsw);
ssk.assert_is_valid((params.dim, radix_ss.count));
output.assert_is_valid((params.dim, radix_ggsw.count));
glev_ciphertext.assert_is_valid((params.dim, radix_ggsw.count));
let k = params.dim.size.0;
@@ -417,7 +417,7 @@ mod tests {
generate_scheme_switch_key(&mut ssk, &sk, &params, &radix);
// Basic validity checks
ssk.assert_valid(&params, &radix);
ssk.assert_is_valid((params.dim, radix.count));
// Check dimensions
let expected_glev_count = (glwe_size * (glwe_size + 1)) / 2;

View File

@@ -10,7 +10,7 @@ use crate::{
},
radix::PolynomialRadixIterator,
scratch::allocate_scratch_ref,
GlweDef, RadixDecomposition, TorusOps,
GlweDef, OverlaySize, RadixDecomposition, TorusOps,
};
/**
@@ -36,7 +36,7 @@ pub fn sample_extract<S>(
) where
S: TorusOps,
{
glwe.assert_valid(params);
glwe.assert_is_valid(params.dim);
assert!(h < params.dim.polynomial_degree.0);
// We are copying parts of the GLWE ciphertext out according to the following rule:

View File

@@ -4,7 +4,7 @@ use crate::{
ops::encryption::encrypt_secret_glev_ciphertext_generic,
polynomial::polynomial_external_mad,
scratch::allocate_scratch_ref,
GlweDef, PlaintextBits, RadixDecomposition, Torus, TorusOps,
GlweDef, OverlaySize, PlaintextBits, RadixDecomposition, Torus, TorusOps,
};
use super::{
@@ -172,8 +172,8 @@ pub fn decrypt_ggsw_ciphertext<S>(
assert_eq!(msg.len(), params.dim.polynomial_degree.0);
params.assert_valid();
radix.assert_valid::<S>();
ggsw_ciphertext.assert_valid(params, radix);
glwe_secret_key.assert_valid(params);
ggsw_ciphertext.assert_is_valid((params.dim, radix.count));
glwe_secret_key.assert_is_valid(params.dim);
// To decrypt a GGSW ciphertext, it suffices to decrypt the first GLWE
// ciphertext in the last row. We can decrypt any of the GLWE ciphertexts in

View File

@@ -6,7 +6,7 @@ use crate::{
ops::encryption::rlwe_encrypt_public,
polynomial::polynomial_scalar_mul,
scratch::allocate_scratch_ref,
GlweDef, RadixDecomposition, Torus, TorusOps,
GlweDef, OverlaySize, RadixDecomposition, Torus, TorusOps,
};
use super::{
@@ -36,9 +36,9 @@ pub(crate) fn encrypt_secret_glev_ciphertext_generic<S>(
S: TorusOps,
{
radix.assert_valid::<S>();
glev_ciphertext.assert_valid(params, radix);
msg.assert_valid(params.dim.polynomial_degree);
glwe_secret_key.assert_valid(params);
glev_ciphertext.assert_is_valid((params.dim, radix.count));
msg.assert_is_valid(params.dim.polynomial_degree);
glwe_secret_key.assert_is_valid(params.dim);
let decomposition_radix_log = radix.radix_log.0;
@@ -142,9 +142,9 @@ pub fn encrypt_rlev_ciphertext<S>(
S: TorusOps,
{
radix.assert_valid::<S>();
msg.assert_valid(params.dim.polynomial_degree);
rlev_ciphertext.assert_valid(params, radix);
rlwe_public_key.assert_valid(params);
msg.assert_is_valid(params.dim.polynomial_degree);
rlev_ciphertext.assert_is_valid((params.dim, radix.count));
rlwe_public_key.assert_is_valid(params.dim);
allocate_scratch_ref!(
scaled_msg,
@@ -172,10 +172,10 @@ pub(crate) fn decrypt_glwe_in_glev<S>(
S: TorusOps,
{
radix.assert_valid::<S>();
msg.assert_valid(params.dim.polynomial_degree);
msg.assert_is_valid(params.dim.polynomial_degree);
params.assert_valid();
glev_ciphertext.assert_valid(params, radix);
glwe_secret_key.assert_valid(params);
glev_ciphertext.assert_is_valid((params.dim, radix.count));
glwe_secret_key.assert_is_valid(params.dim);
let decomposition_radix_log = radix.radix_log.0;

View File

@@ -1,10 +1,11 @@
use sunscreen_math::Zero;
use crate::{
dst::AsSlice,
entities::{LweCiphertextRef, LweSecretKeyRef},
math::{Torus, TorusOps},
rand::{normal_torus, uniform_torus},
LweDef, PlaintextBits,
LweDef, OverlaySize, PlaintextBits,
};
/// Generate a trivial GLWE encryption. Note that the caller will need to scale
@@ -17,7 +18,7 @@ pub fn trivially_encrypt_lwe_ciphertext<S>(
S: TorusOps,
{
params.assert_valid();
c.assert_valid(params);
c.assert_is_valid(params.dim);
let (a, b) = c.a_b_mut(params);

View File

@@ -8,7 +8,7 @@ use crate::{
polynomial::{polynomial_add_assign, polynomial_external_mad},
rand::{binary_torus_polynomial, normal_torus_polynomial},
scratch::allocate_scratch_ref,
GlweDef, PlaintextBits, Torus, TorusOps,
GlweDef, OverlaySize, PlaintextBits, Torus, TorusOps,
};
/// The randomness used to generate a public-key RLWE encryption of a message.
@@ -133,8 +133,8 @@ where
{
assert_eq!(glwe.dim.size.0, 1);
assert_eq!(encoded_msg.len(), glwe.dim.polynomial_degree.0);
ct.assert_valid(glwe);
public_key.assert_valid(glwe);
ct.assert_is_valid(glwe.dim);
public_key.assert_is_valid(glwe.dim);
ct.clear();

View File

@@ -154,10 +154,10 @@ pub fn cmux<S>(
{
params.assert_valid();
radix.assert_valid::<S>();
c.assert_valid(params);
d_0.assert_valid(params);
d_1.assert_valid(params);
b_fft.assert_valid(params, radix);
c.assert_is_valid(params.dim);
d_0.assert_is_valid(params.dim);
d_1.assert_is_valid(params.dim);
b_fft.assert_is_valid((params.dim, radix.count));
allocate_scratch_ref!(diff, GlweCiphertextRef<S>, (params.dim));
@@ -406,9 +406,9 @@ pub fn scheme_switch_fft<S>(
) where
S: TorusOps,
{
ssk_fft.assert_valid(params, radix_ss);
output.assert_valid(params, radix_ggsw);
glev_ciphertext.assert_valid(params, radix_ggsw);
ssk_fft.assert_is_valid((params.dim, radix_ss.count));
output.assert_is_valid((params.dim, radix_ggsw.count));
glev_ciphertext.assert_is_valid((params.dim, radix_ggsw.count));
let k = params.dim.size.0;

View File

@@ -1,4 +1,4 @@
use crate::{entities::LweCiphertextRef, LweDef, Torus, TorusOps};
use crate::{entities::LweCiphertextRef, LweDef, OverlaySize, Torus, TorusOps};
/// Add `amount` to each torus element (mod q) in the ciphertext.
/// This shifts where messages lie on the torus and adds no noise.
@@ -12,8 +12,8 @@ pub fn rotate<S: TorusOps>(
amount: Torus<S>,
lwe: &LweDef,
) {
output.assert_valid(lwe);
input.assert_valid(lwe);
output.assert_is_valid(lwe.dim);
input.assert_is_valid(lwe.dim);
output.a_mut(lwe).clone_from_slice(input.a(lwe));
*output.b_mut(lwe) = input.b(lwe) + amount;

View File

@@ -94,7 +94,7 @@ pub fn generate_keyswitch_key_glwe<S>(
mod tests {
use crate::{
dst::FromSlice,
dst::{AsSlice, FromSlice},
entities::{GlweKeyswitchKey, GlweKeyswitchKeyRef},
high_level::{TEST_GLWE_DEF_1, TEST_RADIX},
Torus,

View File

@@ -7,7 +7,7 @@ use crate::{
},
radix::PolynomialRadixIterator,
scratch::allocate_scratch_ref,
LweDef, PolynomialDegree, RadixDecomposition, TorusOps,
LweDef, OverlaySize, PolynomialDegree, RadixDecomposition, TorusOps,
};
/// Switches a ciphertext under the original key to a ciphertext under the new
@@ -33,9 +33,9 @@ pub fn keyswitch_lwe_to_lwe<S>(
old_params.assert_valid();
new_params.assert_valid();
radix.assert_valid::<S>();
output.assert_valid(new_params);
ciphertext_under_original_key.assert_valid(old_params);
keyswitch_key.assert_valid(old_params, new_params, radix);
output.assert_is_valid(new_params.dim);
ciphertext_under_original_key.assert_is_valid(old_params.dim);
keyswitch_key.assert_is_valid((old_params.dim, new_params.dim, radix.count));
let (ciphertext_a, ciphertext_b) = ciphertext_under_original_key.a_b(old_params);

View File

@@ -1,7 +1,7 @@
use crate::{
entities::{LweKeyswitchKeyRef, LweSecretKeyRef},
ops::encryption::encrypt_lwe_ciphertext,
LweDef, RadixDecomposition, Torus, TorusOps,
LweDef, OverlaySize, RadixDecomposition, Torus, TorusOps,
};
/// Generates a keyswitch key from an original LWE key to a new LWE key. The
@@ -26,10 +26,10 @@ pub fn generate_keyswitch_key_lwe<S>(
old_params.assert_valid();
new_params.assert_valid();
radix.assert_valid::<S>();
new_lwe_secret_key.assert_valid(new_params);
original_lwe_secret_key.assert_valid(old_params);
new_lwe_secret_key.assert_valid(new_params);
keyswitch_key.assert_valid(old_params, new_params, radix);
new_lwe_secret_key.assert_is_valid(new_params.dim);
original_lwe_secret_key.assert_is_valid(old_params.dim);
new_lwe_secret_key.assert_is_valid(new_params.dim);
keyswitch_key.assert_is_valid((old_params.dim, new_params.dim, radix.count));
let decomposition_radix_log = radix.radix_log.0;

View File

@@ -2,7 +2,7 @@ use rayon::iter::{IndexedParallelIterator, ParallelIterator};
use sunscreen_math::Zero;
use crate::{
dst::FromMutSlice,
dst::{AsSlice, FromMutSlice},
entities::{
CircuitBootstrappingKeyswitchKeysRef, GlweCiphertextRef, GlweSecretKeyRef,
LweCiphertextRef, LweSecretKeyRef, PolynomialRef, PrivateFunctionalKeyswitchKeyRef,
@@ -13,7 +13,8 @@ use crate::{
},
radix::{scale_by_decomposition_factor, ScalarRadixIterator},
scratch::allocate_scratch_ref,
GlweDef, LweDef, PrivateFunctionalKeyswitchLweCount, RadixDecomposition, Torus, TorusOps,
GlweDef, LweDef, OverlaySize, PrivateFunctionalKeyswitchLweCount, RadixDecomposition, Torus,
TorusOps,
};
/// Initialize `output`, a
@@ -51,10 +52,10 @@ pub fn generate_private_functional_keyswitch_key<S, F>(
S: TorusOps,
F: Fn(&mut PolynomialRef<Torus<S>>, &[Torus<S>]),
{
output.assert_valid(from_lwe, to_glwe, radix, lwe_count);
output.assert_is_valid((from_lwe.dim, to_glwe.dim, radix.count, *lwe_count));
radix.assert_valid::<S>();
from_key.assert_valid(from_lwe);
to_key.assert_valid(to_glwe);
from_key.assert_is_valid(from_lwe.dim);
to_key.assert_is_valid(to_glwe.dim);
to_glwe.assert_valid();
from_lwe.assert_valid();
lwe_count.assert_valid();
@@ -101,8 +102,8 @@ pub fn private_functional_keyswitch<S: TorusOps>(
radix: &RadixDecomposition,
lwe_count: &PrivateFunctionalKeyswitchLweCount,
) {
output.assert_valid(to_glwe);
pfksk.assert_valid(from_lwe, to_glwe, radix, lwe_count);
output.assert_is_valid(to_glwe.dim);
pfksk.assert_is_valid((from_lwe.dim, to_glwe.dim, radix.count, *lwe_count));
from_lwe.assert_valid();
to_glwe.assert_valid();
radix.assert_valid::<S>();
@@ -138,10 +139,10 @@ pub fn generate_circuit_bootstrapping_pfks_keys<S: TorusOps>(
to_glwe: &GlweDef,
radix: &RadixDecomposition,
) {
output.assert_valid(from_lwe, to_glwe, radix);
from_key.assert_valid(from_lwe);
output.assert_is_valid((from_lwe.dim, to_glwe.dim, radix.count));
from_key.assert_is_valid(from_lwe.dim);
to_glwe.assert_valid();
to_key.assert_valid(to_glwe);
to_key.assert_is_valid(to_glwe.dim);
radix.assert_valid::<S>();
from_lwe.assert_valid();

View File

@@ -10,13 +10,13 @@ use crate::ops::fft_ops::decomposed_polynomial_glev_mad;
use crate::polynomial::polynomial_add_assign;
use crate::radix::PolynomialRadixIterator;
use crate::scratch::allocate_scratch;
use crate::Torus;
use crate::{
entities::{GlweCiphertextFftRef, GlweSecretKeyRef, PublicFunctionalKeyswitchKeyRef},
radix::scale_by_decomposition_factor,
scratch::allocate_scratch_ref,
GlweDef, LweDef, RadixDecomposition, TorusOps,
};
use crate::{OverlaySize, Torus};
/// Generate a public functional keyswitch key, which is used to transform a
/// list of LWE ciphertexts into a GLWE ciphertext while applying a provided
@@ -34,9 +34,9 @@ pub fn generate_public_functional_keyswitch_key<S: TorusOps>(
to_glwe: &GlweDef,
radix: &RadixDecomposition,
) {
from_sk.assert_valid(from_lwe);
to_sk.assert_valid(to_glwe);
output.assert_valid(from_lwe, to_glwe, radix);
from_sk.assert_is_valid(from_lwe.dim);
to_sk.assert_is_valid(to_glwe.dim);
output.assert_is_valid((from_lwe.dim, to_glwe.dim, radix.count));
allocate_scratch_ref!(pt, PolynomialRef<Torus<S>>, (to_glwe.dim.polynomial_degree));
pt.clear();
@@ -83,11 +83,11 @@ pub fn public_functional_keyswitch<S, F>(
S: TorusOps,
F: Fn(&mut PolynomialRef<Torus<S>>, &[Torus<S>]),
{
pufksk.assert_valid(from_lwe, to_glwe, radix);
output.assert_valid(to_glwe);
pufksk.assert_is_valid((from_lwe.dim, to_glwe.dim, radix.count));
output.assert_is_valid(to_glwe.dim);
for i in inputs {
i.assert_valid(from_lwe);
i.assert_is_valid(from_lwe.dim);
}
assert!(inputs.len() <= to_glwe.dim.polynomial_degree.0);