mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-09 14:47:56 -05:00
feat(core): chunked seeded lwe_bsk generation
This commit is contained in:
@@ -4,7 +4,9 @@
|
||||
|
||||
use crate::core_crypto::algorithms::*;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::random::{DefaultRandomGenerator, Distribution, Uniform};
|
||||
use crate::core_crypto::commons::math::random::{
|
||||
CompressionSeed, DefaultRandomGenerator, Distribution, Uniform,
|
||||
};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
@@ -1022,3 +1024,407 @@ where
|
||||
|
||||
assembled_bsk
|
||||
}
|
||||
|
||||
/// A generator for producing chunks of a Seeded LWE bootstrap key.
|
||||
///
|
||||
/// This struct allows for the generation of Seeded LWE bootstrap key chunks, which can be used to
|
||||
/// construct a full Seeded LWE bootstrap key. The generator ensures that the final key would be
|
||||
/// equivalent to the non-chunked generation.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::core_crypto::commons::math::random::CompressionSeed;
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// let input_lwe_dimension = LweDimension(742);
|
||||
/// let chunk_size = ChunkSize(100);
|
||||
/// let decomp_base_log = DecompositionBaseLog(3);
|
||||
/// let decomp_level_count = DecompositionLevelCount(5);
|
||||
/// let glwe_dimension = GlweDimension(1);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let glwe_noise_distribution =
|
||||
/// Gaussian::from_dispersion_parameter(StandardDev(0.00000000000000029403601535432533), 0.0);
|
||||
/// let ciphertext_modulus: CiphertextModulus<u64> = CiphertextModulus::new_native();
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut secret_generator = SecretRandomGenerator::<DefaultRandomGenerator>::new(seeder.seed());
|
||||
/// let compression_seed = CompressionSeed {
|
||||
/// seed: seeder.seed(),
|
||||
/// };
|
||||
/// let input_lwe_secret_key =
|
||||
/// allocate_and_generate_new_binary_lwe_secret_key(input_lwe_dimension, &mut secret_generator);
|
||||
/// let output_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
/// glwe_dimension,
|
||||
/// polynomial_size,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
/// let chunk_generator = SeededLweBootstrapKeyChunkGenerator::new(
|
||||
/// chunk_size,
|
||||
/// input_lwe_dimension,
|
||||
/// glwe_dimension.to_glwe_size(),
|
||||
/// polynomial_size,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// ciphertext_modulus,
|
||||
/// &input_lwe_secret_key,
|
||||
/// &output_glwe_secret_key,
|
||||
/// glwe_noise_distribution,
|
||||
/// compression_seed,
|
||||
/// seeder,
|
||||
/// false,
|
||||
/// );
|
||||
/// let chunks = chunk_generator.collect::<Vec<_>>();
|
||||
/// let assembled_bsk =
|
||||
/// allocate_and_assemble_seeded_lwe_bootstrap_key_from_chunks(chunks.as_slice());
|
||||
/// ```
|
||||
pub struct SeededLweBootstrapKeyChunkGenerator<'a, Cont, Scalar, NoiseDistribution>
|
||||
where
|
||||
NoiseDistribution: Distribution,
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
Cont: Container<Element = Scalar>,
|
||||
{
|
||||
enc_generator: EncryptionRandomGenerator<DefaultRandomGenerator>,
|
||||
chunk_size: ChunkSize,
|
||||
lwe_dim: LweDimension,
|
||||
glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
input_lwe_secret_key: &'a LweSecretKey<Cont>,
|
||||
output_glwe_secret_key: &'a GlweSecretKey<Cont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
compression_seed: CompressionSeed,
|
||||
position: usize,
|
||||
parallel: bool,
|
||||
}
|
||||
|
||||
impl<'a, Cont, Scalar, NoiseDistribution>
|
||||
SeededLweBootstrapKeyChunkGenerator<'a, Cont, Scalar, NoiseDistribution>
|
||||
where
|
||||
NoiseDistribution: Distribution,
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
Cont: Container<Element = Scalar>,
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::use_self)]
|
||||
pub fn new<NoiseSeeder>(
|
||||
chunk_size: ChunkSize,
|
||||
lwe_dim: LweDimension,
|
||||
glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomposition_base_log: DecompositionBaseLog,
|
||||
decomposition_level_count: DecompositionLevelCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
input_lwe_secret_key: &'a LweSecretKey<Cont>,
|
||||
output_glwe_secret_key: &'a GlweSecretKey<Cont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
compression_seed: CompressionSeed,
|
||||
noise_seeder: &'a mut NoiseSeeder,
|
||||
parallel: bool,
|
||||
) -> SeededLweBootstrapKeyChunkGenerator<'a, Cont, Scalar, NoiseDistribution>
|
||||
where
|
||||
// Maybe Sized allows to pass Box<dyn Seeder>.
|
||||
NoiseSeeder: Seeder + ?Sized,
|
||||
{
|
||||
assert!(chunk_size.0 <= lwe_dim.0);
|
||||
let enc_generator = EncryptionRandomGenerator::<DefaultRandomGenerator>::new(
|
||||
compression_seed.seed,
|
||||
noise_seeder,
|
||||
);
|
||||
Self {
|
||||
enc_generator,
|
||||
chunk_size,
|
||||
lwe_dim,
|
||||
glwe_size,
|
||||
polynomial_size,
|
||||
decomposition_base_log,
|
||||
decomposition_level_count,
|
||||
ciphertext_modulus,
|
||||
input_lwe_secret_key,
|
||||
output_glwe_secret_key,
|
||||
noise_distribution,
|
||||
compression_seed,
|
||||
position: 0,
|
||||
parallel,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Cont, Scalar, NoiseDistribution> Iterator
|
||||
for SeededLweBootstrapKeyChunkGenerator<'_, Cont, Scalar, NoiseDistribution>
|
||||
where
|
||||
NoiseDistribution: Distribution + Sync,
|
||||
Scalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
Cont: Container<Element = Scalar> + Sync,
|
||||
{
|
||||
type Item = SeededLweBootstrapKeyChunkOwned<Scalar>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.chunk_size.0 == 0 || self.position >= self.lwe_dim.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let left = self.lwe_dim.0 - self.position;
|
||||
let chunk_size = if left < self.chunk_size.0 {
|
||||
ChunkSize(left)
|
||||
} else {
|
||||
self.chunk_size
|
||||
};
|
||||
|
||||
let mut chunk = SeededLweBootstrapKeyChunkOwned::new(
|
||||
Scalar::ZERO,
|
||||
self.glwe_size,
|
||||
self.polynomial_size,
|
||||
self.decomposition_base_log,
|
||||
self.decomposition_level_count,
|
||||
chunk_size,
|
||||
self.compression_seed,
|
||||
self.ciphertext_modulus,
|
||||
);
|
||||
|
||||
if self.parallel {
|
||||
par_generate_chunked_seeded_lwe_bootstrap_key(
|
||||
self.input_lwe_secret_key,
|
||||
self.output_glwe_secret_key,
|
||||
&mut chunk,
|
||||
self.noise_distribution,
|
||||
&mut self.enc_generator,
|
||||
self.position,
|
||||
)
|
||||
} else {
|
||||
generate_chunked_seeded_lwe_bootstrap_key(
|
||||
self.input_lwe_secret_key,
|
||||
self.output_glwe_secret_key,
|
||||
&mut chunk,
|
||||
self.noise_distribution,
|
||||
&mut self.enc_generator,
|
||||
self.position,
|
||||
)
|
||||
}
|
||||
|
||||
self.position += chunk_size.0;
|
||||
|
||||
Some(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill a [`seeded LWE bootstrap key chunk`](`SeededLweBootstrapKeyChunk`) with a part of a seeded
|
||||
/// bootstrapping key constructed from a target chunk of an input key [`LWE secret
|
||||
/// key`](`LweSecretKey`) and an output key [`GLWE secret key`](`GlweSecretKey`)
|
||||
///
|
||||
/// Consider using [`par_generate_chunked_seeded_lwe_bootstrap_key`] for better key generation
|
||||
/// times.
|
||||
///
|
||||
/// WARNING: this assumes the caller manages the random generator and the order of generation to
|
||||
/// make sure the key is equivalent to the non-chunked version.
|
||||
pub fn generate_chunked_seeded_lwe_bootstrap_key<
|
||||
InputScalar,
|
||||
OutputScalar,
|
||||
NoiseDistribution,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
OutputCont,
|
||||
Gen,
|
||||
>(
|
||||
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
|
||||
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
|
||||
output: &mut SeededLweBootstrapKeyChunk<OutputCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
chunk_start: usize,
|
||||
) where
|
||||
InputScalar: Copy + CastInto<OutputScalar>,
|
||||
OutputScalar: Encryptable<Uniform, NoiseDistribution>,
|
||||
NoiseDistribution: Distribution,
|
||||
InputKeyCont: Container<Element = InputScalar>,
|
||||
OutputKeyCont: Container<Element = OutputScalar>,
|
||||
OutputCont: ContainerMut<Element = OutputScalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let chunk_end = chunk_start + output.chunk_size().0;
|
||||
assert!(
|
||||
chunk_end <= input_lwe_secret_key.lwe_dimension().0,
|
||||
"Expected chunk out of bound of the input LWE secret key \
|
||||
Chunk ending at: {:?}, Input LWE secret key LweDimension {:?}.",
|
||||
chunk_end,
|
||||
input_lwe_secret_key.lwe_dimension()
|
||||
);
|
||||
|
||||
assert!(
|
||||
output.glwe_size() == output_glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
"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().to_glwe_size(),
|
||||
output.glwe_size()
|
||||
);
|
||||
|
||||
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
|
||||
.try_fork_from_config(output.encryption_fork_config(Uniform, noise_distribution))
|
||||
.unwrap();
|
||||
|
||||
for ((mut ggsw, &input_key_element), mut generator) in output
|
||||
.iter_mut()
|
||||
.zip(input_lwe_secret_key.as_ref()[chunk_start..chunk_end].iter())
|
||||
.zip(gen_iter)
|
||||
{
|
||||
encrypt_constant_seeded_ggsw_ciphertext_with_pre_seeded_generator(
|
||||
output_glwe_secret_key,
|
||||
&mut ggsw,
|
||||
Cleartext(input_key_element.cast_into()),
|
||||
noise_distribution,
|
||||
&mut generator,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Parallel variant of [`generate_chunked_seeded_lwe_bootstrap_key`], it is recommended to use this
|
||||
/// function for better key generation times as LWE bootstrapping keys can be quite large.
|
||||
pub fn par_generate_chunked_seeded_lwe_bootstrap_key<
|
||||
InputScalar,
|
||||
OutputScalar,
|
||||
NoiseDistribution,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
OutputCont,
|
||||
Gen,
|
||||
>(
|
||||
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
|
||||
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
|
||||
output: &mut SeededLweBootstrapKeyChunk<OutputCont>,
|
||||
noise_distribution: NoiseDistribution,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
chunk_start: usize,
|
||||
) where
|
||||
InputScalar: Copy + CastInto<OutputScalar> + Sync,
|
||||
OutputScalar: Encryptable<Uniform, NoiseDistribution> + Sync + Send,
|
||||
NoiseDistribution: Distribution + Sync,
|
||||
InputKeyCont: Container<Element = InputScalar>,
|
||||
OutputKeyCont: Container<Element = OutputScalar> + Sync,
|
||||
OutputCont: ContainerMut<Element = OutputScalar>,
|
||||
Gen: ParallelByteRandomGenerator,
|
||||
{
|
||||
let chunk_end = chunk_start + output.chunk_size().0;
|
||||
assert!(
|
||||
chunk_end <= input_lwe_secret_key.lwe_dimension().0,
|
||||
"Expected chunk out of bound of the input LWE secret key \
|
||||
Chunk ending at: {:?}, Input LWE secret key LweDimension {:?}.",
|
||||
chunk_end,
|
||||
input_lwe_secret_key.lwe_dimension()
|
||||
);
|
||||
|
||||
assert!(
|
||||
output.glwe_size() == output_glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
"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().to_glwe_size(),
|
||||
output.glwe_size()
|
||||
);
|
||||
|
||||
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();
|
||||
|
||||
output
|
||||
.par_iter_mut()
|
||||
.zip(input_lwe_secret_key.as_ref()[chunk_start..chunk_end].par_iter())
|
||||
.zip(gen_iter)
|
||||
.for_each(|((mut ggsw, &input_key_element), mut generator)| {
|
||||
par_encrypt_constant_seeded_ggsw_ciphertext_with_pre_seeded_generator(
|
||||
output_glwe_secret_key,
|
||||
&mut ggsw,
|
||||
Cleartext(input_key_element.cast_into()),
|
||||
noise_distribution,
|
||||
&mut generator,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Assemble a vector of [`SeededLweBootstrapKeyChunk`] into an [`SeededLweBootstrapKey`].
|
||||
///
|
||||
/// This function takes a vector of `SeededLweBootstrapKeyChunk` and assemble them into a single
|
||||
/// `SeededLweBootstrapKey`. It considers that chunks are in the correct order, and that they would
|
||||
/// fill the `SeededLweBootstrapKey`.
|
||||
pub fn assemble_seeded_lwe_bootstrap_key_from_chunks<Scalar, Cont, ContMut>(
|
||||
output: &mut SeededLweBootstrapKey<ContMut>,
|
||||
chunks: &[SeededLweBootstrapKeyChunk<Cont>],
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
Cont: Container<Element = Scalar>,
|
||||
ContMut: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let total_chunk_size: usize = chunks.iter().map(|c| c.chunk_size().0).sum();
|
||||
let chunks_lwe_dimension = LweDimension(total_chunk_size);
|
||||
assert!(chunks_lwe_dimension == output.input_lwe_dimension());
|
||||
|
||||
let mut start: usize = 0;
|
||||
for chunk in chunks {
|
||||
assert!(output.glwe_size() == chunk.glwe_size());
|
||||
assert!(output.polynomial_size() == chunk.polynomial_size());
|
||||
assert!(output.decomposition_base_log() == chunk.decomposition_base_log());
|
||||
assert!(output.decomposition_level_count() == chunk.decomposition_level_count());
|
||||
assert!(output.ciphertext_modulus() == chunk.ciphertext_modulus());
|
||||
assert!(output.compression_seed() == chunk.compression_seed());
|
||||
|
||||
let end = start + chunk.as_ref().len();
|
||||
output.as_mut()[start..end].copy_from_slice(chunk.as_ref());
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate a new [`SeededLweBootstrapKey`] and assemble it from a vector of
|
||||
/// [`SeededLweBootstrapKeyChunk`].
|
||||
///
|
||||
/// This function takes multiple `SeededLweBootstrapKeyChunk` and assemble them into a single
|
||||
/// `SeededLweBootstrapKey`. It considers that chunks are in the correct order.
|
||||
pub fn allocate_and_assemble_seeded_lwe_bootstrap_key_from_chunks<Scalar, Cont>(
|
||||
chunks: &[SeededLweBootstrapKeyChunk<Cont>],
|
||||
) -> SeededLweBootstrapKeyOwned<Scalar>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
Cont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert!(!chunks.is_empty());
|
||||
let glwe_size = chunks[0].glwe_size();
|
||||
let polynomial_size = chunks[0].polynomial_size();
|
||||
let decomp_base_log = chunks[0].decomposition_base_log();
|
||||
let decomp_level_count = chunks[0].decomposition_level_count();
|
||||
let total_chunk_size: usize = chunks.iter().map(|c| c.chunk_size().0).sum();
|
||||
let input_lwe_dimension = LweDimension(total_chunk_size);
|
||||
let ciphertext_modulus = chunks[0].ciphertext_modulus();
|
||||
let compression_seed = chunks[0].compression_seed();
|
||||
|
||||
let mut assembled_bsk = SeededLweBootstrapKey::new(
|
||||
Scalar::ZERO,
|
||||
glwe_size,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_dimension,
|
||||
compression_seed,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
assemble_seeded_lwe_bootstrap_key_from_chunks(&mut assembled_bsk, chunks);
|
||||
|
||||
assembled_bsk
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::core_crypto::algorithms::*;
|
||||
use crate::core_crypto::commons::dispersion::StandardDev;
|
||||
use crate::core_crypto::commons::generators::{DeterministicSeeder, EncryptionRandomGenerator};
|
||||
use crate::core_crypto::commons::math::random::{
|
||||
DefaultRandomGenerator, DynamicDistribution, Seed,
|
||||
CompressionSeed, DefaultRandomGenerator, DynamicDistribution, Seed,
|
||||
};
|
||||
use crate::core_crypto::commons::math::torus::UnsignedTorus;
|
||||
use crate::core_crypto::commons::parameters::{
|
||||
@@ -137,7 +137,9 @@ fn test_parallel_and_seeded_and_chunked_bsk_gen_equivalence<T: UnsignedTorus + S
|
||||
|
||||
assert_eq!(sequential_seeded_bsk, parallel_seeded_bsk);
|
||||
|
||||
let ser_decompressed_bsk = sequential_seeded_bsk.decompress_into_lwe_bootstrap_key();
|
||||
let ser_decompressed_bsk = sequential_seeded_bsk
|
||||
.as_view()
|
||||
.decompress_into_lwe_bootstrap_key();
|
||||
|
||||
assert_eq!(ser_decompressed_bsk, sequential_bsk);
|
||||
|
||||
@@ -191,6 +193,61 @@ fn test_parallel_and_seeded_and_chunked_bsk_gen_equivalence<T: UnsignedTorus + S
|
||||
let chunks = par_chunk_generator.collect::<Vec<_>>();
|
||||
let assembled_bsk = allocate_and_assemble_lwe_bootstrap_key_from_chunks(chunks.as_slice());
|
||||
assert_eq!(assembled_bsk, sequential_bsk);
|
||||
|
||||
let mut noise_seeder =
|
||||
DeterministicSeeder::<DefaultRandomGenerator>::new(deterministic_seeder_seed);
|
||||
let compression_seed = CompressionSeed { seed: mask_seed };
|
||||
let seeded_chunk_generator = SeededLweBootstrapKeyChunkGenerator::new(
|
||||
ChunkSize(crate::core_crypto::commons::test_tools::random_usize_between(1..5)),
|
||||
lwe_dim,
|
||||
glwe_dim.to_glwe_size(),
|
||||
poly_size,
|
||||
base_log,
|
||||
level,
|
||||
ciphertext_modulus,
|
||||
&lwe_sk,
|
||||
&glwe_sk,
|
||||
noise_distribution,
|
||||
compression_seed,
|
||||
&mut noise_seeder,
|
||||
false,
|
||||
);
|
||||
|
||||
let seeded_chunks = seeded_chunk_generator.collect::<Vec<_>>();
|
||||
let assembled_seeded_bsk =
|
||||
allocate_and_assemble_seeded_lwe_bootstrap_key_from_chunks(seeded_chunks.as_slice());
|
||||
assert_eq!(assembled_seeded_bsk, sequential_seeded_bsk);
|
||||
assert_eq!(
|
||||
assembled_seeded_bsk.decompress_into_lwe_bootstrap_key(),
|
||||
sequential_bsk
|
||||
);
|
||||
|
||||
let mut noise_seeder =
|
||||
DeterministicSeeder::<DefaultRandomGenerator>::new(deterministic_seeder_seed);
|
||||
let par_seeded_chunk_generator = SeededLweBootstrapKeyChunkGenerator::new(
|
||||
ChunkSize(crate::core_crypto::commons::test_tools::random_usize_between(1..5)),
|
||||
lwe_dim,
|
||||
glwe_dim.to_glwe_size(),
|
||||
poly_size,
|
||||
base_log,
|
||||
level,
|
||||
ciphertext_modulus,
|
||||
&lwe_sk,
|
||||
&glwe_sk,
|
||||
noise_distribution,
|
||||
compression_seed,
|
||||
&mut noise_seeder,
|
||||
true,
|
||||
);
|
||||
|
||||
let seeded_chunks = par_seeded_chunk_generator.collect::<Vec<_>>();
|
||||
let assembled_seeded_bsk =
|
||||
allocate_and_assemble_seeded_lwe_bootstrap_key_from_chunks(seeded_chunks.as_slice());
|
||||
assert_eq!(assembled_seeded_bsk, sequential_seeded_bsk);
|
||||
assert_eq!(
|
||||
assembled_seeded_bsk.decompress_into_lwe_bootstrap_key(),
|
||||
sequential_bsk
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ pub mod seeded_ggsw_ciphertext_list;
|
||||
pub mod seeded_glwe_ciphertext;
|
||||
pub mod seeded_glwe_ciphertext_list;
|
||||
pub mod seeded_lwe_bootstrap_key;
|
||||
pub mod seeded_lwe_bootstrap_key_chunk;
|
||||
pub mod seeded_lwe_ciphertext;
|
||||
pub mod seeded_lwe_ciphertext_list;
|
||||
pub mod seeded_lwe_compact_public_key;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
use tfhe_versionable::VersionsDispatch;
|
||||
|
||||
use crate::core_crypto::prelude::{Container, SeededLweBootstrapKeyChunk, UnsignedInteger};
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum SeededLweBootstrapKeyChunkVersions<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
V0(SeededLweBootstrapKeyChunk<C>),
|
||||
}
|
||||
@@ -40,6 +40,7 @@ pub mod seeded_ggsw_ciphertext_list;
|
||||
pub mod seeded_glwe_ciphertext;
|
||||
pub mod seeded_glwe_ciphertext_list;
|
||||
pub mod seeded_lwe_bootstrap_key;
|
||||
pub mod seeded_lwe_bootstrap_key_chunk;
|
||||
pub mod seeded_lwe_ciphertext;
|
||||
pub mod seeded_lwe_ciphertext_list;
|
||||
pub mod seeded_lwe_compact_public_key;
|
||||
@@ -98,6 +99,7 @@ pub use seeded_ggsw_ciphertext_list::*;
|
||||
pub use seeded_glwe_ciphertext::*;
|
||||
pub use seeded_glwe_ciphertext_list::*;
|
||||
pub use seeded_lwe_bootstrap_key::*;
|
||||
pub use seeded_lwe_bootstrap_key_chunk::*;
|
||||
pub use seeded_lwe_ciphertext::*;
|
||||
pub use seeded_lwe_ciphertext_list::*;
|
||||
pub use seeded_lwe_compact_public_key::*;
|
||||
|
||||
279
tfhe/src/core_crypto/entities/seeded_lwe_bootstrap_key_chunk.rs
Normal file
279
tfhe/src/core_crypto/entities/seeded_lwe_bootstrap_key_chunk.rs
Normal file
@@ -0,0 +1,279 @@
|
||||
//! Module containing the definition of the SeededLweBootstrapKeyChunk.
|
||||
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
use crate::core_crypto::backward_compatibility::entities::seeded_lwe_bootstrap_key_chunk::SeededLweBootstrapKeyChunkVersions;
|
||||
use crate::core_crypto::commons::math::random::CompressionSeed;
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::named::Named;
|
||||
|
||||
/// A [`seeded LWE bootstrap key chunk`](`SeededLweBootstrapKeyChunk`).
|
||||
///
|
||||
/// This is a wrapper type of [`SeededGgswCiphertextList`], [`std::ops::Deref`] and
|
||||
/// [`std::ops::DerefMut`] are implemented to dereference to the underlying
|
||||
/// [`SeededGgswCiphertextList`] for ease of use. See [`SeededGgswCiphertextList`] for additional
|
||||
/// methods.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Versionize)]
|
||||
#[versionize(SeededLweBootstrapKeyChunkVersions)]
|
||||
pub struct SeededLweBootstrapKeyChunk<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
// An SeededLweBootstrapKeyChunk is literally a SeededGgswCiphertextList, so we wrap a
|
||||
// GgswCiphertextList and use Deref to have access to all the primitives of the
|
||||
// SeededGgswCiphertextList easily
|
||||
ggsw_list: SeededGgswCiphertextList<C>,
|
||||
}
|
||||
|
||||
impl<C: Container> Named for SeededLweBootstrapKeyChunk<C>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
const NAME: &'static str = "core_crypto::SeededLweBootstrapKeyChunk";
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> std::ops::Deref
|
||||
for SeededLweBootstrapKeyChunk<C>
|
||||
{
|
||||
type Target = SeededGgswCiphertextList<C>;
|
||||
|
||||
fn deref(&self) -> &SeededGgswCiphertextList<C> {
|
||||
&self.ggsw_list
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> std::ops::DerefMut
|
||||
for SeededLweBootstrapKeyChunk<C>
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut SeededGgswCiphertextList<C> {
|
||||
&mut self.ggsw_list
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> SeededLweBootstrapKeyChunk<C> {
|
||||
/// Create a [`SeededLweBootstrapKeyChunk`] from an existing container.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function only wraps a container in the appropriate type. If you want to generate an LWE
|
||||
/// bootstrap key chunk you need to use
|
||||
/// [`crate::core_crypto::algorithms::generate_chunked_seeded_lwe_bootstrap_key`] or its
|
||||
/// parallel equivalent
|
||||
/// [`crate::core_crypto::algorithms::par_generate_chunked_seeded_lwe_bootstrap_key`]
|
||||
/// using this key as output.
|
||||
///
|
||||
/// This docstring exhibits [`SeededLweBootstrapKeyChunk`] primitives usage.
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for SeededLweBootstrapKeyChunk creation
|
||||
/// let glwe_size = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let decomp_base_log = DecompositionBaseLog(8);
|
||||
/// let decomp_level_count = DecompositionLevelCount(3);
|
||||
/// let chunk_size = ChunkSize(10);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // Get a seeder
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
///
|
||||
/// // Create a new SeededLweBootstrapKeyChunk
|
||||
/// let bsk_chunk = SeededLweBootstrapKeyChunk::new(
|
||||
/// 0u64,
|
||||
/// glwe_size,
|
||||
/// polynomial_size,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// chunk_size,
|
||||
/// seeder.seed().into(),
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// // These methods are "inherited" from SeededGgswCiphertextList and are accessed through the
|
||||
/// // Deref trait
|
||||
/// assert_eq!(bsk_chunk.glwe_size(), glwe_size);
|
||||
/// assert_eq!(bsk_chunk.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(bsk_chunk.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(bsk_chunk.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(bsk_chunk.ciphertext_modulus(), ciphertext_modulus);
|
||||
///
|
||||
/// // These methods are specific to the SeededLweBootstrapKeyChunk
|
||||
/// assert_eq!(bsk_chunk.chunk_size(), chunk_size);
|
||||
/// assert_eq!(
|
||||
/// bsk_chunk.output_lwe_dimension(),
|
||||
/// glwe_size
|
||||
/// .to_glwe_dimension()
|
||||
/// .to_equivalent_lwe_dimension(polynomial_size)
|
||||
/// );
|
||||
///
|
||||
/// let global_compression_seed = bsk_chunk.compression_seed();
|
||||
///
|
||||
/// // Demonstrate how to recover the allocated container
|
||||
/// let underlying_container: Vec<u64> = bsk_chunk.into_container();
|
||||
///
|
||||
/// // Recreate a key using from_container
|
||||
/// let bsk_chunk = SeededLweBootstrapKeyChunk::from_container(
|
||||
/// underlying_container,
|
||||
/// glwe_size,
|
||||
/// polynomial_size,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// global_compression_seed,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(bsk_chunk.glwe_size(), glwe_size);
|
||||
/// assert_eq!(bsk_chunk.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(bsk_chunk.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(bsk_chunk.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(bsk_chunk.ciphertext_modulus(), ciphertext_modulus);
|
||||
/// assert_eq!(bsk_chunk.chunk_size(), chunk_size);
|
||||
/// assert_eq!(
|
||||
/// bsk_chunk.output_lwe_dimension(),
|
||||
/// glwe_size
|
||||
/// .to_glwe_dimension()
|
||||
/// .to_equivalent_lwe_dimension(polynomial_size)
|
||||
/// );
|
||||
/// ```
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
global_compression_seed: CompressionSeed,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
ciphertext_modulus.is_compatible_with_native_modulus(),
|
||||
"Seeded entities are not yet compatible with non power of 2 moduli."
|
||||
);
|
||||
|
||||
Self {
|
||||
ggsw_list: SeededGgswCiphertextList::from_container(
|
||||
container,
|
||||
glwe_size,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
global_compression_seed,
|
||||
ciphertext_modulus,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the [`ChunkSize`] of the input [`SeededLweBootstrapKeyChunk`].
|
||||
///
|
||||
/// See [`SeededLweBootstrapKeyChunk::from_container`] for usage.
|
||||
pub fn chunk_size(&self) -> ChunkSize {
|
||||
ChunkSize(self.ggsw_ciphertext_count().0)
|
||||
}
|
||||
|
||||
/// Return the [`CompressionSeed`] of the global [`SeededGgswCiphertextList`].
|
||||
///
|
||||
/// This is the seed of the global key, not just a single chunk.
|
||||
///
|
||||
/// See [`SeededLweBootstrapKeyChunk::from_container`] for usage.
|
||||
pub fn compression_seed(&self) -> CompressionSeed {
|
||||
self.ggsw_list.compression_seed()
|
||||
}
|
||||
|
||||
/// Return the [`LweDimension`] of the equivalent output [`LweSecretKey`].
|
||||
///
|
||||
/// See [`SeededLweBootstrapKeyChunk::from_container`] for usage.
|
||||
pub fn output_lwe_dimension(&self) -> LweDimension {
|
||||
self.glwe_size()
|
||||
.to_glwe_dimension()
|
||||
.to_equivalent_lwe_dimension(self.polynomial_size())
|
||||
}
|
||||
|
||||
/// Consume the entity and return its underlying container.
|
||||
///
|
||||
/// See [`SeededLweBootstrapKeyChunk::from_container`] for usage.
|
||||
pub fn into_container(self) -> C {
|
||||
self.ggsw_list.into_container()
|
||||
}
|
||||
|
||||
/// Return a view of the [`SeededLweBootstrapKeyChunk`]. This is useful if an algorithm takes a
|
||||
/// view by value.
|
||||
pub fn as_view(&self) -> SeededLweBootstrapKeyChunk<&'_ [Scalar]> {
|
||||
SeededLweBootstrapKeyChunk::from_container(
|
||||
self.as_ref(),
|
||||
self.glwe_size(),
|
||||
self.polynomial_size(),
|
||||
self.decomposition_base_log(),
|
||||
self.decomposition_level_count(),
|
||||
self.compression_seed(),
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> SeededLweBootstrapKeyChunk<C> {
|
||||
/// Mutable variant of [`SeededLweBootstrapKeyChunk::as_view`].
|
||||
pub fn as_mut_view(&mut self) -> SeededLweBootstrapKeyChunk<&'_ mut [Scalar]> {
|
||||
let glwe_size = self.glwe_size();
|
||||
let polynomial_size = self.polynomial_size();
|
||||
let decomp_base_log = self.decomposition_base_log();
|
||||
let decomp_level_count = self.decomposition_level_count();
|
||||
let global_compression_seed = self.compression_seed();
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
SeededLweBootstrapKeyChunk::from_container(
|
||||
self.as_mut(),
|
||||
glwe_size,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
global_compression_seed,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`SeededLweBootstrapKeyChunk`] owning the memory for its own storage.
|
||||
pub type SeededLweBootstrapKeyChunkOwned<Scalar> = SeededLweBootstrapKeyChunk<Vec<Scalar>>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> SeededLweBootstrapKeyChunkOwned<Scalar> {
|
||||
/// Allocate memory and create a new owned [`SeededLweBootstrapKeyChunk`].
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
|
||||
/// type. If you want to generate an LWE bootstrap key you need to use
|
||||
/// [`crate::core_crypto::algorithms::generate_chunked_seeded_lwe_bootstrap_key`] or its
|
||||
/// parallel equivalent
|
||||
/// [`crate::core_crypto::algorithms::par_generate_chunked_seeded_lwe_bootstrap_key`] using
|
||||
/// this key as output.
|
||||
///
|
||||
/// See [`SeededLweBootstrapKeyChunk::from_container`] for usage.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
chunk_size: ChunkSize,
|
||||
global_compression_seed: CompressionSeed,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> Self {
|
||||
Self {
|
||||
ggsw_list: SeededGgswCiphertextList::new(
|
||||
fill_with,
|
||||
glwe_size,
|
||||
polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
GgswCiphertextCount(chunk_size.0),
|
||||
global_compression_seed,
|
||||
ciphertext_modulus,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user