mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-09 14:47:56 -05:00
feat(shortint): add noise squashing for ks32
This commit is contained in:
committed by
Nicolas Sarlin
parent
ef5b984448
commit
45a849ad36
@@ -15,6 +15,9 @@ use crate::named::Named;
|
||||
|
||||
use crate::shortint::atomic_pattern::{AtomicPatternServerKey, StandardAtomicPatternServerKey};
|
||||
|
||||
use crate::shortint::noise_squashing::atomic_pattern::ks32::KS32AtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::atomic_pattern::standard::StandardAtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::atomic_pattern::AtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::server_key::{
|
||||
CompressedModulusSwitchConfiguration, CompressedModulusSwitchNoiseReductionKey,
|
||||
ModulusSwitchConfiguration, ModulusSwitchNoiseReductionKey, ShortintBootstrappingKey,
|
||||
@@ -38,6 +41,7 @@ use crate::integer::key_switching_key::{
|
||||
CompressedKeySwitchingKeyMaterial, KeySwitchingKeyMaterial,
|
||||
};
|
||||
use crate::shortint::atomic_pattern::compressed::CompressedStandardAtomicPatternServerKey;
|
||||
use crate::shortint::noise_squashing::atomic_pattern::compressed::CompressedAtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::{
|
||||
CompressedShortint128BootstrappingKey, Shortint128BootstrappingKey,
|
||||
};
|
||||
@@ -47,6 +51,7 @@ mod cfg_test_imports {
|
||||
pub(super) use crate::core_crypto::commons::math::random::{
|
||||
CompressionSeed, Distribution, Uniform,
|
||||
};
|
||||
pub(super) use crate::core_crypto::prelude::CiphertextModulus as CoreCiphertextModulus;
|
||||
pub(super) use crate::high_level_api::keys::CompactPrivateKey;
|
||||
pub(super) use crate::integer::compression_keys::CompressionPrivateKeys;
|
||||
pub(super) use crate::integer::noise_squashing::NoiseSquashingPrivateKey;
|
||||
@@ -55,6 +60,7 @@ mod cfg_test_imports {
|
||||
pub(super) use crate::shortint::client_key::atomic_pattern::{
|
||||
AtomicPatternClientKey, StandardAtomicPatternClientKey,
|
||||
};
|
||||
pub(super) use crate::shortint::noise_squashing::atomic_pattern::compressed::standard::CompressedStandardAtomicPatternNoiseSquashingKey;
|
||||
pub(super) use crate::shortint::parameters::{
|
||||
ModulusSwitchNoiseReductionParams, ModulusSwitchType, NoiseSquashingParameters,
|
||||
};
|
||||
@@ -431,37 +437,23 @@ impl CompressedNoiseSquashingKey {
|
||||
{
|
||||
let noise_squashing_parameters = private_noise_squashing_key.noise_squashing_parameters();
|
||||
|
||||
let shortint_key = match noise_squashing_parameters {
|
||||
NoiseSquashingParameters::Classic(ns_params) => {
|
||||
let core_bsk =
|
||||
allocate_and_generate_lwe_bootstrapping_key_with_pre_seeded_generator(
|
||||
lwe_secret_key,
|
||||
private_noise_squashing_key
|
||||
.key
|
||||
.post_noise_squashing_secret_key(),
|
||||
ns_params.decomp_base_log,
|
||||
ns_params.decomp_level_count,
|
||||
ns_params.glwe_noise_distribution,
|
||||
ns_params.ciphertext_modulus,
|
||||
generator,
|
||||
);
|
||||
let shortint_key = match computation_parameters.atomic_pattern() {
|
||||
shortint::AtomicPatternKind::Standard(_) => {
|
||||
let bsk = CompressedShortint128BootstrappingKey::generate_with_pre_seeded_generator(
|
||||
lwe_secret_key,
|
||||
computation_parameters.ciphertext_modulus(),
|
||||
computation_parameters.lwe_noise_distribution(),
|
||||
private_noise_squashing_key,
|
||||
generator,
|
||||
);
|
||||
|
||||
let compressed_mod_switch_config =
|
||||
CompressedModulusSwitchConfiguration::generate_with_pre_seeded_generator(
|
||||
&ns_params.modulus_switch_noise_reduction_params,
|
||||
lwe_secret_key,
|
||||
computation_parameters.lwe_noise_distribution(),
|
||||
computation_parameters.ciphertext_modulus(),
|
||||
generator,
|
||||
);
|
||||
|
||||
CompressedShortint128BootstrappingKey::Classic {
|
||||
bsk: core_bsk,
|
||||
modulus_switch_noise_reduction_key: compressed_mod_switch_config,
|
||||
}
|
||||
CompressedAtomicPatternNoiseSquashingKey::Standard(
|
||||
CompressedStandardAtomicPatternNoiseSquashingKey::from_raw_parts(bsk),
|
||||
)
|
||||
}
|
||||
NoiseSquashingParameters::MultiBit(_) => {
|
||||
panic!("Multibit NoiseSquashing is not supported");
|
||||
shortint::AtomicPatternKind::KeySwitch32 => {
|
||||
// Should be completed when Xofkeyset is implemented for the compute KS32 ap
|
||||
todo!()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -482,46 +474,25 @@ impl CompressedNoiseSquashingKey {
|
||||
where
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let CompressedShortint128BootstrappingKey::Classic {
|
||||
bsk: compressed_bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
} = self.key.bootstrapping_key()
|
||||
else {
|
||||
panic!("Noise squashing key is not a classic key")
|
||||
};
|
||||
|
||||
let mut core_bsk = LweBootstrapKeyOwned::new(
|
||||
0u128,
|
||||
compressed_bsk.glwe_size(),
|
||||
compressed_bsk.polynomial_size(),
|
||||
compressed_bsk.decomposition_base_log(),
|
||||
compressed_bsk.decomposition_level_count(),
|
||||
compressed_bsk.input_lwe_dimension(),
|
||||
compressed_bsk.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
decompress_seeded_lwe_bootstrap_key_with_pre_seeded_generator(
|
||||
&mut core_bsk,
|
||||
compressed_bsk,
|
||||
generator,
|
||||
);
|
||||
|
||||
let mut core_fourier_bsk = Fourier128LweBootstrapKeyOwned::new(
|
||||
core_bsk.input_lwe_dimension(),
|
||||
core_bsk.glwe_size(),
|
||||
core_bsk.polynomial_size(),
|
||||
core_bsk.decomposition_base_log(),
|
||||
core_bsk.decomposition_level_count(),
|
||||
);
|
||||
|
||||
par_convert_standard_lwe_bootstrap_key_to_fourier_128(&core_bsk, &mut core_fourier_bsk);
|
||||
|
||||
let ms_nrk =
|
||||
modulus_switch_noise_reduction_key.decompress_with_pre_seeded_generator(generator);
|
||||
|
||||
let decompressed = Shortint128BootstrappingKey::Classic {
|
||||
bsk: core_fourier_bsk,
|
||||
modulus_switch_noise_reduction_key: ms_nrk,
|
||||
let decompressed = match self.key.atomic_pattern() {
|
||||
CompressedAtomicPatternNoiseSquashingKey::Standard(compressed_std) => {
|
||||
AtomicPatternNoiseSquashingKey::Standard(
|
||||
StandardAtomicPatternNoiseSquashingKey::from_raw_parts(
|
||||
compressed_std
|
||||
.bootstrapping_key()
|
||||
.decompress_with_pre_seeded_generator(generator),
|
||||
),
|
||||
)
|
||||
}
|
||||
CompressedAtomicPatternNoiseSquashingKey::KeySwitch32(compressed_ks32) => {
|
||||
AtomicPatternNoiseSquashingKey::KeySwitch32(
|
||||
KS32AtomicPatternNoiseSquashingKey::from_raw_parts(
|
||||
compressed_ks32
|
||||
.bootstrapping_key()
|
||||
.decompress_with_pre_seeded_generator(generator),
|
||||
),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
integer::noise_squashing::NoiseSquashingKey::from_raw_parts(
|
||||
@@ -577,6 +548,110 @@ impl ShortintClassicCompressedBootstrappingKeyParts {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Scalar> CompressedShortint128BootstrappingKey<Scalar>
|
||||
where
|
||||
Scalar: UnsignedTorus,
|
||||
{
|
||||
#[cfg(test)]
|
||||
fn generate_with_pre_seeded_generator<Gen>(
|
||||
lwe_secret_key: &LweSecretKey<Vec<Scalar>>,
|
||||
ciphertext_modulus: CoreCiphertextModulus<Scalar>,
|
||||
lwe_noise_distribution: DynamicDistribution<Scalar>,
|
||||
private_noise_squashing_key: &NoiseSquashingPrivateKey,
|
||||
generator: &mut EncryptionRandomGenerator<Gen>,
|
||||
) -> Self
|
||||
where
|
||||
Gen: ByteRandomGenerator,
|
||||
Scalar: Encryptable<Uniform, DynamicDistribution<Scalar>>,
|
||||
{
|
||||
let noise_squashing_parameters = private_noise_squashing_key.noise_squashing_parameters();
|
||||
|
||||
match noise_squashing_parameters {
|
||||
NoiseSquashingParameters::Classic(ns_params) => {
|
||||
let core_bsk =
|
||||
allocate_and_generate_lwe_bootstrapping_key_with_pre_seeded_generator(
|
||||
lwe_secret_key,
|
||||
private_noise_squashing_key
|
||||
.key
|
||||
.post_noise_squashing_secret_key(),
|
||||
ns_params.decomp_base_log,
|
||||
ns_params.decomp_level_count,
|
||||
ns_params.glwe_noise_distribution,
|
||||
ns_params.ciphertext_modulus,
|
||||
generator,
|
||||
);
|
||||
|
||||
let compressed_mod_switch_config =
|
||||
CompressedModulusSwitchConfiguration::generate_with_pre_seeded_generator(
|
||||
&ns_params.modulus_switch_noise_reduction_params,
|
||||
lwe_secret_key,
|
||||
lwe_noise_distribution,
|
||||
ciphertext_modulus,
|
||||
generator,
|
||||
);
|
||||
|
||||
Self::Classic {
|
||||
bsk: core_bsk,
|
||||
modulus_switch_noise_reduction_key: compressed_mod_switch_config,
|
||||
}
|
||||
}
|
||||
NoiseSquashingParameters::MultiBit(_) => {
|
||||
panic!("Multibit NoiseSquashing is not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decompress_with_pre_seeded_generator<Gen>(
|
||||
&self,
|
||||
generator: &mut MaskRandomGenerator<Gen>,
|
||||
) -> shortint::noise_squashing::Shortint128BootstrappingKey<Scalar>
|
||||
where
|
||||
Gen: ByteRandomGenerator,
|
||||
{
|
||||
let Self::Classic {
|
||||
bsk: compressed_bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
} = self
|
||||
else {
|
||||
panic!("Noise squashing key is not a classic key")
|
||||
};
|
||||
|
||||
let mut core_bsk = LweBootstrapKeyOwned::new(
|
||||
0u128,
|
||||
compressed_bsk.glwe_size(),
|
||||
compressed_bsk.polynomial_size(),
|
||||
compressed_bsk.decomposition_base_log(),
|
||||
compressed_bsk.decomposition_level_count(),
|
||||
compressed_bsk.input_lwe_dimension(),
|
||||
compressed_bsk.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
decompress_seeded_lwe_bootstrap_key_with_pre_seeded_generator(
|
||||
&mut core_bsk,
|
||||
compressed_bsk,
|
||||
generator,
|
||||
);
|
||||
|
||||
let mut core_fourier_bsk = Fourier128LweBootstrapKeyOwned::new(
|
||||
core_bsk.input_lwe_dimension(),
|
||||
core_bsk.glwe_size(),
|
||||
core_bsk.polynomial_size(),
|
||||
core_bsk.decomposition_base_log(),
|
||||
core_bsk.decomposition_level_count(),
|
||||
);
|
||||
|
||||
par_convert_standard_lwe_bootstrap_key_to_fourier_128(&core_bsk, &mut core_fourier_bsk);
|
||||
|
||||
let ms_nrk =
|
||||
modulus_switch_noise_reduction_key.decompress_with_pre_seeded_generator(generator);
|
||||
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
bsk: core_fourier_bsk,
|
||||
modulus_switch_noise_reduction_key: ms_nrk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
struct ShortintMultibitCompressedBootstrappingKeyParts {
|
||||
core_bsk: SeededLweMultiBitBootstrapKeyOwned<u64>,
|
||||
|
||||
@@ -6,12 +6,21 @@ use crate::core_crypto::gpu::lwe_multi_bit_bootstrap_key::CudaLweMultiBitBootstr
|
||||
use crate::core_crypto::gpu::CudaStreams;
|
||||
use crate::integer::gpu::server_key::CudaBootstrappingKey;
|
||||
use crate::integer::noise_squashing::CompressedNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::atomic_pattern::compressed::CompressedAtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::CompressedShortint128BootstrappingKey;
|
||||
use crate::shortint::server_key::CompressedModulusSwitchConfiguration;
|
||||
|
||||
impl CompressedNoiseSquashingKey {
|
||||
pub fn decompress_to_cuda(&self, streams: &CudaStreams) -> CudaNoiseSquashingKey {
|
||||
let bootstrapping_key = match self.key.bootstrapping_key() {
|
||||
let compressed_bsk =
|
||||
if let CompressedAtomicPatternNoiseSquashingKey::Standard(compressed_nsk) =
|
||||
self.key.atomic_pattern()
|
||||
{
|
||||
compressed_nsk.bootstrapping_key()
|
||||
} else {
|
||||
panic!("GPU only supports the Standard atomic pattern")
|
||||
};
|
||||
let bootstrapping_key = match compressed_bsk {
|
||||
CompressedShortint128BootstrappingKey::Classic {
|
||||
bsk: seeded_bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
|
||||
@@ -545,6 +545,15 @@ impl AtomicPatternParameters {
|
||||
Self::KeySwitch32(key_switch32_pbsparameters) => key_switch32_pbsparameters.log2_p_fail,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn atomic_pattern(&self) -> AtomicPatternKind {
|
||||
match self {
|
||||
Self::Standard(parameters) => {
|
||||
AtomicPatternKind::Standard(parameters.encryption_key_choice().into_pbs_order())
|
||||
}
|
||||
Self::KeySwitch32(_) => AtomicPatternKind::KeySwitch32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParameterSetConformant for AtomicPatternServerKey {
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::convert::Infallible;
|
||||
|
||||
use crate::core_crypto::prelude::fft128_lwe_multi_bit_bootstrap_key::Fourier128LweMultiBitBootstrapKeyOwned;
|
||||
use crate::core_crypto::prelude::*;
|
||||
|
||||
use crate::shortint::noise_squashing::atomic_pattern::compressed::ks32::CompressedKS32AtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::atomic_pattern::compressed::standard::CompressedStandardAtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::atomic_pattern::compressed::CompressedAtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::atomic_pattern::ks32::KS32AtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::atomic_pattern::standard::StandardAtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::atomic_pattern::AtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::{
|
||||
CompressedNoiseSquashingKey, CompressedShortint128BootstrappingKey, NoiseSquashingKey,
|
||||
NoiseSquashingPrivateKey, Shortint128BootstrappingKey,
|
||||
CompressedNoiseSquashingKey, CompressedShortint128BootstrappingKey, GenericNoiseSquashingKey,
|
||||
NoiseSquashingKey, NoiseSquashingPrivateKey, Shortint128BootstrappingKey,
|
||||
StandardNoiseSquashingKey,
|
||||
};
|
||||
use crate::shortint::parameters::CoreCiphertextModulus;
|
||||
use crate::shortint::server_key::{
|
||||
@@ -11,6 +21,7 @@ use crate::shortint::server_key::{
|
||||
ModulusSwitchConfiguration, ModulusSwitchNoiseReductionKey,
|
||||
};
|
||||
use crate::shortint::{CarryModulus, MessageModulus};
|
||||
use crate::Error;
|
||||
use tfhe_versionable::{Upgrade, Version, VersionsDispatch};
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
@@ -65,10 +76,10 @@ pub struct NoiseSquashingKeyV1 {
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
}
|
||||
|
||||
impl Upgrade<NoiseSquashingKey> for NoiseSquashingKeyV1 {
|
||||
impl Upgrade<NoiseSquashingKeyV2> for NoiseSquashingKeyV1 {
|
||||
type Error = Infallible;
|
||||
|
||||
fn upgrade(self) -> Result<NoiseSquashingKey, Self::Error> {
|
||||
fn upgrade(self) -> Result<NoiseSquashingKeyV2, Self::Error> {
|
||||
let Self {
|
||||
bootstrapping_key,
|
||||
modulus_switch_noise_reduction_key,
|
||||
@@ -82,30 +93,192 @@ impl Upgrade<NoiseSquashingKey> for NoiseSquashingKeyV1 {
|
||||
modulus_switch_noise_reduction_key,
|
||||
};
|
||||
|
||||
Ok(NoiseSquashingKey::from_raw_parts(
|
||||
Ok(NoiseSquashingKeyV2 {
|
||||
bootstrapping_key,
|
||||
message_modulus,
|
||||
carry_modulus,
|
||||
output_ciphertext_modulus,
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Version)]
|
||||
pub struct NoiseSquashingKeyV2 {
|
||||
bootstrapping_key: Shortint128BootstrappingKey<u64>,
|
||||
message_modulus: MessageModulus,
|
||||
carry_modulus: CarryModulus,
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
}
|
||||
|
||||
impl<AP: 'static> Upgrade<GenericNoiseSquashingKey<AP>> for NoiseSquashingKeyV2 {
|
||||
type Error = Error;
|
||||
|
||||
fn upgrade(self) -> Result<GenericNoiseSquashingKey<AP>, Self::Error> {
|
||||
let std_ap = StandardAtomicPatternNoiseSquashingKey::from_raw_parts(self.bootstrapping_key);
|
||||
|
||||
if TypeId::of::<AP>() == TypeId::of::<AtomicPatternNoiseSquashingKey>() {
|
||||
let ap = AtomicPatternNoiseSquashingKey::Standard(std_ap);
|
||||
let sk: Box<dyn Any + 'static> = Box::new(NoiseSquashingKey::from_raw_parts(
|
||||
ap,
|
||||
self.message_modulus,
|
||||
self.carry_modulus,
|
||||
self.output_ciphertext_modulus,
|
||||
));
|
||||
// We know from the TypeId that AP is of the right type so we can unwrap
|
||||
Ok(*sk.downcast::<GenericNoiseSquashingKey<AP>>().unwrap())
|
||||
} else if TypeId::of::<AP>() == TypeId::of::<StandardAtomicPatternNoiseSquashingKey>() {
|
||||
let sk: Box<dyn Any + 'static> = Box::new(StandardNoiseSquashingKey::from_raw_parts(
|
||||
std_ap,
|
||||
self.message_modulus,
|
||||
self.carry_modulus,
|
||||
self.output_ciphertext_modulus,
|
||||
));
|
||||
// We know from the TypeId that AP is of the right type so we can unwrap
|
||||
Ok(*sk.downcast::<GenericNoiseSquashingKey<AP>>().unwrap())
|
||||
} else {
|
||||
Err(Error::new(
|
||||
"NoiseSquashingKey from TFHE-rs 1.3 and before can only be deserialized to the standard \
|
||||
Atomic Pattern"
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum NoiseSquashingKeyVersions {
|
||||
pub enum NoiseSquashingKeyVersions<AP> {
|
||||
V0(NoiseSquashingKeyV0),
|
||||
V1(NoiseSquashingKeyV1),
|
||||
V2(NoiseSquashingKey),
|
||||
V2(NoiseSquashingKeyV2),
|
||||
V3(GenericNoiseSquashingKey<AP>),
|
||||
}
|
||||
|
||||
#[derive(Version)]
|
||||
pub enum Shortint128BootstrappingKeyV0 {
|
||||
Classic {
|
||||
bsk: Fourier128LweBootstrapKeyOwned,
|
||||
modulus_switch_noise_reduction_key: ModulusSwitchConfiguration<u64>,
|
||||
},
|
||||
MultiBit {
|
||||
bsk: Fourier128LweMultiBitBootstrapKeyOwned,
|
||||
thread_count: ThreadCount,
|
||||
deterministic_execution: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger> Upgrade<Shortint128BootstrappingKey<Scalar>>
|
||||
for Shortint128BootstrappingKeyV0
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn upgrade(self) -> Result<Shortint128BootstrappingKey<Scalar>, Self::Error> {
|
||||
if TypeId::of::<Scalar>() == TypeId::of::<u64>() {
|
||||
Ok(match self {
|
||||
Self::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
} => {
|
||||
let noise_reduction_key =
|
||||
Box::new(modulus_switch_noise_reduction_key) as Box<dyn Any>;
|
||||
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key: *noise_reduction_key
|
||||
.downcast::<ModulusSwitchConfiguration<Scalar>>()
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
Self::MultiBit {
|
||||
bsk,
|
||||
thread_count,
|
||||
deterministic_execution,
|
||||
} => Shortint128BootstrappingKey::MultiBit {
|
||||
bsk,
|
||||
thread_count,
|
||||
deterministic_execution,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
Err(Error::new(
|
||||
"Shortint128BootstrappingKey from TFHE-rs 1.3 and before only support u64 drift\
|
||||
mitigation key coeffecients"
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum Shortint128BootstrappingKeyVersions {
|
||||
V0(Shortint128BootstrappingKey),
|
||||
pub enum Shortint128BootstrappingKeyVersions<Scalar>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
{
|
||||
V0(Shortint128BootstrappingKeyV0),
|
||||
V1(Shortint128BootstrappingKey<Scalar>),
|
||||
}
|
||||
|
||||
#[derive(Version)]
|
||||
pub enum CompressedShortint128BootstrappingKeyV0 {
|
||||
Classic {
|
||||
bsk: SeededLweBootstrapKeyOwned<u128>,
|
||||
modulus_switch_noise_reduction_key: CompressedModulusSwitchConfiguration<u64>,
|
||||
},
|
||||
MultiBit {
|
||||
bsk: SeededLweMultiBitBootstrapKeyOwned<u128>,
|
||||
thread_count: ThreadCount,
|
||||
deterministic_execution: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl<Scalar: UnsignedInteger> Upgrade<CompressedShortint128BootstrappingKey<Scalar>>
|
||||
for CompressedShortint128BootstrappingKeyV0
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn upgrade(self) -> Result<CompressedShortint128BootstrappingKey<Scalar>, Self::Error> {
|
||||
if TypeId::of::<Scalar>() == TypeId::of::<u64>() {
|
||||
Ok(match self {
|
||||
Self::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
} => {
|
||||
let noise_reduction_key =
|
||||
Box::new(modulus_switch_noise_reduction_key) as Box<dyn Any>;
|
||||
|
||||
CompressedShortint128BootstrappingKey::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key: *noise_reduction_key
|
||||
.downcast::<CompressedModulusSwitchConfiguration<Scalar>>()
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
Self::MultiBit {
|
||||
bsk,
|
||||
thread_count,
|
||||
deterministic_execution,
|
||||
} => CompressedShortint128BootstrappingKey::MultiBit {
|
||||
bsk,
|
||||
thread_count,
|
||||
deterministic_execution,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
Err(Error::new(
|
||||
"CompressedShortint128BootstrappingKey from TFHE-rs 1.3 and before only support u64 \
|
||||
drift mitigation key coeffecients"
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum CompressedShortint128BootstrappingKeyVersions {
|
||||
V0(CompressedShortint128BootstrappingKey),
|
||||
pub enum CompressedShortint128BootstrappingKeyVersions<Scalar>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
{
|
||||
V0(CompressedShortint128BootstrappingKeyV0),
|
||||
V1(CompressedShortint128BootstrappingKey<Scalar>),
|
||||
}
|
||||
|
||||
#[derive(Version)]
|
||||
@@ -155,10 +328,10 @@ pub struct CompressedNoiseSquashingKeyV1 {
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
}
|
||||
|
||||
impl Upgrade<CompressedNoiseSquashingKey> for CompressedNoiseSquashingKeyV1 {
|
||||
impl Upgrade<CompressedNoiseSquashingKeyV2> for CompressedNoiseSquashingKeyV1 {
|
||||
type Error = Infallible;
|
||||
|
||||
fn upgrade(self) -> Result<CompressedNoiseSquashingKey, Self::Error> {
|
||||
fn upgrade(self) -> Result<CompressedNoiseSquashingKeyV2, Self::Error> {
|
||||
let Self {
|
||||
bootstrapping_key,
|
||||
modulus_switch_noise_reduction_key,
|
||||
@@ -172,11 +345,38 @@ impl Upgrade<CompressedNoiseSquashingKey> for CompressedNoiseSquashingKeyV1 {
|
||||
modulus_switch_noise_reduction_key,
|
||||
};
|
||||
|
||||
Ok(CompressedNoiseSquashingKey::from_raw_parts(
|
||||
Ok(CompressedNoiseSquashingKeyV2 {
|
||||
bootstrapping_key,
|
||||
message_modulus,
|
||||
carry_modulus,
|
||||
output_ciphertext_modulus,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Version)]
|
||||
pub struct CompressedNoiseSquashingKeyV2 {
|
||||
bootstrapping_key: CompressedShortint128BootstrappingKey<u64>,
|
||||
message_modulus: MessageModulus,
|
||||
carry_modulus: CarryModulus,
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
}
|
||||
|
||||
impl Upgrade<CompressedNoiseSquashingKey> for CompressedNoiseSquashingKeyV2 {
|
||||
type Error = Infallible;
|
||||
|
||||
fn upgrade(self) -> Result<CompressedNoiseSquashingKey, Self::Error> {
|
||||
let std_ap = CompressedStandardAtomicPatternNoiseSquashingKey::from_raw_parts(
|
||||
self.bootstrapping_key,
|
||||
);
|
||||
|
||||
let atomic_pattern = CompressedAtomicPatternNoiseSquashingKey::Standard(std_ap);
|
||||
|
||||
Ok(CompressedNoiseSquashingKey::from_raw_parts(
|
||||
atomic_pattern,
|
||||
self.message_modulus,
|
||||
self.carry_modulus,
|
||||
self.output_ciphertext_modulus,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -185,5 +385,36 @@ impl Upgrade<CompressedNoiseSquashingKey> for CompressedNoiseSquashingKeyV1 {
|
||||
pub enum CompressedNoiseSquashingKeyVersions {
|
||||
V0(CompressedNoiseSquashingKeyV0),
|
||||
V1(CompressedNoiseSquashingKeyV1),
|
||||
V2(CompressedNoiseSquashingKey),
|
||||
V2(CompressedNoiseSquashingKeyV2),
|
||||
V3(CompressedNoiseSquashingKey),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum AtomicPatternNoiseSquashingKeyVersions {
|
||||
V0(AtomicPatternNoiseSquashingKey),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum StandardAtomicPatternNoiseSquashingKeyVersions {
|
||||
V0(StandardAtomicPatternNoiseSquashingKey),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum KS32AtomicPatternNoiseSquashingKeyVersions {
|
||||
V0(KS32AtomicPatternNoiseSquashingKey),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum CompressedAtomicPatternNoiseSquashingKeyVersions {
|
||||
V0(CompressedAtomicPatternNoiseSquashingKey),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum CompressedStandardAtomicPatternNoiseSquashingKeyVersions {
|
||||
V0(CompressedStandardAtomicPatternNoiseSquashingKey),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum CompressedKS32AtomicPatternNoiseSquashingKeyVersions {
|
||||
V0(CompressedKS32AtomicPatternNoiseSquashingKey),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
use crate::shortint::backward_compatibility::noise_squashing::CompressedKS32AtomicPatternNoiseSquashingKeyVersions;
|
||||
use crate::shortint::client_key::atomic_pattern::KS32AtomicPatternClientKey;
|
||||
use crate::shortint::noise_squashing::atomic_pattern::ks32::KS32AtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::{
|
||||
CompressedShortint128BootstrappingKey, NoiseSquashingPrivateKey,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(CompressedKS32AtomicPatternNoiseSquashingKeyVersions)]
|
||||
pub struct CompressedKS32AtomicPatternNoiseSquashingKey {
|
||||
bootstrapping_key: CompressedShortint128BootstrappingKey<u32>,
|
||||
}
|
||||
|
||||
impl CompressedKS32AtomicPatternNoiseSquashingKey {
|
||||
pub fn new(
|
||||
cks: &KS32AtomicPatternClientKey,
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
) -> Self {
|
||||
let parameters = cks.parameters;
|
||||
|
||||
let bootstrapping_key = CompressedShortint128BootstrappingKey::new(
|
||||
&cks.lwe_secret_key,
|
||||
parameters.post_keyswitch_ciphertext_modulus(),
|
||||
parameters.lwe_noise_distribution(),
|
||||
noise_squashing_private_key,
|
||||
);
|
||||
|
||||
Self { bootstrapping_key }
|
||||
}
|
||||
|
||||
pub fn decompress(&self) -> KS32AtomicPatternNoiseSquashingKey {
|
||||
let bootstrapping_key = self.bootstrapping_key.decompress();
|
||||
|
||||
KS32AtomicPatternNoiseSquashingKey::from_raw_parts(bootstrapping_key)
|
||||
}
|
||||
|
||||
pub fn from_raw_parts(bootstrapping_key: CompressedShortint128BootstrappingKey<u32>) -> Self {
|
||||
Self { bootstrapping_key }
|
||||
}
|
||||
|
||||
pub fn into_raw_parts(self) -> CompressedShortint128BootstrappingKey<u32> {
|
||||
self.bootstrapping_key
|
||||
}
|
||||
|
||||
pub fn bootstrapping_key(&self) -> &CompressedShortint128BootstrappingKey<u32> {
|
||||
&self.bootstrapping_key
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
use crate::shortint::backward_compatibility::noise_squashing::CompressedAtomicPatternNoiseSquashingKeyVersions;
|
||||
use crate::shortint::client_key::atomic_pattern::AtomicPatternClientKey;
|
||||
use crate::shortint::noise_squashing::NoiseSquashingPrivateKey;
|
||||
use ks32::CompressedKS32AtomicPatternNoiseSquashingKey;
|
||||
use standard::CompressedStandardAtomicPatternNoiseSquashingKey;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
use super::AtomicPatternNoiseSquashingKey;
|
||||
|
||||
pub mod ks32;
|
||||
pub mod standard;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(CompressedAtomicPatternNoiseSquashingKeyVersions)]
|
||||
pub enum CompressedAtomicPatternNoiseSquashingKey {
|
||||
Standard(CompressedStandardAtomicPatternNoiseSquashingKey),
|
||||
KeySwitch32(CompressedKS32AtomicPatternNoiseSquashingKey),
|
||||
}
|
||||
|
||||
impl CompressedAtomicPatternNoiseSquashingKey {
|
||||
pub fn new(
|
||||
cks: &AtomicPatternClientKey,
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
) -> Self {
|
||||
match cks {
|
||||
AtomicPatternClientKey::Standard(std_cks) => {
|
||||
Self::Standard(CompressedStandardAtomicPatternNoiseSquashingKey::new(
|
||||
std_cks,
|
||||
noise_squashing_private_key,
|
||||
))
|
||||
}
|
||||
AtomicPatternClientKey::KeySwitch32(ks32_cks) => {
|
||||
Self::KeySwitch32(CompressedKS32AtomicPatternNoiseSquashingKey::new(
|
||||
ks32_cks,
|
||||
noise_squashing_private_key,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decompress(&self) -> AtomicPatternNoiseSquashingKey {
|
||||
match self {
|
||||
Self::Standard(std_compressed) => {
|
||||
AtomicPatternNoiseSquashingKey::Standard(std_compressed.decompress())
|
||||
}
|
||||
Self::KeySwitch32(ks32_compressed) => {
|
||||
AtomicPatternNoiseSquashingKey::KeySwitch32(ks32_compressed.decompress())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
use crate::shortint::backward_compatibility::noise_squashing::CompressedStandardAtomicPatternNoiseSquashingKeyVersions;
|
||||
use crate::shortint::client_key::atomic_pattern::StandardAtomicPatternClientKey;
|
||||
use crate::shortint::noise_squashing::atomic_pattern::standard::StandardAtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::{
|
||||
CompressedShortint128BootstrappingKey, NoiseSquashingPrivateKey,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(CompressedStandardAtomicPatternNoiseSquashingKeyVersions)]
|
||||
pub struct CompressedStandardAtomicPatternNoiseSquashingKey {
|
||||
bootstrapping_key: CompressedShortint128BootstrappingKey<u64>,
|
||||
}
|
||||
|
||||
impl CompressedStandardAtomicPatternNoiseSquashingKey {
|
||||
pub fn new(
|
||||
cks: &StandardAtomicPatternClientKey,
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
) -> Self {
|
||||
let parameters = cks.parameters;
|
||||
|
||||
let bootstrapping_key = CompressedShortint128BootstrappingKey::new(
|
||||
&cks.lwe_secret_key,
|
||||
parameters.ciphertext_modulus(),
|
||||
parameters.lwe_noise_distribution(),
|
||||
noise_squashing_private_key,
|
||||
);
|
||||
|
||||
Self { bootstrapping_key }
|
||||
}
|
||||
|
||||
pub fn decompress(&self) -> StandardAtomicPatternNoiseSquashingKey {
|
||||
let bootstrapping_key = self.bootstrapping_key.decompress();
|
||||
|
||||
StandardAtomicPatternNoiseSquashingKey::from_raw_parts(bootstrapping_key)
|
||||
}
|
||||
|
||||
pub fn from_raw_parts(bootstrapping_key: CompressedShortint128BootstrappingKey<u64>) -> Self {
|
||||
Self { bootstrapping_key }
|
||||
}
|
||||
|
||||
pub fn into_raw_parts(self) -> CompressedShortint128BootstrappingKey<u64> {
|
||||
self.bootstrapping_key
|
||||
}
|
||||
|
||||
pub fn bootstrapping_key(&self) -> &CompressedShortint128BootstrappingKey<u64> {
|
||||
&self.bootstrapping_key
|
||||
}
|
||||
}
|
||||
140
tfhe/src/shortint/noise_squashing/atomic_pattern/ks32.rs
Normal file
140
tfhe/src/shortint/noise_squashing/atomic_pattern/ks32.rs
Normal file
@@ -0,0 +1,140 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
use crate::core_crypto::prelude::{
|
||||
generate_programmable_bootstrap_glwe_lut, keyswitch_lwe_ciphertext_with_scalar_change,
|
||||
CiphertextModulus as CoreCiphertextModulus, LweCiphertext,
|
||||
};
|
||||
use crate::shortint::atomic_pattern::AtomicPattern;
|
||||
use crate::shortint::backward_compatibility::noise_squashing::KS32AtomicPatternNoiseSquashingKeyVersions;
|
||||
use crate::shortint::ciphertext::SquashedNoiseCiphertext;
|
||||
use crate::shortint::client_key::atomic_pattern::KS32AtomicPatternClientKey;
|
||||
use crate::shortint::encoding::compute_delta;
|
||||
use crate::shortint::engine::ShortintEngine;
|
||||
use crate::shortint::noise_squashing::{NoiseSquashingPrivateKey, Shortint128BootstrappingKey};
|
||||
use crate::shortint::server_key::{
|
||||
apply_programmable_bootstrap_128, KS32ServerKeyView, ServerKeyView,
|
||||
};
|
||||
use crate::shortint::{CarryModulus, Ciphertext, MessageModulus, PaddingBit};
|
||||
|
||||
use super::NoiseSquashingAtomicPattern;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(KS32AtomicPatternNoiseSquashingKeyVersions)]
|
||||
pub struct KS32AtomicPatternNoiseSquashingKey {
|
||||
bootstrapping_key: Shortint128BootstrappingKey<u32>,
|
||||
}
|
||||
|
||||
impl KS32AtomicPatternNoiseSquashingKey {
|
||||
pub fn new(
|
||||
cks: &KS32AtomicPatternClientKey,
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
) -> Self {
|
||||
let parameters = cks.parameters;
|
||||
|
||||
let bootstrapping_key = Shortint128BootstrappingKey::new(
|
||||
&cks.lwe_secret_key,
|
||||
parameters.post_keyswitch_ciphertext_modulus(),
|
||||
parameters.lwe_noise_distribution(),
|
||||
noise_squashing_private_key,
|
||||
);
|
||||
|
||||
Self { bootstrapping_key }
|
||||
}
|
||||
|
||||
pub fn from_raw_parts(bootstrapping_key: Shortint128BootstrappingKey<u32>) -> Self {
|
||||
Self { bootstrapping_key }
|
||||
}
|
||||
|
||||
pub fn into_raw_parts(self) -> Shortint128BootstrappingKey<u32> {
|
||||
self.bootstrapping_key
|
||||
}
|
||||
|
||||
pub fn bootstrapping_key(&self) -> &Shortint128BootstrappingKey<u32> {
|
||||
&self.bootstrapping_key
|
||||
}
|
||||
}
|
||||
|
||||
impl NoiseSquashingAtomicPattern for KS32AtomicPatternNoiseSquashingKey {
|
||||
fn squash_ciphertext_noise(
|
||||
&self,
|
||||
ciphertext: &Ciphertext,
|
||||
src_server_key: ServerKeyView,
|
||||
output_message_modulus: MessageModulus,
|
||||
output_carry_modulus: CarryModulus,
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
) -> crate::Result<SquashedNoiseCiphertext> {
|
||||
let sks_ap = src_server_key.atomic_pattern.kind();
|
||||
let src_server_key: KS32ServerKeyView = src_server_key.try_into().map_err(|_| {
|
||||
crate::error!(
|
||||
"Incompatible atomic pattern between noise squashing key and server key (noise \
|
||||
squashing ap: KS32, server key ap: {:?})",
|
||||
sks_ap
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut lwe_before_ms = LweCiphertext::new(
|
||||
0u32,
|
||||
src_server_key
|
||||
.atomic_pattern
|
||||
.key_switching_key
|
||||
.output_lwe_size(),
|
||||
src_server_key
|
||||
.atomic_pattern
|
||||
.key_switching_key
|
||||
.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
keyswitch_lwe_ciphertext_with_scalar_change(
|
||||
&src_server_key.atomic_pattern.key_switching_key,
|
||||
&ciphertext.ct,
|
||||
&mut lwe_before_ms,
|
||||
);
|
||||
|
||||
let output_lwe_size = self.bootstrapping_key.output_lwe_dimension().to_lwe_size();
|
||||
|
||||
let mut res = SquashedNoiseCiphertext::new_zero(
|
||||
output_lwe_size,
|
||||
output_ciphertext_modulus,
|
||||
output_message_modulus,
|
||||
output_carry_modulus,
|
||||
);
|
||||
|
||||
let bsk_glwe_size = self.bootstrapping_key.glwe_size();
|
||||
let bsk_polynomial_size = self.bootstrapping_key.polynomial_size();
|
||||
|
||||
let delta = compute_delta(
|
||||
output_ciphertext_modulus,
|
||||
output_message_modulus,
|
||||
output_carry_modulus,
|
||||
PaddingBit::Yes,
|
||||
);
|
||||
|
||||
let output_cleartext_space = output_message_modulus.0 * output_carry_modulus.0;
|
||||
|
||||
let id_lut = generate_programmable_bootstrap_glwe_lut(
|
||||
bsk_polynomial_size,
|
||||
bsk_glwe_size,
|
||||
output_cleartext_space.try_into().unwrap(),
|
||||
output_ciphertext_modulus,
|
||||
delta,
|
||||
|x| x,
|
||||
);
|
||||
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let buffers = engine.get_computation_buffers();
|
||||
|
||||
apply_programmable_bootstrap_128(
|
||||
&self.bootstrapping_key,
|
||||
&lwe_before_ms,
|
||||
res.lwe_ciphertext_mut(),
|
||||
&id_lut,
|
||||
buffers,
|
||||
);
|
||||
});
|
||||
|
||||
res.set_degree(ciphertext.degree);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
81
tfhe/src/shortint/noise_squashing/atomic_pattern/mod.rs
Normal file
81
tfhe/src/shortint/noise_squashing/atomic_pattern/mod.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
use ks32::KS32AtomicPatternNoiseSquashingKey;
|
||||
use standard::StandardAtomicPatternNoiseSquashingKey;
|
||||
|
||||
use crate::core_crypto::prelude::CiphertextModulus as CoreCiphertextModulus;
|
||||
use crate::shortint::backward_compatibility::noise_squashing::AtomicPatternNoiseSquashingKeyVersions;
|
||||
use crate::shortint::ciphertext::SquashedNoiseCiphertext;
|
||||
use crate::shortint::client_key::atomic_pattern::AtomicPatternClientKey;
|
||||
use crate::shortint::server_key::ServerKeyView;
|
||||
use crate::shortint::{CarryModulus, Ciphertext, MessageModulus};
|
||||
|
||||
use super::NoiseSquashingPrivateKey;
|
||||
|
||||
pub mod compressed;
|
||||
pub mod ks32;
|
||||
pub mod standard;
|
||||
|
||||
pub trait NoiseSquashingAtomicPattern {
|
||||
fn squash_ciphertext_noise(
|
||||
&self,
|
||||
ciphertext: &Ciphertext,
|
||||
src_server_key: ServerKeyView,
|
||||
output_message_modulus: MessageModulus,
|
||||
output_carry_modulus: CarryModulus,
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
) -> crate::Result<SquashedNoiseCiphertext>;
|
||||
}
|
||||
|
||||
/// The noise squashing key materials for all the supported Atomic Patterns
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(AtomicPatternNoiseSquashingKeyVersions)]
|
||||
pub enum AtomicPatternNoiseSquashingKey {
|
||||
Standard(StandardAtomicPatternNoiseSquashingKey),
|
||||
KeySwitch32(KS32AtomicPatternNoiseSquashingKey),
|
||||
}
|
||||
|
||||
impl AtomicPatternNoiseSquashingKey {
|
||||
pub fn new(
|
||||
cks: &AtomicPatternClientKey,
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
) -> Self {
|
||||
match cks {
|
||||
AtomicPatternClientKey::Standard(std_cks) => Self::Standard(
|
||||
StandardAtomicPatternNoiseSquashingKey::new(std_cks, noise_squashing_private_key),
|
||||
),
|
||||
AtomicPatternClientKey::KeySwitch32(ks32_cks) => Self::KeySwitch32(
|
||||
KS32AtomicPatternNoiseSquashingKey::new(ks32_cks, noise_squashing_private_key),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NoiseSquashingAtomicPattern for AtomicPatternNoiseSquashingKey {
|
||||
fn squash_ciphertext_noise(
|
||||
&self,
|
||||
ciphertext: &Ciphertext,
|
||||
src_server_key: ServerKeyView,
|
||||
output_message_modulus: MessageModulus,
|
||||
output_carry_modulus: CarryModulus,
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
) -> crate::Result<SquashedNoiseCiphertext> {
|
||||
match self {
|
||||
Self::Standard(std_nsk) => std_nsk.squash_ciphertext_noise(
|
||||
ciphertext,
|
||||
src_server_key,
|
||||
output_message_modulus,
|
||||
output_carry_modulus,
|
||||
output_ciphertext_modulus,
|
||||
),
|
||||
Self::KeySwitch32(ks32_nsk) => ks32_nsk.squash_ciphertext_noise(
|
||||
ciphertext,
|
||||
src_server_key,
|
||||
output_message_modulus,
|
||||
output_carry_modulus,
|
||||
output_ciphertext_modulus,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
152
tfhe/src/shortint/noise_squashing/atomic_pattern/standard.rs
Normal file
152
tfhe/src/shortint/noise_squashing/atomic_pattern/standard.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
use crate::core_crypto::prelude::{
|
||||
generate_programmable_bootstrap_glwe_lut, keyswitch_lwe_ciphertext, CiphertextModulus,
|
||||
LweCiphertext,
|
||||
};
|
||||
use crate::shortint::atomic_pattern::AtomicPattern;
|
||||
use crate::shortint::backward_compatibility::noise_squashing::StandardAtomicPatternNoiseSquashingKeyVersions;
|
||||
use crate::shortint::ciphertext::SquashedNoiseCiphertext;
|
||||
use crate::shortint::client_key::atomic_pattern::{
|
||||
EncryptionAtomicPattern, StandardAtomicPatternClientKey,
|
||||
};
|
||||
use crate::shortint::encoding::compute_delta;
|
||||
use crate::shortint::engine::ShortintEngine;
|
||||
use crate::shortint::noise_squashing::{NoiseSquashingPrivateKey, Shortint128BootstrappingKey};
|
||||
use crate::shortint::server_key::{
|
||||
apply_programmable_bootstrap_128, ServerKeyView, StandardServerKeyView,
|
||||
};
|
||||
use crate::shortint::{CarryModulus, Ciphertext, MessageModulus, PBSOrder, PaddingBit};
|
||||
|
||||
use super::NoiseSquashingAtomicPattern;
|
||||
|
||||
/// The definition of the noise squashing key elements used in the
|
||||
/// [`Standard`](crate::shortint::atomic_pattern::AtomicPatternKind::Standard) atomic pattern
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(StandardAtomicPatternNoiseSquashingKeyVersions)]
|
||||
pub struct StandardAtomicPatternNoiseSquashingKey {
|
||||
bootstrapping_key: Shortint128BootstrappingKey<u64>,
|
||||
}
|
||||
|
||||
impl StandardAtomicPatternNoiseSquashingKey {
|
||||
pub fn new(
|
||||
cks: &StandardAtomicPatternClientKey,
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
) -> Self {
|
||||
let parameters = cks.parameters();
|
||||
|
||||
let bootstrapping_key = Shortint128BootstrappingKey::new(
|
||||
&cks.lwe_secret_key,
|
||||
parameters.ciphertext_modulus(),
|
||||
parameters.lwe_noise_distribution(),
|
||||
noise_squashing_private_key,
|
||||
);
|
||||
|
||||
Self { bootstrapping_key }
|
||||
}
|
||||
|
||||
pub fn from_raw_parts(bootstrapping_key: Shortint128BootstrappingKey<u64>) -> Self {
|
||||
Self { bootstrapping_key }
|
||||
}
|
||||
|
||||
pub fn into_raw_parts(self) -> Shortint128BootstrappingKey<u64> {
|
||||
self.bootstrapping_key
|
||||
}
|
||||
|
||||
pub fn bootstrapping_key(&self) -> &Shortint128BootstrappingKey<u64> {
|
||||
&self.bootstrapping_key
|
||||
}
|
||||
}
|
||||
|
||||
impl NoiseSquashingAtomicPattern for StandardAtomicPatternNoiseSquashingKey {
|
||||
fn squash_ciphertext_noise(
|
||||
&self,
|
||||
ciphertext: &Ciphertext,
|
||||
src_server_key: ServerKeyView,
|
||||
output_message_modulus: MessageModulus,
|
||||
output_carry_modulus: CarryModulus,
|
||||
output_ciphertext_modulus: CiphertextModulus<u128>,
|
||||
) -> crate::Result<SquashedNoiseCiphertext> {
|
||||
let sks_ap = src_server_key.atomic_pattern.kind();
|
||||
let src_server_key: StandardServerKeyView = src_server_key.try_into().map_err(|_| {
|
||||
crate::error!(
|
||||
"Incompatible atomic pattern between noise squashing key and server key (noise \
|
||||
squashing ap: Standard, server key ap: {:?})",
|
||||
sks_ap
|
||||
)
|
||||
})?;
|
||||
|
||||
let lwe_before_ms = match src_server_key.atomic_pattern.pbs_order {
|
||||
// Under the big key, first need to keyswitch
|
||||
PBSOrder::KeyswitchBootstrap => {
|
||||
let mut after_ks_ct = LweCiphertext::new(
|
||||
0u64,
|
||||
src_server_key
|
||||
.atomic_pattern
|
||||
.key_switching_key
|
||||
.output_lwe_size(),
|
||||
src_server_key
|
||||
.atomic_pattern
|
||||
.key_switching_key
|
||||
.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
keyswitch_lwe_ciphertext(
|
||||
&src_server_key.atomic_pattern.key_switching_key,
|
||||
&ciphertext.ct,
|
||||
&mut after_ks_ct,
|
||||
);
|
||||
after_ks_ct
|
||||
}
|
||||
// Under the small key, no need to keyswitch
|
||||
PBSOrder::BootstrapKeyswitch => ciphertext.ct.clone(),
|
||||
};
|
||||
|
||||
let output_lwe_size = self.bootstrapping_key.output_lwe_dimension().to_lwe_size();
|
||||
|
||||
let mut res = SquashedNoiseCiphertext::new_zero(
|
||||
output_lwe_size,
|
||||
output_ciphertext_modulus,
|
||||
output_message_modulus,
|
||||
output_carry_modulus,
|
||||
);
|
||||
|
||||
let bsk_glwe_size = self.bootstrapping_key.glwe_size();
|
||||
let bsk_polynomial_size = self.bootstrapping_key.polynomial_size();
|
||||
|
||||
let delta = compute_delta(
|
||||
output_ciphertext_modulus,
|
||||
output_message_modulus,
|
||||
output_carry_modulus,
|
||||
PaddingBit::Yes,
|
||||
);
|
||||
|
||||
let output_cleartext_space = output_message_modulus.0 * output_carry_modulus.0;
|
||||
|
||||
let id_lut = generate_programmable_bootstrap_glwe_lut(
|
||||
bsk_polynomial_size,
|
||||
bsk_glwe_size,
|
||||
output_cleartext_space.try_into().unwrap(),
|
||||
output_ciphertext_modulus,
|
||||
delta,
|
||||
|x| x,
|
||||
);
|
||||
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let buffers = engine.get_computation_buffers();
|
||||
|
||||
apply_programmable_bootstrap_128(
|
||||
&self.bootstrapping_key,
|
||||
&lwe_before_ms,
|
||||
res.lwe_ciphertext_mut(),
|
||||
&id_lut,
|
||||
buffers,
|
||||
);
|
||||
});
|
||||
|
||||
res.set_degree(ciphertext.degree);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,21 @@
|
||||
use super::atomic_pattern::compressed::CompressedAtomicPatternNoiseSquashingKey;
|
||||
use super::server_key::Shortint128BootstrappingKeyConformanceParams;
|
||||
use super::{NoiseSquashingKey, NoiseSquashingKeyConformanceParams, NoiseSquashingPrivateKey};
|
||||
use crate::conformance::ParameterSetConformant;
|
||||
use crate::core_crypto::algorithms::lwe_bootstrap_key_conversion::par_convert_standard_lwe_bootstrap_key_to_fourier_128;
|
||||
use crate::core_crypto::algorithms::lwe_bootstrap_key_generation::par_allocate_and_generate_new_seeded_lwe_bootstrap_key;
|
||||
use crate::core_crypto::commons::math::random::Uniform;
|
||||
use crate::core_crypto::entities::{Fourier128LweBootstrapKeyOwned, SeededLweBootstrapKeyOwned};
|
||||
use crate::core_crypto::prelude::{
|
||||
par_allocate_and_generate_new_seeded_lwe_multi_bit_bootstrap_key,
|
||||
par_convert_standard_lwe_multi_bit_bootstrap_key_to_fourier_128,
|
||||
Fourier128LweMultiBitBootstrapKey, SeededLweMultiBitBootstrapKeyOwned, ThreadCount,
|
||||
par_convert_standard_lwe_multi_bit_bootstrap_key_to_fourier_128, CastFrom, Container,
|
||||
DynamicDistribution, Encryptable, Fourier128LweMultiBitBootstrapKey, LweSecretKey,
|
||||
SeededLweMultiBitBootstrapKeyOwned, ThreadCount, UnsignedInteger, UnsignedTorus,
|
||||
};
|
||||
use crate::shortint::backward_compatibility::noise_squashing::{
|
||||
CompressedNoiseSquashingKeyVersions, CompressedShortint128BootstrappingKeyVersions,
|
||||
};
|
||||
use crate::shortint::client_key::atomic_pattern::AtomicPatternClientKey;
|
||||
|
||||
use crate::shortint::client_key::ClientKey;
|
||||
use crate::shortint::engine::ShortintEngine;
|
||||
use crate::shortint::noise_squashing::server_key::Shortint128BootstrappingKey;
|
||||
@@ -22,15 +26,19 @@ use crate::shortint::parameters::{
|
||||
use crate::shortint::server_key::{
|
||||
CompressedModulusSwitchConfiguration, ModulusSwitchNoiseReductionKeyConformanceParams,
|
||||
};
|
||||
use crate::shortint::AtomicPatternKind;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(CompressedShortint128BootstrappingKeyVersions)]
|
||||
pub enum CompressedShortint128BootstrappingKey {
|
||||
pub enum CompressedShortint128BootstrappingKey<Scalar>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
{
|
||||
Classic {
|
||||
bsk: SeededLweBootstrapKeyOwned<u128>,
|
||||
modulus_switch_noise_reduction_key: CompressedModulusSwitchConfiguration<u64>,
|
||||
modulus_switch_noise_reduction_key: CompressedModulusSwitchConfiguration<Scalar>,
|
||||
},
|
||||
MultiBit {
|
||||
bsk: SeededLweMultiBitBootstrapKeyOwned<u128>,
|
||||
@@ -39,8 +47,84 @@ pub enum CompressedShortint128BootstrappingKey {
|
||||
},
|
||||
}
|
||||
|
||||
impl CompressedShortint128BootstrappingKey {
|
||||
fn decompress(&self) -> Shortint128BootstrappingKey {
|
||||
impl<Scalar> CompressedShortint128BootstrappingKey<Scalar>
|
||||
where
|
||||
Scalar: UnsignedTorus,
|
||||
{
|
||||
pub(crate) fn new<InputKeyCont>(
|
||||
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
|
||||
ciphertext_modulus: CoreCiphertextModulus<Scalar>,
|
||||
lwe_noise_distribution: DynamicDistribution<Scalar>,
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
) -> Self
|
||||
where
|
||||
InputKeyCont: Container<Element = Scalar> + Sync,
|
||||
Scalar: Encryptable<Uniform, DynamicDistribution<Scalar>> + CastFrom<usize>,
|
||||
{
|
||||
let noise_squashing_parameters = noise_squashing_private_key.noise_squashing_parameters();
|
||||
|
||||
match noise_squashing_parameters {
|
||||
NoiseSquashingParameters::Classic(params) => {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let seeded_bsk = par_allocate_and_generate_new_seeded_lwe_bootstrap_key(
|
||||
input_lwe_secret_key,
|
||||
noise_squashing_private_key.post_noise_squashing_secret_key(),
|
||||
params.decomp_base_log,
|
||||
params.decomp_level_count,
|
||||
params.glwe_noise_distribution,
|
||||
params.ciphertext_modulus,
|
||||
&mut engine.seeder,
|
||||
);
|
||||
|
||||
let modulus_switch_noise_reduction_key = params
|
||||
.modulus_switch_noise_reduction_params
|
||||
.to_compressed_modulus_switch_configuration(
|
||||
input_lwe_secret_key,
|
||||
ciphertext_modulus,
|
||||
lwe_noise_distribution,
|
||||
engine,
|
||||
);
|
||||
|
||||
Self::Classic {
|
||||
bsk: seeded_bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
}
|
||||
})
|
||||
}
|
||||
NoiseSquashingParameters::MultiBit(params) => {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let seeded_bsk =
|
||||
par_allocate_and_generate_new_seeded_lwe_multi_bit_bootstrap_key(
|
||||
input_lwe_secret_key,
|
||||
noise_squashing_private_key.post_noise_squashing_secret_key(),
|
||||
params.decomp_base_log,
|
||||
params.decomp_level_count,
|
||||
params.glwe_noise_distribution,
|
||||
params.grouping_factor,
|
||||
params.ciphertext_modulus,
|
||||
&mut engine.seeder,
|
||||
);
|
||||
|
||||
let thread_count = ShortintEngine::get_thread_count_for_multi_bit_pbs(
|
||||
input_lwe_secret_key.lwe_dimension(),
|
||||
params.glwe_dimension,
|
||||
params.polynomial_size,
|
||||
params.decomp_base_log,
|
||||
params.decomp_level_count,
|
||||
params.grouping_factor,
|
||||
);
|
||||
|
||||
Self::MultiBit {
|
||||
bsk: seeded_bsk,
|
||||
thread_count,
|
||||
deterministic_execution: params.deterministic_execution,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn decompress(&self) -> Shortint128BootstrappingKey<Scalar> {
|
||||
match self {
|
||||
Self::Classic {
|
||||
bsk,
|
||||
@@ -100,183 +184,23 @@ impl CompressedShortint128BootstrappingKey {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(CompressedNoiseSquashingKeyVersions)]
|
||||
pub struct CompressedNoiseSquashingKey {
|
||||
bootstrapping_key: CompressedShortint128BootstrappingKey,
|
||||
message_modulus: MessageModulus,
|
||||
carry_modulus: CarryModulus,
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
}
|
||||
|
||||
impl ClientKey {
|
||||
pub fn new_compressed_noise_squashing_key(
|
||||
&self,
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
) -> CompressedNoiseSquashingKey {
|
||||
let AtomicPatternClientKey::Standard(std_cks) = &self.atomic_pattern else {
|
||||
panic!("Only the standard atomic pattern supports noise squashing")
|
||||
};
|
||||
|
||||
let pbs_parameters = std_cks.parameters;
|
||||
|
||||
let noise_squashing_parameters = noise_squashing_private_key.noise_squashing_parameters();
|
||||
|
||||
assert_eq!(
|
||||
pbs_parameters.message_modulus(),
|
||||
noise_squashing_parameters.message_modulus(),
|
||||
"Mismatched MessageModulus between ClientKey {:?} and NoiseSquashingPrivateKey {:?}.",
|
||||
pbs_parameters.message_modulus(),
|
||||
noise_squashing_parameters.message_modulus()
|
||||
);
|
||||
assert_eq!(
|
||||
pbs_parameters.carry_modulus(),
|
||||
noise_squashing_parameters.carry_modulus(),
|
||||
"Mismatched CarryModulus between ClientKey {:?} and NoiseSquashingPrivateKey {:?}.",
|
||||
pbs_parameters.carry_modulus(),
|
||||
noise_squashing_parameters.carry_modulus()
|
||||
);
|
||||
|
||||
let bootstrapping_key = match noise_squashing_parameters {
|
||||
NoiseSquashingParameters::Classic(params) => {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let seeded_bsk = par_allocate_and_generate_new_seeded_lwe_bootstrap_key(
|
||||
&std_cks.lwe_secret_key,
|
||||
noise_squashing_private_key.post_noise_squashing_secret_key(),
|
||||
params.decomp_base_log,
|
||||
params.decomp_level_count,
|
||||
params.glwe_noise_distribution,
|
||||
params.ciphertext_modulus,
|
||||
&mut engine.seeder,
|
||||
);
|
||||
|
||||
let modulus_switch_noise_reduction_key = params
|
||||
.modulus_switch_noise_reduction_params
|
||||
.to_compressed_modulus_switch_configuration(
|
||||
&std_cks.lwe_secret_key,
|
||||
pbs_parameters.ciphertext_modulus(),
|
||||
pbs_parameters.lwe_noise_distribution(),
|
||||
engine,
|
||||
);
|
||||
|
||||
CompressedShortint128BootstrappingKey::Classic {
|
||||
bsk: seeded_bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
}
|
||||
})
|
||||
}
|
||||
NoiseSquashingParameters::MultiBit(params) => {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let seeded_bsk =
|
||||
par_allocate_and_generate_new_seeded_lwe_multi_bit_bootstrap_key(
|
||||
&std_cks.lwe_secret_key,
|
||||
noise_squashing_private_key.post_noise_squashing_secret_key(),
|
||||
params.decomp_base_log,
|
||||
params.decomp_level_count,
|
||||
params.glwe_noise_distribution,
|
||||
params.grouping_factor,
|
||||
params.ciphertext_modulus,
|
||||
&mut engine.seeder,
|
||||
);
|
||||
|
||||
let thread_count = ShortintEngine::get_thread_count_for_multi_bit_pbs(
|
||||
std_cks.lwe_secret_key.lwe_dimension(),
|
||||
params.glwe_dimension,
|
||||
params.polynomial_size,
|
||||
params.decomp_base_log,
|
||||
params.decomp_level_count,
|
||||
params.grouping_factor,
|
||||
);
|
||||
|
||||
CompressedShortint128BootstrappingKey::MultiBit {
|
||||
bsk: seeded_bsk,
|
||||
thread_count,
|
||||
deterministic_execution: params.deterministic_execution,
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
CompressedNoiseSquashingKey {
|
||||
bootstrapping_key,
|
||||
output_ciphertext_modulus: noise_squashing_parameters.ciphertext_modulus(),
|
||||
message_modulus: noise_squashing_parameters.message_modulus(),
|
||||
carry_modulus: noise_squashing_parameters.carry_modulus(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CompressedNoiseSquashingKey {
|
||||
pub fn new(
|
||||
client_key: &ClientKey,
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
) -> Self {
|
||||
client_key.new_compressed_noise_squashing_key(noise_squashing_private_key)
|
||||
}
|
||||
|
||||
pub fn from_raw_parts(
|
||||
bootstrapping_key: CompressedShortint128BootstrappingKey,
|
||||
message_modulus: MessageModulus,
|
||||
carry_modulus: CarryModulus,
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
) -> Self {
|
||||
Self {
|
||||
bootstrapping_key,
|
||||
message_modulus,
|
||||
carry_modulus,
|
||||
output_ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decompress(&self) -> NoiseSquashingKey {
|
||||
NoiseSquashingKey::from_raw_parts(
|
||||
self.bootstrapping_key.decompress(),
|
||||
self.message_modulus,
|
||||
self.carry_modulus,
|
||||
self.output_ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn bootstrapping_key(&self) -> &CompressedShortint128BootstrappingKey {
|
||||
&self.bootstrapping_key
|
||||
}
|
||||
|
||||
pub fn message_modulus(&self) -> MessageModulus {
|
||||
self.message_modulus
|
||||
}
|
||||
|
||||
pub fn carry_modulus(&self) -> CarryModulus {
|
||||
self.carry_modulus
|
||||
}
|
||||
|
||||
pub fn output_ciphertext_modulus(&self) -> CoreCiphertextModulus<u128> {
|
||||
self.output_ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl ParameterSetConformant for CompressedNoiseSquashingKey {
|
||||
type ParameterSet = NoiseSquashingKeyConformanceParams;
|
||||
impl<Scalar> ParameterSetConformant for CompressedShortint128BootstrappingKey<Scalar>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
{
|
||||
type ParameterSet = Shortint128BootstrappingKeyConformanceParams;
|
||||
|
||||
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
|
||||
let Self {
|
||||
bootstrapping_key,
|
||||
message_modulus,
|
||||
carry_modulus,
|
||||
output_ciphertext_modulus,
|
||||
} = self;
|
||||
|
||||
match (bootstrapping_key, parameter_set) {
|
||||
match (self, parameter_set) {
|
||||
(
|
||||
CompressedShortint128BootstrappingKey::Classic {
|
||||
Self::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
},
|
||||
NoiseSquashingKeyConformanceParams::Classic {
|
||||
Shortint128BootstrappingKeyConformanceParams::Classic {
|
||||
bootstrapping_key_params: expected_bootstrapping_key_params,
|
||||
modulus_switch_noise_reduction_params:
|
||||
expected_modulus_switch_noise_reduction_params,
|
||||
message_modulus: expected_message_modulus,
|
||||
carry_modulus: expected_carry_modulus,
|
||||
},
|
||||
) => {
|
||||
let lwe_dimension = bsk.input_lwe_dimension();
|
||||
@@ -308,28 +232,144 @@ impl ParameterSetConformant for CompressedNoiseSquashingKey {
|
||||
(_, _) => false,
|
||||
};
|
||||
|
||||
modulus_switch_key_ok
|
||||
&& bsk.is_conformant(expected_bootstrapping_key_params)
|
||||
&& *output_ciphertext_modulus
|
||||
== expected_bootstrapping_key_params.ciphertext_modulus
|
||||
&& *message_modulus == *expected_message_modulus
|
||||
&& *carry_modulus == *expected_carry_modulus
|
||||
modulus_switch_key_ok && bsk.is_conformant(expected_bootstrapping_key_params)
|
||||
}
|
||||
(
|
||||
CompressedShortint128BootstrappingKey::MultiBit { bsk, .. },
|
||||
NoiseSquashingKeyConformanceParams::MultiBit {
|
||||
Self::MultiBit { bsk, .. },
|
||||
Shortint128BootstrappingKeyConformanceParams::MultiBit {
|
||||
bootstrapping_key_params: expected_bootstrapping_key_params,
|
||||
message_modulus: expected_message_modulus,
|
||||
carry_modulus: expected_carry_modulus,
|
||||
},
|
||||
) => {
|
||||
bsk.is_conformant(expected_bootstrapping_key_params)
|
||||
&& *output_ciphertext_modulus
|
||||
== expected_bootstrapping_key_params.ciphertext_modulus
|
||||
&& *message_modulus == *expected_message_modulus
|
||||
&& *carry_modulus == *expected_carry_modulus
|
||||
}
|
||||
) => bsk.is_conformant(expected_bootstrapping_key_params),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(CompressedNoiseSquashingKeyVersions)]
|
||||
pub struct CompressedNoiseSquashingKey {
|
||||
atomic_pattern: CompressedAtomicPatternNoiseSquashingKey,
|
||||
message_modulus: MessageModulus,
|
||||
carry_modulus: CarryModulus,
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
}
|
||||
|
||||
impl ClientKey {
|
||||
pub fn new_compressed_noise_squashing_key(
|
||||
&self,
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
) -> CompressedNoiseSquashingKey {
|
||||
let compute_parameters = self.parameters();
|
||||
|
||||
let noise_squashing_parameters = noise_squashing_private_key.noise_squashing_parameters();
|
||||
|
||||
assert_eq!(
|
||||
compute_parameters.message_modulus(),
|
||||
noise_squashing_parameters.message_modulus(),
|
||||
"Mismatched MessageModulus between ClientKey {:?} and NoiseSquashingPrivateKey {:?}.",
|
||||
compute_parameters.message_modulus(),
|
||||
noise_squashing_parameters.message_modulus()
|
||||
);
|
||||
assert_eq!(
|
||||
compute_parameters.carry_modulus(),
|
||||
noise_squashing_parameters.carry_modulus(),
|
||||
"Mismatched CarryModulus between ClientKey {:?} and NoiseSquashingPrivateKey {:?}.",
|
||||
compute_parameters.carry_modulus(),
|
||||
noise_squashing_parameters.carry_modulus()
|
||||
);
|
||||
|
||||
let atomic_pattern = CompressedAtomicPatternNoiseSquashingKey::new(
|
||||
&self.atomic_pattern,
|
||||
noise_squashing_private_key,
|
||||
);
|
||||
|
||||
CompressedNoiseSquashingKey {
|
||||
atomic_pattern,
|
||||
output_ciphertext_modulus: noise_squashing_parameters.ciphertext_modulus(),
|
||||
message_modulus: noise_squashing_parameters.message_modulus(),
|
||||
carry_modulus: noise_squashing_parameters.carry_modulus(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CompressedNoiseSquashingKey {
|
||||
pub fn new(
|
||||
client_key: &ClientKey,
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
) -> Self {
|
||||
client_key.new_compressed_noise_squashing_key(noise_squashing_private_key)
|
||||
}
|
||||
|
||||
pub fn from_raw_parts(
|
||||
atomic_pattern: CompressedAtomicPatternNoiseSquashingKey,
|
||||
message_modulus: MessageModulus,
|
||||
carry_modulus: CarryModulus,
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
) -> Self {
|
||||
Self {
|
||||
atomic_pattern,
|
||||
message_modulus,
|
||||
carry_modulus,
|
||||
output_ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn atomic_pattern(&self) -> &CompressedAtomicPatternNoiseSquashingKey {
|
||||
&self.atomic_pattern
|
||||
}
|
||||
|
||||
pub fn decompress(&self) -> NoiseSquashingKey {
|
||||
NoiseSquashingKey::from_raw_parts(
|
||||
self.atomic_pattern.decompress(),
|
||||
self.message_modulus,
|
||||
self.carry_modulus,
|
||||
self.output_ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn message_modulus(&self) -> MessageModulus {
|
||||
self.message_modulus
|
||||
}
|
||||
|
||||
pub fn carry_modulus(&self) -> CarryModulus {
|
||||
self.carry_modulus
|
||||
}
|
||||
|
||||
pub fn output_ciphertext_modulus(&self) -> CoreCiphertextModulus<u128> {
|
||||
self.output_ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl ParameterSetConformant for CompressedNoiseSquashingKey {
|
||||
type ParameterSet = NoiseSquashingKeyConformanceParams;
|
||||
|
||||
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
|
||||
let Self {
|
||||
atomic_pattern,
|
||||
message_modulus,
|
||||
carry_modulus,
|
||||
output_ciphertext_modulus,
|
||||
} = self;
|
||||
|
||||
let bsk_conformant = match (atomic_pattern, parameter_set.atomic_pattern) {
|
||||
(
|
||||
CompressedAtomicPatternNoiseSquashingKey::Standard(compressed_std),
|
||||
AtomicPatternKind::Standard(_),
|
||||
) => compressed_std
|
||||
.bootstrapping_key()
|
||||
.is_conformant(¶meter_set.pbs_params),
|
||||
(
|
||||
CompressedAtomicPatternNoiseSquashingKey::KeySwitch32(compressed_ks32),
|
||||
AtomicPatternKind::KeySwitch32,
|
||||
) => compressed_ks32
|
||||
.bootstrapping_key()
|
||||
.is_conformant(¶meter_set.pbs_params),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
bsk_conformant
|
||||
&& *output_ciphertext_modulus == parameter_set.output_ciphertext_modulus
|
||||
&& *message_modulus == parameter_set.message_modulus
|
||||
&& *carry_modulus == parameter_set.carry_modulus
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod atomic_pattern;
|
||||
mod compressed_server_key;
|
||||
mod private_key;
|
||||
mod server_key;
|
||||
@@ -10,5 +11,7 @@ pub use compressed_server_key::{
|
||||
pub use private_key::NoiseSquashingPrivateKey;
|
||||
pub(crate) use private_key::NoiseSquashingPrivateKeyView;
|
||||
pub use server_key::{
|
||||
NoiseSquashingKey, NoiseSquashingKeyConformanceParams, Shortint128BootstrappingKey,
|
||||
GenericNoiseSquashingKey, NoiseSquashingKey, NoiseSquashingKeyConformanceParams,
|
||||
NoiseSquashingKeyView, Shortint128BootstrappingKey, StandardNoiseSquashingKey,
|
||||
StandardNoiseSquashingKeyView,
|
||||
};
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
use super::atomic_pattern::standard::StandardAtomicPatternNoiseSquashingKey;
|
||||
use super::atomic_pattern::{AtomicPatternNoiseSquashingKey, NoiseSquashingAtomicPattern};
|
||||
use super::NoiseSquashingPrivateKey;
|
||||
use crate::conformance::ParameterSetConformant;
|
||||
use crate::core_crypto::algorithms::lwe_bootstrap_key_conversion::par_convert_standard_lwe_bootstrap_key_to_fourier_128;
|
||||
use crate::core_crypto::algorithms::lwe_bootstrap_key_generation::par_allocate_and_generate_new_lwe_bootstrap_key;
|
||||
use crate::core_crypto::algorithms::lwe_keyswitch::keyswitch_lwe_ciphertext;
|
||||
use crate::core_crypto::algorithms::lwe_programmable_bootstrapping::{
|
||||
blind_rotate_f128_lwe_ciphertext_mem_optimized,
|
||||
blind_rotate_f128_lwe_ciphertext_mem_optimized_requirement,
|
||||
generate_programmable_bootstrap_glwe_lut,
|
||||
};
|
||||
use crate::core_crypto::entities::{Fourier128LweBootstrapKeyOwned, LweCiphertext};
|
||||
use crate::core_crypto::fft_impl::fft128::math::fft::Fft128;
|
||||
use crate::core_crypto::commons::math::random::Uniform;
|
||||
use crate::core_crypto::entities::Fourier128LweBootstrapKeyOwned;
|
||||
use crate::core_crypto::fft_impl::fft64::crypto::bootstrap::LweBootstrapKeyConformanceParams;
|
||||
use crate::core_crypto::prelude::fft128_lwe_multi_bit_bootstrap_key::Fourier128LweMultiBitBootstrapKeyOwned;
|
||||
use crate::core_crypto::prelude::{
|
||||
multi_bit_programmable_bootstrap_f128_lwe_ciphertext,
|
||||
par_allocate_and_generate_new_lwe_multi_bit_bootstrap_key,
|
||||
par_convert_standard_lwe_multi_bit_bootstrap_key_to_fourier_128, GlweSize,
|
||||
MultiBitBootstrapKeyConformanceParams, ThreadCount,
|
||||
par_convert_standard_lwe_multi_bit_bootstrap_key_to_fourier_128, CastFrom, Container,
|
||||
DynamicDistribution, Encryptable, GlweSize, LweSecretKey,
|
||||
MultiBitBootstrapKeyConformanceParams, ThreadCount, UnsignedInteger,
|
||||
};
|
||||
use crate::shortint::atomic_pattern::{AtomicPattern, AtomicPatternParameters};
|
||||
use crate::shortint::atomic_pattern::AtomicPatternParameters;
|
||||
use crate::shortint::backward_compatibility::noise_squashing::{
|
||||
NoiseSquashingKeyVersions, Shortint128BootstrappingKeyVersions,
|
||||
};
|
||||
use crate::shortint::ciphertext::{Ciphertext, SquashedNoiseCiphertext};
|
||||
use crate::shortint::client_key::atomic_pattern::AtomicPatternClientKey;
|
||||
use crate::shortint::client_key::ClientKey;
|
||||
use crate::shortint::encoding::{compute_delta, PaddingBit};
|
||||
use crate::shortint::engine::ShortintEngine;
|
||||
use crate::shortint::parameters::noise_squashing::NoiseSquashingParameters;
|
||||
use crate::shortint::parameters::{
|
||||
CarryModulus, CoreCiphertextModulus, MessageModulus, ModulusSwitchType, PBSOrder, PBSParameters,
|
||||
CarryModulus, CoreCiphertextModulus, KeySwitch32PBSParameters, MessageModulus,
|
||||
ModulusSwitchType, PBSParameters,
|
||||
};
|
||||
use crate::shortint::prelude::{LweDimension, PolynomialSize};
|
||||
use crate::shortint::server_key::{
|
||||
ModulusSwitchConfiguration, ModulusSwitchNoiseReductionKeyConformanceParams, ServerKey,
|
||||
StandardServerKeyView,
|
||||
UnsupportedOperation,
|
||||
};
|
||||
use crate::shortint::AtomicPatternKind;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
/// A 128b bootstrapping key that can be used for the noise squashing operation
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(Shortint128BootstrappingKeyVersions)]
|
||||
pub enum Shortint128BootstrappingKey {
|
||||
pub enum Shortint128BootstrappingKey<Scalar>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
{
|
||||
Classic {
|
||||
bsk: Fourier128LweBootstrapKeyOwned,
|
||||
modulus_switch_noise_reduction_key: ModulusSwitchConfiguration<u64>,
|
||||
modulus_switch_noise_reduction_key: ModulusSwitchConfiguration<Scalar>,
|
||||
},
|
||||
MultiBit {
|
||||
bsk: Fourier128LweMultiBitBootstrapKeyOwned,
|
||||
@@ -53,71 +53,27 @@ pub enum Shortint128BootstrappingKey {
|
||||
},
|
||||
}
|
||||
|
||||
impl Shortint128BootstrappingKey {
|
||||
pub fn output_lwe_dimension(&self) -> LweDimension {
|
||||
match self {
|
||||
Self::Classic { bsk, .. } => bsk.output_lwe_dimension(),
|
||||
Self::MultiBit { bsk, .. } => bsk.output_lwe_dimension(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glwe_size(&self) -> GlweSize {
|
||||
match self {
|
||||
Self::Classic { bsk, .. } => bsk.glwe_size(),
|
||||
Self::MultiBit { bsk, .. } => bsk.glwe_size(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
match self {
|
||||
Self::Classic { bsk, .. } => bsk.polynomial_size(),
|
||||
Self::MultiBit { bsk, .. } => bsk.polynomial_size(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(NoiseSquashingKeyVersions)]
|
||||
pub struct NoiseSquashingKey {
|
||||
bootstrapping_key: Shortint128BootstrappingKey,
|
||||
message_modulus: MessageModulus,
|
||||
carry_modulus: CarryModulus,
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
}
|
||||
|
||||
impl ClientKey {
|
||||
pub fn new_noise_squashing_key(
|
||||
&self,
|
||||
impl<Scalar> Shortint128BootstrappingKey<Scalar>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
{
|
||||
pub(crate) fn new<InputKeyCont>(
|
||||
input_lwe_secret_key: &LweSecretKey<InputKeyCont>,
|
||||
ciphertext_modulus: CoreCiphertextModulus<Scalar>,
|
||||
lwe_noise_distribution: DynamicDistribution<Scalar>,
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
) -> NoiseSquashingKey {
|
||||
let AtomicPatternClientKey::Standard(std_cks) = &self.atomic_pattern else {
|
||||
panic!("Only the standard atomic pattern supports noise squashing")
|
||||
};
|
||||
|
||||
let pbs_parameters = std_cks.parameters;
|
||||
|
||||
) -> Self
|
||||
where
|
||||
InputKeyCont: Container<Element = Scalar> + Sync,
|
||||
Scalar: Encryptable<Uniform, DynamicDistribution<Scalar>> + CastFrom<usize>,
|
||||
{
|
||||
let noise_squashing_parameters = noise_squashing_private_key.noise_squashing_parameters();
|
||||
|
||||
assert_eq!(
|
||||
pbs_parameters.message_modulus(),
|
||||
noise_squashing_parameters.message_modulus(),
|
||||
"Incompatible MessageModulus ClientKey {:?}, NoiseSquashingPrivateKey {:?}.",
|
||||
pbs_parameters.message_modulus(),
|
||||
noise_squashing_parameters.message_modulus(),
|
||||
);
|
||||
assert_eq!(
|
||||
pbs_parameters.carry_modulus(),
|
||||
noise_squashing_parameters.carry_modulus(),
|
||||
"Incompatible CarryModulus ClientKey {:?}, NoiseSquashingPrivateKey {:?}",
|
||||
pbs_parameters.carry_modulus(),
|
||||
noise_squashing_parameters.carry_modulus(),
|
||||
);
|
||||
|
||||
let bootstrapping_key = match noise_squashing_parameters {
|
||||
match noise_squashing_parameters {
|
||||
NoiseSquashingParameters::Classic(params) => {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let std_bsk = par_allocate_and_generate_new_lwe_bootstrap_key(
|
||||
&std_cks.lwe_secret_key,
|
||||
input_lwe_secret_key,
|
||||
noise_squashing_private_key.post_noise_squashing_secret_key(),
|
||||
params.decomp_base_log,
|
||||
params.decomp_level_count,
|
||||
@@ -139,13 +95,13 @@ impl ClientKey {
|
||||
let modulus_switch_noise_reduction_key = params
|
||||
.modulus_switch_noise_reduction_params
|
||||
.to_modulus_switch_configuration(
|
||||
&std_cks.lwe_secret_key,
|
||||
pbs_parameters.ciphertext_modulus(),
|
||||
pbs_parameters.lwe_noise_distribution(),
|
||||
input_lwe_secret_key,
|
||||
ciphertext_modulus,
|
||||
lwe_noise_distribution,
|
||||
engine,
|
||||
);
|
||||
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
Self::Classic {
|
||||
bsk: fbsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
}
|
||||
@@ -154,7 +110,7 @@ impl ClientKey {
|
||||
NoiseSquashingParameters::MultiBit(params) => {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let std_bsk = par_allocate_and_generate_new_lwe_multi_bit_bootstrap_key(
|
||||
&std_cks.lwe_secret_key,
|
||||
input_lwe_secret_key,
|
||||
noise_squashing_private_key.post_noise_squashing_secret_key(),
|
||||
params.decomp_base_log,
|
||||
params.decomp_level_count,
|
||||
@@ -178,7 +134,7 @@ impl ClientKey {
|
||||
);
|
||||
|
||||
let thread_count = ShortintEngine::get_thread_count_for_multi_bit_pbs(
|
||||
std_cks.lwe_secret_key.lwe_dimension(),
|
||||
input_lwe_secret_key.lwe_dimension(),
|
||||
params.glwe_dimension,
|
||||
params.polynomial_size,
|
||||
params.decomp_base_log,
|
||||
@@ -186,20 +142,174 @@ impl ClientKey {
|
||||
params.grouping_factor,
|
||||
);
|
||||
|
||||
Shortint128BootstrappingKey::MultiBit {
|
||||
Self::MultiBit {
|
||||
bsk: fbsk,
|
||||
thread_count,
|
||||
deterministic_execution: params.deterministic_execution,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output_lwe_dimension(&self) -> LweDimension {
|
||||
match self {
|
||||
Self::Classic { bsk, .. } => bsk.output_lwe_dimension(),
|
||||
Self::MultiBit { bsk, .. } => bsk.output_lwe_dimension(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glwe_size(&self) -> GlweSize {
|
||||
match self {
|
||||
Self::Classic { bsk, .. } => bsk.glwe_size(),
|
||||
Self::MultiBit { bsk, .. } => bsk.glwe_size(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
match self {
|
||||
Self::Classic { bsk, .. } => bsk.polynomial_size(),
|
||||
Self::MultiBit { bsk, .. } => bsk.polynomial_size(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Shortint128BootstrappingKeyConformanceParams {
|
||||
Classic {
|
||||
bootstrapping_key_params: LweBootstrapKeyConformanceParams<u128>,
|
||||
modulus_switch_noise_reduction_params: ModulusSwitchType,
|
||||
},
|
||||
MultiBit {
|
||||
bootstrapping_key_params: MultiBitBootstrapKeyConformanceParams<u128>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<Scalar> ParameterSetConformant for Shortint128BootstrappingKey<Scalar>
|
||||
where
|
||||
Scalar: UnsignedInteger,
|
||||
{
|
||||
type ParameterSet = Shortint128BootstrappingKeyConformanceParams;
|
||||
|
||||
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
|
||||
match (self, parameter_set) {
|
||||
(
|
||||
Self::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
},
|
||||
Shortint128BootstrappingKeyConformanceParams::Classic {
|
||||
bootstrapping_key_params: expected_bootstrapping_key_params,
|
||||
modulus_switch_noise_reduction_params:
|
||||
expected_modulus_switch_noise_reduction_params,
|
||||
},
|
||||
) => {
|
||||
let lwe_dimension = bsk.input_lwe_dimension();
|
||||
|
||||
let modulus_switch_key_ok = match (
|
||||
modulus_switch_noise_reduction_key,
|
||||
expected_modulus_switch_noise_reduction_params,
|
||||
) {
|
||||
(ModulusSwitchConfiguration::Standard, ModulusSwitchType::Standard) => true,
|
||||
(
|
||||
ModulusSwitchConfiguration::CenteredMeanNoiseReduction,
|
||||
ModulusSwitchType::CenteredMeanNoiseReduction,
|
||||
) => true,
|
||||
(
|
||||
ModulusSwitchConfiguration::DriftTechniqueNoiseReduction(key),
|
||||
ModulusSwitchType::DriftTechniqueNoiseReduction(params),
|
||||
) => {
|
||||
let mod_switch_conformance_params =
|
||||
ModulusSwitchNoiseReductionKeyConformanceParams {
|
||||
modulus_switch_noise_reduction_params: *params,
|
||||
lwe_dimension,
|
||||
};
|
||||
|
||||
key.is_conformant(&mod_switch_conformance_params)
|
||||
}
|
||||
(_, _) => false,
|
||||
};
|
||||
|
||||
modulus_switch_key_ok && bsk.is_conformant(expected_bootstrapping_key_params)
|
||||
}
|
||||
(
|
||||
Self::MultiBit { bsk, .. },
|
||||
Shortint128BootstrappingKeyConformanceParams::MultiBit {
|
||||
bootstrapping_key_params: expected_bootstrapping_key_params,
|
||||
},
|
||||
) => bsk.is_conformant(expected_bootstrapping_key_params),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A server key that can be used for any noise squashing atomic patterns
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(NoiseSquashingKeyVersions)]
|
||||
pub struct GenericNoiseSquashingKey<AP> {
|
||||
atomic_pattern: AP,
|
||||
message_modulus: MessageModulus,
|
||||
carry_modulus: CarryModulus,
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
}
|
||||
|
||||
pub type NoiseSquashingKey = GenericNoiseSquashingKey<AtomicPatternNoiseSquashingKey>;
|
||||
pub type StandardNoiseSquashingKey =
|
||||
GenericNoiseSquashingKey<StandardAtomicPatternNoiseSquashingKey>;
|
||||
pub type NoiseSquashingKeyView<'key> =
|
||||
GenericNoiseSquashingKey<&'key AtomicPatternNoiseSquashingKey>;
|
||||
pub type StandardNoiseSquashingKeyView<'key> =
|
||||
GenericNoiseSquashingKey<&'key StandardAtomicPatternNoiseSquashingKey>;
|
||||
|
||||
impl<'key> TryFrom<NoiseSquashingKeyView<'key>> for StandardNoiseSquashingKeyView<'key> {
|
||||
type Error = UnsupportedOperation;
|
||||
|
||||
fn try_from(value: NoiseSquashingKeyView<'key>) -> Result<Self, Self::Error> {
|
||||
let AtomicPatternNoiseSquashingKey::Standard(atomic_pattern) = value.atomic_pattern else {
|
||||
return Err(UnsupportedOperation);
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
atomic_pattern,
|
||||
message_modulus: value.message_modulus,
|
||||
carry_modulus: value.carry_modulus,
|
||||
output_ciphertext_modulus: value.output_ciphertext_modulus,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientKey {
|
||||
pub fn new_noise_squashing_key(
|
||||
&self,
|
||||
noise_squashing_private_key: &NoiseSquashingPrivateKey,
|
||||
) -> NoiseSquashingKey {
|
||||
let compute_parameters = self.parameters();
|
||||
|
||||
let noise_squashing_parameters = noise_squashing_private_key.noise_squashing_parameters();
|
||||
|
||||
assert_eq!(
|
||||
compute_parameters.message_modulus(),
|
||||
noise_squashing_parameters.message_modulus(),
|
||||
"Incompatible MessageModulus ClientKey {:?}, NoiseSquashingPrivateKey {:?}.",
|
||||
compute_parameters.message_modulus(),
|
||||
noise_squashing_parameters.message_modulus(),
|
||||
);
|
||||
assert_eq!(
|
||||
compute_parameters.carry_modulus(),
|
||||
noise_squashing_parameters.carry_modulus(),
|
||||
"Incompatible CarryModulus ClientKey {:?}, NoiseSquashingPrivateKey {:?}",
|
||||
compute_parameters.carry_modulus(),
|
||||
noise_squashing_parameters.carry_modulus(),
|
||||
);
|
||||
|
||||
let atomic_pattern =
|
||||
AtomicPatternNoiseSquashingKey::new(&self.atomic_pattern, noise_squashing_private_key);
|
||||
|
||||
NoiseSquashingKey {
|
||||
bootstrapping_key,
|
||||
output_ciphertext_modulus: noise_squashing_parameters.ciphertext_modulus(),
|
||||
atomic_pattern,
|
||||
message_modulus: noise_squashing_parameters.message_modulus(),
|
||||
carry_modulus: noise_squashing_parameters.carry_modulus(),
|
||||
output_ciphertext_modulus: noise_squashing_parameters.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,14 +322,31 @@ impl NoiseSquashingKey {
|
||||
client_key.new_noise_squashing_key(noise_squashing_private_key)
|
||||
}
|
||||
|
||||
pub fn as_view(&self) -> NoiseSquashingKeyView<'_> {
|
||||
GenericNoiseSquashingKey {
|
||||
atomic_pattern: &self.atomic_pattern,
|
||||
message_modulus: self.message_modulus,
|
||||
carry_modulus: self.carry_modulus,
|
||||
output_ciphertext_modulus: self.output_ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StandardNoiseSquashingKeyView<'_> {
|
||||
pub fn bootstrapping_key(&self) -> &Shortint128BootstrappingKey<u64> {
|
||||
self.atomic_pattern.bootstrapping_key()
|
||||
}
|
||||
}
|
||||
|
||||
impl<AP> GenericNoiseSquashingKey<AP> {
|
||||
pub fn from_raw_parts(
|
||||
bootstrapping_key: Shortint128BootstrappingKey,
|
||||
atomic_pattern: AP,
|
||||
message_modulus: MessageModulus,
|
||||
carry_modulus: CarryModulus,
|
||||
output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
) -> Self {
|
||||
Self {
|
||||
bootstrapping_key,
|
||||
atomic_pattern,
|
||||
message_modulus,
|
||||
carry_modulus,
|
||||
output_ciphertext_modulus,
|
||||
@@ -229,26 +356,44 @@ impl NoiseSquashingKey {
|
||||
pub fn into_raw_parts(
|
||||
self,
|
||||
) -> (
|
||||
Shortint128BootstrappingKey,
|
||||
AP,
|
||||
MessageModulus,
|
||||
CarryModulus,
|
||||
CoreCiphertextModulus<u128>,
|
||||
) {
|
||||
let Self {
|
||||
bootstrapping_key,
|
||||
atomic_pattern,
|
||||
message_modulus,
|
||||
carry_modulus,
|
||||
output_ciphertext_modulus,
|
||||
} = self;
|
||||
|
||||
(
|
||||
bootstrapping_key,
|
||||
atomic_pattern,
|
||||
message_modulus,
|
||||
carry_modulus,
|
||||
output_ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn atomic_pattern(&self) -> &AP {
|
||||
&self.atomic_pattern
|
||||
}
|
||||
|
||||
pub fn message_modulus(&self) -> MessageModulus {
|
||||
self.message_modulus
|
||||
}
|
||||
|
||||
pub fn carry_modulus(&self) -> CarryModulus {
|
||||
self.carry_modulus
|
||||
}
|
||||
|
||||
pub fn output_ciphertext_modulus(&self) -> CoreCiphertextModulus<u128> {
|
||||
self.output_ciphertext_modulus
|
||||
}
|
||||
}
|
||||
|
||||
impl<AP: NoiseSquashingAtomicPattern> GenericNoiseSquashingKey<AP> {
|
||||
pub fn squash_ciphertext_noise(
|
||||
&self,
|
||||
ciphertext: &Ciphertext,
|
||||
@@ -293,173 +438,28 @@ impl NoiseSquashingKey {
|
||||
));
|
||||
}
|
||||
|
||||
// For the moment, noise squashing is only implemented for the Standard AP
|
||||
let src_server_key: StandardServerKeyView =
|
||||
src_server_key.as_view().try_into().map_err(|_| {
|
||||
crate::error!(
|
||||
"Noise squashing is not supported by the selected atomic pattern ({:?})",
|
||||
src_server_key.atomic_pattern.kind()
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(self.unchecked_squash_ciphertext_noise(ciphertext, src_server_key))
|
||||
}
|
||||
|
||||
pub fn unchecked_squash_ciphertext_noise(
|
||||
&self,
|
||||
ciphertext: &Ciphertext,
|
||||
src_server_key: StandardServerKeyView,
|
||||
) -> SquashedNoiseCiphertext {
|
||||
let lwe_before_ms = match src_server_key.atomic_pattern.pbs_order {
|
||||
// Under the big key, first need to keyswitch
|
||||
PBSOrder::KeyswitchBootstrap => {
|
||||
let mut after_ks_ct = LweCiphertext::new(
|
||||
0u64,
|
||||
src_server_key
|
||||
.atomic_pattern
|
||||
.key_switching_key
|
||||
.output_lwe_size(),
|
||||
src_server_key
|
||||
.atomic_pattern
|
||||
.key_switching_key
|
||||
.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
keyswitch_lwe_ciphertext(
|
||||
&src_server_key.atomic_pattern.key_switching_key,
|
||||
&ciphertext.ct,
|
||||
&mut after_ks_ct,
|
||||
);
|
||||
after_ks_ct
|
||||
}
|
||||
// Under the small key, no need to keyswitch
|
||||
PBSOrder::BootstrapKeyswitch => ciphertext.ct.clone(),
|
||||
};
|
||||
|
||||
let output_lwe_size = self.bootstrapping_key.output_lwe_dimension().to_lwe_size();
|
||||
let output_message_modulus = self.message_modulus;
|
||||
let output_carry_modulus = self.carry_modulus;
|
||||
let output_ciphertext_modulus = self.output_ciphertext_modulus;
|
||||
|
||||
let mut res = SquashedNoiseCiphertext::new_zero(
|
||||
output_lwe_size,
|
||||
output_ciphertext_modulus,
|
||||
output_message_modulus,
|
||||
output_carry_modulus,
|
||||
);
|
||||
|
||||
let bsk_glwe_size = self.bootstrapping_key.glwe_size();
|
||||
let bsk_polynomial_size = self.bootstrapping_key.polynomial_size();
|
||||
|
||||
let delta = compute_delta(
|
||||
output_ciphertext_modulus,
|
||||
output_message_modulus,
|
||||
output_carry_modulus,
|
||||
PaddingBit::Yes,
|
||||
);
|
||||
|
||||
let output_cleartext_space = output_message_modulus.0 * output_carry_modulus.0;
|
||||
|
||||
let id_lut = generate_programmable_bootstrap_glwe_lut(
|
||||
bsk_polynomial_size,
|
||||
bsk_glwe_size,
|
||||
output_cleartext_space.try_into().unwrap(),
|
||||
output_ciphertext_modulus,
|
||||
delta,
|
||||
|x| x,
|
||||
);
|
||||
|
||||
match &self.bootstrapping_key {
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
} => {
|
||||
let bsk_glwe_size = bsk.glwe_size();
|
||||
let bsk_polynomial_size = bsk.polynomial_size();
|
||||
|
||||
let fft = Fft128::new(bsk_polynomial_size);
|
||||
let fft = fft.as_view();
|
||||
|
||||
let mem_requirement = blind_rotate_f128_lwe_ciphertext_mem_optimized_requirement::<
|
||||
u128,
|
||||
>(bsk_glwe_size, bsk_polynomial_size, fft)
|
||||
.unwrap()
|
||||
.try_unaligned_bytes_required()
|
||||
.unwrap();
|
||||
|
||||
let br_input_modulus_log =
|
||||
bsk.polynomial_size().to_blind_rotation_input_modulus_log();
|
||||
let lwe_ciphertext_to_squash_noise = modulus_switch_noise_reduction_key
|
||||
.lwe_ciphertext_modulus_switch(&lwe_before_ms, br_input_modulus_log);
|
||||
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let buffers = engine.get_computation_buffers();
|
||||
buffers.resize(mem_requirement);
|
||||
|
||||
blind_rotate_f128_lwe_ciphertext_mem_optimized(
|
||||
&lwe_ciphertext_to_squash_noise,
|
||||
res.lwe_ciphertext_mut(),
|
||||
&id_lut,
|
||||
bsk,
|
||||
fft,
|
||||
buffers.stack(),
|
||||
);
|
||||
});
|
||||
}
|
||||
Shortint128BootstrappingKey::MultiBit {
|
||||
bsk,
|
||||
thread_count,
|
||||
deterministic_execution,
|
||||
} => {
|
||||
multi_bit_programmable_bootstrap_f128_lwe_ciphertext(
|
||||
&lwe_before_ms,
|
||||
res.lwe_ciphertext_mut(),
|
||||
&id_lut,
|
||||
bsk,
|
||||
*thread_count,
|
||||
*deterministic_execution,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
res.set_degree(ciphertext.degree);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn bootstrapping_key(&self) -> &Shortint128BootstrappingKey {
|
||||
&self.bootstrapping_key
|
||||
}
|
||||
|
||||
pub fn message_modulus(&self) -> MessageModulus {
|
||||
self.message_modulus
|
||||
}
|
||||
|
||||
pub fn carry_modulus(&self) -> CarryModulus {
|
||||
self.carry_modulus
|
||||
}
|
||||
|
||||
pub fn output_ciphertext_modulus(&self) -> CoreCiphertextModulus<u128> {
|
||||
self.output_ciphertext_modulus
|
||||
self.atomic_pattern.squash_ciphertext_noise(
|
||||
ciphertext,
|
||||
src_server_key.as_view(),
|
||||
self.message_modulus,
|
||||
self.carry_modulus,
|
||||
self.output_ciphertext_modulus,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum NoiseSquashingKeyConformanceParams {
|
||||
Classic {
|
||||
bootstrapping_key_params: LweBootstrapKeyConformanceParams<u128>,
|
||||
modulus_switch_noise_reduction_params: ModulusSwitchType,
|
||||
message_modulus: MessageModulus,
|
||||
carry_modulus: CarryModulus,
|
||||
},
|
||||
MultiBit {
|
||||
bootstrapping_key_params: MultiBitBootstrapKeyConformanceParams<u128>,
|
||||
message_modulus: MessageModulus,
|
||||
carry_modulus: CarryModulus,
|
||||
},
|
||||
pub struct NoiseSquashingKeyConformanceParams {
|
||||
pub(super) pbs_params: Shortint128BootstrappingKeyConformanceParams,
|
||||
pub(super) atomic_pattern: AtomicPatternKind,
|
||||
pub(super) message_modulus: MessageModulus,
|
||||
pub(super) carry_modulus: CarryModulus,
|
||||
pub(super) output_ciphertext_modulus: CoreCiphertextModulus<u128>,
|
||||
}
|
||||
|
||||
impl TryFrom<(PBSParameters, NoiseSquashingParameters)> for NoiseSquashingKeyConformanceParams {
|
||||
impl TryFrom<(PBSParameters, NoiseSquashingParameters)>
|
||||
for Shortint128BootstrappingKeyConformanceParams
|
||||
{
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(
|
||||
@@ -471,7 +471,7 @@ impl TryFrom<(PBSParameters, NoiseSquashingParameters)> for NoiseSquashingKeyCon
|
||||
return Err(crate::Error::new(format!(
|
||||
"Incompatible MessageModulus (PBS {:?}, NoiseSquashing {:?}) \
|
||||
or CarryModulus (PBS {:?}, NoiseSquashing {:?}) \
|
||||
when creating NoiseSquashingKeyConformanceParams",
|
||||
when creating Shortint128BootstrappingKeyConformanceParams",
|
||||
pbs_params.message_modulus(),
|
||||
noise_squashing_params.message_modulus(),
|
||||
pbs_params.carry_modulus(),
|
||||
@@ -479,8 +479,8 @@ impl TryFrom<(PBSParameters, NoiseSquashingParameters)> for NoiseSquashingKeyCon
|
||||
)));
|
||||
}
|
||||
|
||||
match noise_squashing_params {
|
||||
NoiseSquashingParameters::Classic(params) => Ok(Self::Classic {
|
||||
Ok(match noise_squashing_params {
|
||||
NoiseSquashingParameters::Classic(params) => Self::Classic {
|
||||
bootstrapping_key_params: LweBootstrapKeyConformanceParams {
|
||||
input_lwe_dimension: pbs_params.lwe_dimension(),
|
||||
output_glwe_size: params.glwe_dimension.to_glwe_size(),
|
||||
@@ -490,10 +490,8 @@ impl TryFrom<(PBSParameters, NoiseSquashingParameters)> for NoiseSquashingKeyCon
|
||||
ciphertext_modulus: params.ciphertext_modulus,
|
||||
},
|
||||
modulus_switch_noise_reduction_params: params.modulus_switch_noise_reduction_params,
|
||||
message_modulus: params.message_modulus,
|
||||
carry_modulus: params.carry_modulus,
|
||||
}),
|
||||
NoiseSquashingParameters::MultiBit(params) => Ok(Self::MultiBit {
|
||||
},
|
||||
NoiseSquashingParameters::MultiBit(params) => Self::MultiBit {
|
||||
bootstrapping_key_params: MultiBitBootstrapKeyConformanceParams {
|
||||
input_lwe_dimension: pbs_params.lwe_dimension(),
|
||||
output_glwe_size: params.glwe_dimension.to_glwe_size(),
|
||||
@@ -503,10 +501,57 @@ impl TryFrom<(PBSParameters, NoiseSquashingParameters)> for NoiseSquashingKeyCon
|
||||
grouping_factor: params.grouping_factor,
|
||||
ciphertext_modulus: params.ciphertext_modulus,
|
||||
},
|
||||
message_modulus: params.message_modulus,
|
||||
carry_modulus: params.carry_modulus,
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<(KeySwitch32PBSParameters, NoiseSquashingParameters)>
|
||||
for Shortint128BootstrappingKeyConformanceParams
|
||||
{
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(
|
||||
(pbs_params, noise_squashing_params): (KeySwitch32PBSParameters, NoiseSquashingParameters),
|
||||
) -> Result<Self, Self::Error> {
|
||||
if pbs_params.message_modulus() != noise_squashing_params.message_modulus()
|
||||
|| pbs_params.carry_modulus() != noise_squashing_params.carry_modulus()
|
||||
{
|
||||
return Err(crate::Error::new(format!(
|
||||
"Incompatible MessageModulus (PBS {:?}, NoiseSquashing {:?}) \
|
||||
or CarryModulus (PBS {:?}, NoiseSquashing {:?}) \
|
||||
when creating Shortint128BootstrappingKeyConformanceParams",
|
||||
pbs_params.message_modulus(),
|
||||
noise_squashing_params.message_modulus(),
|
||||
pbs_params.carry_modulus(),
|
||||
noise_squashing_params.carry_modulus()
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(match noise_squashing_params {
|
||||
NoiseSquashingParameters::Classic(params) => Self::Classic {
|
||||
bootstrapping_key_params: LweBootstrapKeyConformanceParams {
|
||||
input_lwe_dimension: pbs_params.lwe_dimension(),
|
||||
output_glwe_size: params.glwe_dimension.to_glwe_size(),
|
||||
polynomial_size: params.polynomial_size,
|
||||
decomp_base_log: params.decomp_base_log,
|
||||
decomp_level_count: params.decomp_level_count,
|
||||
ciphertext_modulus: params.ciphertext_modulus,
|
||||
},
|
||||
modulus_switch_noise_reduction_params: params.modulus_switch_noise_reduction_params,
|
||||
},
|
||||
NoiseSquashingParameters::MultiBit(params) => Self::MultiBit {
|
||||
bootstrapping_key_params: MultiBitBootstrapKeyConformanceParams {
|
||||
input_lwe_dimension: pbs_params.lwe_dimension(),
|
||||
output_glwe_size: params.glwe_dimension.to_glwe_size(),
|
||||
polynomial_size: params.polynomial_size,
|
||||
decomp_base_log: params.decomp_base_log,
|
||||
decomp_level_count: params.decomp_level_count,
|
||||
grouping_factor: params.grouping_factor,
|
||||
ciphertext_modulus: params.ciphertext_modulus,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,14 +563,36 @@ impl TryFrom<(AtomicPatternParameters, NoiseSquashingParameters)>
|
||||
fn try_from(
|
||||
(ap_params, noise_squashing_params): (AtomicPatternParameters, NoiseSquashingParameters),
|
||||
) -> Result<Self, Self::Error> {
|
||||
match ap_params {
|
||||
AtomicPatternParameters::Standard(pbs_params) => {
|
||||
(pbs_params, noise_squashing_params).try_into()
|
||||
}
|
||||
AtomicPatternParameters::KeySwitch32(_) => Err(crate::Error::from(
|
||||
"Noise squashing is not supported by the KS32 Atomic Pattern",
|
||||
)),
|
||||
if ap_params.message_modulus() != noise_squashing_params.message_modulus()
|
||||
|| ap_params.carry_modulus() != noise_squashing_params.carry_modulus()
|
||||
{
|
||||
return Err(crate::Error::new(format!(
|
||||
"Incompatible MessageModulus (PBS {:?}, NoiseSquashing {:?}) \
|
||||
or CarryModulus (PBS {:?}, NoiseSquashing {:?}) \
|
||||
when creating NoiseSquashingKeyConformanceParams",
|
||||
ap_params.message_modulus(),
|
||||
noise_squashing_params.message_modulus(),
|
||||
ap_params.carry_modulus(),
|
||||
noise_squashing_params.carry_modulus()
|
||||
)));
|
||||
}
|
||||
|
||||
let noise_squashing_pbs_params = match ap_params {
|
||||
AtomicPatternParameters::Standard(pbs_params) => {
|
||||
(pbs_params, noise_squashing_params).try_into()?
|
||||
}
|
||||
AtomicPatternParameters::KeySwitch32(ks32_params) => {
|
||||
(ks32_params, noise_squashing_params).try_into()?
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
pbs_params: noise_squashing_pbs_params,
|
||||
atomic_pattern: ap_params.atomic_pattern(),
|
||||
message_modulus: ap_params.message_modulus(),
|
||||
carry_modulus: ap_params.carry_modulus(),
|
||||
output_ciphertext_modulus: noise_squashing_params.ciphertext_modulus(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,74 +601,30 @@ impl ParameterSetConformant for NoiseSquashingKey {
|
||||
|
||||
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
|
||||
let Self {
|
||||
bootstrapping_key,
|
||||
atomic_pattern,
|
||||
message_modulus,
|
||||
carry_modulus,
|
||||
output_ciphertext_modulus,
|
||||
} = self;
|
||||
|
||||
match (bootstrapping_key, parameter_set) {
|
||||
(
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
},
|
||||
NoiseSquashingKeyConformanceParams::Classic {
|
||||
bootstrapping_key_params: expected_bootstrapping_key_params,
|
||||
modulus_switch_noise_reduction_params:
|
||||
expected_modulus_switch_noise_reduction_params,
|
||||
message_modulus: expected_message_modulus,
|
||||
carry_modulus: expected_carry_modulus,
|
||||
},
|
||||
) => {
|
||||
let lwe_dimension = bsk.input_lwe_dimension();
|
||||
|
||||
let modulus_switch_key_ok = match (
|
||||
modulus_switch_noise_reduction_key,
|
||||
expected_modulus_switch_noise_reduction_params,
|
||||
) {
|
||||
(ModulusSwitchConfiguration::Standard, ModulusSwitchType::Standard) => true,
|
||||
(
|
||||
ModulusSwitchConfiguration::CenteredMeanNoiseReduction,
|
||||
ModulusSwitchType::CenteredMeanNoiseReduction,
|
||||
) => true,
|
||||
(
|
||||
ModulusSwitchConfiguration::DriftTechniqueNoiseReduction(key),
|
||||
ModulusSwitchType::DriftTechniqueNoiseReduction(params),
|
||||
) => {
|
||||
let mod_switch_conformance_params =
|
||||
ModulusSwitchNoiseReductionKeyConformanceParams {
|
||||
modulus_switch_noise_reduction_params: *params,
|
||||
lwe_dimension,
|
||||
};
|
||||
|
||||
key.is_conformant(&mod_switch_conformance_params)
|
||||
}
|
||||
(_, _) => false,
|
||||
};
|
||||
|
||||
modulus_switch_key_ok
|
||||
&& bsk.is_conformant(expected_bootstrapping_key_params)
|
||||
&& *output_ciphertext_modulus
|
||||
== expected_bootstrapping_key_params.ciphertext_modulus
|
||||
&& *message_modulus == *expected_message_modulus
|
||||
&& *carry_modulus == *expected_carry_modulus
|
||||
let bsk_conformant = match (atomic_pattern, parameter_set.atomic_pattern) {
|
||||
(AtomicPatternNoiseSquashingKey::Standard(std_nsk), AtomicPatternKind::Standard(_)) => {
|
||||
std_nsk
|
||||
.bootstrapping_key()
|
||||
.is_conformant(¶meter_set.pbs_params)
|
||||
}
|
||||
(
|
||||
Shortint128BootstrappingKey::MultiBit { bsk, .. },
|
||||
NoiseSquashingKeyConformanceParams::MultiBit {
|
||||
bootstrapping_key_params: expected_bootstrapping_key_params,
|
||||
message_modulus: expected_message_modulus,
|
||||
carry_modulus: expected_carry_modulus,
|
||||
},
|
||||
) => {
|
||||
bsk.is_conformant(expected_bootstrapping_key_params)
|
||||
&& *output_ciphertext_modulus
|
||||
== expected_bootstrapping_key_params.ciphertext_modulus
|
||||
&& *message_modulus == *expected_message_modulus
|
||||
&& *carry_modulus == *expected_carry_modulus
|
||||
}
|
||||
AtomicPatternNoiseSquashingKey::KeySwitch32(ks32_nsk),
|
||||
AtomicPatternKind::KeySwitch32,
|
||||
) => ks32_nsk
|
||||
.bootstrapping_key()
|
||||
.is_conformant(¶meter_set.pbs_params),
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
|
||||
bsk_conformant
|
||||
&& *output_ciphertext_modulus == parameter_set.output_ciphertext_modulus
|
||||
&& *message_modulus == parameter_set.message_modulus
|
||||
&& *carry_modulus == parameter_set.carry_modulus
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,14 @@ fn test_multi_bit_noise_squashing_ci_run_filter() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ks32_noise_squashing_ci_run_filter() {
|
||||
test_noise_squashing(
|
||||
PARAM_MESSAGE_2_CARRY_2_KS32_PBS_TUNIFORM_2M128,
|
||||
NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
|
||||
);
|
||||
}
|
||||
|
||||
fn test_noise_squashing(
|
||||
classic_params: impl Into<AtomicPatternParameters>,
|
||||
noise_squashing_params: NoiseSquashingParameters,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use crate::shortint::parameters::{
|
||||
current_params, ClassicPBSParameters, CompactPublicKeyEncryptionParameters,
|
||||
CompressionParameters, MultiBitPBSParameters, NoiseSquashingCompressionParameters,
|
||||
NoiseSquashingParameters, ShortintKeySwitchingParameters,
|
||||
CompressionParameters, KeySwitch32PBSParameters, MultiBitPBSParameters,
|
||||
NoiseSquashingCompressionParameters, NoiseSquashingParameters, ShortintKeySwitchingParameters,
|
||||
};
|
||||
|
||||
use super::v1_4::V1_4_PARAM_MESSAGE_2_CARRY_2_KS32_PBS_TUNIFORM_2M128;
|
||||
|
||||
use current_params::classic::gaussian::p_fail_2_minus_128::ks_pbs::{
|
||||
V1_4_PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M128,
|
||||
V1_4_PARAM_MESSAGE_3_CARRY_3_KS_PBS_GAUSSIAN_2M128,
|
||||
@@ -82,6 +84,11 @@ pub const PARAM_MESSAGE_2_CARRY_2: ClassicPBSParameters = PARAM_MESSAGE_2_CARRY_
|
||||
pub const PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: ClassicPBSParameters =
|
||||
V1_4_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64;
|
||||
|
||||
// Compute KS32
|
||||
// 2M128
|
||||
pub const PARAM_MESSAGE_2_CARRY_2_KS32_PBS_TUNIFORM_2M128: KeySwitch32PBSParameters =
|
||||
V1_4_PARAM_MESSAGE_2_CARRY_2_KS32_PBS_TUNIFORM_2M128;
|
||||
|
||||
// Compression Gaussian
|
||||
// 2M128
|
||||
pub const COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M128: CompressionParameters =
|
||||
|
||||
@@ -43,7 +43,7 @@ use crate::core_crypto::commons::parameters::{
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::fft_impl::fft64::crypto::bootstrap::LweBootstrapKeyConformanceParams;
|
||||
use crate::core_crypto::prelude::{ComputationBuffers, Fft};
|
||||
use crate::core_crypto::prelude::{ComputationBuffers, Fft, Fft128};
|
||||
use crate::shortint::ciphertext::{Ciphertext, Degree, MaxDegree, MaxNoiseLevel, NoiseLevel};
|
||||
use crate::shortint::client_key::ClientKey;
|
||||
use crate::shortint::engine::{
|
||||
@@ -79,12 +79,13 @@ pub use pbs_stats::*;
|
||||
|
||||
use super::atomic_pattern::{
|
||||
AtomicPattern, AtomicPatternMut, AtomicPatternParameters, AtomicPatternServerKey,
|
||||
StandardAtomicPatternServerKey,
|
||||
KS32AtomicPatternServerKey, StandardAtomicPatternServerKey,
|
||||
};
|
||||
use super::backward_compatibility::server_key::{
|
||||
SerializableShortintBootstrappingKeyVersions, ServerKeyVersions,
|
||||
};
|
||||
use super::ciphertext::unchecked_create_trivial_with_lwe_size;
|
||||
use super::noise_squashing::Shortint128BootstrappingKey;
|
||||
use super::parameters::KeySwitch32PBSParameters;
|
||||
use super::PBSParameters;
|
||||
|
||||
@@ -485,10 +486,12 @@ pub type ServerKey = GenericServerKey<AtomicPatternServerKey>;
|
||||
pub type StandardServerKey = GenericServerKey<StandardAtomicPatternServerKey>;
|
||||
pub type ServerKeyView<'key> = GenericServerKey<&'key AtomicPatternServerKey>;
|
||||
pub type StandardServerKeyView<'key> = GenericServerKey<&'key StandardAtomicPatternServerKey>;
|
||||
pub type KS32ServerKeyView<'key> = GenericServerKey<&'key KS32AtomicPatternServerKey>;
|
||||
|
||||
// Manual implementation of Copy because the derive will require AP to be Copy,
|
||||
// Manual implementations of Copy because the derive will require AP to be Copy,
|
||||
// which is actually overrestrictive: https://github.com/rust-lang/rust/issues/26925
|
||||
impl Copy for StandardServerKeyView<'_> {}
|
||||
impl Copy for KS32ServerKeyView<'_> {}
|
||||
|
||||
impl From<StandardServerKey> for ServerKey {
|
||||
fn from(value: StandardServerKey) -> Self {
|
||||
@@ -554,6 +557,25 @@ impl<'key> TryFrom<ServerKeyView<'key>> for StandardServerKeyView<'key> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'key> TryFrom<ServerKeyView<'key>> for KS32ServerKeyView<'key> {
|
||||
type Error = UnsupportedOperation;
|
||||
|
||||
fn try_from(value: ServerKeyView<'key>) -> Result<Self, Self::Error> {
|
||||
let AtomicPatternServerKey::KeySwitch32(atomic_pattern) = value.atomic_pattern else {
|
||||
return Err(UnsupportedOperation);
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
atomic_pattern,
|
||||
message_modulus: value.message_modulus,
|
||||
carry_modulus: value.carry_modulus,
|
||||
max_degree: value.max_degree,
|
||||
max_noise_level: value.max_noise_level,
|
||||
ciphertext_modulus: value.ciphertext_modulus,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The number of elements in a [`LookupTable`] represented by a Glwe ciphertext
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct LookupTableSize {
|
||||
@@ -1513,6 +1535,72 @@ pub(crate) fn apply_programmable_bootstrap<InputScalar, InputCont, OutputScalar,
|
||||
extract_lwe_sample_from_glwe_ciphertext(&glwe_out, lwe_out, MonomialDegree(0));
|
||||
}
|
||||
|
||||
pub(crate) fn apply_programmable_bootstrap_128<InputScalar, InputCont, OutputScalar, OutputCont>(
|
||||
bootstrapping_key: &Shortint128BootstrappingKey<InputScalar>,
|
||||
lwe_in: &LweCiphertext<InputCont>,
|
||||
lwe_out: &mut LweCiphertext<OutputCont>,
|
||||
acc: &GlweCiphertext<Vec<OutputScalar>>,
|
||||
buffers: &mut ComputationBuffers,
|
||||
) where
|
||||
InputScalar: UnsignedTorus + CastInto<usize> + CastFrom<usize> + Sync,
|
||||
InputCont: Container<Element = InputScalar>,
|
||||
OutputScalar: UnsignedTorus + CastFrom<usize>,
|
||||
OutputCont: ContainerMut<Element = OutputScalar>,
|
||||
{
|
||||
match bootstrapping_key {
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
} => {
|
||||
let bsk_glwe_size = bsk.glwe_size();
|
||||
let bsk_polynomial_size = bsk.polynomial_size();
|
||||
|
||||
let fft = Fft128::new(bsk_polynomial_size);
|
||||
let fft = fft.as_view();
|
||||
|
||||
let mem_requirement =
|
||||
blind_rotate_f128_lwe_ciphertext_mem_optimized_requirement::<u128>(
|
||||
bsk_glwe_size,
|
||||
bsk_polynomial_size,
|
||||
fft,
|
||||
)
|
||||
.unwrap()
|
||||
.try_unaligned_bytes_required()
|
||||
.unwrap();
|
||||
|
||||
let br_input_modulus_log = bsk.polynomial_size().to_blind_rotation_input_modulus_log();
|
||||
let lwe_ciphertext_to_squash_noise = modulus_switch_noise_reduction_key
|
||||
.lwe_ciphertext_modulus_switch(lwe_in, br_input_modulus_log);
|
||||
|
||||
buffers.resize(mem_requirement);
|
||||
|
||||
// Also include sample extract
|
||||
blind_rotate_f128_lwe_ciphertext_mem_optimized(
|
||||
&lwe_ciphertext_to_squash_noise,
|
||||
lwe_out,
|
||||
acc,
|
||||
bsk,
|
||||
fft,
|
||||
buffers.stack(),
|
||||
);
|
||||
}
|
||||
Shortint128BootstrappingKey::MultiBit {
|
||||
bsk,
|
||||
thread_count,
|
||||
deterministic_execution,
|
||||
} => {
|
||||
multi_bit_programmable_bootstrap_f128_lwe_ciphertext(
|
||||
lwe_in,
|
||||
lwe_out,
|
||||
acc,
|
||||
bsk,
|
||||
*thread_count,
|
||||
*deterministic_execution,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a lookup table without any encoding specifications
|
||||
///
|
||||
/// It is the responsibility of the function `f` to encode the input cleartexts into valid
|
||||
|
||||
@@ -13,8 +13,10 @@ use crate::shortint::engine::ShortintEngine;
|
||||
use crate::shortint::list_compression::{
|
||||
NoiseSquashingCompressionKey, NoiseSquashingCompressionPrivateKey,
|
||||
};
|
||||
use crate::shortint::noise_squashing::atomic_pattern::AtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::{
|
||||
NoiseSquashingKey, NoiseSquashingPrivateKey, Shortint128BootstrappingKey,
|
||||
StandardNoiseSquashingKeyView,
|
||||
};
|
||||
use crate::shortint::parameters::noise_squashing::NoiseSquashingParameters;
|
||||
use crate::shortint::parameters::test_params::{
|
||||
@@ -302,7 +304,11 @@ fn sanity_check_encrypt_dp_ks_standard_pbs128_packing_ks<P>(
|
||||
}
|
||||
NoiseSimulationModulusSwitchConfig::CenteredMeanNoiseReduction => None,
|
||||
};
|
||||
let br_input_modulus_log = noise_squashing_key
|
||||
|
||||
let standard_nsk = StandardNoiseSquashingKeyView::try_from(noise_squashing_key.as_view())
|
||||
.expect("Noise tests only support standard atomic pattern");
|
||||
|
||||
let br_input_modulus_log = standard_nsk
|
||||
.bootstrapping_key()
|
||||
.polynomial_size()
|
||||
.to_blind_rotation_input_modulus_log();
|
||||
@@ -317,8 +323,8 @@ fn sanity_check_encrypt_dp_ks_standard_pbs128_packing_ks<P>(
|
||||
let max_scalar_mul = sks.max_noise_level.get();
|
||||
|
||||
let id_lut = generate_programmable_bootstrap_glwe_lut(
|
||||
noise_squashing_key.bootstrapping_key().polynomial_size(),
|
||||
noise_squashing_key.bootstrapping_key().glwe_size(),
|
||||
standard_nsk.bootstrapping_key().polynomial_size(),
|
||||
standard_nsk.bootstrapping_key().glwe_size(),
|
||||
u128_encoding
|
||||
.cleartext_space_without_padding()
|
||||
.try_into()
|
||||
@@ -451,6 +457,8 @@ fn encrypt_dp_ks_standard_pbs128_packing_ks_inner_helper(
|
||||
&thread_noise_squashing_compression_key,
|
||||
)
|
||||
};
|
||||
let standard_nsk = StandardNoiseSquashingKeyView::try_from(noise_squashing_key.as_view())
|
||||
.expect("Noise tests only support standard atomic pattern");
|
||||
|
||||
let noise_simulation_modulus_switch_config =
|
||||
noise_squashing_key.noise_simulation_modulus_switch_config();
|
||||
@@ -461,8 +469,8 @@ fn encrypt_dp_ks_standard_pbs128_packing_ks_inner_helper(
|
||||
}
|
||||
NoiseSimulationModulusSwitchConfig::CenteredMeanNoiseReduction => None,
|
||||
};
|
||||
let bsk_polynomial_size = noise_squashing_key.bootstrapping_key().polynomial_size();
|
||||
let bsk_glwe_size = noise_squashing_key.bootstrapping_key().glwe_size();
|
||||
let bsk_polynomial_size = standard_nsk.bootstrapping_key().polynomial_size();
|
||||
let bsk_glwe_size = standard_nsk.bootstrapping_key().glwe_size();
|
||||
let br_input_modulus_log = bsk_polynomial_size.to_blind_rotation_input_modulus_log();
|
||||
|
||||
let u128_encoding = ShortintEncoding {
|
||||
@@ -678,10 +686,13 @@ fn noise_check_encrypt_dp_ks_standard_pbs128_packing_ks_noise<P>(
|
||||
noise_squashing_compression_params,
|
||||
);
|
||||
|
||||
let standard_nsk = StandardNoiseSquashingKeyView::try_from(noise_squashing_key.as_view())
|
||||
.expect("Noise tests only support standard atomic pattern");
|
||||
|
||||
let noise_simulation_modulus_switch_config =
|
||||
noise_squashing_key.noise_simulation_modulus_switch_config();
|
||||
|
||||
let fbsk_128 = match noise_squashing_key.bootstrapping_key() {
|
||||
let fbsk_128 = match standard_nsk.bootstrapping_key() {
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key: _,
|
||||
@@ -693,7 +704,14 @@ fn noise_check_encrypt_dp_ks_standard_pbs128_packing_ks_noise<P>(
|
||||
assert!(noise_simulation_packing_key
|
||||
.matches_actual_pksk(noise_squashing_compression_key.packing_key_switching_key()));
|
||||
|
||||
let br_input_modulus_log = noise_squashing_key
|
||||
let nsk = match noise_squashing_key.atomic_pattern() {
|
||||
AtomicPatternNoiseSquashingKey::Standard(std_nsk) => std_nsk,
|
||||
AtomicPatternNoiseSquashingKey::KeySwitch32(_ks32_nsk) => {
|
||||
todo!()
|
||||
}
|
||||
};
|
||||
|
||||
let br_input_modulus_log = nsk
|
||||
.bootstrapping_key()
|
||||
.polynomial_size()
|
||||
.to_blind_rotation_input_modulus_log();
|
||||
|
||||
@@ -20,7 +20,10 @@ use crate::core_crypto::entities::{
|
||||
};
|
||||
use crate::shortint::client_key::ClientKey;
|
||||
use crate::shortint::list_compression::NoiseSquashingCompressionKey;
|
||||
use crate::shortint::noise_squashing::{NoiseSquashingKey, Shortint128BootstrappingKey};
|
||||
use crate::shortint::noise_squashing::atomic_pattern::AtomicPatternNoiseSquashingKey;
|
||||
use crate::shortint::noise_squashing::{
|
||||
NoiseSquashingKey, Shortint128BootstrappingKey, StandardNoiseSquashingKeyView,
|
||||
};
|
||||
use crate::shortint::parameters::{
|
||||
AtomicPatternParameters, ModulusSwitchType, NoiseSquashingCompressionParameters,
|
||||
NoiseSquashingParameters, PBSParameters,
|
||||
@@ -653,13 +656,15 @@ impl<C: Container<Element = u64>> LweClassicFftBootstrap<DynLwe, DynLwe, LookupT
|
||||
|
||||
impl NoiseSquashingKey {
|
||||
pub fn noise_simulation_modulus_switch_config(&self) -> NoiseSimulationModulusSwitchConfig {
|
||||
match self.bootstrapping_key() {
|
||||
let nsk = StandardNoiseSquashingKeyView::try_from(self.as_view())
|
||||
.expect("Noise tests only support standard atomic pattern");
|
||||
match nsk.bootstrapping_key() {
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
bsk: _,
|
||||
modulus_switch_noise_reduction_key,
|
||||
} => modulus_switch_noise_reduction_key.into(),
|
||||
Shortint128BootstrappingKey::MultiBit { .. } => {
|
||||
panic!("MultiBit ServerKey does support the drift technique")
|
||||
panic!("MultiBit ServerKey does not support the drift technique")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -674,7 +679,10 @@ impl AllocateDriftTechniqueStandardModSwitchResult for NoiseSquashingKey {
|
||||
&self,
|
||||
side_resources: &mut Self::SideResources,
|
||||
) -> (Self::AfterDriftOutput, Self::AfterMsOutput) {
|
||||
match self.bootstrapping_key() {
|
||||
let nsk = StandardNoiseSquashingKeyView::try_from(self.as_view())
|
||||
.expect("Noise tests only support standard atomic pattern");
|
||||
|
||||
match nsk.bootstrapping_key() {
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
bsk: _,
|
||||
modulus_switch_noise_reduction_key,
|
||||
@@ -713,7 +721,14 @@ impl DriftTechniqueStandardModSwitch<DynLwe, DynLwe, DynLwe> for NoiseSquashingK
|
||||
after_mod_switch: &mut DynLwe,
|
||||
side_resources: &mut Self::SideResources,
|
||||
) {
|
||||
match self.bootstrapping_key() {
|
||||
let nsk = match self.atomic_pattern() {
|
||||
AtomicPatternNoiseSquashingKey::Standard(std_nsk) => std_nsk,
|
||||
AtomicPatternNoiseSquashingKey::KeySwitch32(_ks32_nsk) => {
|
||||
todo!()
|
||||
}
|
||||
};
|
||||
|
||||
match nsk.bootstrapping_key() {
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
bsk: _,
|
||||
modulus_switch_noise_reduction_key,
|
||||
@@ -770,7 +785,13 @@ where
|
||||
accumulator: &GlweCiphertext<AccCont>,
|
||||
side_resources: &mut Self::SideResources,
|
||||
) {
|
||||
match self.bootstrapping_key() {
|
||||
let nsk = match self.atomic_pattern() {
|
||||
AtomicPatternNoiseSquashingKey::Standard(std_nsk) => std_nsk,
|
||||
AtomicPatternNoiseSquashingKey::KeySwitch32(_ks32_nsk) => {
|
||||
todo!()
|
||||
}
|
||||
};
|
||||
match nsk.bootstrapping_key() {
|
||||
Shortint128BootstrappingKey::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key: _,
|
||||
|
||||
Reference in New Issue
Block a user