mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-04-28 03:01:21 -04:00
Compare commits
2 Commits
am/chore/m
...
mz/common_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffffce8079 | ||
|
|
80eb5d712a |
7
Makefile
7
Makefile
@@ -1794,6 +1794,13 @@ bench_boolean: install_rs_check_toolchain
|
||||
--bench boolean \
|
||||
--features=boolean,internal-keycache -p tfhe-benchmark
|
||||
|
||||
.PHONY: bench_common_mask # Run benchmarks for CM-PBS
|
||||
bench_common_mask: install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \
|
||||
--bench cm-bench \
|
||||
--features=experimental -p tfhe-benchmark
|
||||
|
||||
|
||||
.PHONY: bench_ks # Run benchmarks for keyswitch
|
||||
bench_ks: install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" __TFHE_RS_PARAM_TYPE=$(BENCH_PARAM_TYPE) __TFHE_RS_PARAMS_SET=$(BENCH_PARAMS_SET) __TFHE_RS_BENCH_TYPE=$(BENCH_TYPE) \
|
||||
|
||||
@@ -54,6 +54,7 @@ internal-keycache = ["tfhe/internal-keycache"]
|
||||
avx512 = ["tfhe/avx512"]
|
||||
pbs-stats = ["tfhe/pbs-stats"]
|
||||
zk-pok = ["tfhe/zk-pok", "dep:tfhe-zk-pok"]
|
||||
experimental = ["tfhe/experimental"]
|
||||
|
||||
[[bench]]
|
||||
name = "boolean"
|
||||
@@ -230,3 +231,9 @@ required-features = ["integer", "internal-keycache"]
|
||||
name = "wasm_benchmarks_parser"
|
||||
path = "src/bin/wasm_benchmarks_parser.rs"
|
||||
required-features = ["shortint", "internal-keycache"]
|
||||
|
||||
[[bench]]
|
||||
name = "cm-bench"
|
||||
path = "benches/core_crypto/cm_bench.rs"
|
||||
harness = false
|
||||
required-features = ["experimental"]
|
||||
|
||||
211
tfhe-benchmark/benches/core_crypto/cm_bench.rs
Normal file
211
tfhe-benchmark/benches/core_crypto/cm_bench.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
use cm_fft64::programmable_bootstrap_cm_lwe_ciphertext;
|
||||
use criterion::{black_box, criterion_main, Criterion};
|
||||
use tfhe::core_crypto::experimental::prelude::cm_lwe_keyswitch_key_generation::allocate_and_generate_new_cm_lwe_keyswitch_key;
|
||||
use tfhe::core_crypto::experimental::prelude::cm_modulus_switch_noise_reduction::improve_lwe_ciphertext_modulus_switch_noise_for_binary_key_cm;
|
||||
use tfhe::core_crypto::experimental::prelude::*;
|
||||
use tfhe::core_crypto::prelude::*;
|
||||
|
||||
fn cm_bench(c: &mut Criterion) {
|
||||
let bench_cm_params_2_minus_64: Vec<CmApParams> = vec![
|
||||
CM_PARAM_2_2_MINUS_64,
|
||||
CM_PARAM_4_2_MINUS_64,
|
||||
CM_PARAM_6_2_MINUS_64,
|
||||
CM_PARAM_8_2_MINUS_64,
|
||||
CM_PARAM_10_2_MINUS_64,
|
||||
CM_PARAM_2_4_MINUS_64,
|
||||
CM_PARAM_4_4_MINUS_64,
|
||||
CM_PARAM_6_4_MINUS_64,
|
||||
CM_PARAM_8_4_MINUS_64,
|
||||
CM_PARAM_10_4_MINUS_64,
|
||||
CM_PARAM_2_6_MINUS_64,
|
||||
CM_PARAM_4_6_MINUS_64,
|
||||
CM_PARAM_6_6_MINUS_64,
|
||||
CM_PARAM_8_6_MINUS_64,
|
||||
CM_PARAM_10_6_MINUS_64,
|
||||
CM_PARAM_2_8_MINUS_64,
|
||||
CM_PARAM_4_8_MINUS_64,
|
||||
CM_PARAM_6_8_MINUS_64,
|
||||
CM_PARAM_8_8_MINUS_64,
|
||||
CM_PARAM_10_8_MINUS_64,
|
||||
];
|
||||
|
||||
cm_bench_for_pfail(c, &bench_cm_params_2_minus_64, "2^-64");
|
||||
|
||||
let bench_cm_params_2_minus_128: Vec<CmApParams> = vec![
|
||||
CM_PARAM_2_2_MINUS_128,
|
||||
CM_PARAM_4_2_MINUS_128,
|
||||
CM_PARAM_6_2_MINUS_128,
|
||||
CM_PARAM_8_2_MINUS_128,
|
||||
CM_PARAM_10_2_MINUS_128,
|
||||
CM_PARAM_2_4_MINUS_128,
|
||||
CM_PARAM_4_4_MINUS_128,
|
||||
CM_PARAM_6_4_MINUS_128,
|
||||
CM_PARAM_8_4_MINUS_128,
|
||||
CM_PARAM_10_4_MINUS_128,
|
||||
CM_PARAM_2_6_MINUS_128,
|
||||
CM_PARAM_4_6_MINUS_128,
|
||||
CM_PARAM_6_6_MINUS_128,
|
||||
CM_PARAM_8_6_MINUS_128,
|
||||
CM_PARAM_10_6_MINUS_128,
|
||||
CM_PARAM_2_8_MINUS_128,
|
||||
CM_PARAM_4_8_MINUS_128,
|
||||
CM_PARAM_6_8_MINUS_128,
|
||||
CM_PARAM_8_8_MINUS_128,
|
||||
CM_PARAM_10_8_MINUS_128,
|
||||
];
|
||||
|
||||
cm_bench_for_pfail(c, &bench_cm_params_2_minus_128, "2^-128");
|
||||
}
|
||||
|
||||
fn cm_bench_for_pfail(c: &mut Criterion, bench_cm_params: &[CmApParams], p_fail: &str) {
|
||||
let mut bench_group = c.benchmark_group("Common Mask Benchmarks");
|
||||
bench_group.sample_size(10);
|
||||
|
||||
// Create the PRNG
|
||||
let mut seeder = new_seeder();
|
||||
let seeder = seeder.as_mut();
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed(), seeder);
|
||||
let mut secret_generator = SecretRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed());
|
||||
|
||||
for cm_param in bench_cm_params {
|
||||
let cm_dimension = cm_param.cm_dimension;
|
||||
|
||||
let ciphertext_modulus = cm_param.ciphertext_modulus;
|
||||
|
||||
let bench_name = format!(
|
||||
"KS-CM-PBS_p={}_w={}_pfail={p_fail}",
|
||||
cm_param.precision, cm_dimension.0,
|
||||
);
|
||||
|
||||
let lwe_noise_distribution = cm_param.lwe_noise_distribution;
|
||||
|
||||
assert_eq!(
|
||||
cm_param.ciphertext_modulus,
|
||||
CiphertextModulus::<u64>::new_native()
|
||||
);
|
||||
let encoding_with_padding = 1 << 63;
|
||||
let glwe_dimension = cm_param.glwe_dimension;
|
||||
let polynomial_size = cm_param.polynomial_size;
|
||||
|
||||
let msg_modulus = 1u64 << cm_param.precision;
|
||||
let delta = encoding_with_padding / msg_modulus;
|
||||
|
||||
let f = |x| x;
|
||||
|
||||
let accumulator = cm_generate_programmable_bootstrap_glwe_lut(
|
||||
polynomial_size,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
msg_modulus.cast_into(),
|
||||
cm_param.ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
);
|
||||
|
||||
let CmBootstrapKeys {
|
||||
small_lwe_sk,
|
||||
big_lwe_sk,
|
||||
bsk,
|
||||
fbsk,
|
||||
} = generate_cm_pbs_keys(cm_param, &mut encryption_generator, &mut secret_generator);
|
||||
drop(bsk);
|
||||
|
||||
let cm_lwe_keyswitch_key = allocate_and_generate_new_cm_lwe_keyswitch_key(
|
||||
&big_lwe_sk,
|
||||
&small_lwe_sk,
|
||||
cm_dimension,
|
||||
cm_param.base_log_ks,
|
||||
cm_param.level_ks,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let plaintexts = PlaintextList::from_container(vec![0_u64; cm_dimension.0]);
|
||||
|
||||
let ct_in = allocate_and_encrypt_new_cm_lwe_ciphertext(
|
||||
&big_lwe_sk,
|
||||
&plaintexts,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let mut ct_after_ks = CmLweCiphertext::new(
|
||||
0u64,
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension(),
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let mut ct_out = CmLweCiphertext::new(
|
||||
0u64,
|
||||
fbsk.output_lwe_dimension(),
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let max_nb_zeros_n = cm_param.max_nb_zeros_n.ceil() as usize;
|
||||
|
||||
let mut encryptions_of_zero = CmLweCiphertextList::new(
|
||||
0,
|
||||
cm_param.lwe_dimension,
|
||||
cm_dimension,
|
||||
CmLweCiphertextCount(max_nb_zeros_n),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let plaintext_list = PlaintextList::new(0, PlaintextCount(cm_dimension.0));
|
||||
|
||||
let plaintext_lists: Vec<_> = (0..max_nb_zeros_n)
|
||||
.map(|_| plaintext_list.clone())
|
||||
.collect();
|
||||
|
||||
encrypt_cm_lwe_ciphertext_list(
|
||||
&small_lwe_sk,
|
||||
&mut encryptions_of_zero,
|
||||
&plaintext_lists,
|
||||
lwe_noise_distribution,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let log_modulus = polynomial_size.to_blind_rotation_input_modulus_log();
|
||||
|
||||
{
|
||||
bench_group.bench_function(&bench_name, |b| {
|
||||
b.iter(|| {
|
||||
cm_keyswitch_lwe_ciphertext(&cm_lwe_keyswitch_key, &ct_in, &mut ct_after_ks);
|
||||
|
||||
improve_lwe_ciphertext_modulus_switch_noise_for_binary_key_cm(
|
||||
&mut ct_after_ks,
|
||||
&encryptions_of_zero,
|
||||
cm_param.r_sigma_factor_n,
|
||||
cm_param.ms_bound_n,
|
||||
cm_param.ms_input_variance_n,
|
||||
log_modulus,
|
||||
);
|
||||
|
||||
programmable_bootstrap_cm_lwe_ciphertext(
|
||||
&ct_after_ks,
|
||||
&mut ct_out,
|
||||
&accumulator.as_view(),
|
||||
&fbsk,
|
||||
);
|
||||
|
||||
black_box(&mut ct_out);
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bench_group.finish();
|
||||
}
|
||||
|
||||
pub fn cm_group() {
|
||||
let mut criterion: Criterion<_> = (Criterion::default()).configure_from_args();
|
||||
|
||||
cm_bench(&mut criterion);
|
||||
}
|
||||
|
||||
criterion_main!(cm_group);
|
||||
@@ -329,13 +329,14 @@ fn encrypt_constant_ggsw_level_matrix_row<Scalar, NoiseDistribution, KeyCont, Ou
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut body = row_as_glwe.get_mut_body();
|
||||
|
||||
if row_index < last_row_index {
|
||||
// Not the last row
|
||||
let sk_poly_list = glwe_secret_key.as_polynomial_list();
|
||||
let sk_poly = sk_poly_list.get(row_index);
|
||||
|
||||
// Copy the key polynomial to the output body, to avoid allocating a temporary buffer
|
||||
let mut body = row_as_glwe.get_mut_body();
|
||||
body.as_mut().copy_from_slice(sk_poly.as_ref());
|
||||
|
||||
let ciphertext_modulus = body.ciphertext_modulus();
|
||||
@@ -352,7 +353,6 @@ fn encrypt_constant_ggsw_level_matrix_row<Scalar, NoiseDistribution, KeyCont, Ou
|
||||
}
|
||||
} else {
|
||||
// The last row needs a slightly different treatment
|
||||
let mut body = row_as_glwe.get_mut_body();
|
||||
let ciphertext_modulus = body.ciphertext_modulus();
|
||||
|
||||
body.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
@@ -90,6 +90,162 @@ impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C> for
|
||||
Self::from_container(from, ciphertext_modulus)
|
||||
}
|
||||
}
|
||||
pub type GlweBodyOwned<Scalar> = GlweBody<Vec<Scalar>>;
|
||||
pub type GlweBodyView<'data, Scalar> = GlweBody<&'data [Scalar]>;
|
||||
pub type GlweBodyMutView<'data, Scalar> = GlweBody<&'data mut [Scalar]>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GlweBodyList<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
pub type GlweBodyListView<'data, Scalar> = GlweBodyList<&'data [Scalar]>;
|
||||
pub type GlweBodyListMutView<'data, Scalar> = GlweBodyList<&'data mut [Scalar]>;
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for GlweBodyList<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for GlweBodyList<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'data, T: UnsignedInteger> CreateFrom<&'data [T]> for GlweBodyListView<'data, T> {
|
||||
type Metadata = GlweBodyListCreationMetadata<T>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: &[T], meta: Self::Metadata) -> GlweBodyListView<'_, T> {
|
||||
let GlweBodyListCreationMetadata {
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
GlweBodyList {
|
||||
data: from,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'data, T: UnsignedInteger> CreateFrom<&'data mut [T]> for GlweBodyListMutView<'data, T> {
|
||||
type Metadata = GlweBodyListCreationMetadata<T>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: &mut [T], meta: Self::Metadata) -> GlweBodyListMutView<'_, T> {
|
||||
let GlweBodyListCreationMetadata {
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
GlweBodyList {
|
||||
data: from,
|
||||
ciphertext_modulus,
|
||||
polynomial_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> GlweBodyList<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
assert!(container.container_len().is_multiple_of(polynomial_size.0));
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<Scalar> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> GlweBodyList<C> {
|
||||
pub fn as_mut_polynomial_list(&mut self) -> PolynomialListMutView<'_, Scalar> {
|
||||
let polynomial_size = self.polynomial_size();
|
||||
PolynomialList::from_container(self.as_mut(), polynomial_size)
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata used in the [`CreateFrom`] implementation to create [`LweBodyList`] entities.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GlweBodyListCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for GlweBodyList<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = GlweCiphertextCreationMetadata<Self::Element>;
|
||||
|
||||
type EntityView<'this>
|
||||
= GlweBodyView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = GlweBodyListCreationMetadata<Self::Element>;
|
||||
|
||||
type SelfView<'this>
|
||||
= GlweBodyListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
GlweCiphertextCreationMetadata {
|
||||
polynomial_size: self.polynomial_size,
|
||||
ciphertext_modulus: self.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.polynomial_size.0
|
||||
}
|
||||
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
GlweBodyListCreationMetadata {
|
||||
polynomial_size: self.polynomial_size,
|
||||
ciphertext_modulus: self.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for GlweBodyList<C>
|
||||
{
|
||||
type EntityMutView<'this>
|
||||
= GlweBodyMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this>
|
||||
= GlweBodyListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
|
||||
/// A convenience structure to easily manipulate the mask of a [`GlweCiphertext`].
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
@@ -150,6 +150,10 @@ impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> LweBodyList<C> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn lwe_body_count(&self) -> LweBodyCount {
|
||||
LweBodyCount(self.data.container_len())
|
||||
}
|
||||
@@ -259,6 +263,10 @@ impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> LweMask<C> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
/// Return the [`LweDimension`] of the [`LweMask`].
|
||||
///
|
||||
/// See [`LweMask::from_container`] for usage.
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
use crate::core_crypto::algorithms::polynomial_algorithms::*;
|
||||
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
use crate::core_crypto::commons::math::torus::UnsignedTorus;
|
||||
use crate::core_crypto::commons::numeric::CastInto;
|
||||
use crate::core_crypto::commons::parameters::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, MonomialDegree, PolynomialSize,
|
||||
};
|
||||
use crate::core_crypto::commons::traits::{
|
||||
ContiguousEntityContainer, ContiguousEntityContainerMut,
|
||||
};
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::fft_impl::common::modulus_switch;
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::FftView;
|
||||
use aligned_vec::CACHELINE_ALIGN;
|
||||
use dyn_stack::{PodStack, StackReq};
|
||||
use itertools::{izip, Itertools};
|
||||
|
||||
pub fn cm_blind_rotate_requirement<Scalar>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
StackReq::any_of(&[
|
||||
// tmp_poly allocation
|
||||
StackReq::new_aligned::<Scalar>(polynomial_size.0, CACHELINE_ALIGN),
|
||||
StackReq::all_of(&[
|
||||
// ct1 allocation
|
||||
StackReq::new_aligned::<Scalar>(
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size),
|
||||
CACHELINE_ALIGN,
|
||||
),
|
||||
// external product
|
||||
cm_add_external_product_assign_requirement::<Scalar>(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
fft,
|
||||
),
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn cm_bootstrap_requirement<Scalar>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
cm_blind_rotate_requirement::<Scalar>(glwe_dimension, cm_dimension, polynomial_size, fft).and(
|
||||
StackReq::new_aligned::<Scalar>(
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size),
|
||||
CACHELINE_ALIGN,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cm_blind_rotate_assign_requirement<Scalar>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
StackReq::all_of(&[
|
||||
StackReq::new_aligned::<Scalar>(
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size),
|
||||
CACHELINE_ALIGN,
|
||||
),
|
||||
cm_cmux_requirement::<Scalar>(glwe_dimension, cm_dimension, polynomial_size, fft),
|
||||
])
|
||||
}
|
||||
|
||||
// CastInto required for PBS modulus switch which returns a usize
|
||||
pub fn cm_blind_rotate_assign<InputScalar, OutputScalar>(
|
||||
cm_bsk: FourierCmLweBootstrapKeyView<'_>,
|
||||
mut luts: CmGlweCiphertextMutView<'_, OutputScalar>,
|
||||
lwe: CmLweCiphertextView<'_, InputScalar>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) where
|
||||
InputScalar: UnsignedTorus + CastInto<usize>,
|
||||
OutputScalar: UnsignedTorus,
|
||||
{
|
||||
let mask = lwe.get_mask();
|
||||
let bodies = lwe.get_bodies();
|
||||
|
||||
let lut_poly_size = luts.polynomial_size();
|
||||
let ciphertext_modulus = luts.ciphertext_modulus();
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
let log_modulus = lut_poly_size.to_blind_rotation_input_modulus_log();
|
||||
|
||||
luts.get_mut_bodies()
|
||||
.as_mut_polynomial_list()
|
||||
.iter_mut()
|
||||
.zip_eq(bodies.iter())
|
||||
.for_each(|(mut poly, body)| {
|
||||
let monomial_degree =
|
||||
MonomialDegree(modulus_switch((*body.data).cast_into(), log_modulus));
|
||||
|
||||
let (tmp_poly, _) = stack.make_aligned_raw(poly.as_ref().len(), CACHELINE_ALIGN);
|
||||
|
||||
let mut tmp_poly = Polynomial::from_container(tmp_poly);
|
||||
tmp_poly.as_mut().copy_from_slice(poly.as_ref());
|
||||
polynomial_wrapping_monic_monomial_div(&mut poly, &tmp_poly, monomial_degree);
|
||||
});
|
||||
|
||||
// We initialize the ct_0 used for the successive cmuxes
|
||||
let mut ct0 = luts;
|
||||
let (ct1, stack) = stack.make_aligned_raw(ct0.as_ref().len(), CACHELINE_ALIGN);
|
||||
let mut ct1 = CmGlweCiphertextMutView::from_container(
|
||||
ct1,
|
||||
cm_bsk.glwe_dimension(),
|
||||
cm_bsk.cm_dimension(),
|
||||
lut_poly_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
for (lwe_mask_element, bootstrap_key_ggsw) in
|
||||
izip!(mask.as_ref().iter(), cm_bsk.into_cm_ggsw_iter())
|
||||
{
|
||||
if *lwe_mask_element != InputScalar::ZERO {
|
||||
let monomial_degree =
|
||||
MonomialDegree(modulus_switch((*lwe_mask_element).cast_into(), log_modulus));
|
||||
|
||||
// we effectively inline the body of cmux here, merging the initial subtraction
|
||||
// operation with the monic polynomial multiplication, then performing the external
|
||||
// product manually
|
||||
|
||||
// We rotate ct_1 and subtract ct_0 (first step of cmux) by performing
|
||||
// ct_1 <- (ct_0 * X^{a_hat}) - ct_0
|
||||
for (mut ct1_poly, ct0_poly) in izip!(
|
||||
ct1.as_mut_polynomial_list().iter_mut(),
|
||||
ct0.as_polynomial_list().iter(),
|
||||
) {
|
||||
polynomial_wrapping_monic_monomial_mul_and_subtract(
|
||||
&mut ct1_poly,
|
||||
&ct0_poly,
|
||||
monomial_degree,
|
||||
);
|
||||
}
|
||||
|
||||
// as_mut_view is required to keep borrow rules consistent
|
||||
// second step of cmux
|
||||
cm_add_external_product_assign(
|
||||
ct0.as_mut_view(),
|
||||
bootstrap_key_ggsw,
|
||||
ct1.as_view(),
|
||||
fft,
|
||||
stack,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if !ciphertext_modulus.is_native_modulus() {
|
||||
// When we convert back from the fourier domain, integer values will contain up to 53
|
||||
// MSBs with information. In our representation of power of 2 moduli < native modulus we
|
||||
// fill the MSBs and leave the LSBs empty, this usage of the signed decomposer allows to
|
||||
// round while keeping the data in the MSBs
|
||||
let signed_decomposer = SignedDecomposer::new(
|
||||
DecompositionBaseLog(ciphertext_modulus.get_custom_modulus().ilog2() as usize),
|
||||
DecompositionLevelCount(1),
|
||||
);
|
||||
ct0.as_mut()
|
||||
.iter_mut()
|
||||
.for_each(|x| *x = signed_decomposer.closest_representable(*x));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_bootstrap<InputScalar, OutputScalar>(
|
||||
cm_bsk: FourierCmLweBootstrapKeyView<'_>,
|
||||
mut lwe_out: CmLweCiphertextMutView<'_, OutputScalar>,
|
||||
lwe_in: CmLweCiphertextView<'_, InputScalar>,
|
||||
accumulator: CmGlweCiphertextView<'_, OutputScalar>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) where
|
||||
// CastInto required for PBS modulus switch which returns a usize
|
||||
InputScalar: UnsignedTorus + CastInto<usize>,
|
||||
OutputScalar: UnsignedTorus,
|
||||
{
|
||||
assert!(lwe_in.ciphertext_modulus().is_power_of_two());
|
||||
assert!(lwe_out.ciphertext_modulus().is_power_of_two());
|
||||
assert_eq!(
|
||||
lwe_out.ciphertext_modulus(),
|
||||
accumulator.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let (local_accumulator_data, stack) =
|
||||
stack.collect_aligned(CACHELINE_ALIGN, accumulator.as_ref().iter().copied());
|
||||
let mut local_accumulator = CmGlweCiphertextMutView::from_container(
|
||||
local_accumulator_data,
|
||||
accumulator.glwe_dimension(),
|
||||
accumulator.cm_dimension(),
|
||||
accumulator.polynomial_size(),
|
||||
accumulator.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
cm_blind_rotate_assign(
|
||||
cm_bsk,
|
||||
local_accumulator.as_mut_view(),
|
||||
lwe_in.as_view(),
|
||||
fft,
|
||||
stack,
|
||||
);
|
||||
|
||||
extract_lwe_sample_from_cm_glwe_ciphertext(&local_accumulator, &mut lwe_out, MonomialDegree(0));
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
//! Module containing primitives pertaining to
|
||||
//! [`CommonMask GGSW ciphertext encryption`](`CmGgswCiphertext`).
|
||||
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::algorithms::*;
|
||||
use crate::core_crypto::commons::ciphertext_modulus::CiphertextModulusKind;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::decomposition::DecompositionLevel;
|
||||
use crate::core_crypto::commons::math::random::{Distribution, Uniform};
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::encrypt_cm_glwe_ciphertext_assign;
|
||||
use crate::core_crypto::prelude::{Cleartext, GlweSecretKey};
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub fn encrypt_constant_cm_ggsw_ciphertext<Scalar, NoiseDistribution, KeyCont, OutputCont, Gen>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
output: &mut CmGgswCiphertext<OutputCont>,
|
||||
// TODO: Use a CleartextList
|
||||
cleartexts: &[Cleartext<Scalar>],
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
for glwe_secret_key in glwe_secret_keys {
|
||||
assert!(
|
||||
output.polynomial_size() == glwe_secret_key.polynomial_size(),
|
||||
"Mismatch between polynomial sizes of output ciphertexts and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output.polynomial_size(),
|
||||
glwe_secret_key.polynomial_size()
|
||||
);
|
||||
|
||||
assert!(
|
||||
output.glwe_dimension() == glwe_secret_key.glwe_dimension(),
|
||||
"Mismatch between GlweDimension of output ciphertexts and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output.glwe_dimension(),
|
||||
glwe_secret_key.glwe_dimension()
|
||||
);
|
||||
}
|
||||
|
||||
// Generators used to have same sequential and parallel key generation
|
||||
let gen_iter = generator
|
||||
.try_fork_from_config(output.encryption_fork_config(Uniform, noise_distribution))
|
||||
.expect("Failed to split generator into ggsw levels");
|
||||
|
||||
let decomp_base_log = output.decomposition_base_log();
|
||||
let decomp_level_count = output.decomposition_level_count();
|
||||
let ciphertext_modulus = output.ciphertext_modulus();
|
||||
|
||||
for (level_index, (mut level_matrix, mut generator)) in
|
||||
output.iter_mut().zip(gen_iter).enumerate()
|
||||
{
|
||||
let decomp_level = DecompositionLevel(decomp_level_count.0 - level_index);
|
||||
|
||||
let factors = cleartexts
|
||||
.iter()
|
||||
.map(|cleartext| {
|
||||
ggsw_encryption_multiplicative_factor(
|
||||
ciphertext_modulus,
|
||||
decomp_level,
|
||||
decomp_base_log,
|
||||
*cleartext,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
// We iterate over the rows of the level matrix, the last row needs special treatment
|
||||
let gen_iter = generator
|
||||
.try_fork_from_config(level_matrix.encryption_fork_config(Uniform, noise_distribution))
|
||||
.expect("Failed to split generator into glwe");
|
||||
|
||||
let last_row_index = level_matrix.glwe_dimension().0;
|
||||
|
||||
for ((row_index, mut row_as_glwe), mut generator) in level_matrix
|
||||
.as_mut_cm_glwe_list()
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.zip(gen_iter)
|
||||
{
|
||||
encrypt_constant_cm_ggsw_level_matrix_row(
|
||||
glwe_secret_keys,
|
||||
(row_index, last_row_index),
|
||||
&factors,
|
||||
&mut row_as_glwe,
|
||||
noise_distribution,
|
||||
&mut generator,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn par_encrypt_constant_cm_ggsw_ciphertext<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
KeyCont,
|
||||
OutputCont,
|
||||
Gen,
|
||||
>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
output: &mut CmGgswCiphertext<OutputCont>,
|
||||
cleartexts: &[Cleartext<Scalar>],
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution> + Sync + Send,
|
||||
NoiseDistribution: Distribution + Sync,
|
||||
KeyCont: Container<Element = Scalar> + Sync,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ParallelByteRandomGenerator,
|
||||
{
|
||||
for glwe_secret_key in glwe_secret_keys {
|
||||
assert!(
|
||||
output.polynomial_size() == glwe_secret_key.polynomial_size(),
|
||||
"Mismatch between polynomial sizes of output ciphertexts and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output.polynomial_size(),
|
||||
glwe_secret_key.polynomial_size()
|
||||
);
|
||||
|
||||
assert!(
|
||||
output.glwe_dimension() == glwe_secret_key.glwe_dimension(),
|
||||
"Mismatch between GlweDimension of output ciphertexts and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output.glwe_dimension(),
|
||||
glwe_secret_key.glwe_dimension()
|
||||
);
|
||||
}
|
||||
|
||||
// Generators used to have same sequential and parallel key generation
|
||||
let gen_iter = generator
|
||||
.par_try_fork_from_config(output.encryption_fork_config(Uniform, noise_distribution))
|
||||
.expect("Failed to split generator into ggsw levels");
|
||||
|
||||
let decomp_base_log = output.decomposition_base_log();
|
||||
let decomp_level_count = output.decomposition_level_count();
|
||||
let ciphertext_modulus = output.ciphertext_modulus();
|
||||
|
||||
output.par_iter_mut().zip(gen_iter).enumerate().for_each(
|
||||
|(level_index, (mut level_matrix, mut generator))| {
|
||||
let decomp_level = DecompositionLevel(decomp_level_count.0 - level_index);
|
||||
|
||||
let factors = cleartexts
|
||||
.iter()
|
||||
.map(|cleartext| {
|
||||
ggsw_encryption_multiplicative_factor(
|
||||
ciphertext_modulus,
|
||||
decomp_level,
|
||||
decomp_base_log,
|
||||
*cleartext,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
// We iterate over the rows of the level matrix, the last row needs special treatment
|
||||
let gen_iter = generator
|
||||
.par_try_fork_from_config(
|
||||
level_matrix.encryption_fork_config(Uniform, noise_distribution),
|
||||
)
|
||||
.expect("Failed to split generator into glwe");
|
||||
|
||||
let last_row_index = level_matrix.glwe_dimension().0;
|
||||
|
||||
level_matrix
|
||||
.as_mut_cm_glwe_list()
|
||||
.par_iter_mut()
|
||||
.enumerate()
|
||||
.zip(gen_iter)
|
||||
.for_each(|((row_index, mut row_as_glwe), mut generator)| {
|
||||
encrypt_constant_cm_ggsw_level_matrix_row(
|
||||
glwe_secret_keys,
|
||||
(row_index, last_row_index),
|
||||
&factors,
|
||||
&mut row_as_glwe,
|
||||
noise_distribution,
|
||||
&mut generator,
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn encrypt_constant_cm_ggsw_level_matrix_row<Scalar, NoiseDistribution, KeyCont, OutputCont, Gen>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
(row_index, first_body_row_index): (usize, usize),
|
||||
factors: &[Scalar],
|
||||
row_as_glwe: &mut CmGlweCiphertext<OutputCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert!(matches!(
|
||||
row_as_glwe.ciphertext_modulus().kind(),
|
||||
CiphertextModulusKind::Native | CiphertextModulusKind::NonNativePowerOfTwo
|
||||
));
|
||||
|
||||
let mut bodies = row_as_glwe.get_mut_bodies();
|
||||
|
||||
if row_index < first_body_row_index {
|
||||
// Mask row
|
||||
|
||||
// The Matrix must encode polynomail list
|
||||
// [factor1 * sk1[row_index], factor2 * sk2[row_index], ...]
|
||||
|
||||
for ((glwe_secret_key, mut body), factor) in glwe_secret_keys
|
||||
.iter()
|
||||
.zip_eq(bodies.iter_mut())
|
||||
.zip_eq(factors.iter())
|
||||
{
|
||||
let sk_poly_list = glwe_secret_key.as_polynomial_list();
|
||||
let sk_poly = sk_poly_list.get(row_index);
|
||||
|
||||
// Copy the key polynomial to the output body, to avoid allocating a temporary buffer
|
||||
body.as_mut().copy_from_slice(sk_poly.as_ref());
|
||||
|
||||
slice_wrapping_scalar_mul_assign(body.as_mut(), *factor)
|
||||
}
|
||||
} else {
|
||||
// Body rows
|
||||
|
||||
// The Matrix must encrypt polynomial list
|
||||
// [0, ..., -factor_i * X^0, ..., 0]
|
||||
// with i = body_row_index
|
||||
|
||||
bodies.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
let body_row_index = row_index - first_body_row_index;
|
||||
|
||||
let encoded = factors[body_row_index].wrapping_neg();
|
||||
|
||||
let mut body = bodies.get_mut(body_row_index);
|
||||
|
||||
// set the constant coefficient (X^0)
|
||||
body.as_mut()[0] = encoded;
|
||||
}
|
||||
encrypt_cm_glwe_ciphertext_assign(glwe_secret_keys, row_as_glwe, noise_distribution, generator);
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
use crate::core_crypto::commons::math::torus::UnsignedTorus;
|
||||
use crate::core_crypto::commons::parameters::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, PolynomialSize,
|
||||
};
|
||||
use crate::core_crypto::commons::traits::Split;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::fft_impl::fft64::crypto::ggsw::{collect_next_term, update_with_fmadd};
|
||||
use crate::core_crypto::fft_impl::fft64::math;
|
||||
use crate::core_crypto::prelude::{ContiguousEntityContainer, ContiguousEntityContainerMut};
|
||||
use aligned_vec::CACHELINE_ALIGN;
|
||||
use dyn_stack::{PodStack, StackReq};
|
||||
use itertools::izip;
|
||||
use math::decomposition::TensorSignedDecompositionLendingIter;
|
||||
use math::fft::FftView;
|
||||
use math::polynomial::FourierPolynomialMutView;
|
||||
use tfhe_fft::c64;
|
||||
|
||||
pub fn cm_add_external_product_assign_requirement<Scalar>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
let align = CACHELINE_ALIGN;
|
||||
let standard_scratch = StackReq::new_aligned::<Scalar>(
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size),
|
||||
align,
|
||||
);
|
||||
let fourier_polynomial_size = polynomial_size.to_fourier_polynomial_size();
|
||||
let fourier_scratch = StackReq::new_aligned::<c64>(
|
||||
cm_glwe_ciphertext_fourier_size(glwe_dimension, cm_dimension, fourier_polynomial_size),
|
||||
align,
|
||||
);
|
||||
let fourier_scratch_single = StackReq::new_aligned::<c64>(fourier_polynomial_size.0, align);
|
||||
|
||||
let substack3 = fft.forward_scratch();
|
||||
let substack2 = substack3.and(fourier_scratch_single);
|
||||
let substack1 = substack2.and(standard_scratch);
|
||||
let substack0 = StackReq::any_of(&[substack1.and(standard_scratch), fft.backward_scratch()]);
|
||||
substack0.and(fourier_scratch)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "__profiling", inline(never))]
|
||||
pub fn cm_add_external_product_assign<Scalar>(
|
||||
mut out: CmGlweCiphertextMutView<'_, Scalar>,
|
||||
ggsw: FourierCmGgswCiphertextView<'_>,
|
||||
glwe: CmGlweCiphertextView<Scalar>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
{
|
||||
// we check that the polynomial sizes match
|
||||
debug_assert_eq!(ggsw.polynomial_size(), glwe.polynomial_size());
|
||||
debug_assert_eq!(ggsw.polynomial_size(), out.polynomial_size());
|
||||
// we check that the glwe sizes match
|
||||
debug_assert_eq!(ggsw.glwe_dimension(), glwe.glwe_dimension());
|
||||
debug_assert_eq!(ggsw.glwe_dimension(), out.glwe_dimension());
|
||||
|
||||
let align = CACHELINE_ALIGN;
|
||||
let fourier_poly_size = ggsw.polynomial_size().to_fourier_polynomial_size().0;
|
||||
|
||||
// we round the input mask and body
|
||||
let decomposer = SignedDecomposer::<Scalar>::new(
|
||||
ggsw.decomposition_base_log(),
|
||||
ggsw.decomposition_level_count(),
|
||||
);
|
||||
|
||||
let (output_fft_buffer, substack0) = stack.make_aligned_raw::<c64>(
|
||||
fourier_poly_size * (ggsw.glwe_dimension().0 + ggsw.cm_dimension().0),
|
||||
align,
|
||||
);
|
||||
// output_fft_buffer is initially uninitialized, considered to be implicitly zero, to avoid
|
||||
// the cost of filling it up with zeros. `is_output_uninit` is set to `false` once
|
||||
// it has been fully initialized for the first time.
|
||||
let mut is_output_uninit = true;
|
||||
|
||||
{
|
||||
// ------------------------------------------------------ EXTERNAL PRODUCT IN FOURIER DOMAIN
|
||||
// In this section, we perform the external product in the fourier domain, and accumulate
|
||||
// the result in the output_fft_buffer variable.
|
||||
let (mut decomposition, substack1) = TensorSignedDecompositionLendingIter::new(
|
||||
glwe.as_ref()
|
||||
.iter()
|
||||
.map(|s| decomposer.init_decomposer_state(*s)),
|
||||
DecompositionBaseLog(decomposer.base_log),
|
||||
DecompositionLevelCount(decomposer.level_count),
|
||||
substack0,
|
||||
);
|
||||
|
||||
// We loop through the levels (we reverse to match the order of the decomposition iterator.)
|
||||
ggsw.into_levels().for_each(|ggsw_decomp_matrix| {
|
||||
// We retrieve the decomposition of this level.
|
||||
let (glwe_level, glwe_decomp_term, substack2) =
|
||||
collect_next_term(&mut decomposition, substack1, align);
|
||||
let glwe_decomp_term = CmGlweCiphertextView::from_container(
|
||||
&*glwe_decomp_term,
|
||||
ggsw.glwe_dimension(),
|
||||
ggsw.cm_dimension(),
|
||||
ggsw.polynomial_size(),
|
||||
out.ciphertext_modulus(),
|
||||
);
|
||||
debug_assert_eq!(ggsw_decomp_matrix.decomposition_level(), glwe_level);
|
||||
|
||||
// For each level we have to add the result of the vector-matrix product between the
|
||||
// decomposition of the glwe, and the ggsw level matrix to the output. To do so, we
|
||||
// iteratively add to the output, the product between every line of the matrix, and
|
||||
// the corresponding (scalar) polynomial in the glwe decomposition:
|
||||
//
|
||||
// ggsw_mat ggsw_mat
|
||||
// glwe_dec | - - - - | < glwe_dec | - - - - |
|
||||
// | - - - | x | - - - - | | - - - | x | - - - - | <
|
||||
// ^ | - - - - | ^ | - - - - |
|
||||
//
|
||||
// t = 1 t = 2 ...
|
||||
|
||||
izip!(
|
||||
ggsw_decomp_matrix.into_rows(),
|
||||
glwe_decomp_term.as_polynomial_list().iter()
|
||||
)
|
||||
.for_each(|(ggsw_row, glwe_poly)| {
|
||||
let (mut fourier, substack3) =
|
||||
substack2.make_aligned_raw::<c64>(fourier_poly_size, align);
|
||||
// We perform the forward fft transform for the glwe polynomial
|
||||
let fourier = fft
|
||||
.forward_as_integer(
|
||||
FourierPolynomialMutView { data: &mut fourier },
|
||||
glwe_poly,
|
||||
substack3,
|
||||
)
|
||||
.data;
|
||||
// Now we loop through the polynomials of the output, and add the
|
||||
// corresponding product of polynomials.
|
||||
|
||||
update_with_fmadd(
|
||||
output_fft_buffer,
|
||||
ggsw_row.data(),
|
||||
fourier,
|
||||
is_output_uninit,
|
||||
fourier_poly_size,
|
||||
);
|
||||
|
||||
// we initialized `output_fft_buffer, so we can set this to false
|
||||
is_output_uninit = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------------------------------- TRANSFORMATION OF RESULT TO STANDARD DOMAIN
|
||||
// In this section, we bring the result from the fourier domain, back to the standard
|
||||
// domain, and add it to the output.
|
||||
//
|
||||
// We iterate over the polynomials in the output.
|
||||
if !is_output_uninit {
|
||||
izip!(
|
||||
out.as_mut_polynomial_list().iter_mut(),
|
||||
output_fft_buffer
|
||||
.into_chunks(fourier_poly_size)
|
||||
.map(|slice| FourierPolynomialMutView { data: slice }),
|
||||
)
|
||||
.for_each(|(out, fourier)| {
|
||||
// The fourier buffer is not re-used afterwards so we can use the in-place version of
|
||||
// the add_backward_as_torus function
|
||||
fft.add_backward_in_place_as_torus(out, fourier, substack0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_cmux_requirement<Scalar>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
cm_add_external_product_assign_requirement::<Scalar>(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
fft,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cm_cmux<Scalar: UnsignedTorus>(
|
||||
ct0: CmGlweCiphertextMutView<'_, Scalar>,
|
||||
mut ct1: CmGlweCiphertextMutView<'_, Scalar>,
|
||||
ggsw: FourierCmGgswCiphertextView<'_>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) {
|
||||
izip!(ct1.as_mut(), ct0.as_ref()).for_each(|(c1, c0)| {
|
||||
*c1 = c1.wrapping_sub(*c0);
|
||||
});
|
||||
cm_add_external_product_assign(ct0, ggsw, ct1.as_view(), fft, stack);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use dyn_stack::PodBuffer;
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::*;
|
||||
use crate::core_crypto::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_cm_external_product() {
|
||||
let glwe_dimension = GlweDimension(2);
|
||||
let cm_dimension = CmDimension(2);
|
||||
let polynomial_size = PolynomialSize(64);
|
||||
let decomp_base_log = DecompositionBaseLog(8);
|
||||
let decomp_level_count = DecompositionLevelCount(3);
|
||||
let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
|
||||
let noise_distribution =
|
||||
DynamicDistribution::new_gaussian_from_std_dev(StandardDev(0.0000006791658447437413));
|
||||
|
||||
let fft = Fft::new(polynomial_size);
|
||||
let fft = fft.as_view();
|
||||
|
||||
let mut mem = PodBuffer::new(StackReq::new_aligned::<u64>(100_000, 512));
|
||||
|
||||
let mut seeder = new_seeder();
|
||||
let seeder = seeder.as_mut();
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed(), seeder);
|
||||
let mut secret_generator =
|
||||
SecretRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed());
|
||||
|
||||
let glwe_secret_keys = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_glwe_secret_key(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
&mut secret_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let mut ggsw = CmGgswCiphertext::new(
|
||||
0u64,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let cleartexts = (0..cm_dimension.0).map(|_| Cleartext(1)).collect_vec();
|
||||
|
||||
encrypt_constant_cm_ggsw_ciphertext(
|
||||
&glwe_secret_keys,
|
||||
&mut ggsw,
|
||||
&cleartexts,
|
||||
noise_distribution,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let mut ggsw_fourier = FourierCmGgswCiphertext::new(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
);
|
||||
|
||||
let stack = PodStack::new(&mut mem);
|
||||
|
||||
ggsw_fourier
|
||||
.as_mut_view()
|
||||
.fill_with_forward_fourier(ggsw.as_view(), fft, stack);
|
||||
|
||||
let mut glwe_in = CmGlweCiphertext::new(
|
||||
0u64,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let input_plaintext_list = PlaintextList::from_container(
|
||||
(0..cm_dimension.0 * polynomial_size.0)
|
||||
.map(|i| (i as u64 + 1) << 60)
|
||||
.collect_vec(),
|
||||
);
|
||||
|
||||
encrypt_cm_glwe_ciphertext(
|
||||
&glwe_secret_keys,
|
||||
&mut glwe_in,
|
||||
&input_plaintext_list,
|
||||
noise_distribution,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let mut glwe_out = CmGlweCiphertext::new(
|
||||
0u64,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let stack = PodStack::new(&mut mem);
|
||||
|
||||
super::cm_add_external_product_assign(
|
||||
glwe_out.as_mut_view(),
|
||||
ggsw_fourier.as_view(),
|
||||
glwe_in.as_view(),
|
||||
fft,
|
||||
stack,
|
||||
);
|
||||
|
||||
let mut decrypted =
|
||||
PlaintextList::new(0, PlaintextCount(cm_dimension.0 * polynomial_size.0));
|
||||
|
||||
decrypt_cm_glwe_ciphertext(&glwe_secret_keys, &glwe_out, &mut decrypted);
|
||||
|
||||
for (i, j) in input_plaintext_list.iter().zip_eq(decrypted.iter()) {
|
||||
let diff = j.0.wrapping_sub(*i.0) as i64;
|
||||
|
||||
assert!(diff.abs() < (1 << 57));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
//! Module containing primitives pertaining to
|
||||
//! [`CommonMask GLWE ciphertext encryption`](`GlweCiphertext`).
|
||||
|
||||
use crate::core_crypto::algorithms::polynomial_algorithms::*;
|
||||
use crate::core_crypto::algorithms::slice_algorithms::{
|
||||
slice_wrapping_scalar_div_assign, slice_wrapping_scalar_mul_assign,
|
||||
};
|
||||
use crate::core_crypto::commons::ciphertext_modulus::CiphertextModulusKind;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::random::{Distribution, Uniform};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::CmDimension;
|
||||
use itertools::Itertools;
|
||||
|
||||
pub fn fill_cm_glwe_mask_and_bodies_for_encryption_assign<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
KeyCont,
|
||||
BodyCont,
|
||||
MaskCont,
|
||||
Gen,
|
||||
>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
output_mask: &mut GlweMask<MaskCont>,
|
||||
output_bodies: &mut GlweBodyList<BodyCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
BodyCont: ContainerMut<Element = Scalar>,
|
||||
MaskCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert_eq!(
|
||||
output_mask.ciphertext_modulus(),
|
||||
output_bodies.ciphertext_modulus(),
|
||||
"Mismatched moduli between output_mask ({:?}) and output_body ({:?})",
|
||||
output_mask.ciphertext_modulus(),
|
||||
output_bodies.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let ciphertext_modulus = output_bodies.ciphertext_modulus();
|
||||
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
generator
|
||||
.fill_slice_with_random_uniform_mask_custom_mod(output_mask.as_mut(), ciphertext_modulus);
|
||||
generator.unsigned_integer_slice_wrapping_add_random_noise_from_distribution_custom_mod_assign(
|
||||
output_bodies.as_mut(),
|
||||
noise_distribution,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
if !ciphertext_modulus.is_native_modulus() {
|
||||
let torus_scaling = ciphertext_modulus.get_power_of_two_scaling_to_native_torus();
|
||||
slice_wrapping_scalar_mul_assign(output_mask.as_mut(), torus_scaling);
|
||||
slice_wrapping_scalar_mul_assign(output_bodies.as_mut(), torus_scaling);
|
||||
}
|
||||
|
||||
for (glwe_secret_key, mut output_body) in
|
||||
glwe_secret_keys.iter().zip_eq(output_bodies.iter_mut())
|
||||
{
|
||||
polynomial_wrapping_add_multisum_assign(
|
||||
&mut output_body.as_mut_polynomial(),
|
||||
&output_mask.as_polynomial_list(),
|
||||
&glwe_secret_key.as_polynomial_list(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encrypt_cm_glwe_ciphertext_assign<Scalar, NoiseDistribution, KeyCont, OutputCont, Gen>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
output: &mut CmGlweCiphertext<OutputCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
for glwe_secret_key in glwe_secret_keys {
|
||||
assert!(
|
||||
output.glwe_dimension() == glwe_secret_key.glwe_dimension(),
|
||||
"Mismatch between GlweDimension of output ciphertext and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output.glwe_dimension(),
|
||||
glwe_secret_key.glwe_dimension()
|
||||
);
|
||||
assert!(
|
||||
output.polynomial_size() == glwe_secret_key.polynomial_size(),
|
||||
"Mismatch between PolynomialSize of output ciphertext and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output.polynomial_size(),
|
||||
glwe_secret_key.polynomial_size()
|
||||
);
|
||||
}
|
||||
|
||||
let (mut mask, mut bodies) = output.get_mut_mask_and_bodies();
|
||||
|
||||
fill_cm_glwe_mask_and_bodies_for_encryption_assign(
|
||||
glwe_secret_keys,
|
||||
&mut mask,
|
||||
&mut bodies,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn fill_cm_glwe_mask_and_bodies_for_encryption<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
KeyCont,
|
||||
InputCont,
|
||||
BodyCont,
|
||||
MaskCont,
|
||||
Gen,
|
||||
>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
output_mask: &mut GlweMask<MaskCont>,
|
||||
output_bodies: &mut GlweBodyList<BodyCont>,
|
||||
encoded: &PlaintextList<InputCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
BodyCont: ContainerMut<Element = Scalar>,
|
||||
MaskCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert_eq!(
|
||||
output_mask.ciphertext_modulus(),
|
||||
output_bodies.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let ciphertext_modulus = output_bodies.ciphertext_modulus();
|
||||
|
||||
let polynomial_size = output_bodies.polynomial_size();
|
||||
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
generator
|
||||
.fill_slice_with_random_uniform_mask_custom_mod(output_mask.as_mut(), ciphertext_modulus);
|
||||
generator.fill_slice_with_random_noise_from_distribution_custom_mod(
|
||||
output_bodies.as_mut(),
|
||||
noise_distribution,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
for ((glwe_secret_key, mut output_body), encoded) in glwe_secret_keys
|
||||
.iter()
|
||||
.zip_eq(output_bodies.iter_mut())
|
||||
.zip_eq(encoded.chunks_exact(polynomial_size.0))
|
||||
{
|
||||
polynomial_wrapping_add_assign(
|
||||
&mut output_body.as_mut_polynomial(),
|
||||
&encoded.as_polynomial(),
|
||||
);
|
||||
|
||||
if !ciphertext_modulus.is_native_modulus() {
|
||||
let torus_scaling = ciphertext_modulus.get_power_of_two_scaling_to_native_torus();
|
||||
slice_wrapping_scalar_mul_assign(output_mask.as_mut(), torus_scaling);
|
||||
slice_wrapping_scalar_mul_assign(output_body.as_mut(), torus_scaling);
|
||||
}
|
||||
|
||||
polynomial_wrapping_add_multisum_assign(
|
||||
&mut output_body.as_mut_polynomial(),
|
||||
&output_mask.as_polynomial_list(),
|
||||
&glwe_secret_key.as_polynomial_list(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encrypt_cm_glwe_ciphertext<Scalar, NoiseDistribution, KeyCont, InputCont, OutputCont, Gen>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
output_glwe_ciphertext: &mut CmGlweCiphertext<OutputCont>,
|
||||
input_plaintext_list: &PlaintextList<InputCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert!(
|
||||
output_glwe_ciphertext.polynomial_size().0 * output_glwe_ciphertext.cm_dimension().0
|
||||
== input_plaintext_list.plaintext_count().0,
|
||||
"Mismatch between PolynomialSize of output ciphertext PlaintextCount of input. \
|
||||
Got {:?} in output, and {:?} in input.",
|
||||
output_glwe_ciphertext.polynomial_size(),
|
||||
input_plaintext_list.plaintext_count()
|
||||
);
|
||||
|
||||
for glwe_secret_key in glwe_secret_keys {
|
||||
assert!(
|
||||
output_glwe_ciphertext.glwe_dimension() == glwe_secret_key.glwe_dimension(),
|
||||
"Mismatch between GlweDimension of output ciphertext and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output_glwe_ciphertext.glwe_dimension(),
|
||||
glwe_secret_key.glwe_dimension()
|
||||
);
|
||||
assert!(
|
||||
output_glwe_ciphertext.polynomial_size() == glwe_secret_key.polynomial_size(),
|
||||
"Mismatch between PolynomialSize of output ciphertext and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
output_glwe_ciphertext.polynomial_size(),
|
||||
glwe_secret_key.polynomial_size()
|
||||
);
|
||||
}
|
||||
|
||||
let (mut mask, mut bodies) = output_glwe_ciphertext.get_mut_mask_and_bodies();
|
||||
|
||||
fill_cm_glwe_mask_and_bodies_for_encryption(
|
||||
glwe_secret_keys,
|
||||
&mut mask,
|
||||
&mut bodies,
|
||||
input_plaintext_list,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn decrypt_cm_glwe_ciphertext<Scalar, KeyCont, InputCont, OutputCont>(
|
||||
glwe_secret_keys: &[GlweSecretKey<KeyCont>],
|
||||
input_cm_glwe_ciphertext: &CmGlweCiphertext<InputCont>,
|
||||
output_plaintext_list: &mut PlaintextList<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
output_plaintext_list.plaintext_count().0
|
||||
== input_cm_glwe_ciphertext.polynomial_size().0 * glwe_secret_keys.len(),
|
||||
"Mismatched output PlaintextCount {:?} and input PolynomialSize {:?}",
|
||||
output_plaintext_list.plaintext_count(),
|
||||
input_cm_glwe_ciphertext.polynomial_size()
|
||||
);
|
||||
for glwe_secret_key in glwe_secret_keys {
|
||||
assert!(
|
||||
glwe_secret_key.glwe_dimension() == input_cm_glwe_ciphertext.glwe_dimension(),
|
||||
"Mismatched GlweDimension between glwe_secret_key {:?} and input_glwe_ciphertext {:?}",
|
||||
glwe_secret_key.glwe_dimension(),
|
||||
input_cm_glwe_ciphertext.glwe_dimension()
|
||||
);
|
||||
assert!(
|
||||
glwe_secret_key.polynomial_size() == input_cm_glwe_ciphertext.polynomial_size(),
|
||||
"Mismatched PolynomialSize between glwe_secret_key {:?} and input_glwe_ciphertext {:?}",
|
||||
glwe_secret_key.polynomial_size(),
|
||||
input_cm_glwe_ciphertext.polynomial_size()
|
||||
);
|
||||
}
|
||||
|
||||
let polynomial_size = input_cm_glwe_ciphertext.polynomial_size();
|
||||
|
||||
let ciphertext_modulus = input_cm_glwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
let (mask, bodies) = input_cm_glwe_ciphertext.get_mask_and_bodies();
|
||||
|
||||
for ((glwe_secret_key, body), mut output_plaintext_list) in glwe_secret_keys
|
||||
.iter()
|
||||
.zip_eq(bodies.iter())
|
||||
.zip_eq(output_plaintext_list.chunks_exact_mut(polynomial_size.0))
|
||||
{
|
||||
output_plaintext_list
|
||||
.as_mut()
|
||||
.copy_from_slice(body.as_ref());
|
||||
|
||||
polynomial_wrapping_sub_multisum_assign(
|
||||
&mut output_plaintext_list.as_mut_polynomial(),
|
||||
&mask.as_polynomial_list(),
|
||||
&glwe_secret_key.as_polynomial_list(),
|
||||
);
|
||||
}
|
||||
|
||||
if !ciphertext_modulus.is_native_modulus() {
|
||||
slice_wrapping_scalar_div_assign(
|
||||
output_plaintext_list.as_mut(),
|
||||
ciphertext_modulus.get_power_of_two_scaling_to_native_torus(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trivially_encrypt_cm_glwe_ciphertext<Scalar, InputCont, OutputCont>(
|
||||
output: &mut CmGlweCiphertext<OutputCont>,
|
||||
encoded: &PlaintextList<InputCont>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
encoded.plaintext_count().0 == output.polynomial_size().0 * output.cm_dimension().0,
|
||||
"Mismatched input PlaintextCount {:?} and output PolynomialSize * CmDimension {:?}",
|
||||
encoded.plaintext_count(),
|
||||
output.polynomial_size().0 * output.cm_dimension().0
|
||||
);
|
||||
|
||||
let (mut mask, mut body) = output.get_mut_mask_and_bodies();
|
||||
|
||||
mask.as_mut().fill(Scalar::ZERO);
|
||||
body.as_mut().copy_from_slice(encoded.as_ref());
|
||||
|
||||
let ciphertext_modulus = body.ciphertext_modulus();
|
||||
|
||||
match ciphertext_modulus.kind() {
|
||||
CiphertextModulusKind::Native | CiphertextModulusKind::Other => (),
|
||||
CiphertextModulusKind::NonNativePowerOfTwo => {
|
||||
slice_wrapping_scalar_mul_assign(
|
||||
body.as_mut(),
|
||||
ciphertext_modulus.get_power_of_two_scaling_to_native_torus(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocate_and_trivially_encrypt_new_cm_glwe_ciphertext<Scalar, InputCont>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
encoded: &PlaintextList<InputCont>,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> CmGlweCiphertextOwned<Scalar>
|
||||
where
|
||||
Scalar: UnsignedTorus,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
{
|
||||
let mut new_ct = CmGlweCiphertextOwned::new(
|
||||
Scalar::ZERO,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let mut bodies = new_ct.get_mut_bodies();
|
||||
bodies.as_mut().copy_from_slice(encoded.as_ref());
|
||||
|
||||
// Manage the non native power of 2 encoding
|
||||
if ciphertext_modulus.kind() == CiphertextModulusKind::NonNativePowerOfTwo {
|
||||
slice_wrapping_scalar_mul_assign(
|
||||
bodies.as_mut(),
|
||||
ciphertext_modulus.get_power_of_two_scaling_to_native_torus(),
|
||||
);
|
||||
}
|
||||
|
||||
new_ct
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
//! Module containing primitives pertaining to the operation usually referred to as a
|
||||
//! _sample extract_ in the literature. Allowing to extract a single
|
||||
//! [`CommonMask LWE Ciphertext`](`CmLweCiphertext`) from a given [`CommonMask GLWE
|
||||
//! ciphertext`](`CmGlweCiphertext`).
|
||||
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::entities::*;
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub fn extract_lwe_sample_from_cm_glwe_ciphertext<Scalar, InputCont, OutputCont>(
|
||||
input_glwe: &CmGlweCiphertext<InputCont>,
|
||||
output_lwe: &mut CmLweCiphertext<OutputCont>,
|
||||
nth: MonomialDegree,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let in_lwe_dim = input_glwe
|
||||
.glwe_dimension()
|
||||
.to_equivalent_lwe_dimension(input_glwe.polynomial_size());
|
||||
|
||||
let out_lwe_dim = output_lwe.lwe_dimension();
|
||||
|
||||
assert_eq!(
|
||||
in_lwe_dim, out_lwe_dim,
|
||||
"Mismatch between equivalent LweDimension of input ciphertext and output ciphertext. \
|
||||
Got {in_lwe_dim:?} for input and {out_lwe_dim:?} for output.",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_glwe.ciphertext_modulus(),
|
||||
output_lwe.ciphertext_modulus(),
|
||||
"Mismatched moduli between input_glwe ({:?}) and output_lwe ({:?})",
|
||||
input_glwe.ciphertext_modulus(),
|
||||
output_lwe.ciphertext_modulus()
|
||||
);
|
||||
|
||||
// We retrieve the bodies and masks of the two ciphertexts.
|
||||
let (mut lwe_mask, mut lwe_bodies) = output_lwe.get_mut_mask_and_bodies();
|
||||
let (glwe_mask, glwe_bodies) = input_glwe.get_mask_and_bodies();
|
||||
|
||||
// We copy the body
|
||||
|
||||
for (lwe_body, glwe_body) in lwe_bodies.iter_mut().zip_eq(glwe_bodies.iter()) {
|
||||
*lwe_body.data = glwe_body.as_ref()[nth.0];
|
||||
}
|
||||
|
||||
// We copy the mask (each polynomial is in the wrong order)
|
||||
lwe_mask.as_mut().copy_from_slice(glwe_mask.as_ref());
|
||||
|
||||
// We compute the number of elements which must be
|
||||
// turned into their opposite
|
||||
let opposite_count = input_glwe.polynomial_size().0 - nth.0 - 1;
|
||||
let ciphertext_modulus = input_glwe.ciphertext_modulus();
|
||||
|
||||
if ciphertext_modulus.is_compatible_with_native_modulus() {
|
||||
// We loop through the polynomials
|
||||
for lwe_mask_poly in lwe_mask
|
||||
.as_mut()
|
||||
.chunks_exact_mut(input_glwe.polynomial_size().0)
|
||||
{
|
||||
// We reverse the polynomial
|
||||
lwe_mask_poly.reverse();
|
||||
// We compute the opposite of the proper coefficients
|
||||
slice_wrapping_opposite_assign(&mut lwe_mask_poly[0..opposite_count]);
|
||||
// We rotate the polynomial properly
|
||||
lwe_mask_poly.rotate_left(opposite_count);
|
||||
}
|
||||
} else {
|
||||
let modulus: Scalar = ciphertext_modulus.get_custom_modulus().cast_into();
|
||||
// We loop through the polynomials
|
||||
for lwe_mask_poly in lwe_mask
|
||||
.as_mut()
|
||||
.chunks_exact_mut(input_glwe.polynomial_size().0)
|
||||
{
|
||||
// We reverse the polynomial
|
||||
lwe_mask_poly.reverse();
|
||||
// We compute the opposite of the proper coefficients
|
||||
slice_wrapping_opposite_assign_custom_mod(
|
||||
&mut lwe_mask_poly[0..opposite_count],
|
||||
modulus,
|
||||
);
|
||||
// We rotate the polynomial properly
|
||||
lwe_mask_poly.rotate_left(opposite_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn par_extract_lwe_sample_from_cm_glwe_ciphertext<Scalar, InputCont, OutputCont>(
|
||||
input_glwe: &CmGlweCiphertext<InputCont>,
|
||||
output_lwe_list: &mut CmLweCiphertextList<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger + Send + Sync,
|
||||
InputCont: Container<Element = Scalar> + Sync,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let thread_count = ThreadCount(rayon::current_num_threads());
|
||||
par_extract_lwe_sample_from_cm_glwe_ciphertext_with_thread_count(
|
||||
input_glwe,
|
||||
output_lwe_list,
|
||||
thread_count,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn par_extract_lwe_sample_from_cm_glwe_ciphertext_with_thread_count<
|
||||
Scalar,
|
||||
InputCont,
|
||||
OutputCont,
|
||||
>(
|
||||
input_glwe: &CmGlweCiphertext<InputCont>,
|
||||
output_lwe_list: &mut CmLweCiphertextList<OutputCont>,
|
||||
thread_count: ThreadCount,
|
||||
) where
|
||||
Scalar: UnsignedInteger + Send + Sync,
|
||||
InputCont: Container<Element = Scalar> + Sync,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let in_lwe_dim = input_glwe
|
||||
.glwe_dimension()
|
||||
.to_equivalent_lwe_dimension(input_glwe.polynomial_size());
|
||||
|
||||
let out_lwe_dim = output_lwe_list.lwe_dimension();
|
||||
|
||||
assert_eq!(
|
||||
in_lwe_dim, out_lwe_dim,
|
||||
"Mismatch between equivalent LweDimension of input ciphertext and output ciphertext. \
|
||||
Got {in_lwe_dim:?} for input and {out_lwe_dim:?} for output.",
|
||||
);
|
||||
|
||||
assert!(
|
||||
input_glwe.polynomial_size().0 <= output_lwe_list.cm_lwe_ciphertext_count().0,
|
||||
"The output LweCiphertextList does not have enough space ({:?}) \
|
||||
to extract all input CmGlweCiphertext coefficients ({})",
|
||||
output_lwe_list.cm_lwe_ciphertext_count(),
|
||||
input_glwe.polynomial_size().0
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_glwe.ciphertext_modulus(),
|
||||
output_lwe_list.ciphertext_modulus(),
|
||||
"Mismatched moduli between input_glwe ({:?}) and output_lwe ({:?})",
|
||||
input_glwe.ciphertext_modulus(),
|
||||
output_lwe_list.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let polynomial_size = input_glwe.polynomial_size();
|
||||
let (glwe_mask, glwe_body) = input_glwe.get_mask_and_bodies();
|
||||
|
||||
let thread_count = thread_count.0.min(rayon::current_num_threads());
|
||||
let chunk_size = polynomial_size.0.div_ceil(thread_count);
|
||||
|
||||
glwe_body
|
||||
.as_ref()
|
||||
.par_chunks(chunk_size)
|
||||
.zip(output_lwe_list.par_chunks_mut(chunk_size))
|
||||
.enumerate()
|
||||
.for_each(
|
||||
|(chunk_idx, (glwe_body_chunk, mut output_lwe_list_chunk))| {
|
||||
for (coeff_idx, (glwe_coeff, mut output_lwe)) in glwe_body_chunk
|
||||
.iter()
|
||||
.zip(output_lwe_list_chunk.iter_mut())
|
||||
.enumerate()
|
||||
{
|
||||
let nth = chunk_idx * chunk_size + coeff_idx;
|
||||
|
||||
let (mut lwe_mask, mut lwe_body) = output_lwe.get_mut_mask_and_bodies();
|
||||
|
||||
// We copy the body
|
||||
lwe_body.as_mut().fill(*glwe_coeff);
|
||||
|
||||
// We copy the mask (each polynomial is in the wrong order)
|
||||
lwe_mask.as_mut().copy_from_slice(glwe_mask.as_ref());
|
||||
|
||||
// We compute the number of elements which must be
|
||||
// turned into their opposite
|
||||
let opposite_count = input_glwe.polynomial_size().0 - nth - 1;
|
||||
let ciphertext_modulus = input_glwe.ciphertext_modulus();
|
||||
|
||||
if ciphertext_modulus.is_compatible_with_native_modulus() {
|
||||
// We loop through the polynomials
|
||||
for lwe_mask_poly in lwe_mask
|
||||
.as_mut()
|
||||
.chunks_exact_mut(input_glwe.polynomial_size().0)
|
||||
{
|
||||
// We reverse the polynomial
|
||||
lwe_mask_poly.reverse();
|
||||
// We compute the opposite of the proper coefficients
|
||||
slice_wrapping_opposite_assign(&mut lwe_mask_poly[0..opposite_count]);
|
||||
// We rotate the polynomial properly
|
||||
lwe_mask_poly.rotate_left(opposite_count);
|
||||
}
|
||||
} else {
|
||||
let modulus: Scalar = ciphertext_modulus.get_custom_modulus().cast_into();
|
||||
// We loop through the polynomials
|
||||
for lwe_mask_poly in lwe_mask
|
||||
.as_mut()
|
||||
.chunks_exact_mut(input_glwe.polynomial_size().0)
|
||||
{
|
||||
// We reverse the polynomial
|
||||
lwe_mask_poly.reverse();
|
||||
// We compute the opposite of the proper coefficients
|
||||
slice_wrapping_opposite_assign_custom_mod(
|
||||
&mut lwe_mask_poly[0..opposite_count],
|
||||
modulus,
|
||||
);
|
||||
// We rotate the polynomial properly
|
||||
lwe_mask_poly.rotate_left(opposite_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
//! Module containing primitives pertaining to the conversion of
|
||||
//! [`CommonMask LWE bootstrap keys`](`CmLweBootstrapKey`) to various representations/numerical
|
||||
//! domains like the Fourier domain.
|
||||
|
||||
use crate::core_crypto::commons::computation_buffers::ComputationBuffers;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::entities::*;
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::{Fft, FftView};
|
||||
use dyn_stack::{PodStack, StackReq};
|
||||
use tfhe_fft::c64;
|
||||
|
||||
pub fn convert_standard_cm_lwe_bootstrap_key_to_fourier<Scalar, InputCont, OutputCont>(
|
||||
input_bsk: &CmLweBootstrapKey<InputCont>,
|
||||
output_bsk: &mut FourierCmLweBootstrapKey<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = c64>,
|
||||
{
|
||||
let mut buffers = ComputationBuffers::new();
|
||||
|
||||
let fft = Fft::new(input_bsk.polynomial_size());
|
||||
let fft = fft.as_view();
|
||||
|
||||
buffers.resize(
|
||||
convert_standard_cm_lwe_bootstrap_key_to_fourier_mem_optimized_requirement(fft)
|
||||
.unaligned_bytes_required(),
|
||||
);
|
||||
|
||||
let stack = buffers.stack();
|
||||
|
||||
convert_standard_cm_lwe_bootstrap_key_to_fourier_mem_optimized(
|
||||
input_bsk, output_bsk, fft, stack,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn convert_standard_cm_lwe_bootstrap_key_to_fourier_mem_optimized<
|
||||
Scalar,
|
||||
InputCont,
|
||||
OutputCont,
|
||||
>(
|
||||
input_bsk: &CmLweBootstrapKey<InputCont>,
|
||||
output_bsk: &mut FourierCmLweBootstrapKey<OutputCont>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = c64>,
|
||||
{
|
||||
assert_eq!(
|
||||
input_bsk.polynomial_size(),
|
||||
output_bsk.polynomial_size(),
|
||||
"Mismatched PolynomialSize between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.polynomial_size(),
|
||||
output_bsk.polynomial_size(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.glwe_dimension(),
|
||||
output_bsk.glwe_dimension(),
|
||||
"Mismatched GlweSize"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.decomposition_base_log(),
|
||||
output_bsk.decomposition_base_log(),
|
||||
"Mismatched DecompositionBaseLog between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.decomposition_base_log(),
|
||||
output_bsk.decomposition_base_log(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.decomposition_level_count(),
|
||||
output_bsk.decomposition_level_count(),
|
||||
"Mismatched DecompositionLevelCount between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.decomposition_level_count(),
|
||||
output_bsk.decomposition_level_count(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.input_lwe_dimension(),
|
||||
output_bsk.input_lwe_dimension(),
|
||||
"Mismatched input LweDimension between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.input_lwe_dimension(),
|
||||
output_bsk.input_lwe_dimension(),
|
||||
);
|
||||
|
||||
output_bsk
|
||||
.as_mut_view()
|
||||
.fill_with_forward_fourier(input_bsk.as_view(), fft, stack);
|
||||
}
|
||||
|
||||
pub fn par_convert_standard_cm_lwe_bootstrap_key_to_fourier<Scalar, InputCont, OutputCont>(
|
||||
input_bsk: &CmLweBootstrapKey<InputCont>,
|
||||
output_bsk: &mut FourierCmLweBootstrapKey<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = c64>,
|
||||
{
|
||||
assert_eq!(
|
||||
input_bsk.polynomial_size(),
|
||||
output_bsk.polynomial_size(),
|
||||
"Mismatched PolynomialSize between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.polynomial_size(),
|
||||
output_bsk.polynomial_size(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.glwe_dimension(),
|
||||
output_bsk.glwe_dimension(),
|
||||
"Mismatched GlweSize"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.decomposition_base_log(),
|
||||
output_bsk.decomposition_base_log(),
|
||||
"Mismatched DecompositionBaseLog between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.decomposition_base_log(),
|
||||
output_bsk.decomposition_base_log(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.decomposition_level_count(),
|
||||
output_bsk.decomposition_level_count(),
|
||||
"Mismatched DecompositionLevelCount between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.decomposition_level_count(),
|
||||
output_bsk.decomposition_level_count(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_bsk.input_lwe_dimension(),
|
||||
output_bsk.input_lwe_dimension(),
|
||||
"Mismatched input LweDimension between input_bsk {:?} and output_bsk {:?}",
|
||||
input_bsk.input_lwe_dimension(),
|
||||
output_bsk.input_lwe_dimension(),
|
||||
);
|
||||
|
||||
let fft = Fft::new(input_bsk.polynomial_size());
|
||||
let fft = fft.as_view();
|
||||
|
||||
output_bsk
|
||||
.as_mut_view()
|
||||
.par_fill_with_forward_fourier(input_bsk.as_view(), fft);
|
||||
}
|
||||
|
||||
pub fn convert_standard_cm_lwe_bootstrap_key_to_fourier_mem_optimized_requirement(
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
fft.forward_scratch()
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
//! Module containing primitives pertaining to the generation of
|
||||
//! [`standard CommonMask LWE bootstrap keys`](`CmLweBootstrapKey`).
|
||||
|
||||
use crate::core_crypto::algorithms::*;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::random::{Distribution, Uniform};
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::prelude::*;
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub fn par_generate_cm_lwe_bootstrap_key<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
OutputCont,
|
||||
Gen,
|
||||
>(
|
||||
input_lwe_secret_keys: &[LweSecretKey<InputKeyCont>],
|
||||
output_glwe_secret_keys: &[GlweSecretKey<OutputKeyCont>],
|
||||
output: &mut CmLweBootstrapKey<OutputCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution> + Sync + Send,
|
||||
NoiseDistribution: Distribution + Sync,
|
||||
InputKeyCont: Container<Element = Scalar> + std::fmt::Debug,
|
||||
OutputKeyCont: Container<Element = Scalar> + Sync,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ParallelByteRandomGenerator,
|
||||
{
|
||||
for input_lwe_secret_key in input_lwe_secret_keys {
|
||||
assert!(
|
||||
output.input_lwe_dimension() == input_lwe_secret_key.lwe_dimension(),
|
||||
"Mismatched LweDimension between input LWE secret key and LWE bootstrap key. \
|
||||
Input LWE secret key LweDimension: {:?}, LWE bootstrap key input LweDimension {:?}.",
|
||||
input_lwe_secret_key.lwe_dimension(),
|
||||
output.input_lwe_dimension()
|
||||
);
|
||||
}
|
||||
let key_len = input_lwe_secret_keys[0].as_view().into_container().len();
|
||||
|
||||
for output_glwe_secret_key in output_glwe_secret_keys {
|
||||
assert!(
|
||||
output.glwe_dimension() == output_glwe_secret_key.glwe_dimension(),
|
||||
"Mismatched GlweSize between output GLWE secret key and LWE bootstrap key. \
|
||||
Output GLWE secret key GlweSize: {:?}, LWE bootstrap key GlweSize {:?}.",
|
||||
output_glwe_secret_key.glwe_dimension(),
|
||||
output.glwe_dimension()
|
||||
);
|
||||
|
||||
assert!(
|
||||
output.polynomial_size() == output_glwe_secret_key.polynomial_size(),
|
||||
"Mismatched PolynomialSize between output GLWE secret key and LWE bootstrap key. \
|
||||
Output GLWE secret key PolynomialSize: {:?}, LWE bootstrap key PolynomialSize {:?}.",
|
||||
output_glwe_secret_key.polynomial_size(),
|
||||
output.polynomial_size()
|
||||
);
|
||||
}
|
||||
|
||||
let gen_iter = generator
|
||||
.par_try_fork_from_config(output.encryption_fork_config(Uniform, noise_distribution))
|
||||
.unwrap();
|
||||
|
||||
let transposed_keys = (0..key_len)
|
||||
.map(|i| {
|
||||
input_lwe_secret_keys
|
||||
.iter()
|
||||
.map(|key| Cleartext(key.as_view().into_container()[i]))
|
||||
.collect_vec()
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
output
|
||||
.par_iter_mut()
|
||||
.zip(transposed_keys.par_iter())
|
||||
.zip(gen_iter)
|
||||
.for_each(|((mut ggsw, input_key_element), mut generator)| {
|
||||
par_encrypt_constant_cm_ggsw_ciphertext(
|
||||
output_glwe_secret_keys,
|
||||
&mut ggsw,
|
||||
input_key_element,
|
||||
noise_distribution,
|
||||
&mut generator,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CmBootstrapKeys<Scalar: UnsignedInteger> {
|
||||
pub small_lwe_sk: Vec<LweSecretKey<Vec<Scalar>>>,
|
||||
pub big_lwe_sk: Vec<LweSecretKey<Vec<Scalar>>>,
|
||||
pub bsk: CmLweBootstrapKeyOwned<Scalar>,
|
||||
pub fbsk: FourierCmLweBootstrapKeyOwned,
|
||||
}
|
||||
|
||||
pub fn generate_cm_pbs_keys(
|
||||
params: &CmApParams,
|
||||
encryption_random_generator: &mut EncryptionRandomGenerator<DefaultRandomGenerator>,
|
||||
secret_random_generator: &mut SecretRandomGenerator<DefaultRandomGenerator>,
|
||||
) -> CmBootstrapKeys<u64> {
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let cm_dimension = params.cm_dimension;
|
||||
|
||||
let glwe_noise_distribution = params.glwe_noise_distribution;
|
||||
|
||||
// Create the LweSecretKey
|
||||
let input_lwe_secret_keys = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_lwe_secret_key(
|
||||
params.lwe_dimension,
|
||||
secret_random_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
let output_glwe_secret_keys = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_glwe_secret_key(
|
||||
params.glwe_dimension,
|
||||
params.polynomial_size,
|
||||
secret_random_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let output_lwe_secret_keys = output_glwe_secret_keys
|
||||
.iter()
|
||||
.map(|a| a.clone().into_lwe_secret_key())
|
||||
.collect_vec();
|
||||
|
||||
let mut bsk = CmLweBootstrapKey::new(
|
||||
0,
|
||||
params.glwe_dimension,
|
||||
cm_dimension,
|
||||
params.polynomial_size,
|
||||
params.base_log_bs,
|
||||
params.level_bs,
|
||||
params.lwe_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
par_generate_cm_lwe_bootstrap_key(
|
||||
&input_lwe_secret_keys,
|
||||
&output_glwe_secret_keys,
|
||||
&mut bsk,
|
||||
glwe_noise_distribution,
|
||||
encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut fbsk = FourierCmLweBootstrapKey::new(
|
||||
params.lwe_dimension,
|
||||
params.glwe_dimension,
|
||||
cm_dimension,
|
||||
params.polynomial_size,
|
||||
params.base_log_bs,
|
||||
params.level_bs,
|
||||
);
|
||||
|
||||
par_convert_standard_cm_lwe_bootstrap_key_to_fourier(&bsk, &mut fbsk);
|
||||
|
||||
CmBootstrapKeys {
|
||||
small_lwe_sk: input_lwe_secret_keys,
|
||||
big_lwe_sk: output_lwe_secret_keys,
|
||||
bsk,
|
||||
fbsk,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
//! Module containing primitives pertaining to `CommonMask LWE ciphertext encryption and
|
||||
//! decryption`.
|
||||
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::random::{Distribution, RandomGenerable};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use itertools::Itertools;
|
||||
|
||||
pub fn fill_cm_lwe_mask_and_bodies_for_encryption<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
KeyCont,
|
||||
EncodedCont,
|
||||
OutputMaskCont,
|
||||
OutputBodyCont,
|
||||
Gen,
|
||||
>(
|
||||
lwe_secret_keys: &[LweSecretKey<KeyCont>],
|
||||
output_mask: &mut LweMask<OutputMaskCont>,
|
||||
output_bodies: &mut LweBodyList<OutputBodyCont>,
|
||||
encoded: &PlaintextList<EncodedCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus + RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
EncodedCont: Container<Element = Scalar>,
|
||||
OutputMaskCont: ContainerMut<Element = Scalar>,
|
||||
OutputBodyCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert_eq!(
|
||||
output_mask.ciphertext_modulus(),
|
||||
output_bodies.ciphertext_modulus(),
|
||||
"Mismatched moduli between mask ({:?}) and body ({:?})",
|
||||
output_mask.ciphertext_modulus(),
|
||||
output_bodies.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let ciphertext_modulus = output_mask.ciphertext_modulus();
|
||||
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
// generate a randomly uniform mask
|
||||
generator
|
||||
.fill_slice_with_random_uniform_mask_custom_mod(output_mask.as_mut(), ciphertext_modulus);
|
||||
|
||||
for ((sk, body), encoded) in lwe_secret_keys
|
||||
.iter()
|
||||
.zip_eq(output_bodies.iter_mut())
|
||||
.zip_eq(encoded.iter())
|
||||
{
|
||||
// generate an error from the given noise_distribution
|
||||
let noise = generator
|
||||
.random_noise_from_distribution_custom_mod(noise_distribution, ciphertext_modulus);
|
||||
// compute the multisum between the secret key and the mask
|
||||
let mask_key_dot_product = slice_wrapping_dot_product(output_mask.as_ref(), sk.as_ref());
|
||||
|
||||
// Store sum(ai * si) + delta * m + e in the body
|
||||
*body.data = mask_key_dot_product
|
||||
.wrapping_add(*encoded.0)
|
||||
.wrapping_add(noise);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encrypt_cm_lwe_ciphertext<Scalar, NoiseDistribution, KeyCont, EncodedCont, OutputCont, Gen>(
|
||||
lwe_secret_keys: &[LweSecretKey<KeyCont>],
|
||||
output: &mut CmLweCiphertext<OutputCont>,
|
||||
encoded: &PlaintextList<EncodedCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus + RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
EncodedCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let (mut mask, mut body) = output.get_mut_mask_and_bodies();
|
||||
|
||||
fill_cm_lwe_mask_and_bodies_for_encryption(
|
||||
lwe_secret_keys,
|
||||
&mut mask,
|
||||
&mut body,
|
||||
encoded,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn decrypt_cm_lwe_ciphertext<Scalar, KeyCont, InputCont>(
|
||||
lwe_secret_keys: &[LweSecretKey<KeyCont>],
|
||||
cm_lwe_ciphertext: &CmLweCiphertext<InputCont>,
|
||||
) -> Vec<Plaintext<Scalar>>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
{
|
||||
for lwe_secret_key in lwe_secret_keys {
|
||||
assert!(
|
||||
cm_lwe_ciphertext.lwe_dimension() == lwe_secret_key.lwe_dimension(),
|
||||
"Mismatch between LweDimension of output ciphertext and input secret key. \
|
||||
Got {:?} in output, and {:?} in secret key.",
|
||||
cm_lwe_ciphertext.lwe_dimension(),
|
||||
lwe_secret_key.lwe_dimension()
|
||||
);
|
||||
}
|
||||
|
||||
let ciphertext_modulus = cm_lwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert!(ciphertext_modulus.is_native_modulus());
|
||||
|
||||
let (mask, bodies) = cm_lwe_ciphertext.get_mask_and_bodies();
|
||||
|
||||
bodies
|
||||
.iter()
|
||||
.zip_eq(lwe_secret_keys.iter())
|
||||
.map(|(body, lwe_secret_key)| {
|
||||
let mask_key_dot_product =
|
||||
slice_wrapping_dot_product(mask.as_ref(), lwe_secret_key.as_ref());
|
||||
|
||||
Plaintext(body.data.wrapping_sub(mask_key_dot_product))
|
||||
})
|
||||
.collect_vec()
|
||||
}
|
||||
|
||||
pub fn allocate_and_encrypt_new_cm_lwe_ciphertext<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
KeyCont,
|
||||
EncodedCont,
|
||||
Gen,
|
||||
>(
|
||||
lwe_secret_keys: &[LweSecretKey<KeyCont>],
|
||||
encoded: &PlaintextList<EncodedCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) -> CmLweCiphertextOwned<Scalar>
|
||||
where
|
||||
Scalar: UnsignedTorus + RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
EncodedCont: Container<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut new_ct = CmLweCiphertextOwned::new(
|
||||
Scalar::ZERO,
|
||||
lwe_secret_keys[0].lwe_dimension(),
|
||||
CmDimension(encoded.as_ref().len()),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
encrypt_cm_lwe_ciphertext(
|
||||
lwe_secret_keys,
|
||||
&mut new_ct,
|
||||
encoded,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
|
||||
new_ct
|
||||
}
|
||||
|
||||
pub fn encrypt_cm_lwe_ciphertext_list<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
KeyCont,
|
||||
OutputCont,
|
||||
InputCont,
|
||||
Gen,
|
||||
>(
|
||||
lwe_secret_keys: &[LweSecretKey<KeyCont>],
|
||||
output: &mut CmLweCiphertextList<OutputCont>,
|
||||
encoded: &[PlaintextList<InputCont>],
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus + RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
NoiseDistribution: Distribution,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
for (mut output, encoded) in output.iter_mut().zip_eq(encoded.iter()) {
|
||||
encrypt_cm_lwe_ciphertext(
|
||||
lwe_secret_keys,
|
||||
&mut output,
|
||||
encoded,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
//! Module containing primitives pertaining to [`CommonMask LWE ciphertext
|
||||
//! keyswitch`](`CmLweKeyswitchKey`).
|
||||
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
use crate::core_crypto::commons::parameters::ThreadCount;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use itertools::Itertools;
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub fn cm_keyswitch_lwe_ciphertext<Scalar, KSKCont, InputCont, OutputCont>(
|
||||
cm_lwe_keyswitch_key: &CmLweKeyswitchKey<KSKCont>,
|
||||
input_lwe_ciphertext: &CmLweCiphertext<InputCont>,
|
||||
output_lwe_ciphertext: &mut CmLweCiphertext<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
KSKCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.input_lwe_dimension() == input_lwe_ciphertext.lwe_dimension(),
|
||||
"Mismatched input LweDimension. \
|
||||
CmLweKeyswitchKey input LweDimension: {:?}, input CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.input_lwe_dimension(),
|
||||
input_lwe_ciphertext.lwe_dimension(),
|
||||
);
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension() == output_lwe_ciphertext.lwe_dimension(),
|
||||
"Mismatched output LweDimension. \
|
||||
CmLweKeyswitchKey output LweDimension: {:?}, output CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension(),
|
||||
output_lwe_ciphertext.lwe_dimension(),
|
||||
);
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.cm_dimension() == input_lwe_ciphertext.cm_dimension(),
|
||||
"Mismatched CmDimension. \
|
||||
CmLweKeyswitchKey CmDimension: {:?}, input CmLweCiphertext CmDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.cm_dimension(),
|
||||
input_lwe_ciphertext.cm_dimension(),
|
||||
);
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.cm_dimension() == output_lwe_ciphertext.cm_dimension(),
|
||||
"Mismatched CmDimension. \
|
||||
CmLweKeyswitchKey CmDimension: {:?}, output CmLweCiphertext CmDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.cm_dimension(),
|
||||
output_lwe_ciphertext.cm_dimension(),
|
||||
);
|
||||
|
||||
let output_ciphertext_modulus = output_lwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert_eq!(
|
||||
cm_lwe_keyswitch_key.ciphertext_modulus(),
|
||||
output_ciphertext_modulus,
|
||||
"Mismatched CiphertextModulus. \
|
||||
CmLweKeyswitchKey CiphertextModulus: {:?}, output CmLweCiphertext CiphertextModulus {:?}.",
|
||||
cm_lwe_keyswitch_key.ciphertext_modulus(),
|
||||
output_ciphertext_modulus
|
||||
);
|
||||
assert!(
|
||||
output_ciphertext_modulus.is_compatible_with_native_modulus(),
|
||||
"This operation currently only supports power of 2 moduli"
|
||||
);
|
||||
|
||||
let input_ciphertext_modulus = input_lwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert!(
|
||||
input_ciphertext_modulus.is_compatible_with_native_modulus(),
|
||||
"This operation currently only supports power of 2 moduli"
|
||||
);
|
||||
|
||||
// Clear the output ciphertext, as it will get updated gradually
|
||||
output_lwe_ciphertext.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
// Copy the input body to the output ciphertext
|
||||
output_lwe_ciphertext
|
||||
.get_mut_bodies()
|
||||
.as_mut()
|
||||
.copy_from_slice(input_lwe_ciphertext.get_bodies().as_ref());
|
||||
|
||||
// We instantiate a decomposer
|
||||
let decomposer = SignedDecomposer::new(
|
||||
cm_lwe_keyswitch_key.decomposition_base_log(),
|
||||
cm_lwe_keyswitch_key.decomposition_level_count(),
|
||||
);
|
||||
|
||||
for (keyswitch_key_block, &input_mask_element) in cm_lwe_keyswitch_key
|
||||
.iter()
|
||||
.zip(input_lwe_ciphertext.get_mask().as_ref())
|
||||
{
|
||||
let decomposition_iter = decomposer.decompose(input_mask_element);
|
||||
// Loop over the levels
|
||||
for (level_key_ciphertext, decomposed) in keyswitch_key_block.iter().zip(decomposition_iter)
|
||||
{
|
||||
slice_wrapping_sub_scalar_mul_assign(
|
||||
output_lwe_ciphertext.as_mut(),
|
||||
level_key_ciphertext.as_ref(),
|
||||
decomposed.value(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn par_cm_keyswitch_lwe_ciphertext<Scalar, KSKCont, InputCont, OutputCont>(
|
||||
cm_lwe_keyswitch_key: &CmLweKeyswitchKey<KSKCont>,
|
||||
input_lwe_ciphertext: &CmLweCiphertext<InputCont>,
|
||||
output_lwe_ciphertext: &mut CmLweCiphertext<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger + Send + Sync,
|
||||
KSKCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let thread_count = ThreadCount(rayon::current_num_threads());
|
||||
par_cm_keyswitch_lwe_ciphertext_with_thread_count(
|
||||
cm_lwe_keyswitch_key,
|
||||
input_lwe_ciphertext,
|
||||
output_lwe_ciphertext,
|
||||
thread_count,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn par_cm_keyswitch_lwe_ciphertext_with_thread_count<Scalar, KSKCont, InputCont, OutputCont>(
|
||||
cm_lwe_keyswitch_key: &CmLweKeyswitchKey<KSKCont>,
|
||||
input_lwe_ciphertext: &CmLweCiphertext<InputCont>,
|
||||
output_lwe_ciphertext: &mut CmLweCiphertext<OutputCont>,
|
||||
thread_count: ThreadCount,
|
||||
) where
|
||||
Scalar: UnsignedInteger + Send + Sync,
|
||||
KSKCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.input_lwe_dimension() == input_lwe_ciphertext.lwe_dimension(),
|
||||
"Mismatched input LweDimension. \
|
||||
CmLweKeyswitchKey input LweDimension: {:?}, input CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.input_lwe_dimension(),
|
||||
input_lwe_ciphertext.lwe_dimension(),
|
||||
);
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension() == output_lwe_ciphertext.lwe_dimension(),
|
||||
"Mismatched output LweDimension. \
|
||||
CmLweKeyswitchKey output LweDimension: {:?}, output CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension(),
|
||||
output_lwe_ciphertext.lwe_dimension(),
|
||||
);
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.cm_dimension() == input_lwe_ciphertext.cm_dimension(),
|
||||
"Mismatched CmDimension. \
|
||||
CmLweKeyswitchKey CmDimension: {:?}, input CmLweCiphertext CmDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.cm_dimension(),
|
||||
input_lwe_ciphertext.cm_dimension(),
|
||||
);
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.cm_dimension() == output_lwe_ciphertext.cm_dimension(),
|
||||
"Mismatched CmDimension. \
|
||||
CmLweKeyswitchKey CmDimension: {:?}, output CmLweCiphertext CmDimension {:?}.",
|
||||
cm_lwe_keyswitch_key.cm_dimension(),
|
||||
output_lwe_ciphertext.cm_dimension(),
|
||||
);
|
||||
|
||||
let output_ciphertext_modulus = output_lwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert_eq!(
|
||||
cm_lwe_keyswitch_key.ciphertext_modulus(),
|
||||
output_ciphertext_modulus,
|
||||
"Mismatched CiphertextModulus. \
|
||||
CmLweKeyswitchKey CiphertextModulus: {:?}, output CmLweCiphertext CiphertextModulus {:?}.",
|
||||
cm_lwe_keyswitch_key.ciphertext_modulus(),
|
||||
output_ciphertext_modulus
|
||||
);
|
||||
assert!(
|
||||
output_ciphertext_modulus.is_compatible_with_native_modulus(),
|
||||
"This operation currently only supports power of 2 moduli"
|
||||
);
|
||||
|
||||
let input_ciphertext_modulus = input_lwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert!(
|
||||
input_ciphertext_modulus.is_compatible_with_native_modulus(),
|
||||
"This operation currently only supports power of 2 moduli"
|
||||
);
|
||||
|
||||
assert!(
|
||||
thread_count.0 != 0,
|
||||
"Got thread_count == 0, this is not supported"
|
||||
);
|
||||
|
||||
// Clear the output ciphertext, as it will get updated gradually
|
||||
output_lwe_ciphertext.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
let output_lwe_dimension = output_lwe_ciphertext.lwe_dimension();
|
||||
|
||||
let cm_dimension = output_lwe_ciphertext.cm_dimension();
|
||||
|
||||
// We instantiate a decomposer
|
||||
let decomposer = SignedDecomposer::new(
|
||||
cm_lwe_keyswitch_key.decomposition_base_log(),
|
||||
cm_lwe_keyswitch_key.decomposition_level_count(),
|
||||
);
|
||||
|
||||
// Don't go above the current number of threads
|
||||
let thread_count = thread_count.0.min(rayon::current_num_threads());
|
||||
let mut intermediate_accumulators = Vec::with_capacity(thread_count);
|
||||
|
||||
// Smallest chunk_size such that thread_count * chunk_size >= input_lwe_dimension
|
||||
let chunk_size = input_lwe_ciphertext
|
||||
.lwe_dimension()
|
||||
.0
|
||||
.div_ceil(thread_count);
|
||||
|
||||
cm_lwe_keyswitch_key
|
||||
.par_chunks(chunk_size)
|
||||
.zip(
|
||||
input_lwe_ciphertext
|
||||
.get_mask()
|
||||
.as_ref()
|
||||
.par_chunks(chunk_size),
|
||||
)
|
||||
.map(|(keyswitch_key_block_chunk, input_mask_element_chunk)| {
|
||||
let mut buffer = CmLweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
output_lwe_dimension,
|
||||
cm_dimension,
|
||||
output_ciphertext_modulus,
|
||||
);
|
||||
|
||||
for (keyswitch_key_block, &input_mask_element) in keyswitch_key_block_chunk
|
||||
.iter()
|
||||
.zip(input_mask_element_chunk.iter())
|
||||
{
|
||||
let decomposition_iter = decomposer.decompose(input_mask_element);
|
||||
// Loop over the levels
|
||||
for (level_key_ciphertext, decomposed) in
|
||||
keyswitch_key_block.iter().zip(decomposition_iter)
|
||||
{
|
||||
slice_wrapping_sub_scalar_mul_assign(
|
||||
buffer.as_mut(),
|
||||
level_key_ciphertext.as_ref(),
|
||||
decomposed.value(),
|
||||
);
|
||||
}
|
||||
}
|
||||
buffer
|
||||
})
|
||||
.collect_into_vec(&mut intermediate_accumulators);
|
||||
|
||||
let reduced = intermediate_accumulators
|
||||
.par_iter_mut()
|
||||
.reduce_with(|lhs, rhs| {
|
||||
lhs.as_mut()
|
||||
.iter_mut()
|
||||
.zip(rhs.as_ref().iter())
|
||||
.for_each(|(dst, &src)| *dst = (*dst).wrapping_add(src));
|
||||
|
||||
lhs
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let reduced = reduced.as_view();
|
||||
|
||||
output_lwe_ciphertext
|
||||
.get_mut_mask()
|
||||
.as_mut()
|
||||
.copy_from_slice(reduced.get_mask().as_ref());
|
||||
|
||||
let reduced_ksed_bodies = reduced.get_bodies();
|
||||
|
||||
// Add the reduced body of the keyswitch to the output body to complete the keyswitch
|
||||
for ((out, in1), in2) in output_lwe_ciphertext
|
||||
.get_mut_bodies()
|
||||
.as_mut()
|
||||
.iter_mut()
|
||||
.zip_eq(input_lwe_ciphertext.get_bodies().as_ref())
|
||||
.zip_eq(reduced_ksed_bodies.as_ref())
|
||||
{
|
||||
*out = in1.wrapping_add(*in2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
//! Module containing primitives pertaining to [`CommonMask LWE keyswitch keys
|
||||
//! generation`](`CmLweKeyswitchKey`)
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, DecompositionTerm};
|
||||
use crate::core_crypto::commons::math::random::{Distribution, Uniform};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::algorithms::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
pub fn generate_cm_lwe_keyswitch_key<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
KSKeyCont,
|
||||
Gen,
|
||||
>(
|
||||
input_cm_lwe_sks: &[LweSecretKey<InputKeyCont>],
|
||||
output_cm_lwe_sks: &[LweSecretKey<OutputKeyCont>],
|
||||
cm_lwe_keyswitch_key: &mut CmLweKeyswitchKey<KSKeyCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution> + UnsignedTorus,
|
||||
NoiseDistribution: Distribution,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar>,
|
||||
KSKeyCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
for input_cm_lwe_sk in input_cm_lwe_sks {
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.input_lwe_dimension() == input_cm_lwe_sk.lwe_dimension(),
|
||||
"The destination CmLweKeyswitchKey input LweDimension is not equal \
|
||||
to the input LweSecretKey LweDimension. Destination: {:?}, input: {:?}",
|
||||
cm_lwe_keyswitch_key.input_lwe_dimension(),
|
||||
input_cm_lwe_sk.lwe_dimension()
|
||||
);
|
||||
}
|
||||
for output_cm_lwe_sk in output_cm_lwe_sks {
|
||||
assert!(
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension() == output_cm_lwe_sk.lwe_dimension(),
|
||||
"The destination CmLweKeyswitchKey output LweDimension is not equal \
|
||||
to the output LweSecretKey LweDimension. Destination: {:?}, output: {:?}",
|
||||
cm_lwe_keyswitch_key.output_lwe_dimension(),
|
||||
output_cm_lwe_sk.lwe_dimension()
|
||||
);
|
||||
}
|
||||
|
||||
let decomp_base_log = cm_lwe_keyswitch_key.decomposition_base_log();
|
||||
let decomp_level_count = cm_lwe_keyswitch_key.decomposition_level_count();
|
||||
let cm_dimension = cm_lwe_keyswitch_key.cm_dimension();
|
||||
let ciphertext_modulus = cm_lwe_keyswitch_key.ciphertext_modulus();
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
// The plaintexts used to encrypt a key element will be stored in this buffer
|
||||
let mut decomposition_plaintexts_buffers = (0..decomp_level_count.0)
|
||||
.map(|_| PlaintextListOwned::new(Scalar::ZERO, PlaintextCount(cm_dimension.0)))
|
||||
.collect_vec();
|
||||
|
||||
// Iterate over the input key elements and the destination lwe_keyswitch_key memory
|
||||
for (input_key_bit_index, mut keyswitch_key_block) in
|
||||
cm_lwe_keyswitch_key.iter_mut().enumerate()
|
||||
{
|
||||
for (pt_index, input_cm_lwe_sk) in input_cm_lwe_sks.iter().enumerate() {
|
||||
// We fill the buffer with the powers of the key elements
|
||||
for (level, message) in (1..=decomp_level_count.0)
|
||||
.rev()
|
||||
.map(DecompositionLevel)
|
||||
.zip_eq(decomposition_plaintexts_buffers.iter_mut())
|
||||
{
|
||||
// Here we take the decomposition term from the native torus, bring it to the torus
|
||||
// we are working with by dividing by the scaling factor and the
|
||||
// encryption will take care of mapping that back to the native
|
||||
// torus
|
||||
message.as_mut()[pt_index] = DecompositionTerm::new(
|
||||
level,
|
||||
decomp_base_log,
|
||||
input_cm_lwe_sk.as_ref()[input_key_bit_index],
|
||||
)
|
||||
.to_recomposition_summand()
|
||||
.wrapping_div(ciphertext_modulus.get_power_of_two_scaling_to_native_torus());
|
||||
}
|
||||
}
|
||||
|
||||
encrypt_cm_lwe_ciphertext_list(
|
||||
output_cm_lwe_sks,
|
||||
&mut keyswitch_key_block,
|
||||
&decomposition_plaintexts_buffers,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn allocate_and_generate_new_cm_lwe_keyswitch_key<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
Gen,
|
||||
>(
|
||||
input_cm_lwe_sks: &[LweSecretKey<InputKeyCont>],
|
||||
output_cm_lwe_sks: &[LweSecretKey<OutputKeyCont>],
|
||||
cm_dimension: CmDimension,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
noise_distribution: NoiseDistribution,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) -> CmLweKeyswitchKeyOwned<Scalar>
|
||||
where
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution> + UnsignedTorus,
|
||||
NoiseDistribution: Distribution,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut new_cm_lwe_keyswitch_key = CmLweKeyswitchKeyOwned::new(
|
||||
Scalar::ZERO,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_cm_lwe_sks[0].lwe_dimension(),
|
||||
output_cm_lwe_sks[0].lwe_dimension(),
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
generate_cm_lwe_keyswitch_key(
|
||||
input_cm_lwe_sks,
|
||||
output_cm_lwe_sks,
|
||||
&mut new_cm_lwe_keyswitch_key,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
|
||||
new_cm_lwe_keyswitch_key
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
//! Module containing primitives pertaining to [`CommonMask LWE ciphertext`](`CmLweCiphertext`)
|
||||
//! linear algebra, like addition, multiplication, etc.
|
||||
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::commons::ciphertext_modulus::CiphertextModulusKind;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::CmLweCiphertext;
|
||||
|
||||
pub fn cm_lwe_ciphertext_add_assign<Scalar, LhsCont, RhsCont>(
|
||||
lhs: &mut CmLweCiphertext<LhsCont>,
|
||||
rhs: &CmLweCiphertext<RhsCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
LhsCont: ContainerMut<Element = Scalar>,
|
||||
RhsCont: Container<Element = Scalar>,
|
||||
{
|
||||
let ciphertext_modulus = rhs.ciphertext_modulus();
|
||||
if ciphertext_modulus.is_compatible_with_native_modulus() {
|
||||
cm_lwe_ciphertext_add_assign_native_mod_compatible(lhs, rhs);
|
||||
} else {
|
||||
cm_lwe_ciphertext_add_assign_other_mod(lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_add_assign_native_mod_compatible<Scalar, LhsCont, RhsCont>(
|
||||
lhs: &mut CmLweCiphertext<LhsCont>,
|
||||
rhs: &CmLweCiphertext<RhsCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
LhsCont: ContainerMut<Element = Scalar>,
|
||||
RhsCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between lhs ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
let ciphertext_modulus = rhs.ciphertext_modulus();
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
slice_wrapping_add_assign(lhs.as_mut(), rhs.as_ref());
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_add_assign_other_mod<Scalar, LhsCont, RhsCont>(
|
||||
lhs: &mut CmLweCiphertext<LhsCont>,
|
||||
rhs: &CmLweCiphertext<RhsCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
LhsCont: ContainerMut<Element = Scalar>,
|
||||
RhsCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between lhs ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
let ciphertext_modulus = rhs.ciphertext_modulus();
|
||||
assert!(!ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
slice_wrapping_add_assign_custom_mod(
|
||||
lhs.as_mut(),
|
||||
rhs.as_ref(),
|
||||
ciphertext_modulus.get_custom_modulus().cast_into(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_add<Scalar, OutputCont, LhsCont, RhsCont>(
|
||||
output: &mut CmLweCiphertext<OutputCont>,
|
||||
lhs: &CmLweCiphertext<LhsCont>,
|
||||
rhs: &CmLweCiphertext<RhsCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
LhsCont: Container<Element = Scalar>,
|
||||
RhsCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between lhs ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
output.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between output ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
output.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
|
||||
slice_wrapping_add(output.as_mut(), lhs.as_ref(), rhs.as_ref());
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_plaintext_add_assign<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Plaintext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let ciphertext_modulus = lhs.ciphertext_modulus();
|
||||
if ciphertext_modulus.is_compatible_with_native_modulus() {
|
||||
cm_lwe_ciphertext_plaintext_add_assign_native_mod_compatible(lhs, rhs);
|
||||
} else {
|
||||
cm_lwe_ciphertext_plaintext_add_assign_other_mod(lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_plaintext_add_assign_native_mod_compatible<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Plaintext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let mut bodies = lhs.get_mut_bodies();
|
||||
let ciphertext_modulus = bodies.ciphertext_modulus();
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
let plaintext = match ciphertext_modulus.kind() {
|
||||
CiphertextModulusKind::Native => rhs.0,
|
||||
// Manage power of 2 encoding
|
||||
CiphertextModulusKind::NonNativePowerOfTwo => rhs
|
||||
.0
|
||||
.wrapping_mul(ciphertext_modulus.get_power_of_two_scaling_to_native_torus()),
|
||||
CiphertextModulusKind::Other => unreachable!(),
|
||||
};
|
||||
|
||||
for body in bodies.as_mut() {
|
||||
*body = body.wrapping_add(plaintext);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_plaintext_add_assign_other_mod<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Plaintext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let mut bodies = lhs.get_mut_bodies();
|
||||
let ciphertext_modulus = bodies.ciphertext_modulus();
|
||||
assert!(!ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
for body in bodies.as_mut() {
|
||||
*body = body
|
||||
.wrapping_add_custom_mod(rhs.0, ciphertext_modulus.get_custom_modulus().cast_into());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_plaintext_sub_assign<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Plaintext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let ciphertext_modulus = lhs.ciphertext_modulus();
|
||||
if ciphertext_modulus.is_compatible_with_native_modulus() {
|
||||
cm_lwe_ciphertext_plaintext_sub_assign_native_mod_compatible(lhs, rhs);
|
||||
} else {
|
||||
cm_lwe_ciphertext_plaintext_sub_assign_other_mod(lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_plaintext_sub_assign_native_mod_compatible<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Plaintext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let mut bodies = lhs.get_mut_bodies();
|
||||
let ciphertext_modulus = bodies.ciphertext_modulus();
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
let plaintext = match ciphertext_modulus.kind() {
|
||||
CiphertextModulusKind::Native => rhs.0,
|
||||
// Manage power of 2 encoding
|
||||
CiphertextModulusKind::NonNativePowerOfTwo => rhs
|
||||
.0
|
||||
.wrapping_mul(ciphertext_modulus.get_power_of_two_scaling_to_native_torus()),
|
||||
CiphertextModulusKind::Other => unreachable!(),
|
||||
};
|
||||
|
||||
for body in bodies.as_mut() {
|
||||
*body = body.wrapping_sub(plaintext);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_plaintext_sub_assign_other_mod<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Plaintext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let mut bodies = lhs.get_mut_bodies();
|
||||
let ciphertext_modulus = bodies.ciphertext_modulus();
|
||||
assert!(!ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
for body in bodies.as_mut() {
|
||||
*body = body
|
||||
.wrapping_sub_custom_mod(rhs.0, ciphertext_modulus.get_custom_modulus().cast_into());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_opposite_assign<Scalar, InCont>(ct: &mut CmLweCiphertext<InCont>)
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
slice_wrapping_opposite_assign(ct.as_mut());
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_cleartext_mul_assign<Scalar, InCont>(
|
||||
lhs: &mut CmLweCiphertext<InCont>,
|
||||
rhs: Cleartext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
slice_wrapping_scalar_mul_assign(lhs.as_mut(), rhs.0);
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_cleartext_mul<Scalar, InputCont, OutputCont>(
|
||||
output: &mut CmLweCiphertext<OutputCont>,
|
||||
lhs: &CmLweCiphertext<InputCont>,
|
||||
rhs: Cleartext<Scalar>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
output.ciphertext_modulus(),
|
||||
lhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between output ({:?}) and lhs ({:?}) CmLweCiphertext",
|
||||
output.ciphertext_modulus(),
|
||||
lhs.ciphertext_modulus()
|
||||
);
|
||||
output.as_mut().copy_from_slice(lhs.as_ref());
|
||||
cm_lwe_ciphertext_cleartext_mul_assign(output, rhs);
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_sub_assign<Scalar, LhsCont, RhsCont>(
|
||||
lhs: &mut CmLweCiphertext<LhsCont>,
|
||||
rhs: &CmLweCiphertext<RhsCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
LhsCont: ContainerMut<Element = Scalar>,
|
||||
RhsCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between lhs ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
|
||||
slice_wrapping_sub_assign(lhs.as_mut(), rhs.as_ref());
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_sub<Scalar, OutputCont, LhsCont, RhsCont>(
|
||||
output: &mut CmLweCiphertext<OutputCont>,
|
||||
lhs: &CmLweCiphertext<LhsCont>,
|
||||
rhs: &CmLweCiphertext<RhsCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
LhsCont: Container<Element = Scalar>,
|
||||
RhsCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between lhs ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
lhs.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
output.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus(),
|
||||
"Mismatched moduli between output ({:?}) and rhs ({:?}) CmLweCiphertext",
|
||||
output.ciphertext_modulus(),
|
||||
rhs.ciphertext_modulus()
|
||||
);
|
||||
|
||||
output.as_mut().copy_from_slice(lhs.as_ref());
|
||||
cm_lwe_ciphertext_sub_assign(output, rhs);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
//! Module containing primitives pertaining to `CommonMask LWE ciphertext
|
||||
//! packing`.
|
||||
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
use crate::core_crypto::commons::numeric::UnsignedInteger;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::{CmLweCiphertext, CmLwePackingKey};
|
||||
use itertools::Itertools;
|
||||
|
||||
pub fn pack_lwe_ciphertexts_into_cm<Scalar, KSKCont, InputCont, OutputCont>(
|
||||
cm_lwe_packing_key: &CmLwePackingKey<KSKCont>,
|
||||
// TODO: support both &[LweCiphertext] and &LweCiphertextList
|
||||
input_lwe_ciphertexts: &[LweCiphertext<InputCont>],
|
||||
output_cm_lwe_ciphertext: &mut CmLweCiphertext<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
KSKCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let input_ciphertext_modulus = input_lwe_ciphertexts[0].ciphertext_modulus();
|
||||
|
||||
for input_lwe_ciphertext in input_lwe_ciphertexts {
|
||||
assert_eq!(
|
||||
cm_lwe_packing_key.input_key_lwe_dimension(),
|
||||
input_lwe_ciphertext.lwe_size().to_lwe_dimension(),
|
||||
"Mismatched input LweDimension. \
|
||||
CmLwePackingKey input LweDimension: {:?}, input CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_packing_key.input_key_lwe_dimension(),
|
||||
input_lwe_ciphertext.lwe_size().to_lwe_dimension(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_lwe_ciphertext.ciphertext_modulus(),
|
||||
input_ciphertext_modulus,
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
cm_lwe_packing_key.output_lwe_dimension(),
|
||||
output_cm_lwe_ciphertext.lwe_dimension(),
|
||||
"Mismatched output LweDimension. \
|
||||
CmLwePackingKey output LweDimension: {:?}, output CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_packing_key.output_lwe_dimension(),
|
||||
output_cm_lwe_ciphertext.lwe_dimension(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cm_lwe_packing_key.output_cm_dimension(),
|
||||
output_cm_lwe_ciphertext.cm_dimension(),
|
||||
"Mismatched output LweDimension. \
|
||||
CmLwePackingKey output LweDimension: {:?}, output CmLweCiphertext LweDimension {:?}.",
|
||||
cm_lwe_packing_key.output_lwe_dimension(),
|
||||
output_cm_lwe_ciphertext.lwe_dimension(),
|
||||
);
|
||||
|
||||
let output_ciphertext_modulus = output_cm_lwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
assert_eq!(
|
||||
cm_lwe_packing_key.ciphertext_modulus(),
|
||||
output_ciphertext_modulus,
|
||||
"Mismatched CiphertextModulus. \
|
||||
CmLwePackingKey CiphertextModulus: {:?}, output CmLweCiphertext CiphertextModulus {:?}.",
|
||||
cm_lwe_packing_key.ciphertext_modulus(),
|
||||
output_ciphertext_modulus
|
||||
);
|
||||
assert!(
|
||||
output_ciphertext_modulus.is_compatible_with_native_modulus(),
|
||||
"This operation currently only supports power of 2 moduli"
|
||||
);
|
||||
|
||||
assert!(
|
||||
input_ciphertext_modulus.is_compatible_with_native_modulus(),
|
||||
"This operation currently only supports power of 2 moduli"
|
||||
);
|
||||
|
||||
// Clear the output ciphertext, as it will get updated gradually
|
||||
output_cm_lwe_ciphertext.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
for (i, (key_part, input_lwe_ciphertext)) in cm_lwe_packing_key
|
||||
.iter()
|
||||
.zip_eq(input_lwe_ciphertexts.iter())
|
||||
.enumerate()
|
||||
{
|
||||
let mut bodies = output_cm_lwe_ciphertext.get_mut_bodies();
|
||||
|
||||
let body = &mut bodies.as_mut()[i];
|
||||
|
||||
*body = body.wrapping_add(*input_lwe_ciphertext.get_body().data);
|
||||
|
||||
// We instantiate a decomposer
|
||||
let decomposer = SignedDecomposer::new(
|
||||
cm_lwe_packing_key.decomposition_base_log(),
|
||||
cm_lwe_packing_key.decomposition_level_count(),
|
||||
);
|
||||
|
||||
for (keyswitch_key_block, &input_mask_element) in key_part
|
||||
.iter()
|
||||
.zip_eq(input_lwe_ciphertext.get_mask().as_ref())
|
||||
{
|
||||
let decomposition_iter = decomposer.decompose(input_mask_element);
|
||||
// Loop over the levels
|
||||
|
||||
for (level_key_ciphertext, decomposed) in
|
||||
keyswitch_key_block.iter().zip_eq(decomposition_iter)
|
||||
{
|
||||
slice_wrapping_sub_scalar_mul_assign(
|
||||
output_cm_lwe_ciphertext.as_mut(),
|
||||
level_key_ciphertext.into_container(),
|
||||
decomposed.value(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
//! Module containing primitives pertaining to
|
||||
//! [`CommonMask LWE packing keys generation`](`CmLwePackingKey`)
|
||||
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, DecompositionTerm};
|
||||
use crate::core_crypto::commons::math::random::{Distribution, RandomGenerable};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::algorithms::*;
|
||||
use crate::core_crypto::experimental::prelude::{
|
||||
CmDimension, CmLwePackingKey, CmLwePackingKeyOwned,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
pub fn generate_cm_lwe_packing_key<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
KSKeyCont,
|
||||
Gen,
|
||||
>(
|
||||
input_lwe_sk: &LweSecretKey<InputKeyCont>,
|
||||
output_lwe_sks: &[LweSecretKey<OutputKeyCont>],
|
||||
lwe_keyswitch_key: &mut CmLwePackingKey<KSKeyCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus + RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
NoiseDistribution: Distribution,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar>,
|
||||
KSKeyCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert!(
|
||||
lwe_keyswitch_key.input_key_lwe_dimension() == input_lwe_sk.lwe_dimension(),
|
||||
"The destination CmLwePackingKey input LweDimension is not equal \
|
||||
to the input LweSecretKey LweDimension. Destination: {:?}, input: {:?}",
|
||||
lwe_keyswitch_key.input_key_lwe_dimension(),
|
||||
input_lwe_sk.lwe_dimension()
|
||||
);
|
||||
|
||||
for output_lwe_sk in output_lwe_sks {
|
||||
assert!(
|
||||
lwe_keyswitch_key.output_lwe_dimension() == output_lwe_sk.lwe_dimension(),
|
||||
"The destination CmLwePackingKey output LweDimension is not equal \
|
||||
to the output LweSecretKey LweDimension. Destination: {:?}, output: {:?}",
|
||||
lwe_keyswitch_key.output_lwe_dimension(),
|
||||
output_lwe_sk.lwe_dimension()
|
||||
);
|
||||
}
|
||||
|
||||
let cm_dimension = CmDimension(output_lwe_sks.len());
|
||||
|
||||
let decomp_base_log = lwe_keyswitch_key.decomposition_base_log();
|
||||
let decomp_level_count = lwe_keyswitch_key.decomposition_level_count();
|
||||
let ciphertext_modulus = lwe_keyswitch_key.ciphertext_modulus();
|
||||
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
|
||||
|
||||
// The plaintexts used to encrypt a key element will be stored in this buffer
|
||||
let mut decomposition_plaintexts_buffer =
|
||||
PlaintextListOwned::new(Scalar::ZERO, PlaintextCount(decomp_level_count.0));
|
||||
|
||||
// Iterate over the input key elements and the destination lwe_keyswitch_key memory
|
||||
for (i, mut keyswitch_key_block) in lwe_keyswitch_key.iter_mut().enumerate() {
|
||||
for (input_key_element, mut keyswitch_key_block_block) in input_lwe_sk
|
||||
.as_ref()
|
||||
.iter()
|
||||
.zip_eq(keyswitch_key_block.iter_mut())
|
||||
{
|
||||
// We fill the buffer with the powers of the key elements
|
||||
for (level, message) in (1..=decomp_level_count.0)
|
||||
.rev()
|
||||
.map(DecompositionLevel)
|
||||
.zip_eq(decomposition_plaintexts_buffer.iter_mut())
|
||||
{
|
||||
// Here we take the decomposition term from the native torus, bring it to the
|
||||
// torus
|
||||
// we are working with by dividing by the scaling factor and the
|
||||
// encryption will take care of mapping that back to the native
|
||||
// torus
|
||||
*message.0 = DecompositionTerm::new(level, decomp_base_log, *input_key_element)
|
||||
.to_recomposition_summand()
|
||||
.wrapping_div(ciphertext_modulus.get_power_of_two_scaling_to_native_torus());
|
||||
}
|
||||
|
||||
let list = decomposition_plaintexts_buffer
|
||||
.iter()
|
||||
.map(|decomposition_plaintext| {
|
||||
PlaintextList::from_container(
|
||||
(0..cm_dimension.0)
|
||||
.map(|j| {
|
||||
if i == j {
|
||||
*decomposition_plaintext.0
|
||||
} else {
|
||||
Scalar::ZERO
|
||||
}
|
||||
})
|
||||
.collect_vec(),
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
encrypt_cm_lwe_ciphertext_list(
|
||||
output_lwe_sks,
|
||||
&mut keyswitch_key_block_block,
|
||||
&list,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocate_and_generate_new_cm_lwe_packing_key<
|
||||
Scalar,
|
||||
NoiseDistribution,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
Gen,
|
||||
>(
|
||||
input_lwe_sk: &LweSecretKey<InputKeyCont>,
|
||||
output_lwe_sk: &[LweSecretKey<OutputKeyCont>],
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
noise_distribution: NoiseDistribution,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) -> CmLwePackingKeyOwned<Scalar>
|
||||
where
|
||||
Scalar: UnsignedTorus + RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
NoiseDistribution: Distribution,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut new_lwe_keyswitch_key = CmLwePackingKeyOwned::new(
|
||||
Scalar::ZERO,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_sk.lwe_dimension(),
|
||||
output_lwe_sk[0].lwe_dimension(),
|
||||
CmDimension(output_lwe_sk.len()),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
generate_cm_lwe_packing_key(
|
||||
input_lwe_sk,
|
||||
output_lwe_sk,
|
||||
&mut new_lwe_keyswitch_key,
|
||||
noise_distribution,
|
||||
generator,
|
||||
);
|
||||
|
||||
new_lwe_keyswitch_key
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
//! Module containing primitives pertaining to the `CommonMask LWE programmable
|
||||
//! bootstrap` using 64 bits FFT for polynomial multiplication.
|
||||
|
||||
use crate::core_crypto::commons::computation_buffers::ComputationBuffers;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::{Fft, FftView};
|
||||
use dyn_stack::PodStack;
|
||||
use tfhe_fft::c64;
|
||||
|
||||
pub fn programmable_bootstrap_cm_lwe_ciphertext<
|
||||
InputScalar,
|
||||
OutputScalar,
|
||||
InputCont,
|
||||
OutputCont,
|
||||
AccCont,
|
||||
KeyCont,
|
||||
>(
|
||||
input: &CmLweCiphertext<InputCont>,
|
||||
output: &mut CmLweCiphertext<OutputCont>,
|
||||
accumulator: &CmGlweCiphertext<AccCont>,
|
||||
fourier_bsk: &FourierCmLweBootstrapKey<KeyCont>,
|
||||
) where
|
||||
// CastInto required for PBS modulus switch which returns a usize
|
||||
InputScalar: UnsignedTorus + CastInto<usize>,
|
||||
OutputScalar: UnsignedTorus,
|
||||
InputCont: Container<Element = InputScalar>,
|
||||
OutputCont: ContainerMut<Element = OutputScalar>,
|
||||
AccCont: Container<Element = OutputScalar>,
|
||||
KeyCont: Container<Element = c64>,
|
||||
{
|
||||
assert!(
|
||||
input.ciphertext_modulus().is_power_of_two(),
|
||||
"This operation requires the input to have a power of two modulus."
|
||||
);
|
||||
assert_eq!(
|
||||
output.ciphertext_modulus(),
|
||||
accumulator.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let mut buffers = ComputationBuffers::new();
|
||||
|
||||
let fft = Fft::new(fourier_bsk.polynomial_size());
|
||||
let fft = fft.as_view();
|
||||
|
||||
buffers.resize(
|
||||
cm_bootstrap_requirement::<OutputScalar>(
|
||||
fourier_bsk.glwe_dimension(),
|
||||
fourier_bsk.cm_dimension(),
|
||||
fourier_bsk.polynomial_size(),
|
||||
fft,
|
||||
)
|
||||
.unaligned_bytes_required(),
|
||||
);
|
||||
|
||||
let stack = buffers.stack();
|
||||
|
||||
cm_programmable_bootstrap_lwe_ciphertext_mem_optimized(
|
||||
input,
|
||||
output,
|
||||
accumulator,
|
||||
fourier_bsk,
|
||||
fft,
|
||||
stack,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn cm_programmable_bootstrap_lwe_ciphertext_mem_optimized<
|
||||
InputScalar,
|
||||
OutputScalar,
|
||||
InputCont,
|
||||
OutputCont,
|
||||
AccCont,
|
||||
KeyCont,
|
||||
>(
|
||||
input: &CmLweCiphertext<InputCont>,
|
||||
output: &mut CmLweCiphertext<OutputCont>,
|
||||
accumulator: &CmGlweCiphertext<AccCont>,
|
||||
fourier_bsk: &FourierCmLweBootstrapKey<KeyCont>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) where
|
||||
// CastInto required for PBS modulus switch which returns a usize
|
||||
InputScalar: UnsignedTorus + CastInto<usize>,
|
||||
OutputScalar: UnsignedTorus,
|
||||
InputCont: Container<Element = InputScalar>,
|
||||
OutputCont: ContainerMut<Element = OutputScalar>,
|
||||
AccCont: Container<Element = OutputScalar>,
|
||||
KeyCont: Container<Element = c64>,
|
||||
{
|
||||
assert_eq!(
|
||||
accumulator.ciphertext_modulus(),
|
||||
output.ciphertext_modulus(),
|
||||
"Mismatched moduli between accumulator ({:?}) and output ({:?})",
|
||||
accumulator.ciphertext_modulus(),
|
||||
output.ciphertext_modulus()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
fourier_bsk.input_lwe_dimension(),
|
||||
input.lwe_dimension(),
|
||||
"Mismatched input LweDimension. \
|
||||
FourierLweBootstrapKey input LweDimension: {:?}, input CmLweCiphertext LweDimension {:?}.",
|
||||
fourier_bsk.input_lwe_dimension(),
|
||||
input.lwe_dimension(),
|
||||
);
|
||||
assert_eq!(
|
||||
fourier_bsk.output_lwe_dimension(),
|
||||
output.lwe_dimension(),
|
||||
"Mismatched output LweDimension. \
|
||||
FourierLweBootstrapKey input LweDimension: {:?}, input CmLweCiphertext LweDimension {:?}.",
|
||||
fourier_bsk.output_lwe_dimension(),
|
||||
output.lwe_dimension(),
|
||||
);
|
||||
assert_eq!(
|
||||
fourier_bsk.cm_dimension(),
|
||||
input.cm_dimension(),
|
||||
"Mismatched input CmDimension. \
|
||||
FourierLweBootstrapKey CmDimension: {:?}, input CmLweCiphertext CmDimension {:?}.",
|
||||
fourier_bsk.cm_dimension(),
|
||||
input.cm_dimension(),
|
||||
);
|
||||
assert_eq!(
|
||||
fourier_bsk.cm_dimension(),
|
||||
output.cm_dimension(),
|
||||
"Mismatched output CmDimension. \
|
||||
FourierLweBootstrapKey CmDimension: {:?}, output CmLweCiphertext CmDimension {:?}.",
|
||||
fourier_bsk.cm_dimension(),
|
||||
output.cm_dimension(),
|
||||
);
|
||||
|
||||
cm_bootstrap(
|
||||
fourier_bsk.as_view(),
|
||||
output.as_mut_view(),
|
||||
input.as_view(),
|
||||
accumulator.as_view(),
|
||||
fft,
|
||||
stack,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
pub mod cm_fft64;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::allocate_and_trivially_encrypt_new_cm_glwe_ciphertext;
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
// TODO: add support for one function per cm slot
|
||||
pub fn cm_generate_programmable_bootstrap_glwe_lut<F, Scalar: UnsignedTorus + CastFrom<usize>>(
|
||||
polynomial_size: PolynomialSize,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
message_modulus: usize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
delta: Scalar,
|
||||
f: F,
|
||||
) -> CmGlweCiphertextOwned<Scalar>
|
||||
where
|
||||
F: Fn(Scalar) -> Scalar,
|
||||
{
|
||||
// N/(p/2) = size of each block, to correct noise from the input we introduce the
|
||||
// notion of box, which manages redundancy to yield a denoised value
|
||||
// for several noisy values around a true input value.
|
||||
let box_size = polynomial_size.0 / message_modulus;
|
||||
|
||||
let accumulator_scalar = build_lut(
|
||||
polynomial_size,
|
||||
message_modulus,
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
box_size,
|
||||
);
|
||||
|
||||
let accumulator_scalar = (0..cm_dimension.0)
|
||||
.flat_map(|_| accumulator_scalar.iter())
|
||||
.copied()
|
||||
.collect_vec();
|
||||
|
||||
let accumulator_plaintext = PlaintextList::from_container(accumulator_scalar);
|
||||
|
||||
allocate_and_trivially_encrypt_new_cm_glwe_ciphertext(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
&accumulator_plaintext,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
fn build_lut<F, Scalar: UnsignedTorus + CastFrom<usize>>(
|
||||
polynomial_size: PolynomialSize,
|
||||
message_modulus: usize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
delta: Scalar,
|
||||
f: F,
|
||||
box_size: usize,
|
||||
) -> Vec<Scalar>
|
||||
where
|
||||
F: Fn(Scalar) -> Scalar,
|
||||
{
|
||||
// Create the accumulator
|
||||
let mut accumulator_scalar = vec![Scalar::ZERO; polynomial_size.0];
|
||||
|
||||
// Fill each box with the encoded denoised value
|
||||
for i in 0..message_modulus {
|
||||
let index = i * box_size;
|
||||
accumulator_scalar[index..index + box_size]
|
||||
.iter_mut()
|
||||
.for_each(|a| *a = f(Scalar::cast_from(i)) * delta);
|
||||
}
|
||||
|
||||
let half_box_size = box_size / 2;
|
||||
|
||||
if ciphertext_modulus.is_compatible_with_native_modulus() {
|
||||
// Negate the first half_box_size coefficients to manage negacyclicity and rotate
|
||||
for a_i in accumulator_scalar[0..half_box_size].iter_mut() {
|
||||
*a_i = (*a_i).wrapping_neg();
|
||||
}
|
||||
} else {
|
||||
let modulus: Scalar = ciphertext_modulus.get_custom_modulus().cast_into();
|
||||
for a_i in accumulator_scalar[0..half_box_size].iter_mut() {
|
||||
*a_i = (*a_i).wrapping_neg_custom_mod(modulus);
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate the accumulator
|
||||
accumulator_scalar.rotate_left(half_box_size);
|
||||
accumulator_scalar
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
use crate::core_crypto::commons::dispersion::Variance;
|
||||
use crate::core_crypto::commons::parameters::{NoiseEstimationMeasureBound, RSigmaFactor};
|
||||
use crate::core_crypto::commons::traits::{Container, ContainerMut, UnsignedInteger};
|
||||
use crate::core_crypto::experimental::entities::{CmLweCiphertext, CmLweCiphertextList};
|
||||
use crate::core_crypto::experimental::prelude::cm_lwe_ciphertext_add_assign;
|
||||
use crate::core_crypto::prelude::modulus_switch_noise_reduction::{
|
||||
measure_modulus_switch_noise_estimation_for_binary_key, Candidate, CandidateResult,
|
||||
};
|
||||
use crate::core_crypto::prelude::{
|
||||
CiphertextModulus, CiphertextModulusLog, ContiguousEntityContainer, DispersionParameter,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
pub fn choose_candidate_to_improve_modulus_switch_noise_for_binary_key<Scalar, C1, C2>(
|
||||
lwe: &CmLweCiphertext<C1>,
|
||||
encryptions_of_zero: &CmLweCiphertextList<C2>,
|
||||
r_sigma_factor: RSigmaFactor,
|
||||
bound: NoiseEstimationMeasureBound,
|
||||
input_variance: Variance,
|
||||
log_modulus: CiphertextModulusLog,
|
||||
) -> CandidateResult
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
C1: Container<Element = Scalar>,
|
||||
C2: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
lwe.lwe_dimension(),
|
||||
encryptions_of_zero.lwe_dimension(),
|
||||
"input lwe size (={:?}) != encryptions of zero lwe size (={:?})",
|
||||
lwe.lwe_dimension(),
|
||||
encryptions_of_zero.lwe_dimension(),
|
||||
);
|
||||
assert_eq!(
|
||||
lwe.ciphertext_modulus(),
|
||||
encryptions_of_zero.ciphertext_modulus(),
|
||||
"input ciphertext_modulus (={:?}) != encryptions of zero ciphertext_modulus (={:?})",
|
||||
lwe.ciphertext_modulus(),
|
||||
encryptions_of_zero.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
assert_ne!(
|
||||
encryptions_of_zero.cm_lwe_ciphertext_count().0,
|
||||
0,
|
||||
"Expected at least one encryption of zero"
|
||||
);
|
||||
assert_eq!(
|
||||
lwe.ciphertext_modulus(),
|
||||
CiphertextModulus::new_native(),
|
||||
"Non native modulus are not supported, got {}",
|
||||
lwe.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
let modulus = lwe.ciphertext_modulus().raw_modulus_float();
|
||||
|
||||
let input_variance = input_variance.get_modular_variance(modulus);
|
||||
|
||||
let mask = lwe.get_mask();
|
||||
|
||||
let mask = mask.as_ref();
|
||||
|
||||
let base_measure = measure_modulus_switch_noise_estimation_for_binary_key(
|
||||
r_sigma_factor,
|
||||
input_variance,
|
||||
log_modulus,
|
||||
mask.iter().copied(),
|
||||
Scalar::ZERO,
|
||||
);
|
||||
|
||||
let mut best_candidate = Candidate::NoAddition;
|
||||
let mut best_measure = base_measure;
|
||||
|
||||
if base_measure <= bound.0 {
|
||||
return CandidateResult::SatisfyingBound(best_candidate);
|
||||
}
|
||||
|
||||
for (index, encryption_of_zero) in encryptions_of_zero.iter().enumerate() {
|
||||
let encryption_of_zero_mask = encryption_of_zero.get_mask();
|
||||
|
||||
let encryption_of_zero_mask = encryption_of_zero_mask.as_ref();
|
||||
|
||||
let mask_sum = mask
|
||||
.iter()
|
||||
.zip_eq(encryption_of_zero_mask.iter())
|
||||
.map(|(a, b)| a.wrapping_add(*b));
|
||||
|
||||
let measure = measure_modulus_switch_noise_estimation_for_binary_key(
|
||||
r_sigma_factor,
|
||||
input_variance,
|
||||
log_modulus,
|
||||
mask_sum,
|
||||
Scalar::ZERO,
|
||||
);
|
||||
|
||||
if measure < best_measure {
|
||||
best_measure = measure;
|
||||
best_candidate = Candidate::AddEncryptionOfZero { index };
|
||||
}
|
||||
|
||||
if measure <= bound.0 {
|
||||
return CandidateResult::SatisfyingBound(best_candidate);
|
||||
}
|
||||
}
|
||||
|
||||
CandidateResult::BestNotSatisfyingBound(best_candidate)
|
||||
}
|
||||
|
||||
pub fn improve_lwe_ciphertext_modulus_switch_noise_for_binary_key_cm<Scalar, C1, C2>(
|
||||
lwe: &mut CmLweCiphertext<C1>,
|
||||
encryptions_of_zero: &CmLweCiphertextList<C2>,
|
||||
r_sigma_factor: RSigmaFactor,
|
||||
bound: NoiseEstimationMeasureBound,
|
||||
input_variance: Variance,
|
||||
log_modulus: CiphertextModulusLog,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
C1: ContainerMut<Element = Scalar>,
|
||||
C2: Container<Element = Scalar>,
|
||||
{
|
||||
let candidate = choose_candidate_to_improve_modulus_switch_noise_for_binary_key(
|
||||
lwe,
|
||||
encryptions_of_zero,
|
||||
r_sigma_factor,
|
||||
bound,
|
||||
input_variance,
|
||||
log_modulus,
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
assert!(
|
||||
matches!(candidate, CandidateResult::SatisfyingBound(_)),
|
||||
"MS noise reduction bound not reached for any candidate"
|
||||
);
|
||||
|
||||
let candidate = match candidate {
|
||||
CandidateResult::SatisfyingBound(candidate) => candidate,
|
||||
CandidateResult::BestNotSatisfyingBound(candidate) => candidate,
|
||||
};
|
||||
|
||||
match candidate {
|
||||
Candidate::NoAddition => {}
|
||||
Candidate::AddEncryptionOfZero { index } => {
|
||||
let encryption_of_zero = encryptions_of_zero.get(index);
|
||||
|
||||
cm_lwe_ciphertext_add_assign(lwe, &encryption_of_zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
||||
pub mod cm_bootstrap;
|
||||
pub mod cm_ggsw_encryption;
|
||||
pub mod cm_ggsw_external_product;
|
||||
pub mod cm_glwe_encryption;
|
||||
pub mod cm_glwe_sample_extraction;
|
||||
pub mod cm_lwe_bootstrap_key_conversion;
|
||||
pub mod cm_lwe_bootstrap_key_generation;
|
||||
pub mod cm_lwe_encryption;
|
||||
pub mod cm_lwe_keyswitch;
|
||||
pub mod cm_lwe_keyswitch_key_generation;
|
||||
pub mod cm_lwe_linear_algebra;
|
||||
pub mod cm_lwe_packing;
|
||||
pub mod cm_lwe_packing_key_generation;
|
||||
pub mod cm_lwe_programmable_bootstrapping;
|
||||
pub mod cm_modulus_switch_noise_reduction;
|
||||
pub mod cm_params;
|
||||
|
||||
pub use cm_bootstrap::*;
|
||||
pub use cm_ggsw_encryption::*;
|
||||
pub use cm_ggsw_external_product::*;
|
||||
pub use cm_glwe_encryption::*;
|
||||
pub use cm_glwe_sample_extraction::*;
|
||||
pub use cm_lwe_bootstrap_key_conversion::*;
|
||||
pub use cm_lwe_bootstrap_key_generation::*;
|
||||
pub use cm_lwe_encryption::*;
|
||||
pub use cm_lwe_keyswitch::*;
|
||||
pub use cm_lwe_linear_algebra::*;
|
||||
pub use cm_lwe_packing::*;
|
||||
pub use cm_lwe_packing_key_generation::*;
|
||||
pub use cm_lwe_programmable_bootstrapping::*;
|
||||
pub use cm_params::*;
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod common_mask_algorithms;
|
||||
pub mod glwe_fast_keyswitch;
|
||||
pub mod glwe_partial_sample_extraction;
|
||||
pub mod lwe_shrinking_keyswitch;
|
||||
@@ -8,6 +9,7 @@ pub mod pseudo_ggsw_encryption;
|
||||
pub mod shared_glwe_secret_key_generation;
|
||||
pub mod shared_lwe_secret_key_generation;
|
||||
|
||||
pub use common_mask_algorithms::*;
|
||||
pub use glwe_fast_keyswitch::*;
|
||||
pub use glwe_partial_sample_extraction::*;
|
||||
pub use lwe_shrinking_keyswitch::*;
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
|
||||
use crate::core_crypto::commons::computation_buffers::ComputationBuffers;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::entities::fourier_pseudo_ggsw_ciphertext::{
|
||||
fill_with_forward_fourier_scratch, PseudoFourierGgswCiphertext,
|
||||
};
|
||||
use crate::core_crypto::experimental::entities::fourier_pseudo_ggsw_ciphertext::PseudoFourierGgswCiphertext;
|
||||
use crate::core_crypto::experimental::entities::pseudo_ggsw_ciphertext::PseudoGgswCiphertext;
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::{Fft, FftView};
|
||||
use dyn_stack::{PodStack, StackReq};
|
||||
@@ -67,5 +65,5 @@ pub fn convert_standard_pseudo_ggsw_ciphertext_to_fourier_mem_optimized<
|
||||
pub fn convert_standard_pseudo_ggsw_ciphertext_to_fourier_mem_optimized_requirement(
|
||||
fft: FftView<'_>,
|
||||
) -> StackReq {
|
||||
fill_with_forward_fourier_scratch(fft)
|
||||
fft.forward_scratch()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
use crate::core_crypto::experimental::prelude::{
|
||||
allocate_and_encrypt_new_cm_lwe_ciphertext, CmDimension,
|
||||
};
|
||||
use crate::core_crypto::prelude::*;
|
||||
use itertools::Itertools;
|
||||
|
||||
#[test]
|
||||
fn cm_encryption() {
|
||||
let lwe_dimension = LweDimension(742);
|
||||
let lwe_noise_distribution =
|
||||
Gaussian::from_dispersion_parameter(StandardDev(0.0000000007069849454709433), 0.);
|
||||
let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
|
||||
let cm_dimension = CmDimension(10);
|
||||
|
||||
// Create the PRNG
|
||||
let mut seeder = new_seeder();
|
||||
let seeder = seeder.as_mut();
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed(), seeder);
|
||||
let mut secret_generator = SecretRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed());
|
||||
|
||||
// Create the LweSecretKey
|
||||
let lwe_secret_keys = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
// Create the plaintext
|
||||
|
||||
let plaintext = (0..cm_dimension.0).map(|i| (i as u64) << 55).collect_vec();
|
||||
|
||||
let plaintext_list = PlaintextList::from_container(plaintext.as_slice());
|
||||
|
||||
// Create a new LweCiphertext
|
||||
let lwe = allocate_and_encrypt_new_cm_lwe_ciphertext(
|
||||
&lwe_secret_keys,
|
||||
&plaintext_list,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
for i in 0..cm_dimension.0 {
|
||||
let decrypted_plaintext =
|
||||
decrypt_lwe_ciphertext(&lwe_secret_keys[i], &lwe.extract_lwe_ciphertext(i));
|
||||
|
||||
// Round and remove encoding
|
||||
// First create a decomposer working on the high 4 bits corresponding to our encoding.
|
||||
let decomposer = SignedDecomposer::new(DecompositionBaseLog(9), DecompositionLevelCount(1));
|
||||
|
||||
let rounded = decomposer.closest_representable(decrypted_plaintext.0);
|
||||
|
||||
// Remove the encoding
|
||||
let cleartext = rounded >> 55;
|
||||
|
||||
// Check we recovered the original message
|
||||
assert_eq!(cleartext, plaintext[i] >> 55);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
use super::super::cm_params::{CmApParams, CM_PARAM_2_2_MINUS_64};
|
||||
use super::super::*;
|
||||
use crate::core_crypto::experimental::prelude::cm_lwe_keyswitch_key_generation::allocate_and_generate_new_cm_lwe_keyswitch_key;
|
||||
use crate::core_crypto::prelude::misc::check_encrypted_content_respects_mod;
|
||||
use crate::core_crypto::prelude::*;
|
||||
use itertools::Itertools;
|
||||
|
||||
const NB_TESTS: usize = 10;
|
||||
|
||||
#[test]
|
||||
fn cm_keyswitch() {
|
||||
cm_keyswitch_generic(&CM_PARAM_2_2_MINUS_64);
|
||||
}
|
||||
|
||||
fn cm_keyswitch_generic(params: &CmApParams) {
|
||||
let in_lwe_dimension = params
|
||||
.glwe_dimension
|
||||
.to_equivalent_lwe_dimension(params.polynomial_size);
|
||||
|
||||
let out_lwe_dimension = params.lwe_dimension;
|
||||
|
||||
let in_lwe_noise_distribution = params.glwe_noise_distribution;
|
||||
|
||||
let key_lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let ks_decomp_base_log = params.base_log_ks;
|
||||
let ks_decomp_level_count = params.level_ks;
|
||||
|
||||
let cm_dimension = params.cm_dimension;
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let msg_modulus = 1 << params.precision;
|
||||
let mut msg = msg_modulus;
|
||||
let delta: u64 = encoding_with_padding / msg_modulus;
|
||||
|
||||
for _ in 0..NB_TESTS {
|
||||
let lwe_sks_in = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_lwe_secret_key(
|
||||
in_lwe_dimension,
|
||||
&mut rsc.secret_random_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let lwe_sks_out = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_lwe_secret_key(
|
||||
out_lwe_dimension,
|
||||
&mut rsc.secret_random_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let cm_lwe_keyswitch_key = allocate_and_generate_new_cm_lwe_keyswitch_key(
|
||||
&lwe_sks_in,
|
||||
&lwe_sks_out,
|
||||
cm_dimension,
|
||||
ks_decomp_base_log,
|
||||
ks_decomp_level_count,
|
||||
key_lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&cm_lwe_keyswitch_key,
|
||||
ciphertext_modulus
|
||||
));
|
||||
while msg != 0 {
|
||||
msg = msg.wrapping_sub(1);
|
||||
|
||||
let pts = PlaintextList::from_container(
|
||||
(0..cm_dimension.0).map(|_| msg * delta).collect_vec(),
|
||||
);
|
||||
|
||||
let ct = allocate_and_encrypt_new_cm_lwe_ciphertext(
|
||||
&lwe_sks_in,
|
||||
&pts,
|
||||
in_lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut output_ct =
|
||||
CmLweCiphertext::new(0, out_lwe_dimension, cm_dimension, ciphertext_modulus);
|
||||
|
||||
cm_keyswitch_lwe_ciphertext(&cm_lwe_keyswitch_key, &ct, &mut output_ct);
|
||||
|
||||
for (i, lwe_sk_out) in lwe_sks_out.iter().enumerate() {
|
||||
let output_ct = output_ct.extract_lwe_ciphertext(i);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&output_ct,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let decrypted = decrypt_lwe_ciphertext(lwe_sk_out, &output_ct);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
assert_eq!(msg, decoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::prelude::misc::check_encrypted_content_respects_mod;
|
||||
use crate::core_crypto::prelude::test::{
|
||||
get_encoding_with_padding, round_decode, ClassicTestParams, TestResources,
|
||||
TEST_PARAMS_4_BITS_NATIVE_U64,
|
||||
};
|
||||
use crate::core_crypto::prelude::*;
|
||||
use itertools::Itertools;
|
||||
|
||||
const NB_TESTS: usize = 10;
|
||||
|
||||
#[test]
|
||||
fn cm_packing() {
|
||||
cm_packing_generic(TEST_PARAMS_4_BITS_NATIVE_U64);
|
||||
}
|
||||
|
||||
fn cm_packing_generic(params: ClassicTestParams<u64>) {
|
||||
let in_lwe_dimension = LweDimension(13);
|
||||
|
||||
let out_lwe_dimension = LweDimension(10);
|
||||
|
||||
let lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
let message_modulus_log = params.message_modulus_log;
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let ks_decomp_base_log = params.ks_base_log;
|
||||
let ks_decomp_level_count = params.ks_level;
|
||||
|
||||
let cm_dimension = CmDimension(10);
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let msg_modulus = 1 << message_modulus_log.0;
|
||||
let mut msg = msg_modulus;
|
||||
let delta: u64 = encoding_with_padding / msg_modulus;
|
||||
|
||||
for _ in 0..NB_TESTS {
|
||||
let lwe_sk_in = allocate_and_generate_new_binary_lwe_secret_key(
|
||||
in_lwe_dimension,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
|
||||
let lwe_sks_out = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_generate_new_binary_lwe_secret_key(
|
||||
out_lwe_dimension,
|
||||
&mut rsc.secret_random_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let packing_key = allocate_and_generate_new_cm_lwe_packing_key(
|
||||
&lwe_sk_in,
|
||||
&lwe_sks_out,
|
||||
ks_decomp_base_log,
|
||||
ks_decomp_level_count,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&packing_key,
|
||||
ciphertext_modulus
|
||||
));
|
||||
while msg != 0 {
|
||||
msg = msg.wrapping_sub(1);
|
||||
|
||||
let ct = (0..cm_dimension.0)
|
||||
.map(|_| {
|
||||
allocate_and_encrypt_new_lwe_ciphertext(
|
||||
&lwe_sk_in,
|
||||
Plaintext(msg * delta),
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
)
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
let mut output_ct =
|
||||
CmLweCiphertext::new(0, out_lwe_dimension, cm_dimension, ciphertext_modulus);
|
||||
|
||||
pack_lwe_ciphertexts_into_cm(&packing_key, &ct, &mut output_ct);
|
||||
|
||||
for (i, lwe_sk_out) in lwe_sks_out.iter().enumerate() {
|
||||
let output_ct = output_ct.extract_lwe_ciphertext(i);
|
||||
|
||||
assert!(check_encrypted_content_respects_mod(
|
||||
&output_ct,
|
||||
ciphertext_modulus
|
||||
));
|
||||
|
||||
let decrypted = decrypt_lwe_ciphertext(lwe_sk_out, &output_ct);
|
||||
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
assert_eq!(msg, decoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
use super::super::cm_lwe_keyswitch_key_generation::allocate_and_generate_new_cm_lwe_keyswitch_key;
|
||||
use super::super::cm_params::{CmApParams, CM_PARAM_2_2_MINUS_64};
|
||||
use super::super::*;
|
||||
use crate::core_crypto::commons::test_tools::{
|
||||
check_both_ratio_under, normality_test_f64, random_uint_between, variance,
|
||||
};
|
||||
use crate::core_crypto::experimental::prelude::cm_modulus_switch_noise_reduction::improve_lwe_ciphertext_modulus_switch_noise_for_binary_key_cm;
|
||||
use crate::core_crypto::prelude::misc::torus_modular_diff;
|
||||
use crate::core_crypto::prelude::*;
|
||||
use cm_fft64::programmable_bootstrap_cm_lwe_ciphertext;
|
||||
use itertools::Itertools;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
|
||||
#[cfg(not(tarpaulin))]
|
||||
const NB_TESTS: usize = 100;
|
||||
#[cfg(tarpaulin)]
|
||||
const NB_TESTS: usize = 1;
|
||||
|
||||
#[test]
|
||||
fn test_cm_pbs_lut_application_test_param() {
|
||||
test_cm_pbs_lut_application(&CM_PARAM_2_2_MINUS_64)
|
||||
}
|
||||
|
||||
fn test_cm_pbs_lut_application(params: &CmApParams) {
|
||||
let cm_dimension = params.cm_dimension;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let msg_modulus = 1u64 << params.precision;
|
||||
let delta = encoding_with_padding / msg_modulus;
|
||||
|
||||
let CmBootstrapKeys {
|
||||
small_lwe_sk,
|
||||
big_lwe_sk,
|
||||
bsk,
|
||||
fbsk,
|
||||
} = generate_cm_pbs_keys(
|
||||
params,
|
||||
&mut rsc.encryption_random_generator,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
drop(bsk);
|
||||
|
||||
for _ in 0..NB_TESTS {
|
||||
let random_lut: Vec<u64> = (0..msg_modulus)
|
||||
.map(|_| random_uint_between(0u64..msg_modulus))
|
||||
.collect();
|
||||
let f = |x: u64| random_lut[x as usize];
|
||||
|
||||
let accumulator = cm_generate_programmable_bootstrap_glwe_lut(
|
||||
polynomial_size,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
msg_modulus.cast_into(),
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
);
|
||||
|
||||
let msgs: Vec<u64> = (0..cm_dimension.0)
|
||||
.map(|_| random_uint_between(0u64..msg_modulus))
|
||||
.collect();
|
||||
|
||||
let plaintexts =
|
||||
PlaintextList::from_container(msgs.iter().map(|&m| m * delta).collect_vec());
|
||||
|
||||
let ct = allocate_and_encrypt_new_cm_lwe_ciphertext(
|
||||
&small_lwe_sk,
|
||||
&plaintexts,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut output_ct = CmLweCiphertext::new(
|
||||
0u64,
|
||||
fbsk.output_lwe_dimension(),
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
programmable_bootstrap_cm_lwe_ciphertext(&ct, &mut output_ct, &accumulator, &fbsk);
|
||||
|
||||
let decrypted = decrypt_cm_lwe_ciphertext(&big_lwe_sk, &output_ct);
|
||||
|
||||
for (decrypted, &input) in decrypted.iter().zip(msgs.iter()) {
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
assert_eq!(decoded, f(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cm_pbs_ap_sequence_test_param() {
|
||||
test_cm_pbs_ap_sequence(&CM_PARAM_2_2_MINUS_64)
|
||||
}
|
||||
|
||||
fn test_cm_pbs_ap_sequence(params: &CmApParams) {
|
||||
let cm_dimension = params.cm_dimension;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
|
||||
let glwe_noise_distribution = params.glwe_noise_distribution;
|
||||
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let msg_modulus = 1u64 << params.precision;
|
||||
let delta = encoding_with_padding / msg_modulus;
|
||||
|
||||
let f = |x| x;
|
||||
|
||||
let accumulator = cm_generate_programmable_bootstrap_glwe_lut(
|
||||
polynomial_size,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
msg_modulus.cast_into(),
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
);
|
||||
|
||||
let CmBootstrapKeys {
|
||||
small_lwe_sk,
|
||||
big_lwe_sk,
|
||||
bsk,
|
||||
fbsk,
|
||||
} = generate_cm_pbs_keys(
|
||||
params,
|
||||
&mut rsc.encryption_random_generator,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
drop(bsk);
|
||||
|
||||
let cm_lwe_keyswitch_key = allocate_and_generate_new_cm_lwe_keyswitch_key(
|
||||
&big_lwe_sk,
|
||||
&small_lwe_sk,
|
||||
cm_dimension,
|
||||
params.base_log_ks,
|
||||
params.level_ks,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut ct_big;
|
||||
|
||||
let mut ct_small = CmLweCiphertext::new(
|
||||
0u64,
|
||||
small_lwe_sk[0].lwe_dimension(),
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let max_nb_zeros_n = params.max_nb_zeros_n;
|
||||
|
||||
let mut encryptions_of_zero = CmLweCiphertextList::new(
|
||||
0,
|
||||
params.lwe_dimension,
|
||||
cm_dimension,
|
||||
max_nb_zeros_n,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let plaintext_list = PlaintextList::new(0, PlaintextCount(cm_dimension.0));
|
||||
|
||||
let plaintext_lists: Vec<_> = (0..max_nb_zeros_n.0)
|
||||
.map(|_| plaintext_list.clone())
|
||||
.collect();
|
||||
|
||||
encrypt_cm_lwe_ciphertext_list(
|
||||
&small_lwe_sk,
|
||||
&mut encryptions_of_zero,
|
||||
&plaintext_lists,
|
||||
lwe_noise_distribution,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let log_modulus = polynomial_size.to_blind_rotation_input_modulus_log();
|
||||
|
||||
for _ in 0..NB_TESTS {
|
||||
// Use zero messages so that multiplication by nu does not change the encrypted value
|
||||
let msgs: Vec<u64> = vec![0u64; cm_dimension.0];
|
||||
|
||||
let plaintexts =
|
||||
PlaintextList::from_container(msgs.iter().map(|&m| m * delta).collect_vec());
|
||||
|
||||
ct_big = allocate_and_encrypt_new_cm_lwe_ciphertext(
|
||||
&big_lwe_sk,
|
||||
&plaintexts,
|
||||
glwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
// depth to bootstrap non clean cts
|
||||
for _ in 0..3 {
|
||||
cm_lwe_ciphertext_cleartext_mul_assign(&mut ct_big, Cleartext(params.nu as u64));
|
||||
|
||||
cm_keyswitch_lwe_ciphertext(&cm_lwe_keyswitch_key, &ct_big, &mut ct_small);
|
||||
|
||||
improve_lwe_ciphertext_modulus_switch_noise_for_binary_key_cm(
|
||||
&mut ct_small,
|
||||
&encryptions_of_zero,
|
||||
params.r_sigma_factor_n,
|
||||
params.ms_bound_n,
|
||||
params.ms_input_variance_n,
|
||||
log_modulus,
|
||||
);
|
||||
|
||||
programmable_bootstrap_cm_lwe_ciphertext(&ct_small, &mut ct_big, &accumulator, &fbsk);
|
||||
|
||||
let decrypted = decrypt_cm_lwe_ciphertext(&big_lwe_sk, &ct_big);
|
||||
|
||||
for (decrypted, &input) in decrypted.iter().zip(msgs.iter()) {
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
assert_eq!(decoded, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cm_pbs_noise_analysis_test_param() {
|
||||
test_cm_pbs_noise_analysis(&CM_PARAM_2_2_MINUS_64)
|
||||
}
|
||||
|
||||
fn test_cm_pbs_noise_analysis(params: &CmApParams) {
|
||||
let cm_dimension = params.cm_dimension;
|
||||
let ciphertext_modulus = params.ciphertext_modulus;
|
||||
|
||||
let small_lwe_noise_distribution = params.lwe_noise_distribution;
|
||||
|
||||
let encoding_with_padding = get_encoding_with_padding(ciphertext_modulus);
|
||||
let glwe_dimension = params.glwe_dimension;
|
||||
let polynomial_size = params.polynomial_size;
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let msg_modulus = 1u64 << params.precision;
|
||||
let delta = encoding_with_padding / msg_modulus;
|
||||
|
||||
let f = |x| x;
|
||||
|
||||
let accumulator = cm_generate_programmable_bootstrap_glwe_lut(
|
||||
polynomial_size,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
msg_modulus.cast_into(),
|
||||
ciphertext_modulus,
|
||||
delta,
|
||||
f,
|
||||
);
|
||||
|
||||
let CmBootstrapKeys {
|
||||
small_lwe_sk,
|
||||
big_lwe_sk,
|
||||
bsk,
|
||||
fbsk,
|
||||
} = generate_cm_pbs_keys(
|
||||
params,
|
||||
&mut rsc.encryption_random_generator,
|
||||
&mut rsc.secret_random_generator,
|
||||
);
|
||||
drop(bsk);
|
||||
|
||||
let noises: Vec<Vec<f64>> = (0..2000)
|
||||
.into_par_iter()
|
||||
.map(move |i| {
|
||||
let msg = i % msg_modulus;
|
||||
|
||||
let mut rsc = TestResources::new();
|
||||
|
||||
let plaintexts = PlaintextList::from_container(
|
||||
(0..cm_dimension.0).map(|_| msg * delta).collect_vec(),
|
||||
);
|
||||
|
||||
let ct = allocate_and_encrypt_new_cm_lwe_ciphertext(
|
||||
&small_lwe_sk,
|
||||
&plaintexts,
|
||||
small_lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
&mut rsc.encryption_random_generator,
|
||||
);
|
||||
|
||||
let mut output_ct = CmLweCiphertext::new(
|
||||
0u64,
|
||||
fbsk.output_lwe_dimension(),
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
programmable_bootstrap_cm_lwe_ciphertext(&ct, &mut output_ct, &accumulator, &fbsk);
|
||||
|
||||
let decrypted = decrypt_cm_lwe_ciphertext(&big_lwe_sk, &output_ct);
|
||||
|
||||
let mut noises = vec![];
|
||||
for (decrypted, plaintext) in decrypted.iter().zip(plaintexts.iter()) {
|
||||
let decoded = round_decode(decrypted.0, delta) % msg_modulus;
|
||||
|
||||
assert_eq!(f(plaintext.0 / delta), decoded);
|
||||
|
||||
let noise = torus_modular_diff(
|
||||
decrypted.0,
|
||||
f(plaintext.0 / delta) * delta,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
noises.push(noise);
|
||||
}
|
||||
noises
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Transpose from [[pbs0_slot0, pbs0_slot1, ...], [pbs1_slot0, pbs1_slot1, ...], ...]
|
||||
// into [[pbs0_slot0, pbs1_slot0, ...], [pbs0_slot1, pbs1_slot1, ...], ...]
|
||||
// To analyse noise for each slot independently
|
||||
let noises_separated: Vec<Vec<f64>> = (0..cm_dimension.0)
|
||||
.map(|i| noises.iter().map(|list| list[i]).collect_vec())
|
||||
.collect_vec();
|
||||
|
||||
let expected_variance = cm_pbs_variance_132_bits_security_gaussian_impl(
|
||||
params.lwe_dimension.0 as f64,
|
||||
params.glwe_dimension.0 as f64,
|
||||
params.polynomial_size.0 as f64,
|
||||
(1_u64 << params.base_log_bs.0) as f64,
|
||||
params.level_bs.0 as f64,
|
||||
2_f64.powi(64),
|
||||
);
|
||||
|
||||
println!("expected_variance: {expected_variance:e}");
|
||||
|
||||
for noises in &noises_separated {
|
||||
let variance = variance(noises);
|
||||
|
||||
println!("variance: {:?}", variance.0);
|
||||
|
||||
let test_result = normality_test_f64(noises, 0.001);
|
||||
|
||||
assert!(test_result.null_hypothesis_is_valid);
|
||||
|
||||
println!("p_value normality: {}", test_result.p_value);
|
||||
|
||||
// TODO: confidence interval to reconsider
|
||||
assert!(check_both_ratio_under(expected_variance, variance.0, 1.1));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_pbs_variance_132_bits_security_gaussian_impl(
|
||||
input_lwe_dimension: f64,
|
||||
output_glwe_dimension: f64,
|
||||
output_polynomial_size: f64,
|
||||
decomposition_base: f64,
|
||||
decomposition_level_count: f64,
|
||||
modulus: f64,
|
||||
) -> f64 {
|
||||
input_lwe_dimension
|
||||
* (2.06537277069845e-33
|
||||
* decomposition_base.powf(2.0)
|
||||
* decomposition_level_count
|
||||
* output_polynomial_size.powf(2.0)
|
||||
* (output_glwe_dimension + 2.0)
|
||||
+ decomposition_level_count
|
||||
* output_polynomial_size
|
||||
* ((-0.0497829131652661 * output_glwe_dimension * output_polynomial_size
|
||||
+ 5.31469187675068)
|
||||
.exp2()
|
||||
+ 16.0 * modulus.powf(-2.0))
|
||||
* ((1_f64 / 12.0) * decomposition_base.powf(2.0) + 0.166666666666667)
|
||||
* (output_glwe_dimension + 2.0)
|
||||
+ (1_f64 / 12.0) * modulus.powf(-2.0)
|
||||
+ (1_f64 / 2.0)
|
||||
* output_glwe_dimension
|
||||
* output_polynomial_size
|
||||
* (0.0208333333333333 * modulus.powf(-2.0)
|
||||
+ 0.0416666666666667
|
||||
* decomposition_base.powf(-2.0 * decomposition_level_count))
|
||||
+ (1_f64 / 24.0) * decomposition_base.powf(-2.0 * decomposition_level_count))
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
mod cm_lwe_keyswitch;
|
||||
mod cm_lwe_packing;
|
||||
mod cm_lwe_programmable_bootstrapping;
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::core_crypto::algorithms::test::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
mod cm_lwe_encryption;
|
||||
mod common_mask;
|
||||
mod lwe_fast_keyswitch;
|
||||
mod lwe_stair_keyswitch;
|
||||
|
||||
@@ -43,3 +43,11 @@ impl crate::core_crypto::commons::parameters::LweDimension {
|
||||
LweSecretKeyUnsharedCoefCount(self.0 - shared_coef_count.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// The number of ciphertexts in an lwe ciphertext list.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct CmLweCiphertextCount(pub usize);
|
||||
|
||||
/// The number of scalar in an LWE mask, or the length of an LWE secret key.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
|
||||
pub struct CmDimension(pub usize);
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
use crate::core_crypto::commons::parameters::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
|
||||
};
|
||||
use crate::core_crypto::commons::traits::{Container, IntoContainerOwned, Split};
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::{
|
||||
par_convert_polynomials_list_to_fourier, FftView, FourierPolynomialList,
|
||||
};
|
||||
use crate::core_crypto::prelude::{ContiguousEntityContainer, UnsignedTorus};
|
||||
use aligned_vec::{avec, ABox};
|
||||
use dyn_stack::PodStack;
|
||||
use itertools::izip;
|
||||
use tfhe_fft::c64;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(bound(deserialize = "C: IntoContainerOwned"))]
|
||||
pub struct FourierCmLweBootstrapKey<C: Container<Element = c64>> {
|
||||
fourier: FourierPolynomialList<C>,
|
||||
input_lwe_dimension: LweDimension,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
}
|
||||
|
||||
pub type FourierCmLweBootstrapKeyView<'a> = FourierCmLweBootstrapKey<&'a [c64]>;
|
||||
pub type FourierCmLweBootstrapKeyMutView<'a> = FourierCmLweBootstrapKey<&'a mut [c64]>;
|
||||
|
||||
impl<C: Container<Element = c64>> FourierCmLweBootstrapKey<C> {
|
||||
pub fn from_container(
|
||||
data: C,
|
||||
input_lwe_dimension: LweDimension,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
data.container_len(),
|
||||
input_lwe_dimension.0
|
||||
* decomposition_level_count.0
|
||||
* (glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size()
|
||||
)
|
||||
);
|
||||
Self {
|
||||
fourier: FourierPolynomialList {
|
||||
data,
|
||||
polynomial_size,
|
||||
},
|
||||
input_lwe_dimension,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_cm_ggsw_iter(self) -> impl DoubleEndedIterator<Item = FourierCmGgswCiphertext<C>>
|
||||
where
|
||||
C: Split,
|
||||
{
|
||||
self.fourier
|
||||
.data
|
||||
.split_into(self.input_lwe_dimension.0)
|
||||
.map(move |slice| {
|
||||
FourierCmGgswCiphertext::from_container(
|
||||
slice,
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.fourier.polynomial_size,
|
||||
self.decomposition_base_log,
|
||||
self.decomposition_level_count,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn input_lwe_dimension(&self) -> LweDimension {
|
||||
self.input_lwe_dimension
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.fourier.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomposition_base_log
|
||||
}
|
||||
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomposition_level_count
|
||||
}
|
||||
|
||||
pub fn output_lwe_dimension(&self) -> LweDimension {
|
||||
self.glwe_dimension
|
||||
.to_equivalent_lwe_dimension(self.polynomial_size())
|
||||
}
|
||||
|
||||
pub fn data(self) -> C {
|
||||
self.fourier.data
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> FourierCmLweBootstrapKeyView<'_> {
|
||||
FourierCmLweBootstrapKeyView {
|
||||
fourier: FourierPolynomialList {
|
||||
data: self.fourier.data.as_ref(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
},
|
||||
input_lwe_dimension: self.input_lwe_dimension,
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut_view(&mut self) -> FourierCmLweBootstrapKeyMutView<'_>
|
||||
where
|
||||
C: AsMut<[c64]>,
|
||||
{
|
||||
FourierCmLweBootstrapKeyMutView {
|
||||
fourier: FourierPolynomialList {
|
||||
data: self.fourier.data.as_mut(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
},
|
||||
input_lwe_dimension: self.input_lwe_dimension,
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type FourierCmLweBootstrapKeyOwned = FourierCmLweBootstrapKey<ABox<[c64]>>;
|
||||
|
||||
impl FourierCmLweBootstrapKey<ABox<[c64]>> {
|
||||
pub fn new(
|
||||
input_lwe_dimension: LweDimension,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
) -> Self {
|
||||
let boxed = avec![
|
||||
c64::default();
|
||||
input_lwe_dimension.0
|
||||
* decomposition_level_count.0
|
||||
* (glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size()
|
||||
)
|
||||
]
|
||||
.into_boxed_slice();
|
||||
|
||||
FourierCmLweBootstrapKey::from_container(
|
||||
boxed,
|
||||
input_lwe_dimension,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl FourierCmLweBootstrapKeyMutView<'_> {
|
||||
pub fn fill_with_forward_fourier<Scalar: UnsignedTorus>(
|
||||
mut self,
|
||||
coef_bsk: CmLweBootstrapKey<&'_ [Scalar]>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) {
|
||||
for (fourier_ggsw, standard_ggsw) in
|
||||
izip!(self.as_mut_view().into_cm_ggsw_iter(), coef_bsk.iter())
|
||||
{
|
||||
fourier_ggsw.fill_with_forward_fourier(standard_ggsw, fft, stack);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn par_fill_with_forward_fourier<Scalar: UnsignedTorus>(
|
||||
self,
|
||||
coef_bsk: CmLweBootstrapKey<&'_ [Scalar]>,
|
||||
fft: FftView<'_>,
|
||||
) {
|
||||
let polynomial_size = self.polynomial_size();
|
||||
par_convert_polynomials_list_to_fourier(
|
||||
self.data(),
|
||||
coef_bsk.into_container(),
|
||||
polynomial_size,
|
||||
fft,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,489 @@
|
||||
use crate::core_crypto::commons::math::decomposition::DecompositionLevel;
|
||||
use crate::core_crypto::commons::math::torus::UnsignedTorus;
|
||||
use crate::core_crypto::commons::parameters::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, PolynomialSize,
|
||||
};
|
||||
use crate::core_crypto::commons::traits::{Container, IntoContainerOwned, Split};
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::fft_impl::fft64::math;
|
||||
use crate::core_crypto::prelude::ContiguousEntityContainer;
|
||||
use aligned_vec::{avec, ABox};
|
||||
use dyn_stack::PodStack;
|
||||
use itertools::izip;
|
||||
use math::fft::{FftView, FourierPolynomialList};
|
||||
use math::polynomial::FourierPolynomialMutView;
|
||||
use tfhe_fft::c64;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(bound(deserialize = "C: IntoContainerOwned"))]
|
||||
pub struct FourierCmGgswCiphertext<C: Container<Element = c64>> {
|
||||
fourier: FourierPolynomialList<C>,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct FourierCmGgswLevelMatrix<C: Container<Element = c64>> {
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level: DecompositionLevel,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct FourierCmGgswLevelRow<C: Container<Element = c64>> {
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level: DecompositionLevel,
|
||||
}
|
||||
|
||||
pub type FourierCmGgswCiphertextView<'a> = FourierCmGgswCiphertext<&'a [c64]>;
|
||||
pub type FourierCmGgswCiphertextMutView<'a> = FourierCmGgswCiphertext<&'a mut [c64]>;
|
||||
pub type FourierCmGgswLevelMatrixView<'a> = FourierCmGgswLevelMatrix<&'a [c64]>;
|
||||
pub type FourierCmGgswLevelMatrixMutView<'a> = FourierCmGgswLevelMatrix<&'a mut [c64]>;
|
||||
pub type FourierCmGgswLevelRowView<'a> = FourierCmGgswLevelRow<&'a [c64]>;
|
||||
pub type FourierCmGgswLevelRowMutView<'a> = FourierCmGgswLevelRow<&'a mut [c64]>;
|
||||
|
||||
impl<C: Container<Element = c64>> FourierCmGgswCiphertext<C> {
|
||||
pub fn from_container(
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
data.container_len(),
|
||||
(glwe_dimension.0 + cm_dimension.0)
|
||||
* decomposition_level_count.0
|
||||
* cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size()
|
||||
)
|
||||
);
|
||||
|
||||
Self {
|
||||
fourier: FourierPolynomialList {
|
||||
data,
|
||||
polynomial_size,
|
||||
},
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.fourier.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomposition_base_log
|
||||
}
|
||||
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomposition_level_count
|
||||
}
|
||||
|
||||
pub fn data(self) -> C {
|
||||
self.fourier.data
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> FourierCmGgswCiphertextView<'_>
|
||||
where
|
||||
C: AsRef<[c64]>,
|
||||
{
|
||||
FourierCmGgswCiphertextView {
|
||||
fourier: FourierPolynomialList {
|
||||
data: self.fourier.data.as_ref(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
},
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut_view(&mut self) -> FourierCmGgswCiphertextMutView<'_>
|
||||
where
|
||||
C: AsMut<[c64]>,
|
||||
{
|
||||
FourierCmGgswCiphertextMutView {
|
||||
fourier: FourierPolynomialList {
|
||||
data: self.fourier.data.as_mut(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
},
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Container<Element = c64>> FourierCmGgswLevelMatrix<C> {
|
||||
pub fn new(
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level: DecompositionLevel,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
data.container_len(),
|
||||
cm_fourier_ggsw_level_matrix_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size()
|
||||
),
|
||||
);
|
||||
Self {
|
||||
data,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_level,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_rows(self) -> impl DoubleEndedIterator<Item = FourierCmGgswLevelRow<C>>
|
||||
where
|
||||
C: Split,
|
||||
{
|
||||
let row_count = self.row_count();
|
||||
self.data
|
||||
.split_into(row_count)
|
||||
.map(move |slice| FourierCmGgswLevelRow {
|
||||
data: slice,
|
||||
polynomial_size: self.polynomial_size,
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_level: self.decomposition_level,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn row_count(&self) -> usize {
|
||||
self.glwe_dimension.0 + self.cm_dimension.0
|
||||
}
|
||||
|
||||
pub fn decomposition_level(&self) -> DecompositionLevel {
|
||||
self.decomposition_level
|
||||
}
|
||||
|
||||
pub fn data(self) -> C {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Container<Element = c64>> FourierCmGgswLevelRow<C> {
|
||||
pub fn new(
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level: DecompositionLevel,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
data.container_len(),
|
||||
cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size()
|
||||
)
|
||||
);
|
||||
Self {
|
||||
data,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_level,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn decomposition_level(&self) -> DecompositionLevel {
|
||||
self.decomposition_level
|
||||
}
|
||||
|
||||
pub fn data(self) -> C {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FourierCmGgswCiphertextView<'a> {
|
||||
pub fn into_levels(self) -> impl DoubleEndedIterator<Item = FourierCmGgswLevelMatrixView<'a>> {
|
||||
let decomposition_level_count = self.decomposition_level_count.0;
|
||||
self.fourier
|
||||
.data
|
||||
.split_into(decomposition_level_count)
|
||||
.enumerate()
|
||||
.map(move |(i, slice)| {
|
||||
FourierCmGgswLevelMatrixView::new(
|
||||
slice,
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.fourier.polynomial_size,
|
||||
DecompositionLevel(decomposition_level_count - i),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FourierCmGgswCiphertextMutView<'_> {
|
||||
pub fn fill_with_forward_fourier<Scalar: UnsignedTorus>(
|
||||
self,
|
||||
coef_ggsw: CmGgswCiphertextView<'_, Scalar>,
|
||||
fft: FftView<'_>,
|
||||
stack: &mut PodStack,
|
||||
) {
|
||||
debug_assert_eq!(coef_ggsw.polynomial_size(), self.polynomial_size());
|
||||
let fourier_poly_size = coef_ggsw.polynomial_size().to_fourier_polynomial_size().0;
|
||||
|
||||
for (fourier_poly, coef_poly) in izip!(
|
||||
self.data().into_chunks(fourier_poly_size),
|
||||
coef_ggsw.as_polynomial_list().iter()
|
||||
) {
|
||||
fft.forward_as_torus(
|
||||
FourierPolynomialMutView { data: fourier_poly },
|
||||
coef_poly,
|
||||
stack,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FourierCmGgswCiphertext<ABox<[c64]>> {
|
||||
pub fn new(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
) -> Self {
|
||||
let boxed = avec![
|
||||
c64::default();
|
||||
polynomial_size.to_fourier_polynomial_size().0
|
||||
* (glwe_dimension.0+cm_dimension.0)
|
||||
* (glwe_dimension.0+cm_dimension.0)
|
||||
* decomposition_level_count.0
|
||||
]
|
||||
.into_boxed_slice();
|
||||
|
||||
FourierCmGgswCiphertext::from_container(
|
||||
boxed,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub struct FourierCmGgswCiphertextList<C: Container<Element = c64>> {
|
||||
fourier: FourierPolynomialList<C>,
|
||||
cm_dimension: CmDimension,
|
||||
glwe_dimension: GlweDimension,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
pub type FourierCmGgswCiphertextListView<'a> = FourierCmGgswCiphertextList<&'a [c64]>;
|
||||
pub type FourierCmGgswCiphertextListMutView<'a> = FourierCmGgswCiphertextList<&'a mut [c64]>;
|
||||
|
||||
impl<C: Container<Element = c64>> FourierCmGgswCiphertextList<C> {
|
||||
pub fn new(
|
||||
data: C,
|
||||
count: usize,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
data.container_len(),
|
||||
count
|
||||
* decomposition_level_count.0
|
||||
* (glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size()
|
||||
)
|
||||
);
|
||||
|
||||
Self {
|
||||
fourier: FourierPolynomialList {
|
||||
data,
|
||||
polynomial_size,
|
||||
},
|
||||
count,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
decomposition_level_count,
|
||||
decomposition_base_log,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data(self) -> C {
|
||||
self.fourier.data
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.fourier.polynomial_size
|
||||
}
|
||||
|
||||
pub fn count(&self) -> usize {
|
||||
self.count
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomposition_level_count
|
||||
}
|
||||
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomposition_base_log
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> FourierCmGgswCiphertextListView<'_> {
|
||||
let fourier = FourierPolynomialList {
|
||||
data: self.fourier.data.as_ref(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
};
|
||||
FourierCmGgswCiphertextListView {
|
||||
fourier,
|
||||
count: self.count,
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut_view(&mut self) -> FourierCmGgswCiphertextListMutView<'_>
|
||||
where
|
||||
C: AsMut<[c64]>,
|
||||
{
|
||||
let fourier = FourierPolynomialList {
|
||||
data: self.fourier.data.as_mut(),
|
||||
polynomial_size: self.fourier.polynomial_size,
|
||||
};
|
||||
FourierCmGgswCiphertextListMutView {
|
||||
fourier,
|
||||
count: self.count,
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
decomposition_level_count: self.decomposition_level_count,
|
||||
decomposition_base_log: self.decomposition_base_log,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_ggsw_iter(self) -> impl DoubleEndedIterator<Item = FourierCmGgswCiphertext<C>>
|
||||
where
|
||||
C: Split,
|
||||
{
|
||||
self.fourier.data.split_into(self.count).map(move |slice| {
|
||||
FourierCmGgswCiphertext::from_container(
|
||||
slice,
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.fourier.polynomial_size,
|
||||
self.decomposition_base_log,
|
||||
self.decomposition_level_count,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn split_at(self, mid: usize) -> (Self, Self)
|
||||
where
|
||||
C: Split,
|
||||
{
|
||||
let polynomial_size = self.fourier.polynomial_size;
|
||||
let glwe_dimension = self.glwe_dimension;
|
||||
let cm_dimension = self.cm_dimension;
|
||||
|
||||
let decomposition_level_count = self.decomposition_level_count;
|
||||
let decomposition_base_log = self.decomposition_base_log;
|
||||
|
||||
let (left, right) = self.fourier.data.split_at(
|
||||
mid * (glwe_dimension.0 + cm_dimension.0)
|
||||
* decomposition_level_count.0
|
||||
* cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size.to_fourier_polynomial_size(),
|
||||
),
|
||||
);
|
||||
(
|
||||
Self::new(
|
||||
left,
|
||||
mid,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
),
|
||||
Self::new(
|
||||
right,
|
||||
self.count - mid,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,607 @@
|
||||
//! Module containing the definition of the CmGgswCiphertext.
|
||||
|
||||
use super::cm_glwe_ciphertext::cm_glwe_ciphertext_encryption_mask_sample_count;
|
||||
use super::cm_glwe_ciphertext_list::{CmGlweCiphertextListMutView, CmGlweCiphertextListView};
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGeneratorForkConfig;
|
||||
use crate::core_crypto::commons::math::random::{Distribution, RandomGenerable};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::prelude::{
|
||||
glwe_ciphertext_encryption_mask_sample_count, glwe_ciphertext_encryption_noise_sample_count,
|
||||
PolynomialListMutView, PolynomialListView,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmGgswCiphertext<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for CmGgswCiphertext<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for CmGgswCiphertext<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_ciphertext_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
) -> usize {
|
||||
decomp_level_count.0 * cm_ggsw_level_matrix_size(glwe_dimension, cm_dimension, polynomial_size)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_level_matrix_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> usize {
|
||||
(glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size)
|
||||
}
|
||||
|
||||
pub fn fourier_cm_ggsw_ciphertext_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
fourier_polynomial_size: FourierPolynomialSize,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
) -> usize {
|
||||
decomp_level_count.0
|
||||
* fourier_cm_ggsw_level_matrix_size(glwe_dimension, cm_dimension, fourier_polynomial_size)
|
||||
}
|
||||
|
||||
pub fn fourier_cm_ggsw_level_matrix_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
fourier_polynomial_size: FourierPolynomialSize,
|
||||
) -> usize {
|
||||
(glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_fourier_size(glwe_dimension, cm_dimension, fourier_polynomial_size)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_ciphertext_encryption_mask_sample_count(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
) -> EncryptionMaskSampleCount {
|
||||
decomp_level_count.0
|
||||
* cm_ggsw_level_matrix_encryption_mask_sample_count(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_level_matrix_encryption_mask_sample_count(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> EncryptionMaskSampleCount {
|
||||
(glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_encryption_mask_sample_count(glwe_dimension, polynomial_size)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_ciphertext_encryption_noise_sample_count(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
) -> EncryptionNoiseSampleCount {
|
||||
decomp_level_count.0
|
||||
* cm_ggsw_level_matrix_encryption_noise_sample_count(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_level_matrix_encryption_noise_sample_count(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> EncryptionNoiseSampleCount {
|
||||
cm_dimension.0
|
||||
* (glwe_dimension.0 + cm_dimension.0)
|
||||
* glwe_ciphertext_encryption_noise_sample_count(polynomial_size)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_ciphertext_encryption_fork_config<Scalar, MaskDistribution, NoiseDistribution>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
mask_distribution: MaskDistribution,
|
||||
noise_distribution: NoiseDistribution,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> EncryptionRandomGeneratorForkConfig
|
||||
where
|
||||
Scalar: UnsignedInteger
|
||||
+ RandomGenerable<MaskDistribution, CustomModulus = Scalar>
|
||||
+ RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
MaskDistribution: Distribution,
|
||||
NoiseDistribution: Distribution,
|
||||
{
|
||||
let ggsw_level_matrix_mask_sample_count = cm_ggsw_level_matrix_encryption_mask_sample_count(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
);
|
||||
let ggsw_level_matrix_noise_sample_count = cm_ggsw_level_matrix_encryption_noise_sample_count(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
);
|
||||
|
||||
let modulus = ciphertext_modulus.get_custom_modulus_as_optional_scalar();
|
||||
|
||||
EncryptionRandomGeneratorForkConfig::new(
|
||||
decomposition_level_count.0,
|
||||
ggsw_level_matrix_mask_sample_count,
|
||||
mask_distribution,
|
||||
ggsw_level_matrix_noise_sample_count,
|
||||
noise_distribution,
|
||||
modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_level_matrix_encryption_fork_config<Scalar, MaskDistribution, NoiseDistribution>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
mask_distribution: MaskDistribution,
|
||||
noise_distribution: NoiseDistribution,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> EncryptionRandomGeneratorForkConfig
|
||||
where
|
||||
Scalar: UnsignedInteger
|
||||
+ RandomGenerable<MaskDistribution, CustomModulus = Scalar>
|
||||
+ RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
MaskDistribution: Distribution,
|
||||
NoiseDistribution: Distribution,
|
||||
{
|
||||
let glwe_ciphertext_mask_sample_count =
|
||||
glwe_ciphertext_encryption_mask_sample_count(glwe_dimension, polynomial_size);
|
||||
let glwe_ciphertext_noise_sample_count =
|
||||
glwe_ciphertext_encryption_noise_sample_count(polynomial_size);
|
||||
|
||||
let modulus = ciphertext_modulus.get_custom_modulus_as_optional_scalar();
|
||||
|
||||
EncryptionRandomGeneratorForkConfig::new(
|
||||
glwe_dimension.0 + cm_dimension.0,
|
||||
glwe_ciphertext_mask_sample_count,
|
||||
mask_distribution,
|
||||
glwe_ciphertext_noise_sample_count,
|
||||
noise_distribution,
|
||||
modulus,
|
||||
)
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmGgswCiphertext<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
container.container_len() > 0,
|
||||
"Got an empty container to create a CmGgswCiphertext"
|
||||
);
|
||||
|
||||
let matrix_size = cm_ggsw_level_matrix_size(glwe_dimension, cm_dimension, polynomial_size);
|
||||
|
||||
assert!(
|
||||
container.container_len().is_multiple_of(matrix_size),
|
||||
"The provided container length is not valid. \
|
||||
It needs to be dividable by glwe_dimension * glwe_dimension * polynomial_size: {}. \
|
||||
Got container length: {} and glwe_dimension: {glwe_dimension:?}, \
|
||||
cm_dimension: {cm_dimension:?}, polynomial_size: {polynomial_size:?}.",
|
||||
matrix_size,
|
||||
container.container_len()
|
||||
);
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
DecompositionLevelCount(self.data.container_len() / self.ggsw_level_matrix_size())
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
pub fn ggsw_level_matrix_size(&self) -> usize {
|
||||
// GlweDimension GlweCiphertext(glwe_dimension,cm_dimension,polynomial_size) per level
|
||||
cm_ggsw_level_matrix_size(self.glwe_dimension, self.cm_dimension, self.polynomial_size)
|
||||
}
|
||||
|
||||
pub fn as_polynomial_list(&self) -> PolynomialListView<'_, Scalar> {
|
||||
PolynomialListView::from_container(self.as_ref(), self.polynomial_size)
|
||||
}
|
||||
|
||||
pub fn as_cm_glwe_list(&self) -> CmGlweCiphertextListView<'_, Scalar> {
|
||||
CmGlweCiphertextListView::from_container(
|
||||
self.as_ref(),
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> CmGgswCiphertextView<'_, Scalar> {
|
||||
CmGgswCiphertextView::from_container(
|
||||
self.as_ref(),
|
||||
self.glwe_dimension(),
|
||||
self.cm_dimension,
|
||||
self.polynomial_size(),
|
||||
self.decomposition_base_log(),
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn encryption_fork_config<MaskDistribution, NoiseDistribution>(
|
||||
&self,
|
||||
mask_distribution: MaskDistribution,
|
||||
noise_distribution: NoiseDistribution,
|
||||
) -> EncryptionRandomGeneratorForkConfig
|
||||
where
|
||||
MaskDistribution: Distribution,
|
||||
NoiseDistribution: Distribution,
|
||||
Scalar: RandomGenerable<MaskDistribution, CustomModulus = Scalar>
|
||||
+ RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
{
|
||||
cm_ggsw_ciphertext_encryption_fork_config(
|
||||
self.glwe_dimension(),
|
||||
self.cm_dimension,
|
||||
self.polynomial_size(),
|
||||
self.decomposition_level_count(),
|
||||
mask_distribution,
|
||||
noise_distribution,
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmGgswCiphertext<C> {
|
||||
pub fn as_mut_polynomial_list(&mut self) -> PolynomialListMutView<'_, Scalar> {
|
||||
let polynomial_size = self.polynomial_size;
|
||||
PolynomialListMutView::from_container(self.as_mut(), polynomial_size)
|
||||
}
|
||||
|
||||
pub fn as_mut_cm_glwe_list(&mut self) -> CmGlweCiphertextListMutView<'_, Scalar> {
|
||||
let polynomial_size = self.polynomial_size;
|
||||
let glwe_dimension = self.glwe_dimension;
|
||||
let cm_dimension = self.cm_dimension;
|
||||
let ciphertext_modulus: CiphertextModulus<Scalar> = self.ciphertext_modulus;
|
||||
CmGlweCiphertextListMutView::from_container(
|
||||
self.as_mut(),
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_mut_view(&mut self) -> CmGgswCiphertextMutView<'_, Scalar> {
|
||||
let glwe_dimension = self.glwe_dimension;
|
||||
let cm_dimension = self.cm_dimension;
|
||||
let polynomial_size = self.polynomial_size();
|
||||
let decomp_base_log = self.decomposition_base_log();
|
||||
let ciphertext_modulus = self.ciphertext_modulus;
|
||||
CmGgswCiphertextMutView::from_container(
|
||||
self.as_mut(),
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmGgswCiphertextOwned<Scalar> = CmGgswCiphertext<Vec<Scalar>>;
|
||||
|
||||
pub type CmGgswCiphertextView<'data, Scalar> = CmGgswCiphertext<&'data [Scalar]>;
|
||||
|
||||
pub type CmGgswCiphertextMutView<'data, Scalar> = CmGgswCiphertext<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmGgswCiphertextOwned<Scalar> {
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self::from_container(
|
||||
vec![
|
||||
fill_with;
|
||||
cm_ggsw_ciphertext_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_level_count
|
||||
)
|
||||
],
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmGgswCiphertextCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub cm_dimension: CmDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub decomp_base_log: DecompositionBaseLog,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for CmGgswCiphertext<C>
|
||||
{
|
||||
type Metadata = CmGgswCiphertextCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmGgswCiphertextCreationMetadata {
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CmGgswLevelMatrix<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmGgswLevelMatrix<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
container.container_len()
|
||||
== cm_ggsw_level_matrix_size(glwe_dimension, cm_dimension, polynomial_size),
|
||||
"The provided container length is not valid. \
|
||||
Expected length of {} (glwe_dimension * glwe_dimension * polynomial_size), got {}",
|
||||
cm_ggsw_level_matrix_size(glwe_dimension, cm_dimension, polynomial_size),
|
||||
container.container_len(),
|
||||
);
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
pub fn as_cm_glwe_list(&self) -> CmGlweCiphertextListView<'_, C::Element> {
|
||||
CmGlweCiphertextListView::from_container(
|
||||
self.data.as_ref(),
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn encryption_fork_config<MaskDistribution, NoiseDistribution>(
|
||||
&self,
|
||||
mask_distribution: MaskDistribution,
|
||||
noise_distribution: NoiseDistribution,
|
||||
) -> EncryptionRandomGeneratorForkConfig
|
||||
where
|
||||
MaskDistribution: Distribution,
|
||||
NoiseDistribution: Distribution,
|
||||
Scalar: RandomGenerable<MaskDistribution, CustomModulus = Scalar>
|
||||
+ RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
{
|
||||
cm_ggsw_level_matrix_encryption_fork_config(
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size(),
|
||||
mask_distribution,
|
||||
noise_distribution,
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmGgswLevelMatrix<C> {
|
||||
pub fn as_mut_cm_glwe_list(&mut self) -> CmGlweCiphertextListMutView<'_, C::Element> {
|
||||
CmGlweCiphertextListMutView::from_container(
|
||||
self.data.as_mut(),
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_fourier_ggsw_level_matrix_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
fourier_polynomial_size: FourierPolynomialSize,
|
||||
) -> usize {
|
||||
(glwe_dimension.0 + cm_dimension.0)
|
||||
* cm_glwe_ciphertext_fourier_size(glwe_dimension, cm_dimension, fourier_polynomial_size)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmGgswLevelMatrixCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub cm_dimension: CmDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for CmGgswLevelMatrix<C>
|
||||
{
|
||||
type Metadata = CmGgswLevelMatrixCreationMetadata<C::Element>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmGgswLevelMatrixCreationMetadata {
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for CmGgswCiphertext<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = CmGgswLevelMatrixCreationMetadata<Self::Element>;
|
||||
|
||||
type EntityView<'this>
|
||||
= CmGgswLevelMatrix<&'this [Self::Element]>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = ();
|
||||
|
||||
type SelfView<'this>
|
||||
= DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
CmGgswLevelMatrixCreationMetadata {
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
polynomial_size: self.polynomial_size,
|
||||
ciphertext_modulus: self.ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.ggsw_level_matrix_size()
|
||||
}
|
||||
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
unimplemented!(
|
||||
"This function is not supported for CmGgswCiphertext. \
|
||||
At the moment it does not make sense to return 'sub' CmGgswCiphertext."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for CmGgswCiphertext<C>
|
||||
{
|
||||
type EntityMutView<'this>
|
||||
= CmGgswLevelMatrix<&'this mut [Self::Element]>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this>
|
||||
= DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
//! Module containing the definition of the GgswCiphertextList.
|
||||
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGeneratorForkConfig;
|
||||
use crate::core_crypto::commons::math::random::{Distribution, RandomGenerable};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use cm_ggsw_ciphertext::{
|
||||
cm_ggsw_ciphertext_encryption_mask_sample_count,
|
||||
cm_ggsw_ciphertext_encryption_noise_sample_count, cm_ggsw_ciphertext_size,
|
||||
CmGgswCiphertextCreationMetadata, CmGgswCiphertextMutView, CmGgswCiphertextView,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmGgswCiphertextList<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for CmGgswCiphertextList<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for CmGgswCiphertextList<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_ciphertext_list_size(
|
||||
ciphertext_count: GgswCiphertextCount,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
) -> usize {
|
||||
ciphertext_count.0
|
||||
* cm_ggsw_ciphertext_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_level_count,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn cm_ggsw_ciphertext_list_encryption_fork_config<Scalar, MaskDistribution, NoiseDistribution>(
|
||||
ggsw_ciphertext_count: GgswCiphertextCount,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
mask_distribution: MaskDistribution,
|
||||
noise_distribution: NoiseDistribution,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> EncryptionRandomGeneratorForkConfig
|
||||
where
|
||||
Scalar: UnsignedInteger
|
||||
+ RandomGenerable<MaskDistribution, CustomModulus = Scalar>
|
||||
+ RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
MaskDistribution: Distribution,
|
||||
NoiseDistribution: Distribution,
|
||||
{
|
||||
let ggsw_mask_sample_count = cm_ggsw_ciphertext_encryption_mask_sample_count(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_level_count,
|
||||
);
|
||||
let ggsw_noise_sample_count = cm_ggsw_ciphertext_encryption_noise_sample_count(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomposition_level_count,
|
||||
);
|
||||
|
||||
let modulus = ciphertext_modulus.get_custom_modulus_as_optional_scalar();
|
||||
|
||||
EncryptionRandomGeneratorForkConfig::new(
|
||||
ggsw_ciphertext_count.0,
|
||||
ggsw_mask_sample_count,
|
||||
mask_distribution,
|
||||
ggsw_noise_sample_count,
|
||||
noise_distribution,
|
||||
modulus,
|
||||
)
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmGgswCiphertextList<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
let ggsw_size = cm_ggsw_ciphertext_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_level_count,
|
||||
);
|
||||
|
||||
assert!(
|
||||
container.container_len().is_multiple_of(ggsw_size),
|
||||
"The provided container length is not valid. \
|
||||
It needs to be dividable by decomp_level_count * glwe_dimension * glwe_dimension * polynomial_size: \
|
||||
{}.Got container length: {} and decomp_level_count: {decomp_level_count:?}, \
|
||||
glwe_dimension: {glwe_dimension:?}, cm_dimension: {cm_dimension:?} polynomial_size: {polynomial_size:?}",
|
||||
ggsw_size,
|
||||
container.container_len()
|
||||
);
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomp_level_count
|
||||
}
|
||||
|
||||
pub fn cm_ggsw_ciphertext_count(&self) -> GgswCiphertextCount {
|
||||
GgswCiphertextCount(
|
||||
self.data.container_len()
|
||||
/ cm_ggsw_ciphertext_size(
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size,
|
||||
self.decomp_level_count,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn as_polynomial_list(&self) -> PolynomialListView<'_, Scalar> {
|
||||
PolynomialList::from_container(self.as_ref(), self.polynomial_size())
|
||||
}
|
||||
|
||||
pub fn encryption_fork_config<MaskDistribution, NoiseDistribution>(
|
||||
&self,
|
||||
mask_distribution: MaskDistribution,
|
||||
noise_distribution: NoiseDistribution,
|
||||
) -> EncryptionRandomGeneratorForkConfig
|
||||
where
|
||||
MaskDistribution: Distribution,
|
||||
NoiseDistribution: Distribution,
|
||||
Scalar: RandomGenerable<MaskDistribution, CustomModulus = Scalar>
|
||||
+ RandomGenerable<NoiseDistribution, CustomModulus = Scalar>,
|
||||
{
|
||||
cm_ggsw_ciphertext_list_encryption_fork_config(
|
||||
self.cm_ggsw_ciphertext_count(),
|
||||
self.glwe_dimension(),
|
||||
self.cm_dimension,
|
||||
self.polynomial_size(),
|
||||
self.decomposition_level_count(),
|
||||
mask_distribution,
|
||||
noise_distribution,
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmGgswCiphertextList<C> {
|
||||
pub fn as_mut_polynomial_list(&mut self) -> PolynomialListMutView<'_, Scalar> {
|
||||
let polynomial_size = self.polynomial_size();
|
||||
PolynomialList::from_container(self.as_mut(), polynomial_size)
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmGgswCiphertextListOwned<Scalar> = CmGgswCiphertextList<Vec<Scalar>>;
|
||||
|
||||
pub type CmGgswCiphertextListView<'data, Scalar> = CmGgswCiphertextList<&'data [Scalar]>;
|
||||
|
||||
pub type CmGgswCiphertextListMutView<'data, Scalar> = CmGgswCiphertextList<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmGgswCiphertextListOwned<Scalar> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
ciphertext_count: GgswCiphertextCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self::from_container(
|
||||
vec![
|
||||
fill_with;
|
||||
cm_ggsw_ciphertext_list_size(
|
||||
ciphertext_count,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_level_count
|
||||
)
|
||||
],
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmGgswCiphertextListCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub cm_dimension: CmDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub decomp_base_log: DecompositionBaseLog,
|
||||
pub decomp_level_count: DecompositionLevelCount,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for CmGgswCiphertextList<C>
|
||||
{
|
||||
type Metadata = CmGgswCiphertextListCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmGgswCiphertextListCreationMetadata {
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for CmGgswCiphertextList<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = CmGgswCiphertextCreationMetadata<Self::Element>;
|
||||
|
||||
type EntityView<'this>
|
||||
= CmGgswCiphertextView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = CmGgswCiphertextListCreationMetadata<Self::Element>;
|
||||
|
||||
type SelfView<'this>
|
||||
= CmGgswCiphertextListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
CmGgswCiphertextCreationMetadata {
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
polynomial_size: self.polynomial_size,
|
||||
decomp_base_log: self.decomp_base_log,
|
||||
ciphertext_modulus: self.ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
cm_ggsw_ciphertext_size(
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size,
|
||||
self.decomp_level_count,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
CmGgswCiphertextListCreationMetadata {
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
polynomial_size: self.polynomial_size,
|
||||
decomp_base_log: self.decomp_base_log,
|
||||
decomp_level_count: self.decomp_level_count,
|
||||
ciphertext_modulus: self.ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for CmGgswCiphertextList<C>
|
||||
{
|
||||
type EntityMutView<'this>
|
||||
= CmGgswCiphertextMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this>
|
||||
= CmGgswCiphertextListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
//! Module containing the definition of the CmGlweCiphertext.
|
||||
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
pub fn cm_glwe_ciphertext_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> usize {
|
||||
(glwe_dimension.0 + cm_dimension.0) * polynomial_size.0
|
||||
}
|
||||
|
||||
pub fn cm_glwe_ciphertext_fourier_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
fourier_polynomial_size: FourierPolynomialSize,
|
||||
) -> usize {
|
||||
(glwe_dimension.0 + cm_dimension.0) * fourier_polynomial_size.0
|
||||
}
|
||||
|
||||
pub fn cm_glwe_ciphertext_mask_size(
|
||||
glwe_dimension: GlweDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> usize {
|
||||
glwe_dimension
|
||||
.to_equivalent_lwe_dimension(polynomial_size)
|
||||
.0
|
||||
}
|
||||
|
||||
pub fn cm_glwe_ciphertext_encryption_mask_sample_count(
|
||||
glwe_dimension: GlweDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> EncryptionMaskSampleCount {
|
||||
EncryptionMaskSampleCount(cm_glwe_ciphertext_mask_size(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn cm_glwe_ciphertext_encryption_noise_sample_count(
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> EncryptionNoiseSampleCount {
|
||||
EncryptionNoiseSampleCount(cm_dimension.0 * polynomial_size.0)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmGlweCiphertext<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for CmGlweCiphertext<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for CmGlweCiphertext<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmGlweCiphertext<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
container.container_len(),
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size)
|
||||
);
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
pub fn get_mask_and_bodies(&self) -> (GlweMask<&[Scalar]>, GlweBodyList<&[Scalar]>) {
|
||||
let (mask, bodies) = self.data.as_ref().split_at(cm_glwe_ciphertext_mask_size(
|
||||
self.glwe_dimension(),
|
||||
self.polynomial_size,
|
||||
));
|
||||
|
||||
(
|
||||
GlweMask::from_container(mask, self.polynomial_size, self.ciphertext_modulus),
|
||||
GlweBodyList::from_container(bodies, self.polynomial_size, self.ciphertext_modulus),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_bodies(&self) -> GlweBodyList<&[Scalar]> {
|
||||
let bodies = &self.data.as_ref()
|
||||
[cm_glwe_ciphertext_mask_size(self.glwe_dimension(), self.polynomial_size)..];
|
||||
|
||||
GlweBodyList::from_container(bodies, self.polynomial_size, self.ciphertext_modulus)
|
||||
}
|
||||
|
||||
pub fn get_mask(&self) -> GlweMask<&[Scalar]> {
|
||||
GlweMask::from_container(
|
||||
&self.as_ref()
|
||||
[0..cm_glwe_ciphertext_mask_size(self.glwe_dimension(), self.polynomial_size)],
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_polynomial_list(&self) -> PolynomialList<&'_ [Scalar]> {
|
||||
PolynomialList::from_container(self.as_ref(), self.polynomial_size)
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> CmGlweCiphertext<&'_ [Scalar]> {
|
||||
CmGlweCiphertext {
|
||||
data: self.data.as_ref(),
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
polynomial_size: self.polynomial_size,
|
||||
cm_dimension: self.cm_dimension,
|
||||
ciphertext_modulus: self.ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmGlweCiphertext<C> {
|
||||
pub fn get_mut_mask_and_bodies(
|
||||
&mut self,
|
||||
) -> (GlweMask<&mut [Scalar]>, GlweBodyList<&mut [Scalar]>) {
|
||||
let glwe_dimension = self.glwe_dimension();
|
||||
let polynomial_size = self.polynomial_size();
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
|
||||
let (mask, bodies) = self
|
||||
.data
|
||||
.as_mut()
|
||||
.split_at_mut(cm_glwe_ciphertext_mask_size(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
));
|
||||
|
||||
(
|
||||
GlweMask::from_container(mask, polynomial_size, ciphertext_modulus),
|
||||
GlweBodyList::from_container(bodies, polynomial_size, ciphertext_modulus),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_mut_bodies(&mut self) -> GlweBodyList<&mut [Scalar]> {
|
||||
let glwe_dimension = self.glwe_dimension();
|
||||
let polynomial_size = self.polynomial_size();
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
|
||||
let bodies = &mut self.data.as_mut()
|
||||
[cm_glwe_ciphertext_mask_size(glwe_dimension, polynomial_size)..];
|
||||
|
||||
GlweBodyList::from_container(bodies, polynomial_size, ciphertext_modulus)
|
||||
}
|
||||
|
||||
pub fn get_mut_mask(&mut self) -> GlweMask<&mut [Scalar]> {
|
||||
let polynomial_size = self.polynomial_size();
|
||||
let glwe_dimension = self.glwe_dimension();
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
|
||||
GlweMask::from_container(
|
||||
&mut self.as_mut()[0..cm_glwe_ciphertext_mask_size(glwe_dimension, polynomial_size)],
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_mut_polynomial_list(&mut self) -> PolynomialList<&'_ mut [Scalar]> {
|
||||
let polynomial_size = self.polynomial_size;
|
||||
PolynomialList::from_container(self.as_mut(), polynomial_size)
|
||||
}
|
||||
|
||||
pub fn as_mut_view(&mut self) -> CmGlweCiphertext<&'_ mut [Scalar]> {
|
||||
CmGlweCiphertext {
|
||||
data: self.data.as_mut(),
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
polynomial_size: self.polynomial_size,
|
||||
ciphertext_modulus: self.ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmGlweCiphertextOwned<Scalar> = CmGlweCiphertext<Vec<Scalar>>;
|
||||
|
||||
pub type CmGlweCiphertextView<'data, Scalar> = CmGlweCiphertext<&'data [Scalar]>;
|
||||
|
||||
pub type CmGlweCiphertextMutView<'data, Scalar> = CmGlweCiphertext<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmGlweCiphertextOwned<Scalar> {
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self::from_container(
|
||||
vec![fill_with; cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size)],
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmGlweCiphertextCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub cm_dimension: CmDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for CmGlweCiphertext<C>
|
||||
{
|
||||
type Metadata = CmGlweCiphertextCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmGlweCiphertextCreationMetadata {
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
//! Module containing the definition of the CmGlweCiphertextList.
|
||||
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmGlweCiphertextList<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for CmGlweCiphertextList<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for CmGlweCiphertextList<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmGlweCiphertextList<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
container.container_len().is_multiple_of(cm_glwe_ciphertext_size(
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
)),
|
||||
"The provided container length is not valid. \
|
||||
It needs to be dividable by (glwe_dimension + cm_dimension) * polynomial_size. \
|
||||
Got container length: {}, glwe_dimension: {glwe_dimension:?}, cm_dimension: {cm_dimension:?}, polynomial_size: {polynomial_size:?}.",
|
||||
container.container_len()
|
||||
);
|
||||
Self {
|
||||
data: container,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn glwe_ciphertext_count(&self) -> GlweCiphertextCount {
|
||||
GlweCiphertextCount(
|
||||
self.data.container_len()
|
||||
/ cm_glwe_ciphertext_size(
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmGlweCiphertextListOwned<Scalar> = CmGlweCiphertextList<Vec<Scalar>>;
|
||||
|
||||
pub type CmGlweCiphertextListView<'data, Scalar> = CmGlweCiphertextList<&'data [Scalar]>;
|
||||
|
||||
pub type CmGlweCiphertextListMutView<'data, Scalar> = CmGlweCiphertextList<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmGlweCiphertextListOwned<Scalar> {
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_count: GlweCiphertextCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self::from_container(
|
||||
vec![
|
||||
fill_with;
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size)
|
||||
* ciphertext_count.0
|
||||
],
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_fn<F>(
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_count: GlweCiphertextCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
mut fill_with: F,
|
||||
) -> Self
|
||||
where
|
||||
F: FnMut(usize, usize) -> Scalar,
|
||||
{
|
||||
let ciphertext_size =
|
||||
cm_glwe_ciphertext_size(glwe_dimension, cm_dimension, polynomial_size);
|
||||
let container: Vec<_> = (0..ciphertext_count.0)
|
||||
.flat_map(move |i| (0..ciphertext_size).map(move |j| (i, j)))
|
||||
.map(|(i, j)| fill_with(i, j))
|
||||
.collect();
|
||||
Self::from_container(
|
||||
container,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmGlweCiphertextListCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub cm_dimension: CmDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for CmGlweCiphertextList<C>
|
||||
{
|
||||
type Metadata = CmGlweCiphertextListCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmGlweCiphertextListCreationMetadata {
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for CmGlweCiphertextList<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = CmGlweCiphertextCreationMetadata<Self::Element>;
|
||||
|
||||
type EntityView<'this>
|
||||
= CmGlweCiphertextView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = CmGlweCiphertextListCreationMetadata<Self::Element>;
|
||||
|
||||
type SelfView<'this>
|
||||
= CmGlweCiphertextListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
CmGlweCiphertextCreationMetadata {
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
polynomial_size: self.polynomial_size(),
|
||||
ciphertext_modulus: self.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
cm_glwe_ciphertext_size(
|
||||
self.glwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.polynomial_size(),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
CmGlweCiphertextListCreationMetadata {
|
||||
glwe_dimension: self.glwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
polynomial_size: self.polynomial_size(),
|
||||
ciphertext_modulus: self.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for CmGlweCiphertextList<C>
|
||||
{
|
||||
type EntityMutView<'this>
|
||||
= CmGlweCiphertextMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this>
|
||||
= CmGlweCiphertextListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
//! Module containing the definition of the CmLweBootstrapKey.
|
||||
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmLweBootstrapKey<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
// An CmLweBootstrapKey is literally a CmGgswCiphertextList, so we wrap a
|
||||
// CmGgswCiphertextList and use Deref to have access to all the primitives of the
|
||||
// CmGgswCiphertextList easily
|
||||
ggsw_list: CmGgswCiphertextList<C>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> std::ops::Deref
|
||||
for CmLweBootstrapKey<C>
|
||||
{
|
||||
type Target = CmGgswCiphertextList<C>;
|
||||
|
||||
fn deref(&self) -> &CmGgswCiphertextList<C> {
|
||||
&self.ggsw_list
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> std::ops::DerefMut
|
||||
for CmLweBootstrapKey<C>
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut CmGgswCiphertextList<C> {
|
||||
&mut self.ggsw_list
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_bootstrap_key_size(
|
||||
input_lwe_dimension: LweDimension,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
) -> usize {
|
||||
cm_ggsw_ciphertext_list_size(
|
||||
GgswCiphertextCount(input_lwe_dimension.0),
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_level_count,
|
||||
)
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmLweBootstrapKey<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
Self {
|
||||
ggsw_list: CmGgswCiphertextList::from_container(
|
||||
container,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
ciphertext_modulus,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_lwe_dimension(&self) -> LweDimension {
|
||||
LweDimension(self.cm_ggsw_ciphertext_count().0)
|
||||
}
|
||||
|
||||
pub fn output_lwe_dimension(&self) -> LweDimension {
|
||||
self.glwe_dimension()
|
||||
.to_equivalent_lwe_dimension(self.polynomial_size())
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.ggsw_list.into_container()
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> CmLweBootstrapKey<&'_ [Scalar]> {
|
||||
CmLweBootstrapKey::from_container(
|
||||
self.as_ref(),
|
||||
self.glwe_dimension(),
|
||||
self.cm_dimension(),
|
||||
self.polynomial_size(),
|
||||
self.decomposition_base_log(),
|
||||
self.decomposition_level_count(),
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmLweBootstrapKey<C> {
|
||||
pub fn as_mut_view(&mut self) -> CmLweBootstrapKey<&'_ mut [Scalar]> {
|
||||
let glwe_dimension = self.glwe_dimension();
|
||||
let cm_dimension = self.cm_dimension();
|
||||
let polynomial_size = self.polynomial_size();
|
||||
let decomp_base_log = self.decomposition_base_log();
|
||||
let decomp_level_count = self.decomposition_level_count();
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
CmLweBootstrapKey::from_container(
|
||||
self.as_mut(),
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmLweBootstrapKeyOwned<Scalar> = CmLweBootstrapKey<Vec<Scalar>>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmLweBootstrapKeyOwned<Scalar> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
glwe_dimension: GlweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_dimension: LweDimension,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self {
|
||||
ggsw_list: CmGgswCiphertextList::new(
|
||||
fill_with,
|
||||
glwe_dimension,
|
||||
cm_dimension,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
GgswCiphertextCount(input_lwe_dimension.0),
|
||||
ciphertext_modulus,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
//! Module containing the definition of the [`CmLweCiphertext`].
|
||||
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
use crate::core_crypto::prelude::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmLweCiphertext<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
lwe_dimension: LweDimension,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for CmLweCiphertext<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for CmLweCiphertext<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmLweCiphertext<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
lwe_dimension: LweDimension,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
container.container_len() > 0,
|
||||
"Got an empty container to create an CmLweCiphertext"
|
||||
);
|
||||
Self {
|
||||
data: container,
|
||||
ciphertext_modulus,
|
||||
lwe_dimension,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lwe_dimension(&self) -> LweDimension {
|
||||
self.lwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
CmDimension(self.data.container_len() - self.lwe_dimension.0)
|
||||
}
|
||||
|
||||
pub fn get_mask_and_bodies(&self) -> (LweMask<&[Scalar]>, LweBodyList<&[Scalar]>) {
|
||||
let (mask, bodies) = self.data.as_ref().split_at(self.lwe_dimension.0);
|
||||
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
(
|
||||
LweMask::from_container(mask, ciphertext_modulus),
|
||||
LweBodyList::from_container(bodies, ciphertext_modulus),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_bodies(&self) -> LweBodyList<&[Scalar]> {
|
||||
self.get_mask_and_bodies().1
|
||||
}
|
||||
|
||||
pub fn get_mask(&self) -> LweMask<&[Scalar]> {
|
||||
self.get_mask_and_bodies().0
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> CmLweCiphertextView<'_, Scalar> {
|
||||
CmLweCiphertextView::from_container(
|
||||
self.as_ref(),
|
||||
self.lwe_dimension,
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
pub fn extract_lwe_ciphertext(&self, index: usize) -> LweCiphertextOwned<Scalar> {
|
||||
let mut extracted_lwe = self.get_mask().into_container().to_vec();
|
||||
|
||||
extracted_lwe.push(self.get_bodies().into_container()[index]);
|
||||
|
||||
LweCiphertext::from_container(extracted_lwe, self.ciphertext_modulus)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmLweCiphertext<C> {
|
||||
pub fn get_mut_mask_and_bodies(
|
||||
&mut self,
|
||||
) -> (LweMask<&mut [Scalar]>, LweBodyList<&mut [Scalar]>) {
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
let (mask, bodies) = self.data.as_mut().split_at(self.lwe_dimension.0);
|
||||
|
||||
(
|
||||
LweMask::from_container(mask, ciphertext_modulus),
|
||||
LweBodyList::from_container(bodies, ciphertext_modulus),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_mut_bodies(&mut self) -> LweBodyList<&mut [Scalar]> {
|
||||
self.get_mut_mask_and_bodies().1
|
||||
}
|
||||
|
||||
pub fn get_mut_mask(&mut self) -> LweMask<&mut [Scalar]> {
|
||||
self.get_mut_mask_and_bodies().0
|
||||
}
|
||||
|
||||
pub fn as_mut_view(&mut self) -> CmLweCiphertextMutView<'_, Scalar> {
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
let lwe_dimension = self.lwe_dimension;
|
||||
|
||||
CmLweCiphertextMutView::from_container(self.as_mut(), lwe_dimension, ciphertext_modulus)
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmLweCiphertextOwned<Scalar> = CmLweCiphertext<Vec<Scalar>>;
|
||||
|
||||
pub type CmLweCiphertextView<'data, Scalar> = CmLweCiphertext<&'data [Scalar]>;
|
||||
|
||||
pub type CmLweCiphertextMutView<'data, Scalar> = CmLweCiphertext<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmLweCiphertextOwned<Scalar> {
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
lwe_dimension: LweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self::from_container(
|
||||
vec![fill_with; lwe_dimension.0 + cm_dimension.0],
|
||||
lwe_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmLweCiphertextCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub lwe_dimension: LweDimension,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C> for CmLweCiphertext<C> {
|
||||
type Metadata = CmLweCiphertextCreationMetadata<C::Element>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmLweCiphertextCreationMetadata {
|
||||
lwe_dimension,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(from, lwe_dimension, ciphertext_modulus)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
//! Module containing the definition of the [`CmLweCiphertextList`].
|
||||
|
||||
use super::cm_lwe_ciphertext::{
|
||||
CmLweCiphertextCreationMetadata, CmLweCiphertextMutView, CmLweCiphertextView,
|
||||
};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmLweCiphertextList<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
lwe_dimension: LweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for CmLweCiphertextList<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for CmLweCiphertextList<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmLweCiphertextList<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
lwe_dimension: LweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
container
|
||||
.container_len()
|
||||
.is_multiple_of(lwe_dimension.0 + cm_dimension.0),
|
||||
"The provided container length is not valid. \
|
||||
It needs to be dividable by lwe_dimension + cm_dimension. \
|
||||
Got container length: {} and lwe_dimension + cm_dimension: {}.",
|
||||
container.container_len(),
|
||||
lwe_dimension.0 + cm_dimension.0
|
||||
);
|
||||
Self {
|
||||
data: container,
|
||||
lwe_dimension,
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lwe_dimension(&self) -> LweDimension {
|
||||
self.lwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn cm_lwe_ciphertext_count(&self) -> CmLweCiphertextCount {
|
||||
CmLweCiphertextCount(
|
||||
self.data.container_len() / (self.lwe_dimension.0 + self.cm_dimension.0),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> CmLweCiphertextListView<'_, Scalar> {
|
||||
CmLweCiphertextListView::from_container(
|
||||
self.as_ref(),
|
||||
self.lwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmLweCiphertextList<C> {
|
||||
pub fn as_mut_view(&mut self) -> CmLweCiphertextListMutView<'_, Scalar> {
|
||||
let lwe_dimension = self.lwe_dimension();
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
let cm_dimension = self.cm_dimension;
|
||||
|
||||
CmLweCiphertextListMutView::from_container(
|
||||
self.as_mut(),
|
||||
lwe_dimension,
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmLweCiphertextListOwned<Scalar> = CmLweCiphertextList<Vec<Scalar>>;
|
||||
|
||||
pub type CmLweCiphertextListView<'data, Scalar> = CmLweCiphertextList<&'data [Scalar]>;
|
||||
|
||||
pub type CmLweCiphertextListMutView<'data, Scalar> = CmLweCiphertextList<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmLweCiphertextListOwned<Scalar> {
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
lwe_dimension: LweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
ciphertext_count: CmLweCiphertextCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self::from_container(
|
||||
vec![fill_with; (lwe_dimension.0 + cm_dimension.0) * ciphertext_count.0],
|
||||
lwe_dimension,
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmLweCiphertextListCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub lwe_dimension: LweDimension,
|
||||
pub cm_dimension: CmDimension,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for CmLweCiphertextList<C>
|
||||
{
|
||||
type Metadata = CmLweCiphertextListCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmLweCiphertextListCreationMetadata {
|
||||
lwe_dimension,
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(from, lwe_dimension, cm_dimension, ciphertext_modulus)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for CmLweCiphertextList<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = CmLweCiphertextCreationMetadata<Self::Element>;
|
||||
|
||||
type EntityView<'this>
|
||||
= CmLweCiphertextView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = CmLweCiphertextListCreationMetadata<Self::Element>;
|
||||
|
||||
type SelfView<'this>
|
||||
= CmLweCiphertextListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
CmLweCiphertextCreationMetadata {
|
||||
ciphertext_modulus: self.ciphertext_modulus,
|
||||
lwe_dimension: self.lwe_dimension,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.lwe_dimension().0 + self.cm_dimension().0
|
||||
}
|
||||
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
CmLweCiphertextListCreationMetadata {
|
||||
lwe_dimension: self.lwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
ciphertext_modulus: self.ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for CmLweCiphertextList<C>
|
||||
{
|
||||
type EntityMutView<'this>
|
||||
= CmLweCiphertextMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this>
|
||||
= CmLweCiphertextListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
//! Module containing the definition of the [`CmLweKeyswitchKey`].
|
||||
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmLweKeyswitchKey<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_dimension: LweDimension,
|
||||
output_lwe_dimension: LweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for CmLweKeyswitchKey<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for CmLweKeyswitchKey<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_keyswitch_key_input_key_element_encrypted_size(
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_lwe_dimension: LweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
) -> usize {
|
||||
// One ciphertext per level encrypted under the output key
|
||||
decomp_level_count.0 * (output_lwe_dimension.0 + cm_dimension.0)
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmLweKeyswitchKey<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_dimension: LweDimension,
|
||||
output_lwe_dimension: LweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
container.container_len() > 0,
|
||||
"Got an empty container to create an CmLweKeyswitchKey"
|
||||
);
|
||||
let input_key_element_size = cm_lwe_keyswitch_key_input_key_element_encrypted_size(
|
||||
decomp_level_count,
|
||||
output_lwe_dimension,
|
||||
cm_dimension,
|
||||
);
|
||||
assert!(
|
||||
container.container_len().is_multiple_of(input_key_element_size),
|
||||
"The provided container length is not valid. \
|
||||
It needs to be dividable by decomp_level_count * (output_lwe_dimension + cm_dimension): {}. \
|
||||
Got container length: {} and decomp_level_count: {decomp_level_count:?}, \
|
||||
output_lwe_dimension: {output_lwe_dimension:?}.",
|
||||
input_key_element_size,
|
||||
container.container_len()
|
||||
);
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomp_level_count
|
||||
}
|
||||
|
||||
pub fn input_lwe_dimension(&self) -> LweDimension {
|
||||
self.input_lwe_dimension
|
||||
}
|
||||
|
||||
pub fn output_lwe_dimension(&self) -> LweDimension {
|
||||
self.output_lwe_dimension
|
||||
}
|
||||
|
||||
pub fn cm_dimension(&self) -> CmDimension {
|
||||
self.cm_dimension
|
||||
}
|
||||
|
||||
pub fn input_key_element_encrypted_size(&self) -> usize {
|
||||
cm_lwe_keyswitch_key_input_key_element_encrypted_size(
|
||||
self.decomp_level_count,
|
||||
self.output_lwe_dimension,
|
||||
self.cm_dimension,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> CmLweKeyswitchKeyView<'_, Scalar> {
|
||||
CmLweKeyswitchKey::from_container(
|
||||
self.as_ref(),
|
||||
self.decomp_base_log,
|
||||
self.decomp_level_count,
|
||||
self.input_lwe_dimension,
|
||||
self.output_lwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn as_cm_lwe_ciphertext_list(&self) -> CmLweCiphertextListView<'_, Scalar> {
|
||||
CmLweCiphertextListView::from_container(
|
||||
self.as_ref(),
|
||||
self.output_lwe_dimension,
|
||||
self.cm_dimension,
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmLweKeyswitchKey<C> {
|
||||
pub fn as_mut_view(&mut self) -> CmLweKeyswitchKeyMutView<'_, Scalar> {
|
||||
let decomp_base_log = self.decomp_base_log;
|
||||
let decomp_level_count = self.decomp_level_count;
|
||||
let input_lwe_dimension = self.input_lwe_dimension;
|
||||
let output_lwe_dimension = self.output_lwe_dimension;
|
||||
let cm_dimension = self.cm_dimension;
|
||||
let ciphertext_modulus = self.ciphertext_modulus;
|
||||
CmLweKeyswitchKey::from_container(
|
||||
self.as_mut(),
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_mut_cm_lwe_ciphertext_list(&mut self) -> CmLweCiphertextListMutView<'_, Scalar> {
|
||||
let output_lwe_dimension = self.output_lwe_dimension();
|
||||
let cm_dimension = self.cm_dimension;
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
CmLweCiphertextListMutView::from_container(
|
||||
self.as_mut(),
|
||||
output_lwe_dimension,
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmLweKeyswitchKeyOwned<Scalar> = CmLweKeyswitchKey<Vec<Scalar>>;
|
||||
|
||||
pub type CmLweKeyswitchKeyView<'data, Scalar> = CmLweKeyswitchKey<&'data [Scalar]>;
|
||||
|
||||
pub type CmLweKeyswitchKeyMutView<'data, Scalar> = CmLweKeyswitchKey<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmLweKeyswitchKeyOwned<Scalar> {
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_key_lwe_dimension: LweDimension,
|
||||
output_key_lwe_dimension: LweDimension,
|
||||
cm_dimension: CmDimension,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self::from_container(
|
||||
vec![
|
||||
fill_with;
|
||||
input_key_lwe_dimension.0
|
||||
* cm_lwe_keyswitch_key_input_key_element_encrypted_size(
|
||||
decomp_level_count,
|
||||
output_key_lwe_dimension,
|
||||
cm_dimension,
|
||||
)
|
||||
],
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_key_lwe_dimension,
|
||||
output_key_lwe_dimension,
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmLweKeyswitchKeyCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub decomp_base_log: DecompositionBaseLog,
|
||||
pub decomp_level_count: DecompositionLevelCount,
|
||||
pub input_lwe_dimension: LweDimension,
|
||||
pub output_lwe_dimension: LweDimension,
|
||||
pub cm_dimension: CmDimension,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for CmLweKeyswitchKey<C>
|
||||
{
|
||||
type Metadata = CmLweKeyswitchKeyCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmLweKeyswitchKeyCreationMetadata {
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_lwe_dimension,
|
||||
ciphertext_modulus,
|
||||
input_lwe_dimension,
|
||||
cm_dimension,
|
||||
} = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for CmLweKeyswitchKey<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = CmLweCiphertextListCreationMetadata<Self::Element>;
|
||||
|
||||
type EntityView<'this>
|
||||
= CmLweCiphertextListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = CmLweKeyswitchKeyCreationMetadata<Self::Element>;
|
||||
|
||||
type SelfView<'this>
|
||||
= CmLweKeyswitchKeyView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
CmLweCiphertextListCreationMetadata {
|
||||
ciphertext_modulus: self.ciphertext_modulus(),
|
||||
lwe_dimension: self.output_lwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.input_key_element_encrypted_size()
|
||||
}
|
||||
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
CmLweKeyswitchKeyCreationMetadata {
|
||||
decomp_base_log: self.decomposition_base_log(),
|
||||
decomp_level_count: self.decomposition_level_count(),
|
||||
output_lwe_dimension: self.output_lwe_dimension,
|
||||
input_lwe_dimension: self.input_lwe_dimension,
|
||||
cm_dimension: self.cm_dimension,
|
||||
ciphertext_modulus: self.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for CmLweKeyswitchKey<C>
|
||||
{
|
||||
type EntityMutView<'this>
|
||||
= CmLweCiphertextListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this>
|
||||
= CmLweKeyswitchKeyMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
//! Module containing the definition of the [`CmLwePackingKey`].
|
||||
|
||||
use super::cm_lwe_packing_key_part::{
|
||||
cm_lwe_packing_key_part_size, CmLwePackingKeyPartCreationMetadata, CmLwePackingKeyPartMutView,
|
||||
CmLwePackingKeyPartView,
|
||||
};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::entities::*;
|
||||
use crate::core_crypto::experimental::prelude::*;
|
||||
|
||||
/// A packing key that converts `d = output_cm_dimension` independent LWE ciphertexts into a
|
||||
/// single CM-LWE ciphertext.
|
||||
///
|
||||
/// For each input index `i`, the i-th [`CmLwePackingKeyPart`] key-switches
|
||||
/// `lwe(m_i)` into `cm_lwe(0, ..., 0, m_i, 0, ..., 0)` where `m_i` is placed at position `i`.
|
||||
/// Summing all `d` results yields `cm_lwe(m_0, m_1, ..., m_{d-1})`.
|
||||
///
|
||||
/// Memory layout (`d` = `output_cm_dimension`, `n` = `input_lwe_dimension`,
|
||||
/// `L` = `decomp_level_count`, `k` = `output_lwe_dimension`):
|
||||
/// `key_part[0] | key_part[1] | ... | key_part[d-1]`, each of size `n * L * (k + d)`.
|
||||
/// Total size: `d * n * L * (k + d)`.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmLwePackingKey<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_dimension: LweDimension,
|
||||
output_lwe_dimension: LweDimension,
|
||||
output_cm_dimension: CmDimension,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for CmLwePackingKey<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for CmLwePackingKey<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_packing_key_size(
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_dimension: LweDimension,
|
||||
output_lwe_dimension: LweDimension,
|
||||
output_cm_dimension: CmDimension,
|
||||
) -> usize {
|
||||
output_cm_dimension.0
|
||||
* cm_lwe_packing_key_part_size(
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
)
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmLwePackingKey<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_dimension: LweDimension,
|
||||
output_lwe_dimension: LweDimension,
|
||||
output_cm_dimension: CmDimension,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
let expected_len = cm_lwe_packing_key_size(
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
);
|
||||
assert_eq!(
|
||||
container.container_len(),
|
||||
expected_len,
|
||||
"The provided container length is not valid. \
|
||||
Expected decomp_level_count * (output_lwe_dimension + output_cm_dimension) * output_cm_dimension * input_lwe_dimension: {}. \
|
||||
Got container length: {} and decomp_level_count: {decomp_level_count:?}, \
|
||||
output_lwe_dimension + output_cm_dimension: {}, input_lwe_dimension: {input_lwe_dimension:?}.",
|
||||
expected_len,
|
||||
container.container_len(),
|
||||
output_lwe_dimension.0 + output_cm_dimension.0,
|
||||
);
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomp_level_count
|
||||
}
|
||||
|
||||
pub fn input_key_lwe_dimension(&self) -> LweDimension {
|
||||
self.input_lwe_dimension
|
||||
}
|
||||
|
||||
pub fn output_lwe_dimension(&self) -> LweDimension {
|
||||
self.output_lwe_dimension
|
||||
}
|
||||
|
||||
pub fn output_cm_dimension(&self) -> CmDimension {
|
||||
self.output_cm_dimension
|
||||
}
|
||||
|
||||
pub fn key_part_size(&self) -> usize {
|
||||
let Self {
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
..
|
||||
} = *self;
|
||||
cm_lwe_packing_key_part_size(
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> CmLwePackingKeyView<'_, Scalar> {
|
||||
let Self {
|
||||
data: _,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
} = *self;
|
||||
CmLwePackingKey::from_container(
|
||||
self.as_ref(),
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn as_cm_lwe_ciphertext_list(&self) -> CmLweCiphertextListView<'_, Scalar> {
|
||||
CmLweCiphertextListView::from_container(
|
||||
self.as_ref(),
|
||||
self.output_lwe_dimension(),
|
||||
self.output_cm_dimension,
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmLwePackingKey<C> {
|
||||
pub fn as_mut_view(&mut self) -> CmLwePackingKeyMutView<'_, Scalar> {
|
||||
let Self {
|
||||
data: _,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
} = *self;
|
||||
CmLwePackingKey::from_container(
|
||||
self.as_mut(),
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_mut_cm_lwe_ciphertext_list(&mut self) -> CmLweCiphertextListMutView<'_, Scalar> {
|
||||
let Self {
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
..
|
||||
} = *self;
|
||||
CmLweCiphertextListMutView::from_container(
|
||||
self.as_mut(),
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmLwePackingKeyOwned<Scalar> = CmLwePackingKey<Vec<Scalar>>;
|
||||
|
||||
pub type CmLwePackingKeyView<'data, Scalar> = CmLwePackingKey<&'data [Scalar]>;
|
||||
|
||||
pub type CmLwePackingKeyMutView<'data, Scalar> = CmLwePackingKey<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmLwePackingKeyOwned<Scalar> {
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_key_lwe_dimension: LweDimension,
|
||||
output_lwe_dimension: LweDimension,
|
||||
output_cm_dimension: CmDimension,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
let expected_len = cm_lwe_packing_key_size(
|
||||
decomp_level_count,
|
||||
input_key_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
);
|
||||
Self::from_container(
|
||||
vec![fill_with; expected_len],
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_key_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmLwePackingKeyCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub decomp_base_log: DecompositionBaseLog,
|
||||
pub decomp_level_count: DecompositionLevelCount,
|
||||
pub input_lwe_dimension: LweDimension,
|
||||
pub output_lwe_dimension: LweDimension,
|
||||
pub output_cm_dimension: CmDimension,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C> for CmLwePackingKey<C> {
|
||||
type Metadata = CmLwePackingKeyCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmLwePackingKeyCreationMetadata {
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for CmLwePackingKey<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = CmLwePackingKeyPartCreationMetadata<Self::Element>;
|
||||
|
||||
type EntityView<'this>
|
||||
= CmLwePackingKeyPartView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = CmLwePackingKeyCreationMetadata<Self::Element>;
|
||||
|
||||
type SelfView<'this>
|
||||
= CmLwePackingKeyView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
let Self {
|
||||
data: _,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
} = *self;
|
||||
CmLwePackingKeyPartCreationMetadata {
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.key_part_size()
|
||||
}
|
||||
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
let Self {
|
||||
data: _,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
} = *self;
|
||||
CmLwePackingKeyCreationMetadata {
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for CmLwePackingKey<C>
|
||||
{
|
||||
type EntityMutView<'this>
|
||||
= CmLwePackingKeyPartMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this>
|
||||
= CmLwePackingKeyMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
//! Module containing the definition of the [`CmLwePackingKeyPart`].
|
||||
|
||||
use super::cm_lwe_ciphertext_list::{
|
||||
CmLweCiphertextListCreationMetadata, CmLweCiphertextListMutView, CmLweCiphertextListView,
|
||||
};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::experimental::prelude::CmDimension;
|
||||
|
||||
/// The i-th part of a [`CmLwePackingKey`](super::cm_lwe_packing_key::CmLwePackingKey): a keyswitch
|
||||
/// key that converts `lwe(m_i)` into `cm_lwe(0, ..., 0, m_i, 0, ..., 0)` where `m_i` is at position
|
||||
/// `i`.
|
||||
///
|
||||
/// Concretely it key-switches each of the `n = input_lwe_dimension` mask elements of the input
|
||||
/// ciphertext into a CM-LWE ciphertext. Each mask element contributes `L` CM-LWE ciphertexts
|
||||
/// (one per decomposition level), each of size `k + d`.
|
||||
///
|
||||
/// Memory layout (`n` = `input_lwe_dimension`, `L` = `decomp_level_count`,
|
||||
/// `k` = `output_lwe_dimension`, `d` = `output_cm_dimension`):
|
||||
/// `block[0] | block[1] | ... | block[n-1]`, each of size `L * (k + d)`.
|
||||
/// Total size: `n * L * (k + d)`.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CmLwePackingKeyPart<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_dimension: LweDimension,
|
||||
output_lwe_dimension: LweDimension,
|
||||
output_cm_dimension: CmDimension,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for CmLwePackingKeyPart<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for CmLwePackingKeyPart<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cm_lwe_packing_key_part_size(
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_dimension: LweDimension,
|
||||
output_lwe_dimension: LweDimension,
|
||||
output_cm_dimension: CmDimension,
|
||||
) -> usize {
|
||||
input_lwe_dimension.0
|
||||
* cm_lwe_packing_key_part_input_key_element_encrypted_size(
|
||||
decomp_level_count,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cm_lwe_packing_key_part_input_key_element_encrypted_size(
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_lwe_dimension: LweDimension,
|
||||
output_cm_dimension: CmDimension,
|
||||
) -> usize {
|
||||
// One ciphertext per level encrypted under the output key
|
||||
decomp_level_count.0 * (output_lwe_dimension.0 + output_cm_dimension.0)
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CmLwePackingKeyPart<C> {
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_dimension: LweDimension,
|
||||
output_lwe_dimension: LweDimension,
|
||||
output_cm_dimension: CmDimension,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
let expected_len = cm_lwe_packing_key_part_size(
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
);
|
||||
assert_eq!(
|
||||
container.container_len(),
|
||||
expected_len,
|
||||
"The provided container length is not valid. \
|
||||
Expected decomp_level_count * (output_lwe_dimension + output_cm_dimension) * input_lwe_dimension: {}. \
|
||||
Got container length: {} and decomp_level_count: {decomp_level_count:?}, \
|
||||
output_lwe_dimension + output_cm_dimension: {}, input_lwe_dimension: {input_lwe_dimension:?}.",
|
||||
expected_len,
|
||||
container.container_len(),
|
||||
output_lwe_dimension.0 + output_cm_dimension.0,
|
||||
);
|
||||
|
||||
Self {
|
||||
data: container,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomp_level_count
|
||||
}
|
||||
|
||||
pub fn input_key_lwe_dimension(&self) -> LweDimension {
|
||||
self.input_lwe_dimension
|
||||
}
|
||||
|
||||
pub fn output_lwe_dimension(&self) -> LweDimension {
|
||||
self.output_lwe_dimension
|
||||
}
|
||||
|
||||
pub fn output_cm_dimension(&self) -> CmDimension {
|
||||
self.output_cm_dimension
|
||||
}
|
||||
|
||||
pub fn input_key_element_encrypted_size(&self) -> usize {
|
||||
cm_lwe_packing_key_part_input_key_element_encrypted_size(
|
||||
self.decomp_level_count,
|
||||
self.output_lwe_dimension,
|
||||
self.output_cm_dimension,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> CmLwePackingKeyPartView<'_, Scalar> {
|
||||
CmLwePackingKeyPart::from_container(
|
||||
self.as_ref(),
|
||||
self.decomp_base_log,
|
||||
self.decomp_level_count,
|
||||
self.input_lwe_dimension,
|
||||
self.output_lwe_dimension,
|
||||
self.output_cm_dimension,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn as_cm_lwe_ciphertext_list(&self) -> CmLweCiphertextListView<'_, Scalar> {
|
||||
CmLweCiphertextListView::from_container(
|
||||
self.as_ref(),
|
||||
self.output_lwe_dimension(),
|
||||
self.output_cm_dimension,
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> CmLwePackingKeyPart<C> {
|
||||
pub fn as_mut_view(&mut self) -> CmLwePackingKeyPartMutView<'_, Scalar> {
|
||||
let Self {
|
||||
data: _,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
} = *self;
|
||||
|
||||
CmLwePackingKeyPart::from_container(
|
||||
self.as_mut(),
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_mut_cm_lwe_ciphertext_list(&mut self) -> CmLweCiphertextListMutView<'_, Scalar> {
|
||||
let Self {
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
..
|
||||
} = *self;
|
||||
|
||||
CmLweCiphertextListMutView::from_container(
|
||||
self.as_mut(),
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmLwePackingKeyPartOwned<Scalar> = CmLwePackingKeyPart<Vec<Scalar>>;
|
||||
|
||||
pub type CmLwePackingKeyPartView<'data, Scalar> = CmLwePackingKeyPart<&'data [Scalar]>;
|
||||
|
||||
pub type CmLwePackingKeyPartMutView<'data, Scalar> = CmLwePackingKeyPart<&'data mut [Scalar]>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> CmLwePackingKeyPartOwned<Scalar> {
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_key_lwe_dimension: LweDimension,
|
||||
output_lwe_dimension: LweDimension,
|
||||
output_cm_dimension: CmDimension,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
let expected_len = cm_lwe_packing_key_part_size(
|
||||
decomp_level_count,
|
||||
input_key_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
);
|
||||
Self::from_container(
|
||||
vec![fill_with; expected_len],
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_key_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CmLwePackingKeyPartCreationMetadata<Scalar: UnsignedInteger> {
|
||||
pub decomp_base_log: DecompositionBaseLog,
|
||||
pub decomp_level_count: DecompositionLevelCount,
|
||||
pub input_lwe_dimension: LweDimension,
|
||||
pub output_lwe_dimension: LweDimension,
|
||||
pub output_cm_dimension: CmDimension,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for CmLwePackingKeyPart<C>
|
||||
{
|
||||
type Metadata = CmLwePackingKeyPartCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let CmLwePackingKeyPartCreationMetadata {
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
} = meta;
|
||||
Self::from_container(
|
||||
from,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for CmLwePackingKeyPart<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = CmLweCiphertextListCreationMetadata<Self::Element>;
|
||||
|
||||
type EntityView<'this>
|
||||
= CmLweCiphertextListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = CmLwePackingKeyPartCreationMetadata<Self::Element>;
|
||||
|
||||
type SelfView<'this>
|
||||
= CmLwePackingKeyPartView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
CmLweCiphertextListCreationMetadata {
|
||||
lwe_dimension: self.output_lwe_dimension(),
|
||||
cm_dimension: self.output_cm_dimension,
|
||||
ciphertext_modulus: self.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.input_key_element_encrypted_size()
|
||||
}
|
||||
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
let Self {
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
..
|
||||
} = *self;
|
||||
CmLwePackingKeyPartCreationMetadata {
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
output_lwe_dimension,
|
||||
output_cm_dimension,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for CmLwePackingKeyPart<C>
|
||||
{
|
||||
type EntityMutView<'this>
|
||||
= CmLweCiphertextListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this>
|
||||
= CmLwePackingKeyPartMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
pub mod cm_bootstrap_key;
|
||||
pub mod cm_fourier_ggsw_ciphertext;
|
||||
pub mod cm_ggsw_ciphertext;
|
||||
pub mod cm_ggsw_ciphertext_list;
|
||||
pub mod cm_glwe_ciphertext;
|
||||
pub mod cm_glwe_ciphertext_list;
|
||||
pub mod cm_lwe_bootstrap_key;
|
||||
pub mod cm_lwe_ciphertext;
|
||||
pub mod cm_lwe_ciphertext_list;
|
||||
pub mod cm_lwe_keyswitch_key;
|
||||
pub mod cm_lwe_packing_key;
|
||||
pub mod cm_lwe_packing_key_part;
|
||||
|
||||
pub use cm_bootstrap_key::*;
|
||||
pub use cm_fourier_ggsw_ciphertext::*;
|
||||
pub use cm_ggsw_ciphertext::*;
|
||||
pub use cm_ggsw_ciphertext_list::*;
|
||||
pub use cm_glwe_ciphertext::*;
|
||||
pub use cm_glwe_ciphertext_list::*;
|
||||
pub use cm_lwe_bootstrap_key::*;
|
||||
pub use cm_lwe_ciphertext::*;
|
||||
pub use cm_lwe_ciphertext_list::*;
|
||||
pub use cm_lwe_keyswitch_key::*;
|
||||
pub use cm_lwe_packing_key::*;
|
||||
pub use cm_lwe_packing_key_part::*;
|
||||
@@ -11,7 +11,7 @@ use crate::core_crypto::fft_impl::fft64::math::decomposition::DecompositionLevel
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::{FftView, FourierPolynomialList};
|
||||
use crate::core_crypto::fft_impl::fft64::math::polynomial::FourierPolynomialMutView;
|
||||
use aligned_vec::{avec, ABox};
|
||||
use dyn_stack::{PodStack, StackReq};
|
||||
use dyn_stack::PodStack;
|
||||
use tfhe_fft::c64;
|
||||
|
||||
/// A pseudo GGSW ciphertext in the Fourier domain.
|
||||
@@ -260,12 +260,6 @@ impl<'a> PseudoFourierGgswCiphertextView<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the required memory for
|
||||
/// [`PseudoFourierGgswCiphertextMutView::fill_with_forward_fourier`].
|
||||
pub fn fill_with_forward_fourier_scratch(fft: FftView<'_>) -> StackReq {
|
||||
fft.forward_scratch()
|
||||
}
|
||||
|
||||
impl PseudoFourierGgswCiphertextMutView<'_> {
|
||||
/// Fill a GGSW ciphertext with the Fourier transform of a GGSW ciphertext in the standard
|
||||
/// domain.
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
pub mod common_mask_entities;
|
||||
pub mod fourier_pseudo_ggsw_ciphertext;
|
||||
pub mod lwe_shrinking_keyswitch_key;
|
||||
pub mod pseudo_ggsw_ciphertext;
|
||||
|
||||
pub use common_mask_entities::*;
|
||||
pub use fourier_pseudo_ggsw_ciphertext::*;
|
||||
pub use lwe_shrinking_keyswitch_key::*;
|
||||
pub use pseudo_ggsw_ciphertext::*;
|
||||
|
||||
@@ -108,14 +108,14 @@ fn plans() -> &'static PlanMap {
|
||||
}
|
||||
|
||||
pub fn setup_custom_fft_plan(plan: Plan) {
|
||||
let fft_size = FourierPolynomialSize(plan.fft_size());
|
||||
let std_poly_size = fft_size.to_standard_polynomial_size();
|
||||
let base_n = FourierPolynomialSize(plan.fft_size());
|
||||
let n = base_n.to_standard_polynomial_size();
|
||||
|
||||
let plan = Arc::new((Twisties::new(fft_size.0), plan));
|
||||
let plan = Arc::new((Twisties::new(base_n.0), plan));
|
||||
|
||||
let global_plans = plans();
|
||||
|
||||
global_plans.set(std_poly_size, plan);
|
||||
global_plans.set(n, plan);
|
||||
}
|
||||
|
||||
/// Return the input slice, cast to the same type.
|
||||
|
||||
@@ -56,7 +56,7 @@ impl<Scalar: UnsignedInteger + CastFrom<u64>> ShortintEncoding<Scalar> {
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger + CastFrom<u64>> ShortintEncoding<Scalar> {
|
||||
/// Return the cleartext space including the space for the [`Self::padding_bit`] if it is set to
|
||||
/// Return the cleatext space including the space for the [`Self::padding_bit`] if it is set to
|
||||
/// [`PaddingBit::Yes`].
|
||||
pub(crate) fn full_cleartext_space(&self) -> Scalar {
|
||||
let cleartext_modulus = self.cleartext_space_without_padding();
|
||||
@@ -69,7 +69,7 @@ impl<Scalar: UnsignedInteger + CastFrom<u64>> ShortintEncoding<Scalar> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the cleartext space defined by the [`Self::message_modulus`] and
|
||||
/// Return the cleatext space defined by the [`Self::message_modulus`] and
|
||||
/// [`Self::carry_modulus`], not taking the value of the [`Self::padding_bit`] into account.
|
||||
pub(crate) fn cleartext_space_without_padding(&self) -> Scalar {
|
||||
(self.message_modulus.0 * self.carry_modulus.0).cast_into()
|
||||
|
||||
@@ -1130,7 +1130,7 @@ pub mod test_utils {
|
||||
/// to 0 to keep the carry free.
|
||||
/// output_modulus: the output cleartext space, continuing the above example, it must contain
|
||||
/// the padding bit, so for 4 bits of cleartext this is actually 2^(1 + 4)==32
|
||||
pub fn cleartext_prf(
|
||||
pub fn cleatext_prf(
|
||||
input_cleartext: u64,
|
||||
random_bits_count: u64,
|
||||
output_modulus: u64,
|
||||
@@ -1163,7 +1163,7 @@ pub mod test_utils {
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
use super::test_utils::cleartext_prf;
|
||||
use super::test_utils::cleatext_prf;
|
||||
use super::*;
|
||||
use crate::core_crypto::commons::math::random::Seed;
|
||||
use crate::core_crypto::prelude::{decrypt_lwe_ciphertext, CastInto, LweSecretKeyView};
|
||||
@@ -1244,7 +1244,7 @@ pub(crate) mod test {
|
||||
|
||||
// includes padding bit
|
||||
let output_modulus = 2 * params.message_modulus().0 * params.carry_modulus().0;
|
||||
let expected_output = cleartext_prf(
|
||||
let expected_output = cleatext_prf(
|
||||
plain_prf_input,
|
||||
random_bits_count,
|
||||
output_modulus,
|
||||
@@ -1477,67 +1477,4 @@ pub(crate) mod test {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oprf_test_uniformity_bits_ci_run_filter() {
|
||||
let sample_count: usize = 100_000;
|
||||
|
||||
let p_value_limit: f64 = 0.000_01;
|
||||
|
||||
use crate::shortint::gen_keys;
|
||||
use crate::shortint::parameters::test_params::{
|
||||
TEST_PARAM_MESSAGE_2_CARRY_2_KS32_PBS_TUNIFORM_2M128,
|
||||
TEST_PARAM_MULTI_BIT_GROUP_3_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M128,
|
||||
};
|
||||
use crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS;
|
||||
|
||||
for params in [
|
||||
ShortintParameterSet::from(
|
||||
TEST_PARAM_MULTI_BIT_GROUP_3_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M128,
|
||||
),
|
||||
ShortintParameterSet::from(PARAM_MESSAGE_2_CARRY_2_KS_PBS),
|
||||
ShortintParameterSet::from(TEST_PARAM_MESSAGE_2_CARRY_2_KS32_PBS_TUNIFORM_2M128),
|
||||
] {
|
||||
let (ck, sk) = gen_keys(params);
|
||||
let oprf_ck = OprfPrivateKey::new(&ck);
|
||||
let oprf_sk = OprfServerKey::new(&oprf_ck, &ck).unwrap();
|
||||
|
||||
let random_bits_per_block = sk.message_modulus.0.ilog2() as u64;
|
||||
|
||||
for random_bits_count in [3u64, 4] {
|
||||
let expected_num_blocks =
|
||||
random_bits_count.div_ceil(random_bits_per_block) as usize;
|
||||
|
||||
test_uniformity(
|
||||
sample_count,
|
||||
p_value_limit,
|
||||
1 << random_bits_count,
|
||||
|seed| {
|
||||
let seed = (seed as u128).to_le_bytes();
|
||||
let blocks = oprf_sk.generate_oblivious_pseudo_random_bits(
|
||||
seed.as_slice(),
|
||||
random_bits_count,
|
||||
&sk,
|
||||
);
|
||||
|
||||
let mut combined: u64 = 0;
|
||||
let mut shift = 0u64;
|
||||
for (i, block) in blocks.iter().enumerate() {
|
||||
let decrypted = ck.decrypt_message_and_carry(block);
|
||||
let block_bits = bits_in_block(
|
||||
i,
|
||||
expected_num_blocks,
|
||||
random_bits_count,
|
||||
random_bits_per_block,
|
||||
);
|
||||
combined |= decrypted << shift;
|
||||
shift += block_bits;
|
||||
}
|
||||
|
||||
combined
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user