mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-09 14:47:56 -05:00
feat(shortint): introduce the KS32 atomic pattern
This commit is contained in:
committed by
Nicolas Sarlin
parent
0fd9537ae0
commit
c17a2527b7
@@ -30,7 +30,7 @@ impl Gaussian<f64> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn standard_dev(&self) -> StandardDev {
|
||||
pub const fn standard_dev(&self) -> StandardDev {
|
||||
StandardDev(self.std)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,6 +267,21 @@ impl<T: UnsignedInteger> DynamicDistribution<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl DynamicDistribution<u32> {
|
||||
pub const fn to_u64_distribution(self) -> DynamicDistribution<u64> {
|
||||
// Depending on how the Scalar type is used, converting it from u32 to u64
|
||||
// might affect the underlying distribution in subtle ways. When adding support for
|
||||
// new distributions, make sure that the result is still correct.
|
||||
match self {
|
||||
Self::Gaussian(gaussian) => DynamicDistribution::Gaussian(gaussian),
|
||||
Self::TUniform(tuniform) => {
|
||||
// Ok because an u32 bound is always also a valid u64 bound
|
||||
DynamicDistribution::new_t_uniform(tuniform.bound_log2())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UnsignedInteger> std::fmt::Display for DynamicDistribution<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
||||
@@ -435,16 +435,18 @@ impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntit
|
||||
Self: 'this;
|
||||
}
|
||||
|
||||
pub struct LweKeyswitchKeyConformanceParams {
|
||||
pub struct LweKeyswitchKeyConformanceParams<Scalar: UnsignedInteger> {
|
||||
pub decomp_base_log: DecompositionBaseLog,
|
||||
pub decomp_level_count: DecompositionLevelCount,
|
||||
pub output_lwe_size: LweSize,
|
||||
pub input_lwe_dimension: LweDimension,
|
||||
pub ciphertext_modulus: CiphertextModulus<u64>,
|
||||
pub ciphertext_modulus: CiphertextModulus<Scalar>,
|
||||
}
|
||||
|
||||
impl<C: Container<Element = u64>> ParameterSetConformant for LweKeyswitchKey<C> {
|
||||
type ParameterSet = LweKeyswitchKeyConformanceParams;
|
||||
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ParameterSetConformant
|
||||
for LweKeyswitchKey<C>
|
||||
{
|
||||
type ParameterSet = LweKeyswitchKeyConformanceParams<Scalar>;
|
||||
|
||||
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
|
||||
let Self {
|
||||
|
||||
@@ -426,7 +426,7 @@ impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntit
|
||||
}
|
||||
|
||||
impl<C: Container<Element = u64>> ParameterSetConformant for SeededLweKeyswitchKey<C> {
|
||||
type ParameterSet = LweKeyswitchKeyConformanceParams;
|
||||
type ParameterSet = LweKeyswitchKeyConformanceParams<u64>;
|
||||
|
||||
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
|
||||
let Self {
|
||||
|
||||
330
tfhe/src/shortint/atomic_pattern/ks32.rs
Normal file
330
tfhe/src/shortint/atomic_pattern/ks32.rs
Normal file
@@ -0,0 +1,330 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tfhe_csprng::seeders::Seed;
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
use super::{
|
||||
apply_blind_rotate, apply_programmable_bootstrap, AtomicPattern, AtomicPatternKind,
|
||||
AtomicPatternMut,
|
||||
};
|
||||
use crate::conformance::ParameterSetConformant;
|
||||
use crate::core_crypto::prelude::{
|
||||
allocate_and_generate_new_lwe_keyswitch_key, extract_lwe_sample_from_glwe_ciphertext,
|
||||
keyswitch_lwe_ciphertext_with_scalar_change, CiphertextModulus as CoreCiphertextModulus,
|
||||
LweCiphertext, LweCiphertextOwned, LweDimension, LweKeyswitchKeyOwned, LweSecretKey,
|
||||
MonomialDegree, MsDecompressionType,
|
||||
};
|
||||
use crate::shortint::backward_compatibility::atomic_pattern::KS32AtomicPatternServerKeyVersions;
|
||||
use crate::shortint::ciphertext::{CompressedModulusSwitchedCiphertext, Degree, NoiseLevel};
|
||||
use crate::shortint::engine::ShortintEngine;
|
||||
use crate::shortint::oprf::generate_pseudo_random_from_pbs;
|
||||
use crate::shortint::parameters::KeySwitch32PBSParameters;
|
||||
use crate::shortint::server_key::{
|
||||
decompress_and_apply_lookup_table, switch_modulus_and_compress, LookupTableOwned,
|
||||
LookupTableSize, ManyLookupTableOwned, ShortintBootstrappingKey,
|
||||
};
|
||||
use crate::shortint::{Ciphertext, CiphertextModulus, ClientKey};
|
||||
|
||||
/// The definition of the server key elements used in the
|
||||
/// [`KeySwitch32`](AtomicPatternKind::KeySwitch32) atomic pattern
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
|
||||
#[versionize(KS32AtomicPatternServerKeyVersions)]
|
||||
pub struct KS32AtomicPatternServerKey {
|
||||
pub key_switching_key: LweKeyswitchKeyOwned<u32>,
|
||||
pub bootstrapping_key: ShortintBootstrappingKey<u32>,
|
||||
pub ciphertext_modulus: CiphertextModulus,
|
||||
}
|
||||
|
||||
impl ParameterSetConformant for KS32AtomicPatternServerKey {
|
||||
type ParameterSet = KeySwitch32PBSParameters;
|
||||
|
||||
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
|
||||
let Self {
|
||||
key_switching_key,
|
||||
bootstrapping_key,
|
||||
ciphertext_modulus,
|
||||
} = self;
|
||||
|
||||
let pbs_conformance_params = parameter_set.into();
|
||||
|
||||
let pbs_key_ok = bootstrapping_key.is_conformant(&pbs_conformance_params);
|
||||
|
||||
let ks_conformance_params = parameter_set.into();
|
||||
|
||||
let ks_key_ok = key_switching_key.is_conformant(&ks_conformance_params);
|
||||
|
||||
*ciphertext_modulus == pbs_conformance_params.ciphertext_modulus && pbs_key_ok && ks_key_ok
|
||||
}
|
||||
}
|
||||
|
||||
impl KS32AtomicPatternServerKey {
|
||||
pub fn new(cks: &ClientKey, engine: &mut ShortintEngine) -> Self {
|
||||
let params = &cks.parameters;
|
||||
|
||||
let pbs_params = params.ks32_parameters().unwrap();
|
||||
|
||||
let in_key = LweSecretKey::from_container(
|
||||
cks.small_lwe_secret_key()
|
||||
.as_ref()
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|x| x as u32)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
let out_key = &cks.glwe_secret_key;
|
||||
|
||||
let bootstrapping_key_base =
|
||||
engine.new_bootstrapping_key_ks32(pbs_params, &in_key, out_key);
|
||||
|
||||
// Creation of the key switching key
|
||||
let key_switching_key = allocate_and_generate_new_lwe_keyswitch_key(
|
||||
&cks.large_lwe_secret_key(),
|
||||
&in_key,
|
||||
params.ks_base_log(),
|
||||
params.ks_level(),
|
||||
pbs_params.lwe_noise_distribution(),
|
||||
CoreCiphertextModulus::new_native(), // Does it make sense to parametrize this ?
|
||||
&mut engine.encryption_generator,
|
||||
);
|
||||
|
||||
Self::from_raw_parts(
|
||||
key_switching_key,
|
||||
bootstrapping_key_base,
|
||||
params.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_raw_parts(
|
||||
key_switching_key: LweKeyswitchKeyOwned<u32>,
|
||||
bootstrapping_key: ShortintBootstrappingKey<u32>,
|
||||
ciphertext_modulus: CiphertextModulus,
|
||||
) -> Self {
|
||||
assert_eq!(
|
||||
key_switching_key.input_key_lwe_dimension(),
|
||||
bootstrapping_key.output_lwe_dimension(),
|
||||
"Mismatch between the input LweKeyswitchKey LweDimension ({:?}) \
|
||||
and the ShortintBootstrappingKey output LweDimension ({:?})",
|
||||
key_switching_key.input_key_lwe_dimension(),
|
||||
bootstrapping_key.output_lwe_dimension()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
key_switching_key.output_key_lwe_dimension(),
|
||||
bootstrapping_key.input_lwe_dimension(),
|
||||
"Mismatch between the output LweKeyswitchKey LweDimension ({:?}) \
|
||||
and the ShortintBootstrappingKey input LweDimension ({:?})",
|
||||
key_switching_key.output_key_lwe_dimension(),
|
||||
bootstrapping_key.input_lwe_dimension()
|
||||
);
|
||||
|
||||
Self {
|
||||
key_switching_key,
|
||||
bootstrapping_key,
|
||||
ciphertext_modulus,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn intermediate_lwe_dimension(&self) -> LweDimension {
|
||||
self.key_switching_key.output_key_lwe_dimension()
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomicPattern for KS32AtomicPatternServerKey {
|
||||
fn ciphertext_lwe_dimension(&self) -> LweDimension {
|
||||
self.key_switching_key.input_key_lwe_dimension()
|
||||
}
|
||||
|
||||
fn ciphertext_modulus(&self) -> CiphertextModulus {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
fn ciphertext_decompression_method(&self) -> MsDecompressionType {
|
||||
match &self.bootstrapping_key {
|
||||
ShortintBootstrappingKey::Classic { .. } => MsDecompressionType::ClassicPbs,
|
||||
ShortintBootstrappingKey::MultiBit { fourier_bsk, .. } => {
|
||||
MsDecompressionType::MultiBitPbs(fourier_bsk.grouping_factor())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_lookup_table_assign(&self, ct: &mut Ciphertext, acc: &LookupTableOwned) {
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let (mut ciphertext_buffer, buffers) = engine.get_buffers(
|
||||
self.intermediate_lwe_dimension(),
|
||||
self.intermediate_ciphertext_modulus(),
|
||||
);
|
||||
|
||||
keyswitch_lwe_ciphertext_with_scalar_change(
|
||||
&self.key_switching_key,
|
||||
&ct.ct,
|
||||
&mut ciphertext_buffer,
|
||||
);
|
||||
|
||||
apply_programmable_bootstrap(
|
||||
&self.bootstrapping_key,
|
||||
&ciphertext_buffer,
|
||||
&mut ct.ct,
|
||||
&acc.acc,
|
||||
buffers,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn apply_many_lookup_table(
|
||||
&self,
|
||||
ct: &Ciphertext,
|
||||
acc: &ManyLookupTableOwned,
|
||||
) -> Vec<Ciphertext> {
|
||||
self.keyswitch_programmable_bootstrap_many_lut(ct, acc)
|
||||
}
|
||||
|
||||
fn lookup_table_size(&self) -> LookupTableSize {
|
||||
LookupTableSize::new(
|
||||
self.bootstrapping_key.glwe_size(),
|
||||
self.bootstrapping_key.polynomial_size(),
|
||||
)
|
||||
}
|
||||
|
||||
fn kind(&self) -> AtomicPatternKind {
|
||||
AtomicPatternKind::KeySwitch32
|
||||
}
|
||||
|
||||
fn deterministic_execution(&self) -> bool {
|
||||
self.bootstrapping_key.deterministic_pbs_execution()
|
||||
}
|
||||
|
||||
fn generate_oblivious_pseudo_random(
|
||||
&self,
|
||||
seed: Seed,
|
||||
random_bits_count: u64,
|
||||
full_bits_count: u64,
|
||||
) -> (LweCiphertextOwned<u64>, Degree) {
|
||||
generate_pseudo_random_from_pbs(
|
||||
&self.bootstrapping_key,
|
||||
seed,
|
||||
random_bits_count,
|
||||
full_bits_count,
|
||||
self.ciphertext_modulus(),
|
||||
)
|
||||
}
|
||||
|
||||
fn switch_modulus_and_compress(&self, ct: &Ciphertext) -> CompressedModulusSwitchedCiphertext {
|
||||
let compressed_modulus_switched_lwe_ciphertext =
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let (mut ciphertext_buffer, _) = engine.get_buffers(
|
||||
self.intermediate_lwe_dimension(),
|
||||
self.intermediate_ciphertext_modulus(),
|
||||
);
|
||||
|
||||
keyswitch_lwe_ciphertext_with_scalar_change(
|
||||
&self.key_switching_key,
|
||||
&ct.ct,
|
||||
&mut ciphertext_buffer,
|
||||
);
|
||||
switch_modulus_and_compress(ciphertext_buffer.as_view(), &self.bootstrapping_key)
|
||||
});
|
||||
|
||||
CompressedModulusSwitchedCiphertext {
|
||||
compressed_modulus_switched_lwe_ciphertext,
|
||||
degree: ct.degree,
|
||||
message_modulus: ct.message_modulus,
|
||||
carry_modulus: ct.carry_modulus,
|
||||
atomic_pattern: ct.atomic_pattern,
|
||||
}
|
||||
}
|
||||
|
||||
fn decompress_and_apply_lookup_table(
|
||||
&self,
|
||||
compressed_ct: &CompressedModulusSwitchedCiphertext,
|
||||
lut: &LookupTableOwned,
|
||||
) -> Ciphertext {
|
||||
let mut output = LweCiphertext::new(
|
||||
0,
|
||||
self.ciphertext_lwe_dimension().to_lwe_size(),
|
||||
self.ciphertext_modulus(),
|
||||
);
|
||||
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let buffers = engine.get_computation_buffers();
|
||||
decompress_and_apply_lookup_table(
|
||||
compressed_ct,
|
||||
&lut.acc,
|
||||
&self.bootstrapping_key,
|
||||
&mut output.as_mut_view(),
|
||||
buffers,
|
||||
);
|
||||
});
|
||||
|
||||
Ciphertext::new(
|
||||
output,
|
||||
lut.degree,
|
||||
NoiseLevel::NOMINAL,
|
||||
compressed_ct.message_modulus,
|
||||
compressed_ct.carry_modulus,
|
||||
compressed_ct.atomic_pattern,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomicPatternMut for KS32AtomicPatternServerKey {
|
||||
fn set_deterministic_execution(&mut self, new_deterministic_execution: bool) {
|
||||
self.bootstrapping_key
|
||||
.set_deterministic_pbs_execution(new_deterministic_execution)
|
||||
}
|
||||
}
|
||||
|
||||
impl KS32AtomicPatternServerKey {
|
||||
pub(crate) fn keyswitch_programmable_bootstrap_many_lut(
|
||||
&self,
|
||||
ct: &Ciphertext,
|
||||
lut: &ManyLookupTableOwned,
|
||||
) -> Vec<Ciphertext> {
|
||||
let mut acc = lut.acc.clone();
|
||||
|
||||
ShortintEngine::with_thread_local_mut(|engine| {
|
||||
let (mut ciphertext_buffer, buffers) = engine.get_buffers(
|
||||
self.intermediate_lwe_dimension(),
|
||||
self.intermediate_ciphertext_modulus(),
|
||||
);
|
||||
|
||||
// Compute a key switch
|
||||
keyswitch_lwe_ciphertext_with_scalar_change(
|
||||
&self.key_switching_key,
|
||||
&ct.ct,
|
||||
&mut ciphertext_buffer,
|
||||
);
|
||||
|
||||
apply_blind_rotate(
|
||||
&self.bootstrapping_key,
|
||||
&ciphertext_buffer.as_view(),
|
||||
&mut acc,
|
||||
buffers,
|
||||
);
|
||||
});
|
||||
|
||||
// The accumulator has been rotated, we can now proceed with the various sample extractions
|
||||
let function_count = lut.function_count();
|
||||
let mut outputs = Vec::with_capacity(function_count);
|
||||
|
||||
for (fn_idx, output_degree) in lut.per_function_output_degree.iter().enumerate() {
|
||||
let monomial_degree = MonomialDegree(fn_idx * lut.sample_extraction_stride);
|
||||
let mut output_shortint_ct = ct.clone();
|
||||
|
||||
extract_lwe_sample_from_glwe_ciphertext(
|
||||
&acc,
|
||||
&mut output_shortint_ct.ct,
|
||||
monomial_degree,
|
||||
);
|
||||
|
||||
output_shortint_ct.degree = *output_degree;
|
||||
output_shortint_ct.set_noise_level_to_nominal();
|
||||
outputs.push(output_shortint_ct);
|
||||
}
|
||||
|
||||
outputs
|
||||
}
|
||||
|
||||
fn intermediate_ciphertext_modulus(&self) -> CoreCiphertextModulus<u32> {
|
||||
self.key_switching_key.ciphertext_modulus()
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
//! For example, in TFHE the standard atomic pattern is the chain of n linear operations, a
|
||||
//! Keyswitch and a PBS.
|
||||
|
||||
pub mod ks32;
|
||||
pub mod standard;
|
||||
|
||||
use std::any::Any;
|
||||
@@ -20,15 +21,19 @@ use crate::core_crypto::prelude::{
|
||||
use super::backward_compatibility::atomic_pattern::*;
|
||||
use super::ciphertext::{CompressedModulusSwitchedCiphertext, Degree};
|
||||
use super::engine::ShortintEngine;
|
||||
use super::parameters::{DynamicDistribution, KeySwitch32PBSParameters};
|
||||
use super::prelude::{DecompositionBaseLog, DecompositionLevelCount};
|
||||
use super::server_key::{
|
||||
apply_blind_rotate, apply_programmable_bootstrap, LookupTableOwned, LookupTableSize,
|
||||
ManyLookupTableOwned,
|
||||
};
|
||||
use super::{
|
||||
CarryModulus, Ciphertext, CiphertextModulus, ClassicPBSParameters, ClientKey, MaxNoiseLevel,
|
||||
MessageModulus, MultiBitPBSParameters, PBSOrder, PBSParameters, ShortintParameterSet,
|
||||
CarryModulus, Ciphertext, CiphertextModulus, ClassicPBSParameters, ClientKey,
|
||||
EncryptionKeyChoice, MaxNoiseLevel, MessageModulus, MultiBitPBSParameters, PBSOrder,
|
||||
PBSParameters,
|
||||
};
|
||||
|
||||
pub use ks32::*;
|
||||
pub use standard::*;
|
||||
|
||||
/// A choice of atomic pattern
|
||||
@@ -41,6 +46,12 @@ pub enum AtomicPatternKind {
|
||||
/// - PBS order (KS -> Bootstrap or Bootstrap -> Keyswitch)
|
||||
/// - PBS kind (classic or multibit)
|
||||
Standard(PBSOrder),
|
||||
/// Similar to the standard AP, but the KeySwitch also changes the scalar type to u32,
|
||||
/// supporting modulus smaller or equal to $$2^{32}$$.
|
||||
///
|
||||
/// This allows to reduce the size of the keyswitching key. This AP only supports the KS -> PBS
|
||||
/// order.
|
||||
KeySwitch32,
|
||||
}
|
||||
|
||||
/// The set of operations needed to implement an Atomic Pattern.
|
||||
@@ -240,6 +251,7 @@ impl<T: AtomicPattern> AtomicPattern for &T {
|
||||
#[allow(clippy::large_enum_variant)] // The most common variant should be `Standard` so we optimize for it
|
||||
pub enum AtomicPatternServerKey {
|
||||
Standard(StandardAtomicPatternServerKey),
|
||||
KeySwitch32(KS32AtomicPatternServerKey),
|
||||
#[serde(skip)]
|
||||
Dynamic(Box<dyn private::DynamicAtomicPattern>),
|
||||
}
|
||||
@@ -252,6 +264,9 @@ impl AtomicPatternServerKey {
|
||||
AtomicPatternParameters::Standard(_) => {
|
||||
Self::Standard(StandardAtomicPatternServerKey::new(cks, engine))
|
||||
}
|
||||
AtomicPatternParameters::KeySwitch32(_) => {
|
||||
Self::KeySwitch32(KS32AtomicPatternServerKey::new(cks, engine))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,6 +275,7 @@ impl AtomicPattern for AtomicPatternServerKey {
|
||||
fn ciphertext_lwe_dimension(&self) -> LweDimension {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.ciphertext_lwe_dimension(),
|
||||
Self::KeySwitch32(ap) => ap.ciphertext_lwe_dimension(),
|
||||
Self::Dynamic(ap) => ap.ciphertext_lwe_dimension(),
|
||||
}
|
||||
}
|
||||
@@ -267,6 +283,7 @@ impl AtomicPattern for AtomicPatternServerKey {
|
||||
fn ciphertext_modulus(&self) -> CiphertextModulus {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.ciphertext_modulus(),
|
||||
Self::KeySwitch32(ap) => ap.ciphertext_modulus(),
|
||||
Self::Dynamic(ap) => ap.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
@@ -274,6 +291,7 @@ impl AtomicPattern for AtomicPatternServerKey {
|
||||
fn ciphertext_decompression_method(&self) -> MsDecompressionType {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.ciphertext_decompression_method(),
|
||||
Self::KeySwitch32(ap) => ap.ciphertext_decompression_method(),
|
||||
Self::Dynamic(ap) => ap.ciphertext_decompression_method(),
|
||||
}
|
||||
}
|
||||
@@ -281,6 +299,7 @@ impl AtomicPattern for AtomicPatternServerKey {
|
||||
fn apply_lookup_table_assign(&self, ct: &mut Ciphertext, acc: &LookupTableOwned) {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.apply_lookup_table_assign(ct, acc),
|
||||
Self::KeySwitch32(ap) => ap.apply_lookup_table_assign(ct, acc),
|
||||
Self::Dynamic(ap) => ap.apply_lookup_table_assign(ct, acc),
|
||||
}
|
||||
}
|
||||
@@ -292,6 +311,7 @@ impl AtomicPattern for AtomicPatternServerKey {
|
||||
) -> Vec<Ciphertext> {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.apply_many_lookup_table(ct, lut),
|
||||
Self::KeySwitch32(ap) => ap.apply_many_lookup_table(ct, lut),
|
||||
Self::Dynamic(ap) => ap.apply_many_lookup_table(ct, lut),
|
||||
}
|
||||
}
|
||||
@@ -299,6 +319,7 @@ impl AtomicPattern for AtomicPatternServerKey {
|
||||
fn lookup_table_size(&self) -> LookupTableSize {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.lookup_table_size(),
|
||||
Self::KeySwitch32(ap) => ap.lookup_table_size(),
|
||||
Self::Dynamic(ap) => ap.lookup_table_size(),
|
||||
}
|
||||
}
|
||||
@@ -306,6 +327,7 @@ impl AtomicPattern for AtomicPatternServerKey {
|
||||
fn kind(&self) -> AtomicPatternKind {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.kind(),
|
||||
Self::KeySwitch32(ap) => ap.kind(),
|
||||
Self::Dynamic(ap) => ap.kind(),
|
||||
}
|
||||
}
|
||||
@@ -313,6 +335,7 @@ impl AtomicPattern for AtomicPatternServerKey {
|
||||
fn deterministic_execution(&self) -> bool {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.deterministic_execution(),
|
||||
Self::KeySwitch32(ap) => ap.deterministic_execution(),
|
||||
Self::Dynamic(ap) => ap.deterministic_execution(),
|
||||
}
|
||||
}
|
||||
@@ -327,6 +350,9 @@ impl AtomicPattern for AtomicPatternServerKey {
|
||||
Self::Standard(ap) => {
|
||||
ap.generate_oblivious_pseudo_random(seed, random_bits_count, full_bits_count)
|
||||
}
|
||||
Self::KeySwitch32(ap) => {
|
||||
ap.generate_oblivious_pseudo_random(seed, random_bits_count, full_bits_count)
|
||||
}
|
||||
Self::Dynamic(ap) => {
|
||||
ap.generate_oblivious_pseudo_random(seed, random_bits_count, full_bits_count)
|
||||
}
|
||||
@@ -336,6 +362,7 @@ impl AtomicPattern for AtomicPatternServerKey {
|
||||
fn switch_modulus_and_compress(&self, ct: &Ciphertext) -> CompressedModulusSwitchedCiphertext {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.switch_modulus_and_compress(ct),
|
||||
Self::KeySwitch32(ap) => ap.switch_modulus_and_compress(ct),
|
||||
Self::Dynamic(ap) => ap.switch_modulus_and_compress(ct),
|
||||
}
|
||||
}
|
||||
@@ -347,6 +374,7 @@ impl AtomicPattern for AtomicPatternServerKey {
|
||||
) -> Ciphertext {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.decompress_and_apply_lookup_table(compressed_ct, lut),
|
||||
Self::KeySwitch32(ap) => ap.decompress_and_apply_lookup_table(compressed_ct, lut),
|
||||
Self::Dynamic(ap) => ap.decompress_and_apply_lookup_table(compressed_ct, lut),
|
||||
}
|
||||
}
|
||||
@@ -356,6 +384,7 @@ impl AtomicPatternMut for AtomicPatternServerKey {
|
||||
fn set_deterministic_execution(&mut self, new_deterministic_execution: bool) {
|
||||
match self {
|
||||
Self::Standard(ap) => ap.set_deterministic_execution(new_deterministic_execution),
|
||||
Self::KeySwitch32(ap) => ap.set_deterministic_execution(new_deterministic_execution),
|
||||
Self::Dynamic(ap) => ap.set_deterministic_execution(new_deterministic_execution),
|
||||
}
|
||||
}
|
||||
@@ -366,6 +395,7 @@ impl AtomicPatternMut for AtomicPatternServerKey {
|
||||
#[versionize(AtomicPatternParametersVersions)]
|
||||
pub enum AtomicPatternParameters {
|
||||
Standard(PBSParameters),
|
||||
KeySwitch32(KeySwitch32PBSParameters),
|
||||
}
|
||||
|
||||
impl From<PBSParameters> for AtomicPatternParameters {
|
||||
@@ -386,54 +416,110 @@ impl From<MultiBitPBSParameters> for AtomicPatternParameters {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AtomicPatternParameters> for ShortintParameterSet {
|
||||
fn from(value: AtomicPatternParameters) -> Self {
|
||||
match value {
|
||||
AtomicPatternParameters::Standard(parameters) => parameters.into(),
|
||||
}
|
||||
impl From<KeySwitch32PBSParameters> for AtomicPatternParameters {
|
||||
fn from(value: KeySwitch32PBSParameters) -> Self {
|
||||
Self::KeySwitch32(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl AtomicPatternParameters {
|
||||
pub fn message_modulus(&self) -> MessageModulus {
|
||||
pub const fn message_modulus(&self) -> MessageModulus {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.message_modulus(),
|
||||
Self::KeySwitch32(parameters) => parameters.message_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn carry_modulus(&self) -> CarryModulus {
|
||||
pub const fn carry_modulus(&self) -> CarryModulus {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.carry_modulus(),
|
||||
Self::KeySwitch32(parameters) => parameters.carry_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_noise_level(&self) -> MaxNoiseLevel {
|
||||
pub const fn max_noise_level(&self) -> MaxNoiseLevel {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.max_noise_level(),
|
||||
Self::KeySwitch32(parameters) => parameters.max_noise_level(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ciphertext_modulus(&self) -> CiphertextModulus {
|
||||
pub const fn encryption_key_choice(&self) -> EncryptionKeyChoice {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.encryption_key_choice(),
|
||||
Self::KeySwitch32(parameters) => parameters.encryption_key_choice(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn ciphertext_modulus(&self) -> CiphertextModulus {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.ciphertext_modulus(),
|
||||
Self::KeySwitch32(parameters) => parameters.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lwe_dimension(&self) -> LweDimension {
|
||||
pub const fn lwe_dimension(&self) -> LweDimension {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.lwe_dimension(),
|
||||
Self::KeySwitch32(parameters) => parameters.lwe_dimension(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glwe_dimension(&self) -> GlweDimension {
|
||||
pub const fn glwe_dimension(&self) -> GlweDimension {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.glwe_dimension(),
|
||||
Self::KeySwitch32(parameters) => parameters.glwe_dimension(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn polynomial_size(&self) -> PolynomialSize {
|
||||
pub const fn lwe_noise_distribution(&self) -> DynamicDistribution<u64> {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.lwe_noise_distribution(),
|
||||
Self::KeySwitch32(parameters) => {
|
||||
parameters.lwe_noise_distribution().to_u64_distribution()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn glwe_noise_distribution(&self) -> DynamicDistribution<u64> {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.glwe_noise_distribution(),
|
||||
Self::KeySwitch32(parameters) => parameters.glwe_noise_distribution(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn polynomial_size(&self) -> PolynomialSize {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.polynomial_size(),
|
||||
Self::KeySwitch32(parameters) => parameters.polynomial_size(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn pbs_base_log(&self) -> DecompositionBaseLog {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.pbs_base_log(),
|
||||
Self::KeySwitch32(parameters) => parameters.pbs_base_log(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn pbs_level(&self) -> DecompositionLevelCount {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.pbs_level(),
|
||||
Self::KeySwitch32(parameters) => parameters.pbs_level(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn ks_base_log(&self) -> DecompositionBaseLog {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.ks_base_log(),
|
||||
Self::KeySwitch32(parameters) => parameters.ks_base_log(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn ks_level(&self) -> DecompositionLevelCount {
|
||||
match self {
|
||||
Self::Standard(parameters) => parameters.ks_level(),
|
||||
Self::KeySwitch32(parameters) => parameters.ks_level(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -446,6 +532,9 @@ impl ParameterSetConformant for AtomicPatternServerKey {
|
||||
(Self::Standard(ap), AtomicPatternParameters::Standard(params)) => {
|
||||
ap.is_conformant(params)
|
||||
}
|
||||
(Self::KeySwitch32(ap), AtomicPatternParameters::KeySwitch32(params)) => {
|
||||
ap.is_conformant(params)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -456,3 +545,9 @@ impl From<StandardAtomicPatternServerKey> for AtomicPatternServerKey {
|
||||
Self::Standard(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<KS32AtomicPatternServerKey> for AtomicPatternServerKey {
|
||||
fn from(value: KS32AtomicPatternServerKey) -> Self {
|
||||
Self::KeySwitch32(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use tfhe_versionable::VersionsDispatch;
|
||||
|
||||
use crate::shortint::atomic_pattern::{AtomicPatternServerKey, StandardAtomicPatternServerKey};
|
||||
use crate::shortint::atomic_pattern::{
|
||||
AtomicPatternServerKey, KS32AtomicPatternServerKey, StandardAtomicPatternServerKey,
|
||||
};
|
||||
use crate::shortint::{AtomicPatternKind, AtomicPatternParameters};
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
@@ -22,3 +24,8 @@ pub enum AtomicPatternServerKeyVersions {
|
||||
pub enum StandardAtomicPatternServerKeyVersions {
|
||||
V0(StandardAtomicPatternServerKey),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum KS32AtomicPatternServerKeyVersions {
|
||||
V0(KS32AtomicPatternServerKey),
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::core_crypto::commons::parameters::{
|
||||
};
|
||||
use crate::shortint::parameters::{ShortintParameterSetInner, SupportedCompactPkeZkScheme};
|
||||
use crate::shortint::*;
|
||||
use parameters::KeySwitch32PBSParameters;
|
||||
use std::convert::Infallible;
|
||||
use tfhe_versionable::{Upgrade, Version, VersionsDispatch};
|
||||
|
||||
@@ -103,3 +104,8 @@ pub enum WopbsParametersVersions {
|
||||
pub enum SupportedCompactPkeZkSchemeVersions {
|
||||
V0(SupportedCompactPkeZkScheme),
|
||||
}
|
||||
|
||||
#[derive(VersionsDispatch)]
|
||||
pub enum KeySwitch32PBSParametersVersions {
|
||||
V0(KeySwitch32PBSParameters),
|
||||
}
|
||||
|
||||
@@ -523,6 +523,8 @@ impl ClientKey {
|
||||
AtomicPatternKind::Standard(PBSOrder::BootstrapKeyswitch) => {
|
||||
self.small_lwe_secret_key()
|
||||
}
|
||||
// KS32 AP is always KeyswitchBootstrap
|
||||
AtomicPatternKind::KeySwitch32 => self.large_lwe_secret_key(),
|
||||
};
|
||||
decrypt_lwe_ciphertext(&lwe_decryption_key, &ct.ct)
|
||||
}
|
||||
|
||||
@@ -5,12 +5,15 @@ use crate::core_crypto::commons::parameters::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, DynamicDistribution, GlweDimension,
|
||||
LweBskGroupingFactor, LweDimension, PolynomialSize, ThreadCount,
|
||||
};
|
||||
use crate::core_crypto::commons::traits::Container;
|
||||
use crate::core_crypto::commons::traits::{CastInto, Container, UnsignedInteger};
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::shortint::atomic_pattern::AtomicPatternServerKey;
|
||||
use crate::shortint::ciphertext::MaxDegree;
|
||||
use crate::shortint::client_key::secret_encryption_key::SecretEncryptionKeyView;
|
||||
use crate::shortint::parameters::{EncryptionKeyChoice, ShortintKeySwitchingParameters};
|
||||
use crate::shortint::parameters::{
|
||||
CoreCiphertextModulus, EncryptionKeyChoice, KeySwitch32PBSParameters,
|
||||
ShortintKeySwitchingParameters,
|
||||
};
|
||||
use crate::shortint::server_key::{
|
||||
CompressedModulusSwitchNoiseReductionKey, ModulusSwitchNoiseReductionKey,
|
||||
ShortintBootstrappingKey, ShortintCompressedBootstrappingKey,
|
||||
@@ -82,6 +85,41 @@ impl ShortintEngine {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_bootstrapping_key_ks32<
|
||||
InKeycont: Container<Element = u32> + Sync,
|
||||
OutKeyCont: Container<Element = u64> + Sync,
|
||||
>(
|
||||
&mut self,
|
||||
pbs_params: KeySwitch32PBSParameters,
|
||||
in_key: &LweSecretKey<InKeycont>,
|
||||
out_key: &GlweSecretKey<OutKeyCont>,
|
||||
) -> ShortintBootstrappingKey<u32> {
|
||||
let bsk = self.new_classic_bootstrapping_key(
|
||||
in_key,
|
||||
out_key,
|
||||
pbs_params.glwe_noise_distribution,
|
||||
pbs_params.pbs_base_log,
|
||||
pbs_params.pbs_level,
|
||||
pbs_params.ciphertext_modulus,
|
||||
);
|
||||
let modulus_switch_noise_reduction_key = pbs_params
|
||||
.modulus_switch_noise_reduction_params
|
||||
.map(|modulus_switch_noise_reduction_params| {
|
||||
ModulusSwitchNoiseReductionKey::new(
|
||||
modulus_switch_noise_reduction_params,
|
||||
in_key,
|
||||
self,
|
||||
CoreCiphertextModulus::new_native(),
|
||||
pbs_params.lwe_noise_distribution,
|
||||
)
|
||||
});
|
||||
|
||||
ShortintBootstrappingKey::Classic {
|
||||
bsk,
|
||||
modulus_switch_noise_reduction_key,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_bootstrapping_key<
|
||||
InKeycont: Container<Element = u64> + Sync,
|
||||
OutKeyCont: Container<Element = u64> + Sync,
|
||||
@@ -147,7 +185,8 @@ impl ShortintEngine {
|
||||
}
|
||||
|
||||
pub fn new_classic_bootstrapping_key<
|
||||
InKeycont: Container<Element = u64>,
|
||||
InputScalar: UnsignedInteger + CastInto<u64>,
|
||||
InKeycont: Container<Element = InputScalar>,
|
||||
OutKeyCont: Container<Element = u64> + Sync,
|
||||
>(
|
||||
&mut self,
|
||||
|
||||
@@ -1057,7 +1057,7 @@ impl CompressedKeySwitchingKey {
|
||||
}
|
||||
|
||||
pub struct KeySwitchingKeyConformanceParams {
|
||||
pub keyswitch_key_conformance_params: LweKeyswitchKeyConformanceParams,
|
||||
pub keyswitch_key_conformance_params: LweKeyswitchKeyConformanceParams<u64>,
|
||||
pub cast_rshift: i8,
|
||||
pub destination_key: EncryptionKeyChoice,
|
||||
}
|
||||
|
||||
@@ -411,6 +411,9 @@ impl TryFrom<(AtomicPatternParameters, NoiseSquashingParameters)>
|
||||
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",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ impl From<AtomicPatternKind> for CompactCiphertextListExpansionKind {
|
||||
fn from(value: AtomicPatternKind) -> Self {
|
||||
match value {
|
||||
AtomicPatternKind::Standard(pbsorder) => Self::NoCasting(pbsorder),
|
||||
AtomicPatternKind::KeySwitch32 => Self::NoCasting(PBSOrder::KeyswitchBootstrap),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,11 +177,7 @@ impl TryFrom<AtomicPatternParameters> for CompactPublicKeyEncryptionParameters {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: AtomicPatternParameters) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
AtomicPatternParameters::Standard(pbsparameters) => {
|
||||
let params: ShortintParameterSet = pbsparameters.into();
|
||||
params.try_into()
|
||||
}
|
||||
}
|
||||
let params: ShortintParameterSet = value.into();
|
||||
params.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
142
tfhe/src/shortint/parameters/ks32.rs
Normal file
142
tfhe/src/shortint/parameters/ks32.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tfhe_versionable::Versionize;
|
||||
|
||||
pub use crate::core_crypto::commons::parameters::{
|
||||
CiphertextModulus as CoreCiphertextModulus, CiphertextModulusLog, DecompositionBaseLog,
|
||||
DecompositionLevelCount, DynamicDistribution, EncryptionKeyChoice, GlweDimension,
|
||||
LweBskGroupingFactor, LweCiphertextCount, LweDimension, NoiseEstimationMeasureBound,
|
||||
PolynomialSize, RSigmaFactor,
|
||||
};
|
||||
use crate::core_crypto::prelude::{
|
||||
LweCiphertextConformanceParams, LweKeyswitchKeyConformanceParams, MsDecompressionType,
|
||||
};
|
||||
use crate::shortint::backward_compatibility::parameters::KeySwitch32PBSParametersVersions;
|
||||
|
||||
use super::{
|
||||
AtomicPatternKind, CarryModulus, CiphertextConformanceParams, CiphertextModulus, Degree,
|
||||
MaxNoiseLevel, MessageModulus, ModulusSwitchNoiseReductionParams, NoiseLevel,
|
||||
};
|
||||
|
||||
/// A set of cryptographic parameters used with the atomic pattern
|
||||
/// [`KeySwitch32`](crate::shortint::atomic_pattern::AtomicPatternKind::KeySwitch32)
|
||||
#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq, Versionize)]
|
||||
#[versionize(KeySwitch32PBSParametersVersions)]
|
||||
pub struct KeySwitch32PBSParameters {
|
||||
pub lwe_dimension: LweDimension,
|
||||
pub glwe_dimension: GlweDimension,
|
||||
pub polynomial_size: PolynomialSize,
|
||||
pub lwe_noise_distribution: DynamicDistribution<u32>,
|
||||
pub glwe_noise_distribution: DynamicDistribution<u64>,
|
||||
pub pbs_base_log: DecompositionBaseLog,
|
||||
pub pbs_level: DecompositionLevelCount,
|
||||
pub ks_base_log: DecompositionBaseLog,
|
||||
pub ks_level: DecompositionLevelCount,
|
||||
pub message_modulus: MessageModulus,
|
||||
pub carry_modulus: CarryModulus,
|
||||
pub max_noise_level: MaxNoiseLevel,
|
||||
pub log2_p_fail: f64,
|
||||
pub ciphertext_modulus: CiphertextModulus,
|
||||
pub modulus_switch_noise_reduction_params: Option<ModulusSwitchNoiseReductionParams>,
|
||||
}
|
||||
|
||||
#[allow(clippy::fallible_impl_from)]
|
||||
impl From<&KeySwitch32PBSParameters> for LweKeyswitchKeyConformanceParams<u32> {
|
||||
fn from(value: &KeySwitch32PBSParameters) -> Self {
|
||||
Self {
|
||||
decomp_base_log: value.ks_base_log(),
|
||||
decomp_level_count: value.ks_level(),
|
||||
output_lwe_size: value.lwe_dimension().to_lwe_size(),
|
||||
input_lwe_dimension: value
|
||||
.glwe_dimension()
|
||||
.to_equivalent_lwe_dimension(value.polynomial_size()),
|
||||
// For the moment we only handle the native u32 modulus for the KSK
|
||||
ciphertext_modulus: CoreCiphertextModulus::new_native(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeySwitch32PBSParameters {
|
||||
pub const fn lwe_dimension(&self) -> LweDimension {
|
||||
self.lwe_dimension
|
||||
}
|
||||
|
||||
pub const fn glwe_dimension(&self) -> GlweDimension {
|
||||
self.glwe_dimension
|
||||
}
|
||||
|
||||
pub const fn polynomial_size(&self) -> PolynomialSize {
|
||||
self.polynomial_size
|
||||
}
|
||||
|
||||
pub const fn lwe_noise_distribution(&self) -> DynamicDistribution<u32> {
|
||||
self.lwe_noise_distribution
|
||||
}
|
||||
|
||||
pub const fn glwe_noise_distribution(&self) -> DynamicDistribution<u64> {
|
||||
self.glwe_noise_distribution
|
||||
}
|
||||
|
||||
pub const fn pbs_base_log(&self) -> DecompositionBaseLog {
|
||||
self.pbs_base_log
|
||||
}
|
||||
|
||||
pub const fn pbs_level(&self) -> DecompositionLevelCount {
|
||||
self.pbs_level
|
||||
}
|
||||
|
||||
pub const fn ks_base_log(&self) -> DecompositionBaseLog {
|
||||
self.ks_base_log
|
||||
}
|
||||
|
||||
pub const fn ks_level(&self) -> DecompositionLevelCount {
|
||||
self.ks_level
|
||||
}
|
||||
|
||||
pub const fn message_modulus(&self) -> MessageModulus {
|
||||
self.message_modulus
|
||||
}
|
||||
|
||||
pub const fn carry_modulus(&self) -> CarryModulus {
|
||||
self.carry_modulus
|
||||
}
|
||||
|
||||
pub const fn max_noise_level(&self) -> MaxNoiseLevel {
|
||||
self.max_noise_level
|
||||
}
|
||||
|
||||
pub const fn ciphertext_modulus(&self) -> CiphertextModulus {
|
||||
self.ciphertext_modulus
|
||||
}
|
||||
|
||||
pub const fn encryption_key_choice(&self) -> EncryptionKeyChoice {
|
||||
// The KS32 atomic pattern is only supported with the KsPbs order
|
||||
EncryptionKeyChoice::Big
|
||||
}
|
||||
|
||||
pub fn to_shortint_conformance_param(&self) -> CiphertextConformanceParams {
|
||||
let expected_dim = self
|
||||
.glwe_dimension
|
||||
.to_equivalent_lwe_dimension(self.polynomial_size);
|
||||
|
||||
let message_modulus = self.message_modulus;
|
||||
let ciphertext_modulus = self.ciphertext_modulus;
|
||||
let carry_modulus = self.carry_modulus;
|
||||
|
||||
let degree = Degree::new(message_modulus.0 - 1);
|
||||
|
||||
let noise_level = NoiseLevel::NOMINAL;
|
||||
|
||||
CiphertextConformanceParams {
|
||||
ct_params: LweCiphertextConformanceParams {
|
||||
lwe_dim: expected_dim,
|
||||
ct_modulus: ciphertext_modulus,
|
||||
ms_decompression_method: MsDecompressionType::ClassicPbs,
|
||||
},
|
||||
message_modulus,
|
||||
carry_modulus,
|
||||
atomic_pattern: AtomicPatternKind::KeySwitch32,
|
||||
degree,
|
||||
noise_level,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ pub mod compact_public_key_only;
|
||||
#[cfg(tarpaulin)]
|
||||
pub mod coverage_parameters;
|
||||
pub mod key_switching;
|
||||
pub mod ks32;
|
||||
pub mod list_compression;
|
||||
pub mod multi_bit;
|
||||
pub mod noise_squashing;
|
||||
@@ -63,6 +64,7 @@ pub use compact_public_key_only::{
|
||||
#[cfg(tarpaulin)]
|
||||
pub use coverage_parameters::*;
|
||||
pub use key_switching::ShortintKeySwitchingParameters;
|
||||
pub use ks32::KeySwitch32PBSParameters;
|
||||
pub use multi_bit::MultiBitPBSParameters;
|
||||
pub use noise_squashing::NoiseSquashingParameters;
|
||||
pub use parameters_wopbs::*;
|
||||
@@ -187,7 +189,7 @@ impl From<MultiBitPBSParameters> for PBSParameters {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PBSParameters> for LweKeyswitchKeyConformanceParams {
|
||||
impl From<&PBSParameters> for LweKeyswitchKeyConformanceParams<u64> {
|
||||
fn from(value: &PBSParameters) -> Self {
|
||||
Self {
|
||||
decomp_base_log: value.ks_base_log(),
|
||||
@@ -318,6 +320,7 @@ pub(crate) enum ShortintParameterSetInner {
|
||||
PBSOnly(PBSParameters),
|
||||
WopbsOnly(WopbsParameters),
|
||||
PBSAndWopbs(PBSParameters, WopbsParameters),
|
||||
KS32PBS(KeySwitch32PBSParameters),
|
||||
}
|
||||
|
||||
impl ShortintParameterSetInner {
|
||||
@@ -353,6 +356,12 @@ impl ShortintParameterSet {
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn new_ks32_pbs_param_set(params: KeySwitch32PBSParameters) -> Self {
|
||||
Self {
|
||||
inner: ShortintParameterSetInner::KS32PBS(params),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_new_pbs_and_wopbs_param_set<P>(
|
||||
(pbs_params, wopbs_params): (P, WopbsParameters),
|
||||
) -> Result<Self, &'static str>
|
||||
@@ -385,6 +394,9 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => {
|
||||
Some(AtomicPatternParameters::Standard(params))
|
||||
}
|
||||
ShortintParameterSetInner::KS32PBS(params) => {
|
||||
Some(AtomicPatternParameters::KeySwitch32(params))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,6 +405,16 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => Some(params),
|
||||
ShortintParameterSetInner::WopbsOnly(_) => None,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => Some(params),
|
||||
ShortintParameterSetInner::KS32PBS(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn ks32_parameters(&self) -> Option<KeySwitch32PBSParameters> {
|
||||
match self.inner {
|
||||
ShortintParameterSetInner::PBSOnly(_) => None,
|
||||
ShortintParameterSetInner::WopbsOnly(_) => None,
|
||||
ShortintParameterSetInner::PBSAndWopbs(_, _) => None,
|
||||
ShortintParameterSetInner::KS32PBS(params) => Some(params),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,6 +423,7 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(_) => None,
|
||||
ShortintParameterSetInner::WopbsOnly(params) => Some(params),
|
||||
ShortintParameterSetInner::PBSAndWopbs(_, params) => Some(params),
|
||||
ShortintParameterSetInner::KS32PBS(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,6 +432,7 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => params.lwe_dimension(),
|
||||
ShortintParameterSetInner::WopbsOnly(params) => params.lwe_dimension,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.lwe_dimension(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => params.lwe_dimension(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +441,7 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => params.glwe_dimension(),
|
||||
ShortintParameterSetInner::WopbsOnly(params) => params.glwe_dimension,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.glwe_dimension(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => params.glwe_dimension(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,6 +450,7 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => params.polynomial_size(),
|
||||
ShortintParameterSetInner::WopbsOnly(params) => params.polynomial_size,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.polynomial_size(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => params.polynomial_size(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,6 +459,9 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => params.lwe_noise_distribution(),
|
||||
ShortintParameterSetInner::WopbsOnly(params) => params.lwe_noise_distribution,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.lwe_noise_distribution(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => {
|
||||
params.lwe_noise_distribution().to_u64_distribution()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,6 +470,7 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => params.glwe_noise_distribution(),
|
||||
ShortintParameterSetInner::WopbsOnly(params) => params.glwe_noise_distribution,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.glwe_noise_distribution(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => params.glwe_noise_distribution(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,6 +479,7 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => params.pbs_base_log(),
|
||||
ShortintParameterSetInner::WopbsOnly(params) => params.pbs_base_log,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.pbs_base_log(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => params.pbs_base_log(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,6 +488,7 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => params.pbs_level(),
|
||||
ShortintParameterSetInner::WopbsOnly(params) => params.pbs_level,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.pbs_level(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => params.pbs_level(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,6 +497,7 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => params.ks_base_log(),
|
||||
ShortintParameterSetInner::WopbsOnly(params) => params.ks_base_log,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.ks_base_log(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => params.ks_base_log(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,6 +506,7 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => params.ks_level(),
|
||||
ShortintParameterSetInner::WopbsOnly(params) => params.ks_level,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.ks_level(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => params.ks_level(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,6 +515,7 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => params.message_modulus(),
|
||||
ShortintParameterSetInner::WopbsOnly(params) => params.message_modulus,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.message_modulus(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => params.message_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,6 +524,7 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => params.carry_modulus(),
|
||||
ShortintParameterSetInner::WopbsOnly(params) => params.carry_modulus,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.carry_modulus(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => params.carry_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,6 +535,7 @@ impl ShortintParameterSet {
|
||||
panic!("WopbsOnly parameters do not have a MaxNoiseLevel information")
|
||||
}
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.max_noise_level(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => params.max_noise_level(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,6 +544,7 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => params.ciphertext_modulus(),
|
||||
ShortintParameterSetInner::WopbsOnly(params) => params.ciphertext_modulus,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.ciphertext_modulus(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => params.ciphertext_modulus(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,6 +553,7 @@ impl ShortintParameterSet {
|
||||
ShortintParameterSetInner::PBSOnly(params) => params.encryption_key_choice(),
|
||||
ShortintParameterSetInner::WopbsOnly(params) => params.encryption_key_choice,
|
||||
ShortintParameterSetInner::PBSAndWopbs(params, _) => params.encryption_key_choice(),
|
||||
ShortintParameterSetInner::KS32PBS(params) => params.encryption_key_choice(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,6 +595,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AtomicPatternParameters> for ShortintParameterSet {
|
||||
fn from(value: AtomicPatternParameters) -> Self {
|
||||
match value {
|
||||
AtomicPatternParameters::Standard(parameters) => Self::new_pbs_param_set(parameters),
|
||||
AtomicPatternParameters::KeySwitch32(parameters) => {
|
||||
Self::new_ks32_pbs_param_set(parameters)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WopbsParameters> for ShortintParameterSet {
|
||||
fn from(value: WopbsParameters) -> Self {
|
||||
Self::new_wopbs_param_set(value)
|
||||
|
||||
1
tfhe/src/shortint/parameters/v1_1/ks32/mod.rs
Normal file
1
tfhe/src/shortint/parameters/v1_1/ks32/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod tuniform;
|
||||
1
tfhe/src/shortint/parameters/v1_1/ks32/tuniform/mod.rs
Normal file
1
tfhe/src/shortint/parameters/v1_1/ks32/tuniform/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod p_fail_2_minus_128;
|
||||
@@ -0,0 +1,35 @@
|
||||
use crate::core_crypto::prelude::DynamicDistribution;
|
||||
use crate::shortint::parameters::{
|
||||
KeySwitch32PBSParameters, LweCiphertextCount, ModulusSwitchNoiseReductionParams,
|
||||
NoiseEstimationMeasureBound, RSigmaFactor, Variance,
|
||||
};
|
||||
use crate::shortint::prelude::{
|
||||
DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize,
|
||||
};
|
||||
use crate::shortint::{CarryModulus, CiphertextModulus, MaxNoiseLevel, MessageModulus};
|
||||
|
||||
// p-fail = 2^-129.358, algorithmic cost ~ 113, 2-norm = 5
|
||||
// Average number of encryptions of 0s ~ 17, peak noise ~ Variance(0.00000140546154228955)
|
||||
pub const V1_1_PARAM_MESSAGE_2_CARRY_2_KS32_PBS_TUNIFORM_2M128: KeySwitch32PBSParameters =
|
||||
KeySwitch32PBSParameters {
|
||||
lwe_dimension: LweDimension(918),
|
||||
glwe_dimension: GlweDimension(1),
|
||||
polynomial_size: PolynomialSize(2048),
|
||||
lwe_noise_distribution: DynamicDistribution::new_t_uniform(13),
|
||||
glwe_noise_distribution: DynamicDistribution::new_t_uniform(17),
|
||||
pbs_base_log: DecompositionBaseLog(23),
|
||||
pbs_level: DecompositionLevelCount(1),
|
||||
ks_base_log: DecompositionBaseLog(4),
|
||||
ks_level: DecompositionLevelCount(4),
|
||||
message_modulus: MessageModulus(4),
|
||||
carry_modulus: CarryModulus(4),
|
||||
max_noise_level: MaxNoiseLevel::new(5),
|
||||
log2_p_fail: -129.358380844,
|
||||
ciphertext_modulus: CiphertextModulus::new_native(),
|
||||
modulus_switch_noise_reduction_params: Some(ModulusSwitchNoiseReductionParams {
|
||||
modulus_switch_zeros_count: LweCiphertextCount(1449),
|
||||
ms_bound: NoiseEstimationMeasureBound(67108864f64),
|
||||
ms_r_sigma_factor: RSigmaFactor(13.179851302864899f64),
|
||||
ms_input_variance: Variance(2.63039392929833E-7f64),
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
pub mod ks_pbs;
|
||||
@@ -5,6 +5,7 @@
|
||||
pub mod classic;
|
||||
pub mod compact_public_key_only;
|
||||
pub mod key_switching;
|
||||
pub mod ks32;
|
||||
pub mod list_compression;
|
||||
pub mod multi_bit;
|
||||
pub mod noise_squashing;
|
||||
@@ -22,6 +23,7 @@ pub use classic::tuniform::p_fail_2_minus_40::ks_pbs::*;
|
||||
pub use classic::tuniform::p_fail_2_minus_64::ks_pbs::*;
|
||||
pub use compact_public_key_only::p_fail_2_minus_128::ks_pbs::*;
|
||||
pub use key_switching::p_fail_2_minus_128::ks_pbs::*;
|
||||
pub use ks32::tuniform::p_fail_2_minus_128::ks_pbs::*;
|
||||
pub use list_compression::p_fail_2_minus_128::*;
|
||||
pub use list_compression::p_fail_2_minus_64::*;
|
||||
pub use multi_bit::gaussian::p_fail_2_minus_128::ks_pbs::*;
|
||||
|
||||
@@ -464,7 +464,7 @@ impl ParameterSetConformant for CompressedServerKey {
|
||||
|
||||
let pbs_key_ok = bootstrapping_key.is_conformant(¶ms);
|
||||
|
||||
let param: LweKeyswitchKeyConformanceParams = parameter_set.into();
|
||||
let param: LweKeyswitchKeyConformanceParams<u64> = parameter_set.into();
|
||||
|
||||
let ks_key_ok = key_switching_key.is_conformant(¶m);
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ use super::backward_compatibility::server_key::{
|
||||
SerializableShortintBootstrappingKeyVersions, ServerKeyVersions,
|
||||
};
|
||||
use super::ciphertext::unchecked_create_trivial_with_lwe_size;
|
||||
use super::parameters::ModulusSwitchNoiseReductionParams;
|
||||
use super::parameters::{KeySwitch32PBSParameters, ModulusSwitchNoiseReductionParams};
|
||||
use super::PBSParameters;
|
||||
|
||||
/// Error returned when the carry buffer is full.
|
||||
@@ -1672,6 +1672,22 @@ impl From<&PBSParameters> for PBSConformanceParams {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&KeySwitch32PBSParameters> for PBSConformanceParams {
|
||||
fn from(value: &KeySwitch32PBSParameters) -> Self {
|
||||
Self {
|
||||
in_lwe_dimension: value.lwe_dimension(),
|
||||
out_glwe_dimension: value.glwe_dimension(),
|
||||
out_polynomial_size: value.polynomial_size(),
|
||||
base_log: value.pbs_base_log(),
|
||||
level: value.pbs_level(),
|
||||
ciphertext_modulus: value.ciphertext_modulus(),
|
||||
pbs_type: PbsTypeConformanceParams::Classic {
|
||||
modulus_switch_noise_reduction: value.modulus_switch_noise_reduction_params,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<InputScalar> ParameterSetConformant for ShortintBootstrappingKey<InputScalar>
|
||||
where
|
||||
InputScalar: UnsignedInteger,
|
||||
|
||||
Reference in New Issue
Block a user