From 0dae18bf5825ed395b70fdcbf04c62ebe5677403 Mon Sep 17 00:00:00 2001 From: "Mayeul@Zama" <69792125+mayeul-zama@users.noreply.github.com> Date: Mon, 30 Sep 2024 11:48:10 +0200 Subject: [PATCH 1/5] feat(all): add CompactPublicKey conformance --- .../core_crypto/entities/glwe_ciphertext.rs | 3 +- .../entities/lwe_compact_public_key.rs | 30 +++++ .../entities/seeded_glwe_ciphertext.rs | 27 +++++ .../entities/seeded_lwe_compact_public_key.rs | 28 ++++- tfhe/src/high_level_api/keys/inner.rs | 20 ++++ tfhe/src/high_level_api/keys/public.rs | 105 ++++++++++++++++++ tfhe/src/integer/public_key/compact.rs | 21 ++++ tfhe/src/shortint/public_key/compact.rs | 36 +++++- 8 files changed, 266 insertions(+), 4 deletions(-) diff --git a/tfhe/src/core_crypto/entities/glwe_ciphertext.rs b/tfhe/src/core_crypto/entities/glwe_ciphertext.rs index 0086a255bb..d1e001ac47 100644 --- a/tfhe/src/core_crypto/entities/glwe_ciphertext.rs +++ b/tfhe/src/core_crypto/entities/glwe_ciphertext.rs @@ -649,7 +649,8 @@ where ciphertext_modulus, } = self; - check_encrypted_content_respects_mod(self, glwe_ct_parameters.ct_modulus) + polynomial_size.0.is_power_of_two() + && check_encrypted_content_respects_mod(self, glwe_ct_parameters.ct_modulus) && data.container_len() == glwe_ciphertext_size( glwe_ct_parameters.glwe_dim.to_glwe_size(), diff --git a/tfhe/src/core_crypto/entities/lwe_compact_public_key.rs b/tfhe/src/core_crypto/entities/lwe_compact_public_key.rs index a6d4d1a27d..be77cbbe0c 100644 --- a/tfhe/src/core_crypto/entities/lwe_compact_public_key.rs +++ b/tfhe/src/core_crypto/entities/lwe_compact_public_key.rs @@ -2,6 +2,7 @@ use tfhe_versionable::Versionize; +use crate::conformance::ParameterSetConformant; use crate::core_crypto::backward_compatibility::entities::lwe_compact_public_key::LweCompactPublicKeyVersions; use crate::core_crypto::commons::parameters::*; use crate::core_crypto::commons::traits::*; @@ -183,3 +184,32 @@ impl LweCompactPublicKeyOwned { Self::from_container(vec![fill_with; 2 * lwe_dimension.0], ciphertext_modulus) } } + +#[derive(Clone, Copy)] +pub struct LweCompactPublicKeyEncryptionParameters { + pub encryption_lwe_dimension: LweDimension, + pub ciphertext_modulus: CiphertextModulus, +} + +impl ParameterSetConformant for LweCompactPublicKey +where + C::Element: UnsignedInteger, +{ + type ParameterSet = LweCompactPublicKeyEncryptionParameters; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { glwe_ciphertext } = self; + + if !parameter_set.encryption_lwe_dimension.0.is_power_of_two() { + return false; + } + + let glwe_ciphertext_conformance_parameters = GlweCiphertextConformanceParameters { + glwe_dim: GlweDimension(1), + polynomial_size: PolynomialSize(parameter_set.encryption_lwe_dimension.0), + ct_modulus: parameter_set.ciphertext_modulus, + }; + + glwe_ciphertext.is_conformant(&glwe_ciphertext_conformance_parameters) + } +} diff --git a/tfhe/src/core_crypto/entities/seeded_glwe_ciphertext.rs b/tfhe/src/core_crypto/entities/seeded_glwe_ciphertext.rs index fe3304c2c9..be16920e6a 100644 --- a/tfhe/src/core_crypto/entities/seeded_glwe_ciphertext.rs +++ b/tfhe/src/core_crypto/entities/seeded_glwe_ciphertext.rs @@ -1,7 +1,9 @@ //! Module containing the definition of the SeededGlweCiphertext. +use misc::check_encrypted_content_respects_mod; use tfhe_versionable::Versionize; +use crate::conformance::ParameterSetConformant; use crate::core_crypto::algorithms::*; use crate::core_crypto::backward_compatibility::entities::seeded_glwe_ciphertext::SeededGlweCiphertextVersions; use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, CompressionSeed}; @@ -296,3 +298,28 @@ impl> CreateFrom Self::from_container(from, glwe_size, compression_seed, ciphertext_modulus) } } + +impl ParameterSetConformant for SeededGlweCiphertext +where + C::Element: UnsignedInteger, +{ + type ParameterSet = GlweCiphertextConformanceParameters; + + fn is_conformant( + &self, + glwe_ct_parameters: &GlweCiphertextConformanceParameters, + ) -> bool { + let Self { + compression_seed: _, + data, + ciphertext_modulus, + glwe_size, + } = self; + + glwe_ct_parameters.polynomial_size.0.is_power_of_two() + && check_encrypted_content_respects_mod(self, glwe_ct_parameters.ct_modulus) + && data.container_len() == glwe_ct_parameters.polynomial_size.0 + && *glwe_size == glwe_ct_parameters.glwe_dim.to_glwe_size() + && *ciphertext_modulus == glwe_ct_parameters.ct_modulus + } +} diff --git a/tfhe/src/core_crypto/entities/seeded_lwe_compact_public_key.rs b/tfhe/src/core_crypto/entities/seeded_lwe_compact_public_key.rs index 6134416039..a70c6f9e56 100644 --- a/tfhe/src/core_crypto/entities/seeded_lwe_compact_public_key.rs +++ b/tfhe/src/core_crypto/entities/seeded_lwe_compact_public_key.rs @@ -2,6 +2,7 @@ use tfhe_versionable::Versionize; +use crate::conformance::ParameterSetConformant; use crate::core_crypto::algorithms::decompress_seeded_lwe_compact_public_key; use crate::core_crypto::backward_compatibility::entities::seeded_lwe_compact_public_key::SeededLweCompactPublicKeyVersions; use crate::core_crypto::commons::math::random::{ActivatedRandomGenerator, CompressionSeed}; @@ -118,7 +119,7 @@ impl> SeededLweCompactPu Self { seeded_glwe_ciphertext: SeededGlweCiphertext::from_container( container, - GlweSize(1), + GlweDimension(1).to_glwe_size(), compression_seed, ciphertext_modulus, ), @@ -229,3 +230,28 @@ impl SeededLweCompactPublicKeyOwned { ) } } + +impl ParameterSetConformant for SeededLweCompactPublicKey +where + C::Element: UnsignedInteger, +{ + type ParameterSet = LweCompactPublicKeyEncryptionParameters; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { + seeded_glwe_ciphertext, + } = self; + + if !parameter_set.encryption_lwe_dimension.0.is_power_of_two() { + return false; + } + + let glwe_ciphertext_conformance_parameters = GlweCiphertextConformanceParameters { + glwe_dim: GlweDimension(1), + polynomial_size: PolynomialSize(parameter_set.encryption_lwe_dimension.0), + ct_modulus: parameter_set.ciphertext_modulus, + }; + + seeded_glwe_ciphertext.is_conformant(&glwe_ciphertext_conformance_parameters) + } +} diff --git a/tfhe/src/high_level_api/keys/inner.rs b/tfhe/src/high_level_api/keys/inner.rs index 173341f876..9a3231c206 100644 --- a/tfhe/src/high_level_api/keys/inner.rs +++ b/tfhe/src/high_level_api/keys/inner.rs @@ -608,3 +608,23 @@ impl ParameterSetConformant for IntegerCompressedServerKey { && compression_is_ok } } + +impl ParameterSetConformant for IntegerCompactPublicKey { + type ParameterSet = CompactPublicKeyEncryptionParameters; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { key } = self; + + key.is_conformant(parameter_set) + } +} + +impl ParameterSetConformant for IntegerCompressedCompactPublicKey { + type ParameterSet = CompactPublicKeyEncryptionParameters; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { key } = self; + + key.is_conformant(parameter_set) + } +} diff --git a/tfhe/src/high_level_api/keys/public.rs b/tfhe/src/high_level_api/keys/public.rs index e7983818d8..c9019fc364 100644 --- a/tfhe/src/high_level_api/keys/public.rs +++ b/tfhe/src/high_level_api/keys/public.rs @@ -20,9 +20,11 @@ use crate::backward_compatibility::keys::{ CompactPublicKeyVersions, CompressedCompactPublicKeyVersions, CompressedPublicKeyVersions, PublicKeyVersions, }; +use crate::conformance::ParameterSetConformant; use crate::high_level_api::keys::{IntegerCompactPublicKey, IntegerCompressedCompactPublicKey}; use crate::named::Named; use crate::prelude::Tagged; +use crate::shortint::parameters::CompactPublicKeyEncryptionParameters; use crate::shortint::MessageModulus; use crate::{Error, Tag}; @@ -240,3 +242,106 @@ impl Tagged for CompressedCompactPublicKey { impl Named for CompressedCompactPublicKey { const NAME: &'static str = "high_level_api::CompressedCompactPublicKey"; } + +impl ParameterSetConformant for CompactPublicKey { + type ParameterSet = CompactPublicKeyEncryptionParameters; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { key, tag: _ } = self; + + key.is_conformant(parameter_set) + } +} + +impl ParameterSetConformant for CompressedCompactPublicKey { + type ParameterSet = CompactPublicKeyEncryptionParameters; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { key, tag: _ } = self; + + key.is_conformant(parameter_set) + } +} + +#[cfg(test)] +mod test { + use crate::conformance::ParameterSetConformant; + use crate::shortint::parameters::{ + CompactPublicKeyEncryptionParameters, PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS, + }; + use crate::{ + generate_keys, ClientKey, CompactPublicKey, CompressedCompactPublicKey, ConfigBuilder, + }; + + #[test] + fn conformance_compact_public_key() { + let params = PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS; + + let config = ConfigBuilder::default() + .use_custom_parameters(params) + .build(); + + let (client_key, _) = generate_keys(config); + + let public_key = CompactPublicKey::new(&client_key); + + let compact_encryption_parameters: CompactPublicKeyEncryptionParameters = + params.try_into().unwrap(); + + assert!(public_key.is_conformant(&compact_encryption_parameters)); + } + + #[test] + fn conformance_compact_public_key_casting() { + let params = crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + + let cpk_params = crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + + let casting_params = crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + + let config = ConfigBuilder::with_custom_parameters(params) + .use_dedicated_compact_public_key_parameters((cpk_params, casting_params)); + + let client_key = ClientKey::generate(config); + + let public_key = CompactPublicKey::new(&client_key); + + assert!(public_key.is_conformant(&cpk_params)); + } + + #[test] + fn conformance_compressed_compact_public_key() { + let params = PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS; + + let config = ConfigBuilder::default() + .use_custom_parameters(params) + .build(); + + let (client_key, _) = generate_keys(config); + + let public_key = CompressedCompactPublicKey::new(&client_key); + + let compact_encryption_parameters: CompactPublicKeyEncryptionParameters = + params.try_into().unwrap(); + + assert!(public_key.is_conformant(&compact_encryption_parameters)); + } + + #[test] + fn conformance_compressed_compact_public_key_casting() { + let params = crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + + let cpk_params = crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + + let casting_params = crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + + let config = ConfigBuilder::with_custom_parameters(params) + .use_dedicated_compact_public_key_parameters((cpk_params, casting_params)); + + let client_key = ClientKey::generate(config); + + let public_key = CompressedCompactPublicKey::new(&client_key); + + assert!(public_key.is_conformant(&cpk_params)); + } +} diff --git a/tfhe/src/integer/public_key/compact.rs b/tfhe/src/integer/public_key/compact.rs index 283ab2301a..307c03476a 100644 --- a/tfhe/src/integer/public_key/compact.rs +++ b/tfhe/src/integer/public_key/compact.rs @@ -1,3 +1,4 @@ +use crate::conformance::ParameterSetConformant; use crate::core_crypto::commons::traits::Container; use crate::integer::backward_compatibility::public_key::{ CompactPrivateKeyVersions, CompactPublicKeyVersions, CompressedCompactPublicKeyVersions, @@ -203,3 +204,23 @@ impl CompressedCompactPublicKey { } } } + +impl ParameterSetConformant for CompactPublicKey { + type ParameterSet = CompactPublicKeyEncryptionParameters; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { key } = self; + + key.is_conformant(parameter_set) + } +} + +impl ParameterSetConformant for CompressedCompactPublicKey { + type ParameterSet = CompactPublicKeyEncryptionParameters; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { key } = self; + + key.is_conformant(parameter_set) + } +} diff --git a/tfhe/src/shortint/public_key/compact.rs b/tfhe/src/shortint/public_key/compact.rs index 34cf4cf82c..3fa2ff19fc 100644 --- a/tfhe/src/shortint/public_key/compact.rs +++ b/tfhe/src/shortint/public_key/compact.rs @@ -1,8 +1,10 @@ +use crate::conformance::ParameterSetConformant; use crate::core_crypto::prelude::{ allocate_and_generate_new_binary_lwe_secret_key, allocate_and_generate_new_seeded_lwe_compact_public_key, generate_lwe_compact_public_key, - Container, LweCiphertextCount, LweCompactCiphertextListOwned, LweCompactPublicKeyOwned, - LweSecretKey, Plaintext, PlaintextList, SeededLweCompactPublicKeyOwned, + Container, LweCiphertextCount, LweCompactCiphertextListOwned, + LweCompactPublicKeyEncryptionParameters, LweCompactPublicKeyOwned, LweSecretKey, Plaintext, + PlaintextList, SeededLweCompactPublicKeyOwned, }; use crate::shortint::backward_compatibility::public_key::{ CompactPrivateKeyVersions, CompactPublicKeyVersions, CompressedCompactPublicKeyVersions, @@ -559,3 +561,33 @@ impl CompressedCompactPublicKey { } } } + +impl ParameterSetConformant for CompactPublicKey { + type ParameterSet = CompactPublicKeyEncryptionParameters; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { key, parameters } = self; + + let core_params = LweCompactPublicKeyEncryptionParameters { + encryption_lwe_dimension: parameter_set.encryption_lwe_dimension, + ciphertext_modulus: parameter_set.ciphertext_modulus, + }; + + parameters == parameter_set && key.is_conformant(&core_params) + } +} + +impl ParameterSetConformant for CompressedCompactPublicKey { + type ParameterSet = CompactPublicKeyEncryptionParameters; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { key, parameters } = self; + + let core_params = LweCompactPublicKeyEncryptionParameters { + encryption_lwe_dimension: parameter_set.encryption_lwe_dimension, + ciphertext_modulus: parameter_set.ciphertext_modulus, + }; + + parameters == parameter_set && key.is_conformant(&core_params) + } +} From 4336fcf60cacef053dfa86e5e16f8e68f799b85b Mon Sep 17 00:00:00 2001 From: "Mayeul@Zama" <69792125+mayeul-zama@users.noreply.github.com> Date: Fri, 27 Sep 2024 17:23:28 +0200 Subject: [PATCH 2/5] feat(all): add ProvenCompactCiphertextList conformance --- tfhe-zk-pok/src/proofs/pke.rs | 9 ++ .../entities/lwe_compact_ciphertext_list.rs | 1 + tfhe/src/high_level_api/compact_list.rs | 92 +++++++++++++++++++ tfhe/src/integer/ciphertext/compact_list.rs | 62 +++++++++++++ tfhe/src/shortint/ciphertext/zk.rs | 84 ++++++++++++++++- tfhe/src/shortint/parameters/mod.rs | 1 + 6 files changed, 248 insertions(+), 1 deletion(-) diff --git a/tfhe-zk-pok/src/proofs/pke.rs b/tfhe-zk-pok/src/proofs/pke.rs index 9463ecbab7..fbfa59ce12 100644 --- a/tfhe-zk-pok/src/proofs/pke.rs +++ b/tfhe-zk-pok/src/proofs/pke.rs @@ -317,6 +317,15 @@ where } } +impl Proof { + pub fn content_is_usable(&self) -> bool { + matches!( + (self.c_hat_t, self.c_h, self.pi_kzg), + (None, None, None) | (Some(_), Some(_), Some(_)) + ) + } +} + #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct PublicCommit { a: Vec, diff --git a/tfhe/src/core_crypto/entities/lwe_compact_ciphertext_list.rs b/tfhe/src/core_crypto/entities/lwe_compact_ciphertext_list.rs index 7fb5545eaf..62dcf501f8 100644 --- a/tfhe/src/core_crypto/entities/lwe_compact_ciphertext_list.rs +++ b/tfhe/src/core_crypto/entities/lwe_compact_ciphertext_list.rs @@ -333,6 +333,7 @@ pub type LweCompactCiphertextListMutView<'data, Scalar> = /// Structure to store the expected properties of a ciphertext /// Can be used on a server to check if client inputs are well formed /// before running a computation on them +#[derive(Copy, Clone)] pub struct LweCiphertextListParameters { pub lwe_dim: LweDimension, pub lwe_ciphertext_count_constraint: ListSizeConstraint, diff --git a/tfhe/src/high_level_api/compact_list.rs b/tfhe/src/high_level_api/compact_list.rs index 723cd7c29a..58837601bb 100644 --- a/tfhe/src/high_level_api/compact_list.rs +++ b/tfhe/src/high_level_api/compact_list.rs @@ -196,6 +196,8 @@ impl ParameterSetConformant for CompactCiphertextList { #[cfg(feature = "zk-pok")] mod zk { use super::*; + use crate::conformance::ParameterSetConformant; + use crate::integer::ciphertext::IntegerProvenCompactCiphertextListConformanceParams; #[derive(Clone, Serialize, Deserialize, Versionize)] #[versionize(ProvenCompactCiphertextListVersions)] @@ -363,6 +365,96 @@ mod zk { }) } } + + impl ParameterSetConformant for ProvenCompactCiphertextList { + type ParameterSet = IntegerProvenCompactCiphertextListConformanceParams; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { inner, tag: _ } = self; + + inner.is_conformant(parameter_set) + } + } + + #[cfg(test)] + mod test { + use super::*; + use crate::integer::ciphertext::IntegerProvenCompactCiphertextListConformanceParams; + use crate::zk::CompactPkeCrs; + use rand::{thread_rng, Rng}; + + #[test] + fn conformance_zk_compact_ciphertext_list() { + let mut rng = thread_rng(); + + let params: crate::shortint::ClassicPBSParameters = + crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let config = crate::ConfigBuilder::with_custom_parameters(params); + + let client_key = crate::ClientKey::generate(config.clone()); + // This is done in an offline phase and the CRS is shared to all clients and the server + let crs = CompactPkeCrs::from_config(config.into(), 64).unwrap(); + let public_zk_params = crs.public_params(); + let public_key = crate::CompactPublicKey::try_new(&client_key).unwrap(); + // This can be left empty, but if provided allows to tie the proof to arbitrary data + let metadata = [b'T', b'F', b'H', b'E', b'-', b'r', b's']; + + let clear_a = rng.gen::(); + let clear_b = rng.gen::(); + + let proven_compact_list = crate::ProvenCompactCiphertextList::builder(&public_key) + .push(clear_a) + .push(clear_b) + .build_with_proof_packed(public_zk_params, &metadata, ZkComputeLoad::Proof) + .unwrap(); + + let params = + IntegerProvenCompactCiphertextListConformanceParams::from_crs_and_parameters( + params.try_into().unwrap(), + &crs, + ); + + assert!(proven_compact_list.is_conformant(¶ms)); + } + + #[test] + fn conformance_zk_compact_ciphertext_list_casting() { + let mut rng = thread_rng(); + + let params = crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + + let cpk_params = crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + + let casting_params = crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + + let config = crate::ConfigBuilder::with_custom_parameters(params) + .use_dedicated_compact_public_key_parameters((cpk_params, casting_params)); + + let client_key = crate::ClientKey::generate(config.clone()); + + let crs = CompactPkeCrs::from_config(config.into(), 64).unwrap(); + let public_zk_params = crs.public_params(); + let public_key = crate::CompactPublicKey::try_new(&client_key).unwrap(); + + let metadata = [b'T', b'F', b'H', b'E', b'-', b'r', b's']; + + let clear_a = rng.gen::(); + let clear_b = rng.gen::(); + + let proven_compact_list = crate::ProvenCompactCiphertextList::builder(&public_key) + .push(clear_a) + .push(clear_b) + .build_with_proof_packed(public_zk_params, &metadata, ZkComputeLoad::Proof) + .unwrap(); + + let params = + IntegerProvenCompactCiphertextListConformanceParams::from_crs_and_parameters( + cpk_params, &crs, + ); + + assert!(proven_compact_list.is_conformant(¶ms)); + } + } } pub struct CompactCiphertextListExpander { diff --git a/tfhe/src/integer/ciphertext/compact_list.rs b/tfhe/src/integer/ciphertext/compact_list.rs index 33d730d544..dc0ffdfd92 100644 --- a/tfhe/src/integer/ciphertext/compact_list.rs +++ b/tfhe/src/integer/ciphertext/compact_list.rs @@ -11,8 +11,18 @@ pub use crate::integer::parameters::{ IntegerCompactCiphertextListCastingMode, IntegerCompactCiphertextListUnpackingMode, }; use crate::integer::{CompactPublicKey, ServerKey}; +#[cfg(feature = "zk-pok")] +use crate::shortint::ciphertext::ProvenCompactCiphertextListConformanceParams; use crate::shortint::parameters::CiphertextConformanceParams; +#[cfg(feature = "zk-pok")] +use crate::shortint::parameters::CompactCiphertextListExpansionKind; +#[cfg(feature = "zk-pok")] +use crate::shortint::parameters::{ + CarryModulus, CiphertextModulus, CompactPublicKeyEncryptionParameters, LweDimension, +}; use crate::shortint::{Ciphertext, MessageModulus}; +#[cfg(feature = "zk-pok")] +use crate::zk::CompactPkeCrs; use rayon::prelude::*; use serde::{Deserialize, Serialize}; use tfhe_versionable::Versionize; @@ -730,6 +740,58 @@ impl ProvenCompactCiphertextList { } } +#[cfg(feature = "zk-pok")] +#[derive(Copy, Clone)] +pub struct IntegerProvenCompactCiphertextListConformanceParams { + pub encryption_lwe_dimension: LweDimension, + pub message_modulus: MessageModulus, + pub carry_modulus: CarryModulus, + pub ciphertext_modulus: CiphertextModulus, + pub expansion_kind: CompactCiphertextListExpansionKind, + pub max_elements_per_compact_list: usize, +} + +#[cfg(feature = "zk-pok")] +impl IntegerProvenCompactCiphertextListConformanceParams { + pub fn from_crs_and_parameters( + value: CompactPublicKeyEncryptionParameters, + crs_params: &CompactPkeCrs, + ) -> 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, + max_elements_per_compact_list: crs_params.public_params().k, + } + } +} + +#[cfg(feature = "zk-pok")] +impl ParameterSetConformant for ProvenCompactCiphertextList { + type ParameterSet = IntegerProvenCompactCiphertextListConformanceParams; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { ct_list, info } = self; + + let total_expected_num_blocks: usize = info.iter().map(|a| a.num_blocks()).sum(); + + 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, + // packing by 2 + total_expected_lwe_count: total_expected_num_blocks.div_ceil(2), + }; + + ct_list.is_conformant(&a) + } +} + #[cfg(feature = "zk-pok")] #[cfg(test)] mod tests { diff --git a/tfhe/src/shortint/ciphertext/zk.rs b/tfhe/src/shortint/ciphertext/zk.rs index ed993eafb9..de33d1313f 100644 --- a/tfhe/src/shortint/ciphertext/zk.rs +++ b/tfhe/src/shortint/ciphertext/zk.rs @@ -1,8 +1,13 @@ +use super::{Degree, NoiseLevel}; +use crate::conformance::{ListSizeConstraint, ParameterSetConformant}; use crate::core_crypto::algorithms::verify_lwe_compact_ciphertext_list; +use crate::core_crypto::prelude::LweCiphertextListParameters; use crate::shortint::backward_compatibility::ciphertext::ProvenCompactCiphertextListVersions; use crate::shortint::ciphertext::CompactCiphertextList; use crate::shortint::parameters::{ - CompactPublicKeyEncryptionParameters, MessageModulus, ShortintCompactCiphertextListCastingMode, + CarryModulus, CiphertextListConformanceParams, CiphertextModulus, + CompactCiphertextListExpansionKind, CompactPublicKeyEncryptionParameters, LweDimension, + MessageModulus, ShortintCompactCiphertextListCastingMode, }; use crate::shortint::{Ciphertext, CompactPublicKey}; use crate::zk::{ @@ -144,6 +149,83 @@ impl ProvenCompactCiphertextList { } } +#[derive(Copy, Clone)] +pub struct ProvenCompactCiphertextListConformanceParams { + pub encryption_lwe_dimension: LweDimension, + pub message_modulus: MessageModulus, + pub carry_modulus: CarryModulus, + pub ciphertext_modulus: CiphertextModulus, + pub expansion_kind: CompactCiphertextListExpansionKind, + pub max_lwe_count_per_compact_list: usize, + pub total_expected_lwe_count: usize, +} + +impl ParameterSetConformant for ProvenCompactCiphertextList { + type ParameterSet = ProvenCompactCiphertextListConformanceParams; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + let Self { proved_lists } = self; + + let ProvenCompactCiphertextListConformanceParams { + max_lwe_count_per_compact_list, + total_expected_lwe_count, + expansion_kind, + encryption_lwe_dimension, + message_modulus, + carry_modulus, + ciphertext_modulus, + } = parameter_set; + + let max_elements_per_compact_list = *max_lwe_count_per_compact_list; + + let mut remaining_len = *total_expected_lwe_count; + + for (compact_ct_list, proof) in proved_lists { + if remaining_len == 0 { + return false; + } + + if !proof.content_is_usable() { + return false; + } + + let expected_len; + + if remaining_len > max_elements_per_compact_list { + remaining_len -= max_elements_per_compact_list; + + expected_len = max_elements_per_compact_list; + } else { + expected_len = remaining_len; + remaining_len = 0; + }; + + let params = CiphertextListConformanceParams { + ct_list_params: LweCiphertextListParameters { + lwe_dim: *encryption_lwe_dimension, + lwe_ciphertext_count_constraint: ListSizeConstraint::exact_size(expected_len), + ct_modulus: *ciphertext_modulus, + }, + message_modulus: *message_modulus, + carry_modulus: *carry_modulus, + degree: Degree::new(message_modulus.0 * message_modulus.0 - 1), + noise_level: NoiseLevel::NOMINAL, + expansion_kind: *expansion_kind, + }; + + if !compact_ct_list.is_conformant(¶ms) { + return false; + } + } + + if remaining_len != 0 { + return false; + } + + true + } +} + #[cfg(test)] mod tests { use crate::shortint::parameters::{ diff --git a/tfhe/src/shortint/parameters/mod.rs b/tfhe/src/shortint/parameters/mod.rs index e88add1ff7..2a72d523c3 100644 --- a/tfhe/src/shortint/parameters/mod.rs +++ b/tfhe/src/shortint/parameters/mod.rs @@ -248,6 +248,7 @@ pub struct CompressedCiphertextConformanceParams { /// Structure to store the expected properties of a ciphertext list /// Can be used on a server to check if client inputs are well formed /// before running a computation on them +#[derive(Copy, Clone)] pub struct CiphertextListConformanceParams { pub ct_list_params: LweCiphertextListParameters, pub message_modulus: MessageModulus, From d441514086e51ad438c326314d8f1a2a47cf42fb Mon Sep 17 00:00:00 2001 From: tmontaigu Date: Mon, 30 Sep 2024 12:06:09 +0200 Subject: [PATCH 3/5] feat(js): add constructors for PublicKeyParams --- tfhe/src/js_on_wasm_api/shortint.rs | 104 ++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/tfhe/src/js_on_wasm_api/shortint.rs b/tfhe/src/js_on_wasm_api/shortint.rs index 44b29aee1b..de0f37aaef 100644 --- a/tfhe/src/js_on_wasm_api/shortint.rs +++ b/tfhe/src/js_on_wasm_api/shortint.rs @@ -1,6 +1,7 @@ use crate::core_crypto::commons::generators::DeterministicSeeder; use crate::core_crypto::commons::math::random::Seed; use crate::core_crypto::prelude::ActivatedRandomGenerator; +use crate::js_on_wasm_api::js_high_level_api::into_js_error; use crate::shortint::parameters::classic::compact_pk::*; use crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; @@ -187,6 +188,22 @@ impl From for ShortintEncryptionKeyChoice { } } +#[derive(Copy, Clone)] +#[wasm_bindgen] +pub enum ShortintPBSOrder { + KeyswitchBootstrap, + BootstrapKeyswitch, +} + +impl From for crate::shortint::parameters::PBSOrder { + fn from(value: ShortintPBSOrder) -> Self { + match value { + ShortintPBSOrder::KeyswitchBootstrap => Self::KeyswitchBootstrap, + ShortintPBSOrder::BootstrapKeyswitch => Self::BootstrapKeyswitch, + } + } +} + #[wasm_bindgen] pub struct ShortintNoiseDistribution( pub(crate) crate::core_crypto::commons::math::random::DynamicDistribution, @@ -212,6 +229,93 @@ impl ShortintCompactPublicKeyEncryptionParameters { } } } + + #[wasm_bindgen] + pub fn new_parameters( + // Public Key Parameters + encryption_lwe_dimension: usize, + encryption_noise_distribution: &ShortintNoiseDistribution, + message_modulus: usize, + carry_modulus: usize, + modulus_power_of_2_exponent: usize, + // Casting Parameters + ks_base_log: usize, + ks_level: usize, + encryption_key_choice: ShortintEncryptionKeyChoice, + ) -> Result { + let ciphertext_modulus = + crate::shortint::parameters::CiphertextModulus::try_new_power_of_2( + modulus_power_of_2_exponent, + ) + .map_err(into_js_error)?; + + let compact_pke_params = crate::shortint::parameters::compact_public_key_only::CompactPublicKeyEncryptionParameters::try_new( + LweDimension(encryption_lwe_dimension), + encryption_noise_distribution.0, + MessageModulus(message_modulus), + CarryModulus(carry_modulus), + ciphertext_modulus, + // These parameters always requires casting + crate::shortint::parameters::CompactCiphertextListExpansionKind::RequiresCasting + ).map_err(into_js_error)?; + + let casting_parameters = + crate::shortint::parameters::key_switching::ShortintKeySwitchingParameters { + ks_base_log: DecompositionBaseLog(ks_base_log), + ks_level: DecompositionLevelCount(ks_level), + destination_key: encryption_key_choice.into(), + }; + + Ok(ShortintCompactPublicKeyEncryptionParameters { + compact_pke_params, + casting_parameters, + }) + } +} + +#[wasm_bindgen] +pub struct ShortintCompactPublicKeyConformanceParameters { + pub(crate) compact_pke_params: + crate::shortint::parameters::compact_public_key_only::CompactPublicKeyEncryptionParameters, +} + +#[wasm_bindgen] +impl ShortintCompactPublicKeyConformanceParameters { + #[wasm_bindgen] + pub fn new( + params: &ShortintCompactPublicKeyEncryptionParameters, + ) -> ShortintCompactPublicKeyConformanceParameters { + ShortintCompactPublicKeyConformanceParameters { + compact_pke_params: params.compact_pke_params, + } + } + + #[wasm_bindgen] + pub fn new_parameters( + encryption_lwe_dimension: usize, + encryption_noise_distribution: &ShortintNoiseDistribution, + message_modulus: usize, + carry_modulus: usize, + modulus_power_of_2_exponent: usize, + ) -> Result { + let ciphertext_modulus = + crate::shortint::parameters::CiphertextModulus::try_new_power_of_2( + modulus_power_of_2_exponent, + ) + .map_err(into_js_error)?; + + crate::shortint::parameters::compact_public_key_only::CompactPublicKeyEncryptionParameters::try_new( + LweDimension(encryption_lwe_dimension), + encryption_noise_distribution.0, + MessageModulus(message_modulus), + CarryModulus(carry_modulus), + ciphertext_modulus, + // If we are checking the conformance of the public key, it means + // we are using dedicated params for the public key, thus, casting is required + crate::shortint::parameters::CompactCiphertextListExpansionKind::RequiresCasting + ).map_err(into_js_error) + .map(|compact_pke_params| ShortintCompactPublicKeyConformanceParameters { compact_pke_params }) + } } macro_rules! expose_predefined_parameters { From 9bdf049e90f066e8dd410f0f8d1f55ac62674c5f Mon Sep 17 00:00:00 2001 From: Nicolas Sarlin Date: Mon, 30 Sep 2024 14:01:45 +0200 Subject: [PATCH 4/5] feat(js): add safe_serialize_conformant for public keys --- .../js_on_wasm_api/js_high_level_api/keys.rs | 29 +++++++++++++++++++ tfhe/src/js_on_wasm_api/shortint.rs | 12 ++++---- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/tfhe/src/js_on_wasm_api/js_high_level_api/keys.rs b/tfhe/src/js_on_wasm_api/js_high_level_api/keys.rs index 4c1f661fde..886c2224c4 100644 --- a/tfhe/src/js_on_wasm_api/js_high_level_api/keys.rs +++ b/tfhe/src/js_on_wasm_api/js_high_level_api/keys.rs @@ -1,6 +1,7 @@ use crate::high_level_api as hlapi; use crate::js_on_wasm_api::js_high_level_api::config::TfheConfig; use crate::js_on_wasm_api::js_high_level_api::{catch_panic, catch_panic_result, into_js_error}; +use crate::js_on_wasm_api::shortint::ShortintCompactPublicKeyEncryptionParameters; use wasm_bindgen::prelude::*; #[wasm_bindgen] @@ -308,6 +309,20 @@ impl TfheCompactPublicKey { .map_err(into_js_error) }) } + + #[wasm_bindgen] + pub fn safe_deserialize_conformant( + buffer: &[u8], + serialized_size_limit: u64, + conformance_params: &ShortintCompactPublicKeyEncryptionParameters, + ) -> Result { + catch_panic_result(|| { + crate::safe_serialization::DeserializationConfig::new(serialized_size_limit) + .deserialize_from(buffer, &conformance_params.compact_pke_params) + .map(Self) + .map_err(into_js_error) + }) + } } #[wasm_bindgen] @@ -364,4 +379,18 @@ impl TfheCompressedCompactPublicKey { .map_err(into_js_error) }) } + + #[wasm_bindgen] + pub fn safe_deserialize_conformant( + buffer: &[u8], + serialized_size_limit: u64, + conformance_params: &ShortintCompactPublicKeyEncryptionParameters, + ) -> Result { + catch_panic_result(|| { + crate::safe_serialization::DeserializationConfig::new(serialized_size_limit) + .deserialize_from(buffer, &conformance_params.compact_pke_params) + .map(Self) + .map_err(into_js_error) + }) + } } diff --git a/tfhe/src/js_on_wasm_api/shortint.rs b/tfhe/src/js_on_wasm_api/shortint.rs index de0f37aaef..3bebf954c0 100644 --- a/tfhe/src/js_on_wasm_api/shortint.rs +++ b/tfhe/src/js_on_wasm_api/shortint.rs @@ -1,3 +1,4 @@ +#![allow(clippy::use_self)] use crate::core_crypto::commons::generators::DeterministicSeeder; use crate::core_crypto::commons::math::random::Seed; use crate::core_crypto::prelude::ActivatedRandomGenerator; @@ -231,6 +232,7 @@ impl ShortintCompactPublicKeyEncryptionParameters { } #[wasm_bindgen] + #[allow(clippy::too_many_arguments)] pub fn new_parameters( // Public Key Parameters encryption_lwe_dimension: usize, @@ -266,7 +268,7 @@ impl ShortintCompactPublicKeyEncryptionParameters { destination_key: encryption_key_choice.into(), }; - Ok(ShortintCompactPublicKeyEncryptionParameters { + Ok(Self { compact_pke_params, casting_parameters, }) @@ -282,10 +284,8 @@ pub struct ShortintCompactPublicKeyConformanceParameters { #[wasm_bindgen] impl ShortintCompactPublicKeyConformanceParameters { #[wasm_bindgen] - pub fn new( - params: &ShortintCompactPublicKeyEncryptionParameters, - ) -> ShortintCompactPublicKeyConformanceParameters { - ShortintCompactPublicKeyConformanceParameters { + pub fn new(params: &ShortintCompactPublicKeyEncryptionParameters) -> Self { + Self { compact_pke_params: params.compact_pke_params, } } @@ -314,7 +314,7 @@ impl ShortintCompactPublicKeyConformanceParameters { // we are using dedicated params for the public key, thus, casting is required crate::shortint::parameters::CompactCiphertextListExpansionKind::RequiresCasting ).map_err(into_js_error) - .map(|compact_pke_params| ShortintCompactPublicKeyConformanceParameters { compact_pke_params }) + .map(|compact_pke_params| Self { compact_pke_params }) } } From 994e87a50033888375f847bbb821feb4697e20f7 Mon Sep 17 00:00:00 2001 From: tmontaigu Date: Mon, 30 Sep 2024 14:45:19 +0200 Subject: [PATCH 5/5] chore(js): add test for compact pk conformance --- tfhe/js_on_wasm_tests/test-hlapi-signed.js | 66 +++++++++++++++++++++- tfhe/src/js_on_wasm_api/shortint.rs | 43 -------------- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/tfhe/js_on_wasm_tests/test-hlapi-signed.js b/tfhe/js_on_wasm_tests/test-hlapi-signed.js index 4fa8ebf69a..1e4b4a2f8c 100644 --- a/tfhe/js_on_wasm_tests/test-hlapi-signed.js +++ b/tfhe/js_on_wasm_tests/test-hlapi-signed.js @@ -1,6 +1,6 @@ const test = require('node:test'); const assert = require('node:assert').strict; -const { performance } = require('perf_hooks'); +const {performance} = require('perf_hooks'); const { init_panic_hook, ShortintParametersName, @@ -8,6 +8,9 @@ const { TfheClientKey, TfhePublicKey, TfheCompressedPublicKey, + TfheCompressedCompactPublicKey, + ShortintCompactPublicKeyEncryptionParametersName, + ShortintCompactPublicKeyEncryptionParameters, TfheCompactPublicKey, TfheConfigBuilder, CompressedFheInt8, @@ -23,6 +26,8 @@ const { CompactPkePublicParams, CompactPkeCrs, ZkComputeLoad, + Shortint, + ShortintEncryptionKeyChoice, } = require("../pkg/tfhe.js"); const { randomBytes, @@ -522,3 +527,62 @@ test('hlapi_compact_ciphertext_list_with_proof', (t) => { // Verifying and expanding is too slow for single threaded node tests. }); + + +test('hlapi_compact_pk_conformance', (t) => { + + const limit = BigInt(1 << 20); + + let blockParams = new ShortintParameters( + ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + ); + let publicKeyParams = new ShortintCompactPublicKeyEncryptionParameters( + ShortintCompactPublicKeyEncryptionParametersName.SHORTINT_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + ); + + let config = TfheConfigBuilder.default() + .use_custom_parameters(blockParams) + .use_dedicated_compact_public_key_parameters(publicKeyParams) + .build(); + + let clientKey = TfheClientKey.generate(config); + let compressedPublicKey = TfheCompressedCompactPublicKey.new(clientKey); + + let serializedCompressedPublicKey = compressedPublicKey.safe_serialize(limit); + let _compressedPublicKey = TfheCompressedCompactPublicKey.safe_deserialize_conformant( + serializedCompressedPublicKey, limit, publicKeyParams); + + let publicKey = compressedPublicKey.decompress(); + let serializedPublicKey = publicKey.safe_serialize(limit); + let _publicKey = TfheCompactPublicKey.safe_deserialize_conformant(serializedPublicKey, limit, publicKeyParams); + + const message_modulus = 4; + const carry_modulus = 4; + const modulus_pow_2_exponent = 64; + const ks_level = 5; + const ks_base_log = 3; + let incorrectPublicKeyParams = ShortintCompactPublicKeyEncryptionParameters.new_parameters( + 512, + Shortint.try_new_t_uniform(42), + message_modulus, + carry_modulus, + modulus_pow_2_exponent, + ks_base_log, + ks_level, + ShortintEncryptionKeyChoice.Small, + ); + + assert.throws( + () => { + let _compressedPublicKey = TfheCompressedCompactPublicKey.safe_deserialize_conformant( + serializedCompressedPublicKey, limit, incorrectPublicKeyParams); + }, + ) + + assert.throws( + () => { + let _publicKey = TfheCompactPublicKey.safe_deserialize_conformant( + serializedPublicKey, limit, incorrectPublicKeyParams); + }, + ) +}) diff --git a/tfhe/src/js_on_wasm_api/shortint.rs b/tfhe/src/js_on_wasm_api/shortint.rs index 3bebf954c0..567e8a9dcb 100644 --- a/tfhe/src/js_on_wasm_api/shortint.rs +++ b/tfhe/src/js_on_wasm_api/shortint.rs @@ -275,49 +275,6 @@ impl ShortintCompactPublicKeyEncryptionParameters { } } -#[wasm_bindgen] -pub struct ShortintCompactPublicKeyConformanceParameters { - pub(crate) compact_pke_params: - crate::shortint::parameters::compact_public_key_only::CompactPublicKeyEncryptionParameters, -} - -#[wasm_bindgen] -impl ShortintCompactPublicKeyConformanceParameters { - #[wasm_bindgen] - pub fn new(params: &ShortintCompactPublicKeyEncryptionParameters) -> Self { - Self { - compact_pke_params: params.compact_pke_params, - } - } - - #[wasm_bindgen] - pub fn new_parameters( - encryption_lwe_dimension: usize, - encryption_noise_distribution: &ShortintNoiseDistribution, - message_modulus: usize, - carry_modulus: usize, - modulus_power_of_2_exponent: usize, - ) -> Result { - let ciphertext_modulus = - crate::shortint::parameters::CiphertextModulus::try_new_power_of_2( - modulus_power_of_2_exponent, - ) - .map_err(into_js_error)?; - - crate::shortint::parameters::compact_public_key_only::CompactPublicKeyEncryptionParameters::try_new( - LweDimension(encryption_lwe_dimension), - encryption_noise_distribution.0, - MessageModulus(message_modulus), - CarryModulus(carry_modulus), - ciphertext_modulus, - // If we are checking the conformance of the public key, it means - // we are using dedicated params for the public key, thus, casting is required - crate::shortint::parameters::CompactCiphertextListExpansionKind::RequiresCasting - ).map_err(into_js_error) - .map(|compact_pke_params| Self { compact_pke_params }) - } -} - macro_rules! expose_predefined_parameters { ( $(