mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-11 07:38:08 -05:00
Compare commits
1 Commits
mz/factori
...
trace_pack
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d07cef91b |
189
tfhe/src/core_crypto/algorithms/glwe_keyswitch.rs
Normal file
189
tfhe/src/core_crypto/algorithms/glwe_keyswitch.rs
Normal file
@@ -0,0 +1,189 @@
|
||||
//! Module containing primitives pertaining to [`GLWE ciphertext
|
||||
//! keyswitch`](`GlweKeyswitchKey#glwe-keyswitch`).
|
||||
|
||||
use crate::core_crypto::algorithms::polynomial_algorithms::*;
|
||||
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
use crate::core_crypto::commons::numeric::UnsignedInteger;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
/// Keyswitch an [`GLWE ciphertext`](`GlweCiphertext`) encrytped under an
|
||||
/// [`GLWE secret key`](`GlweSecretKey`) to another [`GLWE secret key`](`GlweSecretKey`).
|
||||
///
|
||||
/// # Formal Definition
|
||||
///
|
||||
/// See [`GLWE keyswitch key`](`GlweKeyswitchKey#glwe-keyswitch`).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for LweKeyswitchKey creation
|
||||
/// let input_glwe_dimension = GlweDimension(2);
|
||||
/// let poly_size = PolynomialSize(512);
|
||||
/// let glwe_modular_std_dev = StandardDev(0.000007069849454709433);
|
||||
/// let output_glwe_dimension = GlweDimension(1);
|
||||
/// let decomp_base_log = DecompositionBaseLog(3);
|
||||
/// let decomp_level_count = DecompositionLevelCount(5);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // 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 LweSecretKey
|
||||
/// let input_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
/// input_glwe_dimension,
|
||||
/// poly_size,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
/// let output_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
/// output_glwe_dimension,
|
||||
/// poly_size,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
///
|
||||
/// let ksk = allocate_and_generate_new_glwe_keyswitch_key(
|
||||
/// &input_glwe_secret_key,
|
||||
/// &output_glwe_secret_key,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// glwe_modular_std_dev,
|
||||
/// ciphertext_modulus,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// // Create the plaintext
|
||||
/// let msg = 3u64;
|
||||
/// let plaintext_list = PlaintextList::new(msg << 60, PlaintextCount(poly_size.0));
|
||||
///
|
||||
/// // Create a new GlweCiphertext
|
||||
/// let mut input_glwe = GlweCiphertext::new(
|
||||
/// 0u64,
|
||||
/// input_glwe_dimension.to_glwe_size(),
|
||||
/// poly_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// encrypt_glwe_ciphertext(
|
||||
/// &input_glwe_secret_key,
|
||||
/// &mut input_glwe,
|
||||
/// &plaintext_list,
|
||||
/// glwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// let mut output_glwe = GlweCiphertext::new(
|
||||
/// 0u64,
|
||||
/// output_glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
/// output_glwe_secret_key.polynomial_size(),
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// keyswitch_glwe_ciphertext(&ksk, &mut input_glwe, &mut output_glwe);
|
||||
///
|
||||
/// let mut output_plaintext_list = PlaintextList::new(0u64, plaintext_list.plaintext_count());
|
||||
///
|
||||
/// let decrypted_plaintext = decrypt_glwe_ciphertext(
|
||||
/// &output_glwe_secret_key,
|
||||
/// &output_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 keyswitch_glwe_ciphertext<Scalar, KSKCont, InputCont, OutputCont>(
|
||||
glwe_keyswitch_key: &GlweKeyswitchKey<KSKCont>,
|
||||
input_glwe_ciphertext: &mut GlweCiphertext<InputCont>,
|
||||
output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
KSKCont: Container<Element = Scalar>,
|
||||
InputCont: ContainerMut<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
glwe_keyswitch_key.input_key_glwe_dimension()
|
||||
== input_glwe_ciphertext.glwe_size().to_glwe_dimension(),
|
||||
"Mismatched input GlweDimension. \
|
||||
GlweKeyswitchKey input GlweDimension: {:?}, input GlweCiphertext GlweDimension {:?}.",
|
||||
glwe_keyswitch_key.input_key_glwe_dimension(),
|
||||
input_glwe_ciphertext.glwe_size().to_glwe_dimension(),
|
||||
);
|
||||
assert!(
|
||||
glwe_keyswitch_key.output_key_glwe_dimension()
|
||||
== output_glwe_ciphertext.glwe_size().to_glwe_dimension(),
|
||||
"Mismatched output GlweDimension. \
|
||||
GlweKeyswitchKey output GlweDimension: {:?}, output GlweCiphertext GlweDimension {:?}.",
|
||||
glwe_keyswitch_key.output_key_glwe_dimension(),
|
||||
output_glwe_ciphertext.glwe_size().to_glwe_dimension(),
|
||||
);
|
||||
assert!(
|
||||
glwe_keyswitch_key.polynomial_size() == input_glwe_ciphertext.polynomial_size(),
|
||||
"Mismatched input PolynomialSize. \
|
||||
GlweKeyswithcKey input PolynomialSize: {:?}, input GlweCiphertext PolynomialSize {:?}.",
|
||||
glwe_keyswitch_key.polynomial_size(),
|
||||
input_glwe_ciphertext.polynomial_size(),
|
||||
);
|
||||
assert!(
|
||||
glwe_keyswitch_key.polynomial_size() == output_glwe_ciphertext.polynomial_size(),
|
||||
"Mismatched output PolynomialSize. \
|
||||
GlweKeyswitchKey output PolynomialSize: {:?}, output GlweCiphertext PolynomialSize {:?}.",
|
||||
glwe_keyswitch_key.polynomial_size(),
|
||||
output_glwe_ciphertext.polynomial_size(),
|
||||
);
|
||||
|
||||
// Clear the output ciphertext, as it will get updated gradually
|
||||
output_glwe_ciphertext.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
// Copy the input body to the output ciphertext
|
||||
polynomial_wrapping_add_assign(
|
||||
&mut output_glwe_ciphertext.get_mut_body().as_mut_polynomial(),
|
||||
&input_glwe_ciphertext.get_body().as_polynomial(),
|
||||
);
|
||||
|
||||
// We instantiate a decomposer
|
||||
let decomposer = SignedDecomposer::new(
|
||||
glwe_keyswitch_key.decomposition_base_log(),
|
||||
glwe_keyswitch_key.decomposition_level_count(),
|
||||
);
|
||||
|
||||
for (keyswitch_key_block, input_mask_element) in glwe_keyswitch_key
|
||||
.iter()
|
||||
.zip(input_glwe_ciphertext.get_mask().as_polynomial_list().iter())
|
||||
{
|
||||
let mut decomposition_iter = decomposer.decompose_slice(input_mask_element.as_ref());
|
||||
// loop over the number of levels in reverse (from highest to lowest)
|
||||
for level_key_ciphertext in keyswitch_key_block.iter().rev() {
|
||||
let decomposed = decomposition_iter.next_term().unwrap();
|
||||
polynomial_list_wrapping_sub_scalar_mul_assign(
|
||||
&mut output_glwe_ciphertext.as_mut_polynomial_list(),
|
||||
&level_key_ciphertext.as_polynomial_list(),
|
||||
&Polynomial::from_container(decomposed.as_slice()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
207
tfhe/src/core_crypto/algorithms/glwe_keyswitch_key_generation.rs
Normal file
207
tfhe/src/core_crypto/algorithms/glwe_keyswitch_key_generation.rs
Normal file
@@ -0,0 +1,207 @@
|
||||
//! Module containing primitives pertaining to [`GLWE keyswitch key generation`](`GlweKeyswitchKey`)
|
||||
|
||||
use crate::core_crypto::algorithms::*;
|
||||
use crate::core_crypto::commons::dispersion::DispersionParameter;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, DecompositionTerm};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
/// Fill a [`GLWE keyswitch key`](`GlweKeyswitchKey`) with an actual keyswitching key constructed
|
||||
/// from an input and an output key [`GLWE secret key`](`GlweSecretKey`).
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for LweKeyswitchKey creation
|
||||
/// let input_glwe_dimension = GlweDimension(2);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let glwe_modular_std_dev = StandardDev(0.000007069849454709433);
|
||||
/// let output_glwe_dimension = GlweDimension(1);
|
||||
/// let decomp_base_log = DecompositionBaseLog(3);
|
||||
/// let decomp_level_count = DecompositionLevelCount(5);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // 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 input_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
/// input_glwe_dimension,
|
||||
/// polynomial_size,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
/// let output_glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
/// output_glwe_dimension,
|
||||
/// polynomial_size,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
///
|
||||
/// let mut ksk = GlweKeyswitchKey::new(
|
||||
/// 0u64,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// input_glwe_dimension,
|
||||
/// output_glwe_dimension,
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// generate_glwe_keyswitch_key(
|
||||
/// &input_glwe_secret_key,
|
||||
/// &output_glwe_secret_key,
|
||||
/// &mut ksk,
|
||||
/// glwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// assert!(ksk.as_ref().iter().all(|&x| x == 0) == false);
|
||||
/// ```
|
||||
pub fn generate_glwe_keyswitch_key<Scalar, InputKeyCont, OutputKeyCont, KSKeyCont, Gen>(
|
||||
input_glwe_sk: &GlweSecretKey<InputKeyCont>,
|
||||
output_glwe_sk: &GlweSecretKey<OutputKeyCont>,
|
||||
glwe_keyswitch_key: &mut GlweKeyswitchKey<KSKeyCont>,
|
||||
noise_parameters: impl DispersionParameter,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar>,
|
||||
KSKeyCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert!(
|
||||
glwe_keyswitch_key.input_key_glwe_dimension() == input_glwe_sk.glwe_dimension(),
|
||||
"The destination GlweKeyswitchKey input GlweDimension is not equal \
|
||||
to the input GlweSecretKey GlweDimension. Destination: {:?}, input: {:?}",
|
||||
glwe_keyswitch_key.input_key_glwe_dimension(),
|
||||
input_glwe_sk.glwe_dimension()
|
||||
);
|
||||
assert!(
|
||||
glwe_keyswitch_key.output_key_glwe_dimension() == output_glwe_sk.glwe_dimension(),
|
||||
"The destination GlweKeyswitchKey output GlweDimension is not equal \
|
||||
to the output GlweSecretKey GlweDimension. Destination: {:?}, output: {:?}",
|
||||
glwe_keyswitch_key.output_key_glwe_dimension(),
|
||||
input_glwe_sk.glwe_dimension()
|
||||
);
|
||||
assert!(
|
||||
glwe_keyswitch_key.polynomial_size() == input_glwe_sk.polynomial_size(),
|
||||
"The destination GlweKeyswitchKey input PolynomialSize is not equal \
|
||||
to the input GlweSecretKey PolynomialSize. Destination: {:?}, input: {:?}",
|
||||
glwe_keyswitch_key.polynomial_size(),
|
||||
input_glwe_sk.polynomial_size(),
|
||||
);
|
||||
assert!(
|
||||
glwe_keyswitch_key.polynomial_size() == output_glwe_sk.polynomial_size(),
|
||||
"The distination GlweKeyswitchKey output PolynomialSize is not equal \
|
||||
to the output GlweSecretKey PolynomialSize. Destination: {:?}, output: {:?}",
|
||||
glwe_keyswitch_key.polynomial_size(),
|
||||
output_glwe_sk.polynomial_size(),
|
||||
);
|
||||
|
||||
let decomp_base_log = glwe_keyswitch_key.decomposition_base_log();
|
||||
let decomp_level_count = glwe_keyswitch_key.decomposition_level_count();
|
||||
/*
|
||||
// forking the generator and using loop_generator works to generate glwe keyswitch keys but
|
||||
// break lwe_trace_packing_keyswotch_key_generation
|
||||
let gen_iter = generator
|
||||
.fork_glweks_to_glweks_chunks::<Scalar>(
|
||||
decomp_level_count,
|
||||
input_glwe_sk.glwe_dimension(),
|
||||
output_glwe_sk.glwe_dimension().to_glwe_size(),
|
||||
input_glwe_sk.polynomial_size(),
|
||||
)
|
||||
.unwrap();
|
||||
*/
|
||||
|
||||
// Iterate over the input key elements and the destination glwe_keyswitch_key memory
|
||||
//for ((input_key_polynomial, mut keyswitch_key_block), mut _loop_generator) in input_glwe_sk
|
||||
for (input_key_polynomial, mut keyswitch_key_block) in input_glwe_sk
|
||||
.as_polynomial_list()
|
||||
.iter()
|
||||
.zip(glwe_keyswitch_key.iter_mut())
|
||||
//.zip(gen_iter)
|
||||
{
|
||||
// The plaintexts used to encrypt a key element will be stored in this buffer
|
||||
let mut decomposition_polynomials_buffer = PolynomialList::new(
|
||||
Scalar::ZERO,
|
||||
input_glwe_sk.polynomial_size(),
|
||||
PolynomialCount(decomp_level_count.0),
|
||||
);
|
||||
|
||||
// We fill the buffer with the powers of the key elmements
|
||||
for (level, mut message_polynomial) in (1..=decomp_level_count.0)
|
||||
.map(DecompositionLevel)
|
||||
.zip(decomposition_polynomials_buffer.as_mut_view().iter_mut())
|
||||
{
|
||||
for (message, input_key_element) in message_polynomial
|
||||
.iter_mut()
|
||||
.zip(input_key_polynomial.iter())
|
||||
{
|
||||
*message = DecompositionTerm::new(level, decomp_base_log, *input_key_element)
|
||||
.to_recomposition_summand();
|
||||
}
|
||||
}
|
||||
|
||||
let decomposition_plaintexts_buffer =
|
||||
PlaintextList::from_container(decomposition_polynomials_buffer.into_container());
|
||||
|
||||
encrypt_glwe_ciphertext_list(
|
||||
output_glwe_sk,
|
||||
&mut keyswitch_key_block,
|
||||
&decomposition_plaintexts_buffer,
|
||||
noise_parameters,
|
||||
//&mut loop_generator,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate a new [`GLWE keyswitch key`](`GlweKeyswitchKey`) and fill it with an actual
|
||||
/// keyswitching key constructed from an input and an output key
|
||||
/// [`GLWE secret key`](`GlweSecretKey`).
|
||||
///
|
||||
/// See [`keyswitch_glwe_ciphertext`] for usage.
|
||||
pub fn allocate_and_generate_new_glwe_keyswitch_key<Scalar, InputKeyCont, OutputKeyCont, Gen>(
|
||||
input_glwe_sk: &GlweSecretKey<InputKeyCont>,
|
||||
output_glwe_sk: &GlweSecretKey<OutputKeyCont>,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
noise_parameters: impl DispersionParameter,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) -> GlweKeyswitchKeyOwned<Scalar>
|
||||
where
|
||||
Scalar: UnsignedTorus,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut new_glwe_keyswitch_key = GlweKeyswitchKeyOwned::new(
|
||||
Scalar::ZERO,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_glwe_sk.glwe_dimension(),
|
||||
output_glwe_sk.glwe_dimension(),
|
||||
output_glwe_sk.polynomial_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
generate_glwe_keyswitch_key(
|
||||
input_glwe_sk,
|
||||
output_glwe_sk,
|
||||
&mut new_glwe_keyswitch_key,
|
||||
noise_parameters,
|
||||
generator,
|
||||
);
|
||||
|
||||
new_glwe_keyswitch_key
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
//! Module containing primitives pertaining to [`GLWE relinearisation key
|
||||
//! generation`](`GlweRelinearisationKey`).
|
||||
|
||||
use crate::core_crypto::algorithms::polynomial_algorithms::*;
|
||||
use crate::core_crypto::algorithms::*;
|
||||
use crate::core_crypto::commons::dispersion::DispersionParameter;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount, PolynomialCount};
|
||||
|
||||
/// Fill a [`GLWE relinearisation key`](`GlweRelinearisationKey`)
|
||||
/// with an actual key.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for GlweCiphertext creation
|
||||
/// let glwe_size = GlweSize(3);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let decomp_base_log = DecompositionBaseLog(3);
|
||||
/// let decomp_level_count = DecompositionLevelCount(7);
|
||||
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // 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: GlweSecretKey<Vec<u64>> = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
/// glwe_size.to_glwe_dimension(),
|
||||
/// polynomial_size,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
///
|
||||
/// allocate_and_generate_glwe_relinearisation_key(
|
||||
/// &glwe_secret_key,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// glwe_modular_std_dev,
|
||||
/// ciphertext_modulus,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
/// ```
|
||||
pub fn generate_glwe_relinearisation_key<Scalar, GlweKeyCont, RelinKeyCont, Gen>(
|
||||
glwe_secret_key: &GlweSecretKey<GlweKeyCont>,
|
||||
glwe_relinearisation_key: &mut GlweRelinearisationKey<RelinKeyCont>,
|
||||
noise_parameters: impl DispersionParameter,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
GlweKeyCont: Container<Element = Scalar>,
|
||||
RelinKeyCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert_eq!(
|
||||
glwe_secret_key.glwe_dimension(),
|
||||
glwe_relinearisation_key.glwe_dimension()
|
||||
);
|
||||
assert_eq!(
|
||||
glwe_secret_key.polynomial_size(),
|
||||
glwe_relinearisation_key.polynomial_size()
|
||||
);
|
||||
|
||||
// We retrieve decomposition arguments
|
||||
let glwe_dimension = glwe_relinearisation_key.glwe_dimension();
|
||||
let decomp_level_count = glwe_relinearisation_key.decomposition_level_count();
|
||||
let decomp_base_log = glwe_relinearisation_key.decomposition_base_log();
|
||||
let polynomial_size = glwe_relinearisation_key.polynomial_size();
|
||||
let ciphertext_modulus = glwe_relinearisation_key.ciphertext_modulus();
|
||||
|
||||
// Construct the "glwe secret key" we want to keyswitch from, this is made up of the square
|
||||
// and cross terms appearing when squaring glwe_secret_key
|
||||
let mut input_sk_poly_list = PolynomialList::new(
|
||||
Scalar::ZERO,
|
||||
polynomial_size,
|
||||
PolynomialCount(glwe_dimension.0 * (glwe_dimension.0 + 1) / 2),
|
||||
);
|
||||
let mut input_sk_poly_list_iter = input_sk_poly_list.iter_mut();
|
||||
|
||||
for i in 0..glwe_dimension.0 {
|
||||
for j in 0..i + 1 {
|
||||
let mut input_key_pol = input_sk_poly_list_iter.next().unwrap();
|
||||
polynomial_wrapping_sub_mul_assign(
|
||||
&mut input_key_pol,
|
||||
&glwe_secret_key.as_polynomial_list().get(i),
|
||||
&glwe_secret_key.as_polynomial_list().get(j),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let input_glwe_sk = GlweSecretKey::from_container(input_sk_poly_list.as_ref(), polynomial_size);
|
||||
|
||||
let mut glwe_ks_key = GlweKeyswitchKey::from_container(
|
||||
glwe_relinearisation_key.as_mut(),
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
generate_glwe_keyswitch_key(
|
||||
&input_glwe_sk,
|
||||
glwe_secret_key,
|
||||
&mut glwe_ks_key,
|
||||
noise_parameters,
|
||||
generator,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn allocate_and_generate_glwe_relinearisation_key<Scalar, KeyCont, Gen>(
|
||||
glwe_secret_key: &GlweSecretKey<KeyCont>,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
noise_parameters: impl DispersionParameter,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) -> GlweRelinearisationKeyOwned<Scalar>
|
||||
where
|
||||
Scalar: UnsignedTorus,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let mut glwe_relinearisation_key = GlweRelinearisationKeyOwned::new(
|
||||
Scalar::ZERO,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
glwe_secret_key.polynomial_size(),
|
||||
ciphertext_modulus,
|
||||
);
|
||||
generate_glwe_relinearisation_key(
|
||||
glwe_secret_key,
|
||||
&mut glwe_relinearisation_key,
|
||||
noise_parameters,
|
||||
generator,
|
||||
);
|
||||
|
||||
glwe_relinearisation_key
|
||||
}
|
||||
|
||||
/*
|
||||
* Parallel variant of [`generate_glwe_relinearisation_key`]. You may want to use this
|
||||
* variant for better key generation times.
|
||||
*/
|
||||
457
tfhe/src/core_crypto/algorithms/glwe_tensor_product.rs
Normal file
457
tfhe/src/core_crypto/algorithms/glwe_tensor_product.rs
Normal file
@@ -0,0 +1,457 @@
|
||||
use crate::core_crypto::algorithms::polynomial_algorithms::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::prelude::*;
|
||||
|
||||
/// Compute the tensor product of the left-hand side [`GLWE ciphertext`](`GlweCiphertext`) with the
|
||||
/// right-hand side [`GLWE ciphertext`](`GlweCiphertext`)
|
||||
/// writing the result in the output [`GlweCiphertext<Vec<Scalar>>`](`GlweCiphertext<Vec<Scalar>>`).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for LweCiphertext creation
|
||||
/// let glwe_size = GlweSize(3);
|
||||
/// let polynomial_size = PolynomialSize(512);
|
||||
/// let glwe_modular_std_dev = StandardDev(0.000000000000000000029403601535432533);
|
||||
/// let decomp_base_log = DecompositionBaseLog(3);
|
||||
/// let decomp_level_count = DecompositionLevelCount(7);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// let log_delta1 = 59;
|
||||
/// let log_delta2 = 60;
|
||||
/// let log_delta = std::cmp::min(log_delta1, log_delta2);
|
||||
/// let output_log_delta = log_delta1 + log_delta2 - log_delta;
|
||||
///
|
||||
/// // 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 first plaintext, we encrypt a single integer rather than a general polynomial
|
||||
/// let msg_1 = 3u64;
|
||||
/// let encoded_msg_1 = msg_1 << log_delta1;
|
||||
///
|
||||
/// let mut plaintext_list_1 = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
|
||||
/// plaintext_list_1.as_mut()[0] = encoded_msg_1;
|
||||
///
|
||||
/// // Create the first GlweCiphertext
|
||||
/// let mut glwe_1 = GlweCiphertext::new(0u64, glwe_size, polynomial_size, ciphertext_modulus);
|
||||
///
|
||||
/// encrypt_glwe_ciphertext(
|
||||
/// &glwe_secret_key,
|
||||
/// &mut glwe_1,
|
||||
/// &plaintext_list_1,
|
||||
/// glwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// // Create the second plaintext
|
||||
/// let msg_2 = 2u64;
|
||||
/// let encoded_msg_2 = msg_2 << log_delta2;
|
||||
///
|
||||
/// let mut plaintext_list_2 = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
|
||||
/// plaintext_list_2.as_mut()[0] = encoded_msg_2;
|
||||
///
|
||||
/// // Create the second GlweCiphertext
|
||||
/// let mut glwe_2 = GlweCiphertext::new(0u64, glwe_size, polynomial_size, ciphertext_modulus);
|
||||
///
|
||||
/// encrypt_glwe_ciphertext(
|
||||
/// &glwe_secret_key,
|
||||
/// &mut glwe_2,
|
||||
/// &plaintext_list_2,
|
||||
/// glwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// // Perform the tensor product
|
||||
/// let scale = 1u64 << log_delta;
|
||||
/// let tensor_output = glwe_tensor_product(&glwe_1, &glwe_2, scale);
|
||||
///
|
||||
/// // Compute the tensor product key
|
||||
/// let tensor_glwe_dim = GlweDimension((glwe_size.0 - 1) * (glwe_size.0 + 2) / 2);
|
||||
/// let mut tensor_key_poly_list =
|
||||
/// PolynomialList::new(0u64, polynomial_size, PolynomialCount(tensor_glwe_dim.0));
|
||||
/// let mut key_iter = tensor_key_poly_list.iter_mut();
|
||||
///
|
||||
/// for i in 0..glwe_size.0 - 1 {
|
||||
/// for j in 0..i + 1 {
|
||||
/// let mut key_pol = key_iter.next().unwrap();
|
||||
/// polynomial_wrapping_sub_mul_assign(
|
||||
/// &mut key_pol,
|
||||
/// &glwe_secret_key.as_polynomial_list().get(i),
|
||||
/// &glwe_secret_key.as_polynomial_list().get(j),
|
||||
/// );
|
||||
/// }
|
||||
/// let mut key_pol = key_iter.next().unwrap();
|
||||
/// polynomial_wrapping_add_assign(&mut key_pol, &glwe_secret_key.as_polynomial_list().get(i));
|
||||
/// }
|
||||
///
|
||||
/// let tensor_key = GlweSecretKey::from_container(tensor_key_poly_list.as_ref(), polynomial_size);
|
||||
///
|
||||
/// // Decrypt the tensor product ciphertext
|
||||
/// let mut output_plaintext = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
|
||||
///
|
||||
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
|
||||
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(2), DecompositionLevelCount(4));
|
||||
///
|
||||
/// decrypt_glwe_ciphertext(&tensor_key, &tensor_output, &mut output_plaintext);
|
||||
/// output_plaintext
|
||||
/// .iter_mut()
|
||||
/// .for_each(|elt| *elt.0 = decomposer.closest_representable(*elt.0));
|
||||
///
|
||||
/// // Get the raw vector
|
||||
/// let mut cleartext = output_plaintext.into_container();
|
||||
/// // Remove the encoding
|
||||
/// cleartext
|
||||
/// .iter_mut()
|
||||
/// .for_each(|elt| *elt = *elt >> output_log_delta);
|
||||
/// // Get the list immutably
|
||||
/// let cleartext = cleartext;
|
||||
///
|
||||
/// // Compute what the product should be
|
||||
/// let pt1 = Polynomial::from_container(
|
||||
/// plaintext_list_1
|
||||
/// .into_container()
|
||||
/// .iter()
|
||||
/// .map(|&x| <u64 as CastInto<u128>>::cast_into(x))
|
||||
/// .collect::<Vec<_>>(),
|
||||
/// );
|
||||
/// let pt2 = Polynomial::from_container(
|
||||
/// plaintext_list_2
|
||||
/// .into_container()
|
||||
/// .iter()
|
||||
/// .map(|&x| <u64 as CastInto<u128>>::cast_into(x))
|
||||
/// .collect::<Vec<_>>(),
|
||||
/// );
|
||||
///
|
||||
/// let mut product = Polynomial::new(0u128, polynomial_size);
|
||||
/// polynomial_wrapping_mul(&mut product, &pt1, &pt2);
|
||||
///
|
||||
/// let mut scaled_product = Polynomial::new(0u64, polynomial_size);
|
||||
/// scaled_product
|
||||
/// .as_mut()
|
||||
/// .iter_mut()
|
||||
/// .zip(product.as_ref().iter())
|
||||
/// .for_each(|(dest, &source)| {
|
||||
/// *dest = u64::cast_from(source / <u64 as CastInto<u128>>::cast_into(scale))
|
||||
/// >> output_log_delta
|
||||
/// });
|
||||
///
|
||||
/// // Check we recovered the correct message
|
||||
/// cleartext
|
||||
/// .iter()
|
||||
/// .zip(scaled_product.iter())
|
||||
/// .for_each(|(&elt, coeff)| assert_eq!(elt, *coeff));
|
||||
///
|
||||
/// let glwe_relin_key = allocate_and_generate_glwe_relinearisation_key(
|
||||
/// &glwe_secret_key,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// glwe_modular_std_dev,
|
||||
/// ciphertext_modulus,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// let mut output_glwe_ciphertext =
|
||||
/// GlweCiphertext::new(0u64, glwe_size, polynomial_size, ciphertext_modulus);
|
||||
///
|
||||
/// glwe_relinearisation(&tensor_output, &glwe_relin_key, &mut output_glwe_ciphertext);
|
||||
///
|
||||
/// // Decrypt the output glwe ciphertext
|
||||
/// let mut output_plaintext = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
|
||||
///
|
||||
/// decrypt_glwe_ciphertext(
|
||||
/// &glwe_secret_key,
|
||||
/// &output_glwe_ciphertext,
|
||||
/// &mut output_plaintext,
|
||||
/// );
|
||||
/// output_plaintext
|
||||
/// .iter_mut()
|
||||
/// .for_each(|elt| *elt.0 = decomposer.closest_representable(*elt.0));
|
||||
///
|
||||
/// // Get the raw vector
|
||||
/// let mut cleartext = output_plaintext.into_container();
|
||||
/// // Remove the encoding
|
||||
/// cleartext
|
||||
/// .iter_mut()
|
||||
/// .for_each(|elt| *elt = *elt >> output_log_delta);
|
||||
/// // Get the list immutably
|
||||
/// let cleartext = cleartext;
|
||||
///
|
||||
/// // Check we recovered the correct message
|
||||
/// cleartext
|
||||
/// .iter()
|
||||
/// .zip(scaled_product.iter())
|
||||
/// .for_each(|(&elt, coeff)| assert_eq!(elt, *coeff));
|
||||
/// ```
|
||||
/// based on algorithm 1 of `<https://eprint.iacr.org/2021/729.pdf>` in the eprint paper the result
|
||||
/// of the division is rounded,
|
||||
/// here the division in u128 performs a floor hence the induced error might be twice as large
|
||||
pub fn glwe_tensor_product<InputCont, Scalar>(
|
||||
input_glwe_ciphertext_lhs: &GlweCiphertext<InputCont>,
|
||||
input_glwe_ciphertext_rhs: &GlweCiphertext<InputCont>,
|
||||
scale: Scalar,
|
||||
) -> GlweCiphertext<Vec<Scalar>>
|
||||
where
|
||||
Scalar: UnsignedTorus + CastInto<u128> + CastFrom<u128>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
input_glwe_ciphertext_lhs.polynomial_size().0
|
||||
== input_glwe_ciphertext_rhs.polynomial_size().0,
|
||||
"The input glwe ciphertexts do not have the same polynomial size. The polynomial size of \
|
||||
the lhs is {}, while for the rhs it is {}.",
|
||||
input_glwe_ciphertext_lhs.polynomial_size().0,
|
||||
input_glwe_ciphertext_rhs.polynomial_size().0
|
||||
);
|
||||
|
||||
assert!(
|
||||
input_glwe_ciphertext_lhs.glwe_size().0 == input_glwe_ciphertext_rhs.glwe_size().0,
|
||||
"The input glwe ciphertexts do not have the same glwe size. The glwe size of the lhs is \
|
||||
{}, while for the rhs it is {}.",
|
||||
input_glwe_ciphertext_lhs.glwe_size().0,
|
||||
input_glwe_ciphertext_rhs.glwe_size().0
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_glwe_ciphertext_lhs.ciphertext_modulus(),
|
||||
input_glwe_ciphertext_rhs.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let k = input_glwe_ciphertext_lhs.glwe_size().to_glwe_dimension().0;
|
||||
|
||||
// This is k + k*(k-1)/2 + k: k square terms, k*(k-1)/2 cross terms, k linear terms
|
||||
let new_k = GlweDimension(k * (k + 3) / 2);
|
||||
|
||||
let mut output_glwe_ciphertext = GlweCiphertextOwned::new(
|
||||
Scalar::ZERO,
|
||||
new_k.to_glwe_size(),
|
||||
input_glwe_ciphertext_lhs.polynomial_size(),
|
||||
input_glwe_ciphertext_lhs.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
let mut output_mask = output_glwe_ciphertext.get_mut_mask();
|
||||
let mut output_mask_poly_list = output_mask.as_mut_polynomial_list();
|
||||
let mut iter_output_mask = output_mask_poly_list.iter_mut();
|
||||
let input_lhs = PolynomialList::from_container(
|
||||
input_glwe_ciphertext_lhs
|
||||
.get_mask()
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|&x| <Scalar as CastInto<u128>>::cast_into(x))
|
||||
.collect::<Vec<_>>(),
|
||||
input_glwe_ciphertext_lhs.polynomial_size(),
|
||||
);
|
||||
let input_rhs = PolynomialList::from_container(
|
||||
input_glwe_ciphertext_rhs
|
||||
.get_mask()
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|&x| <Scalar as CastInto<u128>>::cast_into(x))
|
||||
.collect::<Vec<_>>(),
|
||||
input_glwe_ciphertext_rhs.polynomial_size(),
|
||||
);
|
||||
|
||||
for (i, a_lhs_i) in input_lhs.iter().enumerate() {
|
||||
for (j, a_rhs_j) in input_rhs.iter().enumerate() {
|
||||
if i == j {
|
||||
//tensor elements corresponding to key -s_i^2
|
||||
let mut temp_poly_sq = Polynomial::new(0u128, a_lhs_i.polynomial_size());
|
||||
polynomial_wrapping_add_mul_assign(&mut temp_poly_sq, &a_lhs_i, &a_rhs_j);
|
||||
|
||||
let mut output_poly_sq = iter_output_mask.next().unwrap();
|
||||
output_poly_sq
|
||||
.as_mut()
|
||||
.iter_mut()
|
||||
.zip(temp_poly_sq.as_ref().iter())
|
||||
.for_each(|(dest, &source)| {
|
||||
*dest =
|
||||
Scalar::cast_from(source / <Scalar as CastInto<u128>>::cast_into(scale))
|
||||
});
|
||||
|
||||
//tensor elements corresponding to key s_i
|
||||
let mut temp_poly_s1 = Polynomial::new(0u128, a_lhs_i.polynomial_size());
|
||||
polynomial_wrapping_add_mul_assign(
|
||||
&mut temp_poly_s1,
|
||||
&a_lhs_i,
|
||||
&Polynomial::from_container(
|
||||
input_glwe_ciphertext_rhs
|
||||
.get_body()
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|&x| <Scalar as CastInto<u128>>::cast_into(x))
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
);
|
||||
|
||||
let mut temp_poly_s2 = Polynomial::new(0u128, a_lhs_i.polynomial_size());
|
||||
polynomial_wrapping_add_mul_assign(
|
||||
&mut temp_poly_s2,
|
||||
&Polynomial::from_container(
|
||||
input_glwe_ciphertext_lhs
|
||||
.get_body()
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|&x| <Scalar as CastInto<u128>>::cast_into(x))
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
&a_rhs_j,
|
||||
);
|
||||
|
||||
polynomial_wrapping_add_assign(&mut temp_poly_s1, &temp_poly_s2);
|
||||
let mut output_poly_s = iter_output_mask.next().unwrap();
|
||||
output_poly_s
|
||||
.as_mut()
|
||||
.iter_mut()
|
||||
.zip(temp_poly_s1.as_ref().iter())
|
||||
.for_each(|(dest, &source)| {
|
||||
*dest =
|
||||
Scalar::cast_from(source / <Scalar as CastInto<u128>>::cast_into(scale))
|
||||
});
|
||||
} else {
|
||||
//when i and j are different we only compute the terms where j < i
|
||||
if j < i {
|
||||
//tensor element corresponding to key -s_i*s_j
|
||||
let mut temp_poly = Polynomial::new(0u128, a_lhs_i.polynomial_size());
|
||||
polynomial_wrapping_add_mul_assign(&mut temp_poly, &a_lhs_i, &a_rhs_j);
|
||||
polynomial_wrapping_add_mul_assign(
|
||||
&mut temp_poly,
|
||||
&input_lhs.get(j),
|
||||
&input_rhs.get(i),
|
||||
);
|
||||
|
||||
let mut output_poly = iter_output_mask.next().unwrap();
|
||||
output_poly
|
||||
.as_mut()
|
||||
.iter_mut()
|
||||
.zip(temp_poly.as_ref().iter())
|
||||
.for_each(|(dest, &source)| {
|
||||
*dest = Scalar::cast_from(
|
||||
source / <Scalar as CastInto<u128>>::cast_into(scale),
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//tensor element corresponding to the body
|
||||
let mut temp_poly_body = Polynomial::new(0u128, input_glwe_ciphertext_lhs.polynomial_size());
|
||||
polynomial_wrapping_add_mul_assign(
|
||||
&mut temp_poly_body,
|
||||
&Polynomial::from_container(
|
||||
input_glwe_ciphertext_lhs
|
||||
.get_body()
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|&x| <Scalar as CastInto<u128>>::cast_into(x))
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
&Polynomial::from_container(
|
||||
input_glwe_ciphertext_rhs
|
||||
.get_body()
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|&x| <Scalar as CastInto<u128>>::cast_into(x))
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
);
|
||||
let mut output_body = output_glwe_ciphertext.get_mut_body();
|
||||
let mut output_poly_body = output_body.as_mut_polynomial();
|
||||
output_poly_body
|
||||
.as_mut()
|
||||
.iter_mut()
|
||||
.zip(temp_poly_body.as_ref().iter())
|
||||
.for_each(|(dest, &source)| {
|
||||
*dest = Scalar::cast_from(source / <Scalar as CastInto<u128>>::cast_into(scale))
|
||||
});
|
||||
output_glwe_ciphertext
|
||||
}
|
||||
|
||||
/// Relinearise the [`GLWE ciphertext`](`GlweCiphertext`) that is output by the
|
||||
/// glwe_tensor_product operation using a [`GLWE relinearisation key`](`GlweRelinearisationKey`).
|
||||
pub fn glwe_relinearisation<InputCont, KeyCont, OutputCont, Scalar>(
|
||||
input_glwe_ciphertext: &GlweCiphertext<InputCont>,
|
||||
relinearisation_key: &GlweRelinearisationKey<KeyCont>,
|
||||
output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
relinearisation_key.glwe_dimension().0 * (relinearisation_key.glwe_dimension().0 + 3) / 2,
|
||||
input_glwe_ciphertext.glwe_size().to_glwe_dimension().0
|
||||
);
|
||||
assert_eq!(
|
||||
relinearisation_key.glwe_size(),
|
||||
output_glwe_ciphertext.glwe_size()
|
||||
);
|
||||
assert_eq!(
|
||||
relinearisation_key.polynomial_size(),
|
||||
input_glwe_ciphertext.polynomial_size()
|
||||
);
|
||||
assert_eq!(
|
||||
relinearisation_key.polynomial_size(),
|
||||
output_glwe_ciphertext.polynomial_size()
|
||||
);
|
||||
|
||||
// Clear the output ciphertext, as it will get updated gradually
|
||||
output_glwe_ciphertext.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
// Copy the input body to the output ciphertext
|
||||
polynomial_wrapping_add_assign(
|
||||
&mut output_glwe_ciphertext.get_mut_body().as_mut_polynomial(),
|
||||
&input_glwe_ciphertext.get_body().as_polynomial(),
|
||||
);
|
||||
|
||||
// We instantiate a decomposer
|
||||
let decomposer = SignedDecomposer::new(
|
||||
relinearisation_key.decomposition_base_log(),
|
||||
relinearisation_key.decomposition_level_count(),
|
||||
);
|
||||
|
||||
let mut relin_key_iter = relinearisation_key.iter();
|
||||
let input_glwe_mask = input_glwe_ciphertext.get_mask();
|
||||
let input_glwe_mask_poly_list = input_glwe_mask.as_polynomial_list();
|
||||
let mut input_poly_iter = input_glwe_mask_poly_list.iter();
|
||||
|
||||
for i in 0..relinearisation_key.glwe_size().0 - 1 {
|
||||
for _ in 0..i + 1 {
|
||||
let ksk = relin_key_iter.next().unwrap();
|
||||
let pol = input_poly_iter.next().unwrap();
|
||||
let mut decomposition_iter = decomposer.decompose_slice(pol.as_ref());
|
||||
// loop over the number of levels in reverse (from highest to lowest)
|
||||
for level_key_ciphertext in ksk.iter().rev() {
|
||||
let decomposed = decomposition_iter.next_term().unwrap();
|
||||
polynomial_list_wrapping_sub_scalar_mul_assign(
|
||||
&mut output_glwe_ciphertext.as_mut_polynomial_list(),
|
||||
&level_key_ciphertext.as_polynomial_list(),
|
||||
&Polynomial::from_container(decomposed.as_slice()),
|
||||
);
|
||||
}
|
||||
}
|
||||
let pol = input_poly_iter.next().unwrap();
|
||||
polynomial_wrapping_add_assign(
|
||||
&mut output_glwe_ciphertext.as_mut_polynomial_list().get_mut(i),
|
||||
&pol,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
//! Module containing primitives pertaining to LWE ciphertext private functional keyswitch and
|
||||
//! packing keyswitch.
|
||||
//!
|
||||
//! Formal description can be found in: \
|
||||
//! Chillotti, I., Gama, N., Georgieva, M. et al. \
|
||||
//! TFHE: Fast Fully Homomorphic Encryption Over the Torus. \
|
||||
//! J. Cryptol 33, 34–91 (2020). \
|
||||
//! <https://doi.org/10.1007/s00145-019-09319-x>
|
||||
|
||||
use crate::core_crypto::algorithms::polynomial_algorithms::*;
|
||||
//use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
//use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
/// Apply a public functional packing keyswitch on an input
|
||||
/// [`LWE ciphertext list`](`LweCiphertextList`) and write
|
||||
/// the result in an output [`GLWE ciphertext`](`GlweCiphertext`).
|
||||
/// # Example
|
||||
/// ```
|
||||
/// //define the inputs for the public functional key switching
|
||||
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// let lwe_dimension = LweDimension(742);
|
||||
///
|
||||
/// // Create the PRNG
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut secret_generator =
|
||||
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
///
|
||||
/// let lwe_secret_key: LweSecretKeyOwned<u64> =
|
||||
/// LweSecretKey::generate_new_binary(lwe_dimension, &mut secret_generator);
|
||||
/// let glwe_size = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let glwe_secret_key: GlweSecretKeyOwned<u64> = GlweSecretKey::generate_new_binary(
|
||||
/// glwe_size.to_glwe_dimension(),
|
||||
/// polynomial_size,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
/// let decomp_base_log = DecompositionBaseLog(8);
|
||||
/// let decomp_level_count = DecompositionLevelCount(3);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
/// let mut lwe_pubfpksk = LwePublicFunctionalPackingKeyswitchKey::new(
|
||||
/// 0u64,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// lwe_dimension,
|
||||
/// glwe_size,
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
|
||||
/// let mut encryption_generator =
|
||||
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
/// generate_lwe_public_functional_packing_keyswitch_key(
|
||||
/// &lwe_secret_key,
|
||||
/// &glwe_secret_key,
|
||||
/// &mut lwe_pubfpksk,
|
||||
/// glwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// let lwe_ciphertext_count = LweCiphertextCount(20);
|
||||
/// let mut lwe_list = LweCiphertextList::new(
|
||||
/// 0u64,
|
||||
/// lwe_dimension.to_lwe_size(),
|
||||
/// lwe_ciphertext_count,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
/// let lwe_plaintext_list = PlaintextList::new(1u64 << 59, PlaintextCount(20));
|
||||
/// encrypt_lwe_ciphertext_list(
|
||||
/// &lwe_secret_key,
|
||||
/// &mut lwe_list,
|
||||
/// &lwe_plaintext_list,
|
||||
/// glwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// let mut output_glwe_ciphertext =
|
||||
/// GlweCiphertext::new(0u64, glwe_size, polynomial_size, ciphertext_modulus);
|
||||
/// public_functional_keyswitch_lwe_ciphertexts_into_glwe_ciphertext(
|
||||
/// &mut lwe_pubfpksk,
|
||||
/// &mut output_glwe_ciphertext,
|
||||
/// &lwe_list,
|
||||
/// |mut x| {
|
||||
/// let mut sum = 0u64;
|
||||
/// x.iter().for_each(|y| sum = sum.wrapping_add(*y));
|
||||
/// let mut temp = vec![sum];
|
||||
/// temp.resize(polynomial_size.0, 0u64);
|
||||
/// Polynomial::from_container(temp)
|
||||
/// },
|
||||
/// );
|
||||
///
|
||||
/// let mut output_plaintext_list = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
|
||||
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(1), DecompositionLevelCount(4));
|
||||
///
|
||||
/// decrypt_glwe_ciphertext(
|
||||
/// &glwe_secret_key,
|
||||
/// &output_glwe_ciphertext,
|
||||
/// &mut output_plaintext_list,
|
||||
/// );
|
||||
/// output_plaintext_list
|
||||
/// .iter_mut()
|
||||
/// .for_each(|x| *x.0 = decomposer.closest_representable(*x.0));
|
||||
///
|
||||
/// // Get the raw vecor
|
||||
/// let mut cleartext = output_plaintext_list.into_container();
|
||||
/// // Remove the encoding
|
||||
/// cleartext.iter_mut().for_each(|x| *x = *x >> 59);
|
||||
/// // Get the list immutably
|
||||
/// let cleartext = cleartext;
|
||||
///
|
||||
/// // Check we get the correct result
|
||||
/// for (index, clear) in cleartext.iter().enumerate() {
|
||||
/// if index == 0 {
|
||||
/// assert_eq!(20, *clear);
|
||||
/// } else {
|
||||
/// assert_eq!(0, *clear);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn public_functional_keyswitch_lwe_ciphertexts_into_glwe_ciphertext<
|
||||
KeyCont,
|
||||
InputCont,
|
||||
OutputCont,
|
||||
Func,
|
||||
Scalar,
|
||||
>(
|
||||
lwe_pubfpksk: &LwePublicFunctionalPackingKeyswitchKey<KeyCont>,
|
||||
output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>,
|
||||
input_lwe_ciphertext_list: &LweCiphertextList<InputCont>,
|
||||
f: Func,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
Func: Fn(Vec<Scalar>) -> Polynomial<Vec<Scalar>>,
|
||||
{
|
||||
assert_eq!(
|
||||
output_glwe_ciphertext.polynomial_size(),
|
||||
lwe_pubfpksk.output_polynomial_size()
|
||||
);
|
||||
assert_eq!(
|
||||
output_glwe_ciphertext.glwe_size(),
|
||||
lwe_pubfpksk.output_glwe_size()
|
||||
);
|
||||
assert_eq!(
|
||||
input_lwe_ciphertext_list.lwe_size().to_lwe_dimension(),
|
||||
lwe_pubfpksk.input_lwe_key_dimension()
|
||||
);
|
||||
//evaluate the function on this list of first elements
|
||||
let mut list_output_function =
|
||||
Vec::with_capacity(input_lwe_ciphertext_list.lwe_ciphertext_count().0);
|
||||
for i in 0..input_lwe_ciphertext_list.lwe_size().to_lwe_dimension().0 {
|
||||
//get list of ith elements of the input lwes
|
||||
let vec_of_ai: Vec<Scalar> = input_lwe_ciphertext_list
|
||||
.iter()
|
||||
.map(|lwe| lwe.get_mask().as_ref()[i])
|
||||
.collect();
|
||||
list_output_function.push(f(vec_of_ai));
|
||||
}
|
||||
// We reset the output
|
||||
output_glwe_ciphertext.as_mut().fill(Scalar::ZERO);
|
||||
let vec_of_b: Vec<Scalar> = input_lwe_ciphertext_list
|
||||
.iter()
|
||||
.map(|lwe| *lwe.get_body().data)
|
||||
.collect();
|
||||
|
||||
assert!(f(vec_of_b.clone()).polynomial_size() == output_glwe_ciphertext.polynomial_size(),
|
||||
"the polynomial size of the output_glwe_ciphertext value needs to be equal to the polynomial size of the output of the function f");
|
||||
|
||||
//initiate the body of the output glwe ciphertext
|
||||
output_glwe_ciphertext
|
||||
.get_mut_body()
|
||||
.as_mut()
|
||||
.copy_from_slice(f(vec_of_b).as_ref());
|
||||
|
||||
//decompose the result of the function
|
||||
// We instantiate a decomposer
|
||||
let decomposer = SignedDecomposer::new(
|
||||
lwe_pubfpksk.decomposition_base_log(),
|
||||
lwe_pubfpksk.decomposition_level_count(),
|
||||
);
|
||||
for (keyswitch_key_block, output_function) in
|
||||
lwe_pubfpksk.iter().zip(list_output_function.iter_mut())
|
||||
{
|
||||
let mut decomposition_iter = decomposer.decompose_slice(output_function.as_ref());
|
||||
// loop over the number of levels in reverse (from highest to lowest)
|
||||
for level_key_ciphertext in keyswitch_key_block.iter().rev() {
|
||||
let decomposed = decomposition_iter.next_term().unwrap();
|
||||
polynomial_list_wrapping_sub_scalar_mul_assign(
|
||||
&mut output_glwe_ciphertext.as_mut_polynomial_list(),
|
||||
&level_key_ciphertext.as_polynomial_list(),
|
||||
&Polynomial::from_container(decomposed.as_slice()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
//! Module containing primitives pertaining to [`LWE public functional packing keyswitch key
|
||||
//! generation`](`LwePublicFunctionalPackingKeyswitchKey`).
|
||||
|
||||
use crate::core_crypto::algorithms::*;
|
||||
use crate::core_crypto::commons::dispersion::DispersionParameter;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, DecompositionTerm};
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use rayon::prelude::*;
|
||||
|
||||
/// Fill an [`LWE public functional packing keyswitch
|
||||
/// key`](`LwePublicFunctionalPackingKeyswitchKey`) with an actual key.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
/// let lwe_dimension = LweDimension(8);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // Create the PRNG
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut secret_generator =
|
||||
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
///
|
||||
/// let input_lwe_secret_key =
|
||||
/// LweSecretKey::generate_new_binary(lwe_dimension, &mut secret_generator);
|
||||
/// let glwe_size = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let output_glwe_secret_key = GlweSecretKey::generate_new_binary(
|
||||
/// glwe_size.to_glwe_dimension(),
|
||||
/// polynomial_size,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
///
|
||||
/// let decomp_base_log = DecompositionBaseLog(8);
|
||||
/// let decomp_level_count = DecompositionLevelCount(3);
|
||||
/// let mut lwe_pubfpksk = LwePublicFunctionalPackingKeyswitchKey::new(
|
||||
/// 0u64,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// lwe_dimension,
|
||||
/// glwe_size,
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
|
||||
/// let mut encryption_generator =
|
||||
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
/// generate_lwe_public_functional_packing_keyswitch_key(
|
||||
/// &input_lwe_secret_key,
|
||||
/// &output_glwe_secret_key,
|
||||
/// &mut lwe_pubfpksk,
|
||||
/// glwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
/// ```
|
||||
pub fn generate_lwe_public_functional_packing_keyswitch_key<
|
||||
Scalar,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
KSKeyCont,
|
||||
Gen,
|
||||
>(
|
||||
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
|
||||
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
|
||||
lwe_pubfpksk: &mut LwePublicFunctionalPackingKeyswitchKey<KSKeyCont>,
|
||||
noise_parameters: impl DispersionParameter + Sync,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar>,
|
||||
KSKeyCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert!(
|
||||
input_lwe_secret_key.lwe_dimension() == lwe_pubfpksk.input_lwe_key_dimension(),
|
||||
"Mismatched LweDimension between input_lwe_secret_key {:?} and lwe_pfpksk input dimension \
|
||||
{:?}.",
|
||||
input_lwe_secret_key.lwe_dimension(),
|
||||
lwe_pubfpksk.input_lwe_key_dimension()
|
||||
);
|
||||
assert!(
|
||||
output_glwe_secret_key.glwe_dimension() == lwe_pubfpksk.output_glwe_key_dimension(),
|
||||
"Mismatched GlweDimension between output_glwe_secret_key {:?} and lwe_pfpksk output \
|
||||
dimension {:?}.",
|
||||
output_glwe_secret_key.glwe_dimension(),
|
||||
lwe_pubfpksk.output_glwe_key_dimension()
|
||||
);
|
||||
assert!(
|
||||
output_glwe_secret_key.polynomial_size() == lwe_pubfpksk.output_polynomial_size(),
|
||||
"Mismatched PolynomialSize between output_glwe_secret_key {:?} and lwe_pfpksk output \
|
||||
polynomial size {:?}.",
|
||||
output_glwe_secret_key.polynomial_size(),
|
||||
lwe_pubfpksk.output_polynomial_size()
|
||||
);
|
||||
|
||||
// We instantiate a buffer
|
||||
let mut messages = PlaintextListOwned::new(
|
||||
Scalar::ZERO,
|
||||
PlaintextCount(
|
||||
lwe_pubfpksk.decomposition_level_count().0 * lwe_pubfpksk.output_polynomial_size().0,
|
||||
),
|
||||
);
|
||||
|
||||
// We retrieve decomposition arguments
|
||||
let decomp_level_count = lwe_pubfpksk.decomposition_level_count();
|
||||
let decomp_base_log = lwe_pubfpksk.decomposition_base_log();
|
||||
let polynomial_size = lwe_pubfpksk.output_polynomial_size();
|
||||
|
||||
let last_key_iter_bit = [Scalar::MAX];
|
||||
// add minus one for the function which will be applied to the decomposed body
|
||||
// ( Scalar::MAX = -Scalar::ONE )
|
||||
let input_key_bit_iter = input_lwe_secret_key
|
||||
.as_ref()
|
||||
.iter()
|
||||
.chain(last_key_iter_bit.iter());
|
||||
|
||||
//TODO should we rename fork_pfpksk_to_pfpksk_chunks?
|
||||
let gen_iter = generator
|
||||
.fork_pfpksk_to_pfpksk_chunks::<Scalar>(
|
||||
decomp_level_count,
|
||||
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
output_glwe_secret_key.polynomial_size(),
|
||||
input_lwe_secret_key.lwe_dimension().to_lwe_size(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// loop over the before key blocks
|
||||
for ((&input_key_bit, mut keyswitch_key_block), mut loop_generator) in input_key_bit_iter
|
||||
.zip(lwe_pubfpksk.iter_mut())
|
||||
.zip(gen_iter)
|
||||
{
|
||||
//we reset the buffer
|
||||
|
||||
// We fill the buffer with the powers of the decomposition scale times the key bits
|
||||
for (level, mut message) in (1..=decomp_level_count.0)
|
||||
.map(DecompositionLevel)
|
||||
.zip(messages.chunks_exact_mut(polynomial_size.0))
|
||||
{
|
||||
message.as_mut()[0] = DecompositionTerm::new(level, decomp_base_log, input_key_bit)
|
||||
.to_recomposition_summand();
|
||||
}
|
||||
|
||||
// We encrypt the buffer
|
||||
encrypt_glwe_ciphertext_list(
|
||||
output_glwe_secret_key,
|
||||
&mut keyswitch_key_block,
|
||||
&messages,
|
||||
noise_parameters,
|
||||
&mut loop_generator,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parallel variant of [`generate_lwe_public_functional_packing_keyswitch_key`]. You may want to
|
||||
/// use this variant for better key generation times.
|
||||
pub fn par_generate_lwe_public_functional_packing_keyswitch_key<
|
||||
Scalar,
|
||||
InputKeyCont,
|
||||
OutputKeyCont,
|
||||
KSKeyCont,
|
||||
Gen,
|
||||
>(
|
||||
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
|
||||
output_glwe_secret_key: &GlweSecretKey<OutputKeyCont>,
|
||||
lwe_pubfpksk: &mut LwePublicFunctionalPackingKeyswitchKey<KSKeyCont>,
|
||||
noise_parameters: impl DispersionParameter + Sync,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus + Sync + Send,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: Container<Element = Scalar> + Sync,
|
||||
KSKeyCont: ContainerMut<Element = Scalar> + Sync,
|
||||
Gen: ParallelByteRandomGenerator,
|
||||
{
|
||||
assert!(
|
||||
input_lwe_secret_key.lwe_dimension() == lwe_pubfpksk.input_lwe_key_dimension(),
|
||||
"Mismatched LweDimension between input_lwe_secret_key {:?} and lwe_pfpksk input dimension \
|
||||
{:?}.",
|
||||
input_lwe_secret_key.lwe_dimension(),
|
||||
lwe_pubfpksk.input_lwe_key_dimension()
|
||||
);
|
||||
assert!(
|
||||
output_glwe_secret_key.glwe_dimension() == lwe_pubfpksk.output_glwe_key_dimension(),
|
||||
"Mismatched GlweDimension between output_glwe_secret_key {:?} and lwe_pfpksk output \
|
||||
dimension {:?}.",
|
||||
output_glwe_secret_key.glwe_dimension(),
|
||||
lwe_pubfpksk.output_glwe_key_dimension()
|
||||
);
|
||||
assert!(
|
||||
output_glwe_secret_key.polynomial_size() == lwe_pubfpksk.output_polynomial_size(),
|
||||
"Mismatched PolynomialSize between output_glwe_secret_key {:?} and lwe_pfpksk output \
|
||||
polynomial size {:?}.",
|
||||
output_glwe_secret_key.polynomial_size(),
|
||||
lwe_pubfpksk.output_polynomial_size()
|
||||
);
|
||||
|
||||
// We retrieve decomposition arguments
|
||||
let decomp_level_count = lwe_pubfpksk.decomposition_level_count();
|
||||
let decomp_base_log = lwe_pubfpksk.decomposition_base_log();
|
||||
let polynomial_size = lwe_pubfpksk.output_polynomial_size();
|
||||
|
||||
let last_key_iter_bit = [Scalar::MAX];
|
||||
// add minus one for the function which will be applied to the decomposed body
|
||||
// ( Scalar::MAX = -Scalar::ONE )
|
||||
let input_key_bit_iter = input_lwe_secret_key
|
||||
.as_ref()
|
||||
.par_iter()
|
||||
.chain(last_key_iter_bit.par_iter());
|
||||
|
||||
let gen_iter = generator
|
||||
.par_fork_pfpksk_to_pfpksk_chunks::<Scalar>(
|
||||
decomp_level_count,
|
||||
output_glwe_secret_key.glwe_dimension().to_glwe_size(),
|
||||
output_glwe_secret_key.polynomial_size(),
|
||||
input_lwe_secret_key.lwe_dimension().to_lwe_size(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let plaintext_count = PlaintextCount(
|
||||
lwe_pubfpksk.decomposition_level_count().0 * lwe_pubfpksk.output_polynomial_size().0,
|
||||
);
|
||||
|
||||
// loop over the before key blocks
|
||||
input_key_bit_iter
|
||||
.zip(lwe_pubfpksk.par_iter_mut())
|
||||
.zip(gen_iter)
|
||||
.for_each(
|
||||
|((&input_key_bit, mut keyswitch_key_block), mut loop_generator)| {
|
||||
// We instantiate a buffer
|
||||
let mut messages = PlaintextListOwned::new(Scalar::ZERO, plaintext_count);
|
||||
|
||||
// We fill the buffer with the powers of the key bits
|
||||
for (level, mut message) in (1..=decomp_level_count.0)
|
||||
.map(DecompositionLevel)
|
||||
.zip(messages.chunks_exact_mut(polynomial_size.0))
|
||||
{
|
||||
message.as_mut()[0] =
|
||||
DecompositionTerm::new(level, decomp_base_log, input_key_bit)
|
||||
.to_recomposition_summand();
|
||||
}
|
||||
|
||||
// We encrypt the buffer
|
||||
encrypt_glwe_ciphertext_list(
|
||||
output_glwe_secret_key,
|
||||
&mut keyswitch_key_block,
|
||||
&messages,
|
||||
noise_parameters,
|
||||
&mut loop_generator,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::core_crypto::commons::generators::DeterministicSeeder;
|
||||
use crate::core_crypto::commons::math::random::Seed;
|
||||
use crate::core_crypto::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_pubfpksk_list_gen_equivalence() {
|
||||
const NB_TESTS: usize = 10;
|
||||
|
||||
for _ in 0..NB_TESTS {
|
||||
// DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield
|
||||
// correct computations
|
||||
let glwe_dimension =
|
||||
GlweDimension(crate::core_crypto::commons::test_tools::random_usize_between(5..10));
|
||||
let polynomial_size = PolynomialSize(
|
||||
crate::core_crypto::commons::test_tools::random_usize_between(5..10),
|
||||
);
|
||||
let pubfpksk_level_count = DecompositionLevelCount(
|
||||
crate::core_crypto::commons::test_tools::random_usize_between(2..5),
|
||||
);
|
||||
let pubfpksk_base_log = DecompositionBaseLog(
|
||||
crate::core_crypto::commons::test_tools::random_usize_between(2..5),
|
||||
);
|
||||
let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
|
||||
let common_encryption_seed =
|
||||
Seed(crate::core_crypto::commons::test_tools::random_uint_between(0..u128::MAX));
|
||||
|
||||
let var_small = Variance::from_variance(2f64.powf(-80.0));
|
||||
|
||||
// Create the PRNG
|
||||
let mut seeder = new_seeder();
|
||||
let mut secret_generator =
|
||||
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
|
||||
let glwe_sk: GlweSecretKeyOwned<u64> = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
&mut secret_generator,
|
||||
);
|
||||
let lwe_big_sk = glwe_sk.clone().into_lwe_secret_key();
|
||||
|
||||
let mut par_pubfpksk = LwePublicFunctionalPackingKeyswitchKey::new(
|
||||
0u64,
|
||||
pubfpksk_base_log,
|
||||
pubfpksk_level_count,
|
||||
lwe_big_sk.lwe_dimension(),
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let mut ser_pubfpksk = LwePublicFunctionalPackingKeyswitchKey::new(
|
||||
0u64,
|
||||
pubfpksk_base_log,
|
||||
pubfpksk_level_count,
|
||||
lwe_big_sk.lwe_dimension(),
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let mut seeder =
|
||||
DeterministicSeeder::<ActivatedRandomGenerator>::new(common_encryption_seed);
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(
|
||||
seeder.seed(),
|
||||
&mut seeder,
|
||||
);
|
||||
|
||||
par_generate_lwe_public_functional_packing_keyswitch_key(
|
||||
&lwe_big_sk,
|
||||
&glwe_sk,
|
||||
&mut par_pubfpksk,
|
||||
var_small,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
let mut seeder =
|
||||
DeterministicSeeder::<ActivatedRandomGenerator>::new(common_encryption_seed);
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(
|
||||
seeder.seed(),
|
||||
&mut seeder,
|
||||
);
|
||||
|
||||
generate_lwe_public_functional_packing_keyswitch_key(
|
||||
&lwe_big_sk,
|
||||
&glwe_sk,
|
||||
&mut ser_pubfpksk,
|
||||
var_small,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
|
||||
assert_eq!(par_pubfpksk, ser_pubfpksk)
|
||||
}
|
||||
}
|
||||
}
|
||||
330
tfhe/src/core_crypto/algorithms/lwe_trace_packing_keyswitch.rs
Normal file
330
tfhe/src/core_crypto/algorithms/lwe_trace_packing_keyswitch.rs
Normal file
@@ -0,0 +1,330 @@
|
||||
//! Module containing primitives pertaining to LWE trace pacling keyswitch.
|
||||
|
||||
use crate::core_crypto::algorithms::glwe_keyswitch::*;
|
||||
use crate::core_crypto::algorithms::polynomial_algorithms::*;
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
/// Apply a trace packing keyswitch on an input [`LWE ciphertext list`](`LweCiphertextList`) and
|
||||
/// pack the result in an output [`GLWE ciphertext`](`GlweCiphertext`).
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for LweTracePackingKeyswitchKey creation
|
||||
/// let lwe_dimension = LweDimension(60);
|
||||
/// let lwe_count = LweCiphertextCount(25);
|
||||
/// let polynomial_size = PolynomialSize(32);
|
||||
/// let glwe_dimension = GlweDimension(2);
|
||||
/// let lwe_modular_std_dev = StandardDev(0.00000000000000000000001);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let mut secret_generator =
|
||||
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
/// let lwe_secret_key =
|
||||
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
|
||||
///
|
||||
/// let mut glwe_secret_key = GlweSecretKey::new_empty_key(0u64, glwe_dimension, polynomial_size);
|
||||
///
|
||||
/// generate_tpksk_output_glwe_secret_key(&lwe_secret_key, &mut glwe_secret_key);
|
||||
///
|
||||
/// let decomp_base_log = DecompositionBaseLog(2);
|
||||
/// let decomp_level_count = DecompositionLevelCount(8);
|
||||
/// let var_small = Variance::from_variance(2f64.powf(-90.0));
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut encryption_generator =
|
||||
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
///
|
||||
/// let mut lwe_tpksk = LweTracePackingKeyswitchKey::new(
|
||||
/// 0u64,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// lwe_dimension.to_lwe_size(),
|
||||
/// glwe_dimension.to_glwe_size(),
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// generate_lwe_trace_packing_keyswitch_key(
|
||||
/// &glwe_secret_key,
|
||||
/// &mut lwe_tpksk,
|
||||
/// var_small,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// let mut lwe_ctxt_list = LweCiphertextList::new(
|
||||
/// 0u64,
|
||||
/// lwe_dimension.to_lwe_size(),
|
||||
/// lwe_count,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// let msg = 1u64;
|
||||
/// let plaintext_list = PlaintextList::new(msg << 56, PlaintextCount(lwe_count.0));
|
||||
///
|
||||
/// encrypt_lwe_ciphertext_list(
|
||||
/// &lwe_secret_key,
|
||||
/// &mut lwe_ctxt_list,
|
||||
/// &plaintext_list,
|
||||
/// lwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// let mut output_glwe_ciphertext = GlweCiphertext::new(
|
||||
/// 0u64,
|
||||
/// glwe_dimension.to_glwe_size(),
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// let mut indices = vec![0_usize; lwe_count.0];
|
||||
/// for (index, item) in indices.iter_mut().enumerate() {
|
||||
/// *item = index;
|
||||
/// }
|
||||
///
|
||||
/// trace_packing_keyswitch_lwe_ciphertext_list_into_glwe_ciphertext(
|
||||
/// &lwe_tpksk,
|
||||
/// &mut output_glwe_ciphertext,
|
||||
/// &lwe_ctxt_list,
|
||||
/// indices,
|
||||
/// );
|
||||
///
|
||||
/// let mut output_plaintext_list = PlaintextList::new(0u64, PlaintextCount(polynomial_size.0));
|
||||
///
|
||||
/// decrypt_glwe_ciphertext(
|
||||
/// &glwe_secret_key,
|
||||
/// &output_glwe_ciphertext,
|
||||
/// &mut output_plaintext_list,
|
||||
/// );
|
||||
///
|
||||
/// // Round and remove encoding
|
||||
/// // First create a decomposer working on the high 8 bits corresponding to our encoding.
|
||||
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(8), 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
|
||||
/// // Due to not having an inverse of 2 the packing multiplies each value by polynomial_size
|
||||
/// // Hence the encoding delta is multiplied by polynomial_size
|
||||
/// cleartext_list
|
||||
/// .iter_mut()
|
||||
/// .for_each(|elt| *elt = *elt >> 56 + polynomial_size.log2().0);
|
||||
/// // Get the list immutably
|
||||
/// let cleartext_list = cleartext_list;
|
||||
///
|
||||
/// // Check we recovered the original message for each plaintext we encrypted
|
||||
/// for (index, elt) in cleartext_list.iter().enumerate() {
|
||||
/// if index < lwe_count.0 {
|
||||
/// assert_eq!(*elt, msg);
|
||||
/// } else {
|
||||
/// assert_eq!(*elt, 0);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn trace_packing_keyswitch_lwe_ciphertext_list_into_glwe_ciphertext<
|
||||
Scalar,
|
||||
KeyCont,
|
||||
InputCont,
|
||||
OutputCont,
|
||||
>(
|
||||
lwe_tpksk: &LweTracePackingKeyswitchKey<KeyCont>,
|
||||
output_glwe_ciphertext: &mut GlweCiphertext<OutputCont>,
|
||||
input_lwe_ciphertext_list: &LweCiphertextList<InputCont>,
|
||||
indices: Vec<usize>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
input_lwe_ciphertext_list.lwe_ciphertext_count().0
|
||||
<= output_glwe_ciphertext.polynomial_size().0
|
||||
);
|
||||
assert_eq!(
|
||||
input_lwe_ciphertext_list.lwe_ciphertext_count().0,
|
||||
indices.len()
|
||||
);
|
||||
assert_eq!(
|
||||
input_lwe_ciphertext_list.lwe_size(),
|
||||
lwe_tpksk.input_lwe_size()
|
||||
);
|
||||
assert!(indices
|
||||
.iter()
|
||||
.all(|&x| x < output_glwe_ciphertext.polynomial_size().0));
|
||||
assert_eq!(
|
||||
output_glwe_ciphertext.polynomial_size(),
|
||||
lwe_tpksk.polynomial_size()
|
||||
);
|
||||
assert_eq!(
|
||||
output_glwe_ciphertext.glwe_size(),
|
||||
lwe_tpksk.output_glwe_size()
|
||||
);
|
||||
assert_eq!(
|
||||
input_lwe_ciphertext_list.ciphertext_modulus(),
|
||||
lwe_tpksk.ciphertext_modulus()
|
||||
);
|
||||
assert_eq!(
|
||||
output_glwe_ciphertext.ciphertext_modulus(),
|
||||
lwe_tpksk.ciphertext_modulus()
|
||||
);
|
||||
|
||||
// We reset the output
|
||||
output_glwe_ciphertext.as_mut().fill(Scalar::ZERO);
|
||||
|
||||
let poly_size = output_glwe_ciphertext.polynomial_size();
|
||||
let glwe_count = GlweCiphertextCount(poly_size.0);
|
||||
let ciphertext_modulus = output_glwe_ciphertext.ciphertext_modulus();
|
||||
|
||||
let mut glwe_list = GlweCiphertextList::new(
|
||||
Scalar::ZERO,
|
||||
output_glwe_ciphertext.glwe_size(),
|
||||
poly_size,
|
||||
glwe_count,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
// Construct the initial Glwe Ciphertexts
|
||||
for (index1, mut glwe_ct) in glwe_list.iter_mut().enumerate() {
|
||||
for (index2, index) in indices.iter().enumerate() {
|
||||
if index1 == *index {
|
||||
let lwe_ct = input_lwe_ciphertext_list.get(index2);
|
||||
let lwe_body = lwe_ct.as_ref().last().unwrap();
|
||||
let lwe_mask = lwe_ct.get_mask();
|
||||
for (index3, mut ring_element) in glwe_ct
|
||||
.get_mut_mask()
|
||||
.as_mut_polynomial_list()
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
{
|
||||
for (index4, coef) in ring_element.iter_mut().enumerate() {
|
||||
if index3 * poly_size.0 + index4 < lwe_mask.lwe_dimension().0 {
|
||||
*coef =
|
||||
coef.wrapping_add(lwe_mask.as_ref()[index3 * poly_size.0 + index4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut poly_to_add = Polynomial::new(Scalar::ZERO, poly_size);
|
||||
poly_to_add[0] = poly_to_add[0].wrapping_add(*lwe_body);
|
||||
polynomial_wrapping_add_assign(
|
||||
&mut glwe_ct.get_mut_body().as_mut_polynomial(),
|
||||
&poly_to_add,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for l in 0..poly_size.log2().0 {
|
||||
for i in 0..(poly_size.0 / 2_usize.pow(l as u32 + 1)) {
|
||||
let ct_0 = glwe_list.get(i);
|
||||
let glwe_size = ct_0.glwe_size();
|
||||
let j = (poly_size.0 / 2_usize.pow(l as u32 + 1)) + i;
|
||||
let ct_1 = glwe_list.get(j);
|
||||
if ct_0.as_ref().iter().any(|&x| x != Scalar::ZERO)
|
||||
|| ct_1.as_ref().iter().any(|&x| x != Scalar::ZERO)
|
||||
{
|
||||
// Rotate ct_1 by N/2^(l+1)
|
||||
for mut pol in glwe_list.get_mut(j).as_mut_polynomial_list().iter_mut() {
|
||||
polynomial_wrapping_monic_monomial_mul_assign(
|
||||
&mut pol,
|
||||
MonomialDegree(poly_size.0 / 2_usize.pow(l as u32 + 1)),
|
||||
);
|
||||
}
|
||||
|
||||
let mut ct_plus =
|
||||
GlweCiphertext::new(Scalar::ZERO, glwe_size, poly_size, ciphertext_modulus);
|
||||
let mut ct_minus =
|
||||
GlweCiphertext::new(Scalar::ZERO, glwe_size, poly_size, ciphertext_modulus);
|
||||
|
||||
for ((mut pol_plus, pol_0), pol_1) in ct_plus
|
||||
.as_mut_polynomial_list()
|
||||
.iter_mut()
|
||||
.zip(glwe_list.get(i).as_polynomial_list().iter())
|
||||
.zip(glwe_list.get(j).as_polynomial_list().iter())
|
||||
{
|
||||
polynomial_wrapping_add_assign(&mut pol_plus, &pol_0);
|
||||
polynomial_wrapping_add_assign(&mut pol_plus, &pol_1);
|
||||
}
|
||||
|
||||
for ((mut pol_minus, pol_0), pol_1) in ct_minus
|
||||
.as_mut_polynomial_list()
|
||||
.iter_mut()
|
||||
.zip(glwe_list.get(i).as_polynomial_list().iter())
|
||||
.zip(glwe_list.get(j).as_polynomial_list().iter())
|
||||
{
|
||||
polynomial_wrapping_add_assign(&mut pol_minus, &pol_0);
|
||||
polynomial_wrapping_sub_assign(&mut pol_minus, &pol_1);
|
||||
}
|
||||
|
||||
// Now we should scale both ct_plus and ct_minus by 2^-1 mod q = (q+1) / 2 for odd q
|
||||
|
||||
let scalar = Scalar::ONE; // change to (q + 1)/2 for odd q
|
||||
if !ciphertext_modulus.is_power_of_two() {
|
||||
// Set scalar to (q + 1)/2
|
||||
}
|
||||
for mut pol in ct_plus.as_mut_polynomial_list().iter_mut() {
|
||||
polynomial_wrapping_scalar_mul_assign(&mut pol, scalar);
|
||||
}
|
||||
for mut pol in ct_minus.as_mut_polynomial_list().iter_mut() {
|
||||
polynomial_wrapping_scalar_mul_assign(&mut pol, scalar);
|
||||
}
|
||||
|
||||
// Apply the automorphism sending X to X^(2^(l+1) + 1) to ct_minus
|
||||
for mut pol in ct_minus.as_mut_polynomial_list().iter_mut() {
|
||||
apply_automorphism_assign(&mut pol, 2_usize.pow(l as u32 + 1) + 1)
|
||||
}
|
||||
|
||||
let mut ks_out = GlweCiphertext::new(
|
||||
Scalar::ZERO,
|
||||
ct_minus.glwe_size(),
|
||||
poly_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
|
||||
let glwe_ksk = GlweKeyswitchKey::from_container(
|
||||
lwe_tpksk.get(l).into_container(),
|
||||
lwe_tpksk.decomposition_base_log(),
|
||||
lwe_tpksk.decomposition_level_count(),
|
||||
lwe_tpksk.output_glwe_size(),
|
||||
lwe_tpksk.polynomial_size(),
|
||||
lwe_tpksk.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
// Perform a Glwe keyswitch on ct_minus
|
||||
keyswitch_glwe_ciphertext(&glwe_ksk, &mut ct_minus, &mut ks_out);
|
||||
|
||||
// Set ct_0 to zero
|
||||
glwe_list.get_mut(i).as_mut().fill(Scalar::ZERO);
|
||||
|
||||
// Add the result to ct_plus and add this to ct_0
|
||||
for ((mut pol_plus, pol_ks), mut pol_0) in ct_plus
|
||||
.as_mut_polynomial_list()
|
||||
.iter_mut()
|
||||
.zip(ks_out.as_polynomial_list().iter())
|
||||
.zip(glwe_list.get_mut(i).as_mut_polynomial_list().iter_mut())
|
||||
{
|
||||
polynomial_wrapping_add_assign(&mut pol_plus, &pol_ks);
|
||||
polynomial_wrapping_add_assign(&mut pol_0, &pol_plus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let res = glwe_list.get(0);
|
||||
output_glwe_ciphertext.as_mut().fill(Scalar::ZERO);
|
||||
for (mut pol_out, pol_res) in output_glwe_ciphertext
|
||||
.as_mut_polynomial_list()
|
||||
.iter_mut()
|
||||
.zip(res.as_polynomial_list().iter())
|
||||
{
|
||||
polynomial_wrapping_add_assign(&mut pol_out, &pol_res);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
//! Module containing primitives pertaining to [`LWE trace packing keyswitch key
|
||||
//! generation`](`LweTracePackingKeyswitchKey`).
|
||||
|
||||
use crate::core_crypto::algorithms::*;
|
||||
use crate::core_crypto::commons::dispersion::DispersionParameter;
|
||||
use crate::core_crypto::commons::generators::EncryptionRandomGenerator;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::prelude::polynomial_algorithms::apply_automorphism_wrapping_add_assign;
|
||||
|
||||
/// Fill a [`GLWE secret key`](`GlweSecretKey`) with an actual key derived from an
|
||||
/// [`LWE secret key`](`LweSecretKey`) for use in the [`LWE trace packing keyswitch key`]
|
||||
/// (`LweTracePackingKeyswitchKey`)
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for GlweCiphertext creation
|
||||
/// let glwe_size = GlweSize(3);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let lwe_dimension = LweDimension(900);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let mut secret_generator =
|
||||
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
/// let lwe_secret_key =
|
||||
/// allocate_and_generate_new_binary_lwe_secret_key(lwe_dimension, &mut secret_generator);
|
||||
///
|
||||
/// let mut glwe_secret_key =
|
||||
/// GlweSecretKey::new_empty_key(0u64, glwe_size.to_glwe_dimension(), polynomial_size);
|
||||
///
|
||||
/// generate_tpksk_output_glwe_secret_key(&lwe_secret_key, &mut glwe_secret_key);
|
||||
///
|
||||
/// let decomp_base_log = DecompositionBaseLog(2);
|
||||
/// let decomp_level_count = DecompositionLevelCount(8);
|
||||
/// let var_small = Variance::from_variance(2f64.powf(-80.0));
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut encryption_generator =
|
||||
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
///
|
||||
/// let mut lwe_tpksk = LweTracePackingKeyswitchKey::new(
|
||||
/// 0u64,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// lwe_dimension.to_lwe_size(),
|
||||
/// glwe_size,
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// generate_lwe_trace_packing_keyswitch_key(
|
||||
/// &glwe_secret_key,
|
||||
/// &mut lwe_tpksk,
|
||||
/// var_small,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
/// ```
|
||||
pub fn generate_tpksk_output_glwe_secret_key<Scalar, InputKeyCont, OutputKeyCont>(
|
||||
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
|
||||
output_glwe_secret_key: &mut GlweSecretKey<OutputKeyCont>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
OutputKeyCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let lwe_dimension = input_lwe_secret_key.lwe_dimension();
|
||||
let glwe_dimension = output_glwe_secret_key.glwe_dimension();
|
||||
let glwe_poly_size = output_glwe_secret_key.polynomial_size();
|
||||
|
||||
assert!(
|
||||
lwe_dimension.0 <= glwe_dimension.0 * glwe_poly_size.0,
|
||||
"Mismatched between input_lwe_secret_key dimension {:?} and number of coefficients of \
|
||||
output_glwe_secret_key {:?}.",
|
||||
lwe_dimension.0,
|
||||
glwe_dimension.0 * glwe_poly_size.0
|
||||
);
|
||||
|
||||
let glwe_key_container = output_glwe_secret_key.as_mut();
|
||||
|
||||
for (index, lwe_key_bit) in input_lwe_secret_key.as_ref().iter().enumerate() {
|
||||
if index % glwe_poly_size.0 == 0 {
|
||||
glwe_key_container[index] = *lwe_key_bit;
|
||||
} else {
|
||||
let rem = index % glwe_poly_size.0;
|
||||
let quo = index / glwe_poly_size.0;
|
||||
let new_index = (quo + 1) * glwe_poly_size.0 - rem;
|
||||
glwe_key_container[new_index] = Scalar::ZERO.wrapping_sub(*lwe_key_bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill an [`LWE trace packing keyswitch key`](`LweTracePackingKeyswitchKey`)
|
||||
/// with an actual key.
|
||||
pub fn generate_lwe_trace_packing_keyswitch_key<Scalar, InputKeyCont, KSKeyCont, Gen>(
|
||||
input_glwe_secret_key: &GlweSecretKey<InputKeyCont>,
|
||||
lwe_tpksk: &mut LweTracePackingKeyswitchKey<KSKeyCont>,
|
||||
noise_parameters: impl DispersionParameter,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) where
|
||||
Scalar: UnsignedTorus,
|
||||
InputKeyCont: Container<Element = Scalar>,
|
||||
KSKeyCont: ContainerMut<Element = Scalar>,
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
assert_eq!(
|
||||
input_glwe_secret_key.glwe_dimension(),
|
||||
lwe_tpksk.output_glwe_key_dimension()
|
||||
);
|
||||
assert_eq!(
|
||||
input_glwe_secret_key.polynomial_size(),
|
||||
lwe_tpksk.polynomial_size()
|
||||
);
|
||||
|
||||
// We retrieve decomposition arguments
|
||||
let glwe_dimension = lwe_tpksk.output_glwe_key_dimension();
|
||||
let decomp_level_count = lwe_tpksk.decomposition_level_count();
|
||||
let decomp_base_log = lwe_tpksk.decomposition_base_log();
|
||||
let polynomial_size = lwe_tpksk.polynomial_size();
|
||||
let ciphertext_modulus = lwe_tpksk.ciphertext_modulus();
|
||||
|
||||
let automorphism_index_iter = 1..=polynomial_size.log2().0;
|
||||
|
||||
let gen_iter = generator
|
||||
.fork_tpksk_to_tpksk_chunks::<Scalar>(
|
||||
decomp_level_count,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// loop over the before key blocks
|
||||
|
||||
for ((auto_index, glwe_keyswitch_block), mut loop_generator) in automorphism_index_iter
|
||||
.zip(lwe_tpksk.iter_mut())
|
||||
.zip(gen_iter)
|
||||
{
|
||||
let mut auto_glwe_sk = GlweSecretKey::new_empty_key(
|
||||
Scalar::ZERO,
|
||||
input_glwe_secret_key.glwe_dimension(),
|
||||
input_glwe_secret_key.polynomial_size(),
|
||||
);
|
||||
let input_key_block_iter = input_glwe_secret_key
|
||||
.as_ref()
|
||||
.chunks_exact(polynomial_size.0);
|
||||
let auto_key_block_iter = auto_glwe_sk.as_mut().chunks_exact_mut(polynomial_size.0);
|
||||
for (auto_key_block, input_key_block) in auto_key_block_iter.zip(input_key_block_iter) {
|
||||
let mut output_poly = Polynomial::from_container(auto_key_block);
|
||||
let input_poly = Polynomial::from_container(input_key_block);
|
||||
apply_automorphism_wrapping_add_assign(
|
||||
&mut output_poly,
|
||||
&input_poly,
|
||||
2_usize.pow(auto_index as u32) + 1,
|
||||
);
|
||||
}
|
||||
let mut glwe_ksk = GlweKeyswitchKey::from_container(
|
||||
glwe_keyswitch_block.into_container(),
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
);
|
||||
generate_glwe_keyswitch_key(
|
||||
&auto_glwe_sk,
|
||||
input_glwe_secret_key,
|
||||
&mut glwe_ksk,
|
||||
noise_parameters,
|
||||
&mut loop_generator,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,12 @@
|
||||
pub mod ggsw_conversion;
|
||||
pub mod ggsw_encryption;
|
||||
pub mod glwe_encryption;
|
||||
pub mod glwe_keyswitch;
|
||||
pub mod glwe_keyswitch_key_generation;
|
||||
pub mod glwe_relinearisation_key_generation;
|
||||
pub mod glwe_sample_extraction;
|
||||
pub mod glwe_secret_key_generation;
|
||||
pub mod glwe_tensor_product;
|
||||
pub mod lwe_bootstrap_key_conversion;
|
||||
pub mod lwe_bootstrap_key_generation;
|
||||
pub mod lwe_encryption;
|
||||
@@ -19,8 +23,12 @@ pub mod lwe_multi_bit_programmable_bootstrapping;
|
||||
pub mod lwe_private_functional_packing_keyswitch;
|
||||
pub mod lwe_private_functional_packing_keyswitch_key_generation;
|
||||
pub mod lwe_programmable_bootstrapping;
|
||||
pub mod lwe_public_functional_packing_keyswitch;
|
||||
pub mod lwe_public_functional_packing_keyswitch_key_generation;
|
||||
pub mod lwe_public_key_generation;
|
||||
pub mod lwe_secret_key_generation;
|
||||
pub mod lwe_trace_packing_keyswitch;
|
||||
pub mod lwe_trace_packing_keyswitch_key_generation;
|
||||
pub mod lwe_wopbs;
|
||||
pub mod polynomial_algorithms;
|
||||
pub mod seeded_ggsw_ciphertext_decompression;
|
||||
@@ -42,8 +50,12 @@ mod test;
|
||||
pub use ggsw_conversion::*;
|
||||
pub use ggsw_encryption::*;
|
||||
pub use glwe_encryption::*;
|
||||
pub use glwe_keyswitch::*;
|
||||
pub use glwe_keyswitch_key_generation::*;
|
||||
pub use glwe_relinearisation_key_generation::*;
|
||||
pub use glwe_sample_extraction::*;
|
||||
pub use glwe_secret_key_generation::*;
|
||||
pub use glwe_tensor_product::*;
|
||||
pub use lwe_bootstrap_key_conversion::*;
|
||||
pub use lwe_bootstrap_key_generation::*;
|
||||
pub use lwe_encryption::*;
|
||||
@@ -56,8 +68,12 @@ pub use lwe_multi_bit_programmable_bootstrapping::*;
|
||||
pub use lwe_private_functional_packing_keyswitch::*;
|
||||
pub use lwe_private_functional_packing_keyswitch_key_generation::*;
|
||||
pub use lwe_programmable_bootstrapping::*;
|
||||
pub use lwe_public_functional_packing_keyswitch::*;
|
||||
pub use lwe_public_functional_packing_keyswitch_key_generation::*;
|
||||
pub use lwe_public_key_generation::*;
|
||||
pub use lwe_secret_key_generation::*;
|
||||
pub use lwe_trace_packing_keyswitch::*;
|
||||
pub use lwe_trace_packing_keyswitch_key_generation::*;
|
||||
pub use lwe_wopbs::*;
|
||||
pub use seeded_ggsw_ciphertext_decompression::*;
|
||||
pub use seeded_ggsw_ciphertext_list_decompression::*;
|
||||
|
||||
@@ -406,6 +406,33 @@ pub fn polynomial_wrapping_mul<Scalar, OutputCont, LhsCont, RhsCont>(
|
||||
polynomial_wrapping_add_mul_assign(output, lhs, rhs);
|
||||
}
|
||||
|
||||
/// Multiply a polynomial by a scalar.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
|
||||
/// unsigned integer capacity.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::algorithms::polynomial_algorithms::*;
|
||||
/// use tfhe::core_crypto::entities::*;
|
||||
/// let mut pol = Polynomial::from_container(vec![1u8, 2, 3, 4, 5, 6]);
|
||||
/// let scalar = 127u8;
|
||||
/// polynomial_wrapping_scalar_mul_assign(&mut pol, scalar);
|
||||
/// assert_eq!(pol.as_ref(), &[127u8, 254, 125, 252, 123, 250]);
|
||||
/// ```
|
||||
pub fn polynomial_wrapping_scalar_mul_assign<Scalar, PolyCont>(
|
||||
output: &mut Polynomial<PolyCont>,
|
||||
scalar: Scalar,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
PolyCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
slice_wrapping_scalar_mul_assign(output, scalar)
|
||||
}
|
||||
|
||||
/// Fill the output polynomial, with the result of the product of two polynomials, reduced modulo
|
||||
/// $(X^{N} + 1)$ with the Karatsuba algorithm Complexity: $O(N^{1.58})$
|
||||
///
|
||||
@@ -530,6 +557,70 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn apply_automorphism_wrapping_add_assign<Scalar, OutputCont, PolyCont>(
|
||||
output: &mut Polynomial<OutputCont>,
|
||||
input: &Polynomial<PolyCont>,
|
||||
automorphism_exponent: usize,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
PolyCont: Container<Element = Scalar>,
|
||||
{
|
||||
// check input and output polynomials have the same size
|
||||
assert_eq!(input.polynomial_size(), output.polynomial_size());
|
||||
// check the automorphism exponent is odd so the function X -> X^automorphism_exponent is an
|
||||
// automorphism (assumes polysize is a power of 2)
|
||||
assert_eq!(automorphism_exponent % 2, 1);
|
||||
|
||||
let poly_size = input.polynomial_size().0;
|
||||
|
||||
for (index, coef) in input.iter().enumerate() {
|
||||
let new_index = (index * automorphism_exponent) % poly_size;
|
||||
if (index * automorphism_exponent) % (2 * poly_size) == new_index {
|
||||
output[new_index] = output[new_index].wrapping_add(*coef);
|
||||
} else {
|
||||
output[new_index] = output[new_index].wrapping_sub(*coef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_automorphism_assign<Scalar, PolyCont>(
|
||||
input: &mut Polynomial<PolyCont>,
|
||||
automorphism_exponent: usize,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
PolyCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let mut temp = Polynomial::new(Scalar::ZERO, input.polynomial_size());
|
||||
apply_automorphism_wrapping_add_assign(&mut temp, input, automorphism_exponent);
|
||||
input.fill(Scalar::ZERO);
|
||||
polynomial_wrapping_add_assign(input, &temp);
|
||||
}
|
||||
|
||||
pub fn polynomial_list_wrapping_sub_scalar_mul_assign<Scalar, InputCont, OutputCont, PolyCont>(
|
||||
output_poly_list: &mut PolynomialList<OutputCont>,
|
||||
input_poly_list: &PolynomialList<InputCont>,
|
||||
scalar_poly: &Polynomial<PolyCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
PolyCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert_eq!(
|
||||
output_poly_list.polynomial_size(),
|
||||
input_poly_list.polynomial_size()
|
||||
);
|
||||
assert_eq!(
|
||||
output_poly_list.polynomial_count(),
|
||||
input_poly_list.polynomial_count()
|
||||
);
|
||||
for (mut output_poly, input_poly) in output_poly_list.iter_mut().zip(input_poly_list.iter()) {
|
||||
polynomial_wrapping_sub_mul_assign(&mut output_poly, &input_poly, scalar_poly)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rand::Rng;
|
||||
|
||||
@@ -169,6 +169,31 @@ impl<G: ByteRandomGenerator> EncryptionRandomGenerator<G> {
|
||||
self.try_fork(lwe_size.0, mask_bytes, noise_bytes)
|
||||
}
|
||||
|
||||
// Forks the generator, when splitting a tpksk into chunks
|
||||
pub(crate) fn fork_tpksk_to_tpksk_chunks<T: UnsignedInteger>(
|
||||
&mut self,
|
||||
level: DecompositionLevelCount,
|
||||
glwe_size: GlweSize,
|
||||
poly_size: PolynomialSize,
|
||||
) -> Result<impl Iterator<Item = EncryptionRandomGenerator<G>>, ForkError> {
|
||||
let mask_bytes = mask_bytes_per_tpksk_chunk::<T>(level, glwe_size, poly_size);
|
||||
let noise_bytes = noise_bytes_per_tpksk_chunk(level, poly_size);
|
||||
self.try_fork(poly_size.log2().0, mask_bytes, noise_bytes)
|
||||
}
|
||||
|
||||
// Forks the generator, when splitting a glwe keyswitch into chunks
|
||||
pub(crate) fn fork_glweks_to_glweks_chunks<T: UnsignedInteger>(
|
||||
&mut self,
|
||||
level: DecompositionLevelCount,
|
||||
input_glwe_dimension: GlweDimension,
|
||||
output_glwe_size: GlweSize,
|
||||
poly_size: PolynomialSize,
|
||||
) -> Result<impl Iterator<Item = EncryptionRandomGenerator<G>>, ForkError> {
|
||||
let mask_bytes = mask_bytes_per_glweks_chunk::<T>(level, output_glwe_size, poly_size);
|
||||
let noise_bytes = noise_bytes_per_glweks_chunk(level, poly_size);
|
||||
self.try_fork(input_glwe_dimension.0, mask_bytes, noise_bytes)
|
||||
}
|
||||
|
||||
// Forks both generators into an iterator
|
||||
fn try_fork(
|
||||
&mut self,
|
||||
@@ -431,6 +456,22 @@ impl<G: ParallelByteRandomGenerator> EncryptionRandomGenerator<G> {
|
||||
self.par_try_fork(lwe_size.0, mask_bytes, noise_bytes)
|
||||
}
|
||||
|
||||
// Forks the generator, when splitting a tpksk into chunks
|
||||
pub(crate) fn par_fork_tpksk_to_tpksk_chunks<T: UnsignedInteger>(
|
||||
&mut self,
|
||||
level: DecompositionLevelCount,
|
||||
glwe_size: GlweSize,
|
||||
poly_size: PolynomialSize,
|
||||
) -> Result<impl IndexedParallelIterator<Item = EncryptionRandomGenerator<G>>, ForkError> {
|
||||
let mask_bytes = mask_bytes_per_tpksk_chunk::<T>(level, glwe_size, poly_size);
|
||||
let noise_bytes = noise_bytes_per_tpksk_chunk(level, poly_size);
|
||||
self.par_try_fork(
|
||||
poly_size.log2().0 * glwe_size.to_glwe_dimension().0,
|
||||
mask_bytes,
|
||||
noise_bytes,
|
||||
)
|
||||
}
|
||||
|
||||
// Forks both generators into a parallel iterator.
|
||||
fn par_try_fork(
|
||||
&mut self,
|
||||
@@ -504,6 +545,32 @@ fn mask_bytes_per_pfpksk<T: UnsignedInteger>(
|
||||
lwe_size.0 * mask_bytes_per_pfpksk_chunk::<T>(level, glwe_size, poly_size)
|
||||
}
|
||||
|
||||
fn mask_bytes_per_tpksk_chunk<T: UnsignedInteger>(
|
||||
level: DecompositionLevelCount,
|
||||
glwe_size: GlweSize,
|
||||
poly_size: PolynomialSize,
|
||||
) -> usize {
|
||||
glwe_size.to_glwe_dimension().0 * mask_bytes_per_glweks_chunk::<T>(level, glwe_size, poly_size)
|
||||
}
|
||||
|
||||
fn mask_bytes_per_tpksk<T: UnsignedInteger>(
|
||||
level: DecompositionLevelCount,
|
||||
glwe_size: GlweSize,
|
||||
poly_size: PolynomialSize,
|
||||
) -> usize {
|
||||
poly_size.log2().0 * mask_bytes_per_tpksk_chunk::<T>(level, glwe_size, poly_size)
|
||||
}
|
||||
|
||||
fn mask_bytes_per_glweks_chunk<T: UnsignedInteger>(
|
||||
level: DecompositionLevelCount,
|
||||
glwe_size: GlweSize,
|
||||
poly_size: PolynomialSize,
|
||||
) -> usize {
|
||||
glwe_size.to_glwe_dimension().0
|
||||
* level.0
|
||||
* mask_bytes_per_glwe::<T>(glwe_size.to_glwe_dimension(), poly_size)
|
||||
}
|
||||
|
||||
fn noise_bytes_per_coef() -> usize {
|
||||
// We use f64 to sample the noise for every precision, and we need 4/pi inputs to generate
|
||||
// such an output (here we take 32 to keep a safety margin).
|
||||
@@ -553,6 +620,27 @@ fn noise_bytes_per_pfpksk(
|
||||
lwe_size.0 * noise_bytes_per_pfpksk_chunk(level, poly_size)
|
||||
}
|
||||
|
||||
fn noise_bytes_per_tpksk_chunk(level: DecompositionLevelCount, poly_size: PolynomialSize) -> usize {
|
||||
level.0 * noise_bytes_per_glwe(poly_size)
|
||||
}
|
||||
|
||||
fn noise_bytes_per_tpksk(
|
||||
level: DecompositionLevelCount,
|
||||
poly_size: PolynomialSize,
|
||||
glwe_size: GlweSize,
|
||||
) -> usize {
|
||||
glwe_size.to_glwe_dimension().0
|
||||
* poly_size.log2().0
|
||||
* noise_bytes_per_tpksk_chunk(level, poly_size)
|
||||
}
|
||||
|
||||
fn noise_bytes_per_glweks_chunk(
|
||||
level: DecompositionLevelCount,
|
||||
poly_size: PolynomialSize,
|
||||
) -> usize {
|
||||
level.0 * noise_bytes_per_glwe(poly_size)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::core_crypto::algorithms::*;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use crate::core_crypto::commons::math::decomposition::SignedDecompositionIter;
|
||||
use crate::core_crypto::commons::math::decomposition::{
|
||||
SignedDecompositionIter, SliceSignedDecompositionIter,
|
||||
};
|
||||
use crate::core_crypto::commons::numeric::{Numeric, UnsignedInteger};
|
||||
use crate::core_crypto::commons::parameters::{DecompositionBaseLog, DecompositionLevelCount};
|
||||
use std::marker::PhantomData;
|
||||
@@ -113,6 +115,29 @@ where
|
||||
res << non_rep_bit_count
|
||||
}
|
||||
|
||||
/// Fills a mutable tensor-like objects with the closest representable values from another
|
||||
/// tensor-like object.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
|
||||
/// let decomposer =
|
||||
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
|
||||
///
|
||||
/// let input = vec![1_340_987_234_u32; 2];
|
||||
/// let mut closest = vec![0u32; 2];
|
||||
/// decomposer.fill_slice_with_closest_representable(&mut closest, &input);
|
||||
/// assert!(closest.iter().all(|&x| x == 1_341_128_704_u32));
|
||||
/// ```
|
||||
pub fn fill_slice_with_closest_representable(&self, output: &mut [Scalar], input: &[Scalar]) {
|
||||
output
|
||||
.iter_mut()
|
||||
.zip(input.iter())
|
||||
.for_each(|(dst, &src)| *dst = self.closest_representable(src));
|
||||
}
|
||||
|
||||
/// Generate an iterator over the terms of the decomposition of the input.
|
||||
///
|
||||
/// # Warning
|
||||
@@ -173,4 +198,87 @@ where
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates an iterator-like object over tensors of terms of the decomposition of the input
|
||||
/// tensor.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// The returned iterator yields the terms $(\tilde{\theta}^{(a)}\_i)\_{a\in\mathbb{N}}$ in
|
||||
/// order of decreasing $i$.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
/// use tfhe::core_crypto::commons::numeric::UnsignedInteger;
|
||||
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
|
||||
/// let decomposer =
|
||||
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
|
||||
/// let decomposable = vec![1_340_987_234_u32, 1_340_987_234_u32];
|
||||
/// let mut decomp = decomposer.decompose_slice(&decomposable);
|
||||
///
|
||||
/// let mut count = 0;
|
||||
/// while let Some(term) = decomp.next_term() {
|
||||
/// assert!(1 <= term.level().0);
|
||||
/// assert!(term.level().0 <= 3);
|
||||
/// for elmt in term.as_slice().iter() {
|
||||
/// let signed_term = elmt.into_signed();
|
||||
/// let half_basis = 2i32.pow(4) / 2i32;
|
||||
/// assert!(-half_basis <= signed_term);
|
||||
/// assert!(signed_term < half_basis);
|
||||
/// }
|
||||
/// count += 1;
|
||||
/// }
|
||||
/// assert_eq!(count, 3);
|
||||
/// ```
|
||||
pub fn decompose_slice(&self, input: &[Scalar]) -> SliceSignedDecompositionIter<Scalar> {
|
||||
// Note that there would be no sense of making the decomposition on an input which was
|
||||
// not rounded to the closest representable first. We then perform it before decomposing.
|
||||
let mut rounded = vec![Scalar::ZERO; input.len()];
|
||||
self.fill_slice_with_closest_representable(&mut rounded, input);
|
||||
SliceSignedDecompositionIter::new(
|
||||
&rounded,
|
||||
DecompositionBaseLog(self.base_log),
|
||||
DecompositionLevelCount(self.level_count),
|
||||
)
|
||||
}
|
||||
|
||||
/// Fills the output tensor with the recomposition of an other tensor.
|
||||
///
|
||||
/// Returns `Some(())` if the decomposition was fresh, and the output was filled with a
|
||||
/// recomposition, and `None`, if not.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
|
||||
/// let decomposer =
|
||||
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
|
||||
/// let decomposable = vec![1_340_987_234_u32; 2];
|
||||
/// let mut rounded = vec![0u32; 2];
|
||||
/// decomposer.fill_slice_with_closest_representable(&mut rounded, &decomposable);
|
||||
/// let mut decomp = decomposer.decompose_slice(&rounded);
|
||||
/// let mut recomposition = vec![0u32; 2];
|
||||
/// decomposer
|
||||
/// .fill_slice_with_recompose(decomp, &mut recomposition)
|
||||
/// .unwrap();
|
||||
/// assert_eq!(recomposition, rounded);
|
||||
/// ```
|
||||
pub fn fill_slice_with_recompose(
|
||||
&self,
|
||||
decomp: SliceSignedDecompositionIter<Scalar>,
|
||||
output: &mut [Scalar],
|
||||
) -> Option<()> {
|
||||
let mut decomp = decomp;
|
||||
if decomp.is_fresh() {
|
||||
while let Some(term) = decomp.next_term() {
|
||||
term.update_slice_with_recomposition_summand_wrapping_addition(output);
|
||||
}
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use crate::core_crypto::commons::math::decomposition::{DecompositionLevel, DecompositionTerm};
|
||||
use crate::core_crypto::commons::math::decomposition::{
|
||||
DecompositionLevel, DecompositionTerm, DecompositionTermSlice,
|
||||
};
|
||||
use crate::core_crypto::commons::numeric::UnsignedInteger;
|
||||
use crate::core_crypto::commons::parameters::{DecompositionBaseLog, DecompositionLevelCount};
|
||||
|
||||
@@ -114,6 +116,155 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator-like object that yields the terms of the signed decomposition of a tensor of values.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// On each call to [`SliceSignedDecompositionIter::next_term`], this structure yields a new
|
||||
/// [`DecompositionTermSlice`], backed by a `Vec` owned by the structure. This vec is mutated at
|
||||
/// each call of the `next_term` method, and as such the term must be dropped before `next_term` is
|
||||
/// called again.
|
||||
///
|
||||
/// Such a pattern can not be implemented with iterators yet (without GATs), which is why this
|
||||
/// iterator must be explicitly called.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// This iterator yields the decomposition in reverse order. That means that the highest level
|
||||
/// will be yielded first.
|
||||
pub struct SliceSignedDecompositionIter<Scalar>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
{
|
||||
// The base log of the decomposition
|
||||
base_log: usize,
|
||||
// The number of levels of the decomposition
|
||||
level_count: usize,
|
||||
// The current level
|
||||
current_level: usize,
|
||||
// A mask which allows to compute the mod B of a value. For B=2^4, this guy is of the form:
|
||||
// ...0001111
|
||||
mod_b_mask: Scalar,
|
||||
// The values being decomposed
|
||||
inputs: Vec<Scalar>,
|
||||
// The internal states of each decomposition
|
||||
states: Vec<Scalar>,
|
||||
// In order to avoid allocating a new Vec every time we yield a decomposition term, we store
|
||||
// a Vec inside the structure and yield slices pointing to it.
|
||||
outputs: Vec<Scalar>,
|
||||
// A flag which stores whether the iterator is a fresh one (for the recompose method).
|
||||
fresh: bool,
|
||||
}
|
||||
|
||||
impl<Scalar> SliceSignedDecompositionIter<Scalar>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
{
|
||||
// Creates a new tensor decomposition iterator.
|
||||
pub(crate) fn new(
|
||||
input: &[Scalar],
|
||||
base_log: DecompositionBaseLog,
|
||||
level: DecompositionLevelCount,
|
||||
) -> SliceSignedDecompositionIter<Scalar> {
|
||||
let len = input.len();
|
||||
SliceSignedDecompositionIter {
|
||||
base_log: base_log.0,
|
||||
level_count: level.0,
|
||||
current_level: level.0,
|
||||
mod_b_mask: (Scalar::ONE << base_log.0) - Scalar::ONE,
|
||||
inputs: input.to_vec(),
|
||||
outputs: vec![Scalar::ZERO; len],
|
||||
states: input
|
||||
.iter()
|
||||
.map(|i| *i >> (Scalar::BITS - base_log.0 * level.0))
|
||||
.collect(),
|
||||
fresh: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_fresh(&self) -> bool {
|
||||
self.fresh
|
||||
}
|
||||
|
||||
/// Returns the logarithm in base two of the base of this decomposition.
|
||||
///
|
||||
/// If the decomposition uses a base $B=2^b$, this returns $b$.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
|
||||
/// let decomposer =
|
||||
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
|
||||
/// let decomposable = vec![1_340_987_234_u32; 2];
|
||||
/// let decomp = decomposer.decompose_slice(&decomposable);
|
||||
/// assert_eq!(decomp.base_log(), DecompositionBaseLog(4));
|
||||
/// ```
|
||||
pub fn base_log(&self) -> DecompositionBaseLog {
|
||||
DecompositionBaseLog(self.base_log)
|
||||
}
|
||||
|
||||
/// Returns the number of levels of this decomposition.
|
||||
///
|
||||
/// If the decomposition uses $l$ levels, this returns $l$.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
|
||||
/// let decomposer =
|
||||
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
|
||||
/// let decomposable = vec![1_340_987_234_u32; 2];
|
||||
/// let decomp = decomposer.decompose_slice(&decomposable);
|
||||
/// assert_eq!(decomp.level_count(), DecompositionLevelCount(3));
|
||||
/// ```
|
||||
pub fn level_count(&self) -> DecompositionLevelCount {
|
||||
DecompositionLevelCount(self.level_count)
|
||||
}
|
||||
|
||||
/// Yield the next term of the decomposition, if any.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Because this function returns a borrowed tensor, owned by the iterator, the term must be
|
||||
/// dropped before `next_term` is called again.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::core_crypto::commons::math::decomposition::{DecompositionLevel, SignedDecomposer};
|
||||
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
|
||||
/// let decomposer =
|
||||
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
|
||||
/// let decomposable = vec![1_340_987_234_u32; 2];
|
||||
/// let mut decomp = decomposer.decompose_slice(&decomposable);
|
||||
/// let term = decomp.next_term().unwrap();
|
||||
/// assert_eq!(term.level(), DecompositionLevel(3));
|
||||
/// assert_eq!(term.as_slice()[0], 4294967295);
|
||||
/// ```
|
||||
pub fn next_term(&mut self) -> Option<DecompositionTermSlice<'_, Scalar>> {
|
||||
// The iterator is not fresh anymore.
|
||||
self.fresh = false;
|
||||
// We check if the decomposition is over
|
||||
if self.current_level == 0 {
|
||||
return None;
|
||||
}
|
||||
// We iterate over the elements of the outputs and decompose
|
||||
for (output_i, state_i) in self.outputs.iter_mut().zip(self.states.iter_mut()) {
|
||||
*output_i = decompose_one_level(self.base_log, state_i, self.mod_b_mask);
|
||||
}
|
||||
self.current_level -= 1;
|
||||
// We return the term tensor.
|
||||
Some(DecompositionTermSlice::new(
|
||||
DecompositionLevel(self.current_level + 1),
|
||||
DecompositionBaseLog(self.base_log),
|
||||
&self.outputs,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn decompose_one_level<S: UnsignedInteger>(base_log: usize, state: &mut S, mod_b_mask: S) -> S {
|
||||
let res = *state & mod_b_mask;
|
||||
*state >>= base_log;
|
||||
|
||||
@@ -91,3 +91,115 @@ where
|
||||
DecompositionLevel(self.level)
|
||||
}
|
||||
}
|
||||
|
||||
/// A tensor whose elements are the terms of the decomposition of another tensor.
|
||||
///
|
||||
/// If we decompose each elements of a set of values $(\theta^{(a)})\_{a\in\mathbb{N}}$ as a set of
|
||||
/// sums $(\sum\_{i=1}^l\tilde{\theta}^{(a)}\_i\frac{q}{B^i})\_{a\in\mathbb{N}}$, this represents a
|
||||
/// set of $(\tilde{\theta}^{(a)}\_i)\_{a\in\mathbb{N}}$.
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct DecompositionTermSlice<'a, Scalar>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
{
|
||||
level: usize,
|
||||
base_log: usize,
|
||||
slice: &'a [Scalar],
|
||||
}
|
||||
|
||||
impl<'a, Scalar> DecompositionTermSlice<'a, Scalar>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
{
|
||||
// Creates a new tensor decomposition term.
|
||||
pub(crate) fn new(
|
||||
level: DecompositionLevel,
|
||||
base_log: DecompositionBaseLog,
|
||||
slice: &'a [Scalar],
|
||||
) -> DecompositionTermSlice<Scalar> {
|
||||
DecompositionTermSlice {
|
||||
level: level.0,
|
||||
base_log: base_log.0,
|
||||
slice,
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills the output tensor with the terms turned to summands.
|
||||
///
|
||||
/// If our term tensor represents a set of $(\tilde{\theta}^{(a)}\_i)\_{a\in\mathbb{N}}$ of the
|
||||
/// decomposition, this method fills the output tensor with a set of
|
||||
/// $(\tilde{\theta}^{(a)}\_i\frac{q}{B^i})\_{a\in\mathbb{N}}$.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
|
||||
/// let decomposer =
|
||||
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
|
||||
/// let input = vec![2u32.pow(19); 2];
|
||||
/// let mut decomp = decomposer.decompose_slice(&input);
|
||||
/// let term = decomp.next_term().unwrap();
|
||||
/// let mut output = vec![0, 2];
|
||||
/// term.fill_slice_with_recomposition_summand(&mut output);
|
||||
/// assert!(output.iter().all(|&x| x == 1048576));
|
||||
/// ```
|
||||
pub fn fill_slice_with_recomposition_summand(&self, output: &mut [Scalar]) {
|
||||
output
|
||||
.iter_mut()
|
||||
.zip(self.slice.iter())
|
||||
.for_each(|(dst, &value)| {
|
||||
let shift: usize = <Scalar as Numeric>::BITS - self.base_log * self.level;
|
||||
*dst = value << shift
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn update_slice_with_recomposition_summand_wrapping_addition(
|
||||
&self,
|
||||
output: &mut [Scalar],
|
||||
) {
|
||||
output
|
||||
.iter_mut()
|
||||
.zip(self.slice.iter())
|
||||
.for_each(|(out, &value)| {
|
||||
let shift: usize = <Scalar as Numeric>::BITS - self.base_log * self.level;
|
||||
*out = (*out).wrapping_add(value << shift);
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns a tensor with the values of term.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
|
||||
/// let decomposer =
|
||||
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
|
||||
/// let input = vec![2u32.pow(19); 2];
|
||||
/// let mut decomp = decomposer.decompose_slice(&input);
|
||||
/// let term = decomp.next_term().unwrap();
|
||||
/// assert_eq!(term.as_slice()[0], 1);
|
||||
/// ```
|
||||
pub fn as_slice(&self) -> &'a [Scalar] {
|
||||
self.slice
|
||||
}
|
||||
|
||||
/// Returns the level of this decomposition term tensor.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use tfhe::core_crypto::commons::math::decomposition::{DecompositionLevel, SignedDecomposer};
|
||||
/// use tfhe::core_crypto::prelude::{DecompositionBaseLog, DecompositionLevelCount};
|
||||
/// let decomposer =
|
||||
/// SignedDecomposer::<u32>::new(DecompositionBaseLog(4), DecompositionLevelCount(3));
|
||||
/// let input = vec![2u32.pow(19); 2];
|
||||
/// let mut decomp = decomposer.decompose_slice(&input);
|
||||
/// let term = decomp.next_term().unwrap();
|
||||
/// assert_eq!(term.level(), DecompositionLevel(3));
|
||||
/// ```
|
||||
pub fn level(&self) -> DecompositionLevel {
|
||||
DecompositionLevel(self.level)
|
||||
}
|
||||
}
|
||||
|
||||
428
tfhe/src/core_crypto/entities/glwe_keyswitch_key.rs
Normal file
428
tfhe/src/core_crypto/entities/glwe_keyswitch_key.rs
Normal file
@@ -0,0 +1,428 @@
|
||||
//! Module containing the definition of the GlweKeyswitchKey.
|
||||
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
/// A [`GLWE keyswitch key`](`GlweKeyswitchKey`).
|
||||
///
|
||||
/// # Formal Definition
|
||||
///
|
||||
/// ## Key Switching Key
|
||||
///
|
||||
/// A key switching key is a vector of GLev ciphertexts (described on the bottom of
|
||||
/// [`this page`](`crate::core_crypto::entities::GgswCiphertext#Glev-ciphertext`)).
|
||||
/// It encrypts the coefficient of
|
||||
/// the [`GLWE secret key`](`crate::core_crypto::entities::GlweSecretKey`)
|
||||
/// $\vec{S}\_{\mathsf{in}}$ under the
|
||||
/// [`GLWE secret key`](`crate::core_crypto::entities::GlweSecretKey`)
|
||||
/// $\vec{S}\_{\mathsf{out}}$.
|
||||
///
|
||||
/// $$\mathsf{KSK}\_{\vec{S}\_{\mathsf{in}}\rightarrow \vec{S}\_{\mathsf{out}}} = \left(
|
||||
/// \overline{\mathsf{CT}\_0}, \cdots , \overline{\mathsf{CT}\_{k\_{\mathsf{in}}-1}}\right)
|
||||
/// \subseteq R\_q^{(k\_{\mathsf{out}}+1)\cdot k\_{\mathsf{in}}\cdot \ell}$$
|
||||
///
|
||||
/// where $\vec{S}\_{\mathsf{in}} = \left( S\_0 , \cdots , S\_{\mathsf{in}-1} \right)$ and for all
|
||||
/// $0\le i <k\_{\mathsf{in}}$ we have $\overline{\mathsf{CT}\_i} \in
|
||||
/// \mathsf{GLev}\_{\vec{S}\_{\mathsf{out}}}^{\beta, \ell}\left(S\_i\right)$.
|
||||
///
|
||||
/// ## GLWE Keyswitch
|
||||
///
|
||||
/// This homomorphic procedure transforms an input
|
||||
/// [`GLWE ciphertext`](`crate::core_crypto::entities::GlweCiphertext`)
|
||||
/// $\mathsf{CT}\_{\mathsf{in}} =
|
||||
/// \left( \vec{A}\_{\mathsf{in}} , B\_{\mathsf{in}}\right) \in \mathsf{GLWE}^{k\_{\mathsf{in}}}\_
|
||||
/// {\vec{S}\_{\mathsf{in}}}( \mathsf{PT} ) \subseteq R\_q^{(k\_{\mathsf{in}}+1)}$ into an
|
||||
/// output [`GLWE ciphertext`](`crate::core_crypto::entities::GlweCiphertext`)
|
||||
/// $\mathsf{CT}\_{\mathsf{out}} =
|
||||
/// \left( \vec{A}\_{\mathsf{out}} , B\_{\mathsf{out}}\right) \in
|
||||
/// \mathsf{GLWE}^{k\_{\mathsf{out}}}\_{\vec{S}\_{\mathsf{out}}}( \mathsf{PT} )\subseteq
|
||||
/// R\_q^{(k\_{\mathsf{out}}+1)}$ where $k\_{\mathsf{in}} = |\vec{S}\_{\mathsf{in}}|$ and
|
||||
/// $k\_{\mathsf{out}} = |\vec{S}\_{\mathsf{out}}|$. It requires a
|
||||
/// [`key switching key`](`crate::core_crypto::entities::GlweKeyswitchKey`).
|
||||
/// The input ciphertext is encrypted under the
|
||||
/// [`GLWE secret key`](`crate::core_crypto::entities::GlweSecretKey`)
|
||||
/// $\vec{S}\_{\mathsf{in}}$ and the output ciphertext is
|
||||
/// encrypted under the [`GLWE secret key`](`crate::core_crypto::entities::GlweSecretKey`)
|
||||
/// $\vec{S}\_{\mathsf{out}}$.
|
||||
///
|
||||
/// $$\mathsf{CT}\_{\mathsf{in}} \in \mathsf{GLWE}^{k\_{\mathsf{in}}}\_{\vec{S}\_{\mathsf{in}}}(
|
||||
/// \mathsf{PT} ) ~~~~~~~~~~\mathsf{KSK}\_{\vec{S}\_{\mathsf{in}}\rightarrow
|
||||
/// \vec{S}\_{\mathsf{out}}}$$ $$ \mathsf{keyswitch}\left(\mathsf{CT}\_{\mathsf{in}} , \mathsf{KSK}
|
||||
/// \right) \rightarrow \mathsf{CT}\_{\mathsf{out}} \in
|
||||
/// \mathsf{GLWE}^{k\_{\mathsf{out}}}\_{\vec{S}\_{\mathsf{out}}} \left( \mathsf{PT} \right)$$
|
||||
///
|
||||
/// ## Algorithm
|
||||
/// ###### inputs:
|
||||
/// - $\mathsf{CT}\_{\mathsf{in}} = \left( \vec{A}\_{\mathsf{in}} , B\_{\mathsf{in}}\right) \in
|
||||
/// \mathsf{GLWE}^{k\_{\mathsf{in}}}\_{\vec{S}\_{\mathsf{in}}}( \mathsf{PT} )$: a [`GLWE
|
||||
/// ciphertext`](`GlweCiphertext`) with $\vec{A}\_{\mathsf{in}}=\left(A\_0, \cdots
|
||||
/// A\_{k\_{\mathsf{in}}-1}\right)$
|
||||
/// - $\mathsf{KSK}\_{\vec{S}\_{\mathsf{in}}\rightarrow \vec{S}\_{\mathsf{out}}}$: a
|
||||
/// [`key switching key`](`crate::core_crypto::entities::GlweKeyswitchKey`)
|
||||
///
|
||||
/// ###### outputs:
|
||||
/// - $\mathsf{CT}\_{\mathsf{out}} \in \mathsf{GLWE}^{k\_{\mathsf{out}}}\_{\vec{S}\_{\mathsf{out}}}
|
||||
/// \left( \mathsf{PT} \right)$: a
|
||||
/// [`GLWE ciphertext`](`crate::core_crypto::entities::GlweCiphertext`)
|
||||
///
|
||||
/// ###### algorithm:
|
||||
/// 1. set $\mathsf{cCT}=\left( 0 , \cdots , 0 , B\_{\mathsf{in}} \right) \in
|
||||
/// R\_q^{(k\_{\mathsf{out}}+1)}$
|
||||
/// 2. compute $\mathsf{CT}\_{\mathsf{out}} = \mathsf{CT} -
|
||||
/// \sum\_{i=0}^{k\_{\mathsf{in}}-1} \mathsf{decompProduct}\left( A\_i , \overline{\mathsf{CT}\_i}
|
||||
/// \right)$
|
||||
/// 3. output $\mathsf{CT}\_{\mathsf{out}}$
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct GlweKeyswitchKey<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_glwe_size: GlweSize,
|
||||
poly_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for GlweKeyswitchKey<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for GlweKeyswitchKey<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of elements in an encryption of an input [`GlweSecretKey`] element for a
|
||||
/// [GlweKeyswitchKey`] given a [`DecompositionLevelCount`] and output [`GlweSize`].
|
||||
pub fn glwe_keyswitch_key_input_key_element_encrypted_size(
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_glwe_size: GlweSize,
|
||||
poly_size: PolynomialSize,
|
||||
) -> usize {
|
||||
// One ciphertext per level encrypted under the output key
|
||||
decomp_level_count.0 * output_glwe_size.0 * poly_size.0
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> GlweKeyswitchKey<C> {
|
||||
/// Create an [`GlweKeyswitchKey`] from an existing container.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function only wraps a container in the appropriate type. If you want to generate an
|
||||
/// [`GlweKeyswitchKey`] you need to call
|
||||
/// [`crate::core_crypto::algorithms::generate_glwe_keyswitch_key`] using this key as output.
|
||||
///
|
||||
/// This docstring exhibits [`GlweKeyswitchKey`] primitives usage.
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for LweKeyswitchKey creation
|
||||
/// let input_glwe_dimension = GlweDimension(1);
|
||||
/// let output_glwe_dimension = GlweDimension(2);
|
||||
/// let poly_size = PolynomialSize(1024);
|
||||
/// let decomp_base_log = DecompositionBaseLog(4);
|
||||
/// let decomp_level_count = DecompositionLevelCount(5);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // Create a new LweKeyswitchKey
|
||||
/// let glwe_ksk = GlweKeyswitchKey::new(
|
||||
/// 0u64,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// input_glwe_dimension,
|
||||
/// output_glwe_dimension,
|
||||
/// poly_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(glwe_ksk.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(glwe_ksk.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(glwe_ksk.input_key_glwe_dimension(), input_glwe_dimension);
|
||||
/// assert_eq!(glwe_ksk.output_key_glwe_dimension(), output_glwe_dimension);
|
||||
/// assert_eq!(glwe_ksk.polynomial_size(), poly_size);
|
||||
/// assert_eq!(
|
||||
/// glwe_ksk.output_glwe_size(),
|
||||
/// output_glwe_dimension.to_glwe_size()
|
||||
/// );
|
||||
/// assert_eq!(glwe_ksk.ciphertext_modulus(), ciphertext_modulus);
|
||||
///
|
||||
/// // Demonstrate how to recover the allocated container
|
||||
/// let underlying_container: Vec<u64> = glwe_ksk.into_container();
|
||||
///
|
||||
/// // Recreate a keyswithc key using from_container
|
||||
/// let glwe_ksk = GlweKeyswitchKey::from_container(
|
||||
/// underlying_container,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// output_glwe_dimension.to_glwe_size(),
|
||||
/// poly_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(glwe_ksk.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(glwe_ksk.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(glwe_ksk.input_key_glwe_dimension(), input_glwe_dimension);
|
||||
/// assert_eq!(glwe_ksk.output_key_glwe_dimension(), output_glwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// glwe_ksk.output_glwe_size(),
|
||||
/// output_glwe_dimension.to_glwe_size()
|
||||
/// );
|
||||
/// assert_eq!(glwe_ksk.ciphertext_modulus(), ciphertext_modulus);
|
||||
/// ```
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_glwe_size: GlweSize,
|
||||
poly_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
container.container_len() > 0,
|
||||
"Got an empty container to create an LweKeyswitchKey"
|
||||
);
|
||||
assert!(
|
||||
container.container_len() % (decomp_level_count.0 * output_glwe_size.0 * poly_size.0)
|
||||
== 0,
|
||||
"The provided container length is not valid. \
|
||||
It needs to be dividable by decomp_level_count * output_glwe_size * output_poly_size: {}. \
|
||||
Got container length: {} and decomp_level_count: {decomp_level_count:?}, \
|
||||
output_glwe_size: {output_glwe_size:?}, poly_size: {poly_size:?}.",
|
||||
decomp_level_count.0 * output_glwe_size.0 * poly_size.0,
|
||||
container.container_len()
|
||||
);
|
||||
|
||||
GlweKeyswitchKey {
|
||||
data: container,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
poly_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionBaseLog`] of the [`LweKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweKeyswitchKey::from_container`] for usage.
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionLevelCount`] of the [`LweKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweKeyswitchKey::from_container`] for usage.
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomp_level_count
|
||||
}
|
||||
|
||||
/// Return the input [`GlweDimension`] of the [`GlweKeyswitchKey`].
|
||||
///
|
||||
/// See [`GlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn input_key_glwe_dimension(&self) -> GlweDimension {
|
||||
GlweDimension(self.data.container_len() / self.input_key_element_encrypted_size())
|
||||
}
|
||||
|
||||
/// Return the input [`PolynomialSize`] of the [`GlweKeyswitchKey`].
|
||||
///
|
||||
/// See [`GlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.poly_size
|
||||
}
|
||||
|
||||
/// Return the output [`GlweDimension`] of the [`GlweKeyswitchKey`].
|
||||
///
|
||||
/// See [`GlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn output_key_glwe_dimension(&self) -> GlweDimension {
|
||||
self.output_glwe_size.to_glwe_dimension()
|
||||
}
|
||||
|
||||
/// Return the output [`GlweSize`] of the [`GlweKeyswitchKey`].
|
||||
///
|
||||
/// See [`GlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn output_glwe_size(&self) -> GlweSize {
|
||||
self.output_glwe_size
|
||||
}
|
||||
|
||||
/// Return the number of elements in an encryption of an input [`GlweSecretKey`] element of the
|
||||
/// current [`GlweKeyswitchKey`].
|
||||
pub fn input_key_element_encrypted_size(&self) -> usize {
|
||||
glwe_keyswitch_key_input_key_element_encrypted_size(
|
||||
self.decomp_level_count,
|
||||
self.output_glwe_size,
|
||||
self.poly_size,
|
||||
)
|
||||
}
|
||||
|
||||
/// Return a view of the [`GlweKeyswitchKey`]. This is useful if an algorithm takes a view by
|
||||
/// value.
|
||||
pub fn as_view(&self) -> GlweKeyswitchKey<&'_ [Scalar]> {
|
||||
GlweKeyswitchKey::from_container(
|
||||
self.as_ref(),
|
||||
self.decomp_base_log,
|
||||
self.decomp_level_count,
|
||||
self.output_glwe_size,
|
||||
self.poly_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
/// Consume the entity and return its underlying container.
|
||||
///
|
||||
/// See [`GlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
/// Return the [`CiphertextModulus`] of the [`GlweKeyswitchKey`].
|
||||
///
|
||||
/// See [`GlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
pub fn as_glwe_ciphertext_list(&self) -> GlweCiphertextListView<'_, Scalar> {
|
||||
GlweCiphertextListView::from_container(
|
||||
self.as_ref(),
|
||||
self.output_glwe_size(),
|
||||
self.polynomial_size(),
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> GlweKeyswitchKey<C> {
|
||||
/// Mutable variant of [`GlweKeyswitchKey::as_view`].
|
||||
pub fn as_mut_view(&mut self) -> GlweKeyswitchKey<&'_ mut [Scalar]> {
|
||||
let decomp_base_log = self.decomp_base_log;
|
||||
let decomp_level_count = self.decomp_level_count;
|
||||
let output_glwe_size = self.output_glwe_size;
|
||||
let poly_size = self.poly_size;
|
||||
let ciphertext_modulus = self.ciphertext_modulus;
|
||||
GlweKeyswitchKey::from_container(
|
||||
self.as_mut(),
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
poly_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_mut_glwe_ciphertext_list(&mut self) -> GlweCiphertextListMutView<'_, Scalar> {
|
||||
let output_glwe_size = self.output_glwe_size();
|
||||
let poly_size = self.polynomial_size();
|
||||
let ciphertext_modulus = self.ciphertext_modulus();
|
||||
GlweCiphertextListMutView::from_container(
|
||||
self.as_mut(),
|
||||
output_glwe_size,
|
||||
poly_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`GlweKeyswitchKey`] owning the memory for its own storage.
|
||||
pub type GlweKeyswitchKeyOwned<Scalar> = GlweKeyswitchKey<Vec<Scalar>>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> GlweKeyswitchKeyOwned<Scalar> {
|
||||
/// Allocate memory and create a new owned [`GlweKeyswitchKey`].
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
|
||||
/// type. If you want to generate an [`GlweKeyswitchKey`] you need to call
|
||||
/// [`crate::core_crypto::algorithms::generate_glwe_keyswitch_key`] using this key as output.
|
||||
///
|
||||
/// See [`GlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_key_glwe_dimension: GlweDimension,
|
||||
output_key_glwe_dimension: GlweDimension,
|
||||
poly_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> GlweKeyswitchKeyOwned<Scalar> {
|
||||
GlweKeyswitchKeyOwned::from_container(
|
||||
vec![
|
||||
fill_with;
|
||||
input_key_glwe_dimension.0
|
||||
* glwe_keyswitch_key_input_key_element_encrypted_size(
|
||||
decomp_level_count,
|
||||
output_key_glwe_dimension.to_glwe_size(),
|
||||
poly_size,
|
||||
)
|
||||
],
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_key_glwe_dimension.to_glwe_size(),
|
||||
poly_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for GlweKeyswitchKey<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = GlweCiphertextListCreationMetadata<Scalar>;
|
||||
|
||||
type EntityView<'this> = GlweCiphertextListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = ();
|
||||
|
||||
// At the moment it does not make sense to return "sub" keyswitch keys. So we use a dummy
|
||||
// placeholder type here.
|
||||
type SelfView<'this> = DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> GlweCiphertextListCreationMetadata<Scalar> {
|
||||
GlweCiphertextListCreationMetadata(
|
||||
self.output_glwe_size(),
|
||||
self.polynomial_size(),
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.input_key_element_encrypted_size()
|
||||
}
|
||||
|
||||
/// Unimplemented for [`GlweKeyswitchKey`]. At the moment it does not make sense to
|
||||
/// return "sub" keyswitch keys.
|
||||
fn get_self_view_creation_metadata(&self) {
|
||||
unimplemented!(
|
||||
"This function is not supported for GlweKeyswitchKey. \
|
||||
At the moment it does not make sense to return 'sub' keyswitch keys."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for GlweKeyswitchKey<C>
|
||||
{
|
||||
type EntityMutView<'this> = GlweCiphertextListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
// At the moment it does not make sense to return "sub" keyswitch keys. So we use a dummy
|
||||
// placeholder type here.
|
||||
type SelfMutView<'this> = DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
382
tfhe/src/core_crypto/entities/glwe_relinearisation_key.rs
Normal file
382
tfhe/src/core_crypto/entities/glwe_relinearisation_key.rs
Normal file
@@ -0,0 +1,382 @@
|
||||
//! Module containing the definition of the GlweRelinearisationKey.
|
||||
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
/// A [`GLWE relinearisation key`](`GlweRelinearisationKey`).
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct GlweRelinearisationKey<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for GlweRelinearisationKey<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]> for GlweRelinearisationKey<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of elements in an encryption of an input [`GlweSecretKey`] element for a
|
||||
/// [`GlweRelinearisationKey`] given a [`DecompositionLevelCount`], [`GlweSize`] and
|
||||
/// [`PolynomialSize`].
|
||||
pub fn glwe_relinearisation_key_input_key_element_encrypted_size(
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> usize {
|
||||
// One ciphertext per level encrypted under the output key
|
||||
decomp_level_count.0 * glwe_size.0 * polynomial_size.0
|
||||
}
|
||||
|
||||
/// Return the number of elements in a [`GlweRelinearisationKey`] given a
|
||||
/// [`DecompositionLevelCount`], [`GlweSize`], and [`PolynomialSize`].
|
||||
pub fn glwe_relinearisation_key_size(
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> usize {
|
||||
(glwe_size.to_glwe_dimension().0 * (glwe_size.to_glwe_dimension().0 + 1)) / 2
|
||||
* glwe_relinearisation_key_input_key_element_encrypted_size(
|
||||
decomp_level_count,
|
||||
glwe_size,
|
||||
polynomial_size,
|
||||
)
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> GlweRelinearisationKey<C> {
|
||||
/// Create a [`GlweRelinearisationKey`] from an existing container.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function only wraps a container in the appropriate type. If you want to generate an
|
||||
/// [`GlweRelinearisationKey`] you need to use
|
||||
/// [`crate::core_crypto::algorithms::generate_glwe_relinearisation_key`]
|
||||
/// using this key as output.
|
||||
///
|
||||
/// This docstring exhibits [`GlweRelinearisationKey`] primitives usage.
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for GlweRelinearisationKey creation
|
||||
/// let glwe_size = GlweSize(3);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let decomp_base_log = DecompositionBaseLog(8);
|
||||
/// let decomp_level_count = DecompositionLevelCount(3);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // Create a new GlweRelinearisationKey
|
||||
/// let relin_key = GlweRelinearisationKey::new(
|
||||
/// 0u64,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// glwe_size,
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(relin_key.glwe_dimension(), glwe_size.to_glwe_dimension());
|
||||
/// assert_eq!(relin_key.glwe_size(), glwe_size);
|
||||
/// assert_eq!(relin_key.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(relin_key.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(relin_key.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(relin_key.ciphertext_modulus(), ciphertext_modulus);
|
||||
///
|
||||
/// // Demonstrate how to recover the allocated container
|
||||
/// let underlying_container: Vec<u64> = relin_key.into_container();
|
||||
///
|
||||
/// // Recreate a key using from_container
|
||||
/// let relin_key = GlweRelinearisationKey::from_container(
|
||||
/// underlying_container,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// glwe_size,
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(relin_key.glwe_dimension(), glwe_size.to_glwe_dimension());
|
||||
/// assert_eq!(relin_key.glwe_size(), glwe_size);
|
||||
/// assert_eq!(relin_key.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(relin_key.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(relin_key.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(relin_key.ciphertext_modulus(), ciphertext_modulus);
|
||||
/// ```
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> GlweRelinearisationKey<C> {
|
||||
assert!(
|
||||
container.container_len() > 0,
|
||||
"Got an empty container to create an LweKeyswitchKey"
|
||||
);
|
||||
assert!(
|
||||
container.container_len()
|
||||
% glwe_relinearisation_key_input_key_element_encrypted_size(
|
||||
decomp_level_count,
|
||||
glwe_size,
|
||||
polynomial_size
|
||||
)
|
||||
== 0,
|
||||
"The provided container length is not valid. \
|
||||
It needs to be divisable by decomp_level_count * glwe_size * polynomial_size:\
|
||||
{}. Got container length: {} and decomp_level_count: {decomp_level_count:?}, \
|
||||
glwe_size: {glwe_size:?}, polynomial_size: {polynomial_size:?}.",
|
||||
glwe_relinearisation_key_input_key_element_encrypted_size(
|
||||
decomp_level_count,
|
||||
glwe_size,
|
||||
polynomial_size
|
||||
),
|
||||
container.container_len()
|
||||
);
|
||||
|
||||
GlweRelinearisationKey {
|
||||
data: container,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
glwe_size,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the [`GlweDimension`] of the [`GlweRelinearisationKey`].
|
||||
///
|
||||
/// See [`GlweRelinearisationKey::from_container`] for usage.
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_size.to_glwe_dimension()
|
||||
}
|
||||
|
||||
/// Return the [`GlweSize`] of the [`GlweRelinearisationKey`].
|
||||
///
|
||||
/// See [`GlweRelinearisationKey::from_container`] for usage.
|
||||
pub fn glwe_size(&self) -> GlweSize {
|
||||
self.glwe_size
|
||||
}
|
||||
|
||||
/// Return the output [`PolynomialSize`] of the [`GlweRelinearisationKey`].
|
||||
///
|
||||
/// See [`GlweRelinearisationKey::from_container`] for usage.
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionLevelCount`] of the [`GlweRelinearisationKey`].
|
||||
///
|
||||
/// See [`GlweRelinearisationKey::from_container`] for usage.
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomp_level_count
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionBaseLog`] of the [`GlweRelinearisationKey`].
|
||||
///
|
||||
/// See [`GlweRelinearisationKey::from_container`] for usage.
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
/// Return the number of elements in an encryption of an input [`GlweSecretKey`] element of the
|
||||
/// current [`GlweRelinearisationKey`].
|
||||
pub fn input_key_element_encrypted_size(&self) -> usize {
|
||||
glwe_relinearisation_key_input_key_element_encrypted_size(
|
||||
self.decomp_level_count,
|
||||
self.glwe_size,
|
||||
self.polynomial_size,
|
||||
)
|
||||
}
|
||||
|
||||
/// Return a view of the [`GlweRelinearisationKey`]. This is useful if an
|
||||
/// algorithm takes a view by value.
|
||||
pub fn as_view(&self) -> GlweRelinearisationKey<&'_ [Scalar]> {
|
||||
GlweRelinearisationKey::from_container(
|
||||
self.as_ref(),
|
||||
self.decomp_base_log,
|
||||
self.decomp_level_count,
|
||||
self.glwe_size,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
/// Consume the entity and return its underlying container.
|
||||
///
|
||||
/// See [`GlweRelinearisationKey::from_container`] for usage.
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
/// Return the [`CiphertextModulus`] of the [`GlweRelinearisationKey`]
|
||||
///
|
||||
/// See [`GlweRelinearisationKey::from_container`] for usage.
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> GlweRelinearisationKey<C> {
|
||||
/// Mutable variant of [`LweTracePackingKeyswitchKey::as_view`].
|
||||
pub fn as_mut_view(&mut self) -> GlweRelinearisationKey<&'_ mut [Scalar]> {
|
||||
let decomp_base_log = self.decomp_base_log;
|
||||
let decomp_level_count = self.decomp_level_count;
|
||||
let glwe_size = self.glwe_size;
|
||||
let polynomial_size = self.polynomial_size;
|
||||
let ciphertext_modulus = self.ciphertext_modulus;
|
||||
|
||||
GlweRelinearisationKey::from_container(
|
||||
self.as_mut(),
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
glwe_size,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`GlweRelinearisationKey`] owning the memory for its own storage.
|
||||
pub type GlweRelinearisationKeyOwned<Scalar> = GlweRelinearisationKey<Vec<Scalar>>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> GlweRelinearisationKeyOwned<Scalar> {
|
||||
/// Create a new [`GlweRelinearisationKey`].
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
|
||||
/// type. If you want to generate an [`GlweRelinearisationKey`] you need to use
|
||||
/// [`crate::core_crypto::algorithms::generate_glwe_relinearisation_key`]
|
||||
/// using this key as output.
|
||||
///
|
||||
/// See [`GlweRelinearisationKey::from_container`] for usage.
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> GlweRelinearisationKeyOwned<Scalar> {
|
||||
GlweRelinearisationKeyOwned::from_container(
|
||||
vec![
|
||||
fill_with;
|
||||
glwe_relinearisation_key_size(decomp_level_count, glwe_size, polynomial_size)
|
||||
],
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
glwe_size,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for GlweRelinearisationKey<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = GlweCiphertextListCreationMetadata<Scalar>;
|
||||
|
||||
type EntityView<'this> = GlweCiphertextListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = ();
|
||||
|
||||
// At the moment it does not make sense to return "sub" packing keyswitch keys. So we use a
|
||||
// dummy placeholder type here.
|
||||
type SelfView<'this> = DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
// Fix to use the correct ciphertext modulus
|
||||
GlweCiphertextListCreationMetadata(
|
||||
self.glwe_size,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.input_key_element_encrypted_size()
|
||||
}
|
||||
|
||||
/// Unimplemented for [`GlweRelinearisationKey`]. At the moment it does not
|
||||
/// make sense to return "sub" packing keyswitch keys.
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
unimplemented!(
|
||||
"This function is not supported for GlweRelinearisationKey. \
|
||||
At the moment it does not make sense to return 'sub' relinearisation keys."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for GlweRelinearisationKey<C>
|
||||
{
|
||||
type EntityMutView<'this> = GlweCiphertextListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
// At the moment it does not make sense to return "sub" relinearisation keys. So we use a
|
||||
// dummy placeholder type here.
|
||||
type SelfMutView<'this> = DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
|
||||
/// Metadata used in the [`CreateFrom`] implementation to create
|
||||
/// [`GlweRelinearisationKey`] entities.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GlweRelinearisationKeyCreationMetadata<Scalar: UnsignedInteger>(
|
||||
pub DecompositionBaseLog,
|
||||
pub DecompositionLevelCount,
|
||||
pub GlweSize,
|
||||
pub PolynomialSize,
|
||||
pub CiphertextModulus<Scalar>,
|
||||
);
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for GlweRelinearisationKey<C>
|
||||
{
|
||||
type Metadata = GlweRelinearisationKeyCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> GlweRelinearisationKey<C> {
|
||||
let GlweRelinearisationKeyCreationMetadata(
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
glwe_size,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
) = meta;
|
||||
GlweRelinearisationKey::from_container(
|
||||
from,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
glwe_size,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
//! Module containing the definition of the LwePublicFunctionalPackingKeyswitchKey.
|
||||
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
/// A [`LWE public functional packing keyswitch key`](`LwePublicFunctionalPackingKeyswitchKey`).
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LwePublicFunctionalPackingKeyswitchKey<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_glwe_size: GlweSize,
|
||||
output_polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]>
|
||||
for LwePublicFunctionalPackingKeyswitchKey<C>
|
||||
{
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]>
|
||||
for LwePublicFunctionalPackingKeyswitchKey<C>
|
||||
{
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of elements in an encryption of a list of input [`LweSecretKey`] elements for
|
||||
/// a [`LwePublicFunctionalPackingKeyswitchKey`] given a [`DecompositionLevelCount`] and output
|
||||
/// [`GlweSize`] and [`PolynomialSize`].
|
||||
pub fn lwe_pubfpksk_input_key_element_encrypted_size(
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_glwe_size: GlweSize,
|
||||
output_polynomial_size: PolynomialSize,
|
||||
) -> usize {
|
||||
// One ciphertext per level encrypted under the output key
|
||||
decomp_level_count.0 * output_glwe_size.0 * output_polynomial_size.0
|
||||
}
|
||||
|
||||
/// Return the number of elements in an [`LwePublicFunctionalPackingKeyswitchKey`] given an input
|
||||
/// ['size of the list of LWe] [`LweSize`], [`DecompositionLevelCount`], output [`GlweSize`], and
|
||||
/// output [`PolynomialSize`].
|
||||
pub fn lwe_pubfpksk_size(
|
||||
input_lwe_size: LweSize,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_glwe_size: GlweSize,
|
||||
output_polynomial_size: PolynomialSize,
|
||||
) -> usize {
|
||||
input_lwe_size.0
|
||||
* lwe_pubfpksk_input_key_element_encrypted_size(
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
output_polynomial_size,
|
||||
)
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>>
|
||||
LwePublicFunctionalPackingKeyswitchKey<C>
|
||||
{
|
||||
/// Create an [`LwePublicFunctionalPackingKeyswitchKey`] from an existing container.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function only wraps a container in the appropriate type. If you want to generate an
|
||||
/// [`LwePublicFunctionalPackingKeyswitchKey`] you need to use
|
||||
/// [`crate::core_crypto::algorithms::generate_lwe_public_functional_packing_keyswitch_key`] or
|
||||
/// the parallel variant
|
||||
/// [`crate::core_crypto::algorithms::par_generate_lwe_public_functional_packing_keyswitch_key`]
|
||||
/// using this key as output.
|
||||
///
|
||||
/// This docstring exhibits [`LwePublicFunctionalPackingKeyswitchKey`] primitives usage.
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for LwePublicFunctionalPackingKeyswitchKey creation
|
||||
/// let glwe_size = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let decomp_base_log = DecompositionBaseLog(8);
|
||||
/// let decomp_level_count = DecompositionLevelCount(3);
|
||||
/// let input_lwe_dimension = LweDimension(600);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // Create a new LwePublicFunctionalPackingKeyswitchKey
|
||||
/// let pubfpksk = LwePublicFunctionalPackingKeyswitchKey::new(
|
||||
/// 0u64,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// input_lwe_dimension,
|
||||
/// glwe_size,
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// pubfpksk.output_glwe_key_dimension(),
|
||||
/// glwe_size.to_glwe_dimension()
|
||||
/// );
|
||||
/// assert_eq!(pubfpksk.output_glwe_size(), glwe_size);
|
||||
/// assert_eq!(pubfpksk.output_polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(pubfpksk.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(pubfpksk.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(pubfpksk.input_lwe_key_dimension(), input_lwe_dimension);
|
||||
/// assert_eq!(pubfpksk.ciphertext_modulus(), ciphertext_modulus);
|
||||
///
|
||||
/// // Demonstrate how to recover the allocated container
|
||||
/// let underlying_container: Vec<u64> = pubfpksk.into_container();
|
||||
///
|
||||
/// // Recreate a key using from_container
|
||||
/// let pubfpksk = LwePublicFunctionalPackingKeyswitchKeyList::from_container(
|
||||
/// underlying_container,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// input_lwe_dimension.to_lwe_size(),
|
||||
/// glwe_size,
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// pubfpksk.output_glwe_key_dimension(),
|
||||
/// glwe_size.to_glwe_dimension()
|
||||
/// );
|
||||
/// assert_eq!(pubfpksk.output_glwe_size(), glwe_size);
|
||||
/// assert_eq!(pubfpksk.output_polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(pubfpksk.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(pubfpksk.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(pubfpksk.input_lwe_key_dimension(), input_lwe_dimension);
|
||||
/// assert_eq!(pubfpksk.ciphertext_modulus(), ciphertext_modulus);
|
||||
/// ```
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_glwe_size: GlweSize,
|
||||
output_polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> LwePublicFunctionalPackingKeyswitchKey<C> {
|
||||
assert!(
|
||||
container.container_len() > 0,
|
||||
"Got an empty container to create an LweKeyswitchKey"
|
||||
);
|
||||
assert!(
|
||||
container.container_len()
|
||||
% lwe_pubfpksk_input_key_element_encrypted_size(
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
output_polynomial_size
|
||||
)
|
||||
== 0,
|
||||
"The provided container length is not valid. \
|
||||
It needs to be dividable by decomp_level_count * output_glwe_size * output_polynomial_size:\
|
||||
{}. Got container length: {} and decomp_level_count: {decomp_level_count:?}, \
|
||||
output_glwe_size: {output_glwe_size:?}, output_polynomial_size: \
|
||||
{output_polynomial_size:?}.",
|
||||
lwe_pubfpksk_input_key_element_encrypted_size(
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
output_polynomial_size
|
||||
),
|
||||
container.container_len()
|
||||
);
|
||||
|
||||
LwePublicFunctionalPackingKeyswitchKey {
|
||||
data: container,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
output_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the output key [`GlweDimension`] of the [`LwePublicFunctionalPackingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn output_glwe_key_dimension(&self) -> GlweDimension {
|
||||
self.output_glwe_size.to_glwe_dimension()
|
||||
}
|
||||
|
||||
/// Return the output [`GlweSize`] of the [`LwePublicFunctionalPackingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn output_glwe_size(&self) -> GlweSize {
|
||||
self.output_glwe_size
|
||||
}
|
||||
|
||||
/// Return the output [`PolynomialSize`] of the [`LwePublicFunctionalPackingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn output_polynomial_size(&self) -> PolynomialSize {
|
||||
self.output_polynomial_size
|
||||
}
|
||||
|
||||
/// Return the input key [`LweDimension`] of the [`LwePublicFunctionalPackingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn input_lwe_key_dimension(&self) -> LweDimension {
|
||||
LweDimension(self.data.container_len() / self.input_key_element_encrypted_size() - 1)
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionLevelCount`] of the [`LwePublicFunctionalPackingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomp_level_count
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionBaseLog`] of the [`LwePublicFunctionalPackingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
/// Return the number of elements in an encryption of an input [`LweSecretKey`] element of the
|
||||
/// current [`LwePublicFunctionalPackingKeyswitchKey`].
|
||||
pub fn input_key_element_encrypted_size(&self) -> usize {
|
||||
lwe_pubfpksk_input_key_element_encrypted_size(
|
||||
self.decomp_level_count,
|
||||
self.output_glwe_size,
|
||||
self.output_polynomial_size,
|
||||
)
|
||||
}
|
||||
|
||||
/// Return a view of the [`LwePublicFunctionalPackingKeyswitchKey`]. This is useful if an
|
||||
/// algorithm takes a view by value.
|
||||
pub fn as_view(&self) -> LwePublicFunctionalPackingKeyswitchKey<&'_ [Scalar]> {
|
||||
LwePublicFunctionalPackingKeyswitchKey::from_container(
|
||||
self.as_ref(),
|
||||
self.decomp_base_log,
|
||||
self.decomp_level_count,
|
||||
self.output_glwe_size,
|
||||
self.output_polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
/// Consume the entity and return its underlying container.
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
/// Return the [`CiphertextModulus`] of the [`LwePublicFunctionalPackingKeyswitchKey`]
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>>
|
||||
LwePublicFunctionalPackingKeyswitchKey<C>
|
||||
{
|
||||
/// Mutable variant of [`LwePublicFunctionalPackingKeyswitchKey::as_view`].
|
||||
pub fn as_mut_view(&mut self) -> LwePublicFunctionalPackingKeyswitchKey<&'_ mut [Scalar]> {
|
||||
let decomp_base_log = self.decomp_base_log;
|
||||
let decomp_level_count = self.decomp_level_count;
|
||||
let output_glwe_size = self.output_glwe_size;
|
||||
let output_polynomial_size = self.output_polynomial_size;
|
||||
let ciphertext_modulus = self.ciphertext_modulus;
|
||||
|
||||
LwePublicFunctionalPackingKeyswitchKey::from_container(
|
||||
self.as_mut(),
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
output_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`LwePublicFunctionalPackingKeyswitchKey`] owning the memory for its own storage.
|
||||
pub type LwePublicFunctionalPackingKeyswitchKeyOwned<Scalar> =
|
||||
LwePublicFunctionalPackingKeyswitchKey<Vec<Scalar>>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> LwePublicFunctionalPackingKeyswitchKeyOwned<Scalar> {
|
||||
/// Create an [`LwePublicFunctionalPackingKeyswitchKey`] from an existing container.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
|
||||
/// type. If you want to generate an [`LwePublicFunctionalPackingKeyswitchKey`] you need to use
|
||||
/// [`crate::core_crypto::algorithms::generate_lwe_public_functional_packing_keyswitch_key`] or
|
||||
/// the parallel variant
|
||||
/// [`crate::core_crypto::algorithms::par_generate_lwe_public_functional_packing_keyswitch_key`]
|
||||
/// using this key as output.
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_key_lwe_dimension: LweDimension,
|
||||
output_glwe_size: GlweSize,
|
||||
output_polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> LwePublicFunctionalPackingKeyswitchKeyOwned<Scalar> {
|
||||
LwePublicFunctionalPackingKeyswitchKeyOwned::from_container(
|
||||
vec![
|
||||
fill_with;
|
||||
lwe_pubfpksk_size(
|
||||
input_key_lwe_dimension.to_lwe_size(),
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
output_polynomial_size
|
||||
)
|
||||
],
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
output_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for LwePublicFunctionalPackingKeyswitchKey<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = GlweCiphertextListCreationMetadata<Scalar>;
|
||||
|
||||
type EntityView<'this> = GlweCiphertextListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = ();
|
||||
|
||||
// At the moment it does not make sense to return "sub" packing keyswitch keys. So we use a
|
||||
// dummy placeholder type here.
|
||||
type SelfView<'this> = DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
GlweCiphertextListCreationMetadata(
|
||||
self.output_glwe_size,
|
||||
self.output_polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.input_key_element_encrypted_size()
|
||||
}
|
||||
|
||||
/// Unimplemented for [`LwePublicFunctionalPackingKeyswitchKey`]. At the moment it does not
|
||||
/// make sense to return "sub" packing keyswitch keys.
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
unimplemented!(
|
||||
"This function is not supported for LwePublicFunctionalPackingKeyswitchKey. \
|
||||
At the moment it does not make sense to return 'sub' packing keyswitch keys."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for LwePublicFunctionalPackingKeyswitchKey<C>
|
||||
{
|
||||
type EntityMutView<'this> = GlweCiphertextListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
// At the moment it does not make sense to return "sub" packing keyswitch keys. So we use a
|
||||
// dummy placeholder type here.
|
||||
type SelfMutView<'this> = DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
|
||||
/// Metadata used in the [`CreateFrom`] implementation to create
|
||||
/// [`LwePublicFunctionalPackingKeyswitchKey`] entities.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct LwePublicFunctionalPackingKeyswitchKeyCreationMetadata<Scalar: UnsignedInteger>(
|
||||
pub DecompositionBaseLog,
|
||||
pub DecompositionLevelCount,
|
||||
pub GlweSize,
|
||||
pub PolynomialSize,
|
||||
pub CiphertextModulus<Scalar>,
|
||||
);
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for LwePublicFunctionalPackingKeyswitchKey<C>
|
||||
{
|
||||
type Metadata = LwePublicFunctionalPackingKeyswitchKeyCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> LwePublicFunctionalPackingKeyswitchKey<C> {
|
||||
let LwePublicFunctionalPackingKeyswitchKeyCreationMetadata(
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
output_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
) = meta;
|
||||
LwePublicFunctionalPackingKeyswitchKey::from_container(
|
||||
from,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
output_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
//! Module containing the definition of the LwePublicFunctionalPackingKeyswitchKeyList.
|
||||
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
/// A contiguous list containing [`LWE private functional packing keyswitch
|
||||
/// keys`](`crate::core_crypto::entities::LwePrivateFunctionalPackingKeyswitchKey`).
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LwePublicFunctionalPackingKeyswitchKeyList<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_size: LweSize,
|
||||
output_glwe_size: GlweSize,
|
||||
output_polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]>
|
||||
for LwePublicFunctionalPackingKeyswitchKeyList<C>
|
||||
{
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]>
|
||||
for LwePublicFunctionalPackingKeyswitchKeyList<C>
|
||||
{
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>>
|
||||
LwePublicFunctionalPackingKeyswitchKeyList<C>
|
||||
{
|
||||
/// Create an [`LwePublicFunctionalPackingKeyswitchKeyList`] from an existing container.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function only wraps a container in the appropriate type. If you want to generate keys
|
||||
/// in the list you need to use
|
||||
/// [`crate::core_crypto::algorithms::generate_lwe_public_functional_packing_keyswitch_key`] or
|
||||
/// the parallel variant
|
||||
/// [`crate::core_crypto::algorithms::par_generate_lwe_public_functional_packing_keyswitch_key`]
|
||||
/// on the individual keys in the list.
|
||||
///
|
||||
/// This docstring exhibits [`LwePublicFunctionalPackingKeyswitchKeyList`] primitives usage.
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for LwePublicFunctionalPackingKeyswitchKeyList creation
|
||||
/// let glwe_size = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let decomp_base_log = DecompositionBaseLog(8);
|
||||
/// let decomp_level_count = DecompositionLevelCount(3);
|
||||
/// let input_lwe_dimension = LweDimension(600);
|
||||
/// let lwe_pubfpksk_count = FunctionalPackingKeyswitchKeyCount(2);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // Create a new LwePublicFunctionalPackingKeyswitchKeyList
|
||||
/// let pubfpksk_list = LwePublicFunctionalPackingKeyswitchKeyList::new(
|
||||
/// 0u64,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// input_lwe_dimension,
|
||||
/// glwe_size,
|
||||
/// polynomial_size,
|
||||
/// lwe_pubfpksk_count,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// pubfpksk_list.output_glwe_key_dimension(),
|
||||
/// glwe_size.to_glwe_dimension()
|
||||
/// );
|
||||
/// assert_eq!(pubfpksk_list.output_glwe_size(), glwe_size);
|
||||
/// assert_eq!(pubfpksk_list.output_polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(pubfpksk_list.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(
|
||||
/// pubfpksk_list.decomposition_level_count(),
|
||||
/// decomp_level_count
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// pubfpksk_list.input_lwe_size(),
|
||||
/// input_lwe_dimension.to_lwe_size()
|
||||
/// );
|
||||
/// assert_eq!(pubfpksk_list.input_lwe_key_dimension(), input_lwe_dimension);
|
||||
/// assert_eq!(pubfpksk_list.lwe_pubfpksk_count(), lwe_pubfpksk_count);
|
||||
/// assert_eq!(pubfpksk_list.ciphertext_modulus(), ciphertext_modulus);
|
||||
///
|
||||
/// // Demonstrate how to recover the allocated container
|
||||
/// let underlying_container: Vec<u64> = pubfpksk_list.into_container();
|
||||
///
|
||||
/// // Recreate a list using from_container
|
||||
/// let pubfpksk_list = LwePublicFunctionalPackingKeyswitchKeyList::from_container(
|
||||
/// underlying_container,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// input_lwe_dimension.to_lwe_size(),
|
||||
/// glwe_size,
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// pubfpksk_list.output_glwe_key_dimension(),
|
||||
/// glwe_size.to_glwe_dimension()
|
||||
/// );
|
||||
/// assert_eq!(pubfpksk_list.output_glwe_size(), glwe_size);
|
||||
/// assert_eq!(pubfpksk_list.output_polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(pubfpksk_list.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(
|
||||
/// pubfpksk_list.decomposition_level_count(),
|
||||
/// decomp_level_count
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// pubfpksk_list.input_lwe_size(),
|
||||
/// input_lwe_dimension.to_lwe_size()
|
||||
/// );
|
||||
/// assert_eq!(pubfpksk_list.input_lwe_key_dimension(), input_lwe_dimension);
|
||||
/// assert_eq!(pubfpksk_list.lwe_pubfpksk_count(), lwe_pubfpksk_count);
|
||||
/// assert_eq!(pubfpksk_list.ciphertext_modulus(), ciphertext_modulus);
|
||||
/// ```
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_size: LweSize,
|
||||
output_glwe_size: GlweSize,
|
||||
output_polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> LwePublicFunctionalPackingKeyswitchKeyList<C> {
|
||||
assert!(
|
||||
container.container_len()
|
||||
% lwe_pubfpksk_size(
|
||||
input_lwe_size,
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
output_polynomial_size
|
||||
)
|
||||
== 0,
|
||||
"The provided container length is not valid. \
|
||||
It needs to be dividable by input_lwe_size * decomp_level_count * output_glwe_size * \
|
||||
output_polynomial_size: {}. Got container length: {} and input_lwe_size: {input_lwe_size:?}\
|
||||
decomp_level_count: {decomp_level_count:?}, output_glwe_size: {output_glwe_size:?}, \
|
||||
output_polynomial_size: {output_polynomial_size:?}.",
|
||||
lwe_pubfpksk_size(
|
||||
input_lwe_size,
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
output_polynomial_size
|
||||
),
|
||||
container.container_len()
|
||||
);
|
||||
|
||||
LwePublicFunctionalPackingKeyswitchKeyList {
|
||||
data: container,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_size,
|
||||
output_glwe_size,
|
||||
output_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the output key [`GlweDimension`] of the [`LwePublicFunctionalPackingKeyswitchKey`]
|
||||
/// stored in the list.
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
|
||||
pub fn output_glwe_key_dimension(&self) -> GlweDimension {
|
||||
self.output_glwe_size.to_glwe_dimension()
|
||||
}
|
||||
|
||||
/// Return the output [`GlweSize`] of the [`LwePublicFunctionalPackingKeyswitchKey`] stored in
|
||||
/// the list.
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
|
||||
pub fn output_glwe_size(&self) -> GlweSize {
|
||||
self.output_glwe_size
|
||||
}
|
||||
|
||||
/// Return the output [`PolynomialSize`] of the [`LwePublicFunctionalPackingKeyswitchKey`]
|
||||
/// stored in the list.
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
|
||||
pub fn output_polynomial_size(&self) -> PolynomialSize {
|
||||
self.output_polynomial_size
|
||||
}
|
||||
|
||||
/// Return the input key [`LweDimension`] of the [`LwePublicFunctionalPackingKeyswitchKey`]
|
||||
/// stored in the list.
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
|
||||
pub fn input_lwe_key_dimension(&self) -> LweDimension {
|
||||
self.input_lwe_size.to_lwe_dimension()
|
||||
}
|
||||
|
||||
/// Return the input [`LweSize`] of the [`LwePublicFunctionalPackingKeyswitchKey`]stored in the
|
||||
/// list.
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
|
||||
pub fn input_lwe_size(&self) -> LweSize {
|
||||
self.input_lwe_size
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionLevelCount`] of the [`LwePublicFunctionalPackingKeyswitchKey`]
|
||||
/// stored in the list.
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomp_level_count
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionBaseLog`] of the [`LwePublicFunctionalPackingKeyswitchKey`]
|
||||
/// stored in the list.
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
/// Return the number of elements in a [`LwePublicFunctionalPackingKeyswitchKey`] stored in
|
||||
/// the list.
|
||||
pub fn lwe_pubfpksk_size(&self) -> usize {
|
||||
lwe_pubfpksk_size(
|
||||
self.input_lwe_size,
|
||||
self.decomp_level_count,
|
||||
self.output_glwe_size,
|
||||
self.output_polynomial_size,
|
||||
)
|
||||
}
|
||||
|
||||
/// Return the [`FunctionalPackingKeyswitchKeyCount`] of the
|
||||
/// [`LwePublicFunctionalPackingKeyswitchKeyList`].
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
|
||||
pub fn lwe_pubfpksk_count(&self) -> FunctionalPackingKeyswitchKeyCount {
|
||||
FunctionalPackingKeyswitchKeyCount(self.as_ref().container_len() / self.lwe_pubfpksk_size())
|
||||
}
|
||||
|
||||
/// Return a view of the [`LwePublicFunctionalPackingKeyswitchKeyList`]. This is useful if an
|
||||
/// algorithm takes a view by value.
|
||||
pub fn as_view(&self) -> LwePublicFunctionalPackingKeyswitchKeyList<&'_ [Scalar]> {
|
||||
LwePublicFunctionalPackingKeyswitchKeyList::from_container(
|
||||
self.as_ref(),
|
||||
self.decomp_base_log,
|
||||
self.decomp_level_count,
|
||||
self.input_lwe_size,
|
||||
self.output_glwe_size,
|
||||
self.output_polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
/// Consume the entity and return its underlying container.
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
/// Return the [`CiphertextModulus`] of the [`LwePublicFunctionalPackingKeyswitchKeyList`]
|
||||
///
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>>
|
||||
LwePublicFunctionalPackingKeyswitchKeyList<C>
|
||||
{
|
||||
/// Mutable variant of [`LwePublicFunctionalPackingKeyswitchKeyList::as_view`].
|
||||
pub fn as_mut_view(&mut self) -> LwePublicFunctionalPackingKeyswitchKeyList<&'_ mut [Scalar]> {
|
||||
let decomp_base_log = self.decomp_base_log;
|
||||
let decomp_level_count = self.decomp_level_count;
|
||||
let input_lwe_size = self.input_lwe_size;
|
||||
let output_glwe_size = self.output_glwe_size;
|
||||
let output_polynomial_size = self.output_polynomial_size;
|
||||
let ciphertext_modulus = self.ciphertext_modulus;
|
||||
|
||||
LwePublicFunctionalPackingKeyswitchKeyList::from_container(
|
||||
self.as_mut(),
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_size,
|
||||
output_glwe_size,
|
||||
output_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`LwePublicFunctionalPackingKeyswitchKeyList`] owning the memory for its own storage.
|
||||
pub type LwePublicFunctionalPackingKeyswitchKeyListOwned<Scalar> =
|
||||
LwePublicFunctionalPackingKeyswitchKeyList<Vec<Scalar>>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> LwePublicFunctionalPackingKeyswitchKeyListOwned<Scalar> {
|
||||
/// Allocate memory and create a new owned [`LwePublicFunctionalPackingKeyswitchKeyList`].
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
|
||||
/// type. If you want to generate keys in the list you need to use
|
||||
/// [`crate::core_crypto::algorithms::generate_lwe_public_functional_packing_keyswitch_key`] or
|
||||
/// the parallel variant
|
||||
/// [`crate::core_crypto::algorithms::par_generate_lwe_public_functional_packing_keyswitch_key`]
|
||||
/// on the individual keys in the list.
|
||||
/// See [`LwePublicFunctionalPackingKeyswitchKeyList::from_container`] for usage.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_key_lwe_dimension: LweDimension,
|
||||
output_glwe_size: GlweSize,
|
||||
output_polynomial_size: PolynomialSize,
|
||||
pubfpksk_count: FunctionalPackingKeyswitchKeyCount,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> LwePublicFunctionalPackingKeyswitchKeyListOwned<Scalar> {
|
||||
LwePublicFunctionalPackingKeyswitchKeyListOwned::from_container(
|
||||
vec![
|
||||
fill_with;
|
||||
pubfpksk_count.0
|
||||
* lwe_pubfpksk_size(
|
||||
input_key_lwe_dimension.to_lwe_size(),
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
output_polynomial_size
|
||||
)
|
||||
],
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_key_lwe_dimension.to_lwe_size(),
|
||||
output_glwe_size,
|
||||
output_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata used in the [`CreateFrom`] implementation to create
|
||||
/// [`LwePublicFunctionalPackingKeyswitchKeyList`] entities.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct LwePublicFunctionalPackingKeyswitchKeyListCreationMetadata<Scalar: UnsignedInteger>(
|
||||
pub DecompositionBaseLog,
|
||||
pub DecompositionLevelCount,
|
||||
pub LweSize,
|
||||
pub GlweSize,
|
||||
pub PolynomialSize,
|
||||
pub CiphertextModulus<Scalar>,
|
||||
);
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for LwePublicFunctionalPackingKeyswitchKeyList<C>
|
||||
{
|
||||
type Metadata = LwePublicFunctionalPackingKeyswitchKeyListCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> Self {
|
||||
let LwePublicFunctionalPackingKeyswitchKeyListCreationMetadata(
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_size,
|
||||
output_glwe_size,
|
||||
output_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
) = meta;
|
||||
LwePublicFunctionalPackingKeyswitchKeyList::from_container(
|
||||
from,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_size,
|
||||
output_glwe_size,
|
||||
output_polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for LwePublicFunctionalPackingKeyswitchKeyList<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = LwePublicFunctionalPackingKeyswitchKeyCreationMetadata<Scalar>;
|
||||
|
||||
type EntityView<'this> = LwePublicFunctionalPackingKeyswitchKey<&'this [Self::Element]>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = LwePublicFunctionalPackingKeyswitchKeyListCreationMetadata<Scalar>;
|
||||
|
||||
type SelfView<'this> = LwePublicFunctionalPackingKeyswitchKeyList<&'this [Self::Element]>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
LwePublicFunctionalPackingKeyswitchKeyCreationMetadata(
|
||||
self.decomposition_base_log(),
|
||||
self.decomposition_level_count(),
|
||||
self.output_glwe_size(),
|
||||
self.output_polynomial_size(),
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.lwe_pubfpksk_size()
|
||||
}
|
||||
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
LwePublicFunctionalPackingKeyswitchKeyListCreationMetadata(
|
||||
self.decomposition_base_log(),
|
||||
self.decomposition_level_count(),
|
||||
self.input_lwe_size(),
|
||||
self.output_glwe_size(),
|
||||
self.output_polynomial_size(),
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for LwePublicFunctionalPackingKeyswitchKeyList<C>
|
||||
{
|
||||
type EntityMutView<'this> = LwePublicFunctionalPackingKeyswitchKey<&'this mut [Self::Element]>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfMutView<'this> = LwePublicFunctionalPackingKeyswitchKeyList<&'this mut [Self::Element]>
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
410
tfhe/src/core_crypto/entities/lwe_trace_packing_keyswitch_key.rs
Normal file
410
tfhe/src/core_crypto/entities/lwe_trace_packing_keyswitch_key.rs
Normal file
@@ -0,0 +1,410 @@
|
||||
//! Module containing the definition of the LweTracePackingKeyswitchKey.
|
||||
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
|
||||
/// An [`LWE trace packing keyswitch key`](`LweTracePackingKeyswitchKey`).
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct LweTracePackingKeyswitchKey<C: Container>
|
||||
where
|
||||
C::Element: UnsignedInteger,
|
||||
{
|
||||
data: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_size: LweSize,
|
||||
output_glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: Container<Element = T>> AsRef<[T]> for LweTracePackingKeyswitchKey<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger, C: ContainerMut<Element = T>> AsMut<[T]>
|
||||
for LweTracePackingKeyswitchKey<C>
|
||||
{
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of elements in an encryption of an input [`LweSecretKey`] element for a
|
||||
/// [`LweTracePackingKeyswitchKey`] given a [`DecompositionLevelCount`] and output
|
||||
/// [`GlweSize`] and [`PolynomialSize`].
|
||||
pub fn lwe_tpksk_input_key_element_encrypted_size(
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> usize {
|
||||
// One ciphertext per level encrypted under the output key
|
||||
decomp_level_count.0 * output_glwe_size.0 * polynomial_size.0
|
||||
}
|
||||
|
||||
/// Return the number of elements in an [`LweTracePackingKeyswitchKey`] given a
|
||||
/// [`DecompositionLevelCount`], output [`GlweSize`], and output [`PolynomialSize`].
|
||||
pub fn lwe_tpksk_size(
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
) -> usize {
|
||||
output_glwe_size.to_glwe_dimension().0
|
||||
* polynomial_size.log2().0
|
||||
* lwe_tpksk_input_key_element_encrypted_size(
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
polynomial_size,
|
||||
)
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> LweTracePackingKeyswitchKey<C> {
|
||||
/// Create an [`LweTracePackingKeyswitchKey`] from an existing container.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function only wraps a container in the appropriate type. If you want to generate an
|
||||
/// [`LweTracePackingKeyswitchKey`] you need to use
|
||||
/// [`crate::core_crypto::algorithms::generate_lwe_trace_packing_keyswitch_key`]
|
||||
/// using this key as output.
|
||||
///
|
||||
/// This docstring exhibits [`LweTracePackingKeyswitchKey`] primitives usage.
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for LweTracePackingKeyswitchKey creation
|
||||
/// let lwe_size = LweSize(1001);
|
||||
/// let glwe_size = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let decomp_base_log = DecompositionBaseLog(8);
|
||||
/// let decomp_level_count = DecompositionLevelCount(3);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // Create a new LweTracePackingKeyswitchKey
|
||||
/// let tpksk = LweTracePackingKeyswitchKey::new(
|
||||
/// 0u64,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// lwe_size,
|
||||
/// glwe_size,
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// tpksk.output_glwe_key_dimension(),
|
||||
/// glwe_size.to_glwe_dimension()
|
||||
/// );
|
||||
/// assert_eq!(tpksk.output_glwe_size(), glwe_size);
|
||||
/// assert_eq!(tpksk.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(tpksk.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(tpksk.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(tpksk.ciphertext_modulus(), ciphertext_modulus);
|
||||
///
|
||||
/// // Demonstrate how to recover the allocated container
|
||||
/// let underlying_container: Vec<u64> = tpksk.into_container();
|
||||
///
|
||||
/// // Recreate a key using from_container
|
||||
/// let tpksk = LweTracePackingKeyswitchKey::from_container(
|
||||
/// underlying_container,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// lwe_size,
|
||||
/// glwe_size,
|
||||
/// polynomial_size,
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// tpksk.output_glwe_key_dimension(),
|
||||
/// glwe_size.to_glwe_dimension()
|
||||
/// );
|
||||
/// assert_eq!(tpksk.input_lwe_size(), lwe_size);
|
||||
/// assert_eq!(tpksk.output_glwe_size(), glwe_size);
|
||||
/// assert_eq!(tpksk.polynomial_size(), polynomial_size);
|
||||
/// assert_eq!(tpksk.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(tpksk.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(tpksk.ciphertext_modulus(), ciphertext_modulus);
|
||||
/// ```
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_size: LweSize,
|
||||
output_glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<C::Element>,
|
||||
) -> LweTracePackingKeyswitchKey<C> {
|
||||
assert!(
|
||||
container.container_len() > 0,
|
||||
"Got an empty container to create an LweKeyswitchKey"
|
||||
);
|
||||
assert!(
|
||||
container.container_len()
|
||||
% lwe_tpksk_input_key_element_encrypted_size(
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
polynomial_size
|
||||
)
|
||||
== 0,
|
||||
"The provided container length is not valid. \
|
||||
It needs to be divisable by decomp_level_count * output_glwe_size * polynomial_size:\
|
||||
{}. Got container length: {} and decomp_level_count: {decomp_level_count:?}, \
|
||||
output_glwe_size: {output_glwe_size:?}, polynomial_size: \
|
||||
{polynomial_size:?}.",
|
||||
lwe_tpksk_input_key_element_encrypted_size(
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
polynomial_size
|
||||
),
|
||||
container.container_len()
|
||||
);
|
||||
|
||||
LweTracePackingKeyswitchKey {
|
||||
data: container,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_size,
|
||||
output_glwe_size,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the output key [`GlweDimension`] of the [`LweTracePackingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn output_glwe_key_dimension(&self) -> GlweDimension {
|
||||
self.output_glwe_size.to_glwe_dimension()
|
||||
}
|
||||
|
||||
/// Return the output [`GlweSize`] of the [`LweTracePackingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn output_glwe_size(&self) -> GlweSize {
|
||||
self.output_glwe_size
|
||||
}
|
||||
|
||||
/// Return the output [`PolynomialSize`] of the [`LweTracePackingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
/// Return the input [`LweSize`] of the [`LweTracePackingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn input_lwe_size(&self) -> LweSize {
|
||||
self.input_lwe_size
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionLevelCount`] of the [`LweTracePackingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomp_level_count
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionBaseLog`] of the [`LweTracePackingKeyswitchKey`].
|
||||
///
|
||||
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
/// Return the number of elements in an encryption of an input [`LweSecretKey`] element of the
|
||||
/// current [`LweTracePackingKeyswitchKey`].
|
||||
pub fn input_key_element_encrypted_size(&self) -> usize {
|
||||
lwe_tpksk_input_key_element_encrypted_size(
|
||||
self.decomp_level_count,
|
||||
self.output_glwe_size,
|
||||
self.polynomial_size,
|
||||
)
|
||||
}
|
||||
|
||||
/// Return a view of the [`LweTracePackingKeyswitchKey`]. This is useful if an
|
||||
/// algorithm takes a view by value.
|
||||
pub fn as_view(&self) -> LweTracePackingKeyswitchKey<&'_ [Scalar]> {
|
||||
LweTracePackingKeyswitchKey::from_container(
|
||||
self.as_ref(),
|
||||
self.decomp_base_log,
|
||||
self.decomp_level_count,
|
||||
self.input_lwe_size,
|
||||
self.output_glwe_size,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
/// Consume the entity and return its underlying container.
|
||||
///
|
||||
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
/// Return the [`CiphertextModulus`] of the [`LweTracePackingKeyswitchKey`]
|
||||
///
|
||||
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus<C::Element> {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> LweTracePackingKeyswitchKey<C> {
|
||||
/// Mutable variant of [`LweTracePackingKeyswitchKey::as_view`].
|
||||
pub fn as_mut_view(&mut self) -> LweTracePackingKeyswitchKey<&'_ mut [Scalar]> {
|
||||
let decomp_base_log = self.decomp_base_log;
|
||||
let decomp_level_count = self.decomp_level_count;
|
||||
let input_lwe_size = self.input_lwe_size;
|
||||
let output_glwe_size = self.output_glwe_size;
|
||||
let polynomial_size = self.polynomial_size;
|
||||
let ciphertext_modulus = self.ciphertext_modulus;
|
||||
|
||||
LweTracePackingKeyswitchKey::from_container(
|
||||
self.as_mut(),
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_size,
|
||||
output_glwe_size,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`LweTracePackingKeyswitchKey`] owning the memory for its own storage.
|
||||
pub type LweTracePackingKeyswitchKeyOwned<Scalar> = LweTracePackingKeyswitchKey<Vec<Scalar>>;
|
||||
|
||||
impl<Scalar: UnsignedInteger> LweTracePackingKeyswitchKeyOwned<Scalar> {
|
||||
/// Create an [`LweTracePackingKeyswitchKey`] from an existing container.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
|
||||
/// type. If you want to generate an [`LweTracePackingKeyswitchKey`] you need to use
|
||||
/// [`crate::core_crypto::algorithms::generate_lwe_trace_packing_keyswitch_key`]
|
||||
/// using this key as output.
|
||||
///
|
||||
/// See [`LweTracePackingKeyswitchKey::from_container`] for usage.
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_lwe_size: LweSize,
|
||||
output_glwe_size: GlweSize,
|
||||
polynomial_size: PolynomialSize,
|
||||
ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
) -> LweTracePackingKeyswitchKeyOwned<Scalar> {
|
||||
LweTracePackingKeyswitchKeyOwned::from_container(
|
||||
vec![fill_with; lwe_tpksk_size(decomp_level_count, output_glwe_size, polynomial_size)],
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_size,
|
||||
output_glwe_size,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ContiguousEntityContainer
|
||||
for LweTracePackingKeyswitchKey<C>
|
||||
{
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = GlweCiphertextListCreationMetadata<Scalar>;
|
||||
|
||||
type EntityView<'this> = GlweCiphertextListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = ();
|
||||
|
||||
// At the moment it does not make sense to return "sub" packing keyswitch keys. So we use a
|
||||
// dummy placeholder type here.
|
||||
type SelfView<'this> = DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> Self::EntityViewMetadata {
|
||||
GlweCiphertextListCreationMetadata(
|
||||
self.output_glwe_size,
|
||||
self.polynomial_size,
|
||||
self.ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.input_key_element_encrypted_size() * self.output_glwe_size.to_glwe_dimension().0
|
||||
}
|
||||
|
||||
/// Unimplemented for [`LweTracePackingKeyswitchKey`]. At the moment it does not
|
||||
/// make sense to return "sub" packing keyswitch keys.
|
||||
fn get_self_view_creation_metadata(&self) -> Self::SelfViewMetadata {
|
||||
unimplemented!(
|
||||
"This function is not supported for LweTracePackingKeyswitchKey. \
|
||||
At the moment it does not make sense to return 'sub' packing keyswitch keys."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntityContainerMut
|
||||
for LweTracePackingKeyswitchKey<C>
|
||||
{
|
||||
type EntityMutView<'this> = GlweCiphertextListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
// At the moment it does not make sense to return "sub" packing keyswitch keys. So we use a
|
||||
// dummy placeholder type here.
|
||||
type SelfMutView<'this> = DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
|
||||
/// Metadata used in the [`CreateFrom`] implementation to create
|
||||
/// [`LweTracePackingKeyswitchKey`] entities.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct LweTracePackingKeyswitchKeyCreationMetadata<Scalar: UnsignedInteger>(
|
||||
pub DecompositionBaseLog,
|
||||
pub DecompositionLevelCount,
|
||||
pub LweSize,
|
||||
pub GlweSize,
|
||||
pub PolynomialSize,
|
||||
pub CiphertextModulus<Scalar>,
|
||||
);
|
||||
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> CreateFrom<C>
|
||||
for LweTracePackingKeyswitchKey<C>
|
||||
{
|
||||
type Metadata = LweTracePackingKeyswitchKeyCreationMetadata<Scalar>;
|
||||
|
||||
#[inline]
|
||||
fn create_from(from: C, meta: Self::Metadata) -> LweTracePackingKeyswitchKey<C> {
|
||||
let LweTracePackingKeyswitchKeyCreationMetadata(
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_size,
|
||||
output_glwe_size,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
) = meta;
|
||||
LweTracePackingKeyswitchKey::from_container(
|
||||
from,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
input_lwe_size,
|
||||
output_glwe_size,
|
||||
polynomial_size,
|
||||
ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ pub mod ggsw_ciphertext;
|
||||
pub mod ggsw_ciphertext_list;
|
||||
pub mod glwe_ciphertext;
|
||||
pub mod glwe_ciphertext_list;
|
||||
pub mod glwe_keyswitch_key;
|
||||
pub mod glwe_relinearisation_key;
|
||||
pub mod glwe_secret_key;
|
||||
pub mod gsw_ciphertext;
|
||||
pub mod lwe_bootstrap_key;
|
||||
@@ -17,8 +19,11 @@ pub mod lwe_keyswitch_key;
|
||||
pub mod lwe_multi_bit_bootstrap_key;
|
||||
pub mod lwe_private_functional_packing_keyswitch_key;
|
||||
pub mod lwe_private_functional_packing_keyswitch_key_list;
|
||||
pub mod lwe_public_functional_packing_keyswitch_key;
|
||||
pub mod lwe_public_functional_packing_keyswitch_key_list;
|
||||
pub mod lwe_public_key;
|
||||
pub mod lwe_secret_key;
|
||||
pub mod lwe_trace_packing_keyswitch_key;
|
||||
pub mod plaintext;
|
||||
pub mod plaintext_list;
|
||||
pub mod polynomial;
|
||||
@@ -27,6 +32,7 @@ pub mod seeded_ggsw_ciphertext;
|
||||
pub mod seeded_ggsw_ciphertext_list;
|
||||
pub mod seeded_glwe_ciphertext;
|
||||
pub mod seeded_glwe_ciphertext_list;
|
||||
pub mod seeded_glwe_keyswitch_key;
|
||||
pub mod seeded_lwe_bootstrap_key;
|
||||
pub mod seeded_lwe_ciphertext;
|
||||
pub mod seeded_lwe_ciphertext_list;
|
||||
@@ -51,6 +57,8 @@ pub use ggsw_ciphertext::*;
|
||||
pub use ggsw_ciphertext_list::*;
|
||||
pub use glwe_ciphertext::*;
|
||||
pub use glwe_ciphertext_list::*;
|
||||
pub use glwe_keyswitch_key::*;
|
||||
pub use glwe_relinearisation_key::*;
|
||||
pub use glwe_secret_key::*;
|
||||
pub use gsw_ciphertext::*;
|
||||
pub use lwe_bootstrap_key::*;
|
||||
@@ -60,8 +68,11 @@ pub use lwe_keyswitch_key::*;
|
||||
pub use lwe_multi_bit_bootstrap_key::*;
|
||||
pub use lwe_private_functional_packing_keyswitch_key::*;
|
||||
pub use lwe_private_functional_packing_keyswitch_key_list::*;
|
||||
pub use lwe_public_functional_packing_keyswitch_key::*;
|
||||
pub use lwe_public_functional_packing_keyswitch_key_list::*;
|
||||
pub use lwe_public_key::*;
|
||||
pub use lwe_secret_key::*;
|
||||
pub use lwe_trace_packing_keyswitch_key::*;
|
||||
pub use plaintext::*;
|
||||
pub use plaintext_list::*;
|
||||
pub use polynomial::*;
|
||||
@@ -70,6 +81,7 @@ pub use seeded_ggsw_ciphertext::*;
|
||||
pub use seeded_ggsw_ciphertext_list::*;
|
||||
pub use seeded_glwe_ciphertext::*;
|
||||
pub use seeded_glwe_ciphertext_list::*;
|
||||
pub use seeded_glwe_keyswitch_key::*;
|
||||
pub use seeded_lwe_bootstrap_key::*;
|
||||
pub use seeded_lwe_ciphertext::*;
|
||||
pub use seeded_lwe_ciphertext_list::*;
|
||||
|
||||
379
tfhe/src/core_crypto/entities/seeded_glwe_keyswitch_key.rs
Normal file
379
tfhe/src/core_crypto/entities/seeded_glwe_keyswitch_key.rs
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
//! Module containing the definition of the SeededGlweKeyswitchKey.
|
||||
|
||||
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 keyswitch key`](`SeededGlweKeyswitchKey`).
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct SeededGlweKeyswitchKey<C: Container> {
|
||||
data: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_glwe_size: GlweSize,
|
||||
poly_size: PolynomialSize,
|
||||
compression_seed: CompressionSeed,
|
||||
}
|
||||
|
||||
impl<T, C: Container<Element = T>> AsRef<[T]> for SeededGlweKeyswitchKey<C> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.data.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, C: ContainerMut<Element = T>> AsMut<[T]> for SeededGlweKeyswitchKey<C> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of elements in an encryption of an input [`GlweSecretKey`] element for a
|
||||
/// [`SeededGlweKeyswitchKey`] given a [`DecompositionLevelCount`] and output [`GlweSize`].
|
||||
pub fn seeded_glwe_keyswitch_key_input_key_element_encrypted_size(
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
) -> usize {
|
||||
// One seeded ciphertext per level
|
||||
decomp_level_count.0
|
||||
}
|
||||
|
||||
impl<Scalar, C: Container<Element = Scalar>> SeededGlweKeyswitchKey<C> {
|
||||
/// Create an [`SeededGlweKeyswitchKey`] from an existing container.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function only wraps a container in the appropriate type. If you want to generate an
|
||||
/// [`SeededGlweKeyswitchKey`] you need to call
|
||||
/// [`crate::core_crypto::algorithms::generate_seeded_glwe_keyswitch_key`] using this key as
|
||||
/// output.
|
||||
///
|
||||
/// This docstring exhibits [`SeededGlweKeyswitchKey`] primitives usage.
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for SeededLweKeyswitchKey creation
|
||||
/// let input_lwe_dimension = LweDimension(600);
|
||||
/// let output_lwe_dimension = LweDimension(1024);
|
||||
/// let decomp_base_log = DecompositionBaseLog(4);
|
||||
/// let decomp_level_count = DecompositionLevelCount(5);
|
||||
///
|
||||
/// // Get a seeder
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
///
|
||||
/// // Create a new SeededLweKeyswitchKey
|
||||
/// let lwe_ksk = SeededLweKeyswitchKey::new(
|
||||
/// 0u64,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// input_lwe_dimension,
|
||||
/// output_lwe_dimension,
|
||||
/// seeder.seed().into(),
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(lwe_ksk.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(lwe_ksk.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(lwe_ksk.input_key_lwe_dimension(), input_lwe_dimension);
|
||||
/// assert_eq!(lwe_ksk.output_key_lwe_dimension(), output_lwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// lwe_ksk.output_lwe_size(),
|
||||
/// output_lwe_dimension.to_lwe_size()
|
||||
/// );
|
||||
///
|
||||
/// let compression_seed = lwe_ksk.compression_seed();
|
||||
///
|
||||
/// // Demonstrate how to recover the allocated container
|
||||
/// let underlying_container: Vec<u64> = lwe_ksk.into_container();
|
||||
///
|
||||
/// // Recreate a secret key using from_container
|
||||
/// let lwe_ksk = SeededLweKeyswitchKey::from_container(
|
||||
/// underlying_container,
|
||||
/// decomp_base_log,
|
||||
/// decomp_level_count,
|
||||
/// output_lwe_dimension.to_lwe_size(),
|
||||
/// compression_seed,
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(lwe_ksk.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(lwe_ksk.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(lwe_ksk.input_key_lwe_dimension(), input_lwe_dimension);
|
||||
/// assert_eq!(lwe_ksk.output_key_lwe_dimension(), output_lwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// lwe_ksk.output_lwe_size(),
|
||||
/// output_lwe_dimension.to_lwe_size()
|
||||
/// );
|
||||
///
|
||||
/// let lwe_ksk = lwe_ksk.decompress_into_lwe_keyswitch_key();
|
||||
///
|
||||
/// assert_eq!(lwe_ksk.decomposition_base_log(), decomp_base_log);
|
||||
/// assert_eq!(lwe_ksk.decomposition_level_count(), decomp_level_count);
|
||||
/// assert_eq!(lwe_ksk.input_key_lwe_dimension(), input_lwe_dimension);
|
||||
/// assert_eq!(lwe_ksk.output_key_lwe_dimension(), output_lwe_dimension);
|
||||
/// assert_eq!(
|
||||
/// lwe_ksk.output_lwe_size(),
|
||||
/// output_lwe_dimension.to_lwe_size()
|
||||
/// );
|
||||
/// ```
|
||||
pub fn from_container(
|
||||
container: C,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_glwe_size: GlweSize,
|
||||
poly_size: PolynomialSize,
|
||||
compression_seed: CompressionSeed,
|
||||
) -> Self {
|
||||
assert!(
|
||||
container.container_len() > 0,
|
||||
"Got an empty container to create an SeededLweKeyswitchKey"
|
||||
);
|
||||
assert!(
|
||||
container.container_len() % (decomp_level_count.0) == 0,
|
||||
"The provided container length is not valid. \
|
||||
It needs to be dividable by decomp_level_count: {}. \
|
||||
Got container length: {} and decomp_level_count: {decomp_level_count:?}.",
|
||||
decomp_level_count.0,
|
||||
container.container_len()
|
||||
);
|
||||
|
||||
SeededGlweKeyswitchKey {
|
||||
data: container,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
poly_size,
|
||||
compression_seed,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionBaseLog`] of the [`SeededGlweKeyswitchKey`].
|
||||
///
|
||||
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn decomposition_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
/// Return the [`DecompositionLevelCount`] of the [`SeededGlweKeyswitchKey`].
|
||||
///
|
||||
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn decomposition_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomp_level_count
|
||||
}
|
||||
|
||||
/// Return the input [`GlweDimension`] of the [`SeededGlweKeyswitchKey`].
|
||||
///
|
||||
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn input_key_glwe_dimension(&self) -> GlweDimension {
|
||||
GlweDimension(self.data.container_len() / (self.seeded_input_key_element_encrypted_size()
|
||||
* self.input_poly_size))
|
||||
}
|
||||
|
||||
/// Return the input [`PolynomialSize`] of the [`SeededGlweKeyswitchKey`].
|
||||
///
|
||||
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.poly_size
|
||||
}
|
||||
|
||||
/// Return the output [`GlweDimension`] of the [`SeededGlweKeyswitchKey`].
|
||||
///
|
||||
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn output_key_glwe_dimension(&self) -> GlweDimension {
|
||||
self.output_glwe_size.to_glwe_dimension()
|
||||
}
|
||||
|
||||
/// Return the output [`GlweSize`] of the [`SeededGlweKeyswitchKey`].
|
||||
///
|
||||
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn output_glwe_size(&self) -> GlweSize {
|
||||
self.output_glwe_size
|
||||
}
|
||||
|
||||
/// Return the output [`CompressionSeed`] of the [`SeededGlweKeyswitchKey`].
|
||||
///
|
||||
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn compression_seed(&self) -> CompressionSeed {
|
||||
self.compression_seed
|
||||
}
|
||||
|
||||
/// Return the number of elements in an encryption of an input [`GlweSecretKey`] element of the
|
||||
/// current [`SeededGlweKeyswitchKey`].
|
||||
pub fn seeded_input_key_element_encrypted_size(&self) -> usize {
|
||||
seeded_glwe_keyswitch_key_input_key_element_encrypted_size(self.decomp_level_count)
|
||||
}
|
||||
|
||||
/// Return a view of the [`SeededGlweKeyswitchKey`]. This is useful if an algorithm takes a view
|
||||
/// by value.
|
||||
pub fn as_view(&self) -> SeededGlweKeyswitchKey<&'_ [Scalar]> {
|
||||
SeededGlweKeyswitchKey::from_container(
|
||||
self.as_ref(),
|
||||
self.decomp_base_log,
|
||||
self.decomp_level_count,
|
||||
self.output_glwe_size,
|
||||
self.poly_size,
|
||||
self.compression_seed,
|
||||
)
|
||||
}
|
||||
|
||||
/// Consume the entity and return its underlying container.
|
||||
///
|
||||
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn into_container(self) -> C {
|
||||
self.data
|
||||
}
|
||||
|
||||
/// Consume the [`SeededGlweKeyswitchKey`] and decompress it into a standard
|
||||
/// [`GlweKeyswitchKey`].
|
||||
///
|
||||
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn decompress_into_glwe_keyswitch_key(self) -> GlweKeyswitchKeyOwned<Scalar>
|
||||
where
|
||||
Scalar: UnsignedTorus,
|
||||
{
|
||||
let mut decompressed_ksk = GlweKeyswitchKeyOwned::new(
|
||||
Scalar::ZERO,
|
||||
self.decomposition_base_log(),
|
||||
self.decomposition_level_count(),
|
||||
self.input_key_glwe_dimension(),
|
||||
self.output_key_glwe_dimension(),
|
||||
self.polynomial_size(),
|
||||
);
|
||||
decompress_seeded_glwe_keyswitch_key::<_, _, _, ActivatedRandomGenerator>(
|
||||
&mut decompressed_ksk,
|
||||
&self,
|
||||
);
|
||||
decompressed_ksk
|
||||
}
|
||||
|
||||
pub fn as_seeded_glwe_ciphertext_list(&self) -> SeededGlweCiphertextListView<'_, Scalar> {
|
||||
SeededGlweCiphertextListView::from_container(
|
||||
self.as_ref(),
|
||||
self.output_glwe_size(),
|
||||
self.polynomial_size(),
|
||||
self.compression_seed(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar, C: ContainerMut<Element = Scalar>> SeededGlweKeyswitchKey<C> {
|
||||
/// Mutable variant of [`SeededGlweKeyswitchKey::as_view`].
|
||||
pub fn as_mut_view(&mut self) -> SeededGlweKeyswitchKey<&'_ mut [Scalar]> {
|
||||
let decomp_base_log = self.decomp_base_log;
|
||||
let decomp_level_count = self.decomp_level_count;
|
||||
let output_glwe_size = self.output_glwe_size;
|
||||
let poly_size = self.poly_size;
|
||||
let compression_seed = self.compression_seed;
|
||||
SeededGlweKeyswitchKey::from_container(
|
||||
self.as_mut(),
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
poly_size,
|
||||
compression_seed,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn as_mut_seeded_glwe_ciphertext_list(
|
||||
&mut self,
|
||||
) -> SeededGlweCiphertextListMutView<'_, Scalar> {
|
||||
let output_glwe_size = self.output_glwe_size();
|
||||
let poly_size = self.polynomial_size();
|
||||
let compression_seed = self.compression_seed();
|
||||
SeededGlweCiphertextListMutView::from_container(
|
||||
self.as_mut(),
|
||||
output_glwe_size,
|
||||
poly_size,
|
||||
compression_seed,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`SeededGlweKeyswitchKey`] owning the memory for its own storage.
|
||||
pub type SeededGlweKeyswitchKeyOwned<Scalar> = SeededGlweKeyswitchKey<Vec<Scalar>>;
|
||||
|
||||
impl<Scalar: Copy> SeededGlweKeyswitchKeyOwned<Scalar> {
|
||||
/// Allocate memory and create a new owned [`SeededGlweKeyswitchKey`].
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function allocates a vector of the appropriate size and wraps it in the appropriate
|
||||
/// type. If you want to generate an [`SeededGlweKeyswitchKey`] you need to call
|
||||
/// [`crate::core_crypto::algorithms::generate_seeded_glwe_keyswitch_key`] using this key as
|
||||
/// output.
|
||||
///
|
||||
/// See [`SeededGlweKeyswitchKey::from_container`] for usage.
|
||||
pub fn new(
|
||||
fill_with: Scalar,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
input_key_glwe_dimension: GlweDimension,
|
||||
output_key_glwe_dimension: GlweDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
compression_seed: CompressionSeed,
|
||||
) -> SeededGlweKeyswitchKeyOwned<Scalar> {
|
||||
SeededGlweKeyswitchKeyOwned::from_container(
|
||||
vec![
|
||||
fill_with;
|
||||
input_key_glwe_dimension.0 * polynomial_size.0
|
||||
* seeded_lwe_keyswitch_key_input_key_element_encrypted_size(decomp_level_count,)
|
||||
],
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_key_glwe_dimension.to_glwe_size(),
|
||||
polynomial_size,
|
||||
compression_seed,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Container> ContiguousEntityContainer for SeededGlweKeyswitchKey<C> {
|
||||
type Element = C::Element;
|
||||
|
||||
type EntityViewMetadata = SeededGlweCiphertextListCreationMetadata;
|
||||
|
||||
type EntityView<'this> = SeededGlweCiphertextListView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
type SelfViewMetadata = ();
|
||||
|
||||
// At the moment it does not make sense to return "sub" keyswitch keys. So we use a dummy
|
||||
// placeholder type here.
|
||||
type SelfView<'this> = DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
fn get_entity_view_creation_metadata(&self) -> SeededGlweCiphertextListCreationMetadata {
|
||||
SeededGlweCiphertextListCreationMetadata(self.output_glwe_size(), self.compression_seed())
|
||||
}
|
||||
|
||||
fn get_entity_view_pod_size(&self) -> usize {
|
||||
self.seeded_input_key_element_encrypted_size()
|
||||
}
|
||||
|
||||
/// Unimplemented for [`SeededGlweKeyswitchKey`]. At the moment it does not make sense to
|
||||
/// return "sub" keyswitch keys.
|
||||
fn get_self_view_creation_metadata(&self) {
|
||||
unimplemented!(
|
||||
"This function is not supported for SeededLweKeyswitchKey. \
|
||||
At the moment it does not make sense to return 'sub' keyswitch keys."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: ContainerMut> ContiguousEntityContainerMut for SeededGlweKeyswitchKey<C> {
|
||||
type EntityMutView<'this> = SeededGlweCiphertextListMutView<'this, Self::Element>
|
||||
where
|
||||
Self: 'this;
|
||||
|
||||
// At the moment it does not make sense to return "sub" keyswitch keys. So we use a dummy
|
||||
// placeholder type here.
|
||||
type SelfMutView<'this> = DummyCreateFrom
|
||||
where
|
||||
Self: 'this;
|
||||
}
|
||||
*/
|
||||
Reference in New Issue
Block a user