diff --git a/tfhe-csprng/src/seeders/mod.rs b/tfhe-csprng/src/seeders/mod.rs index 21a5a9971..524ee354c 100644 --- a/tfhe-csprng/src/seeders/mod.rs +++ b/tfhe-csprng/src/seeders/mod.rs @@ -24,7 +24,7 @@ pub struct XofSeed { } impl XofSeed { - const DOMAIN_SEP_LEN: usize = 8; + pub const DOMAIN_SEP_LEN: usize = 8; // Creates a new seed of 128 bits pub fn new_u128(seed: u128, domain_separator: [u8; Self::DOMAIN_SEP_LEN]) -> Self { diff --git a/tfhe/Cargo.toml b/tfhe/Cargo.toml index bdacc5fc6..a4de0aa18 100644 --- a/tfhe/Cargo.toml +++ b/tfhe/Cargo.toml @@ -70,8 +70,10 @@ aligned-vec = { workspace = true, features = ["default", "serde"] } dyn-stack = { workspace = true, features = ["default"] } paste = "1.0.7" fs2 = { version = "0.4.3", optional = true } -# Used for OPRF in shortint +# Used for OPRF in shortint and rerand sha3 = { version = "0.10", optional = true } +blake3 = { version = "1.8", optional = true } + itertools = { workspace = true } rand_core = { version = "0.6.4", features = ["std"] } strum = { version = "0.27", features = ["derive"], optional = true } @@ -93,7 +95,7 @@ tfhe-hpu-backend = { version = "0.2", path = "../backends/tfhe-hpu-backend", opt [features] boolean = [] -shortint = ["dep:sha3"] +shortint = ["dep:sha3", "dep:blake3"] integer = ["shortint", "dep:strum"] strings = ["integer"] internal-keycache = ["dep:fs2"] diff --git a/tfhe/src/core_crypto/commons/generators/seeder.rs b/tfhe/src/core_crypto/commons/generators/seeder.rs index 85b814b5c..2297f8b6f 100644 --- a/tfhe/src/core_crypto/commons/generators/seeder.rs +++ b/tfhe/src/core_crypto/commons/generators/seeder.rs @@ -3,6 +3,7 @@ use crate::core_crypto::commons::math::random::{ ByteRandomGenerator, RandomGenerable, RandomGenerator, Seed, Seeder, Uniform, }; +use tfhe_csprng::seeders::SeedKind; /// Seeder backed by a CSPRNG /// @@ -37,7 +38,7 @@ pub struct DeterministicSeeder { } impl DeterministicSeeder { - pub fn new(seed: Seed) -> Self { + pub fn new(seed: impl Into) -> Self { Self { generator: RandomGenerator::new(seed), } diff --git a/tfhe/src/high_level_api/array/mod.rs b/tfhe/src/high_level_api/array/mod.rs index 6a38b29fd..f01d7652f 100644 --- a/tfhe/src/high_level_api/array/mod.rs +++ b/tfhe/src/high_level_api/array/mod.rs @@ -15,6 +15,7 @@ use crate::high_level_api::array::traits::HasClear; use crate::high_level_api::global_state; use crate::high_level_api::integers::{FheIntId, FheUintId}; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::{FheBool, FheId, FheInt, FheUint, Tag}; use std::ops::{AddAssign, Mul, RangeBounds}; use traits::{ArrayBackend, BackendDataContainer, BackendDataContainerMut}; @@ -364,7 +365,11 @@ pub fn fhe_uint_array_eq(lhs: &[FheUint], rhs: &[FheUint] let result = cpu_key .pbs_key() .all_eq_slices_parallelized(&tmp_lhs, &tmp_rhs); - FheBool::new(result, cpu_key.tag.clone()) + FheBool::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(gpu_key) => { @@ -379,7 +384,11 @@ pub fn fhe_uint_array_eq(lhs: &[FheUint], rhs: &[FheUint] .collect::>(); let result = gpu_key.key.key.all_eq_slices(&tmp_lhs, &tmp_rhs, streams); - FheBool::new(result, gpu_key.tag.clone()) + FheBool::new( + result, + gpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -406,7 +415,11 @@ pub fn fhe_uint_array_contains_sub_slice( let result = cpu_key .pbs_key() .contains_sub_slice_parallelized(&tmp_lhs, &tmp_pattern); - FheBool::new(result, cpu_key.tag.clone()) + FheBool::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(gpu_key) => { @@ -424,7 +437,11 @@ pub fn fhe_uint_array_contains_sub_slice( .key .key .contains_sub_slice(&tmp_lhs, &tmp_pattern, streams); - FheBool::new(result, gpu_key.tag.clone()) + FheBool::new( + result, + gpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -512,7 +529,11 @@ where /// ``` fn dot_product(bools: &[FheBool], clears: &[Clear]) -> Self { let (blocks, tag) = fhe_bool_dot_product(bools, clears, Id::num_bits() as u32); - Self::new(crate::integer::RadixCiphertext::from(blocks), tag) + Self::new( + crate::integer::RadixCiphertext::from(blocks), + tag, + ReRandomizationMetadata::default(), + ) } } @@ -552,6 +573,10 @@ where /// ``` fn dot_product(bools: &[FheBool], clears: &[Clear]) -> Self { let (blocks, tag) = fhe_bool_dot_product(bools, clears, Id::num_bits() as u32); - Self::new(crate::integer::SignedRadixCiphertext::from(blocks), tag) + Self::new( + crate::integer::SignedRadixCiphertext::from(blocks), + tag, + ReRandomizationMetadata::default(), + ) } } diff --git a/tfhe/src/high_level_api/backward_compatibility/booleans.rs b/tfhe/src/high_level_api/backward_compatibility/booleans.rs index 2b64439a3..8ceedc2b8 100644 --- a/tfhe/src/high_level_api/backward_compatibility/booleans.rs +++ b/tfhe/src/high_level_api/backward_compatibility/booleans.rs @@ -5,6 +5,7 @@ use crate::high_level_api::booleans::{ InnerBoolean, InnerBooleanVersionOwned, InnerCompressedFheBool, InnerSquashedNoiseBoolean, InnerSquashedNoiseBooleanVersionOwned, SquashedNoiseFheBool, }; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::high_level_api::SquashedNoiseCiphertextState; use crate::{CompressedFheBool, FheBool, Tag}; use std::convert::Infallible; @@ -20,21 +21,42 @@ pub struct FheBoolV0 { pub(in crate::high_level_api) ciphertext: InnerBoolean, } -impl Upgrade for FheBoolV0 { +impl Upgrade for FheBoolV0 { type Error = Infallible; - fn upgrade(self) -> Result { - Ok(FheBool { + fn upgrade(self) -> Result { + Ok(FheBoolV1 { ciphertext: self.ciphertext, tag: Tag::default(), }) } } +#[derive(Version)] +pub struct FheBoolV1 { + pub(in crate::high_level_api) ciphertext: InnerBoolean, + pub(crate) tag: Tag, +} + +impl Upgrade for FheBoolV1 { + type Error = Infallible; + + fn upgrade(self) -> Result { + let Self { ciphertext, tag } = self; + + Ok(FheBool::new( + ciphertext, + tag, + ReRandomizationMetadata::default(), + )) + } +} + #[derive(VersionsDispatch)] pub enum FheBoolVersions { V0(FheBoolV0), - V1(FheBool), + V1(FheBoolV1), + V2(FheBool), } #[derive(VersionsDispatch)] 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 c96f82eea..ad8fe7e09 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,3 +1,5 @@ +use crate::high_level_api::compressed_ciphertext_list::InnerCompressedCiphertextList; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::high_level_api::SquashedNoiseCiphertextState; use std::convert::Infallible; use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; @@ -30,13 +32,37 @@ pub struct CompressedCiphertextListV1 { tag: Tag, } -impl Upgrade for CompressedCiphertextListV1 { +impl Upgrade for CompressedCiphertextListV1 { + type Error = Infallible; + + fn upgrade(self) -> Result { + Ok(CompressedCiphertextListV2 { + inner: InnerCompressedCiphertextList::Cpu(self.inner), + tag: self.tag, + }) + } +} + +#[derive(Version)] +pub struct CompressedCiphertextListV2 { + pub(in crate::high_level_api) inner: InnerCompressedCiphertextList, + pub(in crate::high_level_api) tag: Tag, +} + +impl Upgrade for CompressedCiphertextListV2 { type Error = Infallible; fn upgrade(self) -> Result { + let Self { inner, tag } = self; + + // Empty metadata for older lists which did not store any + let re_randomization_metadata = + vec![ReRandomizationMetadata::default(); inner.info().len()]; + Ok(CompressedCiphertextList { - inner: crate::high_level_api::compressed_ciphertext_list::InnerCompressedCiphertextList::Cpu(self.inner), - tag: self.tag, + inner, + tag, + re_randomization_metadata, }) } } @@ -45,7 +71,8 @@ impl Upgrade for CompressedCiphertextListV1 { pub enum CompressedCiphertextListVersions { V0(CompressedCiphertextListV0), V1(CompressedCiphertextListV1), - V2(CompressedCiphertextList), + V2(CompressedCiphertextListV2), + V3(CompressedCiphertextList), } #[derive(VersionsDispatch)] diff --git a/tfhe/src/high_level_api/backward_compatibility/cpk_re_randomization.rs b/tfhe/src/high_level_api/backward_compatibility/cpk_re_randomization.rs new file mode 100644 index 000000000..b3ac777b1 --- /dev/null +++ b/tfhe/src/high_level_api/backward_compatibility/cpk_re_randomization.rs @@ -0,0 +1,7 @@ +use crate::high_level_api::re_randomization::ReRandomizationMetadata; +use tfhe_versionable::VersionsDispatch; + +#[derive(VersionsDispatch)] +pub enum ReRandomizationMetadataVersions { + V0(ReRandomizationMetadata), +} diff --git a/tfhe/src/high_level_api/backward_compatibility/integers.rs b/tfhe/src/high_level_api/backward_compatibility/integers.rs index 414829b3c..6b450a35e 100644 --- a/tfhe/src/high_level_api/backward_compatibility/integers.rs +++ b/tfhe/src/high_level_api/backward_compatibility/integers.rs @@ -9,6 +9,7 @@ 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::re_randomization::ReRandomizationMetadata; use crate::high_level_api::SquashedNoiseCiphertextState; use crate::integer::backward_compatibility::ciphertext::{ CompressedModulusSwitchedRadixCiphertextTFHE06, @@ -131,11 +132,11 @@ pub struct FheIntV0 { pub(in crate::high_level_api) id: Id, } -impl Upgrade> for FheIntV0 { +impl Upgrade> for FheIntV0 { type Error = Infallible; - fn upgrade(self) -> Result, Self::Error> { - Ok(FheInt { + fn upgrade(self) -> Result, Self::Error> { + Ok(FheIntV1 { ciphertext: self.ciphertext, id: self.id, tag: Tag::default(), @@ -143,10 +144,37 @@ impl Upgrade> for FheIntV0 { } } +#[derive(Version)] +pub struct FheIntV1 { + pub(in crate::high_level_api) ciphertext: SignedRadixCiphertext, + pub(in crate::high_level_api) id: Id, + pub(crate) tag: Tag, +} + +impl Upgrade> for FheIntV1 { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { + let Self { + ciphertext, + id, + tag, + } = self; + + Ok(FheInt { + ciphertext, + id, + tag, + re_randomization_metadata: ReRandomizationMetadata::default(), + }) + } +} + #[derive(VersionsDispatch)] pub enum FheIntVersions { V0(FheIntV0), - V1(FheInt), + V1(FheIntV1), + V2(FheInt), } #[derive(Version)] @@ -182,11 +210,11 @@ pub struct FheUintV0 { pub(in crate::high_level_api) id: Id, } -impl Upgrade> for FheUintV0 { +impl Upgrade> for FheUintV0 { type Error = Infallible; - fn upgrade(self) -> Result, Self::Error> { - Ok(FheUint { + fn upgrade(self) -> Result, Self::Error> { + Ok(FheUintV1 { ciphertext: self.ciphertext, id: self.id, tag: Tag::default(), @@ -194,10 +222,37 @@ impl Upgrade> for FheUintV0 { } } +#[derive(Version)] +pub struct FheUintV1 { + pub(in crate::high_level_api) ciphertext: UnsignedRadixCiphertext, + pub(in crate::high_level_api) id: Id, + pub(crate) tag: Tag, +} + +impl Upgrade> for FheUintV1 { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { + let Self { + ciphertext, + id, + tag, + } = self; + + Ok(FheUint { + ciphertext, + id, + tag, + re_randomization_metadata: ReRandomizationMetadata::default(), + }) + } +} + #[derive(VersionsDispatch)] pub enum FheUintVersions { V0(FheUintV0), - V1(FheUint), + V1(FheUintV1), + V2(FheUint), } #[derive(Version)] diff --git a/tfhe/src/high_level_api/backward_compatibility/keys.rs b/tfhe/src/high_level_api/backward_compatibility/keys.rs index dd6bb2250..ccc3e8166 100644 --- a/tfhe/src/high_level_api/backward_compatibility/keys.rs +++ b/tfhe/src/high_level_api/backward_compatibility/keys.rs @@ -1,6 +1,11 @@ use crate::high_level_api::keys::*; +use crate::integer::ciphertext::{ + CompressedNoiseSquashingCompressionKey, NoiseSquashingCompressionKey, + NoiseSquashingCompressionPrivateKey, +}; use crate::integer::compression_keys::{ - CompressedCompressionKey, CompressedDecompressionKey, CompressionKey, DecompressionKey, + CompressedCompressionKey, CompressedDecompressionKey, CompressionKey, CompressionPrivateKeys, + DecompressionKey, }; use crate::integer::noise_squashing::{ CompressedNoiseSquashingKey, NoiseSquashingKey, NoiseSquashingPrivateKey, @@ -208,10 +213,40 @@ impl Upgrade for IntegerClientKeyV3 { pub(crate) struct IntegerClientKeyV4 { pub(crate) key: crate::integer::ClientKey, pub(crate) dedicated_compact_private_key: Option, - pub(crate) compression_key: Option, + pub(crate) compression_key: Option, pub(crate) noise_squashing_private_key: Option, } +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(IntegerClientKeyV5 { + key, + dedicated_compact_private_key, + compression_key, + noise_squashing_private_key, + noise_squashing_compression_private_key: None, + }) + } +} + +#[derive(Version)] +pub(crate) struct IntegerClientKeyV5 { + 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, + pub(crate) noise_squashing_compression_private_key: Option, +} + #[derive(VersionsDispatch)] #[allow(unused)] pub(crate) enum IntegerClientKeyVersions { @@ -220,10 +255,11 @@ pub(crate) enum IntegerClientKeyVersions { V2(IntegerClientKeyV2), V3(IntegerClientKeyV3), V4(IntegerClientKeyV4), - V5(IntegerClientKey), + V5(IntegerClientKeyV5), + V6(IntegerClientKey), } -impl Upgrade for IntegerClientKeyV4 { +impl Upgrade for IntegerClientKeyV5 { type Error = Infallible; fn upgrade(self) -> Result { @@ -232,6 +268,7 @@ impl Upgrade for IntegerClientKeyV4 { dedicated_compact_private_key, compression_key, noise_squashing_private_key, + noise_squashing_compression_private_key, } = self; Ok(IntegerClientKey { @@ -239,7 +276,8 @@ impl Upgrade for IntegerClientKeyV4 { dedicated_compact_private_key, compression_key, noise_squashing_private_key, - noise_squashing_compression_private_key: None, + noise_squashing_compression_private_key, + cpk_re_randomization_ksk_params: None, }) } } @@ -289,7 +327,41 @@ pub struct IntegerServerKeyV5 { pub(crate) noise_squashing_key: Option, } -impl Upgrade for IntegerServerKeyV5 { +impl Upgrade for IntegerServerKeyV5 { + type Error = Infallible; + + fn upgrade(self) -> Result { + let Self { + key, + cpk_key_switching_key_material, + compression_key, + decompression_key, + noise_squashing_key, + } = self; + + Ok(IntegerServerKeyV6 { + key, + cpk_key_switching_key_material, + compression_key, + decompression_key, + noise_squashing_key, + noise_squashing_compression_key: None, + }) + } +} + +#[derive(Version)] +pub struct IntegerServerKeyV6 { + 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, + pub(crate) noise_squashing_compression_key: Option, +} + +impl Upgrade for IntegerServerKeyV6 { type Error = Infallible; fn upgrade(self) -> Result { @@ -299,6 +371,7 @@ impl Upgrade for IntegerServerKeyV5 { compression_key, decompression_key, noise_squashing_key, + noise_squashing_compression_key, } = self; Ok(IntegerServerKey { @@ -307,7 +380,8 @@ impl Upgrade for IntegerServerKeyV5 { compression_key, decompression_key, noise_squashing_key, - noise_squashing_compression_key: None, + noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material: None, }) } } @@ -320,7 +394,8 @@ pub enum IntegerServerKeyVersions { V3(Deprecated), V4(IntegerServerKeyV4), V5(IntegerServerKeyV5), - V6(IntegerServerKey), + V6(IntegerServerKeyV6), + V7(IntegerServerKey), } impl Deprecable for IntegerCompressedServerKey { @@ -368,16 +443,41 @@ pub struct IntegerCompressedServerKeyV3 { pub(crate) noise_squashing_key: Option, } -#[derive(VersionsDispatch)] -pub enum IntegerCompressedServerKeyVersions { - V0(Deprecated), - V1(Deprecated), - V2(IntegerCompressedServerKeyV2), - 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(IntegerCompressedServerKeyV4 { + key, + cpk_key_switching_key_material, + compression_key, + decompression_key, + noise_squashing_key, + noise_squashing_compression_key: None, + }) + } } -impl Upgrade for IntegerCompressedServerKeyV3 { +#[derive(Version)] +pub struct IntegerCompressedServerKeyV4 { + 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, + pub(crate) noise_squashing_compression_key: Option, +} + +impl Upgrade for IntegerCompressedServerKeyV4 { type Error = Infallible; fn upgrade(self) -> Result { @@ -387,6 +487,7 @@ impl Upgrade for IntegerCompressedServerKeyV3 { compression_key, decompression_key, noise_squashing_key, + noise_squashing_compression_key, } = self; Ok(IntegerCompressedServerKey { @@ -395,11 +496,22 @@ impl Upgrade for IntegerCompressedServerKeyV3 { compression_key, decompression_key, noise_squashing_key, - noise_squashing_compression_key: None, + noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material: None, }) } } +#[derive(VersionsDispatch)] +pub enum IntegerCompressedServerKeyVersions { + V0(Deprecated), + V1(Deprecated), + V2(IntegerCompressedServerKeyV2), + V3(IntegerCompressedServerKeyV3), + V4(IntegerCompressedServerKeyV4), + V5(IntegerCompressedServerKey), +} + #[derive(VersionsDispatch)] #[allow(unused)] pub(in crate::high_level_api) enum IntegerCompactPublicKeyVersions { @@ -422,3 +534,13 @@ pub enum KeySwitchingKeyVersions { V0(Deprecated), V1(KeySwitchingKey), } + +#[derive(VersionsDispatch)] +pub enum ReRandomizationKeySwitchingKeyVersions { + V0(ReRandomizationKeySwitchingKey), +} + +#[derive(VersionsDispatch)] +pub enum CompressedReRandomizationKeySwitchingKeyVersions { + V0(CompressedReRandomizationKeySwitchingKey), +} diff --git a/tfhe/src/high_level_api/backward_compatibility/mod.rs b/tfhe/src/high_level_api/backward_compatibility/mod.rs index 020ad94bf..9f8915fed 100644 --- a/tfhe/src/high_level_api/backward_compatibility/mod.rs +++ b/tfhe/src/high_level_api/backward_compatibility/mod.rs @@ -6,6 +6,7 @@ pub mod booleans; pub mod compact_list; pub mod compressed_ciphertext_list; pub mod config; +pub mod cpk_re_randomization; pub mod integers; pub mod keys; #[cfg(feature = "strings")] diff --git a/tfhe/src/high_level_api/booleans/base.rs b/tfhe/src/high_level_api/booleans/base.rs index 5e0c90a33..1e153ae41 100644 --- a/tfhe/src/high_level_api/booleans/base.rs +++ b/tfhe/src/high_level_api/booleans/base.rs @@ -2,11 +2,14 @@ use super::inner::InnerBoolean; use crate::backward_compatibility::booleans::FheBoolVersions; use crate::conformance::ParameterSetConformant; use crate::core_crypto::prelude::{SignedNumeric, UnsignedNumeric}; -use crate::high_level_api::global_state; +use crate::high_level_api::errors::UninitializedReRandKey; use crate::high_level_api::integers::{FheInt, FheIntId, FheUint, FheUintId}; use crate::high_level_api::keys::InternalServerKey; -use crate::high_level_api::traits::{FheEq, IfThenElse, ScalarIfThenElse, Tagged}; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; +use crate::high_level_api::traits::{FheEq, IfThenElse, ReRandomize, ScalarIfThenElse, Tagged}; +use crate::high_level_api::{global_state, CompactPublicKey}; use crate::integer::block_decomposition::DecomposableInto; +use crate::integer::ciphertext::ReRandomizationSeed; #[cfg(feature = "gpu")] use crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock; #[cfg(feature = "gpu")] @@ -61,6 +64,7 @@ use tfhe_hpu_backend::prelude::*; pub struct FheBool { pub(in crate::high_level_api) ciphertext: InnerBoolean, pub(crate) tag: Tag, + pub(crate) re_randomization_metadata: ReRandomizationMetadata, } impl Named for FheBool { @@ -93,7 +97,11 @@ impl ParameterSetConformant for FheBool { type ParameterSet = FheBoolConformanceParams; fn is_conformant(&self, params: &FheBoolConformanceParams) -> bool { - let Self { ciphertext, tag: _ } = self; + let Self { + ciphertext, + tag: _, + re_randomization_metadata: _, + } = self; let BooleanBlock(block) = &*ciphertext.on_cpu(); @@ -102,10 +110,15 @@ impl ParameterSetConformant for FheBool { } impl FheBool { - pub(in crate::high_level_api) fn new>(ciphertext: T, tag: Tag) -> Self { + pub(in crate::high_level_api) fn new>( + ciphertext: T, + tag: Tag, + re_randomization_metadata: ReRandomizationMetadata, + ) -> Self { Self { ciphertext: ciphertext.into(), tag, + re_randomization_metadata, } } @@ -199,6 +212,14 @@ impl FheBool { pub fn is_trivial(&self) -> bool { self.ciphertext.on_cpu().is_trivial() } + + pub fn re_randomization_metadata(&self) -> &ReRandomizationMetadata { + &self.re_randomization_metadata + } + + pub fn re_randomization_metadata_mut(&mut self) -> &mut ReRandomizationMetadata { + &mut self.re_randomization_metadata + } } impl ScalarIfThenElse<&FheUint, Scalar> for FheBool @@ -238,7 +259,11 @@ where &*then_value.ciphertext.on_cpu(), else_value, ); - FheUint::new(inner, cpu_sks.tag.clone()) + FheUint::new( + inner, + cpu_sks.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -289,7 +314,11 @@ where then_value, &*else_value.ciphertext.on_cpu(), ); - FheUint::new(inner, cpu_sks.tag.clone()) + FheUint::new( + inner, + cpu_sks.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -340,7 +369,11 @@ where &*then_value.ciphertext.on_cpu(), else_value, ); - FheInt::new(inner, cpu_sks.tag.clone()) + FheInt::new( + inner, + cpu_sks.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -391,7 +424,11 @@ where then_value, &*else_value.ciphertext.on_cpu(), ); - FheInt::new(inner, cpu_sks.tag.clone()) + FheInt::new( + inner, + cpu_sks.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -436,7 +473,7 @@ impl ScalarIfThenElse<&Self, &Self> for FheBool { panic!("Hpu does not support if_then_else with clear input") } }); - Self::new(ciphertext, tag) + Self::new(ciphertext, tag, ReRandomizationMetadata::default()) } } @@ -459,7 +496,11 @@ where &*ct_then.ciphertext.on_cpu(), &*ct_else.ciphertext.on_cpu(), ); - FheUint::new(inner, cpu_sks.tag.clone()) + FheUint::new( + inner, + cpu_sks.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -471,7 +512,11 @@ where streams, ); - FheUint::new(inner, cuda_key.tag.clone()) + FheUint::new( + inner, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -495,7 +540,11 @@ where ) .pop() .unwrap(); - FheUint::new(hpu_result, device.tag.clone()) + FheUint::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -517,7 +566,7 @@ impl IfThenElse> for FheBool { &*ct_then.ciphertext.on_cpu(), &*ct_else.ciphertext.on_cpu(), ); - FheInt::new(new_ct, key.tag.clone()) + FheInt::new(new_ct, key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -529,7 +578,11 @@ impl IfThenElse> for FheBool { streams, ); - FheInt::new(inner, cuda_key.tag.clone()) + FheInt::new( + inner, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -568,7 +621,7 @@ impl IfThenElse for FheBool { panic!("Hpu does not support bool if then else") } }); - Self::new(ciphertext, tag) + Self::new(ciphertext, tag, ReRandomizationMetadata::default()) } } @@ -613,7 +666,11 @@ where other.borrow().ciphertext.on_cpu().as_ref(), ); let ciphertext = InnerBoolean::Cpu(BooleanBlock::new_unchecked(inner)); - Self::new(ciphertext, key.tag.clone()) + Self::new( + ciphertext, + key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -624,7 +681,11 @@ where streams, ); let ciphertext = InnerBoolean::Cuda(inner); - Self::new(ciphertext, cuda_key.tag.clone()) + Self::new( + ciphertext, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -660,7 +721,11 @@ where other.borrow().ciphertext.on_cpu().as_ref(), ); let ciphertext = InnerBoolean::Cpu(BooleanBlock::new_unchecked(inner)); - Self::new(ciphertext, key.tag.clone()) + Self::new( + ciphertext, + key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -671,7 +736,11 @@ where streams, ); let ciphertext = InnerBoolean::Cuda(inner); - Self::new(ciphertext, cuda_key.tag.clone()) + Self::new( + ciphertext, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -727,7 +796,7 @@ impl FheEq for FheBool { panic!("Hpu does not support FheBool::eq with a bool") } }); - Self::new(ciphertext, tag) + Self::new(ciphertext, tag, ReRandomizationMetadata::default()) } /// Test for equality between a [FheBool] and a [bool] @@ -775,7 +844,7 @@ impl FheEq for FheBool { panic!("Hpu does not support FheBool::ne with a bool") } }); - Self::new(ciphertext, tag) + Self::new(ciphertext, tag, ReRandomizationMetadata::default()) } } @@ -929,7 +998,7 @@ where panic!("Hpu does not support bitand (&)") } }); - FheBool::new(ciphertext, tag) + FheBool::new(ciphertext, tag, ReRandomizationMetadata::default()) } } @@ -1058,7 +1127,7 @@ where panic!("Hpu does not support bitor (|)") } }); - FheBool::new(ciphertext, tag) + FheBool::new(ciphertext, tag, ReRandomizationMetadata::default()) } } @@ -1187,7 +1256,7 @@ where panic!("Hpu does not support bitxor (^)") } }); - FheBool::new(ciphertext, tag) + FheBool::new(ciphertext, tag, ReRandomizationMetadata::default()) } } @@ -1308,7 +1377,7 @@ impl BitAnd for &FheBool { panic!("hpu does not bitand (&) with a bool") } }); - FheBool::new(ciphertext, tag) + FheBool::new(ciphertext, tag, ReRandomizationMetadata::default()) } } @@ -1389,7 +1458,7 @@ impl BitOr for &FheBool { panic!("hpu does not bitor (|) with a bool") } }); - FheBool::new(ciphertext, tag) + FheBool::new(ciphertext, tag, ReRandomizationMetadata::default()) } } @@ -1470,7 +1539,7 @@ impl BitXor for &FheBool { panic!("hpu does not bitxor (^) with a bool") } }); - FheBool::new(ciphertext, tag) + FheBool::new(ciphertext, tag, ReRandomizationMetadata::default()) } } @@ -1969,7 +2038,48 @@ impl std::ops::Not for &FheBool { panic!("Hpu does not support bitnot (!)") } }); - FheBool::new(ciphertext, tag) + FheBool::new(ciphertext, tag, ReRandomizationMetadata::default()) + } +} + +impl ReRandomize for FheBool { + fn add_to_re_randomization_context( + &self, + context: &mut crate::high_level_api::re_randomization::ReRandomizationContext, + ) { + let on_cpu = self.ciphertext.on_cpu(); + context.inner.add_ciphertext(&*on_cpu); + context + .inner + .add_bytes(self.re_randomization_metadata.data()); + } + + fn re_randomize( + &mut self, + compact_public_key: &CompactPublicKey, + seed: ReRandomizationSeed, + ) -> crate::Result<()> { + global_state::with_internal_keys(|key| match key { + InternalServerKey::Cpu(key) => { + let Some(re_randomization_key) = key.re_randomization_cpk_casting_key() else { + return Err(UninitializedReRandKey.into()); + }; + + self.ciphertext.as_cpu_mut().re_randomize( + &compact_public_key.key.key, + &re_randomization_key, + seed, + )?; + + Ok(()) + } + #[cfg(feature = "gpu")] + InternalServerKey::Cuda(_cuda_key) => panic!("GPU does not support CPKReRandomize."), + #[cfg(feature = "hpu")] + InternalServerKey::Hpu(_device) => { + panic!("HPU does not support CPKReRandomize.") + } + }) } } diff --git a/tfhe/src/high_level_api/booleans/compressed.rs b/tfhe/src/high_level_api/booleans/compressed.rs index 8f51b3a62..8a774be7e 100644 --- a/tfhe/src/high_level_api/booleans/compressed.rs +++ b/tfhe/src/high_level_api/booleans/compressed.rs @@ -3,6 +3,7 @@ use crate::backward_compatibility::booleans::{ }; use crate::conformance::ParameterSetConformant; use crate::high_level_api::global_state::with_cpu_internal_keys; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::high_level_api::traits::Tagged; use crate::integer::BooleanBlock; use crate::named::Named; @@ -76,7 +77,11 @@ impl CompressedFheBool { with_cpu_internal_keys(|sk| sk.pbs_key().key.decompress(modulus_switched)) } }); - let mut ciphertext = FheBool::new(ciphertext, self.tag.clone()); + let mut ciphertext = FheBool::new( + ciphertext, + self.tag.clone(), + ReRandomizationMetadata::default(), + ); ciphertext.ciphertext.move_to_device_of_server_key_if_set(); diff --git a/tfhe/src/high_level_api/booleans/encrypt.rs b/tfhe/src/high_level_api/booleans/encrypt.rs index 83d8e2576..0fb343e0c 100644 --- a/tfhe/src/high_level_api/booleans/encrypt.rs +++ b/tfhe/src/high_level_api/booleans/encrypt.rs @@ -2,6 +2,7 @@ use super::base::FheBool; use crate::high_level_api::booleans::inner::InnerBoolean; use crate::high_level_api::global_state; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; #[cfg(feature = "gpu")] use crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock; #[cfg(feature = "gpu")] @@ -14,7 +15,11 @@ impl FheTryEncrypt for FheBool { fn try_encrypt(value: bool, key: &ClientKey) -> Result { let integer_client_key = &key.key.key; - let mut ciphertext = Self::new(integer_client_key.encrypt_bool(value), key.tag.clone()); + let mut ciphertext = Self::new( + integer_client_key.encrypt_bool(value), + key.tag.clone(), + ReRandomizationMetadata::default(), + ); ciphertext.ciphertext.move_to_device_of_server_key_if_set(); Ok(ciphertext) } @@ -55,7 +60,11 @@ impl FheTryEncrypt for FheBool { type Error = crate::Error; fn try_encrypt(value: bool, key: &CompressedPublicKey) -> Result { - let mut ciphertext = Self::new(key.key.encrypt_bool(value), key.tag.clone()); + let mut ciphertext = Self::new( + key.key.encrypt_bool(value), + key.tag.clone(), + ReRandomizationMetadata::default(), + ); ciphertext.ciphertext.move_to_device_of_server_key_if_set(); Ok(ciphertext) } @@ -65,7 +74,11 @@ impl FheTryEncrypt for FheBool { type Error = crate::Error; fn try_encrypt(value: bool, key: &PublicKey) -> Result { - let mut ciphertext = Self::new(key.key.encrypt_bool(value), key.tag.clone()); + let mut ciphertext = Self::new( + key.key.encrypt_bool(value), + key.tag.clone(), + ReRandomizationMetadata::default(), + ); ciphertext.ciphertext.move_to_device_of_server_key_if_set(); Ok(ciphertext) } @@ -105,6 +118,10 @@ impl FheTryTrivialEncrypt for FheBool { panic!("Hpu does not support trivial encryption") } }); - Ok(Self::new(ciphertext, tag)) + Ok(Self::new( + ciphertext, + tag, + ReRandomizationMetadata::default(), + )) } } diff --git a/tfhe/src/high_level_api/booleans/oprf.rs b/tfhe/src/high_level_api/booleans/oprf.rs index 6be175493..c72b0f7e8 100644 --- a/tfhe/src/high_level_api/booleans/oprf.rs +++ b/tfhe/src/high_level_api/booleans/oprf.rs @@ -1,6 +1,7 @@ use super::{FheBool, InnerBoolean}; use crate::high_level_api::global_state; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; #[cfg(feature = "gpu")] use crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock; #[cfg(feature = "gpu")] @@ -57,6 +58,6 @@ impl FheBool { panic!("Hpu does not support random bool generation") } }); - Self::new(ciphertext, tag) + Self::new(ciphertext, tag, ReRandomizationMetadata::default()) } } diff --git a/tfhe/src/high_level_api/compressed_ciphertext_list.rs b/tfhe/src/high_level_api/compressed_ciphertext_list.rs index a450985e6..b641cb4dd 100644 --- a/tfhe/src/high_level_api/compressed_ciphertext_list.rs +++ b/tfhe/src/high_level_api/compressed_ciphertext_list.rs @@ -19,6 +19,7 @@ use crate::high_level_api::global_state::device_of_internal_keys; #[cfg(feature = "gpu")] use crate::high_level_api::global_state::with_cuda_internal_keys; use crate::high_level_api::integers::{FheIntId, FheUintId}; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::integer::ciphertext::{DataKind, Expandable}; use crate::integer::compression_keys::DecompressionKey; #[cfg(feature = "gpu")] @@ -28,6 +29,8 @@ use crate::integer::gpu::ciphertext::compressed_ciphertext_list::{ #[cfg(feature = "gpu")] use crate::integer::gpu::ciphertext::CudaRadixCiphertext; #[cfg(feature = "gpu")] +use crate::integer::gpu::list_compression::server_keys::CudaDecompressionKey; +#[cfg(feature = "gpu")] use crate::integer::parameters::LweDimension; use crate::named::Named; use crate::prelude::{CiphertextList, Tagged}; @@ -60,6 +63,10 @@ impl HlCompressible for FheUint { } } } + + fn get_re_randomization_metadata(&self) -> ReRandomizationMetadata { + self.re_randomization_metadata.clone() + } } impl HlCompressible for FheInt { fn compress_into(self, messages: &mut Vec<(ToBeCompressed, DataKind)>) { @@ -81,6 +88,10 @@ impl HlCompressible for FheInt { } } } + + fn get_re_randomization_metadata(&self) -> ReRandomizationMetadata { + self.re_randomization_metadata.clone() + } } impl HlCompressible for FheBool { fn compress_into(self, messages: &mut Vec<(ToBeCompressed, DataKind)>) { @@ -98,19 +109,47 @@ impl HlCompressible for FheBool { InnerBoolean::Hpu(_) => panic!("HPU does not support compression"), } } + + fn get_re_randomization_metadata(&self) -> ReRandomizationMetadata { + self.re_randomization_metadata.clone() + } } -impl HlExpandable for FheUint {} -impl HlExpandable for FheInt {} -impl HlExpandable for FheBool {} +impl HlExpandable for FheUint { + fn set_re_randomization_metadata(&mut self, meta: ReRandomizationMetadata) { + self.re_randomization_metadata = meta; + } +} +impl HlExpandable for FheInt { + fn set_re_randomization_metadata(&mut self, meta: ReRandomizationMetadata) { + self.re_randomization_metadata = meta; + } +} +impl HlExpandable for FheBool { + fn set_re_randomization_metadata(&mut self, meta: ReRandomizationMetadata) { + self.re_randomization_metadata = meta; + } +} #[cfg(not(feature = "gpu"))] -pub trait HlExpandable: Expandable {} +pub trait HlExpandable: Expandable { + /// Sets the metadata of the ciphertext from the ones in the compressed list + // Defined as an empty default method for backward compatibility + fn set_re_randomization_metadata(&mut self, _meta: ReRandomizationMetadata) {} +} #[cfg(feature = "gpu")] -pub trait HlExpandable: Expandable + CudaExpandable {} +pub trait HlExpandable: Expandable + CudaExpandable { + fn set_re_randomization_metadata(&mut self, _meta: ReRandomizationMetadata) {} +} pub trait HlCompressible { + /// Adds a ciphertext to be compressed. + /// + /// This should push at most one single element at the end of the `messages` vec fn compress_into(self, messages: &mut Vec<(ToBeCompressed, DataKind)>); + fn get_re_randomization_metadata(&self) -> ReRandomizationMetadata { + ReRandomizationMetadata::default() + } } pub enum ToBeCompressed { @@ -121,19 +160,34 @@ pub enum ToBeCompressed { pub struct CompressedCiphertextListBuilder { inner: Vec<(ToBeCompressed, DataKind)>, + rerandomization_metadata: Vec, } impl CompressedCiphertextListBuilder { #[allow(clippy::new_without_default)] pub fn new() -> Self { - Self { inner: vec![] } + Self { + inner: vec![], + rerandomization_metadata: vec![], + } } pub fn push(&mut self, value: T) -> &mut Self where T: HlCompressible, { + let size_before = self.inner.len(); + let meta = value.get_re_randomization_metadata(); value.compress_into(&mut self.inner); + + let size_after = self.inner.len(); + + // `compress_into` should only push a single element at the end of the builder + assert!(size_before <= size_after); + assert!(size_after - size_before <= 1); + if size_after - size_before == 1 { + self.rerandomization_metadata.push(meta) + } self } @@ -148,6 +202,10 @@ impl CompressedCiphertextListBuilder { } pub fn build(&self) -> crate::Result { + if self.inner.len() != self.rerandomization_metadata.len() { + return Err(crate::Error::new("Invalid CompressedCiphertextListBuilder, ct and metadata lists should have the same length".to_owned())); + } + crate::high_level_api::global_state::try_with_internal_keys(|keys| match keys { Some(InternalServerKey::Cpu(cpu_key)) => { let mut flat_cpu_blocks = vec![]; @@ -188,6 +246,7 @@ impl CompressedCiphertextListBuilder { }, ), tag: cpu_key.tag.clone(), + re_randomization_metadata: self.rerandomization_metadata.clone(), } }) } @@ -231,6 +290,7 @@ impl CompressedCiphertextListBuilder { CompressedCiphertextList { inner: InnerCompressedCiphertextList::Cuda(compressed_list), tag: cuda_key.tag.clone(), + re_randomization_metadata: self.rerandomization_metadata.clone(), } }) } @@ -402,6 +462,14 @@ impl InnerCompressedCiphertextList { } } } + + pub(crate) fn info(&self) -> &[DataKind] { + match self { + Self::Cpu(compressed_ciphertext_list) => &compressed_ciphertext_list.info, + #[cfg(feature = "gpu")] + Self::Cuda(compressed_ciphertext_list) => &compressed_ciphertext_list.info, + } + } } impl Versionize for InnerCompressedCiphertextList { @@ -455,6 +523,7 @@ impl Unversionize for InnerCompressedCiphertextList { pub struct CompressedCiphertextList { pub(in crate::high_level_api) inner: InnerCompressedCiphertextList, pub(in crate::high_level_api) tag: Tag, + pub(in crate::high_level_api) re_randomization_metadata: Vec, } impl Named for CompressedCiphertextList { @@ -533,16 +602,12 @@ impl CiphertextList for CompressedCiphertextList { crate::Error::new("Compression key not set in server key".to_owned()) }) .and_then(|decompression_key| { - let mut ct = { - let streams = &cuda_key.streams; - self.inner - .on_gpu(streams) - .get::(index, decompression_key, streams) - }; - if let Ok(Some(ct_ref)) = &mut ct { - ct_ref.tag_mut().set_data(cuda_key.tag.data()) - } - ct + self.get_using_cuda_key( + index, + decompression_key, + &cuda_key.streams, + &cuda_key.tag, + ) }), #[cfg(feature = "hpu")] Some(InternalServerKey::Hpu(_)) => { @@ -579,10 +644,20 @@ impl CiphertextList for CompressedCiphertextList { } impl CompressedCiphertextList { - pub fn into_raw_parts(self) -> (crate::integer::ciphertext::CompressedCiphertextList, Tag) { - let Self { inner, tag } = self; + pub fn into_raw_parts( + self, + ) -> ( + crate::integer::ciphertext::CompressedCiphertextList, + Tag, + Vec, + ) { + let Self { + inner, + tag, + re_randomization_metadata, + } = self; match inner { - InnerCompressedCiphertextList::Cpu(inner) => (inner, tag), + InnerCompressedCiphertextList::Cpu(inner) => (inner, tag, re_randomization_metadata), #[cfg(feature = "gpu")] InnerCompressedCiphertextList::Cuda(inner) => ( with_cuda_internal_keys(|keys| { @@ -590,6 +665,7 @@ impl CompressedCiphertextList { inner.to_compressed_ciphertext_list(streams) }), tag, + re_randomization_metadata, ), } } @@ -597,10 +673,12 @@ impl CompressedCiphertextList { pub fn from_raw_parts( inner: crate::integer::ciphertext::CompressedCiphertextList, tag: Tag, + re_randomization_metadata: Vec, ) -> Self { Self { inner: InnerCompressedCiphertextList::Cpu(inner), tag, + re_randomization_metadata, } } @@ -614,6 +692,19 @@ impl CompressedCiphertextList { } } + fn get_re_randomization_metadata( + &self, + index: usize, + ) -> crate::Result { + Ok(self + .re_randomization_metadata + .get(index) + .ok_or_else(|| { + crate::error!("Unable to retrieve metadata for ciphertext at index {index}.") + })? + .clone()) + } + pub(crate) fn get_using_key( &self, index: usize, @@ -626,6 +717,31 @@ impl CompressedCiphertextList { let mut ct = self.inner.on_cpu().get::(index, decompression_key); if let Ok(Some(ct_ref)) = &mut ct { ct_ref.tag_mut().set_data(tag.data()); + + ct_ref.set_re_randomization_metadata(self.get_re_randomization_metadata(index)?); + } + ct + } + + #[cfg(feature = "gpu")] + pub(crate) fn get_using_cuda_key( + &self, + index: usize, + decompression_key: &CudaDecompressionKey, + streams: &CudaStreams, + tag: &Tag, + ) -> crate::Result> + where + T: HlExpandable + Tagged, + { + let mut ct = self + .inner + .on_gpu(streams) + .get::(index, decompression_key, streams); + if let Ok(Some(ct_ref)) = &mut ct { + ct_ref.tag_mut().set_data(tag.data()); + + ct_ref.set_re_randomization_metadata(self.get_re_randomization_metadata(index)?); } ct } @@ -649,7 +765,7 @@ pub mod gpu { CudaCompressible, CudaExpandable, }; use crate::integer::gpu::ciphertext::CudaRadixCiphertext; - use crate::{FheBool, FheInt, FheUint, Tag}; + use crate::{FheBool, FheInt, FheUint, ReRandomizationMetadata, Tag}; impl CudaCompressible for FheUint { fn compress_into( @@ -711,6 +827,7 @@ pub mod gpu { ciphertext: blocks, }, Tag::default(), + ReRandomizationMetadata::default(), )) } else { Err(crate::error!( @@ -763,6 +880,7 @@ pub mod gpu { ciphertext: blocks, }, Tag::default(), + ReRandomizationMetadata::default(), )) } else { Err(crate::error!( @@ -809,7 +927,11 @@ pub mod gpu { crate::shortint::ciphertext::Degree::new(1); // The expander will be responsible for setting the correct tag - Ok(Self::new(boolean_block, Tag::default())) + Ok(Self::new( + boolean_block, + Tag::default(), + ReRandomizationMetadata::default(), + )) } DataKind::String { .. } => Err(crate::error!( "Tried to expand a FheBool while a FheString is stored in this slot" diff --git a/tfhe/src/high_level_api/config.rs b/tfhe/src/high_level_api/config.rs index fcc199c35..7a41c5c0f 100644 --- a/tfhe/src/high_level_api/config.rs +++ b/tfhe/src/high_level_api/config.rs @@ -3,7 +3,9 @@ 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::{NoiseSquashingCompressionParameters, NoiseSquashingParameters}; +use crate::shortint::parameters::{ + NoiseSquashingCompressionParameters, NoiseSquashingParameters, ShortintKeySwitchingParameters, +}; /// The config type #[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize, Versionize)] @@ -71,7 +73,7 @@ impl ConfigBuilder { /// /// # Note /// - /// This requires nose squashing to be enabled first via [Self::enable_noise_squashing] + /// This requires noise squashing to be enabled first via [Self::enable_noise_squashing] pub fn enable_noise_squashing_compression( mut self, compression_parameters: NoiseSquashingCompressionParameters, @@ -86,6 +88,24 @@ impl ConfigBuilder { self } + /// Enable the re-randomization of ciphertexts after compression using a + /// [crate::CompactPublicKey]. + /// + /// # Note + /// + /// This requires dedicated [crate::CompactPublicKey] parameters to be enabled via + /// [Self::use_dedicated_compact_public_key_parameters]. + pub fn enable_ciphertext_re_randomization( + mut self, + cpk_re_randomization_ksk_params: ShortintKeySwitchingParameters, + ) -> Self { + self.config + .inner + .enable_ciphertext_re_randomization(cpk_re_randomization_ksk_params); + + self + } + pub fn with_custom_parameters

(block_parameters: P) -> Self where P: Into, diff --git a/tfhe/src/high_level_api/errors.rs b/tfhe/src/high_level_api/errors.rs index 7bb54cefc..a92909272 100644 --- a/tfhe/src/high_level_api/errors.rs +++ b/tfhe/src/high_level_api/errors.rs @@ -66,3 +66,25 @@ impl From for Error { Self::new(format!("{value}")) } } + +#[derive(Debug)] +pub struct UninitializedReRandKey; + +impl Display for UninitializedReRandKey { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "No keyswitching key available to perform \ + CompactPublicKey re-randomization. Did you forget to call \ + enable_ciphertext_re_randomization on your Config?" + ) + } +} + +impl std::error::Error for UninitializedReRandKey {} + +impl From for Error { + fn from(value: UninitializedReRandKey) -> Self { + Self::new(format!("{value}")) + } +} diff --git a/tfhe/src/high_level_api/integers/oprf.rs b/tfhe/src/high_level_api/integers/oprf.rs index 040c97cc6..1dc78e432 100644 --- a/tfhe/src/high_level_api/integers/oprf.rs +++ b/tfhe/src/high_level_api/integers/oprf.rs @@ -1,6 +1,7 @@ use super::{FheIntId, FheUint, FheUintId}; use crate::high_level_api::global_state; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; #[cfg(feature = "gpu")] use crate::integer::gpu::ciphertext::{CudaSignedRadixCiphertext, CudaUnsignedRadixCiphertext}; use crate::{FheInt, Seed}; @@ -34,7 +35,7 @@ impl FheUint { Id::num_blocks(key.message_modulus()) as u64, ); - Self::new(ct, key.tag.clone()) + Self::new(ct, key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -48,7 +49,11 @@ impl FheUint { streams, ); - Self::new(d_ct, cuda_key.tag.clone()) + Self::new( + d_ct, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -119,7 +124,7 @@ impl FheUint { Id::num_blocks(key.message_modulus()) as u64, ); - Self::new(ct, key.tag.clone()) + Self::new(ct, key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -133,7 +138,11 @@ impl FheUint { Id::num_blocks(cuda_key.message_modulus()) as u64, streams, ); - Self::new(d_ct, cuda_key.tag.clone()) + Self::new( + d_ct, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -206,7 +215,7 @@ impl FheInt { seed, Id::num_blocks(key.message_modulus()) as u64, ); - Self::new(ct, key.tag.clone()) + Self::new(ct, key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -220,7 +229,11 @@ impl FheInt { streams, ); - Self::new(d_ct, cuda_key.tag.clone()) + Self::new( + d_ct, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -293,7 +306,7 @@ impl FheInt { Id::num_blocks(key.message_modulus()) as u64, ); - Self::new(ct, key.tag.clone()) + Self::new(ct, key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -307,7 +320,11 @@ impl FheInt { Id::num_blocks(cuda_key.message_modulus()) as u64, streams, ); - Self::new(d_ct, cuda_key.tag.clone()) + Self::new( + d_ct, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { diff --git a/tfhe/src/high_level_api/integers/signed/base.rs b/tfhe/src/high_level_api/integers/signed/base.rs index f3e5e205a..393f46161 100644 --- a/tfhe/src/high_level_api/integers/signed/base.rs +++ b/tfhe/src/high_level_api/integers/signed/base.rs @@ -4,11 +4,14 @@ use super::inner::SignedRadixCiphertext; use crate::backward_compatibility::integers::FheIntVersions; use crate::conformance::ParameterSetConformant; use crate::core_crypto::prelude::SignedNumeric; +use crate::high_level_api::errors::UninitializedReRandKey; use crate::high_level_api::global_state; use crate::high_level_api::integers::{FheUint, FheUintId, IntegerId}; -use crate::high_level_api::keys::InternalServerKey; -use crate::high_level_api::traits::Tagged; +use crate::high_level_api::keys::{CompactPublicKey, InternalServerKey}; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; +use crate::high_level_api::traits::{ReRandomize, Tagged}; use crate::integer::block_decomposition::{DecomposableInto, RecomposableSignedInteger}; +use crate::integer::ciphertext::ReRandomizationSeed; use crate::integer::parameters::RadixCiphertextConformanceParams; use crate::named::Named; use crate::prelude::CastFrom; @@ -38,6 +41,7 @@ pub struct FheInt { pub(in crate::high_level_api) ciphertext: SignedRadixCiphertext, pub(in crate::high_level_api) id: Id, pub(crate) tag: Tag, + pub(crate) re_randomization_metadata: ReRandomizationMetadata, } #[derive(Copy, Clone)] @@ -79,6 +83,7 @@ impl ParameterSetConformant for FheInt { ciphertext, id: _, tag: _, + re_randomization_metadata: _, } = self; ciphertext.on_cpu().is_conformant(¶ms.params) @@ -109,32 +114,44 @@ where pub(in crate::high_level_api) fn new( ciphertext: impl Into, tag: Tag, + re_randomization_metadata: ReRandomizationMetadata, ) -> Self { Self { ciphertext: ciphertext.into(), id: Id::default(), tag, + re_randomization_metadata, } } - pub fn into_raw_parts(self) -> (crate::integer::SignedRadixCiphertext, Id, Tag) { + pub fn into_raw_parts( + self, + ) -> ( + crate::integer::SignedRadixCiphertext, + Id, + Tag, + ReRandomizationMetadata, + ) { let Self { ciphertext, id, tag, + re_randomization_metadata, } = self; - (ciphertext.into_cpu(), id, tag) + (ciphertext.into_cpu(), id, tag, re_randomization_metadata) } pub fn from_raw_parts( ciphertext: crate::integer::SignedRadixCiphertext, id: Id, tag: Tag, + re_randomization_metadata: ReRandomizationMetadata, ) -> Self { Self { ciphertext: ciphertext.into(), id, tag, + re_randomization_metadata, } } @@ -192,7 +209,11 @@ where let ciphertext = cpu_key .pbs_key() .abs_parallelized(&*self.ciphertext.on_cpu()); - Self::new(ciphertext, cpu_key.tag.clone()) + Self::new( + ciphertext, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -201,7 +222,11 @@ where .key .key .abs(&*self.ciphertext.on_gpu(streams), streams); - Self::new(result, cuda_key.tag.clone()) + Self::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -233,7 +258,11 @@ where let result = cpu_key .pbs_key() .is_even_parallelized(&*self.ciphertext.on_cpu()); - FheBool::new(result, cpu_key.tag.clone()) + FheBool::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -242,7 +271,11 @@ where .key .key .is_even(&*self.ciphertext.on_gpu(streams), streams); - FheBool::new(result, cuda_key.tag.clone()) + FheBool::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -274,7 +307,11 @@ where let result = cpu_key .pbs_key() .is_odd_parallelized(&*self.ciphertext.on_cpu()); - FheBool::new(result, cpu_key.tag.clone()) + FheBool::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -283,7 +320,11 @@ where .key .key .is_odd(&*self.ciphertext.on_gpu(streams), streams); - FheBool::new(result, cuda_key.tag.clone()) + FheBool::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -319,7 +360,11 @@ where result, crate::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - crate::FheUint32::new(result, cpu_key.tag.clone()) + crate::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -333,7 +378,11 @@ where crate::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus), streams, ); - crate::FheUint32::new(result, cuda_key.tag.clone()) + crate::FheUint32::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -369,7 +418,11 @@ where result, crate::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - crate::FheUint32::new(result, cpu_key.tag.clone()) + crate::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -383,7 +436,11 @@ where crate::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus), streams, ); - crate::FheUint32::new(result, cuda_key.tag.clone()) + crate::FheUint32::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -419,7 +476,11 @@ where result, crate::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - crate::FheUint32::new(result, cpu_key.tag.clone()) + crate::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -433,7 +494,11 @@ where crate::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus), streams, ); - crate::FheUint32::new(result, cuda_key.tag.clone()) + crate::FheUint32::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -469,7 +534,11 @@ where result, crate::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - crate::FheUint32::new(result, cpu_key.tag.clone()) + crate::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -483,7 +552,11 @@ where crate::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus), streams, ); - crate::FheUint32::new(result, cuda_key.tag.clone()) + crate::FheUint32::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -520,7 +593,11 @@ where result, crate::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - crate::FheUint32::new(result, cpu_key.tag.clone()) + crate::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -561,7 +638,11 @@ where result, crate::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - crate::FheUint32::new(result, cpu_key.tag.clone()) + crate::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -603,7 +684,11 @@ where result, crate::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - crate::FheUint32::new(result, cpu_key.tag.clone()) + crate::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -617,7 +702,11 @@ where crate::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus), streams, ); - crate::FheUint32::new(result, cuda_key.tag.clone()) + crate::FheUint32::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -660,8 +749,16 @@ where crate::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); ( - crate::FheUint32::new(result, cpu_key.tag.clone()), - FheBool::new(is_ok, cpu_key.tag.clone()), + crate::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + is_ok, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -677,8 +774,16 @@ where streams, ); ( - crate::FheUint32::new(result, cuda_key.tag.clone()), - FheBool::new(is_ok, cuda_key.tag.clone()), + crate::FheUint32::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + is_ok, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "hpu")] @@ -753,7 +858,11 @@ where let ct = self.ciphertext.on_cpu(); - Self::new(sk.reverse_bits_parallelized(&*ct), cpu_key.tag.clone()) + Self::new( + sk.reverse_bits_parallelized(&*ct), + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -804,7 +913,11 @@ where Id::num_blocks(sk.message_modulus()), ); - Self::new(result, cpu_key.tag.clone()) + Self::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -832,6 +945,14 @@ where { Self::if_then_else(condition, true_value, false_value) } + + pub fn re_randomization_metadata(&self) -> &ReRandomizationMetadata { + &self.re_randomization_metadata + } + + pub fn re_randomization_metadata_mut(&mut self) -> &mut ReRandomizationMetadata { + &mut self.re_randomization_metadata + } } impl CastFrom> for FheInt @@ -863,7 +984,11 @@ where let new_ciphertext = cpu_key .pbs_key() .cast_to_signed(input.ciphertext.into_cpu(), target_num_blocks); - Self::new(new_ciphertext, cpu_key.tag.clone()) + Self::new( + new_ciphertext, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -874,7 +999,11 @@ where target_num_blocks, streams, ); - Self::new(new_ciphertext, cuda_key.tag.clone()) + Self::new( + new_ciphertext, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -913,7 +1042,11 @@ where input.ciphertext.on_cpu().to_owned(), IntoId::num_blocks(cpu_key.message_modulus()), ); - Self::new(new_ciphertext, cpu_key.tag.clone()) + Self::new( + new_ciphertext, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -923,7 +1056,11 @@ where IntoId::num_blocks(cuda_key.message_modulus()), streams, ); - Self::new(new_ciphertext, cuda_key.tag.clone()) + Self::new( + new_ciphertext, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -965,7 +1102,11 @@ where Id::num_blocks(cpu_key.message_modulus()), cpu_key.pbs_key(), ); - Self::new(ciphertext, cpu_key.tag.clone()) + Self::new( + ciphertext, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -975,7 +1116,11 @@ where Id::num_blocks(cuda_key.message_modulus()), streams, ); - Self::new(inner, cuda_key.tag.clone()) + Self::new( + inner, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -984,3 +1129,47 @@ where }) } } + +impl ReRandomize for FheInt +where + Id: FheIntId, +{ + fn add_to_re_randomization_context( + &self, + context: &mut crate::high_level_api::re_randomization::ReRandomizationContext, + ) { + let on_cpu = self.ciphertext.on_cpu(); + context.inner.add_ciphertext(&*on_cpu); + context + .inner + .add_bytes(self.re_randomization_metadata.data()); + } + + fn re_randomize( + &mut self, + compact_public_key: &CompactPublicKey, + seed: ReRandomizationSeed, + ) -> crate::Result<()> { + global_state::with_internal_keys(|key| match key { + InternalServerKey::Cpu(key) => { + let Some(re_randomization_key) = key.re_randomization_cpk_casting_key() else { + return Err(UninitializedReRandKey.into()); + }; + + self.ciphertext.as_cpu_mut().re_randomize( + &compact_public_key.key.key, + &re_randomization_key, + seed, + )?; + + Ok(()) + } + #[cfg(feature = "gpu")] + InternalServerKey::Cuda(_cuda_key) => panic!("GPU does not support CPKReRandomize."), + #[cfg(feature = "hpu")] + InternalServerKey::Hpu(_device) => { + panic!("HPU does not support CPKReRandomize.") + } + }) + } +} diff --git a/tfhe/src/high_level_api/integers/signed/compressed.rs b/tfhe/src/high_level_api/integers/signed/compressed.rs index 9fbe04632..427150374 100644 --- a/tfhe/src/high_level_api/integers/signed/compressed.rs +++ b/tfhe/src/high_level_api/integers/signed/compressed.rs @@ -8,6 +8,7 @@ use crate::core_crypto::prelude::SignedNumeric; use crate::high_level_api::global_state::with_cpu_internal_keys; use crate::high_level_api::integers::signed::base::FheIntConformanceParams; use crate::high_level_api::integers::{FheInt, FheIntId}; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::high_level_api::traits::Tagged; use crate::integer::block_decomposition::DecomposableInto; use crate::integer::ciphertext::{ @@ -113,7 +114,11 @@ where with_cpu_internal_keys(|sk| sk.pbs_key().decompress_signed_parallelized(ct)) } }; - FheInt::new(ciphertext, self.tag.clone()) + FheInt::new( + ciphertext, + self.tag.clone(), + ReRandomizationMetadata::default(), + ) } } diff --git a/tfhe/src/high_level_api/integers/signed/encrypt.rs b/tfhe/src/high_level_api/integers/signed/encrypt.rs index 7a6d34176..8fd9b3983 100644 --- a/tfhe/src/high_level_api/integers/signed/encrypt.rs +++ b/tfhe/src/high_level_api/integers/signed/encrypt.rs @@ -2,6 +2,7 @@ use crate::core_crypto::prelude::SignedNumeric; use crate::high_level_api::global_state; use crate::high_level_api::integers::FheIntId; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::integer::block_decomposition::{DecomposableInto, RecomposableSignedInteger}; #[cfg(feature = "gpu")] use crate::integer::gpu::ciphertext::CudaSignedRadixCiphertext; @@ -52,7 +53,11 @@ where .key .key .encrypt_signed_radix(value, Id::num_blocks(key.message_modulus())); - Ok(Self::new(ciphertext, key.tag.clone())) + Ok(Self::new( + ciphertext, + key.tag.clone(), + ReRandomizationMetadata::default(), + )) } } @@ -67,7 +72,11 @@ where let ciphertext = key .key .encrypt_signed_radix(value, Id::num_blocks(key.message_modulus())); - Ok(Self::new(ciphertext, key.tag.clone())) + Ok(Self::new( + ciphertext, + key.tag.clone(), + ReRandomizationMetadata::default(), + )) } } @@ -82,7 +91,11 @@ where let ciphertext = key .key .encrypt_signed_radix(value, Id::num_blocks(key.message_modulus())); - Ok(Self::new(ciphertext, key.tag.clone())) + Ok(Self::new( + ciphertext, + key.tag.clone(), + ReRandomizationMetadata::default(), + )) } } @@ -108,7 +121,11 @@ where let ciphertext: crate::integer::SignedRadixCiphertext = key .pbs_key() .create_trivial_radix(value, Id::num_blocks(key.message_modulus())); - Ok(Self::new(ciphertext, key.tag.clone())) + Ok(Self::new( + ciphertext, + key.tag.clone(), + ReRandomizationMetadata::default(), + )) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -118,7 +135,11 @@ where Id::num_blocks(cuda_key.key.key.message_modulus), streams, ); - Ok(Self::new(inner, cuda_key.tag.clone())) + Ok(Self::new( + inner, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + )) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_) => panic!("Hpu does not currently support signed operation"), diff --git a/tfhe/src/high_level_api/integers/signed/ops.rs b/tfhe/src/high_level_api/integers/signed/ops.rs index 8010b7428..ede6a5118 100644 --- a/tfhe/src/high_level_api/integers/signed/ops.rs +++ b/tfhe/src/high_level_api/integers/signed/ops.rs @@ -3,6 +3,7 @@ use crate::high_level_api::details::MaybeCloned; use crate::high_level_api::global_state; use crate::high_level_api::integers::{FheIntId, FheUintId}; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; #[cfg(feature = "gpu")] use crate::high_level_api::traits::{ AddSizeOnGpu, BitAndSizeOnGpu, BitNotSizeOnGpu, BitOrSizeOnGpu, BitXorSizeOnGpu, @@ -69,9 +70,13 @@ where cpu_key.pbs_key().create_trivial_zero_radix(Id::num_blocks( cpu_key.message_modulus(), )); - Self::new(radix, cpu_key.tag.clone()) + Self::new( + radix, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) }, - |ct| Self::new(ct, cpu_key.tag.clone()), + |ct| Self::new(ct, cpu_key.tag.clone(), ReRandomizationMetadata::default()), ) } #[cfg(feature = "gpu")] @@ -106,7 +111,11 @@ where streams, ) }); - Self::new(inner, cuda_key.tag.clone()) + Self::new( + inner, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } } #[cfg(feature = "hpu")] @@ -148,7 +157,11 @@ where let inner_result = cpu_key .pbs_key() .max_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - Self::new(inner_result, cpu_key.tag.clone()) + Self::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -158,7 +171,11 @@ where &*rhs.ciphertext.on_gpu(streams), streams, ); - Self::new(inner_result, cuda_key.tag.clone()) + Self::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -209,7 +226,11 @@ where let inner_result = cpu_key .pbs_key() .min_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - Self::new(inner_result, cpu_key.tag.clone()) + Self::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -219,7 +240,11 @@ where &*rhs.ciphertext.on_gpu(streams), streams, ); - Self::new(inner_result, cuda_key.tag.clone()) + Self::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -281,7 +306,11 @@ where let inner_result = cpu_key .pbs_key() .eq_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -291,7 +320,11 @@ where &*rhs.ciphertext.on_gpu(streams), streams, ); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -325,7 +358,11 @@ where let inner_result = cpu_key .pbs_key() .ne_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -335,7 +372,11 @@ where &*rhs.ciphertext.on_gpu(streams), streams, ); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -395,7 +436,11 @@ where let inner_result = cpu_key .pbs_key() .lt_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -405,7 +450,11 @@ where &*rhs.ciphertext.on_gpu(streams), streams, ); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -439,7 +488,11 @@ where let inner_result = cpu_key .pbs_key() .le_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -449,7 +502,11 @@ where &*rhs.ciphertext.on_gpu(streams), streams, ); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -483,7 +540,11 @@ where let inner_result = cpu_key .pbs_key() .gt_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -493,7 +554,11 @@ where &*rhs.ciphertext.on_gpu(streams), streams, ); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -527,7 +592,11 @@ where let inner_result = cpu_key .pbs_key() .ge_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -537,7 +606,11 @@ where &*rhs.ciphertext.on_gpu(streams), streams, ); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -612,8 +685,8 @@ where .pbs_key() .div_rem_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); ( - FheInt::::new(q, cpu_key.tag.clone()), - FheInt::::new(r, cpu_key.tag.clone()), + FheInt::::new(q, cpu_key.tag.clone(), ReRandomizationMetadata::default()), + FheInt::::new(r, cpu_key.tag.clone(), ReRandomizationMetadata::default()), ) } #[cfg(feature = "gpu")] @@ -625,8 +698,8 @@ where streams, ); ( - FheInt::::new(q, cuda_key.tag.clone()), - FheInt::::new(r, cuda_key.tag.clone()), + FheInt::::new(q, cuda_key.tag.clone(), ReRandomizationMetadata::default()), + FheInt::::new(r, cuda_key.tag.clone(), ReRandomizationMetadata::default()), ) } #[cfg(feature = "hpu")] @@ -702,14 +775,14 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .add_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheInt::new(inner_result, cpu_key.tag.clone()) + FheInt::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { {let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .add(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } } #[cfg(feature = "hpu")] @@ -749,14 +822,14 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .sub_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheInt::new(inner_result, cpu_key.tag.clone()) + FheInt::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { {let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .sub(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } } #[cfg(feature = "hpu")] @@ -796,14 +869,14 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .mul_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheInt::new(inner_result, cpu_key.tag.clone()) + FheInt::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { {let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .mul(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } } #[cfg(feature = "hpu")] @@ -841,14 +914,14 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .bitand_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheInt::new(inner_result, cpu_key.tag.clone()) + FheInt::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { {let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .bitand(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } } #[cfg(feature = "hpu")] @@ -886,14 +959,14 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .bitor_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheInt::new(inner_result, cpu_key.tag.clone()) + FheInt::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { {let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .bitor(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } } #[cfg(feature = "hpu")] @@ -931,14 +1004,14 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .bitxor_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheInt::new(inner_result, cpu_key.tag.clone()) + FheInt::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { {let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .bitxor(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } } #[cfg(feature = "hpu")] @@ -984,7 +1057,7 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .div_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheInt::new(inner_result, cpu_key.tag.clone()) + FheInt::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => {let streams = &cuda_key.streams; @@ -993,7 +1066,7 @@ generic_integer_impl_operation!( .key .key .div(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -1039,7 +1112,7 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .rem_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheInt::new(inner_result, cpu_key.tag.clone()) + FheInt::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => {let streams = &cuda_key.streams; @@ -1048,7 +1121,7 @@ generic_integer_impl_operation!( .key .key .rem(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -1157,14 +1230,14 @@ generic_integer_impl_shift_rotate!( let ciphertext = cpu_key .pbs_key() .left_shift_parallelized(&*lhs.ciphertext.on_cpu(), &rhs.ciphertext.on_cpu()); - FheInt::new(ciphertext, cpu_key.tag.clone()) + FheInt::new(ciphertext, cpu_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { {let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .left_shift(&*lhs.ciphertext.on_gpu(streams), &rhs.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } } #[cfg(feature = "hpu")] @@ -1205,14 +1278,14 @@ generic_integer_impl_shift_rotate!( let ciphertext = cpu_key .pbs_key() .right_shift_parallelized(&*lhs.ciphertext.on_cpu(), &rhs.ciphertext.on_cpu()); - FheInt::new(ciphertext, cpu_key.tag.clone()) + FheInt::new(ciphertext, cpu_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { {let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .right_shift(&*lhs.ciphertext.on_gpu(streams), &rhs.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } } #[cfg(feature = "hpu")] @@ -1253,14 +1326,14 @@ generic_integer_impl_shift_rotate!( let ciphertext = cpu_key .pbs_key() .rotate_left_parallelized(&*lhs.ciphertext.on_cpu(), &rhs.ciphertext.on_cpu()); - FheInt::new(ciphertext, cpu_key.tag.clone()) + FheInt::new(ciphertext, cpu_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { {let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .rotate_left(&*lhs.ciphertext.on_gpu(streams), &rhs.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } } #[cfg(feature = "hpu")] @@ -1301,14 +1374,14 @@ generic_integer_impl_shift_rotate!( let ciphertext = cpu_key .pbs_key() .rotate_right_parallelized(&*lhs.ciphertext.on_cpu(), &rhs.ciphertext.on_cpu()); - FheInt::new(ciphertext, cpu_key.tag.clone()) + FheInt::new(ciphertext, cpu_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { {let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .rotate_right(&*lhs.ciphertext.on_gpu(streams), &rhs.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } } #[cfg(feature = "hpu")] @@ -2017,7 +2090,11 @@ where let ciphertext = cpu_key .pbs_key() .neg_parallelized(&*self.ciphertext.on_cpu()); - FheInt::new(ciphertext, cpu_key.tag.clone()) + FheInt::new( + ciphertext, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -2026,7 +2103,11 @@ where .key .key .neg(&*self.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -2091,7 +2172,11 @@ where global_state::with_internal_keys(|keys| match keys { InternalServerKey::Cpu(cpu_key) => { let ciphertext = cpu_key.pbs_key().bitnot(&*self.ciphertext.on_cpu()); - FheInt::new(ciphertext, cpu_key.tag.clone()) + FheInt::new( + ciphertext, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -2100,7 +2185,11 @@ where .key .key .bitnot(&*self.ciphertext.on_gpu(streams), streams); - FheInt::new(inner_result, cuda_key.tag.clone()) + FheInt::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { diff --git a/tfhe/src/high_level_api/integers/signed/overflowing_ops.rs b/tfhe/src/high_level_api/integers/signed/overflowing_ops.rs index 12d619ba1..ed77f44fd 100644 --- a/tfhe/src/high_level_api/integers/signed/overflowing_ops.rs +++ b/tfhe/src/high_level_api/integers/signed/overflowing_ops.rs @@ -2,6 +2,7 @@ use crate::core_crypto::prelude::SignedNumeric; use crate::high_level_api::global_state; use crate::high_level_api::integers::FheIntId; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::integer::block_decomposition::DecomposableInto; use crate::prelude::{OverflowingAdd, OverflowingMul, OverflowingNeg, OverflowingSub}; use crate::{FheBool, FheInt}; @@ -46,8 +47,16 @@ where &other.ciphertext.on_cpu(), ); ( - FheInt::new(result, cpu_key.tag.clone()), - FheBool::new(overflow, cpu_key.tag.clone()), + FheInt::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -59,8 +68,16 @@ where streams, ); ( - FheInt::new(result, cuda_key.tag.clone()), - FheBool::new(overflow, cuda_key.tag.clone()), + FheInt::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "hpu")] @@ -147,8 +164,16 @@ where .pbs_key() .signed_overflowing_scalar_add_parallelized(&self.ciphertext.on_cpu(), other); ( - FheInt::new(result, cpu_key.tag.clone()), - FheBool::new(overflow, cpu_key.tag.clone()), + FheInt::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -160,8 +185,16 @@ where streams, ); ( - FheInt::new(result, cuda_key.tag.clone()), - FheBool::new(overflow, cuda_key.tag.clone()), + FheInt::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "hpu")] @@ -286,8 +319,16 @@ where &other.ciphertext.on_cpu(), ); ( - FheInt::new(result, cpu_key.tag.clone()), - FheBool::new(overflow, cpu_key.tag.clone()), + FheInt::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -299,8 +340,16 @@ where streams, ); ( - FheInt::new(result, cuda_key.tag.clone()), - FheBool::new(overflow, cuda_key.tag.clone()), + FheInt::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "hpu")] @@ -386,8 +435,16 @@ where .pbs_key() .signed_overflowing_scalar_sub_parallelized(&self.ciphertext.on_cpu(), other); ( - FheInt::new(result, cpu_key.tag.clone()), - FheBool::new(overflow, cpu_key.tag.clone()), + FheInt::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -399,8 +456,16 @@ where streams, ); ( - FheInt::new(result, cuda_key.tag.clone()), - FheBool::new(overflow, cuda_key.tag.clone()), + FheInt::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "hpu")] @@ -487,8 +552,16 @@ where &other.ciphertext.on_cpu(), ); ( - FheInt::new(result, cpu_key.tag.clone()), - FheBool::new(overflow, cpu_key.tag.clone()), + FheInt::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -574,8 +647,16 @@ where .pbs_key() .overflowing_neg_parallelized(&*self.ciphertext.on_cpu()); ( - FheInt::new(result, cpu_key.tag.clone()), - FheBool::new(overflow, cpu_key.tag.clone()), + FheInt::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -585,8 +666,16 @@ where &cuda_key.streams, ); ( - FheInt::new(result, cuda_key.tag.clone()), - FheBool::new(overflow, cuda_key.tag.clone()), + FheInt::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "hpu")] diff --git a/tfhe/src/high_level_api/integers/signed/scalar_ops.rs b/tfhe/src/high_level_api/integers/signed/scalar_ops.rs index 8917c1bf2..ee3547577 100644 --- a/tfhe/src/high_level_api/integers/signed/scalar_ops.rs +++ b/tfhe/src/high_level_api/integers/signed/scalar_ops.rs @@ -5,6 +5,7 @@ use crate::high_level_api::global_state; use crate::high_level_api::integers::signed::inner::SignedRadixCiphertext; use crate::high_level_api::integers::FheIntId; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; #[cfg(feature = "gpu")] use crate::high_level_api::traits::{ AddSizeOnGpu, BitAndSizeOnGpu, BitOrSizeOnGpu, BitXorSizeOnGpu, DivRemSizeOnGpu, DivSizeOnGpu, @@ -55,7 +56,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_max_parallelized(&*self.ciphertext.on_cpu(), rhs); - Self::new(inner_result, cpu_key.tag.clone()) + Self::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -65,7 +70,11 @@ where .key .key .scalar_max(&*self.ciphertext.on_gpu(streams), rhs, streams); - Self::new(inner_result, cuda_key.tag.clone()) + Self::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -106,7 +115,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_min_parallelized(&*self.ciphertext.on_cpu(), rhs); - Self::new(inner_result, cpu_key.tag.clone()) + Self::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -116,7 +129,11 @@ where .key .key .scalar_min(&*self.ciphertext.on_gpu(streams), rhs, streams); - Self::new(inner_result, cuda_key.tag.clone()) + Self::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -156,7 +173,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_eq_parallelized(&*self.ciphertext.on_cpu(), rhs); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -166,7 +187,11 @@ where .key .key .scalar_eq(&*self.ciphertext.on_gpu(streams), rhs, streams); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -200,7 +225,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_ne_parallelized(&*self.ciphertext.on_cpu(), rhs); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -210,7 +239,11 @@ where .key .key .scalar_ne(&*self.ciphertext.on_gpu(streams), rhs, streams); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -249,7 +282,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_lt_parallelized(&*self.ciphertext.on_cpu(), rhs); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -259,7 +296,11 @@ where .key .key .scalar_lt(&*self.ciphertext.on_gpu(streams), rhs, streams); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -292,7 +333,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_le_parallelized(&*self.ciphertext.on_cpu(), rhs); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -302,7 +347,11 @@ where .key .key .scalar_le(&*self.ciphertext.on_gpu(streams), rhs, streams); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -335,7 +384,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_gt_parallelized(&*self.ciphertext.on_cpu(), rhs); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -345,7 +398,11 @@ where .key .key .scalar_gt(&*self.ciphertext.on_gpu(streams), rhs, streams); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -378,7 +435,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_ge_parallelized(&*self.ciphertext.on_cpu(), rhs); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -388,7 +449,11 @@ where .key .key .scalar_ge(&*self.ciphertext.on_gpu(streams), rhs, streams); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -564,8 +629,8 @@ macro_rules! generic_integer_impl_scalar_div_rem { .pbs_key() .signed_scalar_div_rem_parallelized(&*self.ciphertext.on_cpu(), rhs); ( - <$concrete_type>::new(q, cpu_key.tag.clone()), - <$concrete_type>::new(r, cpu_key.tag.clone()) + <$concrete_type>::new(q, cpu_key.tag.clone(), ReRandomizationMetadata::default()), + <$concrete_type>::new(r, cpu_key.tag.clone(), ReRandomizationMetadata::default()), ) } #[cfg(feature = "gpu")] @@ -580,8 +645,8 @@ macro_rules! generic_integer_impl_scalar_div_rem { SignedRadixCiphertext::Cuda(inner_r), ); ( - <$concrete_type>::new(q, cuda_key.tag.clone()), - <$concrete_type>::new(r, cuda_key.tag.clone()), + <$concrete_type>::new(q, cuda_key.tag.clone(), ReRandomizationMetadata::default()), + <$concrete_type>::new(r, cuda_key.tag.clone(), ReRandomizationMetadata::default()), ) } #[cfg(feature = "hpu")] diff --git a/tfhe/src/high_level_api/integers/unsigned/base.rs b/tfhe/src/high_level_api/integers/unsigned/base.rs index 6ef47dc77..dc318a1f9 100644 --- a/tfhe/src/high_level_api/integers/unsigned/base.rs +++ b/tfhe/src/high_level_api/integers/unsigned/base.rs @@ -4,12 +4,15 @@ use super::inner::RadixCiphertext; use crate::backward_compatibility::integers::FheUintVersions; use crate::conformance::ParameterSetConformant; use crate::core_crypto::prelude::{CastFrom, UnsignedInteger, UnsignedNumeric}; +use crate::high_level_api::errors::UninitializedReRandKey; use crate::high_level_api::integers::signed::{FheInt, FheIntId}; use crate::high_level_api::integers::IntegerId; -use crate::high_level_api::keys::InternalServerKey; -use crate::high_level_api::traits::{FheWait, Tagged}; +use crate::high_level_api::keys::{CompactPublicKey, InternalServerKey}; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; +use crate::high_level_api::traits::{FheWait, ReRandomize, Tagged}; use crate::high_level_api::{global_state, Device}; use crate::integer::block_decomposition::{DecomposableInto, RecomposableFrom}; +use crate::integer::ciphertext::ReRandomizationSeed; #[cfg(feature = "gpu")] use crate::integer::gpu::ciphertext::CudaIntegerRadixCiphertext; #[cfg(feature = "hpu")] @@ -88,6 +91,7 @@ pub struct FheUint { pub(in crate::high_level_api) ciphertext: RadixCiphertext, pub(in crate::high_level_api) id: Id, pub(crate) tag: Tag, + pub(crate) re_randomization_metadata: ReRandomizationMetadata, } #[derive(Copy, Clone)] @@ -129,6 +133,7 @@ impl ParameterSetConformant for FheUint { ciphertext, id: _, tag: _, + re_randomization_metadata: _, } = self; ciphertext.on_cpu().is_conformant(¶ms.params) @@ -189,12 +194,24 @@ where native: hpu_res .iter() .filter(|x| !x.0.is_boolean()) - .map(|x| Self::new(x.clone(), device.tag.clone())) + .map(|x| { + Self::new( + x.clone(), + device.tag.clone(), + ReRandomizationMetadata::default(), + ) + }) .collect::>(), boolean: hpu_res .iter() .filter(|x| x.0.is_boolean()) - .map(|x| FheBool::new(x.clone(), device.tag.clone())) + .map(|x| { + FheBool::new( + x.clone(), + device.tag.clone(), + ReRandomizationMetadata::default(), + ) + }) .collect::>(), imm: Vec::new(), } @@ -206,7 +223,11 @@ impl FheUint where Id: FheUintId, { - pub(in crate::high_level_api) fn new(ciphertext: T, tag: Tag) -> Self + pub(in crate::high_level_api) fn new( + ciphertext: T, + tag: Tag, + re_randomization_metadata: ReRandomizationMetadata, + ) -> Self where T: Into, { @@ -214,26 +235,41 @@ where ciphertext: ciphertext.into(), id: Id::default(), tag, + re_randomization_metadata, } } - pub fn into_raw_parts(self) -> (crate::integer::RadixCiphertext, Id, Tag) { + pub fn into_raw_parts( + self, + ) -> ( + crate::integer::RadixCiphertext, + Id, + Tag, + ReRandomizationMetadata, + ) { let Self { ciphertext, id, tag, + re_randomization_metadata, } = self; let ciphertext = ciphertext.into_cpu(); - (ciphertext, id, tag) + (ciphertext, id, tag, re_randomization_metadata) } - pub fn from_raw_parts(ciphertext: crate::integer::RadixCiphertext, id: Id, tag: Tag) -> Self { + pub fn from_raw_parts( + ciphertext: crate::integer::RadixCiphertext, + id: Id, + tag: Tag, + re_randomization_metadata: ReRandomizationMetadata, + ) -> Self { Self { ciphertext: RadixCiphertext::Cpu(ciphertext), id, tag, + re_randomization_metadata, } } @@ -301,7 +337,11 @@ where let result = cpu_key .pbs_key() .is_even_parallelized(&*self.ciphertext.on_cpu()); - FheBool::new(result, cpu_key.tag.clone()) + FheBool::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -310,7 +350,11 @@ where .key .key .is_even(&*self.ciphertext.on_gpu(streams), streams); - FheBool::new(result, cuda_key.tag.clone()) + FheBool::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -342,7 +386,11 @@ where let result = cpu_key .pbs_key() .is_odd_parallelized(&*self.ciphertext.on_cpu()); - FheBool::new(result, cpu_key.tag.clone()) + FheBool::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -351,7 +399,11 @@ where .key .key .is_odd(&*self.ciphertext.on_gpu(streams), streams); - FheBool::new(result, cuda_key.tag.clone()) + FheBool::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -480,7 +532,11 @@ where result, super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - super::FheUint32::new(result, cpu_key.tag.clone()) + super::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -494,7 +550,11 @@ where super::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus), streams, ); - super::FheUint32::new(result, cuda_key.tag.clone()) + super::FheUint32::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -511,7 +571,11 @@ where HpuRadixCiphertext::exec(proto, opcode, std::slice::from_ref(&hpu_self), &[]) .pop() .expect("IOP_LEAD0 must return 1 value"); - super::FheUint32::new(hpu_result, device.tag.clone()) + super::FheUint32::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -543,7 +607,11 @@ where result, super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - super::FheUint32::new(result, cpu_key.tag.clone()) + super::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -557,7 +625,11 @@ where super::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus), streams, ); - super::FheUint32::new(result, cuda_key.tag.clone()) + super::FheUint32::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -574,7 +646,11 @@ where HpuRadixCiphertext::exec(proto, opcode, std::slice::from_ref(&hpu_self), &[]) .pop() .expect("IOP_LEAD1 must return 1 value"); - super::FheUint32::new(hpu_result, device.tag.clone()) + super::FheUint32::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -606,7 +682,11 @@ where result, super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - super::FheUint32::new(result, cpu_key.tag.clone()) + super::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -620,7 +700,11 @@ where super::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus), streams, ); - super::FheUint32::new(result, cuda_key.tag.clone()) + super::FheUint32::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -637,7 +721,11 @@ where HpuRadixCiphertext::exec(proto, opcode, std::slice::from_ref(&hpu_self), &[]) .pop() .expect("IOP_TRAIL0 must return 1 value"); - super::FheUint32::new(hpu_result, device.tag.clone()) + super::FheUint32::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -669,7 +757,11 @@ where result, super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - super::FheUint32::new(result, cpu_key.tag.clone()) + super::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -683,7 +775,11 @@ where super::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus), streams, ); - super::FheUint32::new(result, cuda_key.tag.clone()) + super::FheUint32::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -700,7 +796,11 @@ where HpuRadixCiphertext::exec(proto, opcode, std::slice::from_ref(&hpu_self), &[]) .pop() .expect("IOP_TRAIL1 must return 1 value"); - super::FheUint32::new(hpu_result, device.tag.clone()) + super::FheUint32::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -733,7 +833,11 @@ where result, super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - super::FheUint32::new(result, cpu_key.tag.clone()) + super::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -754,7 +858,11 @@ where HpuRadixCiphertext::exec(proto, opcode, std::slice::from_ref(&hpu_self), &[]) .pop() .expect("IOP_COUNT0 must return 1 value"); - super::FheUint32::new(hpu_result, device.tag.clone()) + super::FheUint32::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -787,7 +895,11 @@ where result, super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - super::FheUint32::new(result, cpu_key.tag.clone()) + super::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -808,7 +920,11 @@ where HpuRadixCiphertext::exec(proto, opcode, std::slice::from_ref(&hpu_self), &[]) .pop() .expect("IOP_COUNT1 must return 1 value"); - super::FheUint32::new(hpu_result, device.tag.clone()) + super::FheUint32::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -842,7 +958,11 @@ where result, super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); - super::FheUint32::new(result, cpu_key.tag.clone()) + super::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -856,7 +976,11 @@ where super::FheUint32Id::num_blocks(cuda_key.key.key.message_modulus), streams, ); - super::FheUint32::new(result, cuda_key.tag.clone()) + super::FheUint32::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -873,7 +997,11 @@ where HpuRadixCiphertext::exec(proto, opcode, std::slice::from_ref(&hpu_self), &[]) .pop() .expect("IOP_ILOG2 must return 1 value"); - super::FheUint32::new(hpu_result, device.tag.clone()) + super::FheUint32::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -912,8 +1040,16 @@ where super::FheUint32Id::num_blocks(cpu_key.pbs_key().message_modulus()), ); ( - super::FheUint32::new(result, cpu_key.tag.clone()), - FheBool::new(is_ok, cpu_key.tag.clone()), + super::FheUint32::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + is_ok, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -929,8 +1065,16 @@ where streams, ); ( - super::FheUint32::new(result, cuda_key.tag.clone()), - FheBool::new(is_ok, cuda_key.tag.clone()), + super::FheUint32::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + is_ok, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "hpu")] @@ -996,8 +1140,16 @@ where .pbs_key() .cast_to_unsigned(result, target_num_blocks); Ok(( - FheUint::new(result, cpu_key.tag.clone()), - FheBool::new(matched, cpu_key.tag.clone()), + FheUint::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + matched, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), )) } else { Err(crate::Error::new("Output type does not have enough bits to represent all possible output values".to_string())) @@ -1014,8 +1166,16 @@ where let target_num_blocks = OutId::num_blocks(cuda_key.key.key.message_modulus); if target_num_blocks >= result.ciphertext.d_blocks.lwe_ciphertext_count().0 { Ok(( - FheUint::new(result, cuda_key.tag.clone()), - FheBool::new(matched, cuda_key.tag.clone()), + FheUint::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + matched, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), )) } else { Err(crate::Error::new("Output type does not have enough bits to represent all possible output values".to_string())) @@ -1081,7 +1241,11 @@ where let result = cpu_key .pbs_key() .cast_to_unsigned(result, target_num_blocks); - Ok(FheUint::new(result, cpu_key.tag.clone())) + Ok(FheUint::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + )) } else { Err(crate::Error::new("Output type does not have enough bits to represent all possible output values".to_string())) } @@ -1097,7 +1261,11 @@ where ); let target_num_blocks = OutId::num_blocks(cuda_key.key.key.message_modulus); if target_num_blocks >= result.ciphertext.d_blocks.lwe_ciphertext_count().0 { - Ok(FheUint::new(result, cuda_key.tag.clone())) + Ok(FheUint::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + )) } else { Err(crate::Error::new("Output type does not have enough bits to represent all possible output values".to_string())) } @@ -1136,7 +1304,11 @@ where let ct = self.ciphertext.on_cpu(); - Self::new(sk.reverse_bits_parallelized(&*ct), cpu_key.tag.clone()) + Self::new( + sk.reverse_bits_parallelized(&*ct), + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -1186,7 +1358,11 @@ where Id::num_blocks(sk.message_modulus()), ); - Self::new(result, cpu_key.tag.clone()) + Self::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -1214,6 +1390,14 @@ where { Self::if_then_else(condition, true_value, false_value) } + + pub fn re_randomization_metadata(&self) -> &ReRandomizationMetadata { + &self.re_randomization_metadata + } + + pub fn re_randomization_metadata_mut(&mut self) -> &mut ReRandomizationMetadata { + &mut self.re_randomization_metadata + } } impl TryFrom for FheUint @@ -1267,7 +1451,7 @@ where } } - let mut ciphertext = Self::new(other, Tag::default()); + let mut ciphertext = Self::new(other, Tag::default(), ReRandomizationMetadata::default()); ciphertext.move_to_device_of_server_key_if_set(); Ok(ciphertext) } @@ -1314,7 +1498,11 @@ where input.ciphertext.into_cpu(), IntoId::num_blocks(cpu_key.message_modulus()), ); - Self::new(casted, cpu_key.tag.clone()) + Self::new( + casted, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -1324,7 +1512,11 @@ where IntoId::num_blocks(cuda_key.message_modulus()), streams, ); - Self::new(casted, cuda_key.tag.clone()) + Self::new( + casted, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -1363,7 +1555,11 @@ where input.ciphertext.on_cpu().to_owned(), IntoId::num_blocks(cpu_key.message_modulus()), ); - Self::new(casted, cpu_key.tag.clone()) + Self::new( + casted, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -1373,7 +1569,11 @@ where IntoId::num_blocks(cuda_key.message_modulus()), streams, ); - Self::new(casted, cuda_key.tag.clone()) + Self::new( + casted, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -1412,7 +1612,11 @@ where .on_cpu() .into_owned() .into_radix(Id::num_blocks(cpu_key.message_modulus()), cpu_key.pbs_key()); - Self::new(ciphertext, cpu_key.tag.clone()) + Self::new( + ciphertext, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -1422,7 +1626,11 @@ where Id::num_blocks(cuda_key.message_modulus()), streams, ); - Self::new(inner, cuda_key.tag.clone()) + Self::new( + inner, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -1432,6 +1640,50 @@ where } } +impl ReRandomize for FheUint +where + Id: FheUintId, +{ + fn add_to_re_randomization_context( + &self, + context: &mut crate::high_level_api::re_randomization::ReRandomizationContext, + ) { + let on_cpu = self.ciphertext.on_cpu(); + context.inner.add_ciphertext(&*on_cpu); + context + .inner + .add_bytes(self.re_randomization_metadata.data()); + } + + fn re_randomize( + &mut self, + compact_public_key: &CompactPublicKey, + seed: ReRandomizationSeed, + ) -> crate::Result<()> { + global_state::with_internal_keys(|key| match key { + InternalServerKey::Cpu(key) => { + let Some(re_randomization_key) = key.re_randomization_cpk_casting_key() else { + return Err(UninitializedReRandKey.into()); + }; + + self.ciphertext.as_cpu_mut().re_randomize( + &compact_public_key.key.key, + &re_randomization_key, + seed, + )?; + + Ok(()) + } + #[cfg(feature = "gpu")] + InternalServerKey::Cuda(_cuda_key) => panic!("GPU does not support CPKReRandomize."), + #[cfg(feature = "hpu")] + InternalServerKey::Hpu(_device) => { + panic!("HPU does not support CPKReRandomize.") + } + }) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/tfhe/src/high_level_api/integers/unsigned/compressed.rs b/tfhe/src/high_level_api/integers/unsigned/compressed.rs index 4f29c2cc1..ca298802e 100644 --- a/tfhe/src/high_level_api/integers/unsigned/compressed.rs +++ b/tfhe/src/high_level_api/integers/unsigned/compressed.rs @@ -9,6 +9,7 @@ use crate::high_level_api::global_state::with_cpu_internal_keys; use crate::high_level_api::integers::unsigned::base::{ FheUint, FheUintConformanceParams, FheUintId, }; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::high_level_api::traits::{FheTryEncrypt, Tagged}; use crate::high_level_api::ClientKey; use crate::integer::block_decomposition::DecomposableInto; @@ -111,7 +112,8 @@ where } }; - let mut ciphertext = FheUint::new(inner, self.tag.clone()); + let mut ciphertext = + FheUint::new(inner, self.tag.clone(), ReRandomizationMetadata::default()); ciphertext.move_to_device_of_server_key_if_set(); ciphertext diff --git a/tfhe/src/high_level_api/integers/unsigned/encrypt.rs b/tfhe/src/high_level_api/integers/unsigned/encrypt.rs index 45f4dcb97..64d02ce1e 100644 --- a/tfhe/src/high_level_api/integers/unsigned/encrypt.rs +++ b/tfhe/src/high_level_api/integers/unsigned/encrypt.rs @@ -2,6 +2,7 @@ use crate::core_crypto::prelude::UnsignedNumeric; use crate::high_level_api::global_state; use crate::high_level_api::integers::FheUintId; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::integer::block_decomposition::{DecomposableInto, RecomposableFrom}; #[cfg(feature = "gpu")] use crate::integer::gpu::ciphertext::CudaUnsignedRadixCiphertext; @@ -52,7 +53,11 @@ where .key .key .encrypt_radix(value, Id::num_blocks(key.message_modulus())); - let mut ciphertext = Self::new(cpu_ciphertext, key.tag.clone()); + let mut ciphertext = Self::new( + cpu_ciphertext, + key.tag.clone(), + ReRandomizationMetadata::default(), + ); ciphertext.move_to_device_of_server_key_if_set(); @@ -71,7 +76,11 @@ where let cpu_ciphertext = key .key .encrypt_radix(value, Id::num_blocks(key.message_modulus())); - let mut ciphertext = Self::new(cpu_ciphertext, key.tag.clone()); + let mut ciphertext = Self::new( + cpu_ciphertext, + key.tag.clone(), + ReRandomizationMetadata::default(), + ); ciphertext.move_to_device_of_server_key_if_set(); @@ -90,7 +99,11 @@ where let cpu_ciphertext = key .key .encrypt_radix(value, Id::num_blocks(key.message_modulus())); - let mut ciphertext = Self::new(cpu_ciphertext, key.tag.clone()); + let mut ciphertext = Self::new( + cpu_ciphertext, + key.tag.clone(), + ReRandomizationMetadata::default(), + ); ciphertext.move_to_device_of_server_key_if_set(); Ok(ciphertext) @@ -110,7 +123,11 @@ where let ciphertext: crate::integer::RadixCiphertext = key .pbs_key() .create_trivial_radix(value, Id::num_blocks(key.message_modulus())); - Ok(Self::new(ciphertext, key.tag.clone())) + Ok(Self::new( + ciphertext, + key.tag.clone(), + ReRandomizationMetadata::default(), + )) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -120,7 +137,11 @@ where Id::num_blocks(cuda_key.key.key.message_modulus), streams, ); - Ok(Self::new(inner, cuda_key.tag.clone())) + Ok(Self::new( + inner, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + )) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { diff --git a/tfhe/src/high_level_api/integers/unsigned/ops.rs b/tfhe/src/high_level_api/integers/unsigned/ops.rs index 097c0159e..c4b6f8ba0 100644 --- a/tfhe/src/high_level_api/integers/unsigned/ops.rs +++ b/tfhe/src/high_level_api/integers/unsigned/ops.rs @@ -8,6 +8,7 @@ use crate::high_level_api::details::MaybeCloned; use crate::high_level_api::global_state; use crate::high_level_api::integers::FheUintId; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; #[cfg(feature = "gpu")] use crate::high_level_api::traits::{ AddSizeOnGpu, BitAndSizeOnGpu, BitNotSizeOnGpu, BitOrSizeOnGpu, BitXorSizeOnGpu, @@ -77,9 +78,10 @@ where Id::num_blocks(cpu_key.message_modulus()), )), cpu_key.tag.clone(), + ReRandomizationMetadata::default(), ) }, - |ct| Self::new(ct, cpu_key.tag.clone()), + |ct| Self::new(ct, cpu_key.tag.clone(), ReRandomizationMetadata::default()), ) } #[cfg(feature = "gpu")] @@ -100,7 +102,11 @@ where streams, ) }); - Self::new(inner, cuda_key.tag.clone()) + Self::new( + inner, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -109,7 +115,11 @@ where for o in iter { result += o.ciphertext.into_hpu(device); } - Self::new(result, device.tag.clone()) + Self::new( + result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -165,9 +175,10 @@ where .create_trivial_zero_radix(Id::num_blocks(msg_mod)), ), cpu_key.tag.clone(), + ReRandomizationMetadata::default(), ) }, - |ct| Self::new(ct, cpu_key.tag.clone()), + |ct| Self::new(ct, cpu_key.tag.clone(), ReRandomizationMetadata::default()), ) } #[cfg(feature = "gpu")] @@ -202,7 +213,11 @@ where streams, ) }); - Self::new(inner, cuda_key.tag.clone()) + Self::new( + inner, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } } #[cfg(feature = "hpu")] @@ -211,14 +226,22 @@ where let first = iter.next().unwrap().ciphertext.on_hpu(device); let Some(second) = iter.next() else { - return Self::new(first.clone(), device.tag.clone()); + return Self::new( + first.clone(), + device.tag.clone(), + ReRandomizationMetadata::default(), + ); }; let mut result = &*first + &*second.ciphertext.on_hpu(device); for o in iter { result += &*o.ciphertext.on_hpu(device); } - Self::new(result, device.tag.clone()) + Self::new( + result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -255,7 +278,11 @@ where let inner_result = cpu_key .pbs_key() .max_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - Self::new(inner_result, cpu_key.tag.clone()) + Self::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -265,7 +292,11 @@ where &*rhs.ciphertext.on_gpu(streams), streams, ); - Self::new(inner_result, cuda_key.tag.clone()) + Self::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -318,7 +349,11 @@ where let inner_result = cpu_key .pbs_key() .min_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - Self::new(inner_result, cpu_key.tag.clone()) + Self::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -328,7 +363,11 @@ where &*rhs.ciphertext.on_gpu(streams), streams, ); - Self::new(inner_result, cuda_key.tag.clone()) + Self::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -392,7 +431,11 @@ where let inner_result = cpu_key .pbs_key() .eq_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -402,7 +445,11 @@ where &rhs.ciphertext.on_gpu(streams), streams, ); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -424,7 +471,11 @@ where ) .pop() .unwrap(); - FheBool::new(hpu_result, device.tag.clone()) + FheBool::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -454,7 +505,11 @@ where let inner_result = cpu_key .pbs_key() .ne_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -464,7 +519,11 @@ where &rhs.ciphertext.on_gpu(streams), streams, ); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -486,7 +545,11 @@ where ) .pop() .unwrap(); - FheBool::new(hpu_result, device.tag.clone()) + FheBool::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -542,7 +605,11 @@ where let inner_result = cpu_key .pbs_key() .lt_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -552,7 +619,11 @@ where &rhs.ciphertext.on_gpu(streams), streams, ); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -574,7 +645,11 @@ where ) .pop() .unwrap(); - FheBool::new(hpu_result, device.tag.clone()) + FheBool::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -604,7 +679,11 @@ where let inner_result = cpu_key .pbs_key() .le_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -614,7 +693,11 @@ where &rhs.ciphertext.on_gpu(streams), streams, ); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -636,7 +719,11 @@ where ) .pop() .unwrap(); - FheBool::new(hpu_result, device.tag.clone()) + FheBool::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -666,7 +753,11 @@ where let inner_result = cpu_key .pbs_key() .gt_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -676,7 +767,11 @@ where &rhs.ciphertext.on_gpu(streams), streams, ); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -698,7 +793,11 @@ where ) .pop() .unwrap(); - FheBool::new(hpu_result, device.tag.clone()) + FheBool::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -728,7 +827,11 @@ where let inner_result = cpu_key .pbs_key() .ge_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -738,7 +841,11 @@ where &rhs.ciphertext.on_gpu(streams), streams, ); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -760,7 +867,11 @@ where ) .pop() .unwrap(); - FheBool::new(hpu_result, device.tag.clone()) + FheBool::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -832,8 +943,8 @@ where .pbs_key() .div_rem_parallelized(&*self.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); ( - FheUint::::new(q, cpu_key.tag.clone()), - FheUint::::new(r, cpu_key.tag.clone()), + FheUint::::new(q, cpu_key.tag.clone(), ReRandomizationMetadata::default()), + FheUint::::new(r, cpu_key.tag.clone(), ReRandomizationMetadata::default()), ) } #[cfg(feature = "gpu")] @@ -845,8 +956,16 @@ where streams, ); ( - FheUint::::new(inner_result.0, cuda_key.tag.clone()), - FheUint::::new(inner_result.1, cuda_key.tag.clone()), + FheUint::::new( + inner_result.0, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheUint::::new( + inner_result.1, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "hpu")] @@ -871,8 +990,16 @@ where let remainder = hpu_result.pop().expect("IOP_DIV must return 2 value"); let quotient = hpu_result.pop().expect("IOP_DIV must return 2 value"); ( - FheUint::new(quotient, device.tag.clone()), - FheUint::new(remainder, device.tag.clone()), + FheUint::new( + quotient, + device.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheUint::new( + remainder, + device.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } }) @@ -946,20 +1073,20 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .add_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheUint::new(inner_result, cpu_key.tag.clone()) + FheUint::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .add(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { let hpu_lhs = lhs.ciphertext.on_hpu(device); let hpu_rhs = rhs.ciphertext.on_hpu(device); - FheUint::new(&*hpu_lhs + &*hpu_rhs, device.tag.clone()) + FheUint::new(&*hpu_lhs + &*hpu_rhs, device.tag.clone(), ReRandomizationMetadata::default()) } }) } @@ -994,20 +1121,20 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .sub_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheUint::new(inner_result, cpu_key.tag.clone()) + FheUint::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .sub(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { let hpu_lhs = lhs.ciphertext.on_hpu(device); let hpu_rhs = rhs.ciphertext.on_hpu(device); - FheUint::new(&*hpu_lhs - &*hpu_rhs, device.tag.clone()) + FheUint::new(&*hpu_lhs - &*hpu_rhs, device.tag.clone(), ReRandomizationMetadata::default()) } }) } @@ -1042,20 +1169,20 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .mul_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheUint::new(inner_result, cpu_key.tag.clone()) + FheUint::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .mul(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { let hpu_lhs = lhs.ciphertext.on_hpu(device); let hpu_rhs = rhs.ciphertext.on_hpu(device); - FheUint::new(&*hpu_lhs * &*hpu_rhs, device.tag.clone()) + FheUint::new(&*hpu_lhs * &*hpu_rhs, device.tag.clone(), ReRandomizationMetadata::default()) } }) } @@ -1088,20 +1215,20 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .bitand_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheUint::new(inner_result, cpu_key.tag.clone()) + FheUint::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .bitand(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { let hpu_lhs = lhs.ciphertext.on_hpu(device); let hpu_rhs = rhs.ciphertext.on_hpu(device); - FheUint::new(&*hpu_lhs & &*hpu_rhs, device.tag.clone()) + FheUint::new(&*hpu_lhs & &*hpu_rhs, device.tag.clone(), ReRandomizationMetadata::default()) } }) } @@ -1134,20 +1261,20 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .bitor_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheUint::new(inner_result, cpu_key.tag.clone()) + FheUint::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .bitor(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { let hpu_lhs = lhs.ciphertext.on_hpu(device); let hpu_rhs = rhs.ciphertext.on_hpu(device); - FheUint::new(&*hpu_lhs | &*hpu_rhs, device.tag.clone()) + FheUint::new(&*hpu_lhs | &*hpu_rhs, device.tag.clone(), ReRandomizationMetadata::default()) } }) } @@ -1180,20 +1307,20 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .bitxor_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheUint::new(inner_result, cpu_key.tag.clone()) + FheUint::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .bitxor(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { let hpu_lhs = lhs.ciphertext.on_hpu(device); let hpu_rhs = rhs.ciphertext.on_hpu(device); - FheUint::new(&*hpu_lhs ^ &*hpu_rhs, device.tag.clone()) + FheUint::new(&*hpu_lhs ^ &*hpu_rhs, device.tag.clone(), ReRandomizationMetadata::default()) } }) } @@ -1234,7 +1361,7 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .div_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheUint::new(inner_result, cpu_key.tag.clone()) + FheUint::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -1244,7 +1371,7 @@ generic_integer_impl_operation!( .key .key .div(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -1267,7 +1394,7 @@ generic_integer_impl_operation!( ); let _remainder = hpu_result.pop().expect("IOP_DIV must return 2 value"); let quotient = hpu_result.pop().expect("IOP_DIV must return 2 value"); - FheUint::new(quotient, device.tag.clone()) + FheUint::new(quotient, device.tag.clone(), ReRandomizationMetadata::default()) } }) } @@ -1309,7 +1436,7 @@ generic_integer_impl_operation!( let inner_result = cpu_key .pbs_key() .rem_parallelized(&*lhs.ciphertext.on_cpu(), &*rhs.ciphertext.on_cpu()); - FheUint::new(inner_result, cpu_key.tag.clone()) + FheUint::new(inner_result, cpu_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => @@ -1320,7 +1447,7 @@ generic_integer_impl_operation!( .key .key .rem(&*lhs.ciphertext.on_gpu(streams), &*rhs.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) }, #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -1342,7 +1469,7 @@ generic_integer_impl_operation!( &[], ); let remainder = hpu_result.pop().expect("IOP_MOD must return 1 value"); - FheUint::new(remainder, device.tag.clone()) + FheUint::new(remainder, device.tag.clone(), ReRandomizationMetadata::default()) } }) } @@ -1447,14 +1574,14 @@ generic_integer_impl_shift_rotate!( let ciphertext = cpu_key .pbs_key() .left_shift_parallelized(&*lhs.ciphertext.on_cpu(), &rhs.ciphertext.on_cpu()); - FheUint::new(ciphertext, cpu_key.tag.clone()) + FheUint::new(ciphertext, cpu_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .left_shift(&*lhs.ciphertext.on_gpu(streams), &rhs.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -1475,7 +1602,7 @@ generic_integer_impl_shift_rotate!( &[hpu_lhs.clone(), hpu_rhs.clone()], &[], ).pop().expect("IOP_SHIFT_L must return 1 value"); - FheUint::new(hpu_result, device.tag.clone()) + FheUint::new(hpu_result, device.tag.clone(), ReRandomizationMetadata::default()) } } }) @@ -1511,14 +1638,14 @@ generic_integer_impl_shift_rotate!( let ciphertext = cpu_key .pbs_key() .right_shift_parallelized(&*lhs.ciphertext.on_cpu(), &rhs.ciphertext.on_cpu()); - FheUint::new(ciphertext, cpu_key.tag.clone()) + FheUint::new(ciphertext, cpu_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .right_shift(&*lhs.ciphertext.on_gpu(streams), &rhs.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -1539,7 +1666,7 @@ generic_integer_impl_shift_rotate!( &[hpu_lhs.clone(), hpu_rhs.clone()], &[], ).pop().expect("IOP_SHIFT_R must return 1 value"); - FheUint::new(hpu_result, device.tag.clone()) + FheUint::new(hpu_result, device.tag.clone(), ReRandomizationMetadata::default()) } } }) @@ -1575,14 +1702,14 @@ generic_integer_impl_shift_rotate!( let ciphertext = cpu_key .pbs_key() .rotate_left_parallelized(&*lhs.ciphertext.on_cpu(), &rhs.ciphertext.on_cpu()); - FheUint::new(ciphertext, cpu_key.tag.clone()) + FheUint::new(ciphertext, cpu_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .rotate_left(&*lhs.ciphertext.on_gpu(streams), &rhs.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -1603,7 +1730,7 @@ generic_integer_impl_shift_rotate!( &[hpu_lhs.clone(), hpu_rhs.clone()], &[], ).pop().expect("IOP_ROT_L must return 1 value"); - FheUint::new(hpu_result, device.tag.clone()) + FheUint::new(hpu_result, device.tag.clone(), ReRandomizationMetadata::default()) } } }) @@ -1639,14 +1766,14 @@ generic_integer_impl_shift_rotate!( let ciphertext = cpu_key .pbs_key() .rotate_right_parallelized(&*lhs.ciphertext.on_cpu(), &rhs.ciphertext.on_cpu()); - FheUint::new(ciphertext, cpu_key.tag.clone()) + FheUint::new(ciphertext, cpu_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { let streams = &cuda_key.streams; let inner_result = cuda_key.key.key .rotate_right(&*lhs.ciphertext.on_gpu(streams), &rhs.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new(inner_result, cuda_key.tag.clone(), ReRandomizationMetadata::default()) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -1667,7 +1794,7 @@ generic_integer_impl_shift_rotate!( &[hpu_lhs.clone(), hpu_rhs.clone()], &[], ).pop().expect("IOP_ROT_R must return 1 value"); - FheUint::new(hpu_result, device.tag.clone()) + FheUint::new(hpu_result, device.tag.clone(), ReRandomizationMetadata::default()) } } }) @@ -2492,7 +2619,11 @@ where let ciphertext = cpu_key .pbs_key() .neg_parallelized(&*self.ciphertext.on_cpu()); - FheUint::new(ciphertext, cpu_key.tag.clone()) + FheUint::new( + ciphertext, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -2501,7 +2632,11 @@ where .key .key .neg(&*self.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(device) => { @@ -2522,7 +2657,11 @@ where ) .pop() .expect("SSUB must return a single value"); - FheUint::new(hpu_result, device.tag.clone()) + FheUint::new( + hpu_result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ) } }) } @@ -2583,7 +2722,11 @@ where global_state::with_internal_keys(|key| match key { InternalServerKey::Cpu(cpu_key) => { let ciphertext = cpu_key.pbs_key().bitnot(&*self.ciphertext.on_cpu()); - FheUint::new(ciphertext, cpu_key.tag.clone()) + FheUint::new( + ciphertext, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -2592,7 +2735,11 @@ where .key .key .bitnot(&*self.ciphertext.on_gpu(streams), streams); - FheUint::new(inner_result, cuda_key.tag.clone()) + FheUint::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { diff --git a/tfhe/src/high_level_api/integers/unsigned/overflowing_ops.rs b/tfhe/src/high_level_api/integers/unsigned/overflowing_ops.rs index 941d07abd..cb3949782 100644 --- a/tfhe/src/high_level_api/integers/unsigned/overflowing_ops.rs +++ b/tfhe/src/high_level_api/integers/unsigned/overflowing_ops.rs @@ -2,6 +2,7 @@ use crate::core_crypto::prelude::UnsignedNumeric; use crate::high_level_api::global_state; use crate::high_level_api::integers::FheUintId; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::integer::block_decomposition::DecomposableInto; use crate::prelude::{CastInto, OverflowingAdd, OverflowingMul, OverflowingNeg, OverflowingSub}; use crate::{FheBool, FheUint}; @@ -51,8 +52,16 @@ where &other.ciphertext.on_cpu(), ); ( - FheUint::new(result, cpu_key.tag.clone()), - FheBool::new(overflow, cpu_key.tag.clone()), + FheUint::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -64,8 +73,16 @@ where streams, ); ( - FheUint::::new(inner_result.0, cuda_key.tag.clone()), - FheBool::new(inner_result.1, cuda_key.tag.clone()), + FheUint::::new( + inner_result.0, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + inner_result.1, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "hpu")] @@ -90,8 +107,16 @@ where let overflow = hpu_result.pop().expect("IOP_OVF_ADD must return 2 value"); let result = hpu_result.pop().expect("IOP_OVF_ADD must return 2 value"); ( - FheUint::new(result, device.tag.clone()), - FheBool::new(overflow, device.tag.clone()), + FheUint::new( + result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + device.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } }) @@ -174,8 +199,16 @@ where .pbs_key() .unsigned_overflowing_scalar_add_parallelized(&self.ciphertext.on_cpu(), other); ( - FheUint::new(result, cpu_key.tag.clone()), - FheBool::new(overflow, cpu_key.tag.clone()), + FheUint::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -187,8 +220,16 @@ where streams, ); ( - FheUint::::new(inner_result.0, cuda_key.tag.clone()), - FheBool::new(inner_result.1, cuda_key.tag.clone()), + FheUint::::new( + inner_result.0, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + inner_result.1, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "hpu")] @@ -215,8 +256,16 @@ where let overflow = hpu_result.pop().expect("IOP_OVF_ADDS must return 2 value"); let result = hpu_result.pop().expect("IOP_OVF_ADDS must return 2 value"); ( - FheUint::new(result, device.tag.clone()), - FheBool::new(overflow, device.tag.clone()), + FheUint::new( + result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + device.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } }) @@ -339,8 +388,16 @@ where &other.ciphertext.on_cpu(), ); ( - FheUint::new(result, cpu_key.tag.clone()), - FheBool::new(overflow, cpu_key.tag.clone()), + FheUint::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -352,8 +409,16 @@ where streams, ); ( - FheUint::::new(inner_result.0, cuda_key.tag.clone()), - FheBool::new(inner_result.1, cuda_key.tag.clone()), + FheUint::::new( + inner_result.0, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + inner_result.1, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "hpu")] @@ -378,8 +443,16 @@ where let overflow = hpu_result.pop().expect("IOP_OVF_SUB must return 2 value"); let result = hpu_result.pop().expect("IOP_OVF_SUB must return 2 value"); ( - FheUint::new(result, device.tag.clone()), - FheBool::new(overflow, device.tag.clone()), + FheUint::new( + result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + device.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } }) @@ -462,8 +535,16 @@ where .pbs_key() .unsigned_overflowing_scalar_sub_parallelized(&self.ciphertext.on_cpu(), other); ( - FheUint::new(result, cpu_key.tag.clone()), - FheBool::new(overflow, cpu_key.tag.clone()), + FheUint::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -494,8 +575,16 @@ where let overflow = hpu_result.pop().expect("IOP_OVF_SUBS must return 2 value"); let result = hpu_result.pop().expect("IOP_OVF_SUBS must return 2 value"); ( - FheUint::new(result, device.tag.clone()), - FheBool::new(overflow, device.tag.clone()), + FheUint::new( + result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + device.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } }) @@ -577,8 +666,16 @@ where &other.ciphertext.on_cpu(), ); ( - FheUint::new(result, cpu_key.tag.clone()), - FheBool::new(overflow, cpu_key.tag.clone()), + FheUint::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -607,8 +704,16 @@ where let overflow = hpu_result.pop().expect("IOP_OVF_MUL must return 2 value"); let result = hpu_result.pop().expect("IOP_OVF_MUL must return 2 value"); ( - FheUint::new(result, device.tag.clone()), - FheBool::new(overflow, device.tag.clone()), + FheUint::new( + result, + device.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + device.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } }) @@ -663,8 +768,16 @@ where .pbs_key() .overflowing_neg_parallelized(&*self.ciphertext.on_cpu()); ( - FheUint::new(result, cpu_key.tag.clone()), - FheBool::new(overflow, cpu_key.tag.clone()), + FheUint::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -674,8 +787,16 @@ where &cuda_key.streams, ); ( - FheUint::new(result, cuda_key.tag.clone()), - FheBool::new(overflow, cuda_key.tag.clone()), + FheUint::new( + result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + overflow, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "hpu")] diff --git a/tfhe/src/high_level_api/integers/unsigned/scalar_ops.rs b/tfhe/src/high_level_api/integers/unsigned/scalar_ops.rs index f5dfa127b..d1028df5d 100644 --- a/tfhe/src/high_level_api/integers/unsigned/scalar_ops.rs +++ b/tfhe/src/high_level_api/integers/unsigned/scalar_ops.rs @@ -9,6 +9,7 @@ use crate::high_level_api::errors::UnwrapResultExt; use crate::high_level_api::global_state; use crate::high_level_api::integers::FheUintId; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; #[cfg(feature = "gpu")] use crate::high_level_api::traits::{ AddSizeOnGpu, BitAndSizeOnGpu, BitOrSizeOnGpu, BitXorSizeOnGpu, DivRemSizeOnGpu, DivSizeOnGpu, @@ -65,7 +66,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_eq_parallelized(&*self.ciphertext.on_cpu(), rhs); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -75,7 +80,11 @@ where .key .key .scalar_eq(&*self.ciphertext.on_gpu(streams), rhs, streams); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -109,7 +118,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_ne_parallelized(&*self.ciphertext.on_cpu(), rhs); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -119,7 +132,11 @@ where .key .key .scalar_ne(&*self.ciphertext.on_gpu(streams), rhs, streams); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -159,7 +176,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_lt_parallelized(&*self.ciphertext.on_cpu(), rhs); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -169,7 +190,11 @@ where .key .key .scalar_lt(&*self.ciphertext.on_gpu(streams), rhs, streams); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -203,7 +228,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_le_parallelized(&*self.ciphertext.on_cpu(), rhs); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -213,7 +242,11 @@ where .key .key .scalar_le(&*self.ciphertext.on_gpu(streams), rhs, streams); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -247,7 +280,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_gt_parallelized(&*self.ciphertext.on_cpu(), rhs); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -257,7 +294,11 @@ where .key .key .scalar_gt(&*self.ciphertext.on_gpu(streams), rhs, streams); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -291,7 +332,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_ge_parallelized(&*self.ciphertext.on_cpu(), rhs); - FheBool::new(inner_result, cpu_key.tag.clone()) + FheBool::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -301,7 +346,11 @@ where .key .key .scalar_ge(&*self.ciphertext.on_gpu(streams), rhs, streams); - FheBool::new(inner_result, cuda_key.tag.clone()) + FheBool::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -478,7 +527,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_max_parallelized(&*self.ciphertext.on_cpu(), rhs); - Self::new(inner_result, cpu_key.tag.clone()) + Self::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -488,7 +541,11 @@ where .key .key .scalar_max(&*self.ciphertext.on_gpu(streams), rhs, streams); - Self::new(inner_result, cuda_key.tag.clone()) + Self::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -530,7 +587,11 @@ where let inner_result = cpu_key .pbs_key() .scalar_min_parallelized(&*self.ciphertext.on_cpu(), rhs); - Self::new(inner_result, cpu_key.tag.clone()) + Self::new( + inner_result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(cuda_key) => { @@ -540,7 +601,11 @@ where .key .key .scalar_min(&*self.ciphertext.on_gpu(streams), rhs, streams); - Self::new(inner_result, cuda_key.tag.clone()) + Self::new( + inner_result, + cuda_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "hpu")] InternalServerKey::Hpu(_device) => { @@ -590,7 +655,11 @@ where let result = cpu_key .pbs_key() .scalar_bitslice_parallelized(&self.ciphertext.on_cpu(), range)?; - Ok(FheUint::new(result, cpu_key.tag.clone())) + Ok(FheUint::new( + result, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + )) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -674,8 +743,8 @@ macro_rules! generic_integer_impl_scalar_div_rem { InternalServerKey::Cpu(cpu_key) => { let (q, r) = cpu_key.pbs_key().scalar_div_rem_parallelized(&*self.ciphertext.on_cpu(), rhs); ( - <$concrete_type>::new(q, cpu_key.tag.clone()), - <$concrete_type>::new(r, cpu_key.tag.clone()) + <$concrete_type>::new(q, cpu_key.tag.clone(), ReRandomizationMetadata::default()), + <$concrete_type>::new(r, cpu_key.tag.clone(), ReRandomizationMetadata::default()), ) } #[cfg(feature = "gpu")] @@ -688,8 +757,8 @@ macro_rules! generic_integer_impl_scalar_div_rem { }; let (q, r) = (RadixCiphertext::Cuda(inner_q), RadixCiphertext::Cuda(inner_r)); ( - <$concrete_type>::new(q, cuda_key.tag.clone()), - <$concrete_type>::new(r, cuda_key.tag.clone()) + <$concrete_type>::new(q, cuda_key.tag.clone(), ReRandomizationMetadata::default()), + <$concrete_type>::new(r, cuda_key.tag.clone(), ReRandomizationMetadata::default()), ) } #[cfg(feature = "hpu")] @@ -712,8 +781,8 @@ macro_rules! generic_integer_impl_scalar_div_rem { let remainder = hpu_result.pop().expect("IOP_DIVS must return 2 value"); let quotient = hpu_result.pop().expect("IOP_DIVS must return 2 value"); ( - FheUint::new(quotient, device.tag.clone()), - FheUint::new(remainder, device.tag.clone()), + FheUint::new(quotient, device.tag.clone(), ReRandomizationMetadata::default()), + FheUint::new(remainder, device.tag.clone(), ReRandomizationMetadata::default()), ) } } @@ -757,7 +826,7 @@ macro_rules! generic_integer_impl_scalar_operation { fn $rust_trait_method(self, rhs: $scalar_type) -> Self::Output { let inner_result = $closure(self, rhs); let tag = global_state::tag_of_internal_server_key().unwrap_display(); - <$concrete_type>::new(inner_result, tag) + <$concrete_type>::new(inner_result, tag, ReRandomizationMetadata::default()) } } )* // Closing second repeating pattern @@ -855,7 +924,7 @@ macro_rules! generic_integer_impl_scalar_left_operation { fn $rust_trait_method(self, rhs: &$concrete_type) -> Self::Output { let inner_result = $closure(*self, rhs); let tag = global_state::tag_of_internal_server_key().unwrap_display(); - <$concrete_type>::new(inner_result, tag) + <$concrete_type>::new(inner_result, tag, ReRandomizationMetadata::default()) } } )* // Closing second repeating pattern diff --git a/tfhe/src/high_level_api/keys/client.rs b/tfhe/src/high_level_api/keys/client.rs index 7eb51c75c..0c5590478 100644 --- a/tfhe/src/high_level_api/keys/client.rs +++ b/tfhe/src/high_level_api/keys/client.rs @@ -12,6 +12,7 @@ use crate::integer::compression_keys::CompressionPrivateKeys; use crate::integer::noise_squashing::{NoiseSquashingPrivateKey, NoiseSquashingPrivateKeyView}; use crate::named::Named; use crate::prelude::Tagged; +use crate::shortint::parameters::ShortintKeySwitchingParameters; use crate::shortint::MessageModulus; use crate::Tag; use tfhe_csprng::seeders::Seed; @@ -77,6 +78,7 @@ impl ClientKey { self.key.block_parameters() } + #[allow(clippy::type_complexity)] pub fn into_raw_parts( self, ) -> ( @@ -85,10 +87,11 @@ impl ClientKey { Option, Option, Option, + Option, Tag, ) { - let (cks, cpk, cppk, nsk, nscpk) = self.key.into_raw_parts(); - (cks, cpk, cppk, nsk, nscpk, self.tag) + let (cks, cpk, cppk, nsk, nscpk, cpkrndp) = self.key.into_raw_parts(); + (cks, cpk, cppk, nsk, nscpk, cpkrndp, self.tag) } pub fn from_raw_parts( @@ -100,6 +103,7 @@ impl ClientKey { compression_key: Option, noise_squashing_key: Option, noise_squashing_compression_key: Option, + cpk_re_randomization_ksk_params: Option, tag: Tag, ) -> Self { Self { @@ -109,6 +113,7 @@ impl ClientKey { compression_key, noise_squashing_key, noise_squashing_compression_key, + cpk_re_randomization_ksk_params, ), tag, } diff --git a/tfhe/src/high_level_api/keys/cpk_re_randomization.rs b/tfhe/src/high_level_api/keys/cpk_re_randomization.rs new file mode 100644 index 000000000..de994e93b --- /dev/null +++ b/tfhe/src/high_level_api/keys/cpk_re_randomization.rs @@ -0,0 +1,31 @@ +use crate::high_level_api::backward_compatibility::keys::{ + CompressedReRandomizationKeySwitchingKeyVersions, ReRandomizationKeySwitchingKeyVersions, +}; +use tfhe_versionable::Versionize; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(ReRandomizationKeySwitchingKeyVersions)] +pub enum ReRandomizationKeySwitchingKey { + UseCPKEncryptionKSK, + DedicatedKSK(crate::integer::key_switching_key::KeySwitchingKeyMaterial), +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(CompressedReRandomizationKeySwitchingKeyVersions)] +pub enum CompressedReRandomizationKeySwitchingKey { + UseCPKEncryptionKSK, + DedicatedKSK(crate::integer::key_switching_key::CompressedKeySwitchingKeyMaterial), +} + +impl CompressedReRandomizationKeySwitchingKey { + pub fn decompress(&self) -> ReRandomizationKeySwitchingKey { + match self { + Self::UseCPKEncryptionKSK => ReRandomizationKeySwitchingKey::UseCPKEncryptionKSK, + Self::DedicatedKSK(compressed_key_switching_key_material) => { + ReRandomizationKeySwitchingKey::DedicatedKSK( + compressed_key_switching_key_material.decompress(), + ) + } + } + } +} diff --git a/tfhe/src/high_level_api/keys/inner.rs b/tfhe/src/high_level_api/keys/inner.rs index e9e76550a..668ef69c9 100644 --- a/tfhe/src/high_level_api/keys/inner.rs +++ b/tfhe/src/high_level_api/keys/inner.rs @@ -2,6 +2,9 @@ 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::high_level_api::keys::cpk_re_randomization::{ + CompressedReRandomizationKeySwitchingKey, ReRandomizationKeySwitchingKey, +}; use crate::integer::ciphertext::{ CompressedNoiseSquashingCompressionKey, NoiseSquashingCompressionKey, NoiseSquashingCompressionPrivateKey, @@ -33,12 +36,13 @@ use tfhe_versionable::Versionize; pub(crate) struct IntegerConfig { pub(crate) block_parameters: crate::shortint::atomic_pattern::AtomicPatternParameters, pub(crate) dedicated_compact_public_key_parameters: Option<( - crate::shortint::parameters::CompactPublicKeyEncryptionParameters, - crate::shortint::parameters::ShortintKeySwitchingParameters, + CompactPublicKeyEncryptionParameters, + ShortintKeySwitchingParameters, )>, pub(crate) compression_parameters: Option, pub(crate) noise_squashing_parameters: Option, pub(crate) noise_squashing_compression_parameters: Option, + pub(crate) cpk_re_randomization_ksk_params: Option, } impl IntegerConfig { @@ -51,6 +55,7 @@ impl IntegerConfig { compression_parameters: None, noise_squashing_parameters: None, noise_squashing_compression_parameters: None, + cpk_re_randomization_ksk_params: None, } } @@ -76,6 +81,24 @@ impl IntegerConfig { self.noise_squashing_compression_parameters = Some(compression_parameters); } + pub(crate) fn enable_ciphertext_re_randomization( + &mut self, + cpk_re_randomization_ksk_params: ShortintKeySwitchingParameters, + ) { + assert_ne!( + self.dedicated_compact_public_key_parameters, None, + "Dedicated Compact Public Key parameters must be provided to enable re-randomization." + ); + assert!( + matches!( + cpk_re_randomization_ksk_params.destination_key, + EncryptionKeyChoice::Big + ), + "CompactPublicKey re-randomization can only be enabled targeting the large key." + ); + self.cpk_re_randomization_ksk_params = Some(cpk_re_randomization_ksk_params); + } + pub(crate) fn public_key_encryption_parameters( &self, ) -> Result @@ -103,6 +126,7 @@ impl Default for IntegerConfig { compression_parameters: None, noise_squashing_parameters: None, noise_squashing_compression_parameters: None, + cpk_re_randomization_ksk_params: None, } } } @@ -120,6 +144,10 @@ pub(crate) struct IntegerClientKey { pub(crate) compression_key: Option, pub(crate) noise_squashing_private_key: Option, pub(crate) noise_squashing_compression_private_key: Option, + // The re-randomization happens between a dedicated compact private key and the post PBS secret + // key, it needs additional information on how to create the required key switching key, hence + // this optional field + pub(crate) cpk_re_randomization_ksk_params: Option, } impl IntegerClientKey { @@ -150,15 +178,19 @@ impl IntegerClientKey { .noise_squashing_compression_parameters .map(NoiseSquashingCompressionPrivateKey::new); + let cpk_re_randomization_ksk_params = config.cpk_re_randomization_ksk_params; + Self { key, dedicated_compact_private_key, compression_key, noise_squashing_private_key, noise_squashing_compression_private_key, + cpk_re_randomization_ksk_params, } } + #[allow(clippy::type_complexity)] /// Deconstruct an [`IntegerClientKey`] into its constituents. pub fn into_raw_parts( self, @@ -168,6 +200,7 @@ impl IntegerClientKey { Option, Option, Option, + Option, ) { let Self { key, @@ -175,6 +208,7 @@ impl IntegerClientKey { compression_key, noise_squashing_private_key, noise_squashing_compression_private_key, + cpk_re_randomization_ksk_params, } = self; ( key, @@ -182,6 +216,7 @@ impl IntegerClientKey { compression_key, noise_squashing_private_key, noise_squashing_compression_private_key, + cpk_re_randomization_ksk_params, ) } @@ -196,6 +231,7 @@ impl IntegerClientKey { compression_key: Option, noise_squashing_private_key: Option, noise_squashing_compression_private_key: Option, + cpk_re_randomization_ksk_params: Option, ) -> Self { let shortint_cks: &crate::shortint::ClientKey = key.as_ref(); @@ -224,6 +260,7 @@ impl IntegerClientKey { compression_key, noise_squashing_private_key, noise_squashing_compression_private_key, + cpk_re_randomization_ksk_params, } } @@ -257,12 +294,15 @@ impl From for IntegerClientKey { .noise_squashing_compression_parameters .map(NoiseSquashingCompressionPrivateKey::new); + let cpk_re_randomization_ksk_params = config.cpk_re_randomization_ksk_params; + Self { key, dedicated_compact_private_key, compression_key, noise_squashing_private_key, noise_squashing_compression_private_key, + cpk_re_randomization_ksk_params, } } } @@ -281,6 +321,8 @@ pub struct IntegerServerKey { pub(crate) decompression_key: Option, pub(crate) noise_squashing_key: Option, pub(crate) noise_squashing_compression_key: Option, + pub(crate) cpk_re_randomization_key_switching_key_material: + Option, } impl IntegerServerKey { @@ -330,6 +372,39 @@ impl IntegerServerKey { }, ); + let cpk_re_randomization_key_switching_key_material = match ( + &client_key.dedicated_compact_private_key, + client_key.cpk_re_randomization_ksk_params, + ) { + (Some(compact_private_key), Some(cpk_re_randomization_ksk_params)) => { + assert!( + matches!( + cpk_re_randomization_ksk_params.destination_key, + EncryptionKeyChoice::Big + ), + "CompactPublicKey re-randomization can only be enabled \ + targeting the large secret key." + ); + + if cpk_re_randomization_ksk_params == compact_private_key.1 { + Some(ReRandomizationKeySwitchingKey::UseCPKEncryptionKSK) + } else { + let build_helper = + crate::integer::key_switching_key::KeySwitchingKeyBuildHelper::new( + (&compact_private_key.0, None), + (cks, &base_integer_key), + cpk_re_randomization_ksk_params, + ); + + Some(ReRandomizationKeySwitchingKey::DedicatedKSK( + build_helper.into(), + )) + } + } + (_, None) => None, + _ => panic!("Inconsistent ClientKey set-up for CompactPublicKey re-randomization."), + }; + Self { key: base_integer_key, cpk_key_switching_key_material, @@ -337,6 +412,7 @@ impl IntegerServerKey { decompression_key, noise_squashing_key, noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material, } } @@ -356,6 +432,22 @@ impl IntegerServerKey { }) } + pub(in crate::high_level_api) fn re_randomization_cpk_casting_key( + &self, + ) -> Option> { + self.cpk_re_randomization_key_switching_key_material + .as_ref() + .and_then(|key| match key { + ReRandomizationKeySwitchingKey::UseCPKEncryptionKSK => self + .cpk_key_switching_key_material + .as_ref() + .map(|k| k.as_view()), + ReRandomizationKeySwitchingKey::DedicatedKSK(key_switching_key_material) => { + Some(key_switching_key_material.as_view()) + } + }) + } + pub(in crate::high_level_api) fn message_modulus(&self) -> MessageModulus { self.key.message_modulus() } @@ -388,6 +480,8 @@ pub struct IntegerCompressedServerKey { pub(crate) decompression_key: Option, pub(crate) noise_squashing_key: Option, pub(crate) noise_squashing_compression_key: Option, + pub(crate) cpk_re_randomization_key_switching_key_material: + Option, } impl IntegerCompressedServerKey { @@ -440,6 +534,39 @@ impl IntegerCompressedServerKey { (Some(noise_squashing_key), noise_squashing_compression_key) }); + let cpk_re_randomization_key_switching_key_material = match ( + &client_key.dedicated_compact_private_key, + client_key.cpk_re_randomization_ksk_params, + ) { + (Some(compact_private_key), Some(cpk_re_randomization_ksk_params)) => { + assert!( + matches!( + cpk_re_randomization_ksk_params.destination_key, + EncryptionKeyChoice::Big + ), + "CompactPublicKey re-randomization can only be enabled \ + targeting the large secret key." + ); + + if cpk_re_randomization_ksk_params == compact_private_key.1 { + Some(CompressedReRandomizationKeySwitchingKey::UseCPKEncryptionKSK) + } else { + let build_helper = + crate::integer::key_switching_key::CompressedKeySwitchingKeyBuildHelper::new( + (&compact_private_key.0, None), + (cks, &key), + cpk_re_randomization_ksk_params, + ); + + Some(CompressedReRandomizationKeySwitchingKey::DedicatedKSK( + build_helper.into(), + )) + } + } + (_, None) => None, + _ => panic!("Inconsistent ClientKey set-up for CompactPublicKey re-randomization."), + }; + Self { key, cpk_key_switching_key_material, @@ -447,9 +574,11 @@ impl IntegerCompressedServerKey { decompression_key, noise_squashing_key, noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material, } } + #[allow(clippy::type_complexity)] pub fn into_raw_parts( self, ) -> ( @@ -457,12 +586,28 @@ impl IntegerCompressedServerKey { Option, Option, Option, + Option, + Option, + Option, ) { + let Self { + key, + cpk_key_switching_key_material, + compression_key, + decompression_key, + noise_squashing_key, + noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material, + } = self; + ( - self.key, - self.cpk_key_switching_key_material, - self.compression_key, - self.decompression_key, + key, + cpk_key_switching_key_material, + compression_key, + decompression_key, + noise_squashing_key, + noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material, ) } @@ -475,6 +620,9 @@ impl IntegerCompressedServerKey { decompression_key: Option, noise_squashing_key: Option, noise_squashing_compression_key: Option, + cpk_re_randomization_key_switching_key_material: Option< + CompressedReRandomizationKeySwitchingKey, + >, ) -> Self { Self { key, @@ -483,6 +631,7 @@ impl IntegerCompressedServerKey { decompression_key, noise_squashing_key, noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material, } } @@ -507,6 +656,11 @@ impl IntegerCompressedServerKey { .as_ref() .map(CompressedNoiseSquashingCompressionKey::decompress); + let cpk_re_randomization_key_switching_key_material = self + .cpk_re_randomization_key_switching_key_material + .as_ref() + .map(CompressedReRandomizationKeySwitchingKey::decompress); + IntegerServerKey { key: self.key.decompress(), cpk_key_switching_key_material: self.cpk_key_switching_key_material.as_ref().map( @@ -516,6 +670,7 @@ impl IntegerCompressedServerKey { decompression_key, noise_squashing_key, noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material, } } } @@ -605,6 +760,7 @@ pub struct IntegerServerKeyConformanceParams { pub compression_param: Option, pub noise_squashing_param: Option, pub noise_squashing_compression_param: Option, + pub cpk_re_randomization_ksk_params: Option, } impl> From for IntegerServerKeyConformanceParams { @@ -616,6 +772,7 @@ impl> From for IntegerServerKeyConformanceParams { compression_param: config.inner.compression_parameters, noise_squashing_param: config.inner.noise_squashing_parameters, noise_squashing_compression_param: config.inner.noise_squashing_compression_parameters, + cpk_re_randomization_ksk_params: config.inner.cpk_re_randomization_ksk_params, } } } @@ -676,6 +833,7 @@ impl ParameterSetConformant for IntegerServerKey { decompression_key, noise_squashing_key, noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material, } = self; let cpk_key_switching_key_material_is_ok = match ( @@ -747,11 +905,39 @@ impl ParameterSetConformant for IntegerServerKey { _ => return false, }; + let cpk_re_randomization_key_is_ok = match ( + cpk_key_switching_key_material.as_ref(), + cpk_re_randomization_key_switching_key_material.as_ref(), + parameter_set.cpk_param, + parameter_set.cpk_re_randomization_ksk_params, + ) { + // We need cpk_ksk, to be present when we have the re-randomization key + ( + Some(cpk_ksk), + Some(re_rand_ksk), + Some((cpk_params, _cpk_ksk_params)), + Some(re_rand_ks_params), + ) => (parameter_set.sk_param, cpk_params, re_rand_ks_params) + .try_into() + .is_ok_and(|re_rand_param| { + let key_to_check = match re_rand_ksk { + ReRandomizationKeySwitchingKey::UseCPKEncryptionKSK => cpk_ksk, + ReRandomizationKeySwitchingKey::DedicatedKSK(key) => key, + }; + + key_to_check.is_conformant(&re_rand_param) + }), + // No re-randomization key and no parameters for the key -> ok + (_, None, _, None) => true, + _ => 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 + && cpk_re_randomization_key_is_ok } } @@ -766,6 +952,7 @@ impl ParameterSetConformant for IntegerCompressedServerKey { decompression_key, noise_squashing_key, noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material, } = self; let cpk_key_switching_key_material_is_ok = match ( @@ -837,11 +1024,39 @@ impl ParameterSetConformant for IntegerCompressedServerKey { _ => return false, }; + let cpk_re_randomization_key_is_ok = match ( + cpk_key_switching_key_material.as_ref(), + cpk_re_randomization_key_switching_key_material.as_ref(), + parameter_set.cpk_param, + parameter_set.cpk_re_randomization_ksk_params, + ) { + // We need cpk_ksk, to be present when we have the re-randomization key + ( + Some(cpk_ksk), + Some(re_rand_ksk), + Some((cpk_params, _cpk_ksk_params)), + Some(re_rand_ks_params), + ) => (parameter_set.sk_param, cpk_params, re_rand_ks_params) + .try_into() + .is_ok_and(|re_rand_param| { + let key_to_check = match re_rand_ksk { + CompressedReRandomizationKeySwitchingKey::UseCPKEncryptionKSK => cpk_ksk, + CompressedReRandomizationKeySwitchingKey::DedicatedKSK(key) => key, + }; + + key_to_check.is_conformant(&re_rand_param) + }), + // No re-randomization key and no parameters for the key -> ok + (_, None, _, None) => true, + _ => 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 + && cpk_re_randomization_key_is_ok } } diff --git a/tfhe/src/high_level_api/keys/key_switching_key.rs b/tfhe/src/high_level_api/keys/key_switching_key.rs index 3ebd2cadc..29cfe2165 100644 --- a/tfhe/src/high_level_api/keys/key_switching_key.rs +++ b/tfhe/src/high_level_api/keys/key_switching_key.rs @@ -2,6 +2,7 @@ use tfhe_versionable::Versionize; use crate::backward_compatibility::keys::KeySwitchingKeyVersions; use crate::high_level_api::integers::{FheIntId, FheUintId}; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::integer::BooleanBlock; use crate::named::Named; use crate::prelude::FheKeyswitch; @@ -82,7 +83,11 @@ where fn keyswitch(&self, input: &FheUint) -> FheUint { let radix = input.ciphertext.on_cpu(); let casted = self.key.cast(&*radix); - FheUint::new(casted, self.tag_out.clone()) + FheUint::new( + casted, + self.tag_out.clone(), + ReRandomizationMetadata::default(), + ) } } @@ -93,7 +98,11 @@ where fn keyswitch(&self, input: &FheInt) -> FheInt { let radix = input.ciphertext.on_cpu(); let casted = self.key.cast(&*radix); - FheInt::new(casted, self.tag_out.clone()) + FheInt::new( + casted, + self.tag_out.clone(), + ReRandomizationMetadata::default(), + ) } } @@ -101,7 +110,11 @@ impl FheKeyswitch for KeySwitchingKey { fn keyswitch(&self, input: &FheBool) -> FheBool { let boolean_block = input.ciphertext.on_cpu(); let casted = self.key.key.cast(boolean_block.as_ref()); - FheBool::new(BooleanBlock::new_unchecked(casted), self.tag_out.clone()) + FheBool::new( + BooleanBlock::new_unchecked(casted), + self.tag_out.clone(), + ReRandomizationMetadata::default(), + ) } } diff --git a/tfhe/src/high_level_api/keys/mod.rs b/tfhe/src/high_level_api/keys/mod.rs index 3590b1cd9..018c5ac67 100644 --- a/tfhe/src/high_level_api/keys/mod.rs +++ b/tfhe/src/high_level_api/keys/mod.rs @@ -2,11 +2,15 @@ mod client; mod public; mod server; +mod cpk_re_randomization; mod inner; mod key_switching_key; use crate::high_level_api::config::Config; pub use client::ClientKey; +pub use cpk_re_randomization::{ + CompressedReRandomizationKeySwitchingKey, ReRandomizationKeySwitchingKey, +}; pub(crate) use inner::CompactPrivateKey; pub use key_switching_key::KeySwitchingKey; pub use public::{CompactPublicKey, CompressedCompactPublicKey, CompressedPublicKey, PublicKey}; diff --git a/tfhe/src/high_level_api/keys/server.rs b/tfhe/src/high_level_api/keys/server.rs index 3ebaee418..2cb35fa9a 100644 --- a/tfhe/src/high_level_api/keys/server.rs +++ b/tfhe/src/high_level_api/keys/server.rs @@ -7,7 +7,10 @@ use crate::core_crypto::gpu::lwe_keyswitch_key::CudaLweKeyswitchKey; 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::high_level_api::keys::{ + CompressedReRandomizationKeySwitchingKey, IntegerCompressedServerKey, IntegerServerKey, + ReRandomizationKeySwitchingKey, +}; use crate::integer::ciphertext::{ CompressedNoiseSquashingCompressionKey, NoiseSquashingCompressionKey, }; @@ -65,6 +68,7 @@ impl ServerKey { Option, Option, Option, + Option, Tag, ) { let IntegerServerKey { @@ -74,6 +78,7 @@ impl ServerKey { decompression_key, noise_squashing_key, noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material, } = (*self.key).clone(); ( @@ -83,10 +88,12 @@ impl ServerKey { decompression_key, noise_squashing_key, noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material, self.tag, ) } + #[allow(clippy::too_many_arguments)] pub fn from_raw_parts( key: crate::integer::ServerKey, cpk_key_switching_key_material: Option< @@ -96,6 +103,7 @@ impl ServerKey { decompression_key: Option, noise_squashing_key: Option, noise_squashing_compression_key: Option, + cpk_re_randomization_key_switching_key_material: Option, tag: Tag, ) -> Self { Self { @@ -106,6 +114,7 @@ impl ServerKey { decompression_key, noise_squashing_key, noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material, }), tag, } @@ -126,6 +135,12 @@ impl ServerKey { self.key.cpk_casting_key() } + pub(in crate::high_level_api) fn re_randomization_cpk_casting_key( + &self, + ) -> Option> { + self.key.re_randomization_cpk_casting_key() + } + pub fn noise_squashing_key( &self, ) -> Option<&crate::integer::noise_squashing::NoiseSquashingKey> { @@ -245,6 +260,7 @@ impl CompressedServerKey { } } + #[allow(clippy::type_complexity)] pub fn into_raw_parts( self, ) -> ( @@ -252,12 +268,16 @@ impl CompressedServerKey { Option, Option, Option, + Option, + Option, + Option, Tag, ) { - let (a, b, c, d) = self.integer_key.into_raw_parts(); - (a, b, c, d, self.tag) + let (a, b, c, d, e, f, g) = self.integer_key.into_raw_parts(); + (a, b, c, d, e, f, g, self.tag) } + #[allow(clippy::too_many_arguments)] pub fn from_raw_parts( integer_key: crate::integer::CompressedServerKey, cpk_key_switching_key_material: Option< @@ -267,6 +287,9 @@ impl CompressedServerKey { decompression_key: Option, noise_squashing_key: Option, noise_squashing_compression_key: Option, + cpk_re_randomization_key_switching_key_material: Option< + CompressedReRandomizationKeySwitchingKey, + >, tag: Tag, ) -> Self { Self { @@ -277,6 +300,7 @@ impl CompressedServerKey { decompression_key, noise_squashing_key, noise_squashing_compression_key, + cpk_re_randomization_key_switching_key_material, ), tag, } @@ -576,6 +600,7 @@ mod test { 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_KEYSWITCH_TO_BIG_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, }; @@ -662,11 +687,15 @@ mod test { let noise_squashing_compression_params = NOISE_SQUASHING_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + let cpk_re_randomization_ksk_params = + PARAM_KEYSWITCH_TO_BIG_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) + .enable_ciphertext_re_randomization(cpk_re_randomization_ksk_params) .build(); let ck = ClientKey::generate(config); @@ -709,6 +738,7 @@ mod test { compression_param: None, noise_squashing_param: None, noise_squashing_compression_param: None, + cpk_re_randomization_ksk_params: None, }; assert!(!sk.is_conformant(&conformance_params)); @@ -738,6 +768,7 @@ mod test { compression_param: None, noise_squashing_param: None, noise_squashing_compression_param: None, + cpk_re_randomization_ksk_params: None, }; assert!(!sk.is_conformant(&conformance_params)); @@ -823,11 +854,15 @@ mod test { let noise_squashing_compression_params = NOISE_SQUASHING_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + let cpk_re_randomization_ksk_params = + PARAM_KEYSWITCH_TO_BIG_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) + .enable_ciphertext_re_randomization(cpk_re_randomization_ksk_params) .build(); let ck = ClientKey::generate(config); @@ -870,6 +905,7 @@ mod test { compression_param: None, noise_squashing_param: None, noise_squashing_compression_param: None, + cpk_re_randomization_ksk_params: None, }; assert!(!sk.is_conformant(&conformance_params)); @@ -899,6 +935,7 @@ mod test { compression_param: None, noise_squashing_param: None, noise_squashing_compression_param: None, + cpk_re_randomization_ksk_params: 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 3a303288e..f50317161 100644 --- a/tfhe/src/high_level_api/mod.rs +++ b/tfhe/src/high_level_api/mod.rs @@ -63,7 +63,8 @@ pub use integers::{ pub use keys::CudaServerKey; pub use keys::{ generate_keys, ClientKey, CompactPublicKey, CompressedCompactPublicKey, CompressedPublicKey, - CompressedServerKey, KeySwitchingKey, PublicKey, ServerKey, + CompressedReRandomizationKeySwitchingKey, CompressedServerKey, KeySwitchingKey, PublicKey, + ReRandomizationKeySwitchingKey, ServerKey, }; use strum::FromRepr; @@ -129,6 +130,9 @@ pub use compressed_noise_squashed_ciphertext_list::{ CompressedSquashedNoiseCiphertextList, CompressedSquashedNoiseCiphertextListBuilder, HlSquashedNoiseCompressible, HlSquashedNoiseExpandable, }; +pub use re_randomization::{ + ReRandomizationContext, ReRandomizationMetadata, ReRandomizationSeedGen, +}; #[cfg(feature = "strings")] pub use strings::ascii::{EncryptableString, FheAsciiString, FheStringIsEmpty, FheStringLen}; pub use tag::Tag; @@ -141,6 +145,7 @@ mod errors; mod global_state; mod integers; mod keys; +mod re_randomization; #[cfg(feature = "strings")] mod strings; mod traits; diff --git a/tfhe/src/high_level_api/prelude.rs b/tfhe/src/high_level_api/prelude.rs index 02b017e67..d29f3b664 100644 --- a/tfhe/src/high_level_api/prelude.rs +++ b/tfhe/src/high_level_api/prelude.rs @@ -9,8 +9,8 @@ pub use crate::high_level_api::traits::{ BitSlice, CiphertextList, DivRem, FheDecrypt, FheEncrypt, FheEq, FheKeyswitch, FheMax, FheMin, FheOrd, FheTrivialEncrypt, FheTryEncrypt, FheTryTrivialEncrypt, FheWait, IfThenElse, - OverflowingAdd, OverflowingMul, OverflowingNeg, OverflowingSub, RotateLeft, RotateLeftAssign, - RotateRight, RotateRightAssign, ScalarIfThenElse, SquashNoise, Tagged, + OverflowingAdd, OverflowingMul, OverflowingNeg, OverflowingSub, ReRandomize, RotateLeft, + RotateLeftAssign, RotateRight, RotateRightAssign, ScalarIfThenElse, SquashNoise, Tagged, }; #[cfg(feature = "hpu")] pub use crate::high_level_api::traits::{FheHpu, HpuHandle}; diff --git a/tfhe/src/high_level_api/re_randomization.rs b/tfhe/src/high_level_api/re_randomization.rs new file mode 100644 index 000000000..3a8e8f65b --- /dev/null +++ b/tfhe/src/high_level_api/re_randomization.rs @@ -0,0 +1,234 @@ +use crate::backward_compatibility::cpk_re_randomization::ReRandomizationMetadataVersions; +use crate::core_crypto::commons::math::random::XofSeed; +use crate::high_level_api::keys::CompactPublicKey; +use crate::high_level_api::tag::SmallVec; +use crate::integer::ciphertext::{ReRandomizationSeed, ReRandomizationSeedHasher}; + +use tfhe_versionable::Versionize; + +/// Re-Randomization adds randomness to an existing ciphertext without changing the value it +/// encrypts. +/// +/// It can be used to achieve sIND-CPAD security and needs to be called on every function inputs. +/// +/// This works by encrypting zeros using a public key, then adding theses zeros to the ciphertext. +/// This process is seeded using the [`ReRandomizationContext`] and thus can be made deterministic. +/// +/// The randomization seeds are built from the ciphertexts encrypted values, some metadata that tie +/// them to their origin (such as a zk-pok) and a sequence of bytes that uniquely describe the +/// function that will be applied to them. +/// +/// More precisely, the hash function will be updated with, in order: +/// - the rerand seeder domain separator (e.g: TFHE_Rrd) +/// - the ciphertexts encrypted values +/// - the ciphertexts metadata +/// - the function description (e.g: "FheUint64+FheUint64" + a unique random nonce) +/// - a unique counter for each seed +/// +/// For example, if we want to re-randomize the inputs of a function with two arguments ct1 and ct2, +/// respectively associated to metadata meta1 and meta2, what happens conceptually is: +/// +/// ```text +/// seed1 = hash(rerand_domain_separator, ct1, ct2, meta1, meta2, fn_description, 0) +/// seed2 = hash(rerand_domain_separator, ct1, ct2, meta1, meta2, fn_description, 1) +/// ct1_rerand = ct1 + encrypt(0, pubkey, public_encryption_domain_separator, seed1) +/// ct2_rerand = ct2 + encrypt(0, pubkey, public_encryption_domain_separator, seed2) +/// function(ct1_rerand, ct2_rerand) +/// ``` +/// +/// # Example +/// +/// ```rust +/// use tfhe::prelude::*; +/// use tfhe::shortint::parameters::*; +/// use tfhe::{ +/// generate_keys, set_server_key, CompactPublicKey, ConfigBuilder, FheUint64, +/// ReRandomizationContext, +/// }; +/// +/// let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; +/// let cpk_params = ( +/// PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, +/// PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, +/// ); +/// let re_rand_ks_params = PARAM_KEYSWITCH_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; +/// +/// let config = ConfigBuilder::with_custom_parameters(params) +/// .use_dedicated_compact_public_key_parameters(cpk_params) +/// .enable_ciphertext_re_randomization(re_rand_ks_params) +/// .build(); +/// +/// let (cks, sks) = generate_keys(config); +/// let cpk = CompactPublicKey::new(&cks); +/// +/// let compact_public_encryption_domain_separator = *b"TFHE_Enc"; +/// let rerand_domain_separator = *b"TFHE_Rrd"; +/// +/// set_server_key(sks); +/// +/// let clear_a = 12u64; +/// let clear_b = 37u64; +/// let mut a = FheUint64::encrypt(clear_a, &cks); +/// let mut b = FheUint64::encrypt(clear_b, &cks); +/// +/// // Simulate a 256 bits hash added as metadata +/// let rand_a: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); +/// let rand_b: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); +/// a.re_randomization_metadata_mut().set_data(&rand_a); +/// b.re_randomization_metadata_mut().set_data(&rand_b); +/// +/// // Simulate a 256 bits nonce +/// let nonce: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); +/// +/// let mut re_rand_context = ReRandomizationContext::new( +/// rerand_domain_separator, +/// // First is the function description, second is a nonce +/// [b"FheUint64+FheUint64".as_slice(), nonce.as_slice()], +/// compact_public_encryption_domain_separator, +/// ); +/// +/// // Add ciphertexts to the context +/// re_rand_context.add_ciphertext(&a); +/// re_rand_context.add_ciphertext(&b); +/// +/// let mut seed_gen = re_rand_context.finalize(); +/// +/// a.re_randomize(&cpk, seed_gen.next_seed().unwrap()).unwrap(); +/// b.re_randomize(&cpk, seed_gen.next_seed().unwrap()).unwrap(); +/// +/// let c = a + b; +/// let dec: u64 = c.decrypt(&cks); +/// +/// assert_eq!(clear_a.wrapping_add(clear_b), dec); +/// ``` +pub trait ReRandomize +where + Self: Sized, +{ + fn add_to_re_randomization_context(&self, context: &mut ReRandomizationContext); + + /// Re-randomize the ciphertext using the provided public key and seed. + /// + /// The random elements of the ciphertexts will be changed but it will still encrypt the same + /// value. + fn re_randomize( + &mut self, + compact_public_key: &CompactPublicKey, + seed: ReRandomizationSeed, + ) -> crate::Result<()>; +} + +/// The context in which the ciphertexts to re-randomized will be used. +/// +/// It can be updated with user provided ciphertexts and will then be finalized into a +/// [`ReRandomizationSeedGen`]. +pub struct ReRandomizationContext { + pub(in crate::high_level_api) inner: crate::integer::ciphertext::ReRandomizationContext, +} + +impl ReRandomizationContext { + /// Create a new re-randomization context with the default seed hasher (blake3). + /// + /// `rerand_seeder_domain_separator` is the domain separator that will be fed into the + /// seed generator. + /// `public_encryption_domain_separator` is the domain separator that will be used along this + /// seed to generate the encryptions of zero. + /// `fn_description` is a unique sequence of bytes that represents the functions called on the + /// re-randomized values. + /// + /// (See [`XofSeed`] for more information) + /// + /// # Example + /// ```rust + /// use tfhe::ReRandomizationContext; + /// // Simulate a 256 bits nonce + /// let nonce: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); + /// let _re_rand_context = ReRandomizationContext::new( + /// *b"TFHE_Rrd", + /// [b"FheUint64+FheUint64".as_slice(), &nonce], + /// *b"TFHE_Enc" + /// ); + pub fn new<'a>( + rerand_seeder_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN], + fn_description: impl IntoIterator, + public_encryption_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN], + ) -> Self { + Self { + inner: crate::integer::ciphertext::ReRandomizationContext::new( + rerand_seeder_domain_separator, + fn_description, + public_encryption_domain_separator, + ), + } + } + + /// Create a new re-randomization context with the provided seed hasher. + pub fn new_with_hasher<'a>( + fn_description: impl IntoIterator, + public_encryption_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN], + seed_hasher: ReRandomizationSeedHasher, + ) -> Self { + Self { + inner: crate::integer::ciphertext::ReRandomizationContext::new_with_hasher( + fn_description, + public_encryption_domain_separator, + seed_hasher, + ), + } + } + + /// Adds a new ciphertext to the re-randomization context + pub fn add_ciphertext(&mut self, data: &Data) { + data.add_to_re_randomization_context(self); + } + + /// Consumes the context to create a seed generator + pub fn finalize(self) -> ReRandomizationSeedGen { + ReRandomizationSeedGen { + inner: self.inner.finalize(), + } + } +} + +/// A generator that can be used to obtain seeds needed to re-randomize individual ciphertexts. +/// +/// It should only be used to create one seed per ciphertext that was added to the context +pub struct ReRandomizationSeedGen { + inner: crate::integer::ciphertext::ReRandomizationSeedGen, +} + +impl ReRandomizationSeedGen { + /// Generate the next seed from the seeder. + /// + /// Returns an error if more seeds have been generated than the number of ciphertext added into + /// the context. + pub fn next_seed(&mut self) -> crate::Result { + self.inner.next_seed() + } +} + +/// Metadata linked to a ciphertext that will be used when updating the [`ReRandomizationContext`] +#[derive( + Default, Clone, Debug, serde::Serialize, serde::Deserialize, Versionize, PartialEq, Eq, +)] +#[versionize(ReRandomizationMetadataVersions)] +pub struct ReRandomizationMetadata { + inner: SmallVec, +} + +impl ReRandomizationMetadata { + pub fn new(data: &[u8]) -> Self { + let mut inner = SmallVec::default(); + inner.set_data(data); + + Self { inner } + } + + pub fn data(&self) -> &[u8] { + self.inner.data() + } + + pub fn set_data(&mut self, data: &[u8]) { + self.inner.set_data(data); + } +} diff --git a/tfhe/src/high_level_api/strings/ascii/comp.rs b/tfhe/src/high_level_api/strings/ascii/comp.rs index a611e9dd3..c92ace38b 100644 --- a/tfhe/src/high_level_api/strings/ascii/comp.rs +++ b/tfhe/src/high_level_api/strings/ascii/comp.rs @@ -1,5 +1,6 @@ use crate::high_level_api::global_state::with_internal_keys; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::high_level_api::strings::ascii::FheAsciiString; use crate::prelude::{FheEq, FheEqIgnoreCase, FheOrd}; use crate::strings::ciphertext::ClearString; @@ -12,7 +13,11 @@ impl FheEq<&Self> for FheAsciiString { let inner = cpu_key .string_key() .eq(&self.inner.on_cpu(), (&*other.inner.on_cpu()).into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -31,7 +36,11 @@ impl FheEq<&Self> for FheAsciiString { let inner = cpu_key .string_key() .ne(&self.inner.on_cpu(), (&*other.inner.on_cpu()).into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -50,7 +59,11 @@ impl FheEq<&ClearString> for FheAsciiString { with_internal_keys(|keys| match keys { InternalServerKey::Cpu(cpu_key) => { let inner = cpu_key.string_key().eq(&self.inner.on_cpu(), other.into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -67,7 +80,11 @@ impl FheEq<&ClearString> for FheAsciiString { with_internal_keys(|keys| match keys { InternalServerKey::Cpu(cpu_key) => { let inner = cpu_key.string_key().ne(&self.inner.on_cpu(), other.into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -88,7 +105,11 @@ impl FheOrd<&Self> for FheAsciiString { let inner = cpu_key .string_key() .lt(&self.inner.on_cpu(), (&*other.inner.on_cpu()).into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -107,7 +128,11 @@ impl FheOrd<&Self> for FheAsciiString { let inner = cpu_key .string_key() .le(&self.inner.on_cpu(), (&*other.inner.on_cpu()).into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -126,7 +151,11 @@ impl FheOrd<&Self> for FheAsciiString { let inner = cpu_key .string_key() .gt(&self.inner.on_cpu(), (&*other.inner.on_cpu()).into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -145,7 +174,11 @@ impl FheOrd<&Self> for FheAsciiString { let inner = cpu_key .string_key() .ge(&self.inner.on_cpu(), (&*other.inner.on_cpu()).into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -164,7 +197,11 @@ impl FheOrd<&ClearString> for FheAsciiString { with_internal_keys(|keys| match keys { InternalServerKey::Cpu(cpu_key) => { let inner = cpu_key.string_key().lt(&self.inner.on_cpu(), other.into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -181,7 +218,11 @@ impl FheOrd<&ClearString> for FheAsciiString { with_internal_keys(|keys| match keys { InternalServerKey::Cpu(cpu_key) => { let inner = cpu_key.string_key().le(&self.inner.on_cpu(), other.into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -198,7 +239,11 @@ impl FheOrd<&ClearString> for FheAsciiString { with_internal_keys(|keys| match keys { InternalServerKey::Cpu(cpu_key) => { let inner = cpu_key.string_key().gt(&self.inner.on_cpu(), other.into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -215,7 +260,11 @@ impl FheOrd<&ClearString> for FheAsciiString { with_internal_keys(|keys| match keys { InternalServerKey::Cpu(cpu_key) => { let inner = cpu_key.string_key().ge(&self.inner.on_cpu(), other.into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -255,7 +304,11 @@ impl FheEqIgnoreCase for FheAsciiString { let inner = cpu_key .string_key() .eq_ignore_case(&self.inner.on_cpu(), (&*rhs.inner.on_cpu()).into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -295,7 +348,11 @@ impl FheEqIgnoreCase for FheAsciiString { let inner = cpu_key .string_key() .eq_ignore_case(&self.inner.on_cpu(), rhs.into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { diff --git a/tfhe/src/high_level_api/strings/ascii/contains.rs b/tfhe/src/high_level_api/strings/ascii/contains.rs index d46039031..cc4a47652 100644 --- a/tfhe/src/high_level_api/strings/ascii/contains.rs +++ b/tfhe/src/high_level_api/strings/ascii/contains.rs @@ -1,5 +1,6 @@ use crate::high_level_api::global_state::with_internal_keys; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::high_level_api::strings::ascii::FheAsciiString; use crate::high_level_api::strings::traits::FheStringMatching; use crate::strings::ciphertext::ClearString; @@ -31,7 +32,11 @@ impl FheStringMatching<&Self> for FheAsciiString { let inner = cpu_key .string_key() .contains(&self.inner.on_cpu(), (&*other.inner.on_cpu()).into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -69,7 +74,11 @@ impl FheStringMatching<&Self> for FheAsciiString { let inner = cpu_key .string_key() .starts_with(&self.inner.on_cpu(), (&*other.inner.on_cpu()).into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -107,7 +116,11 @@ impl FheStringMatching<&Self> for FheAsciiString { let inner = cpu_key .string_key() .ends_with(&self.inner.on_cpu(), (&*other.inner.on_cpu()).into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -128,7 +141,11 @@ impl FheStringMatching<&ClearString> for FheAsciiString { let inner = cpu_key .string_key() .contains(&self.inner.on_cpu(), other.into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -147,7 +164,11 @@ impl FheStringMatching<&ClearString> for FheAsciiString { let inner = cpu_key .string_key() .starts_with(&self.inner.on_cpu(), other.into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -166,7 +187,11 @@ impl FheStringMatching<&ClearString> for FheAsciiString { let inner = cpu_key .string_key() .ends_with(&self.inner.on_cpu(), other.into()); - FheBool::new(inner, cpu_key.tag.clone()) + FheBool::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { diff --git a/tfhe/src/high_level_api/strings/ascii/find.rs b/tfhe/src/high_level_api/strings/ascii/find.rs index dd802f6af..2a6f8a89f 100644 --- a/tfhe/src/high_level_api/strings/ascii/find.rs +++ b/tfhe/src/high_level_api/strings/ascii/find.rs @@ -1,5 +1,6 @@ use crate::high_level_api::global_state::with_internal_keys; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::high_level_api::strings::ascii::FheAsciiString; use crate::high_level_api::strings::traits::FheStringFind; use crate::strings::ciphertext::ClearString; @@ -35,8 +36,16 @@ impl FheStringFind<&Self> for FheAsciiString { .string_key() .find(&self.inner.on_cpu(), (&*pat.inner.on_cpu()).into()); ( - FheUint32::new(inner, cpu_key.tag.clone()), - FheBool::new(block, cpu_key.tag.clone()), + FheUint32::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + block, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -79,8 +88,16 @@ impl FheStringFind<&Self> for FheAsciiString { .string_key() .rfind(&self.inner.on_cpu(), (&*pat.inner.on_cpu()).into()); ( - FheUint32::new(inner, cpu_key.tag.clone()), - FheBool::new(block, cpu_key.tag.clone()), + FheUint32::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + block, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -123,8 +140,16 @@ impl FheStringFind<&ClearString> for FheAsciiString { InternalServerKey::Cpu(cpu_key) => { let (inner, block) = cpu_key.string_key().find(&self.inner.on_cpu(), pat.into()); ( - FheUint32::new(inner, cpu_key.tag.clone()), - FheBool::new(block, cpu_key.tag.clone()), + FheUint32::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + block, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -165,8 +190,16 @@ impl FheStringFind<&ClearString> for FheAsciiString { InternalServerKey::Cpu(cpu_key) => { let (inner, block) = cpu_key.string_key().rfind(&self.inner.on_cpu(), pat.into()); ( - FheUint32::new(inner, cpu_key.tag.clone()), - FheBool::new(block, cpu_key.tag.clone()), + FheUint32::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + block, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] diff --git a/tfhe/src/high_level_api/strings/ascii/mod.rs b/tfhe/src/high_level_api/strings/ascii/mod.rs index 7d08db599..b3125fbb5 100644 --- a/tfhe/src/high_level_api/strings/ascii/mod.rs +++ b/tfhe/src/high_level_api/strings/ascii/mod.rs @@ -7,10 +7,12 @@ mod strip; mod trim; pub use crate::high_level_api::backward_compatibility::strings::FheAsciiStringVersions; +use crate::high_level_api::compressed_ciphertext_list::ToBeCompressed; use crate::high_level_api::details::MaybeCloned; use crate::high_level_api::errors::UninitializedServerKey; use crate::high_level_api::global_state; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::integer::ciphertext::{Compressible, DataKind, Expandable}; use crate::named::Named; use crate::prelude::{FheDecrypt, FheTryEncrypt, FheTryTrivialEncrypt, Tagged}; @@ -128,6 +130,7 @@ impl Unversionize for AsciiDevice { pub struct FheAsciiString { pub(crate) inner: AsciiDevice, pub(crate) tag: Tag, + pub(crate) re_randomization_metadata: ReRandomizationMetadata, } impl Named for FheAsciiString { @@ -145,10 +148,15 @@ impl Tagged for FheAsciiString { } impl FheAsciiString { - pub(crate) fn new(inner: impl Into, tag: Tag) -> Self { + pub(crate) fn new( + inner: impl Into, + tag: Tag, + re_randomization_metadata: ReRandomizationMetadata, + ) -> Self { Self { inner: inner.into(), tag, + re_randomization_metadata, } } @@ -308,6 +316,14 @@ impl FheAsciiString { pub fn is_trivial(&self) -> bool { self.inner.on_cpu().is_trivial() } + + pub fn re_randomization_metadata(&self) -> &ReRandomizationMetadata { + &self.re_randomization_metadata + } + + pub fn re_randomization_metadata_mut(&mut self) -> &mut ReRandomizationMetadata { + &mut self.re_randomization_metadata + } } impl<'a> FheTryEncrypt, ClientKey> for FheAsciiString { @@ -325,6 +341,7 @@ impl<'a> FheTryEncrypt, ClientKey> for FheAsciiString { Ok(Self { inner: inner.into(), tag: key.tag.clone(), + re_randomization_metadata: ReRandomizationMetadata::default(), }) } } @@ -360,7 +377,11 @@ impl<'a> FheTryTrivialEncrypt> for FheAsciiString { global_state::try_with_internal_keys(|keys| match keys { Some(InternalServerKey::Cpu(cpu_key)) => { let inner = cpu_key.string_key().trivial_encrypt_ascii(str, padding); - Ok(Self::new(inner, cpu_key.tag.clone())) + Ok(Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + )) } #[cfg(feature = "gpu")] Some(InternalServerKey::Cuda(_)) => Err(crate::error!("CUDA does not support string")), @@ -398,8 +419,13 @@ impl Expandable for FheAsciiString { blocks: Vec, kind: DataKind, ) -> crate::Result { - FheString::from_expanded_blocks(blocks, kind) - .map(|cpu_string| Self::new(cpu_string, Tag::default())) + FheString::from_expanded_blocks(blocks, kind).map(|cpu_string| { + Self::new( + cpu_string, + Tag::default(), + ReRandomizationMetadata::default(), + ) + }) } } @@ -418,29 +444,26 @@ impl crate::integer::gpu::ciphertext::compressed_ciphertext_list::CudaExpandable } } -impl HlExpandable for FheAsciiString {} - impl crate::HlCompressible for FheAsciiString { - fn compress_into( - self, - messages: &mut Vec<( - crate::high_level_api::compressed_ciphertext_list::ToBeCompressed, - DataKind, - )>, - ) { + fn compress_into(self, messages: &mut Vec<(ToBeCompressed, DataKind)>) { match self.inner { AsciiDevice::Cpu(fhe_string) => { let mut blocks = vec![]; let data_kind = fhe_string.compress_into(&mut blocks); if let Some(data_kind) = data_kind { - messages.push(( - crate::high_level_api::compressed_ciphertext_list::ToBeCompressed::Cpu( - blocks, - ), - data_kind, - )); + messages.push((ToBeCompressed::Cpu(blocks), data_kind)); } } } } + + fn get_re_randomization_metadata(&self) -> ReRandomizationMetadata { + self.re_randomization_metadata.clone() + } +} + +impl HlExpandable for FheAsciiString { + fn set_re_randomization_metadata(&mut self, meta: ReRandomizationMetadata) { + self.re_randomization_metadata = meta; + } } diff --git a/tfhe/src/high_level_api/strings/ascii/no_pattern.rs b/tfhe/src/high_level_api/strings/ascii/no_pattern.rs index 3fe11e447..a8ee60f17 100644 --- a/tfhe/src/high_level_api/strings/ascii/no_pattern.rs +++ b/tfhe/src/high_level_api/strings/ascii/no_pattern.rs @@ -1,6 +1,7 @@ use crate::high_level_api::global_state::with_internal_keys; use crate::high_level_api::integers::FheUint16; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::high_level_api::strings::ascii::FheAsciiString; use crate::high_level_api::traits::FheTrivialEncrypt; use crate::prelude::FheStringRepeat; @@ -29,9 +30,11 @@ impl From for FheStringLen { fn from(value: crate::strings::server_key::FheStringLen) -> Self { match value { crate::strings::server_key::FheStringLen::NoPadding(v) => Self::NoPadding(v as u16), - crate::strings::server_key::FheStringLen::Padding(v) => { - Self::Padding(FheUint16::new(v, Tag::default())) - } + crate::strings::server_key::FheStringLen::Padding(v) => Self::Padding(FheUint16::new( + v, + Tag::default(), + ReRandomizationMetadata::default(), + )), } } } @@ -58,7 +61,11 @@ impl From for FheStringIsEmpty { match value { crate::strings::server_key::FheStringIsEmpty::NoPadding(v) => Self::NoPadding(v), crate::strings::server_key::FheStringIsEmpty::Padding(bool_block) => { - Self::Padding(FheBool::new(bool_block, Tag::default())) + Self::Padding(FheBool::new( + bool_block, + Tag::default(), + ReRandomizationMetadata::default(), + )) } } } @@ -182,7 +189,11 @@ impl FheAsciiString { with_internal_keys(|keys| match keys { InternalServerKey::Cpu(cpu_key) => { let inner = cpu_key.string_key().to_lowercase(&self.inner.on_cpu()); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -216,7 +227,11 @@ impl FheAsciiString { with_internal_keys(|keys| match keys { InternalServerKey::Cpu(cpu_key) => { let inner = cpu_key.string_key().to_uppercase(&self.inner.on_cpu()); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -253,7 +268,11 @@ impl FheAsciiString { let inner = cpu_key .string_key() .concat(&self.inner.on_cpu(), &other.inner.on_cpu()); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -312,7 +331,11 @@ impl FheStringRepeat for FheAsciiString { let inner = cpu_key .string_key() .repeat(&self.inner.on_cpu(), &UIntArg::Clear(count)); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -360,7 +383,11 @@ impl FheStringRepeat<(FheUint16, u16)> for FheAsciiString { &self.inner.on_cpu(), &UIntArg::Enc(EncU16::new(count.ciphertext.into_cpu(), Some(bound))), ); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { diff --git a/tfhe/src/high_level_api/strings/ascii/replace.rs b/tfhe/src/high_level_api/strings/ascii/replace.rs index 2416d7496..082bc442c 100644 --- a/tfhe/src/high_level_api/strings/ascii/replace.rs +++ b/tfhe/src/high_level_api/strings/ascii/replace.rs @@ -1,5 +1,6 @@ use crate::high_level_api::global_state::with_internal_keys; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::high_level_api::strings::ascii::FheAsciiString; use crate::high_level_api::strings::traits::FheStringReplace; use crate::prelude::FheStringReplaceN; @@ -36,7 +37,11 @@ impl FheStringReplace<&Self> for FheAsciiString { (&*from.inner.on_cpu()).into(), &to.inner.on_cpu(), ); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -79,7 +84,11 @@ impl FheStringReplace<&ClearString> for FheAsciiString { from.into(), &to.inner.on_cpu(), ); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -121,7 +130,11 @@ impl FheStringReplaceN<&Self, u16> for FheAsciiString { &to.inner.on_cpu(), &UIntArg::Clear(count), ); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -145,7 +158,11 @@ impl FheStringReplaceN<&Self, (FheUint16, u16)> for FheAsciiString { &to.inner.on_cpu(), &UIntArg::Enc(EncU16::new(count.ciphertext.into_cpu(), Some(max))), ); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -187,7 +204,11 @@ impl FheStringReplaceN<&ClearString, u16> for FheAsciiString { &to.inner.on_cpu(), &UIntArg::Clear(count), ); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -211,7 +232,11 @@ impl FheStringReplaceN<&ClearString, (FheUint16, u16)> for FheAsciiString { &to.inner.on_cpu(), &UIntArg::Enc(EncU16::new(count.ciphertext.into_cpu(), Some(max))), ); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { diff --git a/tfhe/src/high_level_api/strings/ascii/strip.rs b/tfhe/src/high_level_api/strings/ascii/strip.rs index e5ce327f2..2da9abc7b 100644 --- a/tfhe/src/high_level_api/strings/ascii/strip.rs +++ b/tfhe/src/high_level_api/strings/ascii/strip.rs @@ -1,5 +1,6 @@ use crate::high_level_api::global_state::with_internal_keys; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::high_level_api::strings::ascii::FheAsciiString; use crate::high_level_api::strings::traits::FheStringStrip; use crate::high_level_api::FheBool; @@ -38,8 +39,16 @@ impl FheStringStrip<&Self> for FheAsciiString { .string_key() .strip_prefix(&self.inner.on_cpu(), (&*pat.inner.on_cpu()).into()); ( - Self::new(inner, cpu_key.tag.clone()), - FheBool::new(block, cpu_key.tag.clone()), + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + block, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -85,8 +94,16 @@ impl FheStringStrip<&Self> for FheAsciiString { .string_key() .strip_suffix(&self.inner.on_cpu(), (&*pat.inner.on_cpu()).into()); ( - Self::new(inner, cpu_key.tag.clone()), - FheBool::new(block, cpu_key.tag.clone()), + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + block, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -134,8 +151,16 @@ impl FheStringStrip<&ClearString> for FheAsciiString { .string_key() .strip_prefix(&self.inner.on_cpu(), pat.into()); ( - Self::new(inner, cpu_key.tag.clone()), - FheBool::new(block, cpu_key.tag.clone()), + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + block, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] @@ -181,8 +206,16 @@ impl FheStringStrip<&ClearString> for FheAsciiString { .string_key() .strip_suffix(&self.inner.on_cpu(), pat.into()); ( - Self::new(inner, cpu_key.tag.clone()), - FheBool::new(block, cpu_key.tag.clone()), + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), + FheBool::new( + block, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ), ) } #[cfg(feature = "gpu")] diff --git a/tfhe/src/high_level_api/strings/ascii/trim.rs b/tfhe/src/high_level_api/strings/ascii/trim.rs index 7c20607de..702878cc7 100644 --- a/tfhe/src/high_level_api/strings/ascii/trim.rs +++ b/tfhe/src/high_level_api/strings/ascii/trim.rs @@ -1,5 +1,6 @@ use crate::high_level_api::global_state::with_internal_keys; use crate::high_level_api::keys::InternalServerKey; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::high_level_api::strings::ascii::FheAsciiString; impl FheAsciiString { @@ -23,7 +24,11 @@ impl FheAsciiString { with_internal_keys(|keys| match keys { InternalServerKey::Cpu(cpu_key) => { let inner = cpu_key.string_key().trim_start(&self.inner.on_cpu()); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -56,7 +61,11 @@ impl FheAsciiString { with_internal_keys(|keys| match keys { InternalServerKey::Cpu(cpu_key) => { let inner = cpu_key.string_key().trim_end(&self.inner.on_cpu()); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { @@ -89,7 +98,11 @@ impl FheAsciiString { with_internal_keys(|keys| match keys { InternalServerKey::Cpu(cpu_key) => { let inner = cpu_key.string_key().trim(&self.inner.on_cpu()); - Self::new(inner, cpu_key.tag.clone()) + Self::new( + inner, + cpu_key.tag.clone(), + ReRandomizationMetadata::default(), + ) } #[cfg(feature = "gpu")] InternalServerKey::Cuda(_) => { diff --git a/tfhe/src/high_level_api/tests/cpk_re_randomization.rs b/tfhe/src/high_level_api/tests/cpk_re_randomization.rs new file mode 100644 index 000000000..5f9bf35bf --- /dev/null +++ b/tfhe/src/high_level_api/tests/cpk_re_randomization.rs @@ -0,0 +1,194 @@ +use crate::high_level_api::prelude::*; +use crate::high_level_api::{ + generate_keys, CompactPublicKey, CompressedCiphertextListBuilder, ConfigBuilder, FheBool, + FheInt8, FheUint64, ReRandomizationContext, +}; +use crate::set_server_key; +use crate::shortint::parameters::{ + COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, + PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, + PARAM_KEYSWITCH_TO_BIG_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, +}; + +#[test] +fn test_re_rand() { + let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + let cpk_params = ( + PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, + PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, + ); + let comp_params = COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + let re_rand_ks_params = PARAM_KEYSWITCH_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + + let config = ConfigBuilder::with_custom_parameters(params) + .use_dedicated_compact_public_key_parameters(cpk_params) + .enable_compression(comp_params) + .enable_ciphertext_re_randomization(re_rand_ks_params) + .build(); + + let (cks, sks) = generate_keys(config); + let cpk = CompactPublicKey::new(&cks); + + let compact_public_encryption_domain_separator = *b"TFHE_Enc"; + let rerand_domain_separator = *b"TFHE_Rrd"; + + set_server_key(sks); + + // Case where we want to compute FheUint64 + FheUint64 and re-randomize those inputs + { + let clear_a = rand::random::(); + let clear_b = rand::random::(); + let mut a = FheUint64::encrypt(clear_a, &cks); + let mut b = FheUint64::encrypt(clear_b, &cks); + + // Simulate a 256 bits hash added as metadata + let rand_a: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); + let rand_b: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); + a.re_randomization_metadata_mut().set_data(&rand_a); + b.re_randomization_metadata_mut().set_data(&rand_b); + + let mut builder = CompressedCiphertextListBuilder::new(); + builder.push(a); + builder.push(b); + let list = builder.build().unwrap(); + + let mut a: FheUint64 = list.get(0).unwrap().unwrap(); + let mut b: FheUint64 = list.get(1).unwrap().unwrap(); + + assert_eq!(a.re_randomization_metadata().data(), &rand_a); + assert_eq!(b.re_randomization_metadata().data(), &rand_b); + + // Simulate a 256 bits nonce + let nonce: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); + + let mut re_rand_context = ReRandomizationContext::new( + rerand_domain_separator, + // First is the function description, second is a nonce + [b"FheUint64+FheUint64".as_slice(), nonce.as_slice()], + compact_public_encryption_domain_separator, + ); + + // Add ciphertexts to the context + + re_rand_context.add_ciphertext(&a); + re_rand_context.add_ciphertext(&b); + + let mut seed_gen = re_rand_context.finalize(); + + a.re_randomize(&cpk, seed_gen.next_seed().unwrap()).unwrap(); + + b.re_randomize(&cpk, seed_gen.next_seed().unwrap()).unwrap(); + + let c = a + b; + let dec: u64 = c.decrypt(&cks); + + assert_eq!(clear_a.wrapping_add(clear_b), dec); + } + + // Case where we want to compute FheInt8 + FheInt8 and re-randomize those inputs + { + let clear_a = rand::random::(); + let clear_b = rand::random::(); + let mut a = FheInt8::encrypt(clear_a, &cks); + let mut b = FheInt8::encrypt(clear_b, &cks); + + // Simulate a 256 bits hash added as metadata + let rand_a: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); + let rand_b: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); + a.re_randomization_metadata_mut().set_data(&rand_a); + b.re_randomization_metadata_mut().set_data(&rand_b); + + let mut builder = CompressedCiphertextListBuilder::new(); + builder.push(a); + builder.push(b); + let list = builder.build().unwrap(); + + let mut a: FheInt8 = list.get(0).unwrap().unwrap(); + let mut b: FheInt8 = list.get(1).unwrap().unwrap(); + + assert_eq!(a.re_randomization_metadata().data(), &rand_a); + assert_eq!(b.re_randomization_metadata().data(), &rand_b); + + // Simulate a 256 bits nonce + let nonce: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); + let compact_public_encryption_domain_separator = *b"TFHE_Enc"; + + let mut re_rand_context = ReRandomizationContext::new( + rerand_domain_separator, + // First is the function description, second is a nonce + [b"FheInt8+FheInt8".as_slice(), nonce.as_slice()], + compact_public_encryption_domain_separator, + ); + + // Add ciphertexts to the context + + re_rand_context.add_ciphertext(&a); + re_rand_context.add_ciphertext(&b); + + let mut seed_gen = re_rand_context.finalize(); + + a.re_randomize(&cpk, seed_gen.next_seed().unwrap()).unwrap(); + + b.re_randomize(&cpk, seed_gen.next_seed().unwrap()).unwrap(); + + let c = a + b; + let dec: i8 = c.decrypt(&cks); + + assert_eq!(clear_a.wrapping_add(clear_b), dec); + } + + // Case where we want to compute FheBool && FheBool and re-randomize those inputs + { + for clear_a in [false, true] { + for clear_b in [false, true] { + let mut a = FheBool::encrypt(clear_a, &cks); + let mut b = FheBool::encrypt(clear_b, &cks); + + // Simulate a 256 bits hash added as metadata + let rand_a: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); + let rand_b: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); + a.re_randomization_metadata_mut().set_data(&rand_a); + b.re_randomization_metadata_mut().set_data(&rand_b); + + let mut builder = CompressedCiphertextListBuilder::new(); + builder.push(a); + builder.push(b); + let list = builder.build().unwrap(); + + let mut a: FheBool = list.get(0).unwrap().unwrap(); + let mut b: FheBool = list.get(1).unwrap().unwrap(); + + assert_eq!(a.re_randomization_metadata().data(), &rand_a); + assert_eq!(b.re_randomization_metadata().data(), &rand_b); + + // Simulate a 256 bits nonce + let nonce: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); + let compact_public_encryption_domain_separator = *b"TFHE_Enc"; + + let mut re_rand_context = ReRandomizationContext::new( + rerand_domain_separator, + // First is the function description, second is a nonce + [b"FheBool&FheBool".as_slice(), nonce.as_slice()], + compact_public_encryption_domain_separator, + ); + + // Add ciphertexts to the context + re_rand_context.add_ciphertext(&a); + re_rand_context.add_ciphertext(&b); + + let mut seed_gen = re_rand_context.finalize(); + + a.re_randomize(&cpk, seed_gen.next_seed().unwrap()).unwrap(); + + b.re_randomize(&cpk, seed_gen.next_seed().unwrap()).unwrap(); + + let c = a & b; + let dec: bool = c.decrypt(&cks); + + assert_eq!(clear_a && clear_b, dec); + } + } + } +} diff --git a/tfhe/src/high_level_api/tests/mod.rs b/tfhe/src/high_level_api/tests/mod.rs index 6b918cbad..5de14f3c5 100644 --- a/tfhe/src/high_level_api/tests/mod.rs +++ b/tfhe/src/high_level_api/tests/mod.rs @@ -1,3 +1,4 @@ +mod cpk_re_randomization; #[cfg(feature = "gpu")] mod gpu_selection; mod noise_distribution; @@ -197,8 +198,15 @@ 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, None, Tag::default()); + let client_key = ClientKey::from_raw_parts( + shortint_key.into(), + None, + None, + None, + None, + None, + Tag::default(), + ); let sks = ServerKey::new(&client_key); let clear_a = 1344u32; diff --git a/tfhe/src/high_level_api/traits.rs b/tfhe/src/high_level_api/traits.rs index d6402802c..869ed3312 100644 --- a/tfhe/src/high_level_api/traits.rs +++ b/tfhe/src/high_level_api/traits.rs @@ -1,6 +1,7 @@ use std::ops::RangeBounds; use crate::error::InvalidRangeError; +pub use crate::high_level_api::re_randomization::ReRandomize; use crate::high_level_api::ClientKey; use crate::{FheBool, Tag}; diff --git a/tfhe/src/high_level_api/upgrade.rs b/tfhe/src/high_level_api/upgrade.rs index 3f70975b7..eb3e9cd67 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, _, nsk, cnsk, tag) = ck.into_raw_parts(); - let ck = ClientKey::from_raw_parts(cks, pk, common_cck, nsk, cnsk, tag); + let (cks, pk, _, nsk, cnsk, cpkrndp, tag) = ck.into_raw_parts(); + let ck = ClientKey::from_raw_parts(cks, pk, common_cck, nsk, cnsk, cpkrndp, tag); let sk = CompressedServerKey::new(&ck); assert_eq!(sk.tag().as_u64(), 0); diff --git a/tfhe/src/high_level_api/utils.rs b/tfhe/src/high_level_api/utils.rs index 3fb21de05..3ec4cd148 100644 --- a/tfhe/src/high_level_api/utils.rs +++ b/tfhe/src/high_level_api/utils.rs @@ -1,5 +1,6 @@ use crate::high_level_api::integers::signed::FheIntId; use crate::high_level_api::integers::unsigned::FheUintId; +use crate::high_level_api::re_randomization::ReRandomizationMetadata; use crate::integer::ciphertext::{DataKind, Expandable}; use crate::integer::BooleanBlock; use crate::shortint::Ciphertext; @@ -18,10 +19,11 @@ impl Expandable for FheUint { DataKind::Unsigned(_) => { let stored_num_bits = num_bits_of_blocks(&blocks) as usize; if stored_num_bits == Id::num_bits() { - // The expander will be responsible for setting the correct tag + // The expander will be responsible for setting the correct tag and metadata Ok(Self::new( crate::integer::RadixCiphertext::from(blocks), Tag::default(), + ReRandomizationMetadata::default(), )) } else { Err(crate::error!( @@ -69,6 +71,7 @@ impl Expandable for FheInt { Ok(Self::new( crate::integer::SignedRadixCiphertext::from(blocks), Tag::default(), + ReRandomizationMetadata::default(), )) } else { Err(crate::error!( @@ -110,8 +113,12 @@ impl Expandable for FheBool { // We know the value is a boolean one (via the data kind) boolean_block.0.degree = crate::shortint::ciphertext::Degree::new(1); - // The expander will be responsible for setting the correct tag - Ok(Self::new(boolean_block, Tag::default())) + // The expander will be responsible for setting the correct tag and metadata + Ok(Self::new( + boolean_block, + Tag::default(), + ReRandomizationMetadata::default(), + )) } DataKind::String { .. } => Err(crate::Error::new( "Tried to expand a FheBool while a string is stored in this slot".to_string(), diff --git a/tfhe/src/integer/ciphertext/base.rs b/tfhe/src/integer/ciphertext/base.rs index fb35a5aed..c5d4b2415 100644 --- a/tfhe/src/integer/ciphertext/base.rs +++ b/tfhe/src/integer/ciphertext/base.rs @@ -7,6 +7,9 @@ use crate::integer::backward_compatibility::ciphertext::{ use crate::integer::block_decomposition::{ BlockRecomposer, RecomposableFrom, RecomposableSignedInteger, }; +use crate::integer::ciphertext::{re_randomize_ciphertext_blocks, ReRandomizationSeed}; +use crate::integer::key_switching_key::KeySwitchingKeyMaterialView; +use crate::integer::CompactPublicKey; use crate::shortint::ciphertext::NotTrivialCiphertextError; use crate::shortint::parameters::CiphertextConformanceParams; use crate::shortint::Ciphertext; @@ -119,6 +122,22 @@ impl RadixCiphertext { bits_in_block, )) } + + pub fn re_randomize( + &mut self, + compact_public_key: &CompactPublicKey, + key_switching_key_material: &KeySwitchingKeyMaterialView, + seed: ReRandomizationSeed, + ) -> crate::Result<()> { + re_randomize_ciphertext_blocks( + &mut self.blocks, + compact_public_key, + key_switching_key_material, + seed, + )?; + + Ok(()) + } } /// Structure containing a ciphertext in radix decomposition @@ -227,6 +246,22 @@ impl SignedRadixCiphertext { bits_in_block, )) } + + pub fn re_randomize( + &mut self, + compact_public_key: &CompactPublicKey, + key_switching_key_material: &KeySwitchingKeyMaterialView, + seed: ReRandomizationSeed, + ) -> crate::Result<()> { + re_randomize_ciphertext_blocks( + &mut self.blocks, + compact_public_key, + key_switching_key_material, + seed, + )?; + + Ok(()) + } } /// Structure containing a ciphertext in CRT decomposition. diff --git a/tfhe/src/integer/ciphertext/boolean_value.rs b/tfhe/src/integer/ciphertext/boolean_value.rs index 08383cb07..0626530b9 100644 --- a/tfhe/src/integer/ciphertext/boolean_value.rs +++ b/tfhe/src/integer/ciphertext/boolean_value.rs @@ -1,6 +1,8 @@ use super::{IntegerCiphertext, IntegerRadixCiphertext}; use crate::integer::backward_compatibility::ciphertext::BooleanBlockVersions; -use crate::integer::{RadixCiphertext, ServerKey}; +use crate::integer::ciphertext::{re_randomize_ciphertext_blocks, ReRandomizationSeed}; +use crate::integer::key_switching_key::KeySwitchingKeyMaterialView; +use crate::integer::{CompactPublicKey, RadixCiphertext, ServerKey}; use crate::shortint::ciphertext::NotTrivialCiphertextError; use crate::shortint::Ciphertext; use serde::{Deserialize, Serialize}; @@ -168,6 +170,22 @@ impl BooleanBlock { pub fn is_trivial(&self) -> bool { self.0.is_trivial() } + + pub fn re_randomize( + &mut self, + compact_public_key: &CompactPublicKey, + key_switching_key_material: &KeySwitchingKeyMaterialView, + seed: ReRandomizationSeed, + ) -> crate::Result<()> { + re_randomize_ciphertext_blocks( + core::slice::from_mut(&mut self.0), + compact_public_key, + key_switching_key_material, + seed, + )?; + + Ok(()) + } } impl AsRef for BooleanBlock { diff --git a/tfhe/src/integer/ciphertext/integer_ciphertext.rs b/tfhe/src/integer/ciphertext/integer_ciphertext.rs index fe1f5b2e1..3548474f3 100644 --- a/tfhe/src/integer/ciphertext/integer_ciphertext.rs +++ b/tfhe/src/integer/ciphertext/integer_ciphertext.rs @@ -96,3 +96,21 @@ impl IntegerCiphertext for CrtCiphertext { self.moduli.clone() } } + +// Yet another trait, this is to avoid breaking the logic with IntegerCiphertext which BooleanBlock +// does not implement for good reasons IIRC. +pub trait AsShortintCiphertextSlice { + fn as_ciphertext_slice(&self) -> &[Ciphertext]; +} + +impl AsShortintCiphertextSlice for T { + fn as_ciphertext_slice(&self) -> &[Ciphertext] { + self.blocks() + } +} + +impl AsShortintCiphertextSlice for super::BooleanBlock { + fn as_ciphertext_slice(&self) -> &[Ciphertext] { + core::slice::from_ref(&self.0) + } +} diff --git a/tfhe/src/integer/ciphertext/mod.rs b/tfhe/src/integer/ciphertext/mod.rs index de6452ec6..907094349 100644 --- a/tfhe/src/integer/ciphertext/mod.rs +++ b/tfhe/src/integer/ciphertext/mod.rs @@ -6,7 +6,10 @@ mod compressed_ciphertext_list; mod compressed_modulus_switched_ciphertext; mod compressed_noise_squashed_ciphertext_list; mod integer_ciphertext; +mod re_randomization; mod squashed_noise; +#[cfg(test)] +mod test; mod utils; pub use base::*; @@ -17,5 +20,6 @@ pub use compressed_ciphertext_list::*; pub use compressed_modulus_switched_ciphertext::*; pub use compressed_noise_squashed_ciphertext_list::*; pub use integer_ciphertext::*; +pub use re_randomization::*; pub use squashed_noise::*; pub use utils::*; diff --git a/tfhe/src/integer/ciphertext/re_randomization.rs b/tfhe/src/integer/ciphertext/re_randomization.rs new file mode 100644 index 000000000..f6c199143 --- /dev/null +++ b/tfhe/src/integer/ciphertext/re_randomization.rs @@ -0,0 +1,154 @@ +// This is linked to the standard ciphertext but has special code in it + +use crate::core_crypto::commons::math::random::XofSeed; +use crate::integer::ciphertext::AsShortintCiphertextSlice; +use crate::integer::key_switching_key::KeySwitchingKeyMaterialView; +use crate::integer::CompactPublicKey; +pub use crate::shortint::ciphertext::{ReRandomizationSeed, ReRandomizationSeedHasher}; +use crate::shortint::Ciphertext; +use crate::Result; + +/// The context that will be hashed and used to generate unique [`ReRandomizationSeed`]. +pub struct ReRandomizationContext { + /// The inner hasher + inner_context: crate::shortint::ciphertext::ReRandomizationContext, + /// The number of integer ciphertexts added to the context. This will define the number of + /// seeds that can be drawn from it + ct_count: u64, + /// Temporary buffer with all the individual shortint cts coefficients that will be hashed in + /// the context + ct_coeffs_buffer: Vec, + /// Temporary buffer with all the ciphertext metadata + meta_buffer: Vec, + /// A piece of data that should be unique to the function being called + fn_description: Vec, +} + +impl ReRandomizationContext { + /// Create a new re-randomization context with the default seed hasher (blake3). + /// + /// `rerand_seeder_domain_separator` is the domain separator that will be fed into the + /// seed generator. + /// `public_encryption_domain_separator` is the domain separator that will be used along this + /// seed to generate the encryptions of zero. + /// `fn_description` is a unique sequence of bytes that represents the functions called on the + /// re-randomized values. + /// + /// (See [`XofSeed`] for more information) + /// + /// # Example + /// ```rust + /// use tfhe::integer::ciphertext::ReRandomizationContext; + /// // Simulate a 256 bits nonce + /// let nonce: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); + /// let _re_rand_context = ReRandomizationContext::new( + /// *b"TFHE_Rrd", + /// [b"FheUint64+FheUint64".as_slice(), &nonce], + /// *b"TFHE_Enc" + /// ); + pub fn new<'a>( + rerand_seeder_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN], + fn_description: impl IntoIterator, + public_encryption_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN], + ) -> Self { + Self { + inner_context: crate::shortint::ciphertext::ReRandomizationContext::new( + rerand_seeder_domain_separator, + public_encryption_domain_separator, + ), + ct_coeffs_buffer: Vec::new(), + ct_count: 0, + meta_buffer: Vec::new(), + fn_description: fn_description.into_iter().flatten().copied().collect(), + } + } + + /// Create a new re-randomization context with the provided seed hasher. + pub fn new_with_hasher<'a>( + fn_description: impl IntoIterator, + public_encryption_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN], + seed_hasher: ReRandomizationSeedHasher, + ) -> Self { + Self { + inner_context: crate::shortint::ciphertext::ReRandomizationContext::new_with_hasher( + public_encryption_domain_separator, + seed_hasher, + ), + ct_coeffs_buffer: Vec::new(), + ct_count: 0, + meta_buffer: Vec::new(), + fn_description: fn_description.into_iter().flatten().copied().collect(), + } + } + + /// Add a new integer ciphertext to the context. + /// + /// The ciphertexts added like this will be stored in a temporary buffer and only hashed during + /// the "finalize" step + pub fn add_ciphertext(&mut self, ciphertext: &T) { + self.ct_coeffs_buffer.extend( + ciphertext + .as_ciphertext_slice() + .iter() + .flat_map(|ct| ct.ct.as_ref()), + ); + self.ct_count += 1; + } + + /// Add a metadata buffer to the context. + /// + /// These bytes will be added to a temporary buffer and will only be hashed during the + /// "finalize" step + pub fn add_bytes(&mut self, data: &[u8]) { + self.meta_buffer.extend_from_slice(data); + } + + /// Consumes the context to instantiate a seed generator + pub fn finalize(mut self) -> ReRandomizationSeedGen { + self.inner_context + .add_ciphertext_data_slice(&self.ct_coeffs_buffer); + self.inner_context.add_bytes(&self.meta_buffer); + self.inner_context.add_bytes(&self.fn_description); + + ReRandomizationSeedGen { + inner: self.inner_context.finalize(), + remaining_seeds_count: self.ct_count, + } + } +} + +/// A generator that can be used to obtain seeds needed to re-randomize individual ciphertexts. +/// +/// This will refuse to generate more seeds than the number of ciphertext added into the context. +pub struct ReRandomizationSeedGen { + inner: crate::shortint::ciphertext::ReRandomizationSeedGen, + remaining_seeds_count: u64, +} + +impl ReRandomizationSeedGen { + /// Generate the next seed from the seeder. + /// + /// Returns an error if more seeds have been generated than the number of ciphertext added into + /// the context. + pub fn next_seed(&mut self) -> Result { + if self.remaining_seeds_count > 0 { + self.remaining_seeds_count -= 1; + Ok(self.inner.next_seed()) + } else { + Err(crate::error!("Trying to draw more seeds than the number of ciphertexts that were added to the context")) + } + } +} + +pub(crate) fn re_randomize_ciphertext_blocks( + blocks: &mut [Ciphertext], + compact_public_key: &CompactPublicKey, + key_switching_key_material: &KeySwitchingKeyMaterialView, + seed: ReRandomizationSeed, +) -> crate::Result<()> { + compact_public_key.key.re_randomize_ciphertexts( + blocks, + &key_switching_key_material.material, + seed, + ) +} diff --git a/tfhe/src/integer/ciphertext/test.rs b/tfhe/src/integer/ciphertext/test.rs new file mode 100644 index 000000000..f0df047aa --- /dev/null +++ b/tfhe/src/integer/ciphertext/test.rs @@ -0,0 +1,162 @@ +use crate::integer::ciphertext::compressed_ciphertext_list::*; +use crate::integer::ciphertext::ReRandomizationContext; +use crate::integer::key_switching_key::{KeySwitchingKeyBuildHelper, KeySwitchingKeyMaterial}; +use crate::integer::{ + gen_keys, BooleanBlock, CompactPrivateKey, CompactPublicKey, IntegerKeyKind, RadixCiphertext, + SignedRadixCiphertext, +}; +use crate::shortint::parameters::test_params::{ + TEST_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, + TEST_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, + TEST_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, + TEST_PARAM_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV2, +}; +use crate::shortint::ShortintParameterSet; +use itertools::Itertools; +use rand::Rng; + +const NB_TESTS: usize = 10; +const NUM_BLOCKS: usize = 32; + +#[test] +fn test_ciphertext_re_randomization_after_compression() { + let params = TEST_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128.into(); + let comp_params = TEST_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + let cpk_params = TEST_PARAM_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV2; + let ks_params = TEST_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + + let (cks, sks) = gen_keys::(params, IntegerKeyKind::Radix); + + let private_compression_key = cks.new_compression_private_key(comp_params); + + let (compression_key, decompression_key) = + cks.new_compression_decompression_keys(&private_compression_key); + + let cpk_private_key = CompactPrivateKey::new(cpk_params); + let cpk = CompactPublicKey::new(&cpk_private_key); + let ksk_material: KeySwitchingKeyMaterial = + KeySwitchingKeyBuildHelper::new((&cpk_private_key, None), (&cks, &sks), ks_params).into(); + let ksk_material = ksk_material.as_view(); + + let rerand_domain_separator = *b"TFHE_Rrd"; + let compact_public_encryption_domain_separator = *b"TFHE_Enc"; + let metadata = b"lol".as_slice(); + + let mut rng = rand::thread_rng(); + + let message_modulus: u128 = cks.parameters().message_modulus().0 as u128; + + // Unsigned + let modulus = message_modulus.pow(NUM_BLOCKS as u32); + for _ in 0..NB_TESTS { + let message = rng.gen::() % modulus; + + let ct = cks.encrypt_radix(message, NUM_BLOCKS); + + let mut builder = CompressedCiphertextListBuilder::new(); + + builder.push(ct); + + let compressed = builder.build(&compression_key); + + let decompressed: RadixCiphertext = compressed.get(0, &decompression_key).unwrap().unwrap(); + + let mut re_randomizer_context = ReRandomizationContext::new( + rerand_domain_separator, + [metadata], + compact_public_encryption_domain_separator, + ); + + re_randomizer_context.add_ciphertext(&decompressed); + + let mut seed_gen = re_randomizer_context.finalize(); + + let mut re_randomized = decompressed.clone(); + re_randomized + .re_randomize(&cpk, &ksk_material, seed_gen.next_seed().unwrap()) + .unwrap(); + + assert_ne!(decompressed, re_randomized); + + let decrypted: u128 = cks.decrypt_radix(&re_randomized); + assert_eq!(decrypted, message); + } + + // Signed + let modulus = message_modulus.pow((NUM_BLOCKS - 1) as u32) as i128; + for _ in 0..NB_TESTS { + let message = rng.gen::() % modulus; + + let ct = cks.encrypt_signed_radix(message, NUM_BLOCKS); + + let mut builder = CompressedCiphertextListBuilder::new(); + + builder.push(ct); + + let compressed = builder.build(&compression_key); + + let decompressed: SignedRadixCiphertext = + compressed.get(0, &decompression_key).unwrap().unwrap(); + + let mut re_randomizer_context = ReRandomizationContext::new( + rerand_domain_separator, + [metadata], + compact_public_encryption_domain_separator, + ); + + re_randomizer_context.add_ciphertext(&decompressed); + + let mut seed_gen = re_randomizer_context.finalize(); + + let mut re_randomized = decompressed.clone(); + re_randomized + .re_randomize(&cpk, &ksk_material, seed_gen.next_seed().unwrap()) + .unwrap(); + + assert_ne!(decompressed, re_randomized); + + let decrypted: i128 = cks.decrypt_signed_radix(&decompressed); + assert_eq!(decrypted, message); + } + + // Boolean + for _ in 0..NB_TESTS { + let messages = [false, true]; + + let cts = messages + .iter() + .map(|message| cks.encrypt_bool(*message)) + .collect_vec(); + + let mut builder = CompressedCiphertextListBuilder::new(); + + builder.extend(cts.into_iter()); + + let compressed = builder.build(&compression_key); + + for (i, message) in messages.iter().enumerate() { + let decompressed: BooleanBlock = + compressed.get(i, &decompression_key).unwrap().unwrap(); + + let mut re_randomizer_context = ReRandomizationContext::new( + rerand_domain_separator, + [metadata], + compact_public_encryption_domain_separator, + ); + + re_randomizer_context.add_ciphertext(&decompressed); + + let mut seed_gen = re_randomizer_context.finalize(); + + let mut re_randomized = decompressed.clone(); + re_randomized + .re_randomize(&cpk, &ksk_material, seed_gen.next_seed().unwrap()) + .unwrap(); + + assert_ne!(decompressed, re_randomized); + + let decrypted = cks.decrypt_bool(&decompressed); + assert_eq!(decrypted, *message); + } + } +} diff --git a/tfhe/src/shortint/ciphertext/mod.rs b/tfhe/src/shortint/ciphertext/mod.rs index 9fe7dd4bb..82ceb2323 100644 --- a/tfhe/src/shortint/ciphertext/mod.rs +++ b/tfhe/src/shortint/ciphertext/mod.rs @@ -3,6 +3,7 @@ mod compact_list; mod compressed; mod compressed_ciphertext_list; mod compressed_modulus_switched_ciphertext; +mod re_randomization; mod squashed_noise; mod standard; #[cfg(feature = "zk-pok")] @@ -13,6 +14,7 @@ pub use compact_list::*; pub use compressed::*; pub use compressed_ciphertext_list::*; pub use compressed_modulus_switched_ciphertext::*; +pub use re_randomization::*; pub use squashed_noise::*; pub use standard::*; #[cfg(feature = "zk-pok")] diff --git a/tfhe/src/shortint/ciphertext/re_randomization.rs b/tfhe/src/shortint/ciphertext/re_randomization.rs new file mode 100644 index 000000000..22425ce46 --- /dev/null +++ b/tfhe/src/shortint/ciphertext/re_randomization.rs @@ -0,0 +1,431 @@ +//! Update the random elements (mask and noise) of a ciphertext without changing its plaintext value +//! +//! This works by generating encrypted zeros using a public key, that will be added to the input +//! ciphertexts + +use crate::core_crypto::algorithms::{ + encrypt_lwe_compact_ciphertext_list_with_compact_public_key, keyswitch_lwe_ciphertext, + lwe_ciphertext_add_assign, +}; +use crate::core_crypto::commons::generators::{ + DeterministicSeeder, EncryptionRandomGenerator, SecretRandomGenerator, +}; +use crate::core_crypto::commons::math::random::{DefaultRandomGenerator, Seeder, XofSeed}; +use crate::core_crypto::commons::parameters::{LweCiphertextCount, PlaintextCount}; +use crate::core_crypto::commons::traits::*; +use crate::core_crypto::entities::{LweCiphertext, LweCompactCiphertextList, PlaintextList}; +use crate::shortint::ciphertext::NoiseLevel; +use crate::shortint::key_switching_key::KeySwitchingKeyMaterialView; +use crate::shortint::{Ciphertext, CompactPublicKey, PBSOrder}; + +use rayon::prelude::*; +use sha3::digest::{ExtendableOutput, Update}; +use std::io::Read; + +/// Size of the re-randomization seed in bits +const RERAND_SEED_BITS: usize = 256; + +/// The XoF algorithm used to generate the re-randomization seed +#[derive(Copy, Clone, Default)] +pub enum ReRandomizationHashAlgo { + /// Used for NIST compliance + Shake256, + /// Faster, should be preferred unless you have specific requirements + #[default] + Blake3, +} + +/// The hash state used for the re-randomization seed generation +#[derive(Clone)] +// blake3 is the larger variant but we expect it to be used more in performance sensitive contexts +#[allow(clippy::large_enum_variant)] +pub enum ReRandomizationSeedHasher { + Shake256(sha3::Shake256), + Blake3(blake3::Hasher), +} + +impl ReRandomizationSeedHasher { + /// Create a new hash state for the provided algorithm + pub fn new( + algo: ReRandomizationHashAlgo, + rerand_root_seed_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN], + ) -> Self { + let mut hasher = match algo { + ReRandomizationHashAlgo::Shake256 => Self::Shake256(sha3::Shake256::default()), + ReRandomizationHashAlgo::Blake3 => Self::Blake3(blake3::Hasher::default()), + }; + + hasher.update(&rerand_root_seed_domain_separator); + hasher + } + + /// Update the hash state with new data + fn update(&mut self, data: &[u8]) { + match self { + Self::Shake256(hasher) => hasher.update(data), + Self::Blake3(hasher) => { + hasher.update(data); + } + } + } + + /// Consume the state to generate a seed + fn finalize(self) -> [u8; RERAND_SEED_BITS / 8] { + let mut res = [0; RERAND_SEED_BITS / 8]; + match self { + Self::Shake256(hasher) => { + let mut reader = hasher.finalize_xof(); + reader + .read_exact(&mut res) + .expect("XoF reader should not EoF"); + } + Self::Blake3(hasher) => { + let mut reader = hasher.finalize_xof(); + reader + .read_exact(&mut res) + .expect("XoF reader should not EoF"); + } + } + res + } +} + +impl From for ReRandomizationSeedHasher { + fn from(value: sha3::Shake256) -> Self { + Self::Shake256(value) + } +} + +impl From for ReRandomizationSeedHasher { + fn from(value: blake3::Hasher) -> Self { + Self::Blake3(value) + } +} + +/// A seed that can be used to re-randomize a ciphertext +/// +/// This type cannot be cloned or copied, as a seed should only be used once. +pub struct ReRandomizationSeed(pub(crate) XofSeed); + +/// The context that will be hashed and used to generate unique [`ReRandomizationSeed`]. +/// +/// At this level, the context will directly hash any data passed to it. +/// This means that the order in which the data will be hashed matches exactly the order in which +/// the different `add_*` functions are called +pub struct ReRandomizationContext { + hash_state: ReRandomizationSeedHasher, + public_encryption_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN], +} + +impl ReRandomizationContext { + /// Create a new re-randomization context with the default seed hasher (blake3). + /// + /// `rerand_seeder_domain_separator` is the domain separator that will be fed into the + /// seed generator. + /// `public_encryption_domain_separator` is the domain separator that will be used along this + /// seed to generate the encryptions of zero. + /// + /// (See [`XofSeed`] for more information) + /// + /// # Example + /// ```rust + /// use tfhe::shortint::ciphertext::ReRandomizationContext; + /// // Simulate a 256 bits nonce + /// let nonce: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); + /// let _re_rand_context = ReRandomizationContext::new( + /// *b"TFHE_Rrd", + /// *b"TFHE_Enc" + /// ); + pub fn new( + rerand_seeder_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN], + public_encryption_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN], + ) -> Self { + let seed_hasher = ReRandomizationSeedHasher::new( + ReRandomizationHashAlgo::default(), + rerand_seeder_domain_separator, + ); + + Self::new_with_hasher(public_encryption_domain_separator, seed_hasher) + } + + /// Create a new re-randomization context with the provided seed hasher. + pub fn new_with_hasher( + public_encryption_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN], + seed_hasher: ReRandomizationSeedHasher, + ) -> Self { + Self { + hash_state: seed_hasher, + public_encryption_domain_separator, + } + } + + /// Adds a new ciphertext to the re-randomization context + pub fn add_ciphertext(&mut self, ciphertext: &Ciphertext) { + self.add_ciphertext_iterator([ciphertext]); + } + + /// Adds bytes to the re-randomization context + pub fn add_bytes(&mut self, data: &[u8]) { + self.hash_state.update(data); + } + + pub fn add_ciphertext_iterator<'a, I>(&mut self, iter: I) + where + I: IntoIterator, + { + // Blake3 algorithm is faster when fed with larger chunks of data, so we copy all the + // ciphertexts into a single buffer. + + // We try to estimate the buffer size and preallocate it. This is an estimate based on the + // following assumptions: + // - all ciphertexts have the same size + // - the iterator size hint is correct + // This is not critical as a bad estimate only results in reallocations in the worst case. + let mut iter = iter.into_iter(); + let Some(first) = iter.next() else { + return; + }; + + let hint = iter.size_hint(); + // Use the max iterator size if it exists, or default to the min one. + let iter_len = hint.1.unwrap_or(hint.0); + let tot_len = first.ct.as_ref().len() * iter_len; + let mut copied: Vec = Vec::with_capacity(tot_len); + + copied.extend(first.ct.as_ref()); + for ciphertext in iter { + copied.extend(ciphertext.ct.as_ref()); + } + + self.add_ciphertext_data_slice(&copied); + } + + pub(crate) fn add_ciphertext_data_slice(&mut self, slice: &[u64]) { + self.hash_state.update(bytemuck::cast_slice(slice)); + } + + /// Consumes the context to create a seed generator + pub fn finalize(self) -> ReRandomizationSeedGen { + let Self { + hash_state, + public_encryption_domain_separator, + } = self; + + ReRandomizationSeedGen { + hash_state, + next_seed_index: 0, + public_encryption_domain_separator, + } + } +} + +/// A generator that can be used to obtain seeds needed to re-randomize individual ciphertexts +pub struct ReRandomizationSeedGen { + hash_state: ReRandomizationSeedHasher, + next_seed_index: u64, + public_encryption_domain_separator: [u8; XofSeed::DOMAIN_SEP_LEN], +} + +impl ReRandomizationSeedGen { + pub fn next_seed(&mut self) -> ReRandomizationSeed { + let current_seed_index = self.next_seed_index; + self.next_seed_index += 1; + + let mut hash_state = self.hash_state.clone(); + hash_state.update(¤t_seed_index.to_le_bytes()); + + let seed_256 = hash_state.finalize(); + + ReRandomizationSeed(XofSeed::new( + seed_256.to_vec(), + self.public_encryption_domain_separator, + )) + } +} + +impl CompactPublicKey { + /// Re-randomize a list of ciphertexts using the provided seed and compact public key + /// + /// The key and seed are used to generate encryptions of zero that will be added to the input + /// ciphertexts + pub fn re_randomize_ciphertexts( + &self, + cts: &mut [Ciphertext], + key_switching_key_material: &KeySwitchingKeyMaterialView, + seed: ReRandomizationSeed, + ) -> crate::Result<()> { + let ksk_pbs_order = key_switching_key_material.destination_key.into_pbs_order(); + let ksk_output_lwe_size = key_switching_key_material + .key_switching_key + .output_lwe_size(); + + if let Some(msg) = cts.iter().find_map(|ct| { + if ct.atomic_pattern.pbs_order() != ksk_pbs_order { + Some( + "Mismatched PBSOrder between Ciphertext being re-randomized and provided \ + KeySwitchingKeyMaterialView.", + ) + } else if ksk_output_lwe_size != ct.ct.lwe_size() { + Some( + "Mismatched LweSwize between Ciphertext being re-randomized and provided \ + KeySwitchingKeyMaterialView.", + ) + } else if ct.noise_level() != NoiseLevel::NOMINAL { + Some("Tried to re-randomize a Ciphertext with non-nominal NoiseLevel.") + } else { + None + } + }) { + return Err(crate::error!("{}", msg)); + } + + if ksk_pbs_order != PBSOrder::KeyswitchBootstrap { + // message is ok since we know that ksk order == cts order + return Err(crate::error!( + "Tried to re-randomize a Ciphertext with unsupported PBSOrder. \ + Required PBSOrder::KeyswitchBootstrap.", + )); + } + + if key_switching_key_material.cast_rshift != 0 { + return Err(crate::error!( + "Tried to re-randomize a Ciphertext using KeySwitchingKeyMaterialView \ + with non-zero cast_rshift, this is unsupported.", + )); + } + + if key_switching_key_material + .key_switching_key + .input_key_lwe_dimension() + != self.parameters().encryption_lwe_dimension + { + return Err(crate::error!( + "Mismatched LweDimension between provided CompactPublicKey and \ + KeySwitchingKeyMaterialView input LweDimension.", + )); + } + + // TODO: what do we do about this ? + let mut deterministic_seeder = DeterministicSeeder::::new(seed.0); + let mut secret_generator = + SecretRandomGenerator::::new(deterministic_seeder.seed()); + let mut encryption_generator = EncryptionRandomGenerator::::new( + deterministic_seeder.seed(), + &mut deterministic_seeder, + ); + + let mut encryption_of_zero = LweCompactCiphertextList::new( + 0, + self.parameters().encryption_lwe_dimension.to_lwe_size(), + LweCiphertextCount(cts.len()), + self.parameters().ciphertext_modulus, + ); + + let plaintext_list = PlaintextList::new( + 0, + PlaintextCount(encryption_of_zero.lwe_ciphertext_count().0), + ); + + let cpk_encryption_noise_distribution = self.parameters().encryption_noise_distribution; + + encrypt_lwe_compact_ciphertext_list_with_compact_public_key( + &self.key, + &mut encryption_of_zero, + &plaintext_list, + cpk_encryption_noise_distribution, + cpk_encryption_noise_distribution, + &mut secret_generator, + &mut encryption_generator, + ); + + let zero_lwes = encryption_of_zero.expand_into_lwe_ciphertext_list(); + + cts.par_iter_mut() + .zip(zero_lwes.par_iter()) + .for_each(|(ct, lwe_randomizer_cpk)| { + let mut lwe_randomizer_ksed = LweCiphertext::new( + 0, + key_switching_key_material + .key_switching_key + .output_lwe_size(), + key_switching_key_material + .key_switching_key + .ciphertext_modulus(), + ); + // Keyswitch used to convert from the cpk params to the compute ones. + // In theory, with a cpk made from the compute secret key, this keyswitch could be + // removed at the cost of an additional key. + keyswitch_lwe_ciphertext( + key_switching_key_material.key_switching_key, + &lwe_randomizer_cpk, + &mut lwe_randomizer_ksed, + ); + + lwe_ciphertext_add_assign(&mut ct.ct, &lwe_randomizer_ksed); + }); + + Ok(()) + } +} + +#[cfg(test)] +mod test { + + use super::*; + use crate::shortint::parameters::test_params::{ + TEST_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, + TEST_PARAM_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV2, + }; + use crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS; + use crate::shortint::{gen_keys, CompactPrivateKey, KeySwitchingKey}; + + /// Test the case where we rerand more ciphertexts that what can be stored in one cpk lwe + #[test] + fn test_rerand_large_ct_count_ci_run_filter() { + let compute_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS; + let pke_params = TEST_PARAM_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV2; + let ks_params = TEST_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + + let (cks, sks) = gen_keys(compute_params); + let privk = CompactPrivateKey::new(pke_params); + let pubk = CompactPublicKey::new(&privk); + let ksk = KeySwitchingKey::new((&privk, None), (&cks, &sks), ks_params); + + let pke_lwe_dim = pke_params.encryption_lwe_dimension.0; + + let msg1 = 1; + let msg2 = 2; + + let mut cts = Vec::with_capacity(pke_lwe_dim * 2); + + for _ in 0..pke_lwe_dim { + let ct1 = cks.encrypt(msg1); + cts.push(ct1); + let ct2 = cks.encrypt(msg2); + cts.push(ct2); + } + + let nonce: [u8; 256 / 8] = core::array::from_fn(|_| rand::random()); + let mut re_rand_context = ReRandomizationContext::new(*b"TFHE_Rrd", *b"TFHE_Enc"); + + re_rand_context.add_ciphertext_iterator(&cts); + re_rand_context.add_bytes(b"ct_radix"); + re_rand_context.add_bytes(b"FheUint4".as_slice()); + re_rand_context.add_bytes(&nonce); + let mut seeder = re_rand_context.finalize(); + + pubk.re_randomize_ciphertexts( + &mut cts, + &ksk.key_switching_key_material.as_view(), + seeder.next_seed(), + ) + .unwrap(); + + for pair in cts.chunks(2) { + let sum = sks.add(&pair[0], &pair[1]); + let dec = cks.decrypt(&sum); + + assert_eq!(dec, msg1 + msg2); + } + } +} diff --git a/tfhe/src/shortint/ciphertext/standard.rs b/tfhe/src/shortint/ciphertext/standard.rs index 1e83d2afc..2c7b64420 100644 --- a/tfhe/src/shortint/ciphertext/standard.rs +++ b/tfhe/src/shortint/ciphertext/standard.rs @@ -5,7 +5,10 @@ use crate::conformance::ParameterSetConformant; use crate::core_crypto::entities::*; use crate::core_crypto::prelude::{allocate_and_trivially_encrypt_new_lwe_ciphertext, LweSize}; use crate::shortint::backward_compatibility::ciphertext::CiphertextVersions; +use crate::shortint::ciphertext::ReRandomizationSeed; +use crate::shortint::key_switching_key::KeySwitchingKeyMaterialView; use crate::shortint::parameters::{AtomicPatternKind, CarryModulus, MessageModulus}; +use crate::shortint::public_key::compact::CompactPublicKey; use crate::shortint::{CiphertextModulus, PaddingBit, ShortintEncoding}; use serde::{Deserialize, Serialize}; use std::fmt::Debug; @@ -249,6 +252,30 @@ impl Ciphertext { Err(NotTrivialCiphertextError) } } + + /// This function can be called after decompressing a [`Ciphertext`] from a + /// [`CompressedCiphertextList`](super::compressed_ciphertext_list::CompressedCiphertextList) to + /// re-randomize it before any computations. + /// + /// This function only supports [`PBSOrder::KeyswitchBootstrap`] ordered + /// [`Ciphertext`]/[`ServerKey`](crate::shortint::ServerKey). + /// + /// It uses a [`CompactPublicKey`] to generate a new encryption of 0, a + /// [`KeySwitchingKeyMaterialView`] is required to keyswitch between the secret key used to + /// generate the [`CompactPublicKey`] to the "big"/post PBS/GLWE secret key from the + /// [`ServerKey`](crate::shortint::ServerKey). + pub fn re_randomize_with_compact_public_key_encryption( + &mut self, + compact_public_key: &CompactPublicKey, + key_switching_key_material: &KeySwitchingKeyMaterialView<'_>, + seed: ReRandomizationSeed, + ) -> crate::Result<()> { + compact_public_key.re_randomize_ciphertexts( + std::slice::from_mut(self), + key_switching_key_material, + seed, + ) + } } pub(crate) fn unchecked_create_trivial_with_lwe_size( @@ -285,6 +312,16 @@ pub(crate) fn unchecked_create_trivial_with_lwe_size( #[cfg(test)] mod tests { use super::*; + use crate::shortint::ciphertext::ReRandomizationContext; + use crate::shortint::key_switching_key::KeySwitchingKeyBuildHelper; + use crate::shortint::keycache::KEY_CACHE; + use crate::shortint::parameters::test_params::{ + TEST_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, + TEST_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, + TEST_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, + TEST_PARAM_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV2, + }; + use crate::shortint::public_key::compact::CompactPrivateKey; use crate::shortint::CiphertextModulus; #[test] @@ -382,4 +419,56 @@ mod tests { c1.clone_from(&c2); assert_eq!(c1, c2); } + + #[test] + fn test_re_randomize_ciphertext_ci_run_filter() { + let params = TEST_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + let comp_params = TEST_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + let cpk_params = TEST_PARAM_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128_ZKV2; + let ks_params = TEST_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + + let key_entry = KEY_CACHE.get_from_param(params); + // Generate the client key and the server key: + let (cks, sks) = (key_entry.client_key(), key_entry.server_key()); + let cpk_private_key = CompactPrivateKey::new(cpk_params); + let cpk = CompactPublicKey::new(&cpk_private_key); + let ksk_material = + KeySwitchingKeyBuildHelper::new((&cpk_private_key, None), (cks, sks), ks_params) + .key_switching_key_material; + let ksk_material = ksk_material.as_view(); + + let private_compression_key = cks.new_compression_private_key(comp_params); + let (compression_key, decompression_key) = + cks.new_compression_decompression_keys(&private_compression_key); + + let msg = cks.parameters().message_modulus().0 - 1; + + for _ in 0..10 { + let ct = cks.encrypt(msg); + + let compressed = compression_key.compress_ciphertexts_into_list(&[ct]); + + let decompressed = decompression_key.unpack(&compressed, 0).unwrap(); + + let mut re_randomizer_context = ReRandomizationContext::new(*b"TFHE_Rrd", *b"TFHE_Enc"); + re_randomizer_context.add_ciphertext(&decompressed); + + let mut seed_gen = re_randomizer_context.finalize(); + + let seed = seed_gen.next_seed(); + + let mut re_randomized = decompressed.clone(); + re_randomized + .re_randomize_with_compact_public_key_encryption(&cpk, &ksk_material, seed) + .unwrap(); + + assert_ne!(decompressed, re_randomized); + + let pbsed = sks.bitand(&re_randomized, &re_randomized); + + let dec = cks.decrypt_message_and_carry(&pbsed); + + assert_eq!(dec, msg); + } + } } diff --git a/tfhe/src/shortint/parameters/aliases.rs b/tfhe/src/shortint/parameters/aliases.rs index 54cf64aa5..ece3fd696 100644 --- a/tfhe/src/shortint/parameters/aliases.rs +++ b/tfhe/src/shortint/parameters/aliases.rs @@ -14,7 +14,10 @@ use current_params::classic::gaussian::p_fail_2_minus_64::ks_pbs::V1_4_PARAM_MES use current_params::classic::tuniform::p_fail_2_minus_128::ks_pbs::V1_4_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; use current_params::classic::tuniform::p_fail_2_minus_64::ks_pbs::V1_4_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use current_params::compact_public_key_only::p_fail_2_minus_128::ks_pbs::V1_4_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; -use current_params::key_switching::p_fail_2_minus_128::ks_pbs::V1_4_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; +use current_params::key_switching::p_fail_2_minus_128::ks_pbs::{ + V1_4_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, + V1_4_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, +}; use current_params::list_compression::p_fail_2_minus_128::{ V1_4_COMP_PARAM_GPU_MULTI_BIT_GROUP_4_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, V1_4_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M128, @@ -114,8 +117,16 @@ pub const PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128: CompactPublicKeyEnc // PKE To Compute Keyswitch pub const PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128: ShortintKeySwitchingParameters = + PARAM_KEYSWITCH_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + +pub const PARAM_KEYSWITCH_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128: + ShortintKeySwitchingParameters = V1_4_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; +pub const PARAM_KEYSWITCH_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128: + ShortintKeySwitchingParameters = + V1_4_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; + // Noise squashing pub const NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128: NoiseSquashingParameters = V1_4_NOISE_SQUASHING_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;