feat(shortint): introduce the KS32 atomic pattern

This commit is contained in:
Nicolas Sarlin
2025-03-17 16:24:34 +01:00
committed by Nicolas Sarlin
parent 0fd9537ae0
commit c17a2527b7
22 changed files with 778 additions and 34 deletions

View File

@@ -30,7 +30,7 @@ impl Gaussian<f64> {
}
}
pub fn standard_dev(&self) -> StandardDev {
pub const fn standard_dev(&self) -> StandardDev {
StandardDev(self.std)
}
}

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@@ -0,0 +1 @@
pub mod tuniform;

View File

@@ -0,0 +1 @@
pub mod p_fail_2_minus_128;

View File

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

View File

@@ -0,0 +1 @@
pub mod ks_pbs;

View File

@@ -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::*;

View File

@@ -464,7 +464,7 @@ impl ParameterSetConformant for CompressedServerKey {
let pbs_key_ok = bootstrapping_key.is_conformant(&params);
let param: LweKeyswitchKeyConformanceParams = parameter_set.into();
let param: LweKeyswitchKeyConformanceParams<u64> = parameter_set.into();
let ks_key_ok = key_switching_key.is_conformant(&param);

View File

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