feat(shortint): add noise squashing for ks32

This commit is contained in:
Nicolas Sarlin
2025-09-24 11:32:10 +02:00
committed by Nicolas Sarlin
parent ef5b984448
commit 45a849ad36
18 changed files with 1687 additions and 627 deletions

View File

@@ -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>,

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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),
}

View File

@@ -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
}
}

View File

@@ -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())
}
}
}
}

View File

@@ -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
}
}

View 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)
}
}

View 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,
),
}
}
}

View 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)
}
}

View File

@@ -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(&parameter_set.pbs_params),
(
CompressedAtomicPatternNoiseSquashingKey::KeySwitch32(compressed_ks32),
AtomicPatternKind::KeySwitch32,
) => compressed_ks32
.bootstrapping_key()
.is_conformant(&parameter_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
}
}

View File

@@ -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,
};

View File

@@ -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(&parameter_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(&parameter_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
}
}

View File

@@ -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,

View File

@@ -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 =

View File

@@ -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

View File

@@ -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();

View File

@@ -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: _,