feat(hl): add noise squashing primitives to the HL API

This commit is contained in:
Arthur Meyre
2025-03-24 18:14:24 +01:00
parent 7ae4f00805
commit 87e1b57803
24 changed files with 1126 additions and 87 deletions

View File

@@ -269,8 +269,7 @@ pub fn test_hl_clientkey(
let test_params = load_hl_params(&test.parameters);
let key: ClientKey = load_and_unversionize(dir, test, format)?;
let (integer_key, _, _, _) = key.into_raw_parts();
let key_params = integer_key.parameters();
let key_params = key.computation_parameters();
if test_params != key_params {
Err(test.failure(

View File

@@ -3,6 +3,7 @@ use tfhe_versionable::{Upgrade, Version, VersionsDispatch};
use crate::high_level_api::booleans::{
InnerBoolean, InnerBooleanVersionOwned, InnerCompressedFheBool,
InnerSquashedNoiseBooleanVersionOwned, SquashedNoiseFheBool,
};
use crate::{CompressedFheBool, FheBool, Tag};
use std::convert::Infallible;
@@ -63,3 +64,16 @@ pub enum CompressedFheBoolVersions {
V0(CompressedFheBoolV0),
V1(CompressedFheBool),
}
// Squashed Noise
// Manual impl
#[derive(Serialize, Deserialize)]
pub(crate) enum InnerSquashedNoiseBooleanVersionedOwned {
V0(InnerSquashedNoiseBooleanVersionOwned),
}
// Squashed Noise
#[derive(VersionsDispatch)]
pub enum SquashedNoiseFheBoolVersions {
V0(SquashedNoiseFheBool),
}

View File

@@ -220,3 +220,26 @@ pub enum CompressedFheUintVersions<Id: FheUintId> {
V0(CompressedFheUintV0<Id>),
V1(CompressedFheUint<Id>),
}
// Squashed Noise
// Manual impl
#[derive(Serialize, Deserialize)]
pub(crate) enum InnerSquashedNoiseRadixCiphertextVersionedOwned {
V0(InnerSquashedNoiseRadixCiphertextVersionOwned),
}
#[derive(VersionsDispatch)]
pub enum SquashedNoiseFheUintVersions {
V0(SquashedNoiseFheUint),
}
// Manual impl
#[derive(Serialize, Deserialize)]
pub(crate) enum InnerSquashedNoiseSignedRadixCiphertextVersionedOwned {
V0(InnerSquashedNoiseSignedRadixCiphertextVersionOwned),
}
#[derive(VersionsDispatch)]
pub enum SquashedNoiseFheIntVersions {
V0(SquashedNoiseFheInt),
}

View File

@@ -1,4 +1,7 @@
use crate::high_level_api::keys::*;
use crate::integer::compression_keys::{
CompressedCompressionKey, CompressedDecompressionKey, CompressionKey, DecompressionKey,
};
use crate::Tag;
use std::convert::Infallible;
use tfhe_versionable::deprecation::{Deprecable, Deprecated};
@@ -158,11 +161,11 @@ pub(crate) struct IntegerClientKeyV2 {
pub(crate) compression_key: Option<crate::shortint::list_compression::CompressionPrivateKeys>,
}
impl Upgrade<IntegerClientKey> for IntegerClientKeyV2 {
impl Upgrade<IntegerClientKeyV3> for IntegerClientKeyV2 {
type Error = Infallible;
fn upgrade(self) -> Result<IntegerClientKey, Self::Error> {
Ok(IntegerClientKey {
fn upgrade(self) -> Result<IntegerClientKeyV3, Self::Error> {
Ok(IntegerClientKeyV3 {
key: self.key,
dedicated_compact_private_key: self.dedicated_compact_private_key,
compression_key: self
@@ -172,13 +175,40 @@ impl Upgrade<IntegerClientKey> for IntegerClientKeyV2 {
}
}
#[derive(Version)]
pub(crate) struct IntegerClientKeyV3 {
pub(crate) key: crate::integer::ClientKey,
pub(crate) dedicated_compact_private_key: Option<CompactPrivateKey>,
pub(crate) compression_key: Option<crate::integer::compression_keys::CompressionPrivateKeys>,
}
impl Upgrade<IntegerClientKey> for IntegerClientKeyV3 {
type Error = Infallible;
fn upgrade(self) -> Result<IntegerClientKey, Self::Error> {
let Self {
key,
dedicated_compact_private_key,
compression_key,
} = self;
Ok(IntegerClientKey {
key,
dedicated_compact_private_key,
compression_key,
noise_squashing_private_key: None,
})
}
}
#[derive(VersionsDispatch)]
#[allow(unused)]
pub(crate) enum IntegerClientKeyVersions {
V0(Deprecated<IntegerClientKey>),
V1(Deprecated<IntegerClientKey>),
V2(IntegerClientKeyV2),
V3(IntegerClientKey),
V3(IntegerClientKeyV3),
V4(IntegerClientKey),
}
impl Deprecable for IntegerServerKey {
@@ -186,13 +216,44 @@ impl Deprecable for IntegerServerKey {
const MIN_SUPPORTED_APP_VERSION: &'static str = "TFHE-rs v0.10";
}
#[derive(Version)]
pub struct IntegerServerKeyV4 {
pub(crate) key: crate::integer::ServerKey,
pub(crate) cpk_key_switching_key_material:
Option<crate::integer::key_switching_key::KeySwitchingKeyMaterial>,
pub(crate) compression_key: Option<CompressionKey>,
pub(crate) decompression_key: Option<DecompressionKey>,
}
impl Upgrade<IntegerServerKey> for IntegerServerKeyV4 {
type Error = Infallible;
fn upgrade(self) -> Result<IntegerServerKey, Self::Error> {
let Self {
key,
cpk_key_switching_key_material,
compression_key,
decompression_key,
} = self;
Ok(IntegerServerKey {
key,
cpk_key_switching_key_material,
compression_key,
decompression_key,
noise_squashing_key: None,
})
}
}
#[derive(VersionsDispatch)]
pub enum IntegerServerKeyVersions {
V0(Deprecated<IntegerServerKey>),
V1(Deprecated<IntegerServerKey>),
V2(Deprecated<IntegerServerKey>),
V3(Deprecated<IntegerServerKey>),
V4(IntegerServerKey),
V4(IntegerServerKeyV4),
V5(IntegerServerKey),
}
impl Deprecable for IntegerCompressedServerKey {
@@ -200,11 +261,42 @@ impl Deprecable for IntegerCompressedServerKey {
const MIN_SUPPORTED_APP_VERSION: &'static str = "TFHE-rs v0.10";
}
#[derive(Version)]
pub struct IntegerCompressedServerKeyV2 {
pub(crate) key: crate::integer::CompressedServerKey,
pub(crate) cpk_key_switching_key_material:
Option<crate::integer::key_switching_key::CompressedKeySwitchingKeyMaterial>,
pub(crate) compression_key: Option<CompressedCompressionKey>,
pub(crate) decompression_key: Option<CompressedDecompressionKey>,
}
impl Upgrade<IntegerCompressedServerKey> for IntegerCompressedServerKeyV2 {
type Error = Infallible;
fn upgrade(self) -> Result<IntegerCompressedServerKey, Self::Error> {
let Self {
key,
cpk_key_switching_key_material,
compression_key,
decompression_key,
} = self;
Ok(IntegerCompressedServerKey {
key,
cpk_key_switching_key_material,
compression_key,
decompression_key,
noise_squashing_key: None,
})
}
}
#[derive(VersionsDispatch)]
pub enum IntegerCompressedServerKeyVersions {
V0(Deprecated<IntegerCompressedServerKey>),
V1(Deprecated<IntegerCompressedServerKey>),
V2(IntegerCompressedServerKey),
V2(IntegerCompressedServerKeyV2),
V3(IntegerCompressedServerKey),
}
#[derive(VersionsDispatch)]

View File

@@ -1,13 +1,16 @@
pub use base::{FheBool, FheBoolConformanceParams};
pub use compressed::CompressedFheBool;
pub use squashed_noise::SquashedNoiseFheBool;
pub(in crate::high_level_api) use compressed::InnerCompressedFheBool;
pub(in crate::high_level_api) use inner::{InnerBoolean, InnerBooleanVersionOwned};
pub(in crate::high_level_api) use squashed_noise::InnerSquashedNoiseBooleanVersionOwned;
mod base;
mod compressed;
mod encrypt;
mod inner;
mod oprf;
mod squashed_noise;
#[cfg(test)]
mod tests;

View File

@@ -0,0 +1,185 @@
use super::base::FheBool;
use crate::backward_compatibility::booleans::{
InnerSquashedNoiseBooleanVersionedOwned, SquashedNoiseFheBoolVersions,
};
use crate::high_level_api::details::MaybeCloned;
use crate::high_level_api::errors::UninitializedNoiseSquashing;
use crate::high_level_api::global_state;
use crate::high_level_api::global_state::with_internal_keys;
use crate::high_level_api::keys::InternalServerKey;
use crate::high_level_api::traits::{FheDecrypt, SquashNoise};
use crate::integer::ciphertext::SquashedNoiseBooleanBlock;
use crate::named::Named;
use crate::{ClientKey, Device, Tag};
use serde::{Deserializer, Serializer};
use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned};
/// Enum that manages the current inner representation of a boolean.
pub(in crate::high_level_api) enum InnerSquashedNoiseBoolean {
Cpu(SquashedNoiseBooleanBlock),
}
impl Clone for InnerSquashedNoiseBoolean {
fn clone(&self) -> Self {
match self {
Self::Cpu(inner) => Self::Cpu(inner.clone()),
}
}
}
impl serde::Serialize for InnerSquashedNoiseBoolean {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.on_cpu().serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for InnerSquashedNoiseBoolean {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let mut deserialized = Self::Cpu(
crate::integer::ciphertext::SquashedNoiseBooleanBlock::deserialize(deserializer)?,
);
deserialized.move_to_device_of_server_key_if_set();
Ok(deserialized)
}
}
// Only CPU data are serialized so we only versionize the CPU type.
#[derive(serde::Serialize, serde::Deserialize)]
#[cfg_attr(dylint_lib = "tfhe_lints", allow(serialize_without_versionize))]
pub(crate) struct InnerSquashedNoiseBooleanVersionOwned(
<crate::integer::ciphertext::SquashedNoiseBooleanBlock as VersionizeOwned>::VersionedOwned,
);
impl Versionize for InnerSquashedNoiseBoolean {
type Versioned<'vers> = InnerSquashedNoiseBooleanVersionedOwned;
fn versionize(&self) -> Self::Versioned<'_> {
let data = self.on_cpu();
let versioned = data.into_owned().versionize_owned();
InnerSquashedNoiseBooleanVersionedOwned::V0(InnerSquashedNoiseBooleanVersionOwned(
versioned,
))
}
}
impl VersionizeOwned for InnerSquashedNoiseBoolean {
type VersionedOwned = InnerSquashedNoiseBooleanVersionedOwned;
fn versionize_owned(self) -> Self::VersionedOwned {
let cpu_data = self.on_cpu();
InnerSquashedNoiseBooleanVersionedOwned::V0(InnerSquashedNoiseBooleanVersionOwned(
cpu_data.into_owned().versionize_owned(),
))
}
}
impl Unversionize for InnerSquashedNoiseBoolean {
fn unversionize(versioned: Self::VersionedOwned) -> Result<Self, UnversionizeError> {
match versioned {
InnerSquashedNoiseBooleanVersionedOwned::V0(v0) => {
let mut unversioned = Self::Cpu(
crate::integer::ciphertext::SquashedNoiseBooleanBlock::unversionize(v0.0)?,
);
unversioned.move_to_device_of_server_key_if_set();
Ok(unversioned)
}
}
}
}
impl InnerSquashedNoiseBoolean {
/// Returns the inner cpu ciphertext if self is on the CPU, otherwise, returns a copy
/// that is on the CPU
pub(crate) fn on_cpu(&self) -> MaybeCloned<'_, SquashedNoiseBooleanBlock> {
match self {
Self::Cpu(ct) => MaybeCloned::Borrowed(ct),
}
}
#[allow(clippy::needless_pass_by_ref_mut)]
pub(crate) fn move_to_device(&mut self, device: Device) {
match (&self, device) {
(Self::Cpu(_), Device::Cpu) => {
// Nothing to do, we already are on the correct device
}
#[cfg(feature = "gpu")]
_ => panic!("Cuda devices do not support noise squashing yet"),
}
}
#[inline]
pub(crate) fn move_to_device_of_server_key_if_set(&mut self) {
if let Some(device) = global_state::device_of_internal_keys() {
self.move_to_device(device);
}
}
}
#[derive(Clone, serde::Deserialize, serde::Serialize, Versionize)]
#[versionize(SquashedNoiseFheBoolVersions)]
pub struct SquashedNoiseFheBool {
inner: InnerSquashedNoiseBoolean,
tag: Tag,
}
impl Named for SquashedNoiseFheBool {
const NAME: &'static str = "high_level_api::SquashedNoiseFheBool";
}
impl SquashedNoiseFheBool {
pub fn underlying_squashed_noise_ciphertext(
&self,
) -> MaybeCloned<'_, SquashedNoiseBooleanBlock> {
self.inner.on_cpu()
}
}
impl FheDecrypt<bool> for SquashedNoiseFheBool {
fn decrypt(&self, key: &ClientKey) -> bool {
key.key
.noise_squashing_private_key
.as_ref()
.map(|noise_squashing_private_key| {
noise_squashing_private_key.decrypt_bool(&self.inner.on_cpu())
})
.expect(
"No noise squashing private key in your ClientKey, cannot decrypt. \
Did you call `enable_noise_squashing` when creating your Config?",
)
.unwrap()
}
}
impl SquashNoise for FheBool {
type Output = SquashedNoiseFheBool;
fn squash_noise(&self) -> crate::Result<Self::Output> {
with_internal_keys(|keys| match keys {
InternalServerKey::Cpu(server_key) => {
let noise_squashing_key = server_key
.key
.noise_squashing_key
.as_ref()
.ok_or(UninitializedNoiseSquashing)?;
Ok(SquashedNoiseFheBool {
inner: InnerSquashedNoiseBoolean::Cpu(
noise_squashing_key.squash_boolean_block_noise(
server_key.key.pbs_key(),
&self.ciphertext.on_cpu(),
)?,
),
tag: server_key.tag.clone(),
})
}
#[cfg(feature = "gpu")]
InternalServerKey::Cuda(_) => Err(crate::error!(
"Cuda devices do not support noise squashing yet"
)),
})
}
}

View File

@@ -3,6 +3,7 @@ use tfhe_versionable::Versionize;
use crate::backward_compatibility::config::ConfigVersions;
use crate::high_level_api::keys::IntegerConfig;
use crate::shortint::parameters::list_compression::CompressionParameters;
use crate::shortint::parameters::NoiseSquashingParameters;
/// The config type
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize, Versionize)]
@@ -48,13 +49,24 @@ impl ConfigBuilder {
self
}
pub fn enable_noise_squashing(
mut self,
noise_squashing_parameters: NoiseSquashingParameters,
) -> Self {
self.config
.inner
.enable_noise_squashing(noise_squashing_parameters);
self
}
pub fn with_custom_parameters<P>(block_parameters: P) -> Self
where
P: Into<crate::shortint::PBSParameters>,
{
Self {
config: Config {
inner: IntegerConfig::new(block_parameters.into(), None),
inner: IntegerConfig::new(block_parameters.into()),
},
}
}
@@ -75,7 +87,7 @@ impl ConfigBuilder {
where
P: Into<crate::shortint::PBSParameters>,
{
self.config.inner = IntegerConfig::new(block_parameters.into(), None);
self.config.inner = IntegerConfig::new(block_parameters.into());
self
}

View File

@@ -1,9 +1,10 @@
use std::ops::Deref;
/// 'smart-pointer' that holds either a borrowed T, or an owned T.
pub(crate) enum MaybeCloned<'a, T> {
///
/// This is essentially like a Cow, except T does not need to be ToOwned
pub enum MaybeCloned<'a, T> {
Borrowed(&'a T),
#[allow(dead_code)]
Cloned(T),
}

View File

@@ -45,3 +45,24 @@ impl From<UninitializedServerKey> for Error {
Self::new(format!("{value}"))
}
}
#[derive(Debug)]
pub struct UninitializedNoiseSquashing;
impl Display for UninitializedNoiseSquashing {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Noise squashing key not set in server key, \
did you forget to call `enable_noise_squashing` when building your Config?",
)
}
}
impl std::error::Error for UninitializedNoiseSquashing {}
impl From<UninitializedNoiseSquashing> for Error {
fn from(value: UninitializedNoiseSquashing) -> Self {
Self::new(format!("{value}"))
}
}

View File

@@ -31,17 +31,18 @@ expand_pub_use_fhe_type!(
);
pub(in crate::high_level_api) use signed::{
CompressedSignedRadixCiphertext, FheIntId, SignedRadixCiphertextVersionOwned,
CompressedSignedRadixCiphertext, FheIntId, InnerSquashedNoiseSignedRadixCiphertextVersionOwned,
SignedRadixCiphertextVersionOwned,
};
pub(in crate::high_level_api) use unsigned::{
CompressedRadixCiphertext, FheUintId,
CompressedRadixCiphertext, FheUintId, InnerSquashedNoiseRadixCiphertextVersionOwned,
RadixCiphertextVersionOwned as UnsignedRadixCiphertextVersionOwned,
};
// These are pub-exported so that their doc can appear in generated rust docs
use crate::high_level_api::traits::FheId;
use crate::shortint::MessageModulus;
pub use signed::{CompressedFheInt, FheInt};
pub use unsigned::{CompressedFheUint, FheUint};
pub use signed::{CompressedFheInt, FheInt, SquashedNoiseFheInt};
pub use unsigned::{CompressedFheUint, FheUint, SquashedNoiseFheUint};
pub mod oprf;
pub(super) mod signed;

View File

@@ -1,5 +1,6 @@
mod base;
mod compressed;
mod squashed_noise;
mod encrypt;
mod inner;
@@ -16,6 +17,8 @@ pub(in crate::high_level_api) use compressed::CompressedSignedRadixCiphertext;
pub(in crate::high_level_api) use inner::{
SignedRadixCiphertext, SignedRadixCiphertextVersionOwned,
};
pub(in crate::high_level_api) use squashed_noise::InnerSquashedNoiseSignedRadixCiphertextVersionOwned;
pub use squashed_noise::SquashedNoiseFheInt;
expand_pub_use_fhe_type!(
pub use static_{

View File

@@ -0,0 +1,204 @@
use super::base::{FheInt, FheIntId};
use crate::backward_compatibility::integers::{
InnerSquashedNoiseSignedRadixCiphertextVersionedOwned, SquashedNoiseFheIntVersions,
};
use crate::high_level_api::details::MaybeCloned;
use crate::high_level_api::errors::UninitializedNoiseSquashing;
use crate::high_level_api::global_state;
use crate::high_level_api::global_state::with_internal_keys;
use crate::high_level_api::keys::InternalServerKey;
use crate::high_level_api::traits::{FheDecrypt, SquashNoise};
use crate::integer::block_decomposition::{RecomposableFrom, SignExtendable};
use crate::named::Named;
use crate::{ClientKey, Device, Tag};
use serde::{Deserializer, Serializer};
use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned};
/// Enum that manages the current inner representation of a squashed noise FheInt .
pub(in crate::high_level_api) enum InnerSquashedNoiseSignedRadixCiphertext {
Cpu(crate::integer::ciphertext::SquashedNoiseSignedRadixCiphertext),
}
impl Clone for InnerSquashedNoiseSignedRadixCiphertext {
fn clone(&self) -> Self {
match self {
Self::Cpu(inner) => Self::Cpu(inner.clone()),
}
}
}
impl serde::Serialize for InnerSquashedNoiseSignedRadixCiphertext {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.on_cpu().serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for InnerSquashedNoiseSignedRadixCiphertext {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let mut deserialized = Self::Cpu(
crate::integer::ciphertext::SquashedNoiseSignedRadixCiphertext::deserialize(
deserializer,
)?,
);
deserialized.move_to_device_of_server_key_if_set();
Ok(deserialized)
}
}
// Only CPU data are serialized so we only versionize the CPU type.
#[derive(serde::Serialize, serde::Deserialize)]
#[cfg_attr(dylint_lib = "tfhe_lints", allow(serialize_without_versionize))]
pub(crate) struct InnerSquashedNoiseSignedRadixCiphertextVersionOwned(
<crate::integer::ciphertext::SquashedNoiseSignedRadixCiphertext as VersionizeOwned>::VersionedOwned,
);
impl Versionize for InnerSquashedNoiseSignedRadixCiphertext {
type Versioned<'vers> = InnerSquashedNoiseSignedRadixCiphertextVersionedOwned;
fn versionize(&self) -> Self::Versioned<'_> {
let data = self.on_cpu();
let versioned = data.into_owned().versionize_owned();
InnerSquashedNoiseSignedRadixCiphertextVersionedOwned::V0(
InnerSquashedNoiseSignedRadixCiphertextVersionOwned(versioned),
)
}
}
impl VersionizeOwned for InnerSquashedNoiseSignedRadixCiphertext {
type VersionedOwned = InnerSquashedNoiseSignedRadixCiphertextVersionedOwned;
fn versionize_owned(self) -> Self::VersionedOwned {
let cpu_data = self.on_cpu();
InnerSquashedNoiseSignedRadixCiphertextVersionedOwned::V0(
InnerSquashedNoiseSignedRadixCiphertextVersionOwned(
cpu_data.into_owned().versionize_owned(),
),
)
}
}
impl Unversionize for InnerSquashedNoiseSignedRadixCiphertext {
fn unversionize(versioned: Self::VersionedOwned) -> Result<Self, UnversionizeError> {
match versioned {
InnerSquashedNoiseSignedRadixCiphertextVersionedOwned::V0(v0) => {
let mut unversioned = Self::Cpu(
crate::integer::ciphertext::SquashedNoiseSignedRadixCiphertext::unversionize(
v0.0,
)?,
);
unversioned.move_to_device_of_server_key_if_set();
Ok(unversioned)
}
}
}
}
impl InnerSquashedNoiseSignedRadixCiphertext {
/// Returns the inner cpu ciphertext if self is on the CPU, otherwise, returns a copy
/// that is on the CPU
pub(crate) fn on_cpu(
&self,
) -> MaybeCloned<'_, crate::integer::ciphertext::SquashedNoiseSignedRadixCiphertext> {
match self {
Self::Cpu(ct) => MaybeCloned::Borrowed(ct),
}
}
#[allow(clippy::needless_pass_by_ref_mut)]
pub(crate) fn move_to_device(&mut self, device: Device) {
match (&self, device) {
(Self::Cpu(_), Device::Cpu) => {
// Nothing to do, we already are on the correct device
}
#[cfg(feature = "gpu")]
_ => panic!("Cuda devices do not support noise squashing yet"),
}
}
#[inline]
pub(crate) fn move_to_device_of_server_key_if_set(&mut self) {
if let Some(device) = global_state::device_of_internal_keys() {
self.move_to_device(device);
}
}
}
#[derive(Clone, serde::Deserialize, serde::Serialize, Versionize)]
#[versionize(SquashedNoiseFheIntVersions)]
pub struct SquashedNoiseFheInt {
inner: InnerSquashedNoiseSignedRadixCiphertext,
tag: Tag,
}
impl Named for SquashedNoiseFheInt {
const NAME: &'static str = "high_level_api::SquashedNoiseFheInt";
}
impl SquashedNoiseFheInt {
pub fn underlying_squashed_noise_ciphertext(
&self,
) -> MaybeCloned<'_, crate::integer::ciphertext::SquashedNoiseSignedRadixCiphertext> {
self.inner.on_cpu()
}
pub fn num_bits(&self) -> usize {
match &self.inner {
InnerSquashedNoiseSignedRadixCiphertext::Cpu(on_cpu) => {
on_cpu.original_block_count * on_cpu.packed_blocks[0].message_modulus().0 as usize
}
}
}
}
impl<Clear> FheDecrypt<Clear> for SquashedNoiseFheInt
where
Clear: RecomposableFrom<u128> + SignExtendable,
{
fn decrypt(&self, key: &ClientKey) -> Clear {
key.key
.noise_squashing_private_key
.as_ref()
.map(|noise_squashing_private_key| {
noise_squashing_private_key.decrypt_signed_radix(&self.inner.on_cpu())
})
.expect(
"No noise squashing private key in your ClientKey, cannot decrypt. \
Did you call `enable_noise_squashing` when creating your Config?",
)
.unwrap()
}
}
impl<Id: FheIntId> SquashNoise for FheInt<Id> {
type Output = SquashedNoiseFheInt;
fn squash_noise(&self) -> crate::Result<Self::Output> {
with_internal_keys(|keys| match keys {
InternalServerKey::Cpu(server_key) => {
let noise_squashing_key = server_key
.key
.noise_squashing_key
.as_ref()
.ok_or(UninitializedNoiseSquashing)?;
Ok(SquashedNoiseFheInt {
inner: InnerSquashedNoiseSignedRadixCiphertext::Cpu(
noise_squashing_key.squash_signed_radix_ciphertext_noise(
server_key.key.pbs_key(),
&self.ciphertext.on_cpu(),
)?,
),
tag: server_key.tag.clone(),
})
}
#[cfg(feature = "gpu")]
InternalServerKey::Cuda(_) => Err(crate::error!(
"Cuda devices do not support noise squashing yet"
)),
})
}
}

View File

@@ -18,12 +18,15 @@ expand_pub_use_fhe_type!(
);
pub use compressed::CompressedFheUint;
pub use squashed_noise::SquashedNoiseFheUint;
pub(in crate::high_level_api) use compressed::CompressedRadixCiphertext;
pub(in crate::high_level_api) use inner::{RadixCiphertext, RadixCiphertextVersionOwned};
pub(in crate::high_level_api) use squashed_noise::InnerSquashedNoiseRadixCiphertextVersionOwned;
mod base;
mod compressed;
mod squashed_noise;
mod static_;
mod encrypt;

View File

@@ -0,0 +1,199 @@
use super::base::{FheUint, FheUintId};
use crate::backward_compatibility::integers::{
InnerSquashedNoiseRadixCiphertextVersionedOwned, SquashedNoiseFheUintVersions,
};
use crate::core_crypto::commons::numeric::UnsignedNumeric;
use crate::high_level_api::details::MaybeCloned;
use crate::high_level_api::errors::UninitializedNoiseSquashing;
use crate::high_level_api::global_state;
use crate::high_level_api::global_state::with_internal_keys;
use crate::high_level_api::keys::InternalServerKey;
use crate::high_level_api::traits::{FheDecrypt, SquashNoise};
use crate::integer::block_decomposition::RecomposableFrom;
use crate::named::Named;
use crate::{ClientKey, Device, Tag};
use serde::{Deserializer, Serializer};
use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned};
/// Enum that manages the current inner representation of a squashed noise FheUint .
pub(in crate::high_level_api) enum InnerSquashedNoiseRadixCiphertext {
Cpu(crate::integer::ciphertext::SquashedNoiseRadixCiphertext),
}
impl Clone for InnerSquashedNoiseRadixCiphertext {
fn clone(&self) -> Self {
match self {
Self::Cpu(inner) => Self::Cpu(inner.clone()),
}
}
}
impl serde::Serialize for InnerSquashedNoiseRadixCiphertext {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.on_cpu().serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for InnerSquashedNoiseRadixCiphertext {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let mut deserialized = Self::Cpu(
crate::integer::ciphertext::SquashedNoiseRadixCiphertext::deserialize(deserializer)?,
);
deserialized.move_to_device_of_server_key_if_set();
Ok(deserialized)
}
}
// Only CPU data are serialized so we only versionize the CPU type.
#[derive(serde::Serialize, serde::Deserialize)]
#[cfg_attr(dylint_lib = "tfhe_lints", allow(serialize_without_versionize))]
pub(crate) struct InnerSquashedNoiseRadixCiphertextVersionOwned(
<crate::integer::ciphertext::SquashedNoiseRadixCiphertext as VersionizeOwned>::VersionedOwned,
);
impl Versionize for InnerSquashedNoiseRadixCiphertext {
type Versioned<'vers> = InnerSquashedNoiseRadixCiphertextVersionedOwned;
fn versionize(&self) -> Self::Versioned<'_> {
let data = self.on_cpu();
let versioned = data.into_owned().versionize_owned();
InnerSquashedNoiseRadixCiphertextVersionedOwned::V0(
InnerSquashedNoiseRadixCiphertextVersionOwned(versioned),
)
}
}
impl VersionizeOwned for InnerSquashedNoiseRadixCiphertext {
type VersionedOwned = InnerSquashedNoiseRadixCiphertextVersionedOwned;
fn versionize_owned(self) -> Self::VersionedOwned {
let cpu_data = self.on_cpu();
InnerSquashedNoiseRadixCiphertextVersionedOwned::V0(
InnerSquashedNoiseRadixCiphertextVersionOwned(cpu_data.into_owned().versionize_owned()),
)
}
}
impl Unversionize for InnerSquashedNoiseRadixCiphertext {
fn unversionize(versioned: Self::VersionedOwned) -> Result<Self, UnversionizeError> {
match versioned {
InnerSquashedNoiseRadixCiphertextVersionedOwned::V0(v0) => {
let mut unversioned = Self::Cpu(
crate::integer::ciphertext::SquashedNoiseRadixCiphertext::unversionize(v0.0)?,
);
unversioned.move_to_device_of_server_key_if_set();
Ok(unversioned)
}
}
}
}
impl InnerSquashedNoiseRadixCiphertext {
/// Returns the inner cpu ciphertext if self is on the CPU, otherwise, returns a copy
/// that is on the CPU
pub(crate) fn on_cpu(
&self,
) -> MaybeCloned<'_, crate::integer::ciphertext::SquashedNoiseRadixCiphertext> {
match self {
Self::Cpu(ct) => MaybeCloned::Borrowed(ct),
}
}
#[allow(clippy::needless_pass_by_ref_mut)]
pub(crate) fn move_to_device(&mut self, device: Device) {
match (&self, device) {
(Self::Cpu(_), Device::Cpu) => {
// Nothing to do, we already are on the correct device
}
#[cfg(feature = "gpu")]
_ => panic!("Cuda devices do not support noise squashing yet"),
}
}
#[inline]
pub(crate) fn move_to_device_of_server_key_if_set(&mut self) {
if let Some(device) = global_state::device_of_internal_keys() {
self.move_to_device(device);
}
}
}
#[derive(Clone, serde::Deserialize, serde::Serialize, Versionize)]
#[versionize(SquashedNoiseFheUintVersions)]
pub struct SquashedNoiseFheUint {
inner: InnerSquashedNoiseRadixCiphertext,
tag: Tag,
}
impl Named for SquashedNoiseFheUint {
const NAME: &'static str = "high_level_api::SquashedNoiseFheUint";
}
impl SquashedNoiseFheUint {
pub fn underlying_squashed_noise_ciphertext(
&self,
) -> MaybeCloned<'_, crate::integer::ciphertext::SquashedNoiseRadixCiphertext> {
self.inner.on_cpu()
}
pub fn num_bits(&self) -> usize {
match &self.inner {
InnerSquashedNoiseRadixCiphertext::Cpu(on_cpu) => {
on_cpu.original_block_count * on_cpu.packed_blocks[0].message_modulus().0 as usize
}
}
}
}
impl<Clear> FheDecrypt<Clear> for SquashedNoiseFheUint
where
Clear: RecomposableFrom<u128> + UnsignedNumeric,
{
fn decrypt(&self, key: &ClientKey) -> Clear {
key.key
.noise_squashing_private_key
.as_ref()
.map(|noise_squashing_private_key| {
noise_squashing_private_key.decrypt_radix(&self.inner.on_cpu())
})
.expect(
"No noise squashing private key in your ClientKey, cannot decrypt. \
Did you call `enable_noise_squashing` when creating your Config?",
)
.unwrap()
}
}
impl<Id: FheUintId> SquashNoise for FheUint<Id> {
type Output = SquashedNoiseFheUint;
fn squash_noise(&self) -> crate::Result<Self::Output> {
with_internal_keys(|keys| match keys {
InternalServerKey::Cpu(server_key) => {
let noise_squashing_key = server_key
.key
.noise_squashing_key
.as_ref()
.ok_or(UninitializedNoiseSquashing)?;
Ok(SquashedNoiseFheUint {
inner: InnerSquashedNoiseRadixCiphertext::Cpu(
noise_squashing_key.squash_radix_ciphertext_noise(
server_key.key.pbs_key(),
&self.ciphertext.on_cpu(),
)?,
),
tag: server_key.tag.clone(),
})
}
#[cfg(feature = "gpu")]
InternalServerKey::Cuda(_) => Err(crate::error!(
"Cuda devices do not support noise squashing yet"
)),
})
}
}

View File

@@ -7,6 +7,7 @@ use crate::high_level_api::backward_compatibility::keys::ClientKeyVersions;
use crate::high_level_api::config::Config;
use crate::high_level_api::keys::{CompactPrivateKey, IntegerClientKey};
use crate::integer::compression_keys::CompressionPrivateKeys;
use crate::integer::noise_squashing::NoiseSquashingPrivateKey;
use crate::named::Named;
use crate::prelude::Tagged;
use crate::shortint::MessageModulus;
@@ -80,10 +81,11 @@ impl ClientKey {
crate::integer::ClientKey,
Option<CompactPrivateKey>,
Option<CompressionPrivateKeys>,
Option<NoiseSquashingPrivateKey>,
Tag,
) {
let (cks, cpk, cppk) = self.key.into_raw_parts();
(cks, cpk, cppk, self.tag)
let (cks, cpk, cppk, nsk) = self.key.into_raw_parts();
(cks, cpk, cppk, nsk, self.tag)
}
pub fn from_raw_parts(
@@ -93,6 +95,7 @@ impl ClientKey {
crate::shortint::parameters::key_switching::ShortintKeySwitchingParameters,
)>,
compression_key: Option<CompressionPrivateKeys>,
noise_squashing_key: Option<NoiseSquashingPrivateKey>,
tag: Tag,
) -> Self {
Self {
@@ -100,6 +103,7 @@ impl ClientKey {
key,
dedicated_compact_private_key,
compression_key,
noise_squashing_key,
),
tag,
}

View File

@@ -6,12 +6,15 @@ use crate::integer::compression_keys::{
CompressedCompressionKey, CompressedDecompressionKey, CompressionKey, CompressionPrivateKeys,
DecompressionKey,
};
use crate::integer::noise_squashing::{
CompressedNoiseSquashingKey, NoiseSquashingKey, NoiseSquashingPrivateKey,
};
use crate::integer::public_key::CompactPublicKey;
use crate::integer::CompressedCompactPublicKey;
use crate::shortint::key_switching_key::KeySwitchingKeyConformanceParams;
use crate::shortint::parameters::list_compression::CompressionParameters;
use crate::shortint::parameters::{
CompactPublicKeyEncryptionParameters, ShortintKeySwitchingParameters,
CompactPublicKeyEncryptionParameters, NoiseSquashingParameters, ShortintKeySwitchingParameters,
};
use crate::shortint::{EncryptionKeyChoice, MessageModulus, PBSParameters};
use crate::{Config, Error};
@@ -30,28 +33,31 @@ pub(crate) struct IntegerConfig {
crate::shortint::parameters::ShortintKeySwitchingParameters,
)>,
pub(crate) compression_parameters: Option<CompressionParameters>,
pub(crate) noise_squashing_parameters: Option<NoiseSquashingParameters>,
}
impl IntegerConfig {
pub(crate) fn new(
block_parameters: crate::shortint::PBSParameters,
dedicated_compact_public_key_parameters: Option<(
crate::shortint::parameters::CompactPublicKeyEncryptionParameters,
crate::shortint::parameters::ShortintKeySwitchingParameters,
)>,
) -> Self {
pub(crate) fn new(block_parameters: crate::shortint::PBSParameters) -> Self {
Self {
block_parameters,
dedicated_compact_public_key_parameters,
dedicated_compact_public_key_parameters: None,
compression_parameters: None,
noise_squashing_parameters: None,
}
}
pub fn enable_compression(&mut self, compression_parameters: CompressionParameters) {
pub(crate) fn enable_compression(&mut self, compression_parameters: CompressionParameters) {
self.compression_parameters = Some(compression_parameters);
}
pub fn public_key_encryption_parameters(
pub(crate) fn enable_noise_squashing(
&mut self,
compression_parameters: NoiseSquashingParameters,
) {
self.noise_squashing_parameters = Some(compression_parameters);
}
pub(crate) fn public_key_encryption_parameters(
&self,
) -> Result<crate::shortint::parameters::CompactPublicKeyEncryptionParameters, crate::Error>
{
@@ -76,6 +82,7 @@ impl Default for IntegerConfig {
block_parameters: params,
dedicated_compact_public_key_parameters: None,
compression_parameters: None,
noise_squashing_parameters: None,
}
}
}
@@ -91,6 +98,7 @@ pub(crate) struct IntegerClientKey {
pub(crate) key: crate::integer::ClientKey,
pub(crate) dedicated_compact_private_key: Option<CompactPrivateKey>,
pub(crate) compression_key: Option<CompressionPrivateKeys>,
pub(crate) noise_squashing_private_key: Option<NoiseSquashingPrivateKey>,
}
impl IntegerClientKey {
@@ -112,10 +120,16 @@ impl IntegerClientKey {
let dedicated_compact_private_key = config
.dedicated_compact_public_key_parameters
.map(|p| (crate::integer::CompactPrivateKey::new(p.0), p.1));
let noise_squashing_private_key = config
.noise_squashing_parameters
.map(NoiseSquashingPrivateKey::new);
Self {
key,
dedicated_compact_private_key,
compression_key,
noise_squashing_private_key,
}
}
@@ -126,13 +140,20 @@ impl IntegerClientKey {
crate::integer::ClientKey,
Option<CompactPrivateKey>,
Option<CompressionPrivateKeys>,
Option<NoiseSquashingPrivateKey>,
) {
let Self {
key,
dedicated_compact_private_key,
compression_key,
noise_squashing_private_key,
} = self;
(key, dedicated_compact_private_key, compression_key)
(
key,
dedicated_compact_private_key,
compression_key,
noise_squashing_private_key,
)
}
/// Construct a, [`IntegerClientKey`] from its constituents.
@@ -144,6 +165,7 @@ impl IntegerClientKey {
key: crate::integer::ClientKey,
dedicated_compact_private_key: Option<CompactPrivateKey>,
compression_key: Option<CompressionPrivateKeys>,
noise_squashing_private_key: Option<NoiseSquashingPrivateKey>,
) -> Self {
let shortint_cks: &crate::shortint::ClientKey = key.as_ref();
@@ -170,6 +192,7 @@ impl IntegerClientKey {
key,
dedicated_compact_private_key,
compression_key,
noise_squashing_private_key,
}
}
@@ -195,10 +218,15 @@ impl From<IntegerConfig> for IntegerClientKey {
.compression_parameters
.map(|params| key.new_compression_private_key(params));
let noise_squashing_private_key = config
.noise_squashing_parameters
.map(NoiseSquashingPrivateKey::new);
Self {
key,
dedicated_compact_private_key,
compression_key,
noise_squashing_private_key,
}
}
}
@@ -215,6 +243,7 @@ pub struct IntegerServerKey {
Option<crate::integer::key_switching_key::KeySwitchingKeyMaterial>,
pub(crate) compression_key: Option<CompressionKey>,
pub(crate) decompression_key: Option<DecompressionKey>,
pub(crate) noise_squashing_key: Option<NoiseSquashingKey>,
}
impl IntegerServerKey {
@@ -246,11 +275,18 @@ impl IntegerServerKey {
build_helper.into()
});
let noise_squashing_key = client_key
.noise_squashing_private_key
.as_ref()
.map(|key| NoiseSquashingKey::new(cks, key));
Self {
key: base_integer_key,
cpk_key_switching_key_material,
compression_key,
decompression_key,
noise_squashing_key,
}
}
@@ -292,6 +328,7 @@ pub struct IntegerCompressedServerKey {
Option<crate::integer::key_switching_key::CompressedKeySwitchingKeyMaterial>,
pub(crate) compression_key: Option<CompressedCompressionKey>,
pub(crate) decompression_key: Option<CompressedDecompressionKey>,
pub(crate) noise_squashing_key: Option<CompressedNoiseSquashingKey>,
}
impl IntegerCompressedServerKey {
@@ -327,11 +364,20 @@ impl IntegerCompressedServerKey {
(Some(compression_keys), Some(decompression_keys))
});
let noise_squashing_key =
client_key
.noise_squashing_private_key
.as_ref()
.map(|noise_squashing_private_key| {
noise_squashing_private_key.new_compressed_noise_squashing_key(&client_key.key)
});
Self {
key,
cpk_key_switching_key_material,
compression_key,
decompression_key,
noise_squashing_key,
}
}
@@ -358,12 +404,14 @@ impl IntegerCompressedServerKey {
>,
compression_key: Option<CompressedCompressionKey>,
decompression_key: Option<CompressedDecompressionKey>,
noise_squashing_key: Option<CompressedNoiseSquashingKey>,
) -> Self {
Self {
key,
cpk_key_switching_key_material,
compression_key,
decompression_key,
noise_squashing_key,
}
}
@@ -378,6 +426,11 @@ impl IntegerCompressedServerKey {
.as_ref()
.map(CompressedDecompressionKey::decompress);
let noise_squashing_key = self
.noise_squashing_key
.as_ref()
.map(CompressedNoiseSquashingKey::decompress);
IntegerServerKey {
key: self.key.decompress(),
cpk_key_switching_key_material: self.cpk_key_switching_key_material.as_ref().map(
@@ -385,6 +438,7 @@ impl IntegerCompressedServerKey {
),
compression_key,
decompression_key,
noise_squashing_key,
}
}
}
@@ -473,14 +527,17 @@ pub struct IntegerServerKeyConformanceParams {
ShortintKeySwitchingParameters,
)>,
pub compression_param: Option<CompressionParameters>,
pub noise_squashing_param: Option<NoiseSquashingParameters>,
}
impl From<Config> for IntegerServerKeyConformanceParams {
fn from(value: Config) -> Self {
impl<C: Into<Config>> From<C> for IntegerServerKeyConformanceParams {
fn from(value: C) -> Self {
let config: Config = value.into();
Self {
sk_param: value.inner.block_parameters,
cpk_param: value.inner.dedicated_compact_public_key_parameters,
compression_param: value.inner.compression_parameters,
sk_param: config.inner.block_parameters,
cpk_param: config.inner.dedicated_compact_public_key_parameters,
compression_param: config.inner.compression_parameters,
noise_squashing_param: config.inner.noise_squashing_parameters,
}
}
}
@@ -539,6 +596,7 @@ impl ParameterSetConformant for IntegerServerKey {
cpk_key_switching_key_material,
compression_key,
decompression_key,
noise_squashing_key,
} = self;
let cpk_key_switching_key_material_is_ok = match (
@@ -572,9 +630,27 @@ impl ParameterSetConformant for IntegerServerKey {
_ => return false,
};
let noise_squashing_key_is_ok = match (
parameter_set.noise_squashing_param.as_ref(),
noise_squashing_key.as_ref(),
) {
(None, None) => true,
(Some(noise_squashing_param), Some(noise_squashing_key)) => {
let noise_squashing_param =
(parameter_set.sk_param, *noise_squashing_param).try_into();
if let Ok(noise_squashing_param) = noise_squashing_param {
noise_squashing_key.is_conformant(&noise_squashing_param)
} else {
return false;
}
}
_ => return false,
};
key.is_conformant(&parameter_set.sk_param)
&& cpk_key_switching_key_material_is_ok
&& compression_is_ok
&& noise_squashing_key_is_ok
}
}
@@ -587,6 +663,7 @@ impl ParameterSetConformant for IntegerCompressedServerKey {
cpk_key_switching_key_material,
compression_key,
decompression_key,
noise_squashing_key,
} = self;
let cpk_key_switching_key_material_is_ok = match (
@@ -620,9 +697,27 @@ impl ParameterSetConformant for IntegerCompressedServerKey {
_ => return false,
};
let noise_squashing_key_is_ok = match (
parameter_set.noise_squashing_param.as_ref(),
noise_squashing_key.as_ref(),
) {
(None, None) => true,
(Some(noise_squashing_param), Some(noise_squashing_key)) => {
let noise_squashing_param =
(parameter_set.sk_param, *noise_squashing_param).try_into();
if let Ok(noise_squashing_param) = noise_squashing_param {
noise_squashing_key.is_conformant(&noise_squashing_param)
} else {
return false;
}
}
_ => return false,
};
key.is_conformant(&parameter_set.sk_param)
&& cpk_key_switching_key_material_is_ok
&& compression_is_ok
&& noise_squashing_key_is_ok
}
}

View File

@@ -11,6 +11,7 @@ use crate::high_level_api::keys::{IntegerCompressedServerKey, IntegerServerKey};
use crate::integer::compression_keys::{
CompressedCompressionKey, CompressedDecompressionKey, CompressionKey, DecompressionKey,
};
use crate::integer::noise_squashing::{CompressedNoiseSquashingKey, NoiseSquashingKey};
use crate::integer::parameters::IntegerCompactCiphertextListExpansionMode;
use crate::named::Named;
use crate::prelude::Tagged;
@@ -53,6 +54,7 @@ impl ServerKey {
Option<crate::integer::key_switching_key::KeySwitchingKeyMaterial>,
Option<CompressionKey>,
Option<DecompressionKey>,
Option<NoiseSquashingKey>,
Tag,
) {
let IntegerServerKey {
@@ -60,6 +62,7 @@ impl ServerKey {
cpk_key_switching_key_material,
compression_key,
decompression_key,
noise_squashing_key,
} = (*self.key).clone();
(
@@ -67,6 +70,7 @@ impl ServerKey {
cpk_key_switching_key_material,
compression_key,
decompression_key,
noise_squashing_key,
self.tag,
)
}
@@ -78,6 +82,7 @@ impl ServerKey {
>,
compression_key: Option<CompressionKey>,
decompression_key: Option<DecompressionKey>,
noise_squashing_key: Option<NoiseSquashingKey>,
tag: Tag,
) -> Self {
Self {
@@ -86,6 +91,7 @@ impl ServerKey {
cpk_key_switching_key_material,
compression_key,
decompression_key,
noise_squashing_key,
}),
tag,
}
@@ -239,6 +245,7 @@ impl CompressedServerKey {
>,
compression_key: Option<CompressedCompressionKey>,
decompression_key: Option<CompressedDecompressionKey>,
noise_squashing_key: Option<CompressedNoiseSquashingKey>,
tag: Tag,
) -> Self {
Self {
@@ -247,6 +254,7 @@ impl CompressedServerKey {
cpk_key_switching_key_material,
compression_key,
decompression_key,
noise_squashing_key,
),
tag,
}
@@ -432,6 +440,7 @@ mod test {
use crate::prelude::ParameterSetConformant;
use crate::shortint::parameters::{
COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
@@ -450,13 +459,7 @@ mod test {
let ck = ClientKey::generate(config);
let sk = ServerKey::new(&ck);
let sk_param = PBSParameters::PBS(PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128);
let conformance_params = IntegerServerKeyConformanceParams {
sk_param,
cpk_param: None,
compression_param: None,
};
let conformance_params = config.into();
assert!(sk.is_conformant(&conformance_params));
}
@@ -470,13 +473,7 @@ mod test {
let ck = ClientKey::generate(config);
let sk = ServerKey::new(&ck);
let sk_param = PBSParameters::PBS(PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128);
let conformance_params = IntegerServerKeyConformanceParams {
sk_param,
cpk_param: None,
compression_param: Some(COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128),
};
let conformance_params = config.into();
assert!(sk.is_conformant(&conformance_params));
}
@@ -488,18 +485,56 @@ mod test {
let casting_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let config = ConfigBuilder::with_custom_parameters(params)
.use_dedicated_compact_public_key_parameters((cpk_params, casting_params));
.use_dedicated_compact_public_key_parameters((cpk_params, casting_params))
.build();
let ck = ClientKey::generate(config);
let sk = ServerKey::new(&ck);
let sk_param = PBSParameters::PBS(PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128);
let conformance_params = config.into();
let conformance_params = IntegerServerKeyConformanceParams {
sk_param,
cpk_param: Some((cpk_params, casting_params)),
compression_param: None,
};
assert!(sk.is_conformant(&conformance_params));
}
{
let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let noise_squashing_params =
NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let config = ConfigBuilder::with_custom_parameters(params)
.enable_noise_squashing(noise_squashing_params)
.build();
let ck = ClientKey::generate(config);
let sk = ServerKey::new(&ck);
let conformance_params = config.into();
assert!(sk.is_conformant(&conformance_params));
}
// Full blockchain configuration
{
let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let cpk_params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let casting_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let comp_params = COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let noise_squashing_params =
NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let config = ConfigBuilder::with_custom_parameters(params)
.use_dedicated_compact_public_key_parameters((cpk_params, casting_params))
.enable_compression(comp_params)
.enable_noise_squashing(noise_squashing_params)
.build();
let ck = ClientKey::generate(config);
let sk = ServerKey::new(&ck);
let conformance_params = config.into();
assert!(sk.is_conformant(&conformance_params));
}
@@ -534,6 +569,7 @@ mod test {
sk_param,
cpk_param: None,
compression_param: None,
noise_squashing_param: None,
};
assert!(!sk.is_conformant(&conformance_params));
@@ -547,7 +583,8 @@ mod test {
let casting_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let config = ConfigBuilder::with_custom_parameters(params)
.use_dedicated_compact_public_key_parameters((cpk_params, casting_params));
.use_dedicated_compact_public_key_parameters((cpk_params, casting_params))
.build();
let ck = ClientKey::generate(config);
let sk = ServerKey::new(&ck);
@@ -560,6 +597,7 @@ mod test {
sk_param,
cpk_param: Some((cpk_params, casting_params)),
compression_param: None,
noise_squashing_param: None,
};
assert!(!sk.is_conformant(&conformance_params));
@@ -577,13 +615,7 @@ mod test {
let ck = ClientKey::generate(config);
let sk = CompressedServerKey::new(&ck);
let sk_param = PBSParameters::PBS(PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128);
let conformance_params = IntegerServerKeyConformanceParams {
sk_param,
cpk_param: None,
compression_param: None,
};
let conformance_params = config.into();
assert!(sk.is_conformant(&conformance_params));
}
@@ -597,14 +629,7 @@ mod test {
let ck = ClientKey::generate(config);
let sk = CompressedServerKey::new(&ck);
let sk_param = PBSParameters::PBS(PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128);
let conformance_params = IntegerServerKeyConformanceParams {
sk_param,
cpk_param: None,
compression_param: Some(COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128),
};
let conformance_params = config.into();
assert!(sk.is_conformant(&conformance_params));
}
{
@@ -615,18 +640,56 @@ mod test {
let casting_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let config = ConfigBuilder::with_custom_parameters(params)
.use_dedicated_compact_public_key_parameters((cpk_params, casting_params));
.use_dedicated_compact_public_key_parameters((cpk_params, casting_params))
.build();
let ck = ClientKey::generate(config);
let sk = CompressedServerKey::new(&ck);
let sk_param = PBSParameters::PBS(PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128);
let conformance_params = config.into();
let conformance_params = IntegerServerKeyConformanceParams {
sk_param,
cpk_param: Some((cpk_params, casting_params)),
compression_param: None,
};
assert!(sk.is_conformant(&conformance_params));
}
{
let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let noise_squashing_params =
NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let config = ConfigBuilder::with_custom_parameters(params)
.enable_noise_squashing(noise_squashing_params)
.build();
let ck = ClientKey::generate(config);
let sk = CompressedServerKey::new(&ck);
let conformance_params = config.into();
assert!(sk.is_conformant(&conformance_params));
}
// Full blockchain configuration
{
let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let cpk_params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let casting_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let comp_params = COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let noise_squashing_params =
NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let config = ConfigBuilder::with_custom_parameters(params)
.use_dedicated_compact_public_key_parameters((cpk_params, casting_params))
.enable_compression(comp_params)
.enable_noise_squashing(noise_squashing_params)
.build();
let ck = ClientKey::generate(config);
let sk = CompressedServerKey::new(&ck);
let conformance_params = config.into();
assert!(sk.is_conformant(&conformance_params));
}
@@ -661,6 +724,7 @@ mod test {
sk_param,
cpk_param: None,
compression_param: None,
noise_squashing_param: None,
};
assert!(!sk.is_conformant(&conformance_params));
@@ -674,7 +738,8 @@ mod test {
let casting_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
let config = ConfigBuilder::with_custom_parameters(params)
.use_dedicated_compact_public_key_parameters((cpk_params, casting_params));
.use_dedicated_compact_public_key_parameters((cpk_params, casting_params))
.build();
let ck = ClientKey::generate(config);
let sk = CompressedServerKey::new(&ck);
@@ -687,6 +752,7 @@ mod test {
sk_param,
cpk_param: Some((cpk_params, casting_params)),
compression_param: None,
noise_squashing_param: None,
};
assert!(!sk.is_conformant(&conformance_params));

View File

@@ -54,7 +54,10 @@ pub use config::{Config, ConfigBuilder};
pub use global_state::CudaGpuChoice;
pub use global_state::{set_server_key, unset_server_key, with_server_key_as_context};
pub use integers::{CompressedFheInt, CompressedFheUint, FheInt, FheUint, IntegerId};
pub use integers::{
CompressedFheInt, CompressedFheUint, FheInt, FheUint, IntegerId, SquashedNoiseFheInt,
SquashedNoiseFheUint,
};
#[cfg(feature = "gpu")]
pub use keys::CudaServerKey;
pub use keys::{
@@ -66,7 +69,9 @@ use strum::FromRepr;
#[cfg(test)]
mod tests;
pub use crate::high_level_api::booleans::{CompressedFheBool, FheBool, FheBoolConformanceParams};
pub use crate::high_level_api::booleans::{
CompressedFheBool, FheBool, FheBoolConformanceParams, SquashedNoiseFheBool,
};
#[cfg(feature = "extended-types")]
expand_pub_use_fhe_type!(

View File

@@ -10,7 +10,7 @@ pub use crate::high_level_api::traits::{
BitSlice, CiphertextList, DivRem, FheDecrypt, FheEncrypt, FheEq, FheKeyswitch, FheMax, FheMin,
FheOrd, FheTrivialEncrypt, FheTryEncrypt, FheTryTrivialEncrypt, IfThenElse, OverflowingAdd,
OverflowingMul, OverflowingSub, RotateLeft, RotateLeftAssign, RotateRight, RotateRightAssign,
ScalarIfThenElse, Tagged,
ScalarIfThenElse, SquashNoise, Tagged,
};
pub use crate::conformance::ParameterSetConformant;

View File

@@ -1,5 +1,6 @@
#[cfg(feature = "gpu")]
mod gpu_selection;
mod noise_squashing;
mod tags_on_entities;
use crate::high_level_api::prelude::*;
@@ -194,7 +195,8 @@ fn test_try_from_single_lwe_encryption_key() {
let shortint_key =
crate::shortint::ClientKey::try_from_lwe_encryption_key(lwe_sk, parameters).unwrap();
let client_key = ClientKey::from_raw_parts(shortint_key.into(), None, None, Tag::default());
let client_key =
ClientKey::from_raw_parts(shortint_key.into(), None, None, None, Tag::default());
let sks = ServerKey::new(&client_key);
let clear_a = 1344u32;

View File

@@ -0,0 +1,75 @@
use crate::high_level_api::prelude::*;
use crate::high_level_api::{
generate_keys, ConfigBuilder, FheBool, FheInt10, FheInt8, FheUint256, FheUint32,
};
use crate::integer::U256;
use crate::set_server_key;
use crate::shortint::parameters::{
NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
};
use rand::prelude::*;
#[test]
fn test_noise_squashing() {
let config =
ConfigBuilder::with_custom_parameters(PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128)
.enable_noise_squashing(NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128)
.build();
let (cks, sks) = generate_keys(config);
set_server_key(sks);
let mut rng = thread_rng();
// Non native type for clear
let clear: U256 = rng.gen();
let enc = FheUint256::encrypt(clear, &cks);
let bitand = &enc & &enc;
let squashed = bitand.squash_noise().unwrap();
let recovered: U256 = squashed.decrypt(&cks);
assert_eq!(clear, recovered);
// Native unsigned
let clear: u32 = rng.gen();
let enc = FheUint32::encrypt(clear, &cks);
let bitand = &enc & &enc;
let squashed = bitand.squash_noise().unwrap();
let recovered: u32 = squashed.decrypt(&cks);
assert_eq!(clear, recovered);
// Non native signed with proper input range
let clear: i16 = rng.gen_range(-1 << 9..1 << 9);
let enc = FheInt10::encrypt(clear, &cks);
let bitand = &enc & &enc;
let squashed = bitand.squash_noise().unwrap();
let recovered: i16 = squashed.decrypt(&cks);
assert_eq!(clear, recovered);
// Native signed
let clear: i8 = rng.gen();
let enc = FheInt8::encrypt(clear, &cks);
let bitand = &enc & &enc;
let squashed = bitand.squash_noise().unwrap();
let recovered: i8 = squashed.decrypt(&cks);
assert_eq!(clear, recovered);
// Booleans
for clear in [false, true] {
let enc = FheBool::encrypt(clear, &cks);
let bitand = &enc & &enc;
let squashed = bitand.squash_noise().unwrap();
let recovered: bool = squashed.decrypt(&cks);
assert_eq!(clear, recovered);
}
}

View File

@@ -204,3 +204,9 @@ pub trait CiphertextList {
}
pub trait FheId: Copy + Default {}
pub trait SquashNoise {
type Output;
fn squash_noise(&self) -> crate::Result<Self::Output>;
}

View File

@@ -379,7 +379,7 @@ where
/// to the final result.
///
/// Input is expected in little endian order.
pub(crate) fn recompose_unsigned<U>(input: impl Iterator<Item = U>, bits_in_block: u32) -> T
pub fn recompose_unsigned<U>(input: impl Iterator<Item = U>, bits_in_block: u32) -> T
where
T: RecomposableFrom<U>,
{
@@ -425,7 +425,7 @@ where
/// to the final result.
///
/// Input is expected in little endian order.
pub(crate) fn recompose_signed<U>(input: impl Iterator<Item = U>, bits_in_block: u32) -> T
pub fn recompose_signed<U>(input: impl Iterator<Item = U>, bits_in_block: u32) -> T
where
T: RecomposableFrom<U> + SignExtendable,
{
@@ -448,7 +448,7 @@ where
/// the last limb are ignored.
///
/// Input is expected in little endian order.
pub(crate) fn recompose_signed_with_size<U>(
pub fn recompose_signed_with_size<U>(
input: impl Iterator<Item = U>,
bits_in_block: u32,
signed_integer_size: u32,

View File

@@ -25,3 +25,29 @@ pub struct SquashedNoiseSignedRadixCiphertext {
pub struct SquashedNoiseBooleanBlock {
pub(crate) ciphertext: SquashedNoiseCiphertext,
}
impl SquashedNoiseRadixCiphertext {
pub fn packed_blocks(&self) -> &[SquashedNoiseCiphertext] {
&self.packed_blocks
}
pub fn original_block_count(&self) -> usize {
self.original_block_count
}
}
impl SquashedNoiseSignedRadixCiphertext {
pub fn packed_blocks(&self) -> &[SquashedNoiseCiphertext] {
&self.packed_blocks
}
pub fn original_block_count(&self) -> usize {
self.original_block_count
}
}
impl SquashedNoiseBooleanBlock {
pub fn packed_blocks(&self) -> &SquashedNoiseCiphertext {
&self.ciphertext
}
}