diff --git a/tfhe/src/high_level_api/backward_compatibility/booleans.rs b/tfhe/src/high_level_api/backward_compatibility/booleans.rs index 60677e739..2b64439a3 100644 --- a/tfhe/src/high_level_api/backward_compatibility/booleans.rs +++ b/tfhe/src/high_level_api/backward_compatibility/booleans.rs @@ -2,9 +2,10 @@ use serde::{Deserialize, Serialize}; use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; use crate::high_level_api::booleans::{ - InnerBoolean, InnerBooleanVersionOwned, InnerCompressedFheBool, + InnerBoolean, InnerBooleanVersionOwned, InnerCompressedFheBool, InnerSquashedNoiseBoolean, InnerSquashedNoiseBooleanVersionOwned, SquashedNoiseFheBool, }; +use crate::high_level_api::SquashedNoiseCiphertextState; use crate::{CompressedFheBool, FheBool, Tag}; use std::convert::Infallible; @@ -72,8 +73,27 @@ pub(crate) enum InnerSquashedNoiseBooleanVersionedOwned { V0(InnerSquashedNoiseBooleanVersionOwned), } +#[derive(Version)] +pub struct SquashedNoiseFheBoolV0 { + pub(in crate::high_level_api) inner: InnerSquashedNoiseBoolean, + pub(in crate::high_level_api) tag: Tag, +} + +impl Upgrade for SquashedNoiseFheBoolV0 { + type Error = Infallible; + + fn upgrade(self) -> Result { + Ok(SquashedNoiseFheBool::new( + self.inner, + SquashedNoiseCiphertextState::Normal, + self.tag, + )) + } +} + // Squashed Noise #[derive(VersionsDispatch)] pub enum SquashedNoiseFheBoolVersions { - V0(SquashedNoiseFheBool), + V0(SquashedNoiseFheBoolV0), + V1(SquashedNoiseFheBool), } diff --git a/tfhe/src/high_level_api/backward_compatibility/compressed_ciphertext_list.rs b/tfhe/src/high_level_api/backward_compatibility/compressed_ciphertext_list.rs index b679e8d74..c96f82eea 100644 --- a/tfhe/src/high_level_api/backward_compatibility/compressed_ciphertext_list.rs +++ b/tfhe/src/high_level_api/backward_compatibility/compressed_ciphertext_list.rs @@ -1,7 +1,14 @@ +use crate::high_level_api::SquashedNoiseCiphertextState; use std::convert::Infallible; use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; -use crate::{CompressedCiphertextList, Tag}; +use crate::{CompressedCiphertextList, CompressedSquashedNoiseCiphertextList, Tag}; + +#[derive(VersionsDispatch)] +#[allow(unused)] +pub(crate) enum SquashedNoiseCiphertextStateVersions { + V0(SquashedNoiseCiphertextState), +} #[derive(Version)] pub struct CompressedCiphertextListV0(crate::integer::ciphertext::CompressedCiphertextList); @@ -40,3 +47,8 @@ pub enum CompressedCiphertextListVersions { V1(CompressedCiphertextListV1), V2(CompressedCiphertextList), } + +#[derive(VersionsDispatch)] +pub enum CompressedSquashedNoiseCiphertextListVersions { + V0(CompressedSquashedNoiseCiphertextList), +} diff --git a/tfhe/src/high_level_api/backward_compatibility/integers.rs b/tfhe/src/high_level_api/backward_compatibility/integers.rs index 1a85d7c59..414829b3c 100644 --- a/tfhe/src/high_level_api/backward_compatibility/integers.rs +++ b/tfhe/src/high_level_api/backward_compatibility/integers.rs @@ -3,8 +3,13 @@ use std::convert::Infallible; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; +use self::signed::SignedRadixCiphertext; +use self::unsigned::RadixCiphertext as UnsignedRadixCiphertext; use crate::high_level_api::global_state::with_cpu_internal_keys; +use crate::high_level_api::integers::signed::InnerSquashedNoiseSignedRadixCiphertext; +use crate::high_level_api::integers::unsigned::InnerSquashedNoiseRadixCiphertext; use crate::high_level_api::integers::*; +use crate::high_level_api::SquashedNoiseCiphertextState; use crate::integer::backward_compatibility::ciphertext::{ CompressedModulusSwitchedRadixCiphertextTFHE06, CompressedModulusSwitchedSignedRadixCiphertextTFHE06, @@ -19,9 +24,6 @@ use crate::shortint::{Ciphertext, ServerKey}; use crate::Tag; use serde::{Deserialize, Serialize}; -use self::signed::SignedRadixCiphertext; -use self::unsigned::RadixCiphertext as UnsignedRadixCiphertext; - // Manual impl #[derive(Serialize, Deserialize)] pub(crate) enum SignedRadixCiphertextVersionedOwned { @@ -228,9 +230,28 @@ pub(crate) enum InnerSquashedNoiseRadixCiphertextVersionedOwned { V0(InnerSquashedNoiseRadixCiphertextVersionOwned), } +#[derive(Version)] +pub struct SquashedNoiseFheUintV0 { + pub(in crate::high_level_api) inner: InnerSquashedNoiseRadixCiphertext, + pub(in crate::high_level_api) tag: Tag, +} + +impl Upgrade for SquashedNoiseFheUintV0 { + type Error = Infallible; + + fn upgrade(self) -> Result { + Ok(SquashedNoiseFheUint::new( + self.inner, + SquashedNoiseCiphertextState::Normal, + self.tag, + )) + } +} + #[derive(VersionsDispatch)] pub enum SquashedNoiseFheUintVersions { - V0(SquashedNoiseFheUint), + V0(SquashedNoiseFheUintV0), + V1(SquashedNoiseFheUint), } // Manual impl @@ -239,7 +260,26 @@ pub(crate) enum InnerSquashedNoiseSignedRadixCiphertextVersionedOwned { V0(InnerSquashedNoiseSignedRadixCiphertextVersionOwned), } +#[derive(Version)] +pub struct SquashedNoiseFheIntV0 { + pub(in crate::high_level_api) inner: InnerSquashedNoiseSignedRadixCiphertext, + pub(in crate::high_level_api) tag: Tag, +} + +impl Upgrade for SquashedNoiseFheIntV0 { + type Error = Infallible; + + fn upgrade(self) -> Result { + Ok(SquashedNoiseFheInt::new( + self.inner, + SquashedNoiseCiphertextState::Normal, + self.tag, + )) + } +} + #[derive(VersionsDispatch)] pub enum SquashedNoiseFheIntVersions { - V0(SquashedNoiseFheInt), + V0(SquashedNoiseFheIntV0), + V1(SquashedNoiseFheInt), } diff --git a/tfhe/src/high_level_api/backward_compatibility/keys.rs b/tfhe/src/high_level_api/backward_compatibility/keys.rs index ea617a735..dd6bb2250 100644 --- a/tfhe/src/high_level_api/backward_compatibility/keys.rs +++ b/tfhe/src/high_level_api/backward_compatibility/keys.rs @@ -2,6 +2,9 @@ use crate::high_level_api::keys::*; use crate::integer::compression_keys::{ CompressedCompressionKey, CompressedDecompressionKey, CompressionKey, DecompressionKey, }; +use crate::integer::noise_squashing::{ + CompressedNoiseSquashingKey, NoiseSquashingKey, NoiseSquashingPrivateKey, +}; use crate::Tag; use std::convert::Infallible; use tfhe_versionable::deprecation::{Deprecable, Deprecated}; @@ -182,17 +185,17 @@ pub(crate) struct IntegerClientKeyV3 { pub(crate) compression_key: Option, } -impl Upgrade for IntegerClientKeyV3 { +impl Upgrade for IntegerClientKeyV3 { type Error = Infallible; - fn upgrade(self) -> Result { + fn upgrade(self) -> Result { let Self { key, dedicated_compact_private_key, compression_key, } = self; - Ok(IntegerClientKey { + Ok(IntegerClientKeyV4 { key, dedicated_compact_private_key, compression_key, @@ -201,6 +204,14 @@ impl Upgrade for IntegerClientKeyV3 { } } +#[derive(Version)] +pub(crate) struct IntegerClientKeyV4 { + pub(crate) key: crate::integer::ClientKey, + pub(crate) dedicated_compact_private_key: Option, + pub(crate) compression_key: Option, + pub(crate) noise_squashing_private_key: Option, +} + #[derive(VersionsDispatch)] #[allow(unused)] pub(crate) enum IntegerClientKeyVersions { @@ -208,7 +219,29 @@ pub(crate) enum IntegerClientKeyVersions { V1(Deprecated), V2(IntegerClientKeyV2), V3(IntegerClientKeyV3), - V4(IntegerClientKey), + V4(IntegerClientKeyV4), + V5(IntegerClientKey), +} + +impl Upgrade for IntegerClientKeyV4 { + type Error = Infallible; + + fn upgrade(self) -> Result { + let Self { + key, + dedicated_compact_private_key, + compression_key, + noise_squashing_private_key, + } = self; + + Ok(IntegerClientKey { + key, + dedicated_compact_private_key, + compression_key, + noise_squashing_private_key, + noise_squashing_compression_private_key: None, + }) + } } impl Deprecable for IntegerServerKey { @@ -225,7 +258,38 @@ pub struct IntegerServerKeyV4 { pub(crate) decompression_key: Option, } -impl Upgrade for IntegerServerKeyV4 { +impl Upgrade for IntegerServerKeyV4 { + type Error = Infallible; + + fn upgrade(self) -> Result { + let Self { + key, + cpk_key_switching_key_material, + compression_key, + decompression_key, + } = self; + + Ok(IntegerServerKeyV5 { + key, + cpk_key_switching_key_material, + compression_key, + decompression_key, + noise_squashing_key: None, + }) + } +} + +#[derive(Version)] +pub struct IntegerServerKeyV5 { + pub(crate) key: crate::integer::ServerKey, + pub(crate) cpk_key_switching_key_material: + Option, + pub(crate) compression_key: Option, + pub(crate) decompression_key: Option, + pub(crate) noise_squashing_key: Option, +} + +impl Upgrade for IntegerServerKeyV5 { type Error = Infallible; fn upgrade(self) -> Result { @@ -234,6 +298,7 @@ impl Upgrade for IntegerServerKeyV4 { cpk_key_switching_key_material, compression_key, decompression_key, + noise_squashing_key, } = self; Ok(IntegerServerKey { @@ -241,7 +306,8 @@ impl Upgrade for IntegerServerKeyV4 { cpk_key_switching_key_material, compression_key, decompression_key, - noise_squashing_key: None, + noise_squashing_key, + noise_squashing_compression_key: None, }) } } @@ -253,7 +319,8 @@ pub enum IntegerServerKeyVersions { V2(Deprecated), V3(Deprecated), V4(IntegerServerKeyV4), - V5(IntegerServerKey), + V5(IntegerServerKeyV5), + V6(IntegerServerKey), } impl Deprecable for IntegerCompressedServerKey { @@ -270,10 +337,10 @@ pub struct IntegerCompressedServerKeyV2 { pub(crate) decompression_key: Option, } -impl Upgrade for IntegerCompressedServerKeyV2 { +impl Upgrade for IntegerCompressedServerKeyV2 { type Error = Infallible; - fn upgrade(self) -> Result { + fn upgrade(self) -> Result { let Self { key, cpk_key_switching_key_material, @@ -281,7 +348,7 @@ impl Upgrade for IntegerCompressedServerKeyV2 { decompression_key, } = self; - Ok(IntegerCompressedServerKey { + Ok(IntegerCompressedServerKeyV3 { key, cpk_key_switching_key_material, compression_key, @@ -291,12 +358,46 @@ impl Upgrade for IntegerCompressedServerKeyV2 { } } +#[derive(Version)] +pub struct IntegerCompressedServerKeyV3 { + pub(crate) key: crate::integer::CompressedServerKey, + pub(crate) cpk_key_switching_key_material: + Option, + pub(crate) compression_key: Option, + pub(crate) decompression_key: Option, + pub(crate) noise_squashing_key: Option, +} + #[derive(VersionsDispatch)] pub enum IntegerCompressedServerKeyVersions { V0(Deprecated), V1(Deprecated), V2(IntegerCompressedServerKeyV2), - V3(IntegerCompressedServerKey), + V3(IntegerCompressedServerKeyV3), + V4(IntegerCompressedServerKey), +} + +impl Upgrade for IntegerCompressedServerKeyV3 { + type Error = Infallible; + + fn upgrade(self) -> Result { + let Self { + key, + cpk_key_switching_key_material, + compression_key, + decompression_key, + noise_squashing_key, + } = self; + + Ok(IntegerCompressedServerKey { + key, + cpk_key_switching_key_material, + compression_key, + decompression_key, + noise_squashing_key, + noise_squashing_compression_key: None, + }) + } } #[derive(VersionsDispatch)] diff --git a/tfhe/src/high_level_api/booleans/mod.rs b/tfhe/src/high_level_api/booleans/mod.rs index 8ee8d65e7..87d8c2058 100644 --- a/tfhe/src/high_level_api/booleans/mod.rs +++ b/tfhe/src/high_level_api/booleans/mod.rs @@ -4,7 +4,9 @@ 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; +pub(in crate::high_level_api) use squashed_noise::{ + InnerSquashedNoiseBoolean, InnerSquashedNoiseBooleanVersionOwned, +}; mod base; mod compressed; diff --git a/tfhe/src/high_level_api/booleans/squashed_noise.rs b/tfhe/src/high_level_api/booleans/squashed_noise.rs index 5d87f8daf..0721c479a 100644 --- a/tfhe/src/high_level_api/booleans/squashed_noise.rs +++ b/tfhe/src/high_level_api/booleans/squashed_noise.rs @@ -6,7 +6,8 @@ use crate::high_level_api::details::MaybeCloned; use crate::high_level_api::errors::UninitializedNoiseSquashing; use crate::high_level_api::global_state::{self, with_internal_keys}; use crate::high_level_api::keys::InternalServerKey; -use crate::high_level_api::traits::{FheDecrypt, SquashNoise}; +use crate::high_level_api::traits::{FheDecrypt, SquashNoise, Tagged}; +use crate::high_level_api::SquashedNoiseCiphertextState; use crate::integer::ciphertext::SquashedNoiseBooleanBlock; #[cfg(feature = "gpu")] use crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock; @@ -125,10 +126,21 @@ impl InnerSquashedNoiseBoolean { #[derive(Clone, serde::Deserialize, serde::Serialize, Versionize)] #[versionize(SquashedNoiseFheBoolVersions)] pub struct SquashedNoiseFheBool { - inner: InnerSquashedNoiseBoolean, + pub(in crate::high_level_api) inner: InnerSquashedNoiseBoolean, + pub(in crate::high_level_api) state: SquashedNoiseCiphertextState, tag: Tag, } +impl SquashedNoiseFheBool { + pub(in crate::high_level_api) fn new( + inner: InnerSquashedNoiseBoolean, + state: SquashedNoiseCiphertextState, + tag: Tag, + ) -> Self { + Self { inner, state, tag } + } +} + impl Named for SquashedNoiseFheBool { const NAME: &'static str = "high_level_api::SquashedNoiseFheBool"; } @@ -143,20 +155,23 @@ impl SquashedNoiseFheBool { impl FheDecrypt 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?", - ) + let noise_squashing_private_key = key.private_noise_squashing_decryption_key(self.state); + noise_squashing_private_key + .decrypt_bool(&self.inner.on_cpu()) .unwrap() } } +impl Tagged for SquashedNoiseFheBool { + fn tag(&self) -> &Tag { + &self.tag + } + + fn tag_mut(&mut self) -> &mut Tag { + &mut self.tag + } +} + impl SquashNoise for FheBool { type Output = SquashedNoiseFheBool; @@ -176,6 +191,7 @@ impl SquashNoise for FheBool { &self.ciphertext.on_cpu(), )?, ), + state: SquashedNoiseCiphertextState::Normal, tag: server_key.tag.clone(), }) } @@ -211,6 +227,7 @@ impl SquashNoise for FheBool { Ok(SquashedNoiseFheBool { inner: InnerSquashedNoiseBoolean::Cpu(cpu_squashed_block), + state: SquashedNoiseCiphertextState::Normal, tag: cuda_key.tag.clone(), }) } diff --git a/tfhe/src/high_level_api/compressed_noise_squashed_ciphertext_list.rs b/tfhe/src/high_level_api/compressed_noise_squashed_ciphertext_list.rs new file mode 100644 index 000000000..64030aee6 --- /dev/null +++ b/tfhe/src/high_level_api/compressed_noise_squashed_ciphertext_list.rs @@ -0,0 +1,432 @@ +use crate::backward_compatibility::compressed_ciphertext_list::CompressedSquashedNoiseCiphertextListVersions; +use crate::high_level_api::booleans::InnerSquashedNoiseBoolean; +use crate::high_level_api::global_state::try_with_internal_keys; +use crate::high_level_api::integers::signed::InnerSquashedNoiseSignedRadixCiphertext; +use crate::high_level_api::integers::unsigned::InnerSquashedNoiseRadixCiphertext; +use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::traits::Tagged; +use crate::high_level_api::SquashedNoiseCiphertextState; +use crate::integer::ciphertext::{ + CompressedSquashedNoiseCiphertextList as IntegerCompressedSquashedNoiseCiphertextList, + DataKind, SquashedNoiseBooleanBlock, SquashedNoiseExpandable, SquashedNoiseRadixCiphertext, + SquashedNoiseSignedRadixCiphertext, +}; +use crate::named::Named; +use crate::shortint::ciphertext::SquashedNoiseCiphertext; +use crate::{SquashedNoiseFheBool, SquashedNoiseFheInt, SquashedNoiseFheUint, Tag, Versionize}; +use serde::{Deserialize, Serialize}; +use tfhe_versionable::{Unversionize, UnversionizeError, VersionizeOwned}; + +pub(in crate::high_level_api) enum InnerCompressedSquashedNoiseCiphertextList { + Cpu(IntegerCompressedSquashedNoiseCiphertextList), +} + +impl Versionize for InnerCompressedSquashedNoiseCiphertextList { + type Versioned<'vers> + = ::Versioned<'vers> + where + Self: 'vers; + + fn versionize(&self) -> Self::Versioned<'_> { + self.on_cpu().versionize() + } +} + +impl VersionizeOwned for InnerCompressedSquashedNoiseCiphertextList { + type VersionedOwned = + ::VersionedOwned; + + fn versionize_owned(self) -> Self::VersionedOwned { + self.into_cpu().versionize_owned() + } +} + +impl Unversionize for InnerCompressedSquashedNoiseCiphertextList { + fn unversionize(versioned: Self::VersionedOwned) -> Result { + IntegerCompressedSquashedNoiseCiphertextList::unversionize(versioned).map(Self::Cpu) + } +} + +impl Serialize for InnerCompressedSquashedNoiseCiphertextList { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.on_cpu().serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for InnerCompressedSquashedNoiseCiphertextList { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let mut new = IntegerCompressedSquashedNoiseCiphertextList::deserialize(deserializer) + .map(Self::Cpu)?; + + if let Some(device) = crate::high_level_api::global_state::device_of_internal_keys() { + new.move_to_device(device) + .map_err(serde::de::Error::custom)?; + } + + Ok(new) + } +} + +impl InnerCompressedSquashedNoiseCiphertextList { + fn on_cpu(&self) -> &IntegerCompressedSquashedNoiseCiphertextList { + match self { + Self::Cpu(inner) => inner, + } + } + + fn into_cpu(self) -> IntegerCompressedSquashedNoiseCiphertextList { + match self { + Self::Cpu(inner) => inner, + } + } + + fn current_device(&self) -> crate::Device { + match self { + Self::Cpu(_) => crate::Device::Cpu, + } + } + + #[allow(clippy::unnecessary_wraps, reason = "It depends on activated features")] + fn move_to_device(&mut self, target_device: crate::Device) -> crate::Result<()> { + let current_device = self.current_device(); + if current_device == target_device { + return Ok(()); + } + + let cpu_ct = self.on_cpu(); + + match target_device { + crate::Device::Cpu => { + *self = Self::Cpu(cpu_ct.to_owned()); + Ok(()) + } + #[cfg(feature = "gpu")] + crate::Device::CudaGpu => Err(crate::error!( + "Cuda does not support CompressedSquashedNoiseCiphertextList" + )), + #[cfg(feature = "hpu")] + crate::Device::Hpu => Err(crate::error!( + "Hpu does not support CompressedSquashedNoiseCiphertextList" + )), + } + } +} + +#[derive(Serialize, Deserialize, Versionize)] +#[versionize(CompressedSquashedNoiseCiphertextListVersions)] +pub struct CompressedSquashedNoiseCiphertextList { + pub(in crate::high_level_api) inner: InnerCompressedSquashedNoiseCiphertextList, + pub(crate) tag: Tag, +} + +impl Named for CompressedSquashedNoiseCiphertextList { + const NAME: &'static str = "high_level_api::CompressedSquashedNoiseCiphertextList"; +} + +impl CompressedSquashedNoiseCiphertextList { + pub fn builder() -> CompressedSquashedNoiseCiphertextListBuilder { + CompressedSquashedNoiseCiphertextListBuilder::new() + } + + pub fn len(&self) -> usize { + match &self.inner { + InnerCompressedSquashedNoiseCiphertextList::Cpu(inner) => inner.len(), + } + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn get(&self, index: usize) -> crate::Result> + where + T: HlSquashedNoiseExpandable + Tagged, + { + let mut r = match &self.inner { + InnerCompressedSquashedNoiseCiphertextList::Cpu(inner) => inner.get::(index), + }; + + if let Ok(Some(ct)) = &mut r { + *ct.tag_mut() = self.tag.clone(); + } + r + } +} + +pub trait HlSquashedNoiseExpandable: SquashedNoiseExpandable {} + +fn create_error_message(tried: DataKind, actual: DataKind) -> crate::Error { + fn name(kind: DataKind) -> &'static str { + match kind { + DataKind::Unsigned(_) => "SquashedNoiseFheUint", + DataKind::Signed(_) => "SquashedNoiseFheInt", + DataKind::Boolean => "SquashedNoiseFheBool", + DataKind::String { .. } => "SquashedNoiseFheString", + } + } + crate::error!( + "Tried to expand a {}, but a {} is stored in this slot", + name(tried), + name(actual) + ) +} + +impl SquashedNoiseExpandable for SquashedNoiseFheBool { + fn from_expanded_blocks( + blocks: Vec, + kind: DataKind, + ) -> crate::Result { + if kind == DataKind::Boolean { + SquashedNoiseBooleanBlock::from_expanded_blocks(blocks, kind).map(|v| { + Self::new( + InnerSquashedNoiseBoolean::Cpu(v), + SquashedNoiseCiphertextState::PostDecompression, + Tag::default(), + ) + }) + } else { + Err(create_error_message(DataKind::Boolean, kind)) + } + } +} + +impl SquashedNoiseExpandable for SquashedNoiseFheUint { + fn from_expanded_blocks( + blocks: Vec, + kind: DataKind, + ) -> crate::Result { + if matches!(kind, DataKind::Unsigned(_)) { + SquashedNoiseRadixCiphertext::from_expanded_blocks(blocks, kind).map(|v| { + Self::new( + InnerSquashedNoiseRadixCiphertext::Cpu(v), + SquashedNoiseCiphertextState::PostDecompression, + Tag::default(), + ) + }) + } else { + Err(create_error_message(DataKind::Unsigned(0), kind)) + } + } +} + +impl SquashedNoiseExpandable for SquashedNoiseFheInt { + fn from_expanded_blocks( + blocks: Vec, + kind: DataKind, + ) -> crate::Result { + if matches!(kind, DataKind::Signed(_)) { + SquashedNoiseSignedRadixCiphertext::from_expanded_blocks(blocks, kind).map(|v| { + Self::new( + InnerSquashedNoiseSignedRadixCiphertext::Cpu(v), + SquashedNoiseCiphertextState::PostDecompression, + Tag::default(), + ) + }) + } else { + Err(create_error_message(DataKind::Signed(0), kind)) + } + } +} + +impl HlSquashedNoiseExpandable for SquashedNoiseFheBool {} +impl HlSquashedNoiseExpandable for SquashedNoiseFheUint {} +impl HlSquashedNoiseExpandable for SquashedNoiseFheInt {} + +mod private { + use crate::shortint::ciphertext::SquashedNoiseCiphertext; + + pub enum SquashedNoiseToBeCompressed { + Cpu(Vec), + } +} + +pub trait HlSquashedNoiseCompressible { + fn compress_into(self, messages: &mut Vec<(private::SquashedNoiseToBeCompressed, DataKind)>); +} + +impl HlSquashedNoiseCompressible for SquashedNoiseFheBool { + fn compress_into(self, messages: &mut Vec<(private::SquashedNoiseToBeCompressed, DataKind)>) { + let kind = DataKind::Boolean; + match self.inner { + InnerSquashedNoiseBoolean::Cpu(cpu_ct) => messages.push(( + private::SquashedNoiseToBeCompressed::Cpu(vec![cpu_ct.ciphertext]), + kind, + )), + } + } +} + +impl HlSquashedNoiseCompressible for SquashedNoiseFheUint { + fn compress_into(self, messages: &mut Vec<(private::SquashedNoiseToBeCompressed, DataKind)>) { + match self.inner { + InnerSquashedNoiseRadixCiphertext::Cpu(cpu_ct) => { + let kind = DataKind::Unsigned(cpu_ct.original_block_count); + messages.push(( + private::SquashedNoiseToBeCompressed::Cpu(cpu_ct.packed_blocks), + kind, + )) + } + } + } +} + +impl HlSquashedNoiseCompressible for SquashedNoiseFheInt { + fn compress_into(self, messages: &mut Vec<(private::SquashedNoiseToBeCompressed, DataKind)>) { + match self.inner { + InnerSquashedNoiseSignedRadixCiphertext::Cpu(cpu_ct) => { + let kind = DataKind::Signed(cpu_ct.original_block_count); + messages.push(( + private::SquashedNoiseToBeCompressed::Cpu(cpu_ct.packed_blocks), + kind, + )) + } + } + } +} + +pub struct CompressedSquashedNoiseCiphertextListBuilder { + inner: Vec<(private::SquashedNoiseToBeCompressed, DataKind)>, +} + +impl Default for CompressedSquashedNoiseCiphertextListBuilder { + fn default() -> Self { + Self::new() + } +} + +impl CompressedSquashedNoiseCiphertextListBuilder { + pub fn new() -> Self { + Self { inner: vec![] } + } + + pub fn push(&mut self, value: T) -> &mut Self + where + T: HlSquashedNoiseCompressible, + { + value.compress_into(&mut self.inner); + self + } + + pub fn build(&self) -> crate::Result { + try_with_internal_keys(|keys| match keys { + Some(InternalServerKey::Cpu(cpu_key)) => { + let mut flat_cpu_blocks = vec![]; + for (element, _) in &self.inner { + match element { + private::SquashedNoiseToBeCompressed::Cpu(cpu_blocks) => { + flat_cpu_blocks.extend_from_slice(cpu_blocks.as_slice()); + } + } + } + cpu_key + .key + .noise_squashing_compression_key + .as_ref() + .ok_or_else(|| { + crate::Error::new( + "Compression key for squashed noise data not set in server key" + .to_owned(), + ) + }) + .map(|compression_key| { + let compressed_list = compression_key + .key + .compress_noise_squashed_ciphertexts_into_list(&flat_cpu_blocks); + let info = self.inner.iter().map(|(_, kind)| *kind).collect(); + + CompressedSquashedNoiseCiphertextList { + inner: InnerCompressedSquashedNoiseCiphertextList::Cpu( + IntegerCompressedSquashedNoiseCiphertextList { + list: compressed_list, + info, + }, + ), + tag: cpu_key.tag.clone(), + } + }) + } + #[cfg(feature = "gpu")] + Some(InternalServerKey::Cuda(_)) => Err(crate::error!( + "Cuda GPU does not support compression of squashed noise ciphertexts" + )), + #[cfg(feature = "hpu")] + Some(InternalServerKey::Hpu(_)) => Err(crate::error!( + "HPU does not support compression of squashed noise ciphertexts" + )), + None => Err(crate::high_level_api::errors::UninitializedServerKey.into()), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::prelude::*; + use crate::safe_serialization::{safe_deserialize, safe_serialize}; + use crate::shortint::parameters::current_params::*; + use crate::{generate_keys, set_server_key, ConfigBuilder, FheBool, FheInt32, FheUint32}; + use rand::Rng; + + #[test] + fn test_compressed_squashed_noise_ciphertext_list() { + let params = V1_3_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + let noise_squashing_params = + V1_3_NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + let noise_squashing_compression_params = + V1_3_NOISE_SQUASHING_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + + let config = ConfigBuilder::with_custom_parameters(params) + .enable_noise_squashing(noise_squashing_params) + .enable_noise_squashing_compression(noise_squashing_compression_params) + .build(); + + let (cks, sks) = generate_keys(config); + + let mut rng = rand::thread_rng(); + + let clear_a = rng.gen::(); + let clear_b = rng.gen::(); + let clear_c = rng.gen_bool(0.5); + + let a = FheInt32::encrypt(clear_a, &cks); + let b = FheUint32::encrypt(clear_b, &cks); + let c = FheBool::encrypt(clear_c, &cks); + + set_server_key(sks); + + let ns_a = a.squash_noise().unwrap(); + let ns_b = b.squash_noise().unwrap(); + let ns_c = c.squash_noise().unwrap(); + + let list = CompressedSquashedNoiseCiphertextList::builder() + .push(ns_a) + .push(ns_b) + .push(ns_c) + .build() + .unwrap(); + + let mut serialized_list = vec![]; + safe_serialize(&list, &mut serialized_list, 1 << 24).unwrap(); + let list: CompressedSquashedNoiseCiphertextList = + safe_deserialize(serialized_list.as_slice(), 1 << 24).unwrap(); + + let ns_a: SquashedNoiseFheInt = list.get(0).unwrap().unwrap(); + let ns_b: SquashedNoiseFheUint = list.get(1).unwrap().unwrap(); + let ns_c: SquashedNoiseFheBool = list.get(2).unwrap().unwrap(); + + let decrypted: i32 = ns_a.decrypt(&cks); + assert_eq!(decrypted, clear_a); + + let decrypted: u32 = ns_b.decrypt(&cks); + assert_eq!(decrypted, clear_b); + + let decrypted: bool = ns_c.decrypt(&cks); + assert_eq!(decrypted, clear_c); + } +} diff --git a/tfhe/src/high_level_api/config.rs b/tfhe/src/high_level_api/config.rs index a8ede6b54..46361d7b3 100644 --- a/tfhe/src/high_level_api/config.rs +++ b/tfhe/src/high_level_api/config.rs @@ -3,7 +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; +use crate::shortint::parameters::{NoiseSquashingCompressionParameters, NoiseSquashingParameters}; /// The config type #[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize, Versionize)] @@ -67,6 +67,20 @@ impl ConfigBuilder { self } + pub fn enable_noise_squashing_compression( + mut self, + compression_parameters: NoiseSquashingCompressionParameters, + ) -> Self { + assert_ne!( + self.config.inner.noise_squashing_parameters, None, + "Noise squashing must be enabled first" + ); + self.config + .inner + .enable_noise_squashing_compression(compression_parameters); + self + } + pub fn with_custom_parameters

(block_parameters: P) -> Self where P: Into, diff --git a/tfhe/src/high_level_api/integers/signed/mod.rs b/tfhe/src/high_level_api/integers/signed/mod.rs index 828fdb9c0..f61a332d0 100644 --- a/tfhe/src/high_level_api/integers/signed/mod.rs +++ b/tfhe/src/high_level_api/integers/signed/mod.rs @@ -17,8 +17,10 @@ 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; +pub(in crate::high_level_api) use squashed_noise::{ + InnerSquashedNoiseSignedRadixCiphertext, InnerSquashedNoiseSignedRadixCiphertextVersionOwned, +}; expand_pub_use_fhe_type!( pub use static_{ diff --git a/tfhe/src/high_level_api/integers/signed/squashed_noise.rs b/tfhe/src/high_level_api/integers/signed/squashed_noise.rs index 926381bad..77438fe8b 100644 --- a/tfhe/src/high_level_api/integers/signed/squashed_noise.rs +++ b/tfhe/src/high_level_api/integers/signed/squashed_noise.rs @@ -7,8 +7,10 @@ use crate::high_level_api::errors::UninitializedNoiseSquashing; use crate::high_level_api::global_state::{self, with_internal_keys}; use crate::high_level_api::keys::InternalServerKey; use crate::high_level_api::traits::{FheDecrypt, SquashNoise}; +use crate::high_level_api::SquashedNoiseCiphertextState; use crate::integer::block_decomposition::{RecomposableFrom, SignExtendable}; use crate::named::Named; +use crate::prelude::Tagged; use crate::{ClientKey, Device, Tag}; use serde::{Deserializer, Serializer}; use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned}; @@ -129,7 +131,8 @@ impl InnerSquashedNoiseSignedRadixCiphertext { #[derive(Clone, serde::Deserialize, serde::Serialize, Versionize)] #[versionize(SquashedNoiseFheIntVersions)] pub struct SquashedNoiseFheInt { - inner: InnerSquashedNoiseSignedRadixCiphertext, + pub(in crate::high_level_api) inner: InnerSquashedNoiseSignedRadixCiphertext, + pub(in crate::high_level_api) state: SquashedNoiseCiphertextState, tag: Tag, } @@ -138,6 +141,14 @@ impl Named for SquashedNoiseFheInt { } impl SquashedNoiseFheInt { + pub(in crate::high_level_api) fn new( + inner: InnerSquashedNoiseSignedRadixCiphertext, + state: SquashedNoiseCiphertextState, + tag: Tag, + ) -> Self { + Self { inner, state, tag } + } + pub fn underlying_squashed_noise_ciphertext( &self, ) -> MaybeCloned<'_, crate::integer::ciphertext::SquashedNoiseSignedRadixCiphertext> { @@ -158,20 +169,24 @@ where Clear: RecomposableFrom + 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?", - ) + let noise_squashing_private_key = key.private_noise_squashing_decryption_key(self.state); + + noise_squashing_private_key + .decrypt_signed_radix(&self.inner.on_cpu()) .unwrap() } } +impl Tagged for SquashedNoiseFheInt { + fn tag(&self) -> &Tag { + &self.tag + } + + fn tag_mut(&mut self) -> &mut Tag { + &mut self.tag + } +} + impl SquashNoise for FheInt { type Output = SquashedNoiseFheInt; @@ -191,6 +206,7 @@ impl SquashNoise for FheInt { &self.ciphertext.on_cpu(), )?, ), + state: SquashedNoiseCiphertextState::Normal, tag: server_key.tag.clone(), }) } @@ -213,6 +229,7 @@ impl SquashNoise for FheInt { cuda_squashed_ct.to_squashed_noise_signed_radix_ciphertext(streams); Ok(SquashedNoiseFheInt { inner: InnerSquashedNoiseSignedRadixCiphertext::Cpu(cpu_squashed_ct), + state: SquashedNoiseCiphertextState::Normal, tag: cuda_key.tag.clone(), }) } diff --git a/tfhe/src/high_level_api/integers/unsigned/mod.rs b/tfhe/src/high_level_api/integers/unsigned/mod.rs index f8f54e0d3..0bd3f52f0 100644 --- a/tfhe/src/high_level_api/integers/unsigned/mod.rs +++ b/tfhe/src/high_level_api/integers/unsigned/mod.rs @@ -22,7 +22,9 @@ 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; +pub(in crate::high_level_api) use squashed_noise::{ + InnerSquashedNoiseRadixCiphertext, InnerSquashedNoiseRadixCiphertextVersionOwned, +}; mod base; mod compressed; diff --git a/tfhe/src/high_level_api/integers/unsigned/squashed_noise.rs b/tfhe/src/high_level_api/integers/unsigned/squashed_noise.rs index 1d79874a0..59e50778b 100644 --- a/tfhe/src/high_level_api/integers/unsigned/squashed_noise.rs +++ b/tfhe/src/high_level_api/integers/unsigned/squashed_noise.rs @@ -7,7 +7,8 @@ use crate::high_level_api::details::MaybeCloned; use crate::high_level_api::errors::UninitializedNoiseSquashing; use crate::high_level_api::global_state::{self, with_internal_keys}; use crate::high_level_api::keys::InternalServerKey; -use crate::high_level_api::traits::{FheDecrypt, SquashNoise}; +use crate::high_level_api::traits::{FheDecrypt, SquashNoise, Tagged}; +use crate::high_level_api::SquashedNoiseCiphertextState; use crate::integer::block_decomposition::RecomposableFrom; use crate::named::Named; use crate::{ClientKey, Device, Tag}; @@ -124,7 +125,8 @@ impl InnerSquashedNoiseRadixCiphertext { #[derive(Clone, serde::Deserialize, serde::Serialize, Versionize)] #[versionize(SquashedNoiseFheUintVersions)] pub struct SquashedNoiseFheUint { - inner: InnerSquashedNoiseRadixCiphertext, + pub(in crate::high_level_api) inner: InnerSquashedNoiseRadixCiphertext, + pub(in crate::high_level_api) state: SquashedNoiseCiphertextState, tag: Tag, } @@ -133,6 +135,14 @@ impl Named for SquashedNoiseFheUint { } impl SquashedNoiseFheUint { + pub(in crate::high_level_api) fn new( + inner: InnerSquashedNoiseRadixCiphertext, + state: SquashedNoiseCiphertextState, + tag: Tag, + ) -> Self { + Self { inner, state, tag } + } + pub fn underlying_squashed_noise_ciphertext( &self, ) -> MaybeCloned<'_, crate::integer::ciphertext::SquashedNoiseRadixCiphertext> { @@ -153,20 +163,23 @@ where Clear: RecomposableFrom + 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?", - ) + let noise_squashing_private_key = key.private_noise_squashing_decryption_key(self.state); + noise_squashing_private_key + .decrypt_radix(&self.inner.on_cpu()) .unwrap() } } +impl Tagged for SquashedNoiseFheUint { + fn tag(&self) -> &Tag { + &self.tag + } + + fn tag_mut(&mut self) -> &mut Tag { + &mut self.tag + } +} + impl SquashNoise for FheUint { type Output = SquashedNoiseFheUint; @@ -186,6 +199,7 @@ impl SquashNoise for FheUint { &self.ciphertext.on_cpu(), )?, ), + state: SquashedNoiseCiphertextState::Normal, tag: server_key.tag.clone(), }) } @@ -207,6 +221,7 @@ impl SquashNoise for FheUint { let squashed_ct = cuda_squashed_ct.to_squashed_noise_radix_ciphertext(streams); Ok(SquashedNoiseFheUint { inner: InnerSquashedNoiseRadixCiphertext::Cpu(squashed_ct), + state: SquashedNoiseCiphertextState::Normal, tag: cuda_key.tag.clone(), }) } diff --git a/tfhe/src/high_level_api/keys/client.rs b/tfhe/src/high_level_api/keys/client.rs index 0d0f8dd13..7eb51c75c 100644 --- a/tfhe/src/high_level_api/keys/client.rs +++ b/tfhe/src/high_level_api/keys/client.rs @@ -6,8 +6,10 @@ use super::{CompressedServerKey, ServerKey}; 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::high_level_api::SquashedNoiseCiphertextState; +use crate::integer::ciphertext::NoiseSquashingCompressionPrivateKey; use crate::integer::compression_keys::CompressionPrivateKeys; -use crate::integer::noise_squashing::NoiseSquashingPrivateKey; +use crate::integer::noise_squashing::{NoiseSquashingPrivateKey, NoiseSquashingPrivateKeyView}; use crate::named::Named; use crate::prelude::Tagged; use crate::shortint::MessageModulus; @@ -82,10 +84,11 @@ impl ClientKey { Option, Option, Option, + Option, Tag, ) { - let (cks, cpk, cppk, nsk) = self.key.into_raw_parts(); - (cks, cpk, cppk, nsk, self.tag) + let (cks, cpk, cppk, nsk, nscpk) = self.key.into_raw_parts(); + (cks, cpk, cppk, nsk, nscpk, self.tag) } pub fn from_raw_parts( @@ -96,6 +99,7 @@ impl ClientKey { )>, compression_key: Option, noise_squashing_key: Option, + noise_squashing_compression_key: Option, tag: Tag, ) -> Self { Self { @@ -104,6 +108,7 @@ impl ClientKey { dedicated_compact_private_key, compression_key, noise_squashing_key, + noise_squashing_compression_key, ), tag, } @@ -125,6 +130,38 @@ impl ClientKey { pub(crate) fn message_modulus(&self) -> MessageModulus { self.key.block_parameters().message_modulus() } + + /// Returns a view of the private key to be used to decrypt a squashed noise + /// ciphertext depending on its state + /// + /// # Panics + /// + /// Panics if the key supposed to be used for the given state cannot be found + pub(crate) fn private_noise_squashing_decryption_key( + &self, + state: SquashedNoiseCiphertextState, + ) -> NoiseSquashingPrivateKeyView<'_> { + match state { + SquashedNoiseCiphertextState::Normal => self + .key + .noise_squashing_private_key + .as_ref() + .map(|key| key.as_view()) + .expect( + "No noise squashing private key in your ClientKey, cannot decrypt. \ + Did you call `enable_noise_squashing` when creating your Config?", + ), + SquashedNoiseCiphertextState::PostDecompression => self + .key + .noise_squashing_compression_private_key + .as_ref() + .map(|key| key.private_key_view()) + .expect( + "No noise squashing private key in your ClientKey, cannot decrypt. \ + Did you call `enable_noise_squashing_compression` when creating your Config?", + ), + } + } } impl Tagged for ClientKey { diff --git a/tfhe/src/high_level_api/keys/inner.rs b/tfhe/src/high_level_api/keys/inner.rs index 9797c1b41..93656cd69 100644 --- a/tfhe/src/high_level_api/keys/inner.rs +++ b/tfhe/src/high_level_api/keys/inner.rs @@ -2,6 +2,10 @@ use crate::conformance::ParameterSetConformant; use crate::core_crypto::commons::generators::DeterministicSeeder; use crate::core_crypto::prelude::{DefaultRandomGenerator, LweKeyswitchKeyConformanceParams}; use crate::high_level_api::backward_compatibility::keys::*; +use crate::integer::ciphertext::{ + CompressedNoiseSquashingCompressionKey, NoiseSquashingCompressionKey, + NoiseSquashingCompressionPrivateKey, +}; use crate::integer::compression_keys::{ CompressedCompressionKey, CompressedDecompressionKey, CompressionKey, CompressionPrivateKeys, DecompressionKey, @@ -15,7 +19,8 @@ use crate::shortint::atomic_pattern::AtomicPatternParameters; use crate::shortint::key_switching_key::KeySwitchingKeyConformanceParams; use crate::shortint::parameters::list_compression::CompressionParameters; use crate::shortint::parameters::{ - CompactPublicKeyEncryptionParameters, NoiseSquashingParameters, ShortintKeySwitchingParameters, + CompactPublicKeyEncryptionParameters, NoiseSquashingCompressionParameters, + NoiseSquashingParameters, ShortintKeySwitchingParameters, }; use crate::shortint::{EncryptionKeyChoice, MessageModulus}; use crate::{Config, Error}; @@ -33,6 +38,7 @@ pub(crate) struct IntegerConfig { )>, pub(crate) compression_parameters: Option, pub(crate) noise_squashing_parameters: Option, + pub(crate) noise_squashing_compression_parameters: Option, } impl IntegerConfig { @@ -44,6 +50,7 @@ impl IntegerConfig { dedicated_compact_public_key_parameters: None, compression_parameters: None, noise_squashing_parameters: None, + noise_squashing_compression_parameters: None, } } @@ -58,6 +65,17 @@ impl IntegerConfig { self.noise_squashing_parameters = Some(compression_parameters); } + pub(crate) fn enable_noise_squashing_compression( + &mut self, + compression_parameters: NoiseSquashingCompressionParameters, + ) { + assert_ne!( + self.noise_squashing_parameters, None, + "Noise squashing must be enabled first" + ); + self.noise_squashing_compression_parameters = Some(compression_parameters); + } + pub(crate) fn public_key_encryption_parameters( &self, ) -> Result @@ -84,6 +102,7 @@ impl Default for IntegerConfig { dedicated_compact_public_key_parameters: None, compression_parameters: None, noise_squashing_parameters: None, + noise_squashing_compression_parameters: None, } } } @@ -100,6 +119,7 @@ pub(crate) struct IntegerClientKey { pub(crate) dedicated_compact_private_key: Option, pub(crate) compression_key: Option, pub(crate) noise_squashing_private_key: Option, + pub(crate) noise_squashing_compression_private_key: Option, } impl IntegerClientKey { @@ -126,11 +146,16 @@ impl IntegerClientKey { .noise_squashing_parameters .map(NoiseSquashingPrivateKey::new); + let noise_squashing_compression_private_key = config + .noise_squashing_compression_parameters + .map(NoiseSquashingCompressionPrivateKey::new); + Self { key, dedicated_compact_private_key, compression_key, noise_squashing_private_key, + noise_squashing_compression_private_key, } } @@ -142,18 +167,21 @@ impl IntegerClientKey { Option, Option, Option, + Option, ) { let Self { key, dedicated_compact_private_key, compression_key, noise_squashing_private_key, + noise_squashing_compression_private_key, } = self; ( key, dedicated_compact_private_key, compression_key, noise_squashing_private_key, + noise_squashing_compression_private_key, ) } @@ -167,6 +195,7 @@ impl IntegerClientKey { dedicated_compact_private_key: Option, compression_key: Option, noise_squashing_private_key: Option, + noise_squashing_compression_private_key: Option, ) -> Self { let shortint_cks: &crate::shortint::ClientKey = key.as_ref(); @@ -194,6 +223,7 @@ impl IntegerClientKey { dedicated_compact_private_key, compression_key, noise_squashing_private_key, + noise_squashing_compression_private_key, } } @@ -223,11 +253,16 @@ impl From for IntegerClientKey { .noise_squashing_parameters .map(NoiseSquashingPrivateKey::new); + let noise_squashing_compression_private_key = config + .noise_squashing_compression_parameters + .map(NoiseSquashingCompressionPrivateKey::new); + Self { key, dedicated_compact_private_key, compression_key, noise_squashing_private_key, + noise_squashing_compression_private_key, } } } @@ -245,6 +280,7 @@ pub struct IntegerServerKey { pub(crate) compression_key: Option, pub(crate) decompression_key: Option, pub(crate) noise_squashing_key: Option, + pub(crate) noise_squashing_compression_key: Option, } impl IntegerServerKey { @@ -277,10 +313,22 @@ impl IntegerServerKey { build_helper.into() }); - let noise_squashing_key = client_key - .noise_squashing_private_key - .as_ref() - .map(|key| NoiseSquashingKey::new(cks, key)); + let (noise_squashing_key, noise_squashing_compression_key) = + client_key.noise_squashing_private_key.as_ref().map_or_else( + || (None, None), + |noise_squashing_private_key| { + let noise_squashing_key = + NoiseSquashingKey::new(cks, noise_squashing_private_key); + let noise_squashing_compression_key = client_key + .noise_squashing_compression_private_key + .as_ref() + .map(|comp_private_key| { + noise_squashing_private_key + .new_noise_squashing_compression_key(comp_private_key) + }); + (Some(noise_squashing_key), noise_squashing_compression_key) + }, + ); Self { key: base_integer_key, @@ -288,6 +336,7 @@ impl IntegerServerKey { compression_key, decompression_key, noise_squashing_key, + noise_squashing_compression_key, } } @@ -335,6 +384,7 @@ pub struct IntegerCompressedServerKey { pub(crate) compression_key: Option, pub(crate) decompression_key: Option, pub(crate) noise_squashing_key: Option, + pub(crate) noise_squashing_compression_key: Option, } impl IntegerCompressedServerKey { @@ -370,13 +420,22 @@ 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) - }); + let (noise_squashing_key, noise_squashing_compression_key) = client_key + .noise_squashing_private_key + .as_ref() + .map_or((None, None), |noise_squashing_private_key| { + let noise_squashing_key = + noise_squashing_private_key.new_compressed_noise_squashing_key(&client_key.key); + + let noise_squashing_compression_key = client_key + .noise_squashing_compression_private_key + .as_ref() + .map(|comp_private_key| { + noise_squashing_private_key + .new_compressed_noise_squashing_compression_key(comp_private_key) + }); + (Some(noise_squashing_key), noise_squashing_compression_key) + }); Self { key, @@ -384,6 +443,7 @@ impl IntegerCompressedServerKey { compression_key, decompression_key, noise_squashing_key, + noise_squashing_compression_key, } } @@ -411,6 +471,7 @@ impl IntegerCompressedServerKey { compression_key: Option, decompression_key: Option, noise_squashing_key: Option, + noise_squashing_compression_key: Option, ) -> Self { Self { key, @@ -418,6 +479,7 @@ impl IntegerCompressedServerKey { compression_key, decompression_key, noise_squashing_key, + noise_squashing_compression_key, } } @@ -437,6 +499,11 @@ impl IntegerCompressedServerKey { .as_ref() .map(CompressedNoiseSquashingKey::decompress); + let noise_squashing_compression_key = self + .noise_squashing_compression_key + .as_ref() + .map(CompressedNoiseSquashingCompressionKey::decompress); + IntegerServerKey { key: self.key.decompress(), cpk_key_switching_key_material: self.cpk_key_switching_key_material.as_ref().map( @@ -445,6 +512,7 @@ impl IntegerCompressedServerKey { compression_key, decompression_key, noise_squashing_key, + noise_squashing_compression_key, } } } @@ -533,6 +601,7 @@ pub struct IntegerServerKeyConformanceParams { )>, pub compression_param: Option, pub noise_squashing_param: Option, + pub noise_squashing_compression_param: Option, } impl> From for IntegerServerKeyConformanceParams { @@ -543,6 +612,7 @@ impl> From for IntegerServerKeyConformanceParams { cpk_param: config.inner.dedicated_compact_public_key_parameters, compression_param: config.inner.compression_parameters, noise_squashing_param: config.inner.noise_squashing_parameters, + noise_squashing_compression_param: config.inner.noise_squashing_compression_parameters, } } } @@ -602,6 +672,7 @@ impl ParameterSetConformant for IntegerServerKey { compression_key, decompression_key, noise_squashing_key, + noise_squashing_compression_key, } = self; let cpk_key_switching_key_material_is_ok = match ( @@ -652,10 +723,32 @@ impl ParameterSetConformant for IntegerServerKey { _ => return false, }; + let noise_squashing_compression_key_is_ok = match ( + parameter_set.noise_squashing_param.as_ref(), + parameter_set.noise_squashing_compression_param.as_ref(), + noise_squashing_compression_key.as_ref(), + ) { + (None, None, None) | (Some(_), None, None) => true, + ( + Some(noise_squashing_parameters), + Some(noise_squashing_compression_param), + Some(noise_squashing_compression_key), + ) => { + let noise_squashing_compression_param = ( + *noise_squashing_parameters, + *noise_squashing_compression_param, + ) + .into(); + noise_squashing_compression_key.is_conformant(&noise_squashing_compression_param) + } + _ => return false, + }; + key.is_conformant(¶meter_set.sk_param) && cpk_key_switching_key_material_is_ok && compression_is_ok && noise_squashing_key_is_ok + && noise_squashing_compression_key_is_ok } } @@ -669,6 +762,7 @@ impl ParameterSetConformant for IntegerCompressedServerKey { compression_key, decompression_key, noise_squashing_key, + noise_squashing_compression_key, } = self; let cpk_key_switching_key_material_is_ok = match ( @@ -719,10 +813,32 @@ impl ParameterSetConformant for IntegerCompressedServerKey { _ => return false, }; + let noise_squashing_compression_key_is_ok = match ( + parameter_set.noise_squashing_param.as_ref(), + parameter_set.noise_squashing_compression_param.as_ref(), + noise_squashing_compression_key.as_ref(), + ) { + (None, None, None) | (Some(_), None, None) => true, + ( + Some(noise_squashing_parameters), + Some(noise_squashing_compression_param), + Some(noise_squashing_compression_key), + ) => { + let noise_squashing_compression_param = ( + *noise_squashing_parameters, + *noise_squashing_compression_param, + ) + .into(); + noise_squashing_compression_key.is_conformant(&noise_squashing_compression_param) + } + _ => return false, + }; + key.is_conformant(¶meter_set.sk_param) && cpk_key_switching_key_material_is_ok && compression_is_ok && noise_squashing_key_is_ok + && noise_squashing_compression_key_is_ok } } diff --git a/tfhe/src/high_level_api/keys/server.rs b/tfhe/src/high_level_api/keys/server.rs index 4295d6073..d8309b6c9 100644 --- a/tfhe/src/high_level_api/keys/server.rs +++ b/tfhe/src/high_level_api/keys/server.rs @@ -1,9 +1,3 @@ -#[cfg(feature = "hpu")] -pub(in crate::high_level_api) use hpu::HpuTaggedDevice; -#[cfg(feature = "hpu")] -use tfhe_hpu_backend::prelude::HpuDevice; -use tfhe_versionable::Versionize; - use super::ClientKey; use crate::backward_compatibility::keys::{CompressedServerKeyVersions, ServerKeyVersions}; use crate::conformance::ParameterSetConformant; @@ -14,6 +8,9 @@ use crate::core_crypto::gpu::{synchronize_devices, CudaStreams}; #[cfg(feature = "gpu")] use crate::high_level_api::keys::inner::IntegerCudaServerKey; use crate::high_level_api::keys::{IntegerCompressedServerKey, IntegerServerKey}; +use crate::integer::ciphertext::{ + CompressedNoiseSquashingCompressionKey, NoiseSquashingCompressionKey, +}; use crate::integer::compression_keys::{ CompressedCompressionKey, CompressedDecompressionKey, CompressionKey, DecompressionKey, }; @@ -25,7 +22,12 @@ use crate::shortint::MessageModulus; #[cfg(feature = "gpu")] use crate::GpuIndex; use crate::{Device, Tag}; +#[cfg(feature = "hpu")] +pub(in crate::high_level_api) use hpu::HpuTaggedDevice; use std::sync::Arc; +#[cfg(feature = "hpu")] +use tfhe_hpu_backend::prelude::HpuDevice; +use tfhe_versionable::Versionize; /// Key of the server /// @@ -53,6 +55,7 @@ impl ServerKey { } } + #[allow(clippy::type_complexity)] pub fn into_raw_parts( self, ) -> ( @@ -61,6 +64,7 @@ impl ServerKey { Option, Option, Option, + Option, Tag, ) { let IntegerServerKey { @@ -69,6 +73,7 @@ impl ServerKey { compression_key, decompression_key, noise_squashing_key, + noise_squashing_compression_key, } = (*self.key).clone(); ( @@ -77,6 +82,7 @@ impl ServerKey { compression_key, decompression_key, noise_squashing_key, + noise_squashing_compression_key, self.tag, ) } @@ -89,6 +95,7 @@ impl ServerKey { compression_key: Option, decompression_key: Option, noise_squashing_key: Option, + noise_squashing_compression_key: Option, tag: Tag, ) -> Self { Self { @@ -98,6 +105,7 @@ impl ServerKey { compression_key, decompression_key, noise_squashing_key, + noise_squashing_compression_key, }), tag, } @@ -258,6 +266,7 @@ impl CompressedServerKey { compression_key: Option, decompression_key: Option, noise_squashing_key: Option, + noise_squashing_compression_key: Option, tag: Tag, ) -> Self { Self { @@ -267,6 +276,7 @@ impl CompressedServerKey { compression_key, decompression_key, noise_squashing_key, + noise_squashing_compression_key, ), tag, } @@ -546,6 +556,7 @@ mod test { use crate::prelude::ParameterSetConformant; use crate::shortint::parameters::{ COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, + NOISE_SQUASHING_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, @@ -631,10 +642,14 @@ mod test { let noise_squashing_params = NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + let noise_squashing_compression_params = + NOISE_SQUASHING_COMP_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) + .enable_noise_squashing_compression(noise_squashing_compression_params) .build(); let ck = ClientKey::generate(config); @@ -676,6 +691,7 @@ mod test { cpk_param: None, compression_param: None, noise_squashing_param: None, + noise_squashing_compression_param: None, }; assert!(!sk.is_conformant(&conformance_params)); @@ -704,6 +720,7 @@ mod test { cpk_param: Some((cpk_params, casting_params)), compression_param: None, noise_squashing_param: None, + noise_squashing_compression_param: None, }; assert!(!sk.is_conformant(&conformance_params)); @@ -786,10 +803,14 @@ mod test { let noise_squashing_params = NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + let noise_squashing_compression_params = + NOISE_SQUASHING_COMP_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) + .enable_noise_squashing_compression(noise_squashing_compression_params) .build(); let ck = ClientKey::generate(config); @@ -831,6 +852,7 @@ mod test { cpk_param: None, compression_param: None, noise_squashing_param: None, + noise_squashing_compression_param: None, }; assert!(!sk.is_conformant(&conformance_params)); @@ -859,6 +881,7 @@ mod test { cpk_param: Some((cpk_params, casting_params)), compression_param: None, noise_squashing_param: None, + noise_squashing_compression_param: None, }; assert!(!sk.is_conformant(&conformance_params)); diff --git a/tfhe/src/high_level_api/mod.rs b/tfhe/src/high_level_api/mod.rs index c63d1f13d..3a303288e 100644 --- a/tfhe/src/high_level_api/mod.rs +++ b/tfhe/src/high_level_api/mod.rs @@ -48,7 +48,8 @@ macro_rules! export_concrete_array_types { pub use crate::core_crypto::commons::math::random::Seed; pub use crate::integer::server_key::MatchValues; -use crate::{error, Error}; +use crate::{error, Error, Versionize}; +use backward_compatibility::compressed_ciphertext_list::SquashedNoiseCiphertextStateVersions; pub use config::{Config, ConfigBuilder}; #[cfg(feature = "gpu")] pub use global_state::CudaGpuChoice; @@ -124,6 +125,10 @@ pub use compact_list::{ pub use compressed_ciphertext_list::{ CompressedCiphertextList, CompressedCiphertextListBuilder, HlCompressible, HlExpandable, }; +pub use compressed_noise_squashed_ciphertext_list::{ + CompressedSquashedNoiseCiphertextList, CompressedSquashedNoiseCiphertextListBuilder, + HlSquashedNoiseCompressible, HlSquashedNoiseExpandable, +}; #[cfg(feature = "strings")] pub use strings::ascii::{EncryptableString, FheAsciiString, FheStringIsEmpty, FheStringLen}; pub use tag::Tag; @@ -152,6 +157,7 @@ mod tag; #[cfg(feature = "gpu")] pub use crate::core_crypto::gpu::vec::GpuIndex; +mod compressed_noise_squashed_ciphertext_list; pub(in crate::high_level_api) mod details; /// The tfhe prelude. pub mod prelude; @@ -271,3 +277,10 @@ impl TryFrom for FheTypes { Self::from_repr(value).ok_or_else(|| error!("Invalid value for FheTypes: {}", value)) } } + +#[derive(serde::Serialize, serde::Deserialize, Copy, Clone, Versionize)] +#[versionize(SquashedNoiseCiphertextStateVersions)] +pub(crate) enum SquashedNoiseCiphertextState { + Normal, + PostDecompression, +} diff --git a/tfhe/src/high_level_api/tests/mod.rs b/tfhe/src/high_level_api/tests/mod.rs index f823973d7..6b918cbad 100644 --- a/tfhe/src/high_level_api/tests/mod.rs +++ b/tfhe/src/high_level_api/tests/mod.rs @@ -198,7 +198,7 @@ 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, None, Tag::default()); + ClientKey::from_raw_parts(shortint_key.into(), None, None, None, None, Tag::default()); let sks = ServerKey::new(&client_key); let clear_a = 1344u32; diff --git a/tfhe/src/high_level_api/upgrade.rs b/tfhe/src/high_level_api/upgrade.rs index 9a9517254..3f70975b7 100644 --- a/tfhe/src/high_level_api/upgrade.rs +++ b/tfhe/src/high_level_api/upgrade.rs @@ -828,8 +828,8 @@ mod tests { // We need the private compression key to be common between GPU and CPU // for the rest of the test to work. This is the only way to do it // until a more convenient API is added - let (cks, pk, _, cnsk, tag) = ck.into_raw_parts(); - let ck = ClientKey::from_raw_parts(cks, pk, common_cck, cnsk, tag); + let (cks, pk, _, nsk, cnsk, tag) = ck.into_raw_parts(); + let ck = ClientKey::from_raw_parts(cks, pk, common_cck, nsk, cnsk, tag); let sk = CompressedServerKey::new(&ck); assert_eq!(sk.tag().as_u64(), 0); diff --git a/tfhe/src/integer/ciphertext/compressed_noise_squashed_ciphertext_list.rs b/tfhe/src/integer/ciphertext/compressed_noise_squashed_ciphertext_list.rs index 964f0fcc4..a489815f4 100644 --- a/tfhe/src/integer/ciphertext/compressed_noise_squashed_ciphertext_list.rs +++ b/tfhe/src/integer/ciphertext/compressed_noise_squashed_ciphertext_list.rs @@ -2,6 +2,7 @@ use super::{ DataKind, SquashedNoiseBooleanBlock, SquashedNoiseRadixCiphertext, SquashedNoiseSignedRadixCiphertext, }; +use crate::conformance::ParameterSetConformant; use crate::core_crypto::commons::math::random::{Deserialize, Serialize}; use crate::integer::backward_compatibility::list_compression::{ CompressedNoiseSquashingCompressionKeyVersions, CompressedSquashedNoiseCiphertextListVersions, @@ -16,6 +17,7 @@ use crate::shortint::ciphertext::{ use crate::shortint::list_compression::{ CompressedNoiseSquashingCompressionKey as ShortintCompressedNoiseSquashingCompressionKey, NoiseSquashingCompressionKey as ShortintNoiseSquashingCompressionKey, + NoiseSquashingCompressionKeyConformanceParams, NoiseSquashingCompressionPrivateKey as ShortintNoiseSquashingCompressionPrivateKey, }; use crate::shortint::parameters::NoiseSquashingCompressionParameters; @@ -53,6 +55,15 @@ pub struct CompressedNoiseSquashingCompressionKey { pub(crate) key: ShortintCompressedNoiseSquashingCompressionKey, } +impl ParameterSetConformant for CompressedNoiseSquashingCompressionKey { + type ParameterSet = NoiseSquashingCompressionKeyConformanceParams; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { key } = self; + key.is_conformant(parameter_set) + } +} + impl CompressedNoiseSquashingCompressionKey { pub fn decompress(&self) -> NoiseSquashingCompressionKey { let key = self.key.decompress(); @@ -74,6 +85,15 @@ impl Named for NoiseSquashingCompressionKey { const NAME: &'static str = "integer::NoiseSquashingCompressionKey"; } +impl ParameterSetConformant for NoiseSquashingCompressionKey { + type ParameterSet = NoiseSquashingCompressionKeyConformanceParams; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { key } = self; + key.is_conformant(parameter_set) + } +} + impl NoiseSquashingPrivateKey { pub fn new_noise_squashing_compression_key( &self, @@ -102,8 +122,8 @@ impl NoiseSquashingPrivateKey { #[derive(Clone, Debug, Serialize, Deserialize, Versionize)] #[versionize(CompressedSquashedNoiseCiphertextListVersions)] pub struct CompressedSquashedNoiseCiphertextList { - list: ShortintCompressedSquashedNoiseCiphertextList, - info: Vec, + pub(crate) list: ShortintCompressedSquashedNoiseCiphertextList, + pub(crate) info: Vec, } impl Named for CompressedSquashedNoiseCiphertextList { @@ -175,6 +195,10 @@ mod sealed { impl Sealed for SquashedNoiseRadixCiphertext {} impl Sealed for SquashedNoiseSignedRadixCiphertext {} impl Sealed for SquashedNoiseBooleanBlock {} + + impl Sealed for crate::SquashedNoiseFheBool {} + impl Sealed for crate::SquashedNoiseFheUint {} + impl Sealed for crate::SquashedNoiseFheInt {} } pub trait SquashedNoiseCompressible: sealed::Sealed { diff --git a/tfhe/src/shortint/list_compression/mod.rs b/tfhe/src/shortint/list_compression/mod.rs index 010c047ca..194c66612 100644 --- a/tfhe/src/shortint/list_compression/mod.rs +++ b/tfhe/src/shortint/list_compression/mod.rs @@ -9,5 +9,6 @@ pub use compressed_server_keys::{ }; pub use private_key::{CompressionPrivateKeys, NoiseSquashingCompressionPrivateKey}; pub use server_keys::{ - CompressionKey, CompressionKeyConformanceParams, DecompressionKey, NoiseSquashingCompressionKey, + CompressionKey, CompressionKeyConformanceParams, DecompressionKey, + NoiseSquashingCompressionKey, NoiseSquashingCompressionKeyConformanceParams, };