From 4d6aeebd0c44ddfb902a2946c8b157be5aa33914 Mon Sep 17 00:00:00 2001 From: Nicolas Sarlin Date: Fri, 28 Nov 2025 17:34:32 +0100 Subject: [PATCH] chore: detect degree in compact list conformance --- .../data-handling/serialization.md | 10 +- .../integers/unsigned/tests/cpu.rs | 9 +- .../integers/unsigned/tests/gpu.rs | 25 +- tfhe/src/integer/ciphertext/compact_list.rs | 217 ++++++++++++++---- tfhe/src/integer/parameters/mod.rs | 47 +++- tfhe/src/safe_serialization.rs | 111 +++++---- tfhe/src/shortint/ciphertext/zk.rs | 23 +- tfhe/src/shortint/parameters/mod.rs | 20 -- 8 files changed, 311 insertions(+), 151 deletions(-) diff --git a/tfhe/docs/fhe-computation/data-handling/serialization.md b/tfhe/docs/fhe-computation/data-handling/serialization.md index 8bb99c051..3c709cbdc 100644 --- a/tfhe/docs/fhe-computation/data-handling/serialization.md +++ b/tfhe/docs/fhe-computation/data-handling/serialization.md @@ -105,15 +105,15 @@ fn main() { let msgs = [27, 188u8]; let mut builder = CompactCiphertextList::builder(&public_key); builder.extend(msgs.iter().copied()); - let compact_list = builder.build(); + let compact_list = builder.build_packed(); let mut buffer = vec![]; safe_serialize(&compact_list, &mut buffer, 1 << 20).unwrap(); - let conformance_params = CompactCiphertextListConformanceParams { - shortint_params: params_1.to_shortint_conformance_param(), - num_elements_constraint: ListSizeConstraint::exact_size(2), - }; + let conformance_params = + CompactCiphertextListConformanceParams::from_parameters_and_size_constraint( + params_1.try_into().unwrap(), + ListSizeConstraint::exact_size(2)); safe_deserialize_conformant::(buffer.as_slice(), 1 << 20, &conformance_params) .unwrap(); } diff --git a/tfhe/src/high_level_api/integers/unsigned/tests/cpu.rs b/tfhe/src/high_level_api/integers/unsigned/tests/cpu.rs index f68086e99..d16488164 100644 --- a/tfhe/src/high_level_api/integers/unsigned/tests/cpu.rs +++ b/tfhe/src/high_level_api/integers/unsigned/tests/cpu.rs @@ -534,10 +534,11 @@ fn test_safe_deserialize_conformant_compact_fhe_uint32() { .serialize_into(&a, &mut serialized) .unwrap(); - let params = CompactCiphertextListConformanceParams { - shortint_params: block_params.to_shortint_conformance_param(), - num_elements_constraint: ListSizeConstraint::exact_size(clears.len()), - }; + let params = CompactCiphertextListConformanceParams::from_parameters_and_size_constraint( + pk.parameters(), + ListSizeConstraint::exact_size(clears.len()), + ) + .allow_unpacked(); let deserialized_a = DeserializationConfig::new(1 << 20) .deserialize_from::(serialized.as_slice(), ¶ms) .unwrap(); diff --git a/tfhe/src/high_level_api/integers/unsigned/tests/gpu.rs b/tfhe/src/high_level_api/integers/unsigned/tests/gpu.rs index d610c6b7c..5ed3fd18f 100644 --- a/tfhe/src/high_level_api/integers/unsigned/tests/gpu.rs +++ b/tfhe/src/high_level_api/integers/unsigned/tests/gpu.rs @@ -591,18 +591,21 @@ fn test_safe_deserialize_conformant_compact_fhe_uint32_gpu() { .unwrap(); let params = if i == 0 { - CompactCiphertextListConformanceParams { - shortint_params: PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128 - .to_shortint_conformance_param(), - num_elements_constraint: ListSizeConstraint::exact_size(clears.len()), - } + CompactCiphertextListConformanceParams::from_parameters_and_size_constraint( + PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128 + .try_into() + .unwrap(), + ListSizeConstraint::exact_size(clears.len()), + ) + .allow_unpacked() } else if i == 1 { - CompactCiphertextListConformanceParams { - shortint_params: - PARAM_GPU_MULTI_BIT_GROUP_4_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128 - .to_shortint_conformance_param(), - num_elements_constraint: ListSizeConstraint::exact_size(clears.len()), - } + CompactCiphertextListConformanceParams::from_parameters_and_size_constraint( + PARAM_GPU_MULTI_BIT_GROUP_4_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128 + .try_into() + .unwrap(), + ListSizeConstraint::exact_size(clears.len()), + ) + .allow_unpacked() } else { panic!("Unexpected parameter set") }; diff --git a/tfhe/src/integer/ciphertext/compact_list.rs b/tfhe/src/integer/ciphertext/compact_list.rs index cdd4ec4bc..2feafd9e6 100644 --- a/tfhe/src/integer/ciphertext/compact_list.rs +++ b/tfhe/src/integer/ciphertext/compact_list.rs @@ -1,6 +1,6 @@ use super::{DataKind, Expandable}; use crate::conformance::{ListSizeConstraint, ParameterSetConformant}; -use crate::core_crypto::prelude::Numeric; +use crate::core_crypto::prelude::{LweCiphertextListConformanceParams, Numeric}; use crate::integer::backward_compatibility::ciphertext::CompactCiphertextListVersions; #[cfg(feature = "zk-pok")] use crate::integer::backward_compatibility::ciphertext::ProvenCompactCiphertextListVersions; @@ -13,7 +13,8 @@ use crate::shortint::ciphertext::Degree; #[cfg(feature = "zk-pok")] use crate::shortint::ciphertext::ProvenCompactCiphertextListConformanceParams; use crate::shortint::parameters::{ - CastingFunctionsOwned, CiphertextConformanceParams, ShortintCompactCiphertextListCastingMode, + CastingFunctionsOwned, CiphertextListConformanceParams, + ShortintCompactCiphertextListCastingMode, }; #[cfg(feature = "zk-pok")] use crate::shortint::parameters::{ @@ -405,13 +406,62 @@ impl ParameterSetConformant for CompactCiphertextList { type ParameterSet = CompactCiphertextListConformanceParams; fn is_conformant(&self, params: &CompactCiphertextListConformanceParams) -> bool { - let Self { ct_list: _, info } = self; + let Self { ct_list, info } = self; - if !params.num_elements_constraint.is_valid(info.len()) { + let CompactCiphertextListConformanceParams { + encryption_lwe_dimension, + message_modulus, + carry_modulus, + ciphertext_modulus, + expansion_kind, + num_elements_constraint, + allow_unpacked, + } = params; + + if !num_elements_constraint.is_valid(info.len()) { return false; } - self.is_conformant_with_shortint_params(params.shortint_params) + let is_packed = self.is_packed(); + let is_unpacked = !is_packed; + let forbid_unpacked = !allow_unpacked; + + if is_unpacked && forbid_unpacked { + return false; + } + + let total_expected_num_blocks: usize = info + .iter() + .map(|a| a.num_blocks(self.message_modulus())) + .sum(); + + let total_expected_lwe_count = if is_packed { + total_expected_num_blocks.div_ceil(2) + } else { + total_expected_num_blocks + }; + + let degree = if is_packed { + Degree::new(message_modulus.0 * message_modulus.0 - 1) + } else { + Degree::new(message_modulus.0 - 1) + }; + + let shortint_params = CiphertextListConformanceParams { + expansion_kind: *expansion_kind, + message_modulus: *message_modulus, + carry_modulus: *carry_modulus, + ct_list_params: LweCiphertextListConformanceParams { + lwe_dim: *encryption_lwe_dimension, + lwe_ciphertext_count_constraint: ListSizeConstraint::exact_size( + total_expected_lwe_count, + ), + ct_modulus: *ciphertext_modulus, + }, + degree, + }; + + ct_list.is_conformant(&shortint_params) } } @@ -951,28 +1001,6 @@ impl CompactCiphertextList { pub fn message_modulus(&self) -> MessageModulus { self.ct_list.message_modulus } - - fn is_conformant_with_shortint_params( - &self, - shortint_params: CiphertextConformanceParams, - ) -> bool { - let Self { ct_list, info } = self; - - let mut num_blocks: usize = info - .iter() - .copied() - .map(|kind| kind.num_blocks(self.message_modulus())) - .sum(); - // This expects packing, halve the number of blocks with enough capacity - if shortint_params.degree.get() - == (shortint_params.message_modulus.0 * shortint_params.carry_modulus.0) - 1 - { - num_blocks = num_blocks.div_ceil(2); - } - let shortint_list_params = shortint_params - .to_ct_list_conformance_parameters(ListSizeConstraint::exact_size(num_blocks)); - ct_list.is_conformant(&shortint_list_params) - } } #[cfg(feature = "zk-pok")] @@ -1110,6 +1138,7 @@ pub struct IntegerProvenCompactCiphertextListConformanceParams { pub ciphertext_modulus: CiphertextModulus, pub expansion_kind: CompactCiphertextListExpansionKind, pub max_elements_per_compact_list: usize, + pub allow_unpacked: bool, pub zk_conformance_params: CompactPkeProofConformanceParams, } @@ -1133,6 +1162,7 @@ impl IntegerProvenCompactCiphertextListConformanceParams { ciphertext_modulus: value.ciphertext_modulus, expansion_kind: value.expansion_kind, max_elements_per_compact_list: crs.max_num_messages().0, + allow_unpacked: false, zk_conformance_params: CompactPkeProofConformanceParams::new(crs.scheme_version()), } } @@ -1157,6 +1187,16 @@ impl IntegerProvenCompactCiphertextListConformanceParams { ..self } } + + /// Allow the list to be composed of unpacked ciphertexts. + /// + /// Note that this means that the ciphertexts won't be sanitized. + pub fn allow_unpacked(self) -> Self { + Self { + allow_unpacked: true, + ..self + } + } } #[cfg(feature = "zk-pok")] @@ -1166,14 +1206,30 @@ impl ParameterSetConformant for ProvenCompactCiphertextList { fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { let Self { ct_list, info } = self; + // Early return if the list is considered empty + match (ct_list.proved_lists.is_empty(), info.is_empty()) { + (true, true) => return true, + (true, false) => return false, + (false, true) => return false, + (false, false) => {} + } + + let IntegerProvenCompactCiphertextListConformanceParams { + encryption_lwe_dimension, + message_modulus, + carry_modulus, + ciphertext_modulus, + expansion_kind, + max_elements_per_compact_list, + allow_unpacked, + zk_conformance_params, + } = *parameter_set; + let is_packed = self.is_packed(); + let is_unpacked = !is_packed; + let forbid_unpacked = !allow_unpacked; - let all_have_same_packing = ct_list - .proved_lists - .iter() - .all(|(list, _)| list.is_packed() == is_packed); - - if !all_have_same_packing { + if is_unpacked && forbid_unpacked { return false; } @@ -1182,19 +1238,22 @@ impl ParameterSetConformant for ProvenCompactCiphertextList { .map(|a| a.num_blocks(self.message_modulus())) .sum(); - let total_expected_lwe_count = - total_expected_num_blocks.div_ceil(if is_packed { 2 } else { 1 }); + let total_expected_lwe_count = if is_packed { + total_expected_num_blocks.div_ceil(2) + } else { + total_expected_num_blocks + }; let a = ProvenCompactCiphertextListConformanceParams { - expansion_kind: parameter_set.expansion_kind, - encryption_lwe_dimension: parameter_set.encryption_lwe_dimension, - message_modulus: parameter_set.message_modulus, - carry_modulus: parameter_set.carry_modulus, - ciphertext_modulus: parameter_set.ciphertext_modulus, - max_lwe_count_per_compact_list: parameter_set.max_elements_per_compact_list, + expansion_kind, + encryption_lwe_dimension, + message_modulus, + carry_modulus, + ciphertext_modulus, + max_lwe_count_per_compact_list: max_elements_per_compact_list, // packing by 2 total_expected_lwe_count, - zk_conformance_params: parameter_set.zk_conformance_params, + zk_conformance_params, }; ct_list.is_conformant(&a) @@ -1236,8 +1295,7 @@ mod zk_pok_tests { use crate::zk::{CompactPkeCrs, ZkComputeLoad, ZkVerificationOutcome}; use rand::random; - #[test] - fn test_zk_compact_ciphertext_list_encryption_ci_run_filter() { + fn test_zk_list(is_packed: bool) { let pke_params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; let ksk_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; let fhe_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; @@ -1258,14 +1316,34 @@ mod zk_pok_tests { let ksk = KeySwitchingKey::new((&compact_private_key, None), (&cks, &sk), ksk_params); let pk = CompactPublicKey::new(&compact_private_key); + let conformance_params = + IntegerProvenCompactCiphertextListConformanceParams::from_crs_and_parameters( + pke_params, &crs, + ); + let msgs = (0..512) .map(|_| random::() % modulus) .collect::>(); - let proven_ct = CompactCiphertextList::builder(&pk) - .extend_with_num_blocks(msgs.iter().copied(), num_blocks) - .build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof) - .unwrap(); + let mut builder = CompactCiphertextList::builder(&pk); + builder.extend_with_num_blocks(msgs.iter().copied(), num_blocks); + + let proven_ct = if is_packed { + builder + .build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof) + .unwrap() + } else { + builder + .build_with_proof(&crs, &metadata, ZkComputeLoad::Proof) + .unwrap() + }; + + if is_packed { + assert!(proven_ct.is_conformant(&conformance_params)); + } else { + assert!(!proven_ct.is_conformant(&conformance_params)); + assert!(proven_ct.is_conformant(&conformance_params.allow_unpacked())); + } let expander = proven_ct .verify_and_expand( @@ -1298,6 +1376,16 @@ mod zk_pok_tests { } } + #[test] + fn test_zk_compact_ciphertext_list_encryption() { + test_zk_list(true); + } + + #[test] + fn test_unpacked_list() { + test_zk_list(false); + } + #[test] fn test_empty_list() { let pke_params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128; @@ -1313,6 +1401,11 @@ mod zk_pok_tests { let ksk = KeySwitchingKey::new((&compact_private_key, None), (&cks, &sk), ksk_params); let pk = CompactPublicKey::new(&compact_private_key); + let conformance_params = + IntegerProvenCompactCiphertextListConformanceParams::from_crs_and_parameters( + pke_params, &crs, + ); + // Test by pushing with zero blocks { let proven_ct = CompactCiphertextList::builder(&pk) @@ -1327,6 +1420,7 @@ mod zk_pok_tests { proven_ct.verify(&crs, &pk, &metadata), ZkVerificationOutcome::Valid ); + assert!(proven_ct.is_conformant(&conformance_params)); assert!(matches!( proven_ct.verify_and_expand( &crs, @@ -1350,6 +1444,7 @@ mod zk_pok_tests { proven_ct.verify(&crs, &pk, &metadata), ZkVerificationOutcome::Valid ); + assert!(proven_ct.is_conformant(&conformance_params)); assert!(matches!( proven_ct.verify_and_expand( &crs, @@ -1360,6 +1455,32 @@ mod zk_pok_tests { Ok(vec) if vec.is_empty() )); } + + // Test with empty ct vec and not empty info vec + { + let mut proven_ct = CompactCiphertextList::builder(&pk) + .push(1u8) + .push(-1i8) + .build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof) + .unwrap(); + + proven_ct.ct_list.proved_lists = Vec::new(); + + assert!(!proven_ct.is_conformant(&conformance_params)); + } + + // Test with not empty ct vec and empty info vec + { + let mut proven_ct = CompactCiphertextList::builder(&pk) + .push(1u8) + .push(-1i8) + .build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof) + .unwrap(); + + proven_ct.info = Vec::new(); + + assert!(!proven_ct.is_conformant(&conformance_params)); + } } /// In this test we check the behavior of the proven list when the info vec diff --git a/tfhe/src/integer/parameters/mod.rs b/tfhe/src/integer/parameters/mod.rs index 84bb68303..bc974b6e3 100644 --- a/tfhe/src/integer/parameters/mod.rs +++ b/tfhe/src/integer/parameters/mod.rs @@ -3,7 +3,8 @@ use crate::conformance::ListSizeConstraint; use crate::integer::key_switching_key::KeySwitchingKeyView; use crate::integer::server_key::ServerKey; use crate::shortint::parameters::{ - CarryModulus, CiphertextConformanceParams, EncryptionKeyChoice, MessageModulus, + CarryModulus, CiphertextConformanceParams, CompactCiphertextListExpansionKind, + CompactPublicKeyEncryptionParameters, EncryptionKeyChoice, MessageModulus, }; pub use crate::shortint::parameters::{ DecompositionBaseLog, DecompositionLevelCount, DynamicDistribution, GlweDimension, @@ -171,16 +172,6 @@ pub struct RadixCiphertextConformanceParams { /// Can be used on a server to check if client inputs are well formed /// before running a computation on them impl RadixCiphertextConformanceParams { - pub fn to_ct_list_conformance_parameters( - &self, - list_constraint: ListSizeConstraint, - ) -> CompactCiphertextListConformanceParams { - CompactCiphertextListConformanceParams { - shortint_params: self.shortint_params, - num_elements_constraint: list_constraint, - } - } - pub fn from_pbs_parameters>( params: P, num_blocks_per_integer: usize, @@ -198,6 +189,38 @@ impl RadixCiphertextConformanceParams { /// before running a computation on them #[derive(Copy, Clone)] pub struct CompactCiphertextListConformanceParams { - pub shortint_params: CiphertextConformanceParams, + pub encryption_lwe_dimension: LweDimension, + pub message_modulus: MessageModulus, + pub carry_modulus: CarryModulus, + pub ciphertext_modulus: CiphertextModulus, + pub expansion_kind: CompactCiphertextListExpansionKind, pub num_elements_constraint: ListSizeConstraint, + pub allow_unpacked: bool, +} + +impl CompactCiphertextListConformanceParams { + pub fn from_parameters_and_size_constraint( + value: CompactPublicKeyEncryptionParameters, + num_elements_constraint: ListSizeConstraint, + ) -> Self { + Self { + encryption_lwe_dimension: value.encryption_lwe_dimension, + message_modulus: value.message_modulus, + carry_modulus: value.carry_modulus, + ciphertext_modulus: value.ciphertext_modulus, + expansion_kind: value.expansion_kind, + num_elements_constraint, + allow_unpacked: false, + } + } + + /// Allow the list to be composed of unpacked ciphertexts. + /// + /// Note that this means that the ciphertexts won't be sanitized. + pub fn allow_unpacked(self) -> Self { + Self { + allow_unpacked: true, + ..self + } + } } diff --git a/tfhe/src/safe_serialization.rs b/tfhe/src/safe_serialization.rs index d97014517..eeddac9bf 100644 --- a/tfhe/src/safe_serialization.rs +++ b/tfhe/src/safe_serialization.rs @@ -718,17 +718,13 @@ mod test_integer { use crate::high_level_api::{generate_keys, ConfigBuilder}; use crate::prelude::*; use crate::safe_serialization::{DeserializationConfig, SerializationConfig}; - use crate::shortint::parameters::{ - PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, - PARAM_MESSAGE_3_CARRY_3_KS_PBS_GAUSSIAN_2M128, - }; + use crate::shortint::parameters::PARAM_MESSAGE_3_CARRY_3_KS_PBS_GAUSSIAN_2M128; use crate::{ set_server_key, CompactCiphertextList, CompactCiphertextListConformanceParams, CompactPublicKey, FheUint8, }; - #[test] - fn safe_deserialization_ct_list() { + fn test_safe_deserialization_ct_list(is_packed: bool) { let (client_key, sks) = generate_keys(ConfigBuilder::default().build()); set_server_key(sks); @@ -736,11 +732,16 @@ mod test_integer { let msg = [27u8, 10, 3]; - let ct_list = CompactCiphertextList::builder(&public_key) - .push(27u8) - .push(10u8) - .push(3u8) - .build(); + let mut builder = CompactCiphertextList::builder(&public_key); + for value in msg { + builder.push(value); + } + + let ct_list = if is_packed { + builder.build_packed() + } else { + builder.build() + }; let mut buffer = vec![]; @@ -751,17 +752,33 @@ mod test_integer { assert_eq!(size as usize, buffer.len()); - let to_param_set = |list_size_constraint| CompactCiphertextListConformanceParams { - shortint_params: PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128 - .to_shortint_conformance_param(), - num_elements_constraint: list_size_constraint, + let to_param_set = |list_size_constraint| { + let params = + CompactCiphertextListConformanceParams::from_parameters_and_size_constraint( + public_key.parameters(), + list_size_constraint, + ); + + if is_packed { + params + } else { + params.allow_unpacked() + } }; + let wrong_pke_params = + CompactCiphertextListConformanceParams::from_parameters_and_size_constraint( + PARAM_MESSAGE_3_CARRY_3_KS_PBS_GAUSSIAN_2M128 + .try_into() + .unwrap(), + ListSizeConstraint::exact_size(3), + ); + for param_set in [ - CompactCiphertextListConformanceParams { - shortint_params: PARAM_MESSAGE_3_CARRY_3_KS_PBS_GAUSSIAN_2M128 - .to_shortint_conformance_param(), - num_elements_constraint: ListSizeConstraint::exact_size(3), + if is_packed { + wrong_pke_params + } else { + wrong_pke_params.allow_unpacked() }, to_param_set(ListSizeConstraint::exact_size(2)), to_param_set(ListSizeConstraint::exact_size(4)), @@ -779,22 +796,14 @@ mod test_integer { ListSizeConstraint::try_size_in_range(3, 4).unwrap(), ListSizeConstraint::try_size_in_range(2, 4).unwrap(), ] { - let params = CompactCiphertextListConformanceParams { - shortint_params: PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128 - .to_shortint_conformance_param(), - num_elements_constraint: len_constraint, - }; + let params = to_param_set(len_constraint); DeserializationConfig::new(1 << 20) .deserialize_from::(buffer.as_slice(), ¶ms) .unwrap(); } - let params = CompactCiphertextListConformanceParams { - shortint_params: PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128 - .to_shortint_conformance_param(), - num_elements_constraint: ListSizeConstraint::exact_size(3), - }; + let params = to_param_set(ListSizeConstraint::exact_size(3)); let ct2 = DeserializationConfig::new(1 << 20) .deserialize_from::(buffer.as_slice(), ¶ms) .unwrap(); @@ -810,6 +819,16 @@ mod test_integer { assert_eq!(&msg[..], &dec); } + #[test] + fn safe_deserialization_ct_list() { + test_safe_deserialization_ct_list(false); + } + + #[test] + fn safe_deserialization_ct_list_packed() { + test_safe_deserialization_ct_list(true); + } + #[test] fn safe_deserialization_ct_list_versioned() { let (client_key, sks) = generate_keys(ConfigBuilder::default().build()); @@ -834,18 +853,22 @@ mod test_integer { assert_eq!(size as usize, buffer.len()); - let to_param_set = |list_size_constraint| CompactCiphertextListConformanceParams { - shortint_params: PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128 - .to_shortint_conformance_param(), - num_elements_constraint: list_size_constraint, + let to_param_set = |list_size_constraint| { + CompactCiphertextListConformanceParams::from_parameters_and_size_constraint( + public_key.parameters(), + list_size_constraint, + ) + .allow_unpacked() }; for param_set in [ - CompactCiphertextListConformanceParams { - shortint_params: PARAM_MESSAGE_3_CARRY_3_KS_PBS_GAUSSIAN_2M128 - .to_shortint_conformance_param(), - num_elements_constraint: ListSizeConstraint::exact_size(3), - }, + CompactCiphertextListConformanceParams::from_parameters_and_size_constraint( + PARAM_MESSAGE_3_CARRY_3_KS_PBS_GAUSSIAN_2M128 + .try_into() + .unwrap(), + ListSizeConstraint::exact_size(3), + ) + .allow_unpacked(), to_param_set(ListSizeConstraint::exact_size(2)), to_param_set(ListSizeConstraint::exact_size(4)), to_param_set(ListSizeConstraint::try_size_in_range(1, 2).unwrap()), @@ -862,22 +885,14 @@ mod test_integer { ListSizeConstraint::try_size_in_range(3, 4).unwrap(), ListSizeConstraint::try_size_in_range(2, 4).unwrap(), ] { - let params = CompactCiphertextListConformanceParams { - shortint_params: PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128 - .to_shortint_conformance_param(), - num_elements_constraint: len_constraint, - }; + let params = to_param_set(len_constraint); DeserializationConfig::new(1 << 20) .deserialize_from::(buffer.as_slice(), ¶ms) .unwrap(); } - let params = CompactCiphertextListConformanceParams { - shortint_params: PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128 - .to_shortint_conformance_param(), - num_elements_constraint: ListSizeConstraint::exact_size(3), - }; + let params = to_param_set(ListSizeConstraint::exact_size(3)); let ct2 = DeserializationConfig::new(1 << 20) .deserialize_from::(buffer.as_slice(), ¶ms) .unwrap(); diff --git a/tfhe/src/shortint/ciphertext/zk.rs b/tfhe/src/shortint/ciphertext/zk.rs index 6d64c166e..e415979de 100644 --- a/tfhe/src/shortint/ciphertext/zk.rs +++ b/tfhe/src/shortint/ciphertext/zk.rs @@ -272,16 +272,26 @@ impl ParameterSetConformant for ProvenCompactCiphertextList { let max_elements_per_compact_list = *max_lwe_count_per_compact_list; let mut remaining_len = *total_expected_lwe_count; + let mut first_is_packed = None; for (compact_ct_list, proof) in proved_lists { - if !proof.is_conformant(zk_conformance_params) { - return false; + match first_is_packed { + None => first_is_packed = Some(compact_ct_list.is_packed()), + Some(first_is_packed) => { + if first_is_packed != compact_ct_list.is_packed() { + return false; + } + } } if remaining_len == 0 { return false; } + if !proof.is_conformant(zk_conformance_params) { + return false; + } + let expected_len; if remaining_len > max_elements_per_compact_list { @@ -293,6 +303,12 @@ impl ParameterSetConformant for ProvenCompactCiphertextList { remaining_len = 0; } + let degree = if compact_ct_list.is_packed() { + Degree::new(message_modulus.0 * message_modulus.0 - 1) + } else { + Degree::new(message_modulus.0 - 1) + }; + let params = CiphertextListConformanceParams { ct_list_params: LweCiphertextListConformanceParams { lwe_dim: *encryption_lwe_dimension, @@ -301,7 +317,8 @@ impl ParameterSetConformant for ProvenCompactCiphertextList { }, message_modulus: *message_modulus, carry_modulus: *carry_modulus, - degree: Degree::new(message_modulus.0 * message_modulus.0 - 1), + + degree, expansion_kind: *expansion_kind, }; diff --git a/tfhe/src/shortint/parameters/mod.rs b/tfhe/src/shortint/parameters/mod.rs index 45a8b5889..b1dc462ce 100644 --- a/tfhe/src/shortint/parameters/mod.rs +++ b/tfhe/src/shortint/parameters/mod.rs @@ -5,7 +5,6 @@ //! homomorphic evaluation of integer circuits as well as a list of secure cryptographic parameter //! sets. -use crate::conformance::ListSizeConstraint; pub use crate::core_crypto::commons::dispersion::{StandardDev, Variance}; use crate::core_crypto::commons::math::random::{CompressionSeed, Uniform}; pub use crate::core_crypto::commons::parameters::{ @@ -198,25 +197,6 @@ pub struct CiphertextListConformanceParams { pub expansion_kind: CompactCiphertextListExpansionKind, } -impl CiphertextConformanceParams { - pub fn to_ct_list_conformance_parameters( - &self, - list_constraint: ListSizeConstraint, - ) -> CiphertextListConformanceParams { - CiphertextListConformanceParams { - ct_list_params: LweCiphertextListConformanceParams { - lwe_dim: self.ct_params.lwe_dim, - ct_modulus: self.ct_params.ct_modulus, - lwe_ciphertext_count_constraint: list_constraint, - }, - message_modulus: self.message_modulus, - carry_modulus: self.carry_modulus, - degree: self.degree, - expansion_kind: self.atomic_pattern.into(), - } - } -} - impl From for PBSParameters { fn from(value: ClassicPBSParameters) -> Self { Self::PBS(value)