mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-09 14:47:56 -05:00
test(shortint): pbs 128 + compression test with new noise measurement
This commit is contained in:
@@ -55,10 +55,10 @@ impl<Scalar: UnsignedInteger + CastFrom<u64>> ShortintEncoding<Scalar> {
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger + CastFrom<u64>> ShortintEncoding<Scalar> {
|
||||
/// Return the cleatext space including the space for the padding bit if it is set to
|
||||
/// Return the cleatext space including the space for the [`Self::padding_bit`] if it is set to
|
||||
/// [`PaddingBit::Yes`].
|
||||
pub(crate) fn full_cleartext_space(&self) -> Scalar {
|
||||
let cleartext_modulus: Scalar = (self.message_modulus.0 * self.carry_modulus.0).cast_into();
|
||||
let cleartext_modulus = self.cleartext_space_without_padding();
|
||||
|
||||
cleartext_modulus
|
||||
* if self.padding_bit == PaddingBit::No {
|
||||
@@ -68,6 +68,12 @@ impl<Scalar: UnsignedInteger + CastFrom<u64>> ShortintEncoding<Scalar> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the cleatext space defined by the [`Self::message_modulus`] and
|
||||
/// [`Self::carry_modulus`], not taking the value of the [`Self::padding_bit`] into account.
|
||||
pub(crate) fn cleartext_space_without_padding(&self) -> Scalar {
|
||||
(self.message_modulus.0 * self.carry_modulus.0).cast_into()
|
||||
}
|
||||
|
||||
pub(crate) fn encode(&self, value: Cleartext<Scalar>) -> Plaintext<Scalar> {
|
||||
let delta = compute_delta(
|
||||
self.ciphertext_modulus,
|
||||
|
||||
@@ -131,4 +131,12 @@ impl NoiseSquashingCompressionPrivateKey {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn post_packing_ks_key(&self) -> &GlweSecretKeyOwned<u128> {
|
||||
&self.post_packing_ks_key
|
||||
}
|
||||
|
||||
pub fn params(&self) -> NoiseSquashingCompressionParameters {
|
||||
self.params
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,6 +248,14 @@ pub struct NoiseSquashingCompressionKey {
|
||||
}
|
||||
|
||||
impl NoiseSquashingCompressionKey {
|
||||
pub fn new(
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
noise_squashing_compression_private_key: &NoiseSquashingCompressionPrivateKey,
|
||||
) -> Self {
|
||||
noise_squashing_private_key
|
||||
.new_noise_squashing_compression_key(noise_squashing_compression_private_key)
|
||||
}
|
||||
|
||||
/// Construct from raw parts
|
||||
///
|
||||
/// # Panics
|
||||
@@ -278,6 +286,14 @@ impl NoiseSquashingCompressionKey {
|
||||
|
||||
(packing_key_switching_key, lwe_per_glwe)
|
||||
}
|
||||
|
||||
pub fn packing_key_switching_key(&self) -> &LwePackingKeyswitchKey<Vec<u128>> {
|
||||
&self.packing_key_switching_key
|
||||
}
|
||||
|
||||
pub fn lwe_per_glwe(&self) -> LweCiphertextCount {
|
||||
self.lwe_per_glwe
|
||||
}
|
||||
}
|
||||
|
||||
impl NoiseSquashingPrivateKey {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::core_crypto::algorithms::glwe_secret_key_generation::allocate_and_generate_new_binary_glwe_secret_key;
|
||||
use crate::core_crypto::algorithms::lwe_encryption::decrypt_lwe_ciphertext;
|
||||
use crate::core_crypto::entities::glwe_secret_key::GlweSecretKeyOwned;
|
||||
use crate::core_crypto::entities::lwe_secret_key::LweSecretKeyView;
|
||||
use crate::shortint::backward_compatibility::noise_squashing::NoiseSquashingPrivateKeyVersions;
|
||||
use crate::shortint::ciphertext::SquashedNoiseCiphertext;
|
||||
use crate::shortint::encoding::ShortintEncoding;
|
||||
@@ -71,6 +72,10 @@ impl NoiseSquashingPrivateKey {
|
||||
pub fn into_raw_parts(self) -> (GlweSecretKeyOwned<u128>, NoiseSquashingParameters) {
|
||||
(self.post_noise_squashing_secret_key, self.params)
|
||||
}
|
||||
|
||||
pub fn post_noise_squashing_lwe_secret_key(&self) -> LweSecretKeyView<'_, u128> {
|
||||
self.post_noise_squashing_secret_key.as_lwe_secret_key()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct NoiseSquashingPrivateKeyView<'a> {
|
||||
|
||||
@@ -54,21 +54,21 @@ pub enum Shortint128BootstrappingKey {
|
||||
}
|
||||
|
||||
impl Shortint128BootstrappingKey {
|
||||
fn output_lwe_dimension(&self) -> LweDimension {
|
||||
pub fn output_lwe_dimension(&self) -> LweDimension {
|
||||
match self {
|
||||
Self::Classic { bsk, .. } => bsk.output_lwe_dimension(),
|
||||
Self::MultiBit { bsk, .. } => bsk.output_lwe_dimension(),
|
||||
}
|
||||
}
|
||||
|
||||
fn glwe_size(&self) -> GlweSize {
|
||||
pub fn glwe_size(&self) -> GlweSize {
|
||||
match self {
|
||||
Self::Classic { bsk, .. } => bsk.glwe_size(),
|
||||
Self::MultiBit { bsk, .. } => bsk.glwe_size(),
|
||||
}
|
||||
}
|
||||
|
||||
fn polynomial_size(&self) -> PolynomialSize {
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
match self {
|
||||
Self::Classic { bsk, .. } => bsk.polynomial_size(),
|
||||
Self::MultiBit { bsk, .. } => bsk.polynomial_size(),
|
||||
@@ -427,6 +427,10 @@ impl NoiseSquashingKey {
|
||||
res
|
||||
}
|
||||
|
||||
pub fn bootstrapping_key(&self) -> &Shortint128BootstrappingKey {
|
||||
&self.bootstrapping_key
|
||||
}
|
||||
|
||||
pub fn message_modulus(&self) -> MessageModulus {
|
||||
self.message_modulus
|
||||
}
|
||||
|
||||
@@ -0,0 +1,804 @@
|
||||
use super::should_use_single_key_debug;
|
||||
use super::utils::noise_simulation::*;
|
||||
use super::utils::traits::*;
|
||||
use super::utils::{mean_and_variance_check, DecryptionAndNoiseResult, NoiseSample};
|
||||
use crate::core_crypto::algorithms::lwe_programmable_bootstrapping::generate_programmable_bootstrap_glwe_lut;
|
||||
use crate::core_crypto::commons::dispersion::Variance;
|
||||
use crate::core_crypto::commons::parameters::CiphertextModulusLog;
|
||||
use crate::shortint::atomic_pattern::AtomicPatternServerKey;
|
||||
use crate::shortint::client_key::atomic_pattern::AtomicPatternClientKey;
|
||||
use crate::shortint::client_key::ClientKey;
|
||||
use crate::shortint::encoding::{PaddingBit, ShortintEncoding};
|
||||
use crate::shortint::engine::ShortintEngine;
|
||||
use crate::shortint::list_compression::{
|
||||
NoiseSquashingCompressionKey, NoiseSquashingCompressionPrivateKey,
|
||||
};
|
||||
use crate::shortint::noise_squashing::{
|
||||
NoiseSquashingKey, NoiseSquashingPrivateKey, Shortint128BootstrappingKey,
|
||||
};
|
||||
use crate::shortint::parameters::noise_squashing::NoiseSquashingParameters;
|
||||
use crate::shortint::parameters::test_params::{
|
||||
TEST_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
||||
TEST_PARAM_NOISE_SQUASHING_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
||||
TEST_PARAM_NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
||||
};
|
||||
use crate::shortint::parameters::{AtomicPatternParameters, NoiseSquashingCompressionParameters};
|
||||
use crate::shortint::server_key::{ModulusSwitchConfiguration, ServerKey};
|
||||
use rayon::prelude::*;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn dp_ks_standard_pbs128<
|
||||
InputCt,
|
||||
ScalarMulResult,
|
||||
KsResult,
|
||||
DriftTechniqueResult,
|
||||
MsResult,
|
||||
PbsResult,
|
||||
DPScalar,
|
||||
KsKey,
|
||||
DriftKey,
|
||||
Bsk,
|
||||
Accumulator,
|
||||
Resources,
|
||||
>(
|
||||
input: InputCt,
|
||||
scalar: DPScalar,
|
||||
ksk: &KsKey,
|
||||
mod_switch_noise_reduction_key_128: &DriftKey,
|
||||
bsk_128: &Bsk,
|
||||
br_input_modulus_log: CiphertextModulusLog,
|
||||
accumulator: &Accumulator,
|
||||
side_resources: &mut Resources,
|
||||
) -> (
|
||||
InputCt,
|
||||
ScalarMulResult,
|
||||
KsResult,
|
||||
DriftTechniqueResult,
|
||||
MsResult,
|
||||
PbsResult,
|
||||
)
|
||||
where
|
||||
// InputCt needs to be multipliable by the given scalar
|
||||
InputCt: ScalarMul<DPScalar, Output = ScalarMulResult, SideResources = Resources>,
|
||||
// We need to be able to allocate the result and keyswitch the result of the ScalarMul
|
||||
KsKey: AllocateKeyswtichResult<Output = KsResult, SideResources = Resources>
|
||||
+ Keyswitch<ScalarMulResult, KsResult, SideResources = Resources>,
|
||||
// We need to be able to allocate the result and apply drift technique + mod switch it
|
||||
DriftKey: AllocateDriftTechniqueStandardModSwitchResult<
|
||||
AfterDriftOutput = DriftTechniqueResult,
|
||||
AfterMsOutput = MsResult,
|
||||
SideResources = Resources,
|
||||
> + DrifTechniqueStandardModSwitch<
|
||||
KsResult,
|
||||
DriftTechniqueResult,
|
||||
MsResult,
|
||||
SideResources = Resources,
|
||||
>,
|
||||
// The accumulator has the information about the output size and modulus, therefore it is the
|
||||
// one to allocate the blind rotation result
|
||||
Accumulator: AllocateBlindRotationResult<Output = PbsResult, SideResources = Resources>,
|
||||
// We need to be able to apply the PBS
|
||||
Bsk: StandardFft128Bootstrap<MsResult, PbsResult, Accumulator, SideResources = Resources>,
|
||||
{
|
||||
let after_dp = input.scalar_mul(scalar, side_resources);
|
||||
let mut ks_result = ksk.allocate_keyswitch_result(side_resources);
|
||||
ksk.keyswitch(&after_dp, &mut ks_result, side_resources);
|
||||
let (mut drift_technique_result, mut ms_result) = mod_switch_noise_reduction_key_128
|
||||
.allocate_drift_technique_standard_mod_switch_result(side_resources);
|
||||
mod_switch_noise_reduction_key_128.drift_technique_and_standard_mod_switch(
|
||||
br_input_modulus_log,
|
||||
&ks_result,
|
||||
&mut drift_technique_result,
|
||||
&mut ms_result,
|
||||
side_resources,
|
||||
);
|
||||
|
||||
let mut pbs_result = accumulator.allocated_blind_rotation_result(side_resources);
|
||||
bsk_128.standard_fft_128_pbs(&ms_result, &mut pbs_result, accumulator, side_resources);
|
||||
(
|
||||
input,
|
||||
after_dp,
|
||||
ks_result,
|
||||
drift_technique_result,
|
||||
ms_result,
|
||||
pbs_result,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn dp_ks_standard_pbs128_packing_ks<
|
||||
InputCt,
|
||||
ScalarMulResult,
|
||||
KsResult,
|
||||
DriftTechniqueResult,
|
||||
MsResult,
|
||||
PbsResult,
|
||||
PackingResult,
|
||||
DPScalar,
|
||||
KsKey,
|
||||
DriftKey,
|
||||
Bsk,
|
||||
PackingKey,
|
||||
Accumulator,
|
||||
Resources,
|
||||
>(
|
||||
input: Vec<InputCt>,
|
||||
scalar: DPScalar,
|
||||
ksk: &KsKey,
|
||||
mod_switch_noise_reduction_key_128: &DriftKey,
|
||||
bsk_128: &Bsk,
|
||||
br_input_modulus_log: CiphertextModulusLog,
|
||||
accumulator: &Accumulator,
|
||||
packing_ksk: &PackingKey,
|
||||
side_resources: &mut [Resources],
|
||||
) -> (
|
||||
Vec<(
|
||||
InputCt,
|
||||
ScalarMulResult,
|
||||
KsResult,
|
||||
DriftTechniqueResult,
|
||||
MsResult,
|
||||
PbsResult,
|
||||
)>,
|
||||
PackingResult,
|
||||
)
|
||||
where
|
||||
// InputCt needs to be multipliable by the given scalar
|
||||
InputCt: ScalarMul<DPScalar, Output = ScalarMulResult, SideResources = Resources> + Send,
|
||||
// We need to be able to allocate the result and keyswitch the result of the ScalarMul
|
||||
KsKey: AllocateKeyswtichResult<Output = KsResult, SideResources = Resources>
|
||||
+ Keyswitch<ScalarMulResult, KsResult, SideResources = Resources>
|
||||
+ Sync,
|
||||
// We need to be able to allocate the result and apply drift technique + mod switch it
|
||||
DriftKey: AllocateDriftTechniqueStandardModSwitchResult<
|
||||
AfterDriftOutput = DriftTechniqueResult,
|
||||
AfterMsOutput = MsResult,
|
||||
SideResources = Resources,
|
||||
> + DrifTechniqueStandardModSwitch<
|
||||
KsResult,
|
||||
DriftTechniqueResult,
|
||||
MsResult,
|
||||
SideResources = Resources,
|
||||
> + Sync,
|
||||
// The accumulator has the information about the output size and modulus, therefore it is the
|
||||
// one to allocate the blind rotation result
|
||||
Accumulator: AllocateBlindRotationResult<Output = PbsResult, SideResources = Resources> + Sync,
|
||||
// We need to be able to apply the PBS
|
||||
Bsk:
|
||||
StandardFft128Bootstrap<MsResult, PbsResult, Accumulator, SideResources = Resources> + Sync,
|
||||
PackingKey: AllocatePackingKeyswitchResult<Output = PackingResult, SideResources = Resources>
|
||||
+ for<'a> LwePackingKeyswitch<[&'a PbsResult], PackingResult, SideResources = Resources>,
|
||||
Resources: Send,
|
||||
ScalarMulResult: Send,
|
||||
KsResult: Send,
|
||||
DriftTechniqueResult: Send,
|
||||
MsResult: Send,
|
||||
PbsResult: Send,
|
||||
DPScalar: Copy + Sync + Send,
|
||||
{
|
||||
let res: Vec<_> = input
|
||||
.into_par_iter()
|
||||
.zip(side_resources.par_iter_mut())
|
||||
.map(|(input, side_resources)| {
|
||||
let (input, after_dp, ks_result, drift_technique_result, ms_result, pbs_result) =
|
||||
dp_ks_standard_pbs128(
|
||||
input,
|
||||
scalar,
|
||||
ksk,
|
||||
mod_switch_noise_reduction_key_128,
|
||||
bsk_128,
|
||||
br_input_modulus_log,
|
||||
accumulator,
|
||||
side_resources,
|
||||
);
|
||||
|
||||
(
|
||||
input,
|
||||
after_dp,
|
||||
ks_result,
|
||||
drift_technique_result,
|
||||
ms_result,
|
||||
pbs_result,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let pbs_results: Vec<_> = res
|
||||
.iter()
|
||||
.map(
|
||||
|(_input, _after_dp, _ks_result, _drift_technique_result, _ms_result, pbs_result)| {
|
||||
pbs_result
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
|
||||
let mut packing_result = packing_ksk.allocate_packing_keyswitch_result(&mut side_resources[0]);
|
||||
packing_ksk.keyswitch_lwes_and_pack_in_glwe(
|
||||
pbs_results.as_slice(),
|
||||
&mut packing_result,
|
||||
&mut side_resources[0],
|
||||
);
|
||||
|
||||
(res, packing_result)
|
||||
}
|
||||
|
||||
/// Test function to verify that the noise checking tools match the actual atomic patterns
|
||||
/// implemented in shortint
|
||||
fn sanity_check_encrypt_dp_ks_standard_pbs128_packing_ks<P>(
|
||||
params: P,
|
||||
noise_squashing_params: NoiseSquashingParameters,
|
||||
noise_squashing_compression_params: NoiseSquashingCompressionParameters,
|
||||
) where
|
||||
P: Into<AtomicPatternParameters>,
|
||||
{
|
||||
let params: AtomicPatternParameters = params.into();
|
||||
let cks = ClientKey::new(params);
|
||||
let sks = ServerKey::new(&cks);
|
||||
let noise_squashing_private_key = NoiseSquashingPrivateKey::new(noise_squashing_params);
|
||||
let noise_squashing_key = NoiseSquashingKey::new(&cks, &noise_squashing_private_key);
|
||||
let noise_squashing_compression_private_key =
|
||||
NoiseSquashingCompressionPrivateKey::new(noise_squashing_compression_params);
|
||||
let noise_squashing_compression_key = NoiseSquashingCompressionKey::new(
|
||||
&noise_squashing_private_key,
|
||||
&noise_squashing_compression_private_key,
|
||||
);
|
||||
|
||||
let lwe_per_glwe = noise_squashing_compression_key.lwe_per_glwe();
|
||||
|
||||
let u128_encoding = ShortintEncoding {
|
||||
ciphertext_modulus: noise_squashing_params.ciphertext_modulus(),
|
||||
message_modulus: noise_squashing_params.message_modulus(),
|
||||
carry_modulus: noise_squashing_params.carry_modulus(),
|
||||
padding_bit: PaddingBit::Yes,
|
||||
};
|
||||
|
||||
let max_scalar_mul = sks.max_noise_level.get();
|
||||
|
||||
match &sks.atomic_pattern {
|
||||
AtomicPatternServerKey::Standard(standard_atomic_pattern_server_key) => {
|
||||
let ksk = &standard_atomic_pattern_server_key.key_switching_key;
|
||||
let bsk = noise_squashing_key.bootstrapping_key();
|
||||
let (fbsk, drift_key) = match bsk {
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
} => (bsk, modulus_switch_noise_reduction_key),
|
||||
Shortint128BootstrappingKey::MultiBit { .. } => todo!(),
|
||||
};
|
||||
let drift_key = {
|
||||
match drift_key {
|
||||
ModulusSwitchConfiguration::Standard => None,
|
||||
ModulusSwitchConfiguration::DriftTechniqueNoiseReduction(
|
||||
modulus_switch_noise_reduction_key,
|
||||
) => Some(modulus_switch_noise_reduction_key),
|
||||
ModulusSwitchConfiguration::CenteredMeanNoiseReduction => None,
|
||||
}
|
||||
.unwrap()
|
||||
};
|
||||
let pksk = noise_squashing_compression_key.packing_key_switching_key();
|
||||
|
||||
let id_lut = generate_programmable_bootstrap_glwe_lut(
|
||||
fbsk.polynomial_size(),
|
||||
fbsk.glwe_size(),
|
||||
u128_encoding
|
||||
.cleartext_space_without_padding()
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
u128_encoding.ciphertext_modulus,
|
||||
u128_encoding.delta(),
|
||||
|x| x,
|
||||
);
|
||||
|
||||
let br_input_modulus_log = fbsk.polynomial_size().to_blind_rotation_input_modulus_log();
|
||||
|
||||
let input_zeros: Vec<_> = (0..lwe_per_glwe.0).map(|_| cks.encrypt(0)).collect();
|
||||
let mut side_resources = vec![(); input_zeros.len()];
|
||||
let input_zero_as_lwe: Vec<_> = input_zeros.iter().map(|ct| ct.ct.clone()).collect();
|
||||
|
||||
let (_before_packing, mut after_packing) = dp_ks_standard_pbs128_packing_ks(
|
||||
input_zero_as_lwe,
|
||||
max_scalar_mul,
|
||||
ksk,
|
||||
drift_key,
|
||||
fbsk,
|
||||
br_input_modulus_log,
|
||||
&id_lut,
|
||||
pksk,
|
||||
&mut side_resources,
|
||||
);
|
||||
|
||||
let noise_squashed: Vec<_> = input_zeros
|
||||
.into_par_iter()
|
||||
.map(|mut ct| {
|
||||
sks.unchecked_scalar_mul_assign(&mut ct, max_scalar_mul.try_into().unwrap());
|
||||
noise_squashing_key.squash_ciphertext_noise(&ct, &sks)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let compressed = noise_squashing_compression_key
|
||||
.compress_noise_squashed_ciphertexts_into_list(&noise_squashed);
|
||||
|
||||
let underlying_glwes = compressed.glwe_ciphertext_list;
|
||||
|
||||
assert_eq!(underlying_glwes.len(), 1);
|
||||
|
||||
let extracted = underlying_glwes[0].extract();
|
||||
|
||||
// Bodies that were not filled are discarded
|
||||
after_packing.get_mut_body().as_mut()[lwe_per_glwe.0..].fill(0);
|
||||
|
||||
assert_eq!(after_packing.as_view(), extracted.as_view());
|
||||
}
|
||||
AtomicPatternServerKey::KeySwitch32(_ks32_atomic_pattern_server_key) => {
|
||||
todo!();
|
||||
}
|
||||
AtomicPatternServerKey::Dynamic(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sanity_check_encrypt_dp_ks_standard_pbs128_packing_ks_test_param_message_2_carry_2_ks_pbs_tuniform_2m128(
|
||||
) {
|
||||
sanity_check_encrypt_dp_ks_standard_pbs128_packing_ks(
|
||||
TEST_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
||||
TEST_PARAM_NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
||||
TEST_PARAM_NOISE_SQUASHING_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn encrypt_dp_ks_standard_pbs128_packing_ks_inner_helper(
|
||||
params: AtomicPatternParameters,
|
||||
noise_squashing_params: NoiseSquashingParameters,
|
||||
noise_squashing_compression_params: NoiseSquashingCompressionParameters,
|
||||
single_cks: &ClientKey,
|
||||
single_sks: &ServerKey,
|
||||
single_noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
single_noise_squashing_key: &NoiseSquashingKey,
|
||||
single_noise_squashing_compression_private_key: &NoiseSquashingCompressionPrivateKey,
|
||||
single_noise_squashing_compression_key: &NoiseSquashingCompressionKey,
|
||||
msg: u64,
|
||||
scalar_for_multiplication: u64,
|
||||
) -> (
|
||||
Vec<(
|
||||
DecryptionAndNoiseResult,
|
||||
DecryptionAndNoiseResult,
|
||||
DecryptionAndNoiseResult,
|
||||
DecryptionAndNoiseResult,
|
||||
DecryptionAndNoiseResult,
|
||||
DecryptionAndNoiseResult,
|
||||
)>,
|
||||
Vec<DecryptionAndNoiseResult>,
|
||||
) {
|
||||
let mut engine = ShortintEngine::new();
|
||||
let thread_cks;
|
||||
let thread_sks;
|
||||
let thread_private_noise_squashing_key;
|
||||
let thread_noise_squashing_key;
|
||||
let thread_private_noise_squashing_compression_key;
|
||||
let thread_noise_squashing_compression_key;
|
||||
let (
|
||||
cks,
|
||||
sks,
|
||||
noise_squashing_private_key,
|
||||
noise_squashing_key,
|
||||
noise_squashing_compression_private_key,
|
||||
noise_squashing_compression_key,
|
||||
) = if should_use_single_key_debug() {
|
||||
(
|
||||
single_cks,
|
||||
single_sks,
|
||||
single_noise_squashing_private_key,
|
||||
single_noise_squashing_key,
|
||||
single_noise_squashing_compression_private_key,
|
||||
single_noise_squashing_compression_key,
|
||||
)
|
||||
} else {
|
||||
thread_cks = engine.new_client_key(params);
|
||||
thread_sks = engine.new_server_key(&thread_cks);
|
||||
thread_private_noise_squashing_key = NoiseSquashingPrivateKey::new(noise_squashing_params);
|
||||
thread_noise_squashing_key =
|
||||
NoiseSquashingKey::new(&thread_cks, &thread_private_noise_squashing_key);
|
||||
thread_private_noise_squashing_compression_key =
|
||||
NoiseSquashingCompressionPrivateKey::new(noise_squashing_compression_params);
|
||||
thread_noise_squashing_compression_key = NoiseSquashingCompressionKey::new(
|
||||
&thread_private_noise_squashing_key,
|
||||
&thread_private_noise_squashing_compression_key,
|
||||
);
|
||||
|
||||
(
|
||||
&thread_cks,
|
||||
&thread_sks,
|
||||
&thread_private_noise_squashing_key,
|
||||
&thread_noise_squashing_key,
|
||||
&thread_private_noise_squashing_compression_key,
|
||||
&thread_noise_squashing_compression_key,
|
||||
)
|
||||
};
|
||||
|
||||
let ksk = match &sks.atomic_pattern {
|
||||
AtomicPatternServerKey::Standard(standard_atomic_pattern_server_key) => {
|
||||
&standard_atomic_pattern_server_key.key_switching_key
|
||||
}
|
||||
AtomicPatternServerKey::KeySwitch32(_) => {
|
||||
todo!()
|
||||
}
|
||||
AtomicPatternServerKey::Dynamic(_) => unimplemented!(),
|
||||
};
|
||||
|
||||
let (bsk_128, drift_key) = {
|
||||
let (bsk, drift_key) = match noise_squashing_key.bootstrapping_key() {
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
} => (bsk, modulus_switch_noise_reduction_key),
|
||||
Shortint128BootstrappingKey::MultiBit { .. } => todo!(),
|
||||
};
|
||||
|
||||
let drift_key = match drift_key {
|
||||
ModulusSwitchConfiguration::Standard => None,
|
||||
ModulusSwitchConfiguration::DriftTechniqueNoiseReduction(
|
||||
modulus_switch_noise_reduction_key,
|
||||
) => Some(modulus_switch_noise_reduction_key),
|
||||
ModulusSwitchConfiguration::CenteredMeanNoiseReduction => None,
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
(bsk, drift_key)
|
||||
};
|
||||
|
||||
let bsk_polynomial_size = bsk_128.polynomial_size();
|
||||
let bsk_glwe_size = bsk_128.glwe_size();
|
||||
let br_input_modulus_log = bsk_128
|
||||
.polynomial_size()
|
||||
.to_blind_rotation_input_modulus_log();
|
||||
|
||||
let compression_pksk = noise_squashing_compression_key.packing_key_switching_key();
|
||||
|
||||
let u128_encoding = ShortintEncoding {
|
||||
ciphertext_modulus: noise_squashing_params.ciphertext_modulus(),
|
||||
message_modulus: noise_squashing_params.message_modulus(),
|
||||
carry_modulus: noise_squashing_params.carry_modulus(),
|
||||
padding_bit: PaddingBit::Yes,
|
||||
};
|
||||
|
||||
let id_lut = generate_programmable_bootstrap_glwe_lut(
|
||||
bsk_polynomial_size,
|
||||
bsk_glwe_size,
|
||||
u128_encoding
|
||||
.cleartext_space_without_padding()
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
u128_encoding.ciphertext_modulus,
|
||||
u128_encoding.delta(),
|
||||
|x| x,
|
||||
);
|
||||
|
||||
let lwe_per_glwe = noise_squashing_compression_key.lwe_per_glwe();
|
||||
|
||||
let inputs: Vec<_> = (0..lwe_per_glwe.0).map(|_| cks.encrypt(msg).ct).collect();
|
||||
let mut side_resources = vec![(); inputs.len()];
|
||||
|
||||
let (before_packing, after_packing) = dp_ks_standard_pbs128_packing_ks(
|
||||
inputs,
|
||||
scalar_for_multiplication,
|
||||
ksk,
|
||||
drift_key,
|
||||
bsk_128,
|
||||
br_input_modulus_log,
|
||||
&id_lut,
|
||||
compression_pksk,
|
||||
side_resources.as_mut_slice(),
|
||||
);
|
||||
|
||||
let u64_encoding = ShortintEncoding::from_parameters(params, PaddingBit::Yes);
|
||||
|
||||
let before_packing: Vec<_> = before_packing
|
||||
.into_iter()
|
||||
.map(
|
||||
|(input, after_dp, after_ks, after_drift, after_ms, after_pbs128)| match &cks
|
||||
.atomic_pattern
|
||||
{
|
||||
AtomicPatternClientKey::Standard(standard_atomic_pattern_client_key) => (
|
||||
DecryptionAndNoiseResult::new(
|
||||
&input,
|
||||
&standard_atomic_pattern_client_key.large_lwe_secret_key(),
|
||||
msg,
|
||||
&u64_encoding,
|
||||
),
|
||||
DecryptionAndNoiseResult::new(
|
||||
&after_dp,
|
||||
&standard_atomic_pattern_client_key.large_lwe_secret_key(),
|
||||
msg,
|
||||
&u64_encoding,
|
||||
),
|
||||
DecryptionAndNoiseResult::new(
|
||||
&after_ks,
|
||||
&standard_atomic_pattern_client_key.small_lwe_secret_key(),
|
||||
msg,
|
||||
&u64_encoding,
|
||||
),
|
||||
DecryptionAndNoiseResult::new(
|
||||
&after_drift,
|
||||
&standard_atomic_pattern_client_key.small_lwe_secret_key(),
|
||||
msg,
|
||||
&u64_encoding,
|
||||
),
|
||||
DecryptionAndNoiseResult::new(
|
||||
&after_ms,
|
||||
&standard_atomic_pattern_client_key.small_lwe_secret_key(),
|
||||
msg,
|
||||
&u64_encoding,
|
||||
),
|
||||
DecryptionAndNoiseResult::new(
|
||||
&after_pbs128,
|
||||
&noise_squashing_private_key.post_noise_squashing_lwe_secret_key(),
|
||||
msg.into(),
|
||||
&u128_encoding,
|
||||
),
|
||||
),
|
||||
AtomicPatternClientKey::KeySwitch32(_) => todo!(),
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
|
||||
let after_packing = DecryptionAndNoiseResult::new_from_glwe(
|
||||
&after_packing,
|
||||
noise_squashing_compression_private_key.post_packing_ks_key(),
|
||||
lwe_per_glwe,
|
||||
msg.into(),
|
||||
&u128_encoding,
|
||||
);
|
||||
|
||||
assert_eq!(after_packing.len(), lwe_per_glwe.0);
|
||||
|
||||
(before_packing, after_packing)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn encrypt_dp_ks_standard_pbs128_packing_ks_noise_helper(
|
||||
params: AtomicPatternParameters,
|
||||
noise_squashing_params: NoiseSquashingParameters,
|
||||
noise_squashing_compression_params: NoiseSquashingCompressionParameters,
|
||||
single_cks: &ClientKey,
|
||||
single_sks: &ServerKey,
|
||||
single_noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
single_noise_squashing_key: &NoiseSquashingKey,
|
||||
single_noise_squashing_compression_private_key: &NoiseSquashingCompressionPrivateKey,
|
||||
single_noise_squashing_compression_key: &NoiseSquashingCompressionKey,
|
||||
msg: u64,
|
||||
scalar_for_multiplication: u64,
|
||||
) -> (
|
||||
Vec<(
|
||||
NoiseSample,
|
||||
NoiseSample,
|
||||
NoiseSample,
|
||||
NoiseSample,
|
||||
NoiseSample,
|
||||
NoiseSample,
|
||||
)>,
|
||||
Vec<NoiseSample>,
|
||||
) {
|
||||
let (before_compression, after_compression) =
|
||||
encrypt_dp_ks_standard_pbs128_packing_ks_inner_helper(
|
||||
params,
|
||||
noise_squashing_params,
|
||||
noise_squashing_compression_params,
|
||||
single_cks,
|
||||
single_sks,
|
||||
single_noise_squashing_private_key,
|
||||
single_noise_squashing_key,
|
||||
single_noise_squashing_compression_private_key,
|
||||
single_noise_squashing_compression_key,
|
||||
msg,
|
||||
scalar_for_multiplication,
|
||||
);
|
||||
|
||||
(
|
||||
before_compression
|
||||
.into_iter()
|
||||
.map(
|
||||
|(input, after_dp, after_ks, after_drift, after_ms, after_pbs)| {
|
||||
(
|
||||
input
|
||||
.get_noise_if_decryption_was_correct()
|
||||
.expect("Decryption Failed"),
|
||||
after_dp
|
||||
.get_noise_if_decryption_was_correct()
|
||||
.expect("Decryption Failed"),
|
||||
after_ks
|
||||
.get_noise_if_decryption_was_correct()
|
||||
.expect("Decryption Failed"),
|
||||
after_drift
|
||||
.get_noise_if_decryption_was_correct()
|
||||
.expect("Decryption Failed"),
|
||||
after_ms
|
||||
.get_noise_if_decryption_was_correct()
|
||||
.expect("Decryption Failed"),
|
||||
after_pbs
|
||||
.get_noise_if_decryption_was_correct()
|
||||
.expect("Decryption Failed"),
|
||||
)
|
||||
},
|
||||
)
|
||||
.collect(),
|
||||
after_compression
|
||||
.into_iter()
|
||||
.map(|after_compression| {
|
||||
after_compression
|
||||
.get_noise_if_decryption_was_correct()
|
||||
.expect("Decryption Failed")
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn noise_check_encrypt_dp_ks_standard_pbs128_packing_ks_noise<P>(
|
||||
params: P,
|
||||
noise_squashing_params: NoiseSquashingParameters,
|
||||
noise_squashing_compression_params: NoiseSquashingCompressionParameters,
|
||||
) where
|
||||
P: Into<AtomicPatternParameters>,
|
||||
{
|
||||
let params: AtomicPatternParameters = params.into();
|
||||
let cks = ClientKey::new(params);
|
||||
let sks = ServerKey::new(&cks);
|
||||
let noise_squashing_private_key = NoiseSquashingPrivateKey::new(noise_squashing_params);
|
||||
let noise_squashing_key = NoiseSquashingKey::new(&cks, &noise_squashing_private_key);
|
||||
let noise_squashing_compression_private_key =
|
||||
NoiseSquashingCompressionPrivateKey::new(noise_squashing_compression_params);
|
||||
let noise_squashing_compression_key = NoiseSquashingCompressionKey::new(
|
||||
&noise_squashing_private_key,
|
||||
&noise_squashing_compression_private_key,
|
||||
);
|
||||
|
||||
let noise_simulation_ksk = NoiseSimulationLweKsk::new_from_atomic_pattern_parameters(params);
|
||||
let noise_simulation_drift_key =
|
||||
NoiseSimulationDriftTechniqueKey::new_from_atomic_pattern_parameters(params);
|
||||
let noise_simulation_bsk128 =
|
||||
NoiseSimulationLweFourier128Bsk::new_from_parameters(params, noise_squashing_params);
|
||||
let noise_simulation_packing_key = NoiseSimulationLwePackingKeyswitchKey::new_from_params(
|
||||
noise_squashing_params,
|
||||
noise_squashing_compression_params,
|
||||
);
|
||||
|
||||
let fbsk_128 = match noise_squashing_key.bootstrapping_key() {
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key: _,
|
||||
} => bsk,
|
||||
Shortint128BootstrappingKey::MultiBit { .. } => todo!(),
|
||||
};
|
||||
|
||||
assert!(noise_simulation_bsk128.matches_actual_bsk(fbsk_128));
|
||||
assert!(noise_simulation_packing_key
|
||||
.matches_actual_pksk(noise_squashing_compression_key.packing_key_switching_key()));
|
||||
|
||||
let br_input_modulus_log = noise_squashing_key
|
||||
.bootstrapping_key()
|
||||
.polynomial_size()
|
||||
.to_blind_rotation_input_modulus_log();
|
||||
|
||||
match &sks.atomic_pattern {
|
||||
AtomicPatternServerKey::Standard(standard_atomic_pattern_server_key) => {
|
||||
assert!(noise_simulation_ksk
|
||||
.matches_actual_ksk(&standard_atomic_pattern_server_key.key_switching_key));
|
||||
}
|
||||
AtomicPatternServerKey::KeySwitch32(ks32_atomic_pattern_server_key) => {
|
||||
assert!(noise_simulation_ksk
|
||||
.matches_actual_ksk(&ks32_atomic_pattern_server_key.key_switching_key));
|
||||
}
|
||||
AtomicPatternServerKey::Dynamic(_) => unimplemented!(),
|
||||
}
|
||||
|
||||
let max_scalar_mul = sks.max_noise_level.get();
|
||||
|
||||
let noise_simulation_accumulator = NoiseSimulationGlwe::new(
|
||||
noise_simulation_bsk128
|
||||
.output_glwe_size()
|
||||
.to_glwe_dimension(),
|
||||
noise_simulation_bsk128.output_polynomial_size(),
|
||||
Variance(0.0),
|
||||
noise_simulation_bsk128.modulus(),
|
||||
);
|
||||
|
||||
let (_before_packing_sim, after_packing_sim) = {
|
||||
let noise_simulation = NoiseSimulationLwe::encrypt(&cks, 0);
|
||||
dp_ks_standard_pbs128_packing_ks(
|
||||
vec![noise_simulation; noise_squashing_compression_key.lwe_per_glwe().0],
|
||||
max_scalar_mul,
|
||||
&noise_simulation_ksk,
|
||||
&noise_simulation_drift_key,
|
||||
&noise_simulation_bsk128,
|
||||
br_input_modulus_log,
|
||||
&noise_simulation_accumulator,
|
||||
&noise_simulation_packing_key,
|
||||
&mut vec![(); noise_squashing_compression_key.lwe_per_glwe().0],
|
||||
)
|
||||
};
|
||||
|
||||
let after_packing_sim = after_packing_sim.into_lwe();
|
||||
|
||||
// Check that the circuit is correct with respect to core implementation, i.e. does not crash on
|
||||
// dimension checks
|
||||
let (expected_lwe_dimension_out, expected_modulus_f64_out) = {
|
||||
let pksk = noise_squashing_compression_key.packing_key_switching_key();
|
||||
|
||||
let out_glwe_dim = pksk.output_key_glwe_dimension();
|
||||
let out_poly_size = pksk.output_key_polynomial_size();
|
||||
|
||||
(
|
||||
out_glwe_dim.to_equivalent_lwe_dimension(out_poly_size),
|
||||
pksk.ciphertext_modulus().raw_modulus_float(),
|
||||
)
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
after_packing_sim.lwe_dimension(),
|
||||
expected_lwe_dimension_out
|
||||
);
|
||||
assert_eq!(
|
||||
after_packing_sim.modulus().as_f64(),
|
||||
expected_modulus_f64_out
|
||||
);
|
||||
|
||||
let cleartext_modulus = params.message_modulus().0 * params.carry_modulus().0;
|
||||
let mut noise_samples_after_packing = vec![];
|
||||
|
||||
let sample_count_per_msg = 1000usize.div_ceil(noise_squashing_compression_key.lwe_per_glwe().0);
|
||||
|
||||
for _ in 0..cleartext_modulus {
|
||||
let current_noise_samples_after_packing: Vec<_> = (0..sample_count_per_msg)
|
||||
.into_par_iter()
|
||||
.map(|_| {
|
||||
let (_before_packing, after_packing) =
|
||||
encrypt_dp_ks_standard_pbs128_packing_ks_noise_helper(
|
||||
params,
|
||||
noise_squashing_params,
|
||||
noise_squashing_compression_params,
|
||||
&cks,
|
||||
&sks,
|
||||
&noise_squashing_private_key,
|
||||
&noise_squashing_key,
|
||||
&noise_squashing_compression_private_key,
|
||||
&noise_squashing_compression_key,
|
||||
0,
|
||||
max_scalar_mul,
|
||||
);
|
||||
after_packing
|
||||
})
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
noise_samples_after_packing.extend(
|
||||
current_noise_samples_after_packing
|
||||
.into_iter()
|
||||
.map(|x| x.value),
|
||||
);
|
||||
}
|
||||
|
||||
let after_packing_is_ok = mean_and_variance_check(
|
||||
&noise_samples_after_packing,
|
||||
"after_packing",
|
||||
0.0,
|
||||
after_packing_sim.variance(),
|
||||
noise_squashing_compression_params.packing_ks_key_noise_distribution,
|
||||
after_packing_sim.lwe_dimension(),
|
||||
after_packing_sim.modulus().as_f64(),
|
||||
);
|
||||
|
||||
assert!(after_packing_is_ok);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_noise_check_encrypt_dp_ks_standard_pbs128_packing_ks_noise_param_message_2_carry_2_ks_pbs_tuniform_2m128(
|
||||
) {
|
||||
noise_check_encrypt_dp_ks_standard_pbs128_packing_ks_noise(
|
||||
TEST_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
||||
TEST_PARAM_NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
||||
TEST_PARAM_NOISE_SQUASHING_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub(crate) mod encrypt_dp_ks_modswitch;
|
||||
pub(crate) mod encrypt_dp_ks_pbs128_packingks;
|
||||
pub(crate) mod utils;
|
||||
|
||||
pub fn should_use_single_key_debug() -> bool {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pub mod noise_simulation;
|
||||
pub mod traits;
|
||||
|
||||
use crate::core_crypto::algorithms::glwe_encryption::decrypt_glwe_ciphertext;
|
||||
use crate::core_crypto::algorithms::lwe_encryption::decrypt_lwe_ciphertext;
|
||||
use crate::core_crypto::algorithms::lwe_keyswitch::{
|
||||
keyswitch_lwe_ciphertext, keyswitch_lwe_ciphertext_with_scalar_change,
|
||||
@@ -8,6 +9,8 @@ use crate::core_crypto::algorithms::lwe_keyswitch::{
|
||||
use crate::core_crypto::algorithms::lwe_linear_algebra::{
|
||||
lwe_ciphertext_cleartext_mul, lwe_ciphertext_cleartext_mul_assign,
|
||||
};
|
||||
use crate::core_crypto::algorithms::lwe_packing_keyswitch::par_keyswitch_lwe_ciphertext_list_and_pack_in_glwe_ciphertext;
|
||||
use crate::core_crypto::algorithms::lwe_programmable_bootstrapping::fft128_pbs::programmable_bootstrap_f128_lwe_ciphertext;
|
||||
use crate::core_crypto::algorithms::lwe_programmable_bootstrapping::fft64_pbs::programmable_bootstrap_lwe_ciphertext;
|
||||
use crate::core_crypto::algorithms::misc::torus_modular_diff;
|
||||
use crate::core_crypto::algorithms::test::round_decode;
|
||||
@@ -19,7 +22,7 @@ use crate::core_crypto::commons::noise_formulas::secure_noise::{
|
||||
};
|
||||
use crate::core_crypto::commons::numeric::{CastFrom, CastInto, UnsignedInteger};
|
||||
use crate::core_crypto::commons::parameters::{
|
||||
CiphertextModulusLog, DynamicDistribution, LweDimension,
|
||||
CiphertextModulusLog, DynamicDistribution, LweCiphertextCount, LweDimension, PlaintextCount,
|
||||
};
|
||||
use crate::core_crypto::commons::test_tools::{
|
||||
arithmetic_mean, equivalent_pfail_gaussian_noise, gaussian_mean_confidence_interval,
|
||||
@@ -27,12 +30,16 @@ use crate::core_crypto::commons::test_tools::{
|
||||
pfail_clopper_pearson_exact_confidence_interval, variance, NormalityTestResult,
|
||||
};
|
||||
use crate::core_crypto::commons::traits::container::{Container, ContainerMut};
|
||||
use crate::core_crypto::entities::glwe_ciphertext::GlweCiphertext;
|
||||
use crate::core_crypto::entities::glwe_ciphertext::{GlweCiphertext, GlweCiphertextOwned};
|
||||
use crate::core_crypto::entities::glwe_secret_key::GlweSecretKey;
|
||||
use crate::core_crypto::entities::lwe_ciphertext::{LweCiphertext, LweCiphertextOwned};
|
||||
use crate::core_crypto::entities::lwe_ciphertext_list::LweCiphertextList;
|
||||
use crate::core_crypto::entities::lwe_keyswitch_key::LweKeyswitchKey;
|
||||
use crate::core_crypto::entities::lwe_packing_keyswitch_key::LwePackingKeyswitchKey;
|
||||
use crate::core_crypto::entities::lwe_secret_key::LweSecretKey;
|
||||
use crate::core_crypto::entities::Cleartext;
|
||||
use crate::core_crypto::entities::{Cleartext, PlaintextList};
|
||||
use crate::core_crypto::fft_impl::common::modulus_switch;
|
||||
use crate::core_crypto::fft_impl::fft128::crypto::bootstrap::Fourier128LweBootstrapKey;
|
||||
use crate::core_crypto::fft_impl::fft64::c64;
|
||||
use crate::core_crypto::fft_impl::fft64::crypto::bootstrap::FourierLweBootstrapKey;
|
||||
use crate::core_crypto::fft_impl::fft64::math::fft::id;
|
||||
@@ -414,6 +421,53 @@ impl DecryptionAndNoiseResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_glwe<Scalar: UnsignedInteger + CastFrom<u64>, CtCont, KeyCont>(
|
||||
ct: &GlweCiphertext<CtCont>,
|
||||
secret_key: &GlweSecretKey<KeyCont>,
|
||||
lwe_per_glwe: LweCiphertextCount,
|
||||
expected_msg: Scalar,
|
||||
encoding: &ShortintEncoding<Scalar>,
|
||||
) -> Vec<Self>
|
||||
where
|
||||
CtCont: Container<Element = Scalar>,
|
||||
KeyCont: Container<Element = Scalar>,
|
||||
{
|
||||
let mut decrypted =
|
||||
PlaintextList::new(Scalar::ZERO, PlaintextCount(ct.polynomial_size().0));
|
||||
|
||||
let delta = encoding.delta();
|
||||
let cleartext_modulus_with_padding = encoding.full_cleartext_space();
|
||||
|
||||
decrypt_glwe_ciphertext(secret_key, ct, &mut decrypted);
|
||||
|
||||
let expected_plaintext = expected_msg * delta;
|
||||
|
||||
decrypted
|
||||
.as_ref()
|
||||
.iter()
|
||||
.take(lwe_per_glwe.0)
|
||||
.map(|&decrypted_plaintext| {
|
||||
// We apply the modulus on the cleartext + the padding bit
|
||||
let decoded_msg =
|
||||
round_decode(decrypted_plaintext, delta) % cleartext_modulus_with_padding;
|
||||
|
||||
let noise = torus_modular_diff(
|
||||
expected_plaintext,
|
||||
decrypted_plaintext,
|
||||
ct.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
if decoded_msg == expected_msg {
|
||||
Self::DecryptionSucceeded {
|
||||
noise: NoiseSample { value: noise },
|
||||
}
|
||||
} else {
|
||||
Self::DecryptionFailed
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_noise_if_decryption_was_correct(&self) -> Option<NoiseSample> {
|
||||
match self {
|
||||
Self::DecryptionSucceeded { noise } => Some(*noise),
|
||||
@@ -726,3 +780,74 @@ impl<
|
||||
programmable_bootstrap_lwe_ciphertext(input, output, accumulator, self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
InputScalar: UnsignedTorus + CastInto<usize>,
|
||||
OutputScalar: UnsignedTorus,
|
||||
KeyCont: Container<Element = f64>,
|
||||
InputCont: Container<Element = InputScalar>,
|
||||
OutputCont: ContainerMut<Element = OutputScalar>,
|
||||
AccCont: Container<Element = OutputScalar>,
|
||||
>
|
||||
StandardFft128Bootstrap<
|
||||
LweCiphertext<InputCont>,
|
||||
LweCiphertext<OutputCont>,
|
||||
GlweCiphertext<AccCont>,
|
||||
> for Fourier128LweBootstrapKey<KeyCont>
|
||||
{
|
||||
type SideResources = ();
|
||||
|
||||
fn standard_fft_128_pbs(
|
||||
&self,
|
||||
input: &LweCiphertext<InputCont>,
|
||||
output: &mut LweCiphertext<OutputCont>,
|
||||
accumulator: &GlweCiphertext<AccCont>,
|
||||
_side_resources: &mut Self::SideResources,
|
||||
) {
|
||||
programmable_bootstrap_f128_lwe_ciphertext(input, output, accumulator, self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger, KeyCont: Container<Element = Scalar>> AllocatePackingKeyswitchResult
|
||||
for LwePackingKeyswitchKey<KeyCont>
|
||||
{
|
||||
type Output = GlweCiphertextOwned<Scalar>;
|
||||
type SideResources = ();
|
||||
|
||||
fn allocate_packing_keyswitch_result(
|
||||
&self,
|
||||
_side_resources: &mut Self::SideResources,
|
||||
) -> Self::Output {
|
||||
Self::Output::new(
|
||||
Scalar::ZERO,
|
||||
self.output_glwe_size(),
|
||||
self.output_polynomial_size(),
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Scalar: UnsignedInteger,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
KeyCont: Container<Element = Scalar> + Sync,
|
||||
> LwePackingKeyswitch<[&LweCiphertext<InputCont>], GlweCiphertext<OutputCont>>
|
||||
for LwePackingKeyswitchKey<KeyCont>
|
||||
{
|
||||
type SideResources = ();
|
||||
|
||||
fn keyswitch_lwes_and_pack_in_glwe(
|
||||
&self,
|
||||
input: &[&LweCiphertext<InputCont>],
|
||||
output: &mut GlweCiphertext<OutputCont>,
|
||||
_side_resources: &mut Self::SideResources,
|
||||
) {
|
||||
let input = LweCiphertextList::new_from_lwe_ciphertext_iterator(
|
||||
input.iter().map(|lwe| lwe.as_view()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
par_keyswitch_lwe_ciphertext_list_and_pack_in_glwe_ciphertext(self, &input, output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,28 @@ use crate::core_crypto::commons::noise_formulas::lwe_keyswitch::{
|
||||
keyswitch_additive_variance_132_bits_security_gaussian,
|
||||
keyswitch_additive_variance_132_bits_security_tuniform,
|
||||
};
|
||||
use crate::core_crypto::commons::noise_formulas::lwe_packing_keyswitch::{
|
||||
packing_keyswitch_additive_variance_132_bits_security_gaussian,
|
||||
packing_keyswitch_additive_variance_132_bits_security_tuniform,
|
||||
};
|
||||
use crate::core_crypto::commons::noise_formulas::lwe_programmable_bootstrap_128::{
|
||||
pbs_128_variance_132_bits_security_gaussian_fft_mul,
|
||||
pbs_128_variance_132_bits_security_tuniform_fft_mul,
|
||||
};
|
||||
use crate::core_crypto::commons::noise_formulas::modulus_switch::modulus_switch_additive_variance;
|
||||
use crate::core_crypto::commons::numeric::{CastInto, UnsignedInteger};
|
||||
use crate::core_crypto::commons::parameters::{
|
||||
CiphertextModulusLog, DecompositionBaseLog, DecompositionLevelCount, DynamicDistribution,
|
||||
LweDimension,
|
||||
GlweDimension, GlweSize, LweDimension, PolynomialSize,
|
||||
};
|
||||
use crate::core_crypto::commons::traits::container::Container;
|
||||
use crate::core_crypto::entities::lwe_keyswitch_key::LweKeyswitchKey;
|
||||
use crate::core_crypto::entities::lwe_packing_keyswitch_key::LwePackingKeyswitchKey;
|
||||
use crate::core_crypto::fft_impl::fft128::crypto::bootstrap::Fourier128LweBootstrapKey;
|
||||
use crate::shortint::client_key::ClientKey;
|
||||
use crate::shortint::parameters::noise_squashing::{
|
||||
NoiseSquashingCompressionParameters, NoiseSquashingParameters,
|
||||
};
|
||||
use crate::shortint::server_key::modulus_switch_noise_reduction::ModulusSwitchNoiseReductionKey;
|
||||
use crate::shortint::AtomicPatternParameters;
|
||||
|
||||
@@ -67,7 +80,7 @@ impl NoiseSimulationModulus {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct NoiseSimulationLwe {
|
||||
lwe_dimension: LweDimension,
|
||||
variance: Variance,
|
||||
@@ -75,14 +88,6 @@ pub struct NoiseSimulationLwe {
|
||||
}
|
||||
|
||||
impl NoiseSimulationLwe {
|
||||
pub fn new_zero() -> Self {
|
||||
Self {
|
||||
lwe_dimension: LweDimension(0),
|
||||
variance: Variance(-2.0f64.powi(128)),
|
||||
modulus: NoiseSimulationModulus::Other(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lwe_dimension(&self) -> LweDimension {
|
||||
self.lwe_dimension
|
||||
}
|
||||
@@ -207,7 +212,11 @@ impl AllocateKeyswtichResult for NoiseSimulationLweKsk {
|
||||
type SideResources = ();
|
||||
|
||||
fn allocate_keyswitch_result(&self, _side_resources: &mut Self::SideResources) -> Self::Output {
|
||||
Self::Output::new_zero()
|
||||
Self::Output {
|
||||
lwe_dimension: self.output_lwe_dimension,
|
||||
variance: Variance(-2.0f64.powi(128)),
|
||||
modulus: self.output_modulus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,3 +417,385 @@ impl DrifTechniqueStandardModSwitch<NoiseSimulationLwe, NoiseSimulationLwe, Nois
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct NoiseSimulationGlwe {
|
||||
glwe_dimension: GlweDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
variance_per_occupied_slot: Variance,
|
||||
modulus: NoiseSimulationModulus,
|
||||
}
|
||||
|
||||
impl NoiseSimulationGlwe {
|
||||
pub fn new(
|
||||
glwe_dimension: GlweDimension,
|
||||
polynomial_size: PolynomialSize,
|
||||
variance_per_occupied_slot: Variance,
|
||||
modulus: NoiseSimulationModulus,
|
||||
) -> Self {
|
||||
Self {
|
||||
glwe_dimension,
|
||||
polynomial_size,
|
||||
variance_per_occupied_slot,
|
||||
modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_lwe(self) -> NoiseSimulationLwe {
|
||||
let lwe_dimension = self
|
||||
.glwe_dimension()
|
||||
.to_equivalent_lwe_dimension(self.polynomial_size());
|
||||
NoiseSimulationLwe {
|
||||
lwe_dimension,
|
||||
variance: self.variance_per_occupied_slot(),
|
||||
modulus: self.modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub fn variance_per_occupied_slot(&self) -> Variance {
|
||||
self.variance_per_occupied_slot
|
||||
}
|
||||
|
||||
pub fn modulus(&self) -> NoiseSimulationModulus {
|
||||
self.modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl AllocateBlindRotationResult for NoiseSimulationGlwe {
|
||||
type Output = NoiseSimulationLwe;
|
||||
type SideResources = ();
|
||||
|
||||
fn allocated_blind_rotation_result(
|
||||
&self,
|
||||
_side_resources: &mut Self::SideResources,
|
||||
) -> Self::Output {
|
||||
let lwe_dimension = self
|
||||
.glwe_dimension()
|
||||
.to_equivalent_lwe_dimension(self.polynomial_size());
|
||||
|
||||
Self::Output {
|
||||
lwe_dimension,
|
||||
variance: self.variance_per_occupied_slot(),
|
||||
modulus: self.modulus(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct NoiseSimulationLweFourier128Bsk {
|
||||
input_lwe_dimension: LweDimension,
|
||||
output_glwe_size: GlweSize,
|
||||
output_polynomial_size: PolynomialSize,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
noise_distribution: DynamicDistribution<u128>,
|
||||
modulus: NoiseSimulationModulus,
|
||||
}
|
||||
|
||||
impl NoiseSimulationLweFourier128Bsk {
|
||||
// We can't really build a key from an already generated key as we need to know what the noise
|
||||
// distribution is.
|
||||
pub fn new_from_parameters(
|
||||
params: AtomicPatternParameters,
|
||||
noise_squashing_params: NoiseSquashingParameters,
|
||||
) -> Self {
|
||||
Self {
|
||||
input_lwe_dimension: params.lwe_dimension(),
|
||||
output_glwe_size: noise_squashing_params.glwe_dimension().to_glwe_size(),
|
||||
output_polynomial_size: noise_squashing_params.polynomial_size(),
|
||||
decomp_base_log: noise_squashing_params.decomp_base_log(),
|
||||
decomp_level_count: noise_squashing_params.decomp_level_count(),
|
||||
noise_distribution: noise_squashing_params.glwe_noise_distribution(),
|
||||
modulus: NoiseSimulationModulus::from_ciphertext_modulus(
|
||||
noise_squashing_params.ciphertext_modulus(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn matches_actual_bsk<C: Container<Element = f64>>(
|
||||
&self,
|
||||
lwe_bsk: &Fourier128LweBootstrapKey<C>,
|
||||
) -> bool {
|
||||
let Self {
|
||||
input_lwe_dimension,
|
||||
output_glwe_size: glwe_size,
|
||||
output_polynomial_size: polynomial_size,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
noise_distribution: _,
|
||||
modulus: _,
|
||||
} = *self;
|
||||
|
||||
let bsk_input_lwe_dimension = lwe_bsk.input_lwe_dimension();
|
||||
let bsk_glwe_size = lwe_bsk.glwe_size();
|
||||
let bsk_polynomial_size = lwe_bsk.polynomial_size();
|
||||
let bsk_decomp_base_log = lwe_bsk.decomposition_base_log();
|
||||
let bsk_decomp_level_count = lwe_bsk.decomposition_level_count();
|
||||
|
||||
input_lwe_dimension == bsk_input_lwe_dimension
|
||||
&& glwe_size == bsk_glwe_size
|
||||
&& polynomial_size == bsk_polynomial_size
|
||||
&& decomp_base_log == bsk_decomp_base_log
|
||||
&& decomp_level_count == bsk_decomp_level_count
|
||||
}
|
||||
|
||||
pub fn input_lwe_dimension(&self) -> LweDimension {
|
||||
self.input_lwe_dimension
|
||||
}
|
||||
|
||||
pub fn output_glwe_size(&self) -> GlweSize {
|
||||
self.output_glwe_size
|
||||
}
|
||||
|
||||
pub fn output_polynomial_size(&self) -> PolynomialSize {
|
||||
self.output_polynomial_size
|
||||
}
|
||||
|
||||
pub fn decomp_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
pub fn decomp_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomp_level_count
|
||||
}
|
||||
|
||||
pub fn noise_distribution(&self) -> DynamicDistribution<u128> {
|
||||
self.noise_distribution
|
||||
}
|
||||
|
||||
pub fn modulus(&self) -> NoiseSimulationModulus {
|
||||
self.modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl StandardFft128Bootstrap<NoiseSimulationLwe, NoiseSimulationLwe, NoiseSimulationGlwe>
|
||||
for NoiseSimulationLweFourier128Bsk
|
||||
{
|
||||
type SideResources = ();
|
||||
|
||||
fn standard_fft_128_pbs(
|
||||
&self,
|
||||
input: &NoiseSimulationLwe,
|
||||
output: &mut NoiseSimulationLwe,
|
||||
accumulator: &NoiseSimulationGlwe,
|
||||
_side_resources: &mut Self::SideResources,
|
||||
) {
|
||||
assert_eq!(self.input_lwe_dimension(), input.lwe_dimension());
|
||||
assert_eq!(
|
||||
self.output_glwe_size(),
|
||||
accumulator.glwe_dimension().to_glwe_size()
|
||||
);
|
||||
assert_eq!(self.output_polynomial_size(), accumulator.polynomial_size());
|
||||
assert_eq!(self.modulus(), accumulator.modulus());
|
||||
|
||||
let br_additive_variance = match self.noise_distribution() {
|
||||
DynamicDistribution::Gaussian(_) => {
|
||||
pbs_128_variance_132_bits_security_gaussian_fft_mul(
|
||||
self.input_lwe_dimension(),
|
||||
self.output_glwe_size().to_glwe_dimension(),
|
||||
self.output_polynomial_size(),
|
||||
self.decomp_base_log(),
|
||||
self.decomp_level_count(),
|
||||
// Current PBS 128 implem has 104 bits of equivalent mantissa
|
||||
104.0f64,
|
||||
self.modulus().as_f64(),
|
||||
)
|
||||
}
|
||||
DynamicDistribution::TUniform(_) => {
|
||||
pbs_128_variance_132_bits_security_tuniform_fft_mul(
|
||||
self.input_lwe_dimension(),
|
||||
self.output_glwe_size().to_glwe_dimension(),
|
||||
self.output_polynomial_size(),
|
||||
self.decomp_base_log(),
|
||||
self.decomp_level_count(),
|
||||
// Current PBS 128 implem has 104 bits of equivalent mantissa
|
||||
104.0f64,
|
||||
self.modulus().as_f64(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let output_lwe_dimension = self
|
||||
.output_glwe_size()
|
||||
.to_glwe_dimension()
|
||||
.to_equivalent_lwe_dimension(self.output_polynomial_size());
|
||||
|
||||
output.lwe_dimension = output_lwe_dimension;
|
||||
output.variance =
|
||||
Variance(accumulator.variance_per_occupied_slot().0 + br_additive_variance.0);
|
||||
output.modulus = accumulator.modulus;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct NoiseSimulationLwePackingKeyswitchKey {
|
||||
input_lwe_dimension: LweDimension,
|
||||
decomp_base_log: DecompositionBaseLog,
|
||||
decomp_level_count: DecompositionLevelCount,
|
||||
output_glwe_size: GlweSize,
|
||||
output_polynomial_size: PolynomialSize,
|
||||
noise_distribution: DynamicDistribution<u128>,
|
||||
modulus: NoiseSimulationModulus,
|
||||
}
|
||||
|
||||
impl NoiseSimulationLwePackingKeyswitchKey {
|
||||
pub fn new_from_params(
|
||||
noise_squashing_params: NoiseSquashingParameters,
|
||||
noise_squashing_compression_params: NoiseSquashingCompressionParameters,
|
||||
) -> Self {
|
||||
let squashing_lwe_dim = noise_squashing_params
|
||||
.glwe_dimension()
|
||||
.to_equivalent_lwe_dimension(noise_squashing_params.polynomial_size());
|
||||
|
||||
Self {
|
||||
input_lwe_dimension: squashing_lwe_dim,
|
||||
decomp_base_log: noise_squashing_compression_params.packing_ks_base_log,
|
||||
decomp_level_count: noise_squashing_compression_params.packing_ks_level,
|
||||
output_glwe_size: noise_squashing_compression_params
|
||||
.packing_ks_glwe_dimension
|
||||
.to_glwe_size(),
|
||||
output_polynomial_size: noise_squashing_compression_params.packing_ks_polynomial_size,
|
||||
noise_distribution: noise_squashing_compression_params
|
||||
.packing_ks_key_noise_distribution,
|
||||
modulus: NoiseSimulationModulus::from_ciphertext_modulus(
|
||||
noise_squashing_compression_params.ciphertext_modulus,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn matches_actual_pksk<Scalar: UnsignedInteger, KeyCont: Container<Element = Scalar>>(
|
||||
&self,
|
||||
pksk: &LwePackingKeyswitchKey<KeyCont>,
|
||||
) -> bool {
|
||||
let Self {
|
||||
input_lwe_dimension,
|
||||
decomp_base_log,
|
||||
decomp_level_count,
|
||||
output_glwe_size,
|
||||
output_polynomial_size,
|
||||
noise_distribution: _,
|
||||
modulus,
|
||||
} = *self;
|
||||
|
||||
let pksk_input_lwe_dimension = pksk.input_key_lwe_dimension();
|
||||
let pksk_decomp_base_log = pksk.decomposition_base_log();
|
||||
let pksk_decomp_level_count = pksk.decomposition_level_count();
|
||||
let pksk_output_glwe_size = pksk.output_glwe_size();
|
||||
let pksk_output_polynomial_size = pksk.output_key_polynomial_size();
|
||||
let pksk_modulus =
|
||||
NoiseSimulationModulus::from_ciphertext_modulus(pksk.ciphertext_modulus());
|
||||
|
||||
input_lwe_dimension == pksk_input_lwe_dimension
|
||||
&& decomp_base_log == pksk_decomp_base_log
|
||||
&& decomp_level_count == pksk_decomp_level_count
|
||||
&& output_glwe_size == pksk_output_glwe_size
|
||||
&& output_polynomial_size == pksk_output_polynomial_size
|
||||
&& modulus == pksk_modulus
|
||||
}
|
||||
|
||||
pub fn input_lwe_dimension(&self) -> LweDimension {
|
||||
self.input_lwe_dimension
|
||||
}
|
||||
|
||||
pub fn decomp_base_log(&self) -> DecompositionBaseLog {
|
||||
self.decomp_base_log
|
||||
}
|
||||
|
||||
pub fn decomp_level_count(&self) -> DecompositionLevelCount {
|
||||
self.decomp_level_count
|
||||
}
|
||||
|
||||
pub fn output_glwe_size(&self) -> GlweSize {
|
||||
self.output_glwe_size
|
||||
}
|
||||
|
||||
pub fn output_polynomial_size(&self) -> PolynomialSize {
|
||||
self.output_polynomial_size
|
||||
}
|
||||
|
||||
pub fn noise_distribution(&self) -> DynamicDistribution<u128> {
|
||||
self.noise_distribution
|
||||
}
|
||||
|
||||
pub fn modulus(&self) -> NoiseSimulationModulus {
|
||||
self.modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl AllocatePackingKeyswitchResult for NoiseSimulationLwePackingKeyswitchKey {
|
||||
type Output = NoiseSimulationGlwe;
|
||||
type SideResources = ();
|
||||
|
||||
fn allocate_packing_keyswitch_result(
|
||||
&self,
|
||||
_side_resources: &mut Self::SideResources,
|
||||
) -> Self::Output {
|
||||
Self::Output {
|
||||
glwe_dimension: self.output_glwe_size().to_glwe_dimension(),
|
||||
polynomial_size: self.output_polynomial_size(),
|
||||
variance_per_occupied_slot: Variance(-2.0f64.powi(128)),
|
||||
modulus: self.modulus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LwePackingKeyswitch<[&NoiseSimulationLwe], NoiseSimulationGlwe>
|
||||
for NoiseSimulationLwePackingKeyswitchKey
|
||||
{
|
||||
type SideResources = ();
|
||||
|
||||
fn keyswitch_lwes_and_pack_in_glwe(
|
||||
&self,
|
||||
input: &[&NoiseSimulationLwe],
|
||||
output: &mut NoiseSimulationGlwe,
|
||||
_side_resources: &mut Self::SideResources,
|
||||
) {
|
||||
let mut input_iter = input.iter();
|
||||
let input = input_iter.next().unwrap();
|
||||
|
||||
let mut lwe_to_pack = 1;
|
||||
|
||||
assert!(input_iter.inspect(|_| lwe_to_pack += 1).all(|x| x == input));
|
||||
|
||||
assert_eq!(input.lwe_dimension(), self.input_lwe_dimension());
|
||||
|
||||
let packing_ks_additive_var = match self.noise_distribution() {
|
||||
DynamicDistribution::Gaussian(_) => {
|
||||
packing_keyswitch_additive_variance_132_bits_security_gaussian(
|
||||
self.input_lwe_dimension(),
|
||||
self.output_glwe_size().to_glwe_dimension(),
|
||||
self.output_polynomial_size(),
|
||||
self.decomp_base_log(),
|
||||
self.decomp_level_count(),
|
||||
lwe_to_pack.into(),
|
||||
self.modulus().as_f64(),
|
||||
)
|
||||
}
|
||||
DynamicDistribution::TUniform(_) => {
|
||||
packing_keyswitch_additive_variance_132_bits_security_tuniform(
|
||||
self.input_lwe_dimension(),
|
||||
self.output_glwe_size().to_glwe_dimension(),
|
||||
self.output_polynomial_size(),
|
||||
self.decomp_base_log(),
|
||||
self.decomp_level_count(),
|
||||
lwe_to_pack.into(),
|
||||
self.modulus().as_f64(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
output.glwe_dimension = self.output_glwe_size().to_glwe_dimension();
|
||||
output.polynomial_size = self.output_polynomial_size();
|
||||
output.variance_per_occupied_slot =
|
||||
Variance(input.variance().0 + packing_ks_additive_var.0);
|
||||
output.modulus = self.modulus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,3 +97,36 @@ pub trait StandardFftBootstrap<Input, Output, Accumulator> {
|
||||
side_resources: &mut Self::SideResources,
|
||||
);
|
||||
}
|
||||
|
||||
pub trait StandardFft128Bootstrap<Input, Output, Accumulator> {
|
||||
type SideResources;
|
||||
|
||||
fn standard_fft_128_pbs(
|
||||
&self,
|
||||
input: &Input,
|
||||
output: &mut Output,
|
||||
accumulator: &Accumulator,
|
||||
side_resources: &mut Self::SideResources,
|
||||
);
|
||||
}
|
||||
|
||||
pub trait AllocatePackingKeyswitchResult {
|
||||
type Output;
|
||||
type SideResources;
|
||||
|
||||
fn allocate_packing_keyswitch_result(
|
||||
&self,
|
||||
side_resources: &mut Self::SideResources,
|
||||
) -> Self::Output;
|
||||
}
|
||||
|
||||
pub trait LwePackingKeyswitch<Input: ?Sized, Output> {
|
||||
type SideResources;
|
||||
|
||||
fn keyswitch_lwes_and_pack_in_glwe(
|
||||
&self,
|
||||
input: &Input,
|
||||
output: &mut Output,
|
||||
side_resources: &mut Self::SideResources,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user