Skip to content

Commit b544192

Browse files
committed
feat(shortint): add compression for squashed noise ciphertexts
1 parent 5f82f31 commit b544192

File tree

14 files changed

+578
-36
lines changed

14 files changed

+578
-36
lines changed

tfhe/src/core_crypto/entities/lwe_packing_keyswitch_key.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -402,17 +402,19 @@ impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntit
402402
Self: 'this;
403403
}
404404

405-
pub struct LwePackingKeyswitchKeyConformanceParams {
405+
pub struct LwePackingKeyswitchKeyConformanceParams<Scalar: UnsignedInteger> {
406406
pub decomp_base_log: DecompositionBaseLog,
407407
pub decomp_level_count: DecompositionLevelCount,
408408
pub input_lwe_dimension: LweDimension,
409409
pub output_glwe_size: GlweSize,
410410
pub output_polynomial_size: PolynomialSize,
411-
pub ciphertext_modulus: CiphertextModulus<u64>,
411+
pub ciphertext_modulus: CiphertextModulus<Scalar>,
412412
}
413413

414-
impl<C: Container<Element = u64>> ParameterSetConformant for LwePackingKeyswitchKey<C> {
415-
type ParameterSet = LwePackingKeyswitchKeyConformanceParams;
414+
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ParameterSetConformant
415+
for LwePackingKeyswitchKey<C>
416+
{
417+
type ParameterSet = LwePackingKeyswitchKeyConformanceParams<Scalar>;
416418

417419
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
418420
let Self {

tfhe/src/core_crypto/entities/seeded_lwe_packing_keyswitch_key.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,8 +461,10 @@ impl<Scalar: UnsignedInteger, C: ContainerMut<Element = Scalar>> ContiguousEntit
461461
Self: 'this;
462462
}
463463

464-
impl<C: Container<Element = u64>> ParameterSetConformant for SeededLwePackingKeyswitchKey<C> {
465-
type ParameterSet = LwePackingKeyswitchKeyConformanceParams;
464+
impl<Scalar: UnsignedInteger, C: Container<Element = Scalar>> ParameterSetConformant
465+
for SeededLwePackingKeyswitchKey<C>
466+
{
467+
type ParameterSet = LwePackingKeyswitchKeyConformanceParams<Scalar>;
466468

467469
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
468470
let Self {

tfhe/src/integer/ciphertext/compressed_ciphertext_list.rs

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -172,29 +172,26 @@ mod tests {
172172

173173
#[test]
174174
fn test_empty_list_compression() {
175-
for params in [
176-
V1_0_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128.into(),
177-
V1_0_PARAM_MULTI_BIT_GROUP_2_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64.into(),
178-
] {
179-
let (cks, _) = gen_keys::<ShortintParameterSet>(params, IntegerKeyKind::Radix);
175+
let (cks, _) = gen_keys::<ShortintParameterSet>(
176+
TEST_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128.into(),
177+
IntegerKeyKind::Radix,
178+
);
180179

181-
let private_compression_key = cks.new_compression_private_key(
182-
V1_0_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128,
183-
);
180+
let private_compression_key = cks
181+
.new_compression_private_key(TEST_COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128);
184182

185-
let (compression_key, decompression_key) =
186-
cks.new_compression_decompression_keys(&private_compression_key);
183+
let (compression_key, decompression_key) =
184+
cks.new_compression_decompression_keys(&private_compression_key);
187185

188-
let builder = CompressedCiphertextListBuilder::new();
186+
let builder = CompressedCiphertextListBuilder::new();
189187

190-
let compressed = builder.build(&compression_key);
188+
let compressed = builder.build(&compression_key);
191189

192-
assert_eq!(compressed.len(), 0);
193-
assert!(compressed
194-
.get::<RadixCiphertext>(0, &decompression_key)
195-
.unwrap()
196-
.is_none())
197-
}
190+
assert_eq!(compressed.len(), 0);
191+
assert!(compressed
192+
.get::<RadixCiphertext>(0, &decompression_key)
193+
.unwrap()
194+
.is_none())
198195
}
199196

200197
#[test]

tfhe/src/shortint/backward_compatibility/ciphertext/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,8 @@ pub enum CompressedCiphertextListVersions {
148148
pub enum SquashedNoiseCiphertextVersions {
149149
V0(SquashedNoiseCiphertext),
150150
}
151+
152+
#[derive(VersionsDispatch)]
153+
pub enum CompressedSquashedNoiseCiphertextListVersions {
154+
V0(CompressedSquashedNoiseCiphertextList),
155+
}

tfhe/src/shortint/backward_compatibility/list_compression.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ use tfhe_versionable::deprecation::{Deprecable, Deprecated};
22
use tfhe_versionable::VersionsDispatch;
33

44
use crate::shortint::list_compression::{
5-
CompressedCompressionKey, CompressedDecompressionKey, CompressionKey, CompressionPrivateKeys,
6-
DecompressionKey,
5+
CompressedCompressionKey, CompressedDecompressionKey, CompressedNoiseSquashingCompressionKey,
6+
CompressionKey, CompressionPrivateKeys, DecompressionKey, NoiseSquashingCompressionKey,
7+
NoiseSquashingCompressionPrivateKey,
78
};
89

910
#[derive(VersionsDispatch)]
@@ -43,3 +44,18 @@ pub enum CompressedDecompressionKeyVersions {
4344
pub enum CompressionPrivateKeysVersions {
4445
V0(CompressionPrivateKeys),
4546
}
47+
48+
#[derive(VersionsDispatch)]
49+
pub enum NoiseSquashingCompressionKeyVersions {
50+
V0(NoiseSquashingCompressionKey),
51+
}
52+
53+
#[derive(VersionsDispatch)]
54+
pub enum NoiseSquashingCompressionPrivateKeyVersions {
55+
V0(NoiseSquashingCompressionPrivateKey),
56+
}
57+
58+
#[derive(VersionsDispatch)]
59+
pub enum CompressedNoiseSquashingCompressionKeyVersions {
60+
V0(CompressedNoiseSquashingCompressionKey),
61+
}
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
use tfhe_versionable::VersionsDispatch;
22

3-
use super::parameters::list_compression::CompressionParameters;
3+
use super::parameters::list_compression::{
4+
CompressionParameters, NoiseSquashingCompressionParameters,
5+
};
46

57
#[derive(VersionsDispatch)]
68
pub enum CompressionParametersVersions {
79
V0(CompressionParameters),
810
}
11+
12+
#[derive(VersionsDispatch)]
13+
pub enum NoiseSquashingCompressionParametersVersions {
14+
V0(NoiseSquashingCompressionParameters),
15+
}

tfhe/src/shortint/ciphertext/compressed_ciphertext_list.rs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ use tfhe_versionable::Versionize;
33
use self::compressed_modulus_switched_glwe_ciphertext::CompressedModulusSwitchedGlweCiphertext;
44
use crate::conformance::ParameterSetConformant;
55
use crate::core_crypto::prelude::*;
6-
use crate::shortint::backward_compatibility::ciphertext::CompressedCiphertextListVersions;
6+
use crate::shortint::backward_compatibility::ciphertext::{
7+
CompressedCiphertextListVersions, CompressedSquashedNoiseCiphertextListVersions,
8+
};
79
use crate::shortint::parameters::CompressedCiphertextConformanceParams;
810
use crate::shortint::{CarryModulus, MessageModulus};
911

12+
use super::SquashedNoiseCiphertext;
13+
1014
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Versionize)]
1115
#[versionize(CompressedCiphertextListVersions)]
1216
pub struct CompressedCiphertextList {
@@ -75,3 +79,61 @@ impl ParameterSetConformant for CompressedCiphertextList {
7579
&& *pbs_order == params.pbs_order
7680
}
7781
}
82+
83+
/// A compressed list of [`SquashedNoiseCiphertext`].
84+
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Versionize)]
85+
#[versionize(CompressedSquashedNoiseCiphertextListVersions)]
86+
pub struct CompressedSquashedNoiseCiphertextList {
87+
pub glwe_ciphertext_list: GlweCiphertextListOwned<u128>,
88+
pub message_modulus: MessageModulus,
89+
pub lwe_per_glwe: LweCiphertextCount,
90+
pub count: CiphertextCount,
91+
}
92+
93+
impl CompressedSquashedNoiseCiphertextList {
94+
/// Unpack a single ciphertext from the list.
95+
///
96+
/// Return an error if the index is greater than the size of the list.
97+
///
98+
/// After unpacking, the individual ciphertexts must be decrypted with the
99+
/// [`NoiseSquashingPrivateKey`] derived from the [`NoiseSquashingCompressionPrivateKey`] used
100+
/// for compression.
101+
///
102+
/// [`NoiseSquashingPrivateKey`]: crate::shortint::noise_squashing::NoiseSquashingPrivateKey
103+
/// [`NoiseSquashingCompressionPrivateKey`]: crate::shortint::list_compression::NoiseSquashingCompressionPrivateKey
104+
pub fn unpack(&self, index: usize) -> Result<SquashedNoiseCiphertext, crate::Error> {
105+
if index >= self.count.0 {
106+
return Err(crate::Error::new(format!(
107+
"Tried getting index {index} for CompressedNoiseSquashedCiphertextList \
108+
with {} elements, out of bound access.",
109+
self.count.0
110+
)));
111+
}
112+
113+
let lwe_per_glwe = self.lwe_per_glwe.0;
114+
let glwe_idx = index / lwe_per_glwe;
115+
116+
let glwe_dimension = self.glwe_ciphertext_list.glwe_size().to_glwe_dimension();
117+
let polynomial_size = self.glwe_ciphertext_list.polynomial_size();
118+
let ciphertext_modulus = self.glwe_ciphertext_list.ciphertext_modulus();
119+
120+
let lwe_size = glwe_dimension
121+
.to_equivalent_lwe_dimension(polynomial_size)
122+
.to_lwe_size();
123+
124+
let glwe = self.glwe_ciphertext_list.get(glwe_idx);
125+
126+
let monomial_degree = MonomialDegree(index % lwe_per_glwe);
127+
128+
let mut extracted_lwe =
129+
SquashedNoiseCiphertext::new_zero(lwe_size, ciphertext_modulus, self.message_modulus);
130+
131+
extract_lwe_sample_from_glwe_ciphertext(
132+
&glwe,
133+
extracted_lwe.lwe_ciphertext_mut(),
134+
monomial_degree,
135+
);
136+
137+
Ok(extracted_lwe)
138+
}
139+
}

tfhe/src/shortint/list_compression/compressed_server_keys.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
use super::server_keys::NoiseSquashingCompressionKeyConformanceParams;
12
use super::{
23
CompressionKey, CompressionKeyConformanceParams, CompressionPrivateKeys, DecompressionKey,
4+
NoiseSquashingCompressionKey,
35
};
46
use crate::conformance::ParameterSetConformant;
57
use crate::core_crypto::fft_impl::fft64::crypto::bootstrap::LweBootstrapKeyConformanceParams;
@@ -12,6 +14,7 @@ use crate::core_crypto::prelude::{
1214
};
1315
use crate::shortint::backward_compatibility::list_compression::{
1416
CompressedCompressionKeyVersions, CompressedDecompressionKeyVersions,
17+
CompressedNoiseSquashingCompressionKeyVersions,
1518
};
1619
use crate::shortint::client_key::ClientKey;
1720
use crate::shortint::engine::ShortintEngine;
@@ -179,3 +182,49 @@ impl ParameterSetConformant for CompressedDecompressionKey {
179182
blind_rotate_key.is_conformant(&params) && *lwe_per_glwe == parameter_set.lwe_per_glwe
180183
}
181184
}
185+
186+
#[derive(Clone, Debug, Serialize, Deserialize, Versionize)]
187+
#[versionize(CompressedNoiseSquashingCompressionKeyVersions)]
188+
pub struct CompressedNoiseSquashingCompressionKey {
189+
pub packing_key_switching_key: SeededLwePackingKeyswitchKey<Vec<u128>>,
190+
pub lwe_per_glwe: LweCiphertextCount,
191+
}
192+
193+
impl CompressedNoiseSquashingCompressionKey {
194+
pub fn decompress(&self) -> NoiseSquashingCompressionKey {
195+
let packing_key_switching_key = self
196+
.packing_key_switching_key
197+
.as_view()
198+
.decompress_into_lwe_packing_keyswitch_key();
199+
200+
NoiseSquashingCompressionKey {
201+
packing_key_switching_key,
202+
lwe_per_glwe: self.lwe_per_glwe,
203+
}
204+
}
205+
}
206+
207+
impl ParameterSetConformant for CompressedNoiseSquashingCompressionKey {
208+
type ParameterSet = NoiseSquashingCompressionKeyConformanceParams;
209+
210+
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool {
211+
let Self {
212+
packing_key_switching_key,
213+
lwe_per_glwe,
214+
} = self;
215+
216+
let params = LwePackingKeyswitchKeyConformanceParams {
217+
decomp_base_log: parameter_set.packing_ks_base_log,
218+
decomp_level_count: parameter_set.packing_ks_level,
219+
input_lwe_dimension: parameter_set
220+
.uncompressed_glwe_dimension
221+
.to_equivalent_lwe_dimension(parameter_set.uncompressed_polynomial_size),
222+
output_glwe_size: parameter_set.packing_ks_glwe_dimension.to_glwe_size(),
223+
output_polynomial_size: parameter_set.packing_ks_polynomial_size,
224+
ciphertext_modulus: parameter_set.cipherext_modulus,
225+
};
226+
227+
packing_key_switching_key.is_conformant(&params)
228+
&& *lwe_per_glwe == parameter_set.lwe_per_glwe
229+
}
230+
}
Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
mod compressed_server_keys;
22
mod compression;
3+
mod noise_squashing_compression;
34
mod private_key;
45
mod server_keys;
56

6-
pub use compressed_server_keys::{CompressedCompressionKey, CompressedDecompressionKey};
7-
pub use private_key::CompressionPrivateKeys;
8-
pub use server_keys::{CompressionKey, CompressionKeyConformanceParams, DecompressionKey};
7+
pub use compressed_server_keys::{
8+
CompressedCompressionKey, CompressedDecompressionKey, CompressedNoiseSquashingCompressionKey,
9+
};
10+
pub use private_key::{CompressionPrivateKeys, NoiseSquashingCompressionPrivateKey};
11+
pub use server_keys::{
12+
CompressionKey, CompressionKeyConformanceParams, DecompressionKey, NoiseSquashingCompressionKey,
13+
};

0 commit comments

Comments
 (0)