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, NoiseRandomGenerator, SecretRandomGenerator,
};
#[cfg(feature = "zk-pok")]
use crate::core_crypto::commons::math::random::BoundedDistribution;
use crate::core_crypto::commons::math::random::{
DefaultRandomGenerator, Distribution, RandomGenerable, RandomGenerator, Uniform, UniformBinary,
};
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
use rayon::prelude::*;
pub fn fill_lwe_mask_and_body_for_encryption<Scalar, NoiseDistribution, KeyCont, OutputCont, Gen>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output_mask: &mut LweMask<OutputCont>,
output_body: &mut LweBodyRefMut<Scalar>,
encoded: Plaintext<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,
{
assert_eq!(
output_mask.ciphertext_modulus(),
output_body.ciphertext_modulus(),
"Mismatched moduli between mask ({:?}) and body ({:?})",
output_mask.ciphertext_modulus(),
output_body.ciphertext_modulus()
);
let ciphertext_modulus = output_mask.ciphertext_modulus();
if ciphertext_modulus.is_compatible_with_native_modulus() {
fill_lwe_mask_and_body_for_encryption_native_mod_compatible(
lwe_secret_key,
output_mask,
output_body,
encoded,
noise_distribution,
generator,
);
} else {
fill_lwe_mask_and_body_for_encryption_other_mod(
lwe_secret_key,
output_mask,
output_body,
encoded,
noise_distribution,
generator,
);
}
}
pub fn fill_lwe_mask_and_body_for_encryption_native_mod_compatible<
Scalar,
NoiseDistribution,
KeyCont,
OutputCont,
Gen,
>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output_mask: &mut LweMask<OutputCont>,
output_body: &mut LweBodyRefMut<Scalar>,
encoded: Plaintext<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,
{
assert_eq!(
output_mask.ciphertext_modulus(),
output_body.ciphertext_modulus(),
"Mismatched moduli between mask ({:?}) and body ({:?})",
output_mask.ciphertext_modulus(),
output_body.ciphertext_modulus()
);
let ciphertext_modulus = output_mask.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);
let noise =
generator.random_noise_from_distribution_custom_mod(noise_distribution, ciphertext_modulus);
let mask_key_dot_product =
slice_wrapping_dot_product(output_mask.as_ref(), lwe_secret_key.as_ref());
*output_body.data = mask_key_dot_product
.wrapping_add(encoded.0)
.wrapping_add(noise);
match ciphertext_modulus.kind() {
CiphertextModulusKind::Native => (),
CiphertextModulusKind::NonNativePowerOfTwo => {
let torus_scaling = ciphertext_modulus.get_power_of_two_scaling_to_native_torus();
slice_wrapping_scalar_mul_assign(output_mask.as_mut(), torus_scaling);
*output_body.data = (*output_body.data).wrapping_mul(torus_scaling);
}
CiphertextModulusKind::Other => unreachable!(),
}
}
pub fn fill_lwe_mask_and_body_for_encryption_other_mod<
Scalar,
NoiseDistribution,
KeyCont,
OutputCont,
Gen,
>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output_mask: &mut LweMask<OutputCont>,
output_body: &mut LweBodyRefMut<Scalar>,
encoded: Plaintext<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,
{
assert_eq!(
output_mask.ciphertext_modulus(),
output_body.ciphertext_modulus(),
"Mismatched moduli between mask ({:?}) and body ({:?})",
output_mask.ciphertext_modulus(),
output_body.ciphertext_modulus()
);
let ciphertext_modulus = output_mask.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);
let noise =
generator.random_noise_from_distribution_custom_mod(noise_distribution, ciphertext_modulus);
let ciphertext_modulus_as_scalar: Scalar = ciphertext_modulus.get_custom_modulus().cast_into();
let mask_key_dot_product = slice_wrapping_dot_product_custom_mod(
output_mask.as_ref(),
lwe_secret_key.as_ref(),
ciphertext_modulus_as_scalar,
);
*output_body.data = mask_key_dot_product
.wrapping_add_custom_mod(encoded.0, ciphertext_modulus_as_scalar)
.wrapping_add_custom_mod(noise, ciphertext_modulus_as_scalar);
}
pub fn encrypt_lwe_ciphertext<Scalar, NoiseDistribution, KeyCont, OutputCont, Gen>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output: &mut LweCiphertext<OutputCont>,
encoded: Plaintext<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,
{
assert!(
output.lwe_size().to_lwe_dimension() == lwe_secret_key.lwe_dimension(),
"Mismatch between LweDimension of output ciphertext and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output.lwe_size().to_lwe_dimension(),
lwe_secret_key.lwe_dimension()
);
let (mut mask, mut body) = output.get_mut_mask_and_body();
fill_lwe_mask_and_body_for_encryption(
lwe_secret_key,
&mut mask,
&mut body,
encoded,
noise_distribution,
generator,
);
}
pub fn allocate_and_encrypt_new_lwe_ciphertext<Scalar, NoiseDistribution, KeyCont, Gen>(
lwe_secret_key: &LweSecretKey<KeyCont>,
encoded: Plaintext<Scalar>,
noise_distribution: NoiseDistribution,
ciphertext_modulus: CiphertextModulus<Scalar>,
generator: &mut EncryptionRandomGenerator<Gen>,
) -> LweCiphertextOwned<Scalar>
where
Scalar: Encryptable<Uniform, NoiseDistribution>,
NoiseDistribution: Distribution,
KeyCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let mut new_ct = LweCiphertextOwned::new(
Scalar::ZERO,
lwe_secret_key.lwe_dimension().to_lwe_size(),
ciphertext_modulus,
);
encrypt_lwe_ciphertext(
lwe_secret_key,
&mut new_ct,
encoded,
noise_distribution,
generator,
);
new_ct
}
pub fn trivially_encrypt_lwe_ciphertext<Scalar, OutputCont>(
output: &mut LweCiphertext<OutputCont>,
encoded: Plaintext<Scalar>,
) where
Scalar: UnsignedTorus,
OutputCont: ContainerMut<Element = Scalar>,
{
output.get_mut_mask().as_mut().fill(Scalar::ZERO);
let output_body = output.get_mut_body();
let ciphertext_modulus = output_body.ciphertext_modulus();
*output_body.data = match ciphertext_modulus.kind() {
CiphertextModulusKind::Native | CiphertextModulusKind::Other => encoded.0,
CiphertextModulusKind::NonNativePowerOfTwo => {
encoded.0 * ciphertext_modulus.get_power_of_two_scaling_to_native_torus()
}
};
}
pub fn allocate_and_trivially_encrypt_new_lwe_ciphertext<Scalar>(
lwe_size: LweSize,
encoded: Plaintext<Scalar>,
ciphertext_modulus: CiphertextModulus<Scalar>,
) -> LweCiphertextOwned<Scalar>
where
Scalar: UnsignedTorus,
{
let mut new_ct = LweCiphertextOwned::new(Scalar::ZERO, lwe_size, ciphertext_modulus);
*new_ct.get_mut_body().data = encoded.0;
let output_body = new_ct.get_mut_body();
let ciphertext_modulus = output_body.ciphertext_modulus();
*output_body.data = match ciphertext_modulus.kind() {
CiphertextModulusKind::Native | CiphertextModulusKind::Other => encoded.0,
CiphertextModulusKind::NonNativePowerOfTwo => {
encoded.0 * ciphertext_modulus.get_power_of_two_scaling_to_native_torus()
}
};
new_ct
}
pub fn decrypt_lwe_ciphertext<Scalar, KeyCont, InputCont>(
lwe_secret_key: &LweSecretKey<KeyCont>,
lwe_ciphertext: &LweCiphertext<InputCont>,
) -> Plaintext<Scalar>
where
Scalar: UnsignedInteger,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
{
let ciphertext_modulus = lwe_ciphertext.ciphertext_modulus();
if ciphertext_modulus.is_compatible_with_native_modulus() {
decrypt_lwe_ciphertext_native_mod_compatible(lwe_secret_key, lwe_ciphertext)
} else {
decrypt_lwe_ciphertext_other_mod(lwe_secret_key, lwe_ciphertext)
}
}
pub fn decrypt_lwe_ciphertext_native_mod_compatible<Scalar, KeyCont, InputCont>(
lwe_secret_key: &LweSecretKey<KeyCont>,
lwe_ciphertext: &LweCiphertext<InputCont>,
) -> Plaintext<Scalar>
where
Scalar: UnsignedInteger,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
{
assert!(
lwe_ciphertext.lwe_size().to_lwe_dimension() == lwe_secret_key.lwe_dimension(),
"Mismatch between LweDimension of output ciphertext and input secret key. \
Got {:?} in output, and {:?} in secret key.",
lwe_ciphertext.lwe_size().to_lwe_dimension(),
lwe_secret_key.lwe_dimension()
);
let ciphertext_modulus = lwe_ciphertext.ciphertext_modulus();
assert!(ciphertext_modulus.is_compatible_with_native_modulus());
let (mask, body) = lwe_ciphertext.get_mask_and_body();
let mask_key_dot_product = slice_wrapping_dot_product(mask.as_ref(), lwe_secret_key.as_ref());
let plaintext = (*body.data).wrapping_sub(mask_key_dot_product);
match ciphertext_modulus.kind() {
CiphertextModulusKind::Native => Plaintext(plaintext),
CiphertextModulusKind::NonNativePowerOfTwo => {
Plaintext(
plaintext
.wrapping_div(ciphertext_modulus.get_power_of_two_scaling_to_native_torus()),
)
}
CiphertextModulusKind::Other => unreachable!(),
}
}
pub fn decrypt_lwe_ciphertext_other_mod<Scalar, KeyCont, InputCont>(
lwe_secret_key: &LweSecretKey<KeyCont>,
lwe_ciphertext: &LweCiphertext<InputCont>,
) -> Plaintext<Scalar>
where
Scalar: UnsignedInteger,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
{
assert!(
lwe_ciphertext.lwe_size().to_lwe_dimension() == lwe_secret_key.lwe_dimension(),
"Mismatch between LweDimension of output ciphertext and input secret key. \
Got {:?} in output, and {:?} in secret key.",
lwe_ciphertext.lwe_size().to_lwe_dimension(),
lwe_secret_key.lwe_dimension()
);
let ciphertext_modulus = lwe_ciphertext.ciphertext_modulus();
assert!(!ciphertext_modulus.is_compatible_with_native_modulus());
let (mask, body) = lwe_ciphertext.get_mask_and_body();
let ciphertext_modulus_as_scalar: Scalar = ciphertext_modulus.get_custom_modulus().cast_into();
Plaintext((*body.data).wrapping_sub_custom_mod(
slice_wrapping_dot_product_custom_mod(
mask.as_ref(),
lwe_secret_key.as_ref(),
ciphertext_modulus_as_scalar,
),
ciphertext_modulus_as_scalar,
))
}
pub fn encrypt_lwe_ciphertext_list<Scalar, NoiseDistribution, KeyCont, OutputCont, InputCont, Gen>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output: &mut LweCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
noise_distribution: NoiseDistribution,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: Encryptable<Uniform, NoiseDistribution>,
NoiseDistribution: Distribution,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
InputCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
output.lwe_ciphertext_count().0 == encoded.plaintext_count().0,
"Mismatch between number of output ciphertexts and input plaintexts. \
Got {:?} plaintexts, and {:?} ciphertext.",
encoded.plaintext_count(),
output.lwe_ciphertext_count()
);
let gen_iter = generator
.try_fork_from_config(output.encryption_fork_config(Uniform, noise_distribution))
.unwrap();
for ((encoded_plaintext_ref, mut ciphertext), mut loop_generator) in
encoded.iter().zip(output.iter_mut()).zip(gen_iter)
{
encrypt_lwe_ciphertext(
lwe_secret_key,
&mut ciphertext,
encoded_plaintext_ref.into(),
noise_distribution,
&mut loop_generator,
);
}
}
pub fn par_encrypt_lwe_ciphertext_list<
Scalar,
NoiseDistribution,
KeyCont,
OutputCont,
InputCont,
Gen,
>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output: &mut LweCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
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>,
InputCont: Container<Element = Scalar>,
Gen: ParallelByteRandomGenerator,
{
assert!(
output.lwe_ciphertext_count().0 == encoded.plaintext_count().0,
"Mismatch between number of output ciphertexts and input plaintexts. \
Got {:?} plaintexts, and {:?} ciphertext.",
encoded.plaintext_count(),
output.lwe_ciphertext_count()
);
let gen_iter = generator
.par_try_fork_from_config(output.encryption_fork_config(Uniform, noise_distribution))
.unwrap();
encoded
.par_iter()
.zip(output.par_iter_mut())
.zip(gen_iter)
.for_each(|((encoded_plaintext_ref, mut ciphertext), mut generator)| {
encrypt_lwe_ciphertext(
lwe_secret_key,
&mut ciphertext,
encoded_plaintext_ref.into(),
noise_distribution,
&mut generator,
);
});
}
pub fn decrypt_lwe_ciphertext_list<Scalar, KeyCont, InputCont, OutputCont>(
lwe_secret_key: &LweSecretKey<KeyCont>,
input_lwe_ciphertext_list: &LweCiphertextList<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_lwe_ciphertext_list.lwe_ciphertext_count().0,
"Mismatched output PlaintextCount {:?} and input LweCiphertextCount ({:?}).",
output_plaintext_list.plaintext_count(),
input_lwe_ciphertext_list.lwe_ciphertext_count(),
);
for (ciphertext, output_plaintext) in input_lwe_ciphertext_list
.iter()
.zip(output_plaintext_list.iter_mut())
{
*output_plaintext.0 = decrypt_lwe_ciphertext(lwe_secret_key, &ciphertext).0;
}
}
pub fn encrypt_lwe_ciphertext_with_public_key<Scalar, KeyCont, OutputCont, Gen>(
lwe_public_key: &LwePublicKey<KeyCont>,
output: &mut LweCiphertext<OutputCont>,
encoded: Plaintext<Scalar>,
generator: &mut SecretRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert_eq!(
lwe_public_key.ciphertext_modulus(),
output.ciphertext_modulus(),
"Mismatched moduli between lwe_public_key ({:?}) and output ({:?})",
lwe_public_key.ciphertext_modulus(),
output.ciphertext_modulus()
);
assert!(
output.lwe_size().to_lwe_dimension() == lwe_public_key.lwe_size().to_lwe_dimension(),
"Mismatch between LweDimension of output ciphertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.lwe_size().to_lwe_dimension(),
lwe_public_key.lwe_size().to_lwe_dimension()
);
output.as_mut().fill(Scalar::ZERO);
let mut tmp_zero_encryption =
LweCiphertext::new(Scalar::ZERO, output.lwe_size(), output.ciphertext_modulus());
let mut ct_choice = vec![Scalar::ZERO; lwe_public_key.zero_encryption_count().0];
generator.fill_slice_with_random_uniform_binary(&mut ct_choice);
for (&chosen, public_encryption_of_zero) in ct_choice.iter().zip(lwe_public_key.iter()) {
lwe_ciphertext_cleartext_mul(
&mut tmp_zero_encryption,
&public_encryption_of_zero,
Cleartext(chosen),
);
lwe_ciphertext_add_assign(output, &tmp_zero_encryption);
}
lwe_ciphertext_plaintext_add_assign(output, encoded);
}
pub fn encrypt_lwe_ciphertext_with_seeded_public_key<Scalar, KeyCont, OutputCont, Gen>(
lwe_public_key: &SeededLwePublicKey<KeyCont>,
output: &mut LweCiphertext<OutputCont>,
encoded: Plaintext<Scalar>,
generator: &mut SecretRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert_eq!(
lwe_public_key.ciphertext_modulus(),
output.ciphertext_modulus(),
"Mismatched moduli between lwe_public_key ({:?}) and output ({:?})",
lwe_public_key.ciphertext_modulus(),
output.ciphertext_modulus()
);
assert!(
output.lwe_size().to_lwe_dimension() == lwe_public_key.lwe_size().to_lwe_dimension(),
"Mismatch between LweDimension of output ciphertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.lwe_size().to_lwe_dimension(),
lwe_public_key.lwe_size().to_lwe_dimension()
);
encrypt_lwe_ciphertext_iterator_with_seeded_public_key(
lwe_public_key,
std::iter::once(output.as_mut_view()),
std::iter::once(encoded),
generator,
);
}
pub fn encrypt_lwe_ciphertext_iterator_with_seeded_public_key<Scalar, KeyCont, OutputCont, Gen>(
lwe_public_key: &SeededLwePublicKey<KeyCont>,
output: impl IntoIterator<Item = LweCiphertext<OutputCont>>,
encoded: impl IntoIterator<Item = Plaintext<Scalar>>,
generator: &mut SecretRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let mut output: Vec<_> = output.into_iter().collect();
let output = output.as_mut_slice();
if output.is_empty() {
return;
}
let encoded: Vec<_> = encoded.into_iter().collect();
assert_eq!(
output.len(),
encoded.len(),
"Mismatched Plaintext Iterator and LweCiphertext Iterator lengths."
);
let output_ciphertext_modulus = output[0].ciphertext_modulus();
assert!(
output
.iter()
.all(|lwe| lwe.ciphertext_modulus() == output_ciphertext_modulus),
"The input LweCiphertext Iterator must have homogeneous CiphertextModulus"
);
assert_eq!(
lwe_public_key.ciphertext_modulus(),
output_ciphertext_modulus,
"Mismatched moduli between lwe_public_key ({:?}) and output ({:?})",
lwe_public_key.ciphertext_modulus(),
output_ciphertext_modulus
);
let output_lwe_size = output[0].lwe_size();
assert!(
output.iter().all(|lwe| lwe.lwe_size() == output_lwe_size),
"The input LweCiphertext Iterator must have homogeneous LweSize"
);
assert!(
output_lwe_size.to_lwe_dimension() == lwe_public_key.lwe_size().to_lwe_dimension(),
"Mismatch between LweDimension of output ciphertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output_lwe_size.to_lwe_dimension(),
lwe_public_key.lwe_size().to_lwe_dimension()
);
for output_ct in output.iter_mut() {
output_ct.as_mut().fill(Scalar::ZERO);
}
let mut tmp_zero_encryption = LweCiphertext::new(
Scalar::ZERO,
lwe_public_key.lwe_size(),
output_ciphertext_modulus,
);
let mut random_generator =
RandomGenerator::<DefaultRandomGenerator>::new(lwe_public_key.compression_seed());
for public_encryption_of_zero_body in lwe_public_key.iter() {
let (mut mask, body) = tmp_zero_encryption.get_mut_mask_and_body();
random_generator
.fill_slice_with_random_uniform_custom_mod(mask.as_mut(), output_ciphertext_modulus);
if output_ciphertext_modulus.is_non_native_power_of_two() {
slice_wrapping_scalar_mul_assign(
mask.as_mut(),
output_ciphertext_modulus.get_power_of_two_scaling_to_native_torus(),
);
}
*body.data = *public_encryption_of_zero_body.data;
for output_ct in output.iter_mut() {
let chosen = generator.generate_random_uniform_binary();
slice_wrapping_add_scalar_mul_assign(
output_ct.as_mut(),
tmp_zero_encryption.as_ref(),
chosen,
);
}
}
for (output_ct, plaintext) in output.iter_mut().zip(encoded.into_iter()) {
lwe_ciphertext_plaintext_add_assign(output_ct, plaintext);
}
}
pub fn encrypt_seeded_lwe_ciphertext_list_with_pre_seeded_generator<
Scalar,
NoiseDistribution,
KeyCont,
OutputCont,
InputCont,
Gen,
>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output: &mut SeededLweCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
noise_distribution: NoiseDistribution,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: Encryptable<Uniform, NoiseDistribution>,
NoiseDistribution: Distribution,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
InputCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
assert!(
output.lwe_size().to_lwe_dimension() == lwe_secret_key.lwe_dimension(),
"Mismatched LweDimension between input LweSecretKey {:?} and output \
SeededLweCiphertextList {:?}.",
lwe_secret_key.lwe_dimension(),
output.lwe_size().to_lwe_dimension(),
);
assert!(
output.lwe_ciphertext_count().0 == encoded.plaintext_count().0,
"Mismatch between number of output ciphertexts and input plaintexts. \
Got {:?} plaintexts, and {:?} ciphertext.",
encoded.plaintext_count(),
output.lwe_ciphertext_count()
);
let mut output_mask = LweMask::from_container(
vec![Scalar::ZERO; output.lwe_size().to_lwe_dimension().0],
output.ciphertext_modulus(),
);
let gen_iter = generator
.try_fork_from_config(output.encryption_fork_config(Uniform, noise_distribution))
.unwrap();
for ((mut output_body, plaintext), mut loop_generator) in
output.iter_mut().zip(encoded.iter()).zip(gen_iter)
{
fill_lwe_mask_and_body_for_encryption(
lwe_secret_key,
&mut output_mask,
&mut output_body,
plaintext.into(),
noise_distribution,
&mut loop_generator,
);
}
}
pub fn encrypt_seeded_lwe_ciphertext_list<
Scalar,
NoiseDistribution,
KeyCont,
OutputCont,
InputCont,
NoiseSeeder,
>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output: &mut SeededLweCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
noise_distribution: NoiseDistribution,
noise_seeder: &mut NoiseSeeder,
) where
Scalar: Encryptable<Uniform, NoiseDistribution>,
NoiseDistribution: Distribution,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
InputCont: Container<Element = Scalar>,
NoiseSeeder: Seeder + ?Sized,
{
let mut generator = EncryptionRandomGenerator::<DefaultRandomGenerator>::new(
output.compression_seed(),
noise_seeder,
);
encrypt_seeded_lwe_ciphertext_list_with_pre_seeded_generator(
lwe_secret_key,
output,
encoded,
noise_distribution,
&mut generator,
);
}
pub fn par_encrypt_seeded_lwe_ciphertext_list_with_pre_seeded_generator<
Scalar,
NoiseDistribution,
KeyCont,
OutputCont,
InputCont,
Gen,
>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output: &mut SeededLweCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
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> + Sync,
InputCont: Container<Element = Scalar>,
Gen: ParallelByteRandomGenerator,
{
assert!(
output.lwe_size().to_lwe_dimension() == lwe_secret_key.lwe_dimension(),
"Mismatched LweDimension between input LweSecretKey {:?} and output \
SeededLweCiphertextList {:?}.",
lwe_secret_key.lwe_dimension(),
output.lwe_size().to_lwe_dimension(),
);
assert!(
output.lwe_ciphertext_count().0 == encoded.plaintext_count().0,
"Mismatch between number of output ciphertexts and input plaintexts. \
Got {:?} plaintexts, and {:?} ciphertext.",
encoded.plaintext_count(),
output.lwe_ciphertext_count()
);
let gen_iter = generator
.par_try_fork_from_config(output.encryption_fork_config(Uniform, noise_distribution))
.unwrap();
let lwe_dimension = output.lwe_size().to_lwe_dimension();
let ciphertext_modulus = output.ciphertext_modulus();
output
.par_iter_mut()
.zip(encoded.par_iter())
.zip(gen_iter)
.for_each(|((mut output_body, plaintext), mut loop_generator)| {
let mut output_mask =
LweMask::from_container(vec![Scalar::ZERO; lwe_dimension.0], ciphertext_modulus);
fill_lwe_mask_and_body_for_encryption(
lwe_secret_key,
&mut output_mask,
&mut output_body,
plaintext.into(),
noise_distribution,
&mut loop_generator,
);
});
}
pub fn par_encrypt_seeded_lwe_ciphertext_list<
Scalar,
NoiseDistribution,
KeyCont,
OutputCont,
InputCont,
NoiseSeeder,
>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output: &mut SeededLweCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
noise_distribution: NoiseDistribution,
noise_seeder: &mut NoiseSeeder,
) where
Scalar: Encryptable<Uniform, NoiseDistribution> + Sync + Send,
NoiseDistribution: Distribution + Sync,
KeyCont: Container<Element = Scalar> + Sync,
OutputCont: ContainerMut<Element = Scalar> + Sync,
InputCont: Container<Element = Scalar>,
NoiseSeeder: Seeder + ?Sized,
{
let mut generator = EncryptionRandomGenerator::<DefaultRandomGenerator>::new(
output.compression_seed(),
noise_seeder,
);
par_encrypt_seeded_lwe_ciphertext_list_with_pre_seeded_generator(
lwe_secret_key,
output,
encoded,
noise_distribution,
&mut generator,
);
}
pub fn encrypt_seeded_lwe_ciphertext_with_pre_seeded_generator<
Scalar,
NoiseDistribution,
KeyCont,
Gen,
>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output: &mut SeededLweCiphertext<Scalar>,
encoded: Plaintext<Scalar>,
noise_distribution: NoiseDistribution,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: Encryptable<Uniform, NoiseDistribution>,
NoiseDistribution: Distribution,
KeyCont: Container<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let mut mask = LweMask::from_container(
vec![Scalar::ZERO; lwe_secret_key.lwe_dimension().0],
output.ciphertext_modulus(),
);
fill_lwe_mask_and_body_for_encryption(
lwe_secret_key,
&mut mask,
&mut output.get_mut_body(),
encoded,
noise_distribution,
generator,
);
}
pub fn encrypt_seeded_lwe_ciphertext<Scalar, NoiseDistribution, KeyCont, NoiseSeeder>(
lwe_secret_key: &LweSecretKey<KeyCont>,
output: &mut SeededLweCiphertext<Scalar>,
encoded: Plaintext<Scalar>,
noise_distribution: NoiseDistribution,
noise_seeder: &mut NoiseSeeder,
) where
Scalar: Encryptable<Uniform, NoiseDistribution>,
NoiseDistribution: Distribution,
KeyCont: Container<Element = Scalar>,
NoiseSeeder: Seeder + ?Sized,
{
let mut encryption_generator = EncryptionRandomGenerator::<DefaultRandomGenerator>::new(
output.compression_seed(),
noise_seeder,
);
encrypt_seeded_lwe_ciphertext_with_pre_seeded_generator(
lwe_secret_key,
output,
encoded,
noise_distribution,
&mut encryption_generator,
);
}
pub fn allocate_and_encrypt_new_seeded_lwe_ciphertext<
Scalar,
NoiseDistribution,
KeyCont,
NoiseSeeder,
>(
lwe_secret_key: &LweSecretKey<KeyCont>,
encoded: Plaintext<Scalar>,
noise_distribution: NoiseDistribution,
ciphertext_modulus: CiphertextModulus<Scalar>,
noise_seeder: &mut NoiseSeeder,
) -> SeededLweCiphertext<Scalar>
where
Scalar: Encryptable<Uniform, NoiseDistribution>,
NoiseDistribution: Distribution,
KeyCont: Container<Element = Scalar>,
NoiseSeeder: Seeder + ?Sized,
{
let mut seeded_ct = SeededLweCiphertext::new(
Scalar::ZERO,
lwe_secret_key.lwe_dimension().to_lwe_size(),
noise_seeder.seed().into(),
ciphertext_modulus,
);
encrypt_seeded_lwe_ciphertext(
lwe_secret_key,
&mut seeded_ct,
encoded,
noise_distribution,
noise_seeder,
);
seeded_ct
}
struct CompactPublicKeyRandomVectors<Scalar> {
#[cfg_attr(not(feature = "zk-pok"), allow(unused))]
binary_random_vector: Vec<Scalar>,
#[cfg_attr(not(feature = "zk-pok"), allow(unused))]
mask_noise: Vec<Scalar>,
#[cfg_attr(not(feature = "zk-pok"), allow(unused))]
body_noise: Vec<Scalar>,
}
#[cfg(feature = "zk-pok")]
fn verify_zero_knowledge_preconditions<Scalar, KeyCont, MaskDistribution, BodyDistribution>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
ciphertext_count: LweCiphertextCount,
ciphertext_modulus: CiphertextModulus<Scalar>,
delta: Scalar,
mask_noise_distribution: MaskDistribution,
body_noise_distribution: BodyDistribution,
crs: &CompactPkeCrs,
) -> crate::Result<()>
where
Scalar: UnsignedInteger + CastFrom<u64>,
Scalar::Signed: CastFrom<u64>,
i64: CastFrom<Scalar>,
u64: CastFrom<Scalar> + CastInto<Scalar::Signed>,
MaskDistribution: BoundedDistribution<Scalar::Signed>,
BodyDistribution: BoundedDistribution<Scalar::Signed>,
KeyCont: Container<Element = Scalar>,
{
let exclusive_max = crs.exclusive_max_noise();
if mask_noise_distribution.contains(exclusive_max.cast_into()) {
return Err(
"The given random distribution would create random values out \
of the expected bounds of given to the CRS"
.into(),
);
}
if body_noise_distribution.contains(exclusive_max.cast_into()) {
return Err(
"The given random distribution would create random values out \
of the expected bounds of given to the CRS"
.into(),
);
}
if !ciphertext_modulus.is_native_modulus() {
return Err("This operation only supports native modulus".into());
}
if Scalar::BITS > 64 {
return Err("Zero knowledge proof do not support moduli greater than 2**64".into());
}
if ciphertext_modulus != crs.ciphertext_modulus() {
return Err("Mismatched modulus between CRS and ciphertexts".into());
}
if ciphertext_count > crs.max_num_messages() {
return Err(format!(
"CRS allows at most {} ciphertexts to be proven at once, {} contained in the list",
crs.max_num_messages().0,
ciphertext_count.0
)
.into());
}
if lwe_compact_public_key.lwe_dimension() > crs.lwe_dimension() {
return Err(format!(
"CRS allows a LweDimension of at most {}, current dimension: {}",
crs.lwe_dimension().0,
lwe_compact_public_key.lwe_dimension().0
)
.into());
}
let plaintext_modulus = ((1u64 << (u64::BITS - 1) as usize) / u64::cast_from(delta)) * 2;
if plaintext_modulus != crs.plaintext_modulus() {
return Err(format!(
"Mismatched plaintext modulus: CRS expects {}, requested modulus: {plaintext_modulus:?}",
crs.plaintext_modulus()
).into());
}
Ok(())
}
fn encrypt_lwe_ciphertext_with_compact_public_key_impl<
Scalar,
KeyCont,
OutputCont,
MaskDistribution,
NoiseDistribution,
EncryptionGen,
>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
output: &mut LweCiphertext<OutputCont>,
encoded: Plaintext<Scalar>,
mask_noise_distribution: MaskDistribution,
body_noise_distribution: NoiseDistribution,
noise_generator: &mut NoiseRandomGenerator<EncryptionGen>,
) -> CompactPublicKeyRandomVectors<Scalar>
where
Scalar: Encryptable<MaskDistribution, NoiseDistribution> + RandomGenerable<UniformBinary>,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
MaskDistribution: Distribution,
NoiseDistribution: Distribution,
EncryptionGen: ByteRandomGenerator,
{
assert!(
output.lwe_size().to_lwe_dimension() == lwe_compact_public_key.lwe_dimension(),
"Mismatch between LweDimension of output ciphertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.lwe_size().to_lwe_dimension(),
lwe_compact_public_key.lwe_dimension()
);
assert!(
lwe_compact_public_key.ciphertext_modulus() == output.ciphertext_modulus(),
"Mismatch between CiphertextModulus of output ciphertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.ciphertext_modulus(),
lwe_compact_public_key.ciphertext_modulus()
);
assert!(
output.ciphertext_modulus().is_native_modulus(),
"This operation only supports native moduli"
);
let mut binary_random_vector = vec![Scalar::ZERO; lwe_compact_public_key.lwe_dimension().0];
noise_generator.fill_slice_with_random_uniform_binary_bits(&mut binary_random_vector);
let mut mask_noise = vec![Scalar::ZERO; lwe_compact_public_key.lwe_dimension().0];
noise_generator
.fill_slice_with_random_noise_from_distribution(&mut mask_noise, mask_noise_distribution);
let body_noise = noise_generator.random_noise_from_distribution(body_noise_distribution);
{
let (mut ct_mask, ct_body) = output.get_mut_mask_and_body();
let (pk_mask, pk_body) = lwe_compact_public_key.get_mask_and_body();
{
slice_semi_reverse_negacyclic_convolution(
ct_mask.as_mut(),
pk_mask.as_ref(),
&binary_random_vector,
);
slice_wrapping_add_assign(ct_mask.as_mut(), mask_noise.as_slice());
}
{
*ct_body.data = slice_wrapping_dot_product(pk_body.as_ref(), &binary_random_vector);
*ct_body.data = (*ct_body.data).wrapping_add(body_noise);
*ct_body.data = (*ct_body.data).wrapping_add(encoded.0);
}
}
CompactPublicKeyRandomVectors {
binary_random_vector,
mask_noise,
body_noise: vec![body_noise],
}
}
pub fn encrypt_lwe_ciphertext_with_compact_public_key<
Scalar,
MaskDistribution,
NoiseDistribution,
KeyCont,
OutputCont,
EncryptionGen,
>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
output: &mut LweCiphertext<OutputCont>,
encoded: Plaintext<Scalar>,
mask_noise_distribution: MaskDistribution,
body_noise_distribution: NoiseDistribution,
noise_generator: &mut NoiseRandomGenerator<EncryptionGen>,
) where
Scalar: Encryptable<MaskDistribution, NoiseDistribution> + RandomGenerable<UniformBinary>,
MaskDistribution: Distribution,
NoiseDistribution: Distribution,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
EncryptionGen: ByteRandomGenerator,
{
let _ = encrypt_lwe_ciphertext_with_compact_public_key_impl(
lwe_compact_public_key,
output,
encoded,
mask_noise_distribution,
body_noise_distribution,
noise_generator,
);
}
#[cfg(feature = "zk-pok")]
#[allow(clippy::too_many_arguments)]
pub fn encrypt_and_prove_lwe_ciphertext_with_compact_public_key<
Scalar,
KeyCont,
OutputCont,
MaskDistribution,
NoiseDistribution,
EncryptionGen,
>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
output: &mut LweCiphertext<OutputCont>,
message: Cleartext<Scalar>,
delta: Scalar,
mask_noise_distribution: MaskDistribution,
body_noise_distribution: NoiseDistribution,
noise_generator: &mut NoiseRandomGenerator<EncryptionGen>,
crs: &CompactPkeCrs,
metadata: &[u8],
load: ZkComputeLoad,
) -> crate::Result<CompactPkeProof>
where
Scalar: Encryptable<MaskDistribution, NoiseDistribution>
+ RandomGenerable<UniformBinary>
+ CastFrom<u64>,
Scalar::Signed: CastFrom<u64>,
i64: CastFrom<Scalar>,
u64: CastFrom<Scalar> + CastInto<Scalar::Signed>,
KeyCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
MaskDistribution: BoundedDistribution<Scalar::Signed>,
NoiseDistribution: BoundedDistribution<Scalar::Signed>,
EncryptionGen: ByteRandomGenerator,
{
verify_zero_knowledge_preconditions(
lwe_compact_public_key,
LweCiphertextCount(1),
output.ciphertext_modulus(),
delta,
mask_noise_distribution,
body_noise_distribution,
crs,
)?;
let CompactPublicKeyRandomVectors {
binary_random_vector,
mask_noise,
body_noise,
} = encrypt_lwe_ciphertext_with_compact_public_key_impl(
lwe_compact_public_key,
output,
Plaintext(message.0 * delta),
mask_noise_distribution,
body_noise_distribution,
noise_generator,
);
let mut zk_seed = [0u8; 16];
noise_generator.fill_bytes(&mut zk_seed);
Ok(crs.prove(
lwe_compact_public_key,
&vec![message.0],
&LweCompactCiphertextList::from_container(
output.as_ref(),
output.lwe_size(),
LweCiphertextCount(1),
output.ciphertext_modulus(),
),
&binary_random_vector,
&mask_noise,
&body_noise,
metadata,
load,
zk_seed,
))
}
fn encrypt_lwe_compact_ciphertext_list_with_compact_public_key_impl<
Scalar,
KeyCont,
InputCont,
OutputCont,
MaskDistribution,
NoiseDistribution,
EncryptionGen,
>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
output: &mut LweCompactCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
mask_noise_distribution: MaskDistribution,
body_noise_distribution: NoiseDistribution,
noise_generator: &mut NoiseRandomGenerator<EncryptionGen>,
slice_semi_reverse_negacyclic_convolution_impl: fn(
output: &mut [Scalar],
lhs: &[Scalar],
rhs: &[Scalar],
),
) -> CompactPublicKeyRandomVectors<Scalar>
where
Scalar: Encryptable<MaskDistribution, NoiseDistribution> + RandomGenerable<UniformBinary>,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
MaskDistribution: Distribution,
NoiseDistribution: Distribution,
EncryptionGen: ByteRandomGenerator,
{
assert!(
output.lwe_size().to_lwe_dimension() == lwe_compact_public_key.lwe_dimension(),
"Mismatch between LweDimension of output ciphertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.lwe_size().to_lwe_dimension(),
lwe_compact_public_key.lwe_dimension()
);
assert!(
lwe_compact_public_key.ciphertext_modulus() == output.ciphertext_modulus(),
"Mismatch between CiphertextModulus of output ciphertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.ciphertext_modulus(),
lwe_compact_public_key.ciphertext_modulus()
);
assert!(
output.lwe_ciphertext_count().0 == encoded.plaintext_count().0,
"Mismatch between LweCiphertextCount of output ciphertext and \
PlaintextCount of input list. Got {:?} in output, and {:?} in input plaintext list.",
output.lwe_ciphertext_count(),
encoded.plaintext_count()
);
assert!(
output.ciphertext_modulus().is_native_modulus(),
"This operation only supports native moduli"
);
let (pk_mask, pk_body) = lwe_compact_public_key.get_mask_and_body();
let (mut output_mask_list, mut output_body_list) = output.get_mut_mask_and_body_list();
let mut binary_random_vector = vec![Scalar::ZERO; output_mask_list.lwe_mask_list_size()];
noise_generator.fill_slice_with_random_uniform_binary_bits(&mut binary_random_vector);
let mut mask_noise = vec![Scalar::ZERO; output_mask_list.lwe_mask_list_size()];
noise_generator
.fill_slice_with_random_noise_from_distribution(&mut mask_noise, mask_noise_distribution);
let mut body_noise = vec![Scalar::ZERO; encoded.plaintext_count().0];
noise_generator
.fill_slice_with_random_noise_from_distribution(&mut body_noise, body_noise_distribution);
let max_ciphertext_per_bin = lwe_compact_public_key.lwe_dimension().0;
output_mask_list
.iter_mut()
.zip(
output_body_list
.chunks_mut(max_ciphertext_per_bin)
.zip(encoded.chunks(max_ciphertext_per_bin))
.zip(binary_random_vector.chunks(max_ciphertext_per_bin))
.zip(mask_noise.as_slice().chunks(max_ciphertext_per_bin))
.zip(body_noise.as_slice().chunks(max_ciphertext_per_bin)),
)
.for_each(
|(
mut output_mask,
(
(
((mut output_body_chunk, input_plaintext_chunk), binary_random_slice),
mask_noise,
),
body_noise,
),
)| {
let mut pk_body_convolved = vec![Scalar::ZERO; max_ciphertext_per_bin];
slice_semi_reverse_negacyclic_convolution_impl(
output_mask.as_mut(),
pk_mask.as_ref(),
binary_random_slice,
);
slice_semi_reverse_negacyclic_convolution_impl(
pk_body_convolved.as_mut_slice(),
pk_body.as_ref(),
binary_random_slice,
);
slice_wrapping_add_assign(output_mask.as_mut(), mask_noise);
output_body_chunk
.iter_mut()
.zip(
pk_body_convolved
.iter()
.rev()
.zip(input_plaintext_chunk.iter()),
)
.zip(body_noise)
.for_each(|((dst, (&src, plaintext)), body_noise)| {
*dst.data = src.wrapping_add(*body_noise).wrapping_add(*plaintext.0);
});
},
);
CompactPublicKeyRandomVectors {
binary_random_vector,
mask_noise,
body_noise,
}
}
pub fn encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
Scalar,
MaskDistribution,
NoiseDistribution,
KeyCont,
InputCont,
OutputCont,
EncryptionGen,
>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
output: &mut LweCompactCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
mask_noise_distribution: MaskDistribution,
body_noise_distribution: NoiseDistribution,
noise_generator: &mut NoiseRandomGenerator<EncryptionGen>,
) where
Scalar: Encryptable<MaskDistribution, NoiseDistribution> + RandomGenerable<UniformBinary>,
MaskDistribution: Distribution,
NoiseDistribution: Distribution,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
EncryptionGen: ByteRandomGenerator,
{
let _ = encrypt_lwe_compact_ciphertext_list_with_compact_public_key_impl(
lwe_compact_public_key,
output,
encoded,
mask_noise_distribution,
body_noise_distribution,
noise_generator,
slice_semi_reverse_negacyclic_convolution,
);
}
#[cfg(feature = "zk-pok")]
#[allow(clippy::too_many_arguments)]
pub fn encrypt_and_prove_lwe_compact_ciphertext_list_with_compact_public_key<
Scalar,
KeyCont,
InputCont,
OutputCont,
MaskDistribution,
NoiseDistribution,
EncryptionGen,
>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
output: &mut LweCompactCiphertextList<OutputCont>,
messages: &InputCont,
delta: Scalar,
mask_noise_distribution: MaskDistribution,
body_noise_distribution: NoiseDistribution,
noise_generator: &mut NoiseRandomGenerator<EncryptionGen>,
crs: &CompactPkeCrs,
metadata: &[u8],
load: ZkComputeLoad,
) -> crate::Result<CompactPkeProof>
where
Scalar: Encryptable<MaskDistribution, NoiseDistribution>
+ RandomGenerable<UniformBinary>
+ CastFrom<u64>,
Scalar::Signed: CastFrom<u64>,
i64: CastFrom<Scalar>,
u64: CastFrom<Scalar> + CastInto<Scalar::Signed>,
MaskDistribution: BoundedDistribution<Scalar::Signed>,
NoiseDistribution: BoundedDistribution<Scalar::Signed>,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
EncryptionGen: ByteRandomGenerator,
{
verify_zero_knowledge_preconditions(
lwe_compact_public_key,
output.lwe_ciphertext_count(),
output.ciphertext_modulus(),
delta,
mask_noise_distribution,
body_noise_distribution,
crs,
)?;
let encoded = PlaintextList::from_container(
messages
.as_ref()
.iter()
.copied()
.map(|m| m * delta)
.collect::<Vec<_>>(),
);
let CompactPublicKeyRandomVectors {
binary_random_vector,
mask_noise,
body_noise,
} = encrypt_lwe_compact_ciphertext_list_with_compact_public_key_impl(
lwe_compact_public_key,
output,
&encoded,
mask_noise_distribution,
body_noise_distribution,
noise_generator,
slice_semi_reverse_negacyclic_convolution,
);
let mut zk_seed = [0u8; 16];
noise_generator.fill_bytes(&mut zk_seed);
Ok(crs.prove(
lwe_compact_public_key,
messages,
output,
&binary_random_vector,
&mask_noise,
&body_noise,
metadata,
load,
zk_seed,
))
}
fn par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key_impl<
Scalar,
KeyCont,
InputCont,
OutputCont,
MaskDistribution,
NoiseDistribution,
EncryptionGen,
>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
output: &mut LweCompactCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
mask_noise_distribution: MaskDistribution,
body_noise_distribution: NoiseDistribution,
noise_generator: &mut NoiseRandomGenerator<EncryptionGen>,
) -> CompactPublicKeyRandomVectors<Scalar>
where
Scalar: Encryptable<MaskDistribution, NoiseDistribution> + RandomGenerable<UniformBinary>,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
MaskDistribution: Distribution,
NoiseDistribution: Distribution,
EncryptionGen: ByteRandomGenerator,
{
assert!(
output.lwe_size().to_lwe_dimension() == lwe_compact_public_key.lwe_dimension(),
"Mismatch between LweDimension of output ciphertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.lwe_size().to_lwe_dimension(),
lwe_compact_public_key.lwe_dimension()
);
assert!(
lwe_compact_public_key.ciphertext_modulus() == output.ciphertext_modulus(),
"Mismatch between CiphertextModulus of output ciphertext and input public key. \
Got {:?} in output, and {:?} in public key.",
output.ciphertext_modulus(),
lwe_compact_public_key.ciphertext_modulus()
);
assert!(
output.lwe_ciphertext_count().0 == encoded.plaintext_count().0,
"Mismatch between LweCiphertextCount of output ciphertext and \
PlaintextCount of input list. Got {:?} in output, and {:?} in input plaintext list.",
output.lwe_ciphertext_count(),
encoded.plaintext_count()
);
assert!(
output.ciphertext_modulus().is_native_modulus(),
"This operation only supports native moduli"
);
let (pk_mask, pk_body) = lwe_compact_public_key.get_mask_and_body();
let (mut output_mask_list, mut output_body_list) = output.get_mut_mask_and_body_list();
let mut binary_random_vector = vec![Scalar::ZERO; output_mask_list.lwe_mask_list_size()];
noise_generator.fill_slice_with_random_uniform_binary_bits(&mut binary_random_vector);
let mut mask_noise = vec![Scalar::ZERO; output_mask_list.lwe_mask_list_size()];
noise_generator
.fill_slice_with_random_noise_from_distribution(&mut mask_noise, mask_noise_distribution);
let mut body_noise = vec![Scalar::ZERO; encoded.plaintext_count().0];
noise_generator
.fill_slice_with_random_noise_from_distribution(&mut body_noise, body_noise_distribution);
let max_ciphertext_per_bin = lwe_compact_public_key.lwe_dimension().0;
output_mask_list
.par_iter_mut()
.zip(
output_body_list
.par_chunks_mut(max_ciphertext_per_bin)
.zip(encoded.par_chunks(max_ciphertext_per_bin))
.zip(binary_random_vector.par_chunks(max_ciphertext_per_bin))
.zip(mask_noise.as_slice().par_chunks(max_ciphertext_per_bin))
.zip(body_noise.as_slice().par_chunks(max_ciphertext_per_bin)),
)
.for_each(
|(
mut output_mask,
(
(
((mut output_body_chunk, input_plaintext_chunk), binary_random_slice),
mask_noise,
),
body_noise,
),
)| {
let mut pk_body_convolved = vec![Scalar::ZERO; max_ciphertext_per_bin];
rayon::join(
|| {
slice_semi_reverse_negacyclic_convolution(
output_mask.as_mut(),
pk_mask.as_ref(),
binary_random_slice,
);
},
|| {
slice_semi_reverse_negacyclic_convolution(
pk_body_convolved.as_mut_slice(),
pk_body.as_ref(),
binary_random_slice,
);
},
);
slice_wrapping_add_assign(output_mask.as_mut(), mask_noise);
output_body_chunk
.iter_mut()
.zip(
pk_body_convolved
.iter()
.rev()
.zip(input_plaintext_chunk.iter()),
)
.zip(body_noise)
.for_each(|((dst, (&src, plaintext)), body_noise)| {
*dst.data = src.wrapping_add(*body_noise).wrapping_add(*plaintext.0);
});
},
);
CompactPublicKeyRandomVectors {
binary_random_vector,
mask_noise,
body_noise,
}
}
pub fn par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
Scalar,
MaskDistribution,
NoiseDistribution,
KeyCont,
InputCont,
OutputCont,
EncryptionGen,
>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
output: &mut LweCompactCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
mask_noise_distribution: MaskDistribution,
body_noise_distribution: NoiseDistribution,
noise_generator: &mut NoiseRandomGenerator<EncryptionGen>,
) where
Scalar: Encryptable<MaskDistribution, NoiseDistribution>
+ RandomGenerable<UniformBinary>
+ Sync
+ Send,
MaskDistribution: Distribution + Sync,
NoiseDistribution: Distribution + Sync,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
EncryptionGen: ParallelByteRandomGenerator,
{
let _ = par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key_impl(
lwe_compact_public_key,
output,
encoded,
mask_noise_distribution,
body_noise_distribution,
noise_generator,
);
}
#[cfg(feature = "zk-pok")]
#[allow(clippy::too_many_arguments)]
pub fn par_encrypt_and_prove_lwe_compact_ciphertext_list_with_compact_public_key<
Scalar,
KeyCont,
InputCont,
OutputCont,
MaskDistribution,
NoiseDistribution,
EncryptionGen,
>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
output: &mut LweCompactCiphertextList<OutputCont>,
messages: &InputCont,
delta: Scalar,
mask_noise_distribution: MaskDistribution,
body_noise_distribution: NoiseDistribution,
noise_generator: &mut NoiseRandomGenerator<EncryptionGen>,
crs: &CompactPkeCrs,
metadata: &[u8],
load: ZkComputeLoad,
) -> crate::Result<CompactPkeProof>
where
Scalar: Encryptable<MaskDistribution, NoiseDistribution>
+ RandomGenerable<UniformBinary>
+ CastFrom<u64>,
Scalar::Signed: CastFrom<u64>,
i64: CastFrom<Scalar>,
u64: CastFrom<Scalar> + CastInto<Scalar::Signed>,
MaskDistribution: BoundedDistribution<Scalar::Signed>,
NoiseDistribution: BoundedDistribution<Scalar::Signed>,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
EncryptionGen: ByteRandomGenerator,
{
verify_zero_knowledge_preconditions(
lwe_compact_public_key,
output.lwe_ciphertext_count(),
output.ciphertext_modulus(),
delta,
mask_noise_distribution,
body_noise_distribution,
crs,
)?;
let encoded = PlaintextList::from_container(
messages
.as_ref()
.iter()
.copied()
.map(|m| m * delta)
.collect::<Vec<_>>(),
);
let CompactPublicKeyRandomVectors {
binary_random_vector,
mask_noise,
body_noise,
} = par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key_impl(
lwe_compact_public_key,
output,
&encoded,
mask_noise_distribution,
body_noise_distribution,
noise_generator,
);
let mut zk_seed = [0u8; 16];
noise_generator.fill_bytes(&mut zk_seed);
Ok(crs.prove(
lwe_compact_public_key,
messages,
output,
&binary_random_vector,
&mask_noise,
&body_noise,
metadata,
load,
zk_seed,
))
}
pub mod re_randomization {
use super::*;
pub fn rerand_encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
Scalar,
MaskDistribution,
NoiseDistribution,
KeyCont,
InputCont,
OutputCont,
EncryptionGen,
>(
lwe_compact_public_key: &LweCompactPublicKey<KeyCont>,
output: &mut LweCompactCiphertextList<OutputCont>,
encoded: &PlaintextList<InputCont>,
mask_noise_distribution: MaskDistribution,
body_noise_distribution: NoiseDistribution,
noise_generator: &mut NoiseRandomGenerator<EncryptionGen>,
) where
Scalar: Encryptable<MaskDistribution, NoiseDistribution> + RandomGenerable<UniformBinary>,
MaskDistribution: Distribution,
NoiseDistribution: Distribution,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
EncryptionGen: ByteRandomGenerator,
{
let _ = encrypt_lwe_compact_ciphertext_list_with_compact_public_key_impl(
lwe_compact_public_key,
output,
encoded,
mask_noise_distribution,
body_noise_distribution,
noise_generator,
slice_binary_semi_reverse_negacyclic_convolution,
);
}
}
#[cfg(test)]
mod test {
use crate::core_crypto::commons::generators::DeterministicSeeder;
use crate::core_crypto::commons::test_tools;
use crate::core_crypto::prelude::*;
#[test]
fn test_compact_public_key_encryption() {
use rand::Rng;
let lwe_dimension = LweDimension(2048);
let glwe_noise_distribution = Gaussian::from_dispersion_parameter(
StandardDev(0.00000000000000029403601535432533),
0.0,
);
let ciphertext_modulus = CiphertextModulus::new_native();
let mut secret_random_generator = test_tools::new_secret_random_generator();
let mut encryption_random_generator = test_tools::new_encryption_random_generator();
let mut thread_rng = rand::thread_rng();
for _ in 0..10_000 {
let lwe_sk =
LweSecretKey::generate_new_binary(lwe_dimension, &mut secret_random_generator);
let mut compact_lwe_pk =
LweCompactPublicKey::new(0u64, lwe_dimension, ciphertext_modulus);
generate_lwe_compact_public_key(
&lwe_sk,
&mut compact_lwe_pk,
glwe_noise_distribution,
&mut encryption_random_generator,
);
let msg: u64 = thread_rng.gen();
let msg = msg % 16;
let plaintext = Plaintext(msg << 60);
let mut output_ct = LweCiphertext::new(
0u64,
lwe_dimension.to_lwe_size(),
CiphertextModulus::new_native(),
);
encrypt_lwe_ciphertext_with_compact_public_key(
&compact_lwe_pk,
&mut output_ct,
plaintext,
glwe_noise_distribution,
glwe_noise_distribution,
encryption_random_generator.noise_generator_mut(),
);
let decrypted_plaintext = decrypt_lwe_ciphertext(&lwe_sk, &output_ct);
let signed_decomposer =
SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
let cleartext = signed_decomposer.closest_representable(decrypted_plaintext.0) >> 60;
assert_eq!(cleartext, msg);
}
}
#[test]
fn test_par_compact_lwe_list_public_key_encryption_equivalence() {
use rand::Rng;
let lwe_dimension = LweDimension(2048);
let glwe_noise_distribution = Gaussian::from_dispersion_parameter(
StandardDev(0.00000000000000029403601535432533),
0.0,
);
let ciphertext_modulus = CiphertextModulus::new_native();
let mut thread_rng = rand::thread_rng();
for _ in 0..100 {
let ct_count: usize = thread_rng.gen();
let ct_count = ct_count % (lwe_dimension.0 * 4) + 1;
let lwe_ciphertext_count = LweCiphertextCount(ct_count);
println!("{lwe_dimension:?} {ct_count:?}");
let seed = test_tools::random_seed();
let mut input_plaintext_list =
PlaintextList::new(0u64, PlaintextCount(lwe_ciphertext_count.0));
input_plaintext_list.iter_mut().for_each(|x| {
let msg: u64 = thread_rng.gen();
*x.0 = (msg % 16) << 60;
});
let par_lwe_ct_list = {
let mut deterministic_seeder =
DeterministicSeeder::<DefaultRandomGenerator>::new(seed);
let mut secret_random_generator =
SecretRandomGenerator::<DefaultRandomGenerator>::new(
deterministic_seeder.seed(),
);
let mut encryption_random_generator =
EncryptionRandomGenerator::<DefaultRandomGenerator>::new(
deterministic_seeder.seed(),
&mut deterministic_seeder,
);
let lwe_sk =
LweSecretKey::generate_new_binary(lwe_dimension, &mut secret_random_generator);
let mut compact_lwe_pk =
LweCompactPublicKey::new(0u64, lwe_dimension, ciphertext_modulus);
generate_lwe_compact_public_key(
&lwe_sk,
&mut compact_lwe_pk,
glwe_noise_distribution,
&mut encryption_random_generator,
);
let mut output_compact_ct_list = LweCompactCiphertextList::new(
0u64,
lwe_dimension.to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key(
&compact_lwe_pk,
&mut output_compact_ct_list,
&input_plaintext_list,
glwe_noise_distribution,
glwe_noise_distribution,
encryption_random_generator.noise_generator_mut(),
);
let mut output_plaintext_list = input_plaintext_list.clone();
output_plaintext_list.as_mut().fill(0u64);
let lwe_ciphertext_list =
output_compact_ct_list.par_expand_into_lwe_ciphertext_list();
decrypt_lwe_ciphertext_list(
&lwe_sk,
&lwe_ciphertext_list,
&mut output_plaintext_list,
);
let signed_decomposer =
SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
output_plaintext_list
.iter_mut()
.for_each(|x| *x.0 = signed_decomposer.closest_representable(*x.0));
assert_eq!(input_plaintext_list, output_plaintext_list);
lwe_ciphertext_list
};
let ser_lwe_ct_list = {
let mut deterministic_seeder =
DeterministicSeeder::<DefaultRandomGenerator>::new(seed);
let mut secret_random_generator =
SecretRandomGenerator::<DefaultRandomGenerator>::new(
deterministic_seeder.seed(),
);
let mut encryption_random_generator =
EncryptionRandomGenerator::<DefaultRandomGenerator>::new(
deterministic_seeder.seed(),
&mut deterministic_seeder,
);
let lwe_sk =
LweSecretKey::generate_new_binary(lwe_dimension, &mut secret_random_generator);
let mut compact_lwe_pk =
LweCompactPublicKey::new(0u64, lwe_dimension, ciphertext_modulus);
generate_lwe_compact_public_key(
&lwe_sk,
&mut compact_lwe_pk,
glwe_noise_distribution,
&mut encryption_random_generator,
);
let mut output_compact_ct_list = LweCompactCiphertextList::new(
0u64,
lwe_dimension.to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
encrypt_lwe_compact_ciphertext_list_with_compact_public_key(
&compact_lwe_pk,
&mut output_compact_ct_list,
&input_plaintext_list,
glwe_noise_distribution,
glwe_noise_distribution,
encryption_random_generator.noise_generator_mut(),
);
let mut output_plaintext_list = input_plaintext_list.clone();
output_plaintext_list.as_mut().fill(0u64);
let lwe_ciphertext_list = output_compact_ct_list.expand_into_lwe_ciphertext_list();
decrypt_lwe_ciphertext_list(
&lwe_sk,
&lwe_ciphertext_list,
&mut output_plaintext_list,
);
let signed_decomposer =
SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
output_plaintext_list
.iter_mut()
.for_each(|x| *x.0 = signed_decomposer.closest_representable(*x.0));
assert_eq!(input_plaintext_list, output_plaintext_list);
lwe_ciphertext_list
};
assert_eq!(ser_lwe_ct_list, par_lwe_ct_list);
}
}
#[test]
fn test_rerand_compact_lwe_list_public_key_encryption_equivalence() {
use rand::Rng;
use re_randomization::rerand_encrypt_lwe_compact_ciphertext_list_with_compact_public_key;
let lwe_dimension = LweDimension(2048);
let glwe_noise_distribution = TUniform::new(17);
let ciphertext_modulus = CiphertextModulus::new_native();
let mut thread_rng = rand::thread_rng();
let cleartext_bits_without_padding = 4;
let cleartext_modulus = 1 << cleartext_bits_without_padding;
let cleartext_bits_with_padding = cleartext_bits_without_padding + 1;
let signed_decomposer = SignedDecomposer::new(
DecompositionBaseLog(cleartext_bits_with_padding),
DecompositionLevelCount(1),
);
for _ in 0..100 {
let ct_count: usize = thread_rng.gen_range(1..=lwe_dimension.0 * 4);
let lwe_ciphertext_count = LweCiphertextCount(ct_count);
println!("{lwe_dimension:?} {ct_count:?}");
let seed = test_tools::random_seed();
println!("seed={seed:?}");
let mut input_plaintext_list =
PlaintextList::new(0u64, PlaintextCount(lwe_ciphertext_count.0));
input_plaintext_list.iter_mut().for_each(|x| {
let msg: u64 = thread_rng.gen();
*x.0 =
(msg % cleartext_modulus) << (u64::BITS - cleartext_bits_with_padding as u32);
});
let get_seeded_gen = || {
let mut deterministic_seeder =
DeterministicSeeder::<DefaultRandomGenerator>::new(seed);
(
SecretRandomGenerator::<DefaultRandomGenerator>::new(
deterministic_seeder.seed(),
),
EncryptionRandomGenerator::<DefaultRandomGenerator>::new(
deterministic_seeder.seed(),
&mut deterministic_seeder,
),
)
};
let rerand_lwe_ct_list = {
let (mut secret_random_generator, mut encryption_random_generator) =
get_seeded_gen();
let lwe_sk =
LweSecretKey::generate_new_binary(lwe_dimension, &mut secret_random_generator);
let mut compact_lwe_pk =
LweCompactPublicKey::new(0u64, lwe_dimension, ciphertext_modulus);
generate_lwe_compact_public_key(
&lwe_sk,
&mut compact_lwe_pk,
glwe_noise_distribution,
&mut encryption_random_generator,
);
let mut output_compact_ct_list = LweCompactCiphertextList::new(
0u64,
lwe_dimension.to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
rerand_encrypt_lwe_compact_ciphertext_list_with_compact_public_key(
&compact_lwe_pk,
&mut output_compact_ct_list,
&input_plaintext_list,
glwe_noise_distribution,
glwe_noise_distribution,
encryption_random_generator.noise_generator_mut(),
);
let mut output_plaintext_list = input_plaintext_list.clone();
output_plaintext_list.as_mut().fill(0u64);
let lwe_ciphertext_list = output_compact_ct_list.expand_into_lwe_ciphertext_list();
decrypt_lwe_ciphertext_list(
&lwe_sk,
&lwe_ciphertext_list,
&mut output_plaintext_list,
);
output_plaintext_list
.iter_mut()
.for_each(|x| *x.0 = signed_decomposer.closest_representable(*x.0));
assert_eq!(input_plaintext_list, output_plaintext_list);
lwe_ciphertext_list
};
let lwe_ct_list = {
let (mut secret_random_generator, mut encryption_random_generator) =
get_seeded_gen();
let lwe_sk =
LweSecretKey::generate_new_binary(lwe_dimension, &mut secret_random_generator);
let mut compact_lwe_pk =
LweCompactPublicKey::new(0u64, lwe_dimension, ciphertext_modulus);
generate_lwe_compact_public_key(
&lwe_sk,
&mut compact_lwe_pk,
glwe_noise_distribution,
&mut encryption_random_generator,
);
let mut output_compact_ct_list = LweCompactCiphertextList::new(
0u64,
lwe_dimension.to_lwe_size(),
lwe_ciphertext_count,
ciphertext_modulus,
);
encrypt_lwe_compact_ciphertext_list_with_compact_public_key(
&compact_lwe_pk,
&mut output_compact_ct_list,
&input_plaintext_list,
glwe_noise_distribution,
glwe_noise_distribution,
encryption_random_generator.noise_generator_mut(),
);
let mut output_plaintext_list = input_plaintext_list.clone();
output_plaintext_list.as_mut().fill(0u64);
let lwe_ciphertext_list = output_compact_ct_list.expand_into_lwe_ciphertext_list();
decrypt_lwe_ciphertext_list(
&lwe_sk,
&lwe_ciphertext_list,
&mut output_plaintext_list,
);
output_plaintext_list
.iter_mut()
.for_each(|x| *x.0 = signed_decomposer.closest_representable(*x.0));
assert_eq!(input_plaintext_list, output_plaintext_list);
lwe_ciphertext_list
};
assert_eq!(lwe_ct_list, rerand_lwe_ct_list);
}
}
}