feat(tfhe): add SeededGlweCiphertext

This commit is contained in:
Arthur Meyre
2022-12-14 17:52:03 +01:00
committed by jborfila
parent d773d3e7ff
commit 268371fda6
6 changed files with 483 additions and 20 deletions

View File

@@ -1,10 +1,40 @@
use crate::core_crypto::algorithms::polynomial_algorithms::*;
use crate::core_crypto::commons::dispersion::DispersionParameter;
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
use crate::core_crypto::commons::math::random::ActivatedRandomGenerator;
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Convenience function to share the core logic of the GLWE assign encryption between all functions
/// needing it.
pub fn fill_glwe_mask_and_body_for_encryption_assign<KeyCont, BodyCont, MaskCont, Scalar, Gen>(
glwe_secret_key: &GlweSecretKey<KeyCont>,
output_mask: &mut GlweMask<MaskCont>,
output_body: &mut GlweBody<BodyCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
BodyCont: ContainerMut<Element = Scalar>,
MaskCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
generator.unsigned_torus_slice_wrapping_add_random_noise_assign(
output_body.as_mut(),
noise_parameters,
);
generator.fill_slice_with_random_mask(output_mask.as_mut());
polynomial_wrapping_add_multisum_assign(
&mut output_body.as_mut_polynomial(),
&output_mask.as_polynomial_list(),
&glwe_secret_key.as_polynomial_list(),
);
}
/// Variant of [`encrypt_glwe_ciphertext`] which assumes that the plaintexts to encrypt are already
/// loaded in the body of the output [`GLWE ciphertext`](`GlweCiphertext`), this is sometimes useful
/// to avoid allocating a [`PlaintextList`] in situ.
@@ -112,14 +142,44 @@ pub fn encrypt_glwe_ciphertext_assign<Scalar, KeyCont, OutputCont, Gen>(
let (mut mask, mut body) = output.get_mut_mask_and_body();
generator.fill_slice_with_random_mask(mask.as_mut());
fill_glwe_mask_and_body_for_encryption_assign(
glwe_secret_key,
&mut mask,
&mut body,
noise_parameters,
generator,
);
}
generator
.unsigned_torus_slice_wrapping_add_random_noise_assign(body.as_mut(), noise_parameters);
/// Convenience function to share the core logic of the GLWE encryption between all functions
/// needing it.
pub fn fill_glwe_mask_and_body_for_encryption<KeyCont, InputCont, BodyCont, MaskCont, Scalar, Gen>(
glwe_secret_key: &GlweSecretKey<KeyCont>,
output_mask: &mut GlweMask<MaskCont>,
output_body: &mut GlweBody<BodyCont>,
encoded: &PlaintextList<InputCont>,
noise_parameters: impl DispersionParameter,
generator: &mut EncryptionRandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
BodyCont: ContainerMut<Element = Scalar>,
MaskCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
generator.fill_slice_with_random_noise(output_body.as_mut(), noise_parameters);
generator.fill_slice_with_random_mask(output_mask.as_mut());
polynomial_wrapping_add_assign(
&mut output_body.as_mut_polynomial(),
&encoded.as_polynomial(),
);
polynomial_wrapping_add_multisum_assign(
&mut body.as_mut_polynomial(),
&mask.as_polynomial_list(),
&mut output_body.as_mut_polynomial(),
&output_mask.as_polynomial_list(),
&glwe_secret_key.as_polynomial_list(),
);
}
@@ -252,19 +312,13 @@ pub fn encrypt_glwe_ciphertext<Scalar, KeyCont, InputCont, OutputCont, Gen>(
let (mut mask, mut body) = output_glwe_ciphertext.get_mut_mask_and_body();
generator.fill_slice_with_random_mask(mask.as_mut());
generator.fill_slice_with_random_noise(body.as_mut(), noise_parameters);
polynomial_wrapping_add_assign(
&mut body.as_mut_polynomial(),
&input_plaintext_list.as_polynomial(),
);
polynomial_wrapping_add_multisum_assign(
&mut body.as_mut_polynomial(),
&mask.as_polynomial_list(),
&glwe_secret_key.as_polynomial_list(),
fill_glwe_mask_and_body_for_encryption(
glwe_secret_key,
&mut mask,
&mut body,
input_plaintext_list,
noise_parameters,
generator,
);
}
@@ -679,3 +733,139 @@ where
new_ct
}
/// Encrypt a [`PlaintextList`] in a
/// [`compressed/seeded GLWE ciphertext`](`SeededGlweCiphertext`).
///
/// ```
/// use tfhe::core_crypto::commons::generators::{
/// EncryptionRandomGenerator, SecretRandomGenerator,
/// };
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
/// use tfhe::core_crypto::commons::math::random::ActivatedRandomGenerator;
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for GlweCiphertext creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
///
/// // Create the PRNG
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
/// let mut encryption_generator =
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
/// let mut secret_generator =
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
///
/// // Create the GlweSecretKey
/// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
/// glwe_size.to_glwe_dimension(),
/// polynomial_size,
/// &mut secret_generator,
/// );
///
/// // Create the plaintext
/// let msg = 3u64;
/// let encoded_msg = msg << 60;
/// let plaintext_list = PlaintextList::new(encoded_msg, PlaintextCount(polynomial_size.0));
///
/// // Create a new GlweCiphertext
/// let mut glwe =
/// SeededGlweCiphertext::new(0u64, glwe_size, polynomial_size, seeder.seed().into());
///
/// encrypt_seeded_glwe_ciphertext(
/// &glwe_secret_key,
/// &mut glwe,
/// &plaintext_list,
/// glwe_modular_std_dev,
/// seeder,
/// );
///
/// let glwe = glwe.decompress_into_glwe_ciphertext();
///
/// let mut output_plaintext_list = PlaintextList::new(0u64, plaintext_list.plaintext_count());
///
/// decrypt_glwe_ciphertext(&glwe_secret_key, &glwe, &mut output_plaintext_list);
///
/// // Round and remove encoding
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
///
/// output_plaintext_list
/// .iter_mut()
/// .for_each(|elt| *elt.0 = decomposer.closest_representable(*elt.0));
///
/// // Get the raw vector
/// let mut cleartext_list = output_plaintext_list.into_container();
/// // Remove the encoding
/// cleartext_list.iter_mut().for_each(|elt| *elt = *elt >> 60);
/// // Get the list immutably
/// let cleartext_list = cleartext_list;
///
/// // Check we recovered the original message for each plaintext we encrypted
/// cleartext_list.iter().for_each(|&elt| assert_eq!(elt, msg));
/// ```
pub fn encrypt_seeded_glwe_ciphertext<Scalar, KeyCont, InputCont, OutputCont, NoiseSeeder>(
glwe_secret_key: &GlweSecretKey<KeyCont>,
output_glwe_ciphertext: &mut SeededGlweCiphertext<OutputCont>,
input_plaintext_list: &PlaintextList<InputCont>,
noise_parameters: impl DispersionParameter,
noise_seeder: &mut NoiseSeeder,
) where
Scalar: UnsignedTorus,
KeyCont: Container<Element = Scalar>,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
// Maybe Sized allows to pass Box<dyn Seeder>.
NoiseSeeder: Seeder + ?Sized,
{
assert!(
output_glwe_ciphertext.polynomial_size().0 == input_plaintext_list.plaintext_count().0,
"Mismatch between PolynomialSize of output cipertext PlaintextCount of input. \
Got {:?} in output, and {:?} in input.",
output_glwe_ciphertext.polynomial_size(),
input_plaintext_list.plaintext_count()
);
assert!(
output_glwe_ciphertext.glwe_size().to_glwe_dimension() == glwe_secret_key.glwe_dimension(),
"Mismatch between GlweDimension of output cipertext and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output_glwe_ciphertext.glwe_size().to_glwe_dimension(),
glwe_secret_key.glwe_dimension()
);
assert!(
output_glwe_ciphertext.polynomial_size() == glwe_secret_key.polynomial_size(),
"Mismatch between PolynomialSize of output cipertext and input secret key. \
Got {:?} in output, and {:?} in secret key.",
output_glwe_ciphertext.polynomial_size(),
glwe_secret_key.polynomial_size()
);
let mut generator = EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(
output_glwe_ciphertext.compression_seed().seed,
noise_seeder,
);
let glwe_dimension = output_glwe_ciphertext.glwe_size().to_glwe_dimension();
let polynomial_size = output_glwe_ciphertext.polynomial_size();
let mut body = output_glwe_ciphertext.get_mut_body();
let mut tmp_mask = GlweMask::from_container(
vec![Scalar::ZERO; glwe_ciphertext_mask_size(glwe_dimension, polynomial_size)],
polynomial_size,
);
fill_glwe_mask_and_body_for_encryption(
glwe_secret_key,
&mut tmp_mask,
&mut body,
input_plaintext_list,
noise_parameters,
&mut generator,
);
}

View File

@@ -16,6 +16,7 @@ pub mod lwe_secret_key_generation;
pub mod lwe_wopbs;
pub mod misc;
pub mod polynomial_algorithms;
pub mod seeded_glwe_ciphertext_decompression;
pub mod seeded_lwe_ciphertext_list_decompression;
pub mod seeded_lwe_public_key_decompression;
pub mod seeded_lwe_public_key_generation;
@@ -40,6 +41,7 @@ pub use lwe_public_key_generation::*;
pub use lwe_secret_key_generation::*;
pub use lwe_wopbs::*;
pub use misc::*;
pub use seeded_glwe_ciphertext_decompression::*;
pub use seeded_lwe_ciphertext_list_decompression::*;
pub use seeded_lwe_public_key_decompression::*;
pub use seeded_lwe_public_key_generation::*;

View File

@@ -0,0 +1,48 @@
use crate::core_crypto::commons::math::random::RandomGenerator;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// Convenience function to share the core logic of the decompression algorithm for
/// [`SeededGlweCiphertext`] between all functions needing it.
pub fn decompress_seeded_glwe_ciphertext_with_existing_generator<
Scalar,
InputCont,
OutputCont,
Gen,
>(
output_glwe: &mut GlweCiphertext<OutputCont>,
input_seeded_glwe: &SeededGlweCiphertext<InputCont>,
generator: &mut RandomGenerator<Gen>,
) where
Scalar: UnsignedTorus,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let (mut output_mask, mut output_body) = output_glwe.get_mut_mask_and_body();
// generate a uniformly random mask
generator.fill_slice_with_random_uniform(output_mask.as_mut());
output_body
.as_mut()
.copy_from_slice(input_seeded_glwe.get_body().as_ref());
}
/// Decompress a [`SeededGlweCiphertext`], without consuming it, into a standard
/// [`GlweCiphertext`].
pub fn decompress_seeded_glwe_ciphertext<Scalar, InputCont, OutputCont, Gen>(
output_glwe: &mut GlweCiphertext<OutputCont>,
input_seeded_glwe: &SeededGlweCiphertext<InputCont>,
) where
Scalar: UnsignedTorus,
InputCont: Container<Element = Scalar>,
OutputCont: ContainerMut<Element = Scalar>,
Gen: ByteRandomGenerator,
{
let mut generator = RandomGenerator::<Gen>::new(input_seeded_glwe.compression_seed().seed);
decompress_seeded_glwe_ciphertext_with_existing_generator::<_, _, _, Gen>(
output_glwe,
input_seeded_glwe,
&mut generator,
)
}

View File

@@ -17,6 +17,7 @@ pub mod plaintext;
pub mod plaintext_list;
pub mod polynomial;
pub mod polynomial_list;
pub mod seeded_glwe_ciphertext;
pub mod seeded_lwe_ciphertext_list;
pub mod seeded_lwe_public_key;
@@ -42,5 +43,6 @@ pub use plaintext::*;
pub use plaintext_list::*;
pub use polynomial::*;
pub use polynomial_list::*;
pub use seeded_glwe_ciphertext::*;
pub use seeded_lwe_ciphertext_list::*;
pub use seeded_lwe_public_key::*;

View File

@@ -0,0 +1,221 @@
use crate::core_crypto::algorithms::*;
use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, CompressionSeed};
use crate::core_crypto::commons::parameters::*;
use crate::core_crypto::commons::traits::*;
use crate::core_crypto::entities::*;
/// A [`seeded GLWE ciphertext`](`SeededGlweCiphertext`).
#[derive(Clone, Debug, PartialEq)]
pub struct SeededGlweCiphertext<C: Container> {
data: C,
glwe_size: GlweSize,
compression_seed: CompressionSeed,
}
impl<T, C: Container<Element = T>> AsRef<[T]> for SeededGlweCiphertext<C> {
fn as_ref(&self) -> &[T] {
self.data.as_ref()
}
}
impl<T, C: ContainerMut<Element = T>> AsMut<[T]> for SeededGlweCiphertext<C> {
fn as_mut(&mut self) -> &mut [T] {
self.data.as_mut()
}
}
impl<Scalar, C: Container<Element = Scalar>> SeededGlweCiphertext<C> {
/// Create a [`SeededGlweCiphertext`] from an existing container.
///
/// # Note
///
/// This function only wraps a container in the appropriate type. If you want to encrypt data
/// you need to use [`crate::core_crypto::algorithms::encrypt_seeded_glwe_ciphertext`] using
/// this ciphertext as output.
///
/// This docstring exhibits [`SeededGlweCiphertext`] primitives usage.
///
/// ```
/// use tfhe::core_crypto::prelude::*;
/// use tfhe::seeders::new_seeder;
///
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
/// // computations
/// // Define parameters for SeededGlweCiphertext creation
/// let glwe_size = GlweSize(2);
/// let polynomial_size = PolynomialSize(1024);
///
/// // Get a seeder
/// let mut seeder = new_seeder();
/// let seeder = seeder.as_mut();
///
/// // Create a new SeededGlweCiphertext
/// let mut glwe =
/// SeededGlweCiphertext::new(0u64, glwe_size, polynomial_size, seeder.seed().into());
///
/// assert_eq!(glwe.glwe_size(), glwe_size);
/// assert_eq!(glwe.polynomial_size(), polynomial_size);
/// assert_eq!(glwe.get_body().polynomial_size(), polynomial_size);
/// assert_eq!(glwe.get_mut_body().polynomial_size(), polynomial_size);
///
/// let compression_seed = glwe.compression_seed();
///
/// // Demonstrate how to recover the allocated container
/// let underlying_container: Vec<u64> = glwe.into_container();
///
/// // Recreate a ciphertext using from_container
/// let mut glwe =
/// SeededGlweCiphertext::from_container(underlying_container, glwe_size, compression_seed);
///
/// assert_eq!(glwe.glwe_size(), glwe_size);
/// assert_eq!(glwe.polynomial_size(), polynomial_size);
/// assert_eq!(glwe.get_body().polynomial_size(), polynomial_size);
/// assert_eq!(glwe.get_mut_body().polynomial_size(), polynomial_size);
///
/// // Decompress the ciphertext
/// let mut glwe = glwe.decompress_into_glwe_ciphertext();
///
/// assert_eq!(glwe.glwe_size(), glwe_size);
/// assert_eq!(glwe.polynomial_size(), polynomial_size);
/// assert_eq!(glwe.get_body().polynomial_size(), polynomial_size);
/// assert_eq!(glwe.get_mut_body().polynomial_size(), polynomial_size);
/// assert_eq!(
/// glwe.get_mask().glwe_dimension(),
/// glwe_size.to_glwe_dimension()
/// );
/// assert_eq!(
/// glwe.get_mut_mask().glwe_dimension(),
/// glwe_size.to_glwe_dimension()
/// );
/// assert_eq!(glwe.get_mask().polynomial_size(), polynomial_size);
/// assert_eq!(glwe.get_mut_mask().polynomial_size(), polynomial_size);
/// ```
pub fn from_container(
container: C,
glwe_size: GlweSize,
compression_seed: CompressionSeed,
) -> SeededGlweCiphertext<C> {
assert!(
container.container_len() > 0,
"Got an empty container to create a SeededGlweCiphertext"
);
SeededGlweCiphertext {
data: container,
glwe_size,
compression_seed,
}
}
/// Return the [`GlweSize`] of the [`SeededGlweCiphertext`].
///
/// See [`SeededGlweCiphertext::from_container`] for usage.
pub fn glwe_size(&self) -> GlweSize {
self.glwe_size
}
/// Return the [`PolynomialSize`] of the [`SeededGlweCiphertext`].
///
/// See [`SeededGlweCiphertext::from_container`] for usage.
pub fn polynomial_size(&self) -> PolynomialSize {
PolynomialSize(self.data.container_len())
}
/// Return the [`CompressionSeed`] of the [`SeededGlweCiphertext`].
///
/// See [`SeededGlweCiphertext::from_container`] for usage.
pub fn compression_seed(&self) -> CompressionSeed {
self.compression_seed
}
/// Return an immutable view to the [`GlweBody`] of a [`SeededGlweCiphertext`].
///
/// See [`SeededGlweCiphertext::from_container`] for usage.
pub fn get_body(&self) -> GlweBody<&[Scalar]> {
GlweBody::from_container(self.as_ref())
}
/// Consume the entity and return its underlying container.
///
/// See [`SeededGlweCiphertext::from_container`] for usage.
pub fn into_container(self) -> C {
self.data
}
/// Consume the [`SeededGlweCiphertext`] and decompress it into a standard
/// [`GlweCiphertext`].
///
/// See [`SeededGlweCiphertext::from_container`] for usage.
pub fn decompress_into_glwe_ciphertext(self) -> GlweCiphertextOwned<Scalar>
where
Scalar: UnsignedTorus,
{
let mut decompressed_ct =
GlweCiphertext::new(Scalar::ZERO, self.glwe_size(), self.polynomial_size());
decompress_seeded_glwe_ciphertext::<_, _, _, ActivatedRandomGenerator>(
&mut decompressed_ct,
&self,
);
decompressed_ct
}
/// Return a view of the [`SeededGlweCiphertext`]. This is useful if an algorithm takes a view
/// by value.
pub fn as_view(&self) -> SeededGlweCiphertext<&'_ [Scalar]> {
SeededGlweCiphertext {
data: self.data.as_ref(),
glwe_size: self.glwe_size,
compression_seed: self.compression_seed,
}
}
}
impl<Scalar, C: ContainerMut<Element = Scalar>> SeededGlweCiphertext<C> {
/// Mutable variant of [`SeededGlweCiphertext::get_body`].
///
/// See [`SeededGlweCiphertext::from_container`] for usage.
pub fn get_mut_body(&mut self) -> GlweBody<&mut [Scalar]> {
GlweBody::from_container(self.as_mut())
}
/// Mutable variant of [`SeededGlweCiphertext::as_view`].
pub fn as_mut_view(&mut self) -> SeededGlweCiphertext<&'_ mut [Scalar]> {
SeededGlweCiphertext {
data: self.data.as_mut(),
glwe_size: self.glwe_size,
compression_seed: self.compression_seed,
}
}
}
/// A [`SeededGlweCiphertext`] owning the memory for its own storage.
pub type SeededGlweCiphertextOwned<Scalar> = SeededGlweCiphertext<Vec<Scalar>>;
/// A [`SeededGlweCiphertext`] immutably borrowing memory for its own storage.
pub type SeededGlweCiphertextView<'data, Scalar> = SeededGlweCiphertext<&'data [Scalar]>;
/// A [`SeededGlweCiphertext`] mutably borrowing memory for its own storage.
pub type SeededGlweCiphertextMutView<'data, Scalar> = SeededGlweCiphertext<&'data mut [Scalar]>;
impl<Scalar: Copy> SeededGlweCiphertextOwned<Scalar> {
/// Allocate memory and create a new owned [`SeededGlweCiphertext`].
///
/// # Note
///
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
/// type. If you want to encrypt data you need to use
/// [`crate::core_crypto::algorithms::encrypt_glwe_ciphertext`] using this ciphertext as
/// output.
///
///
/// See [`SeededGlweCiphertext::from_container`] for usage.
pub fn new(
fill_with: Scalar,
glwe_size: GlweSize,
polynomial_size: PolynomialSize,
compression_seed: CompressionSeed,
) -> SeededGlweCiphertextOwned<Scalar> {
SeededGlweCiphertextOwned::from_container(
vec![fill_with; polynomial_size.0],
glwe_size,
compression_seed,
)
}
}

View File

@@ -36,7 +36,7 @@ impl ShortintEngine {
pub(crate) fn unchecked_neg_assign_with_z(
&mut self,
_server_key: &ServerKey,
server_key: &ServerKey,
ct: &mut Ciphertext,
) -> EngineResult<u64> {
// z = ceil( degree / 2^p ) * 2^p
@@ -46,7 +46,7 @@ impl ShortintEngine {
// Value of the shift we multiply our messages by
let delta =
(1_u64 << 63) / (_server_key.message_modulus.0 * _server_key.carry_modulus.0) as u64;
(1_u64 << 63) / (server_key.message_modulus.0 * server_key.carry_modulus.0) as u64;
//Scaling + 1 on the padding bit
let w = Plaintext(z * delta);