diff --git a/tfhe-zk-pok/src/backward_compatibility/mod.rs b/tfhe-zk-pok/src/backward_compatibility/mod.rs index 81769bd91d..b1b4b6f4ea 100644 --- a/tfhe-zk-pok/src/backward_compatibility/mod.rs +++ b/tfhe-zk-pok/src/backward_compatibility/mod.rs @@ -60,14 +60,14 @@ impl Display for IncompleteProof { impl Error for IncompleteProof {} #[derive(VersionsDispatch)] -#[allow(dead_code)] pub(crate) enum GroupElementsVersions { + #[allow(dead_code)] V0(GroupElements), } #[derive(VersionsDispatch)] -#[allow(dead_code)] pub(crate) enum SerializableGroupElementsVersions { + #[allow(dead_code)] V0(SerializableGroupElements), } diff --git a/tfhe-zk-pok/src/backward_compatibility/pke.rs b/tfhe-zk-pok/src/backward_compatibility/pke.rs index c84a026474..4ece54522f 100644 --- a/tfhe-zk-pok/src/backward_compatibility/pke.rs +++ b/tfhe-zk-pok/src/backward_compatibility/pke.rs @@ -49,8 +49,8 @@ pub enum ProofVersions { } #[derive(VersionsDispatch)] -#[allow(dead_code)] pub(crate) enum ComputeLoadProofFieldVersions { + #[allow(dead_code)] V0(ComputeLoadProofFields), } @@ -107,11 +107,11 @@ where } #[derive(VersionsDispatch)] -#[allow(dead_code)] pub(crate) enum CompressedComputeLoadProofFieldsVersions where G::G1: Compressible, G::G2: Compressible, { + #[allow(dead_code)] V0(CompressedComputeLoadProofFields), } diff --git a/tfhe-zk-pok/src/backward_compatibility/pke_v2.rs b/tfhe-zk-pok/src/backward_compatibility/pke_v2.rs index 43af705592..70894811d6 100644 --- a/tfhe-zk-pok/src/backward_compatibility/pke_v2.rs +++ b/tfhe-zk-pok/src/backward_compatibility/pke_v2.rs @@ -62,8 +62,8 @@ pub enum ProofVersions { } #[derive(VersionsDispatch)] -#[allow(dead_code)] -pub(crate) enum ComputeLoadProofFieldVersions { +pub(crate) enum ComputeLoadProofFieldsVersions { + #[allow(dead_code)] V0(ComputeLoadProofFields), } @@ -132,11 +132,11 @@ where } #[derive(VersionsDispatch)] -#[allow(dead_code)] pub(crate) enum CompressedComputeLoadProofFieldsVersions where G::G1: Compressible, G::G2: Compressible, { + #[allow(dead_code)] V0(CompressedComputeLoadProofFields), } diff --git a/tfhe-zk-pok/src/proofs/mod.rs b/tfhe-zk-pok/src/proofs/mod.rs index f3cd1b5305..fceb97ca22 100644 --- a/tfhe-zk-pok/src/proofs/mod.rs +++ b/tfhe-zk-pok/src/proofs/mod.rs @@ -153,6 +153,7 @@ fn assert_pke_proof_preconditions( big_d: usize, big_d_max: usize, ) { + assert!(k_max <= d); assert_eq!(c1.len(), d); assert_eq!(e1.len(), d); diff --git a/tfhe-zk-pok/src/proofs/pke_v2.rs b/tfhe-zk-pok/src/proofs/pke_v2.rs index b018c85111..05fc3a38ee 100644 --- a/tfhe-zk-pok/src/proofs/pke_v2.rs +++ b/tfhe-zk-pok/src/proofs/pke_v2.rs @@ -2,7 +2,7 @@ #![allow(non_snake_case)] use super::*; -use crate::backward_compatibility::pke_v2::{CompressedProofVersions, ProofVersions}; +use crate::backward_compatibility::pke_v2::*; use crate::backward_compatibility::BoundVersions; use crate::curve_api::{CompressedG1, CompressedG2}; use crate::four_squares::*; @@ -29,7 +29,7 @@ fn bit_iter(x: u64, nbits: u32) -> impl Iterator { serialize = "PublicParams: Into" ) )] -#[versionize(convert = SerializablePKEv2PublicParams)] +#[versionize(try_convert = SerializablePKEv2PublicParams)] pub struct PublicParams { pub(crate) g_lists: GroupElements, pub(crate) D: usize, @@ -300,7 +300,8 @@ impl Proof { /// These fields can be pre-computed on the prover side in the faster Verifier scheme. If that's the /// case, they should be included in the proof. -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Versionize)] +#[versionize(ComputeLoadProofFieldsVersions)] pub(crate) struct ComputeLoadProofFields { pub(crate) C_hat_h3: G::G2, pub(crate) C_hat_w: G::G2, @@ -332,11 +333,12 @@ where pub(crate) compute_load_proof_fields: Option>, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Versionize)] #[serde(bound( deserialize = "G: Curve, CompressedG1: serde::Deserialize<'de>, CompressedG2: serde::Deserialize<'de>", serialize = "G: Curve, CompressedG1: serde::Serialize, CompressedG2: serde::Serialize" ))] +#[versionize(CompressedComputeLoadProofFieldsVersions)] pub(crate) struct CompressedComputeLoadProofFields where G::G1: Compressible, @@ -496,6 +498,12 @@ pub fn compute_crs_params( msbs_zero_padding_bit_count: u64, bound_type: Bound, ) -> (usize, usize, u128, usize) { + assert!( + k <= d, + "Invalid parameters for zk_pok, the maximum number of messages k should be smaller \ +than the lwe dimension d. Please pick a smaller k: k = {k}, d = {d}" + ); + let mut B_bound_squared = { (match bound_type { // GHL factor is 9.75, 9.75**2 = 95.0625 @@ -525,7 +533,7 @@ Please select a smaller B, d and/or k" // safely used for this assert!( m_bound <= 64, - "Invalid parameters for zk_pok, w e only support 64 bits integer. \ + "Invalid parameters for zk_pok, we only support 64 bits integer. \ The computed m parameter is {m_bound} > 64. Please select a smaller B, d and/or k" ); diff --git a/tfhe/benches/integer/zk_pke.rs b/tfhe/benches/integer/zk_pke.rs index 2c97a64acd..8d789caf1f 100644 --- a/tfhe/benches/integer/zk_pke.rs +++ b/tfhe/benches/integer/zk_pke.rs @@ -8,13 +8,14 @@ use rayon::prelude::*; use std::fs::{File, OpenOptions}; use std::io::Write; use std::path::Path; +use tfhe::core_crypto::prelude::LweCiphertextCount; use tfhe::integer::key_switching_key::KeySwitchingKey; use tfhe::integer::parameters::IntegerCompactCiphertextListExpansionMode; use tfhe::integer::{ClientKey, CompactPrivateKey, CompactPublicKey, ServerKey}; use tfhe::keycache::NamedParam; use tfhe::shortint::parameters::classic::tuniform::p_fail_2_minus_64::ks_pbs::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; -use tfhe::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; -use tfhe::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +use tfhe::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +use tfhe::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use tfhe::shortint::parameters::PBSParameters; use tfhe::zk::{CompactPkeCrs, ZkComputeLoad}; use utilities::{write_to_json, OperatorType}; @@ -33,8 +34,8 @@ fn pke_zk_proof(c: &mut Criterion) { .measurement_time(std::time::Duration::from_secs(60)); for (param_pke, _param_casting, param_fhe) in [( - PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, - PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, )] { let param_name = param_fhe.name(); @@ -64,8 +65,11 @@ fn pke_zk_proof(c: &mut Criterion) { let fhe_uint_count = bits / 64; - let crs = - CompactPkeCrs::from_shortint_params(param_pke, num_block * fhe_uint_count).unwrap(); + let crs = CompactPkeCrs::from_shortint_params( + param_pke, + LweCiphertextCount(num_block * fhe_uint_count), + ) + .unwrap(); for compute_load in [ZkComputeLoad::Proof, ZkComputeLoad::Verify] { let zk_load = match compute_load { @@ -152,8 +156,8 @@ fn pke_zk_verify(c: &mut Criterion, results_file: &Path) { .expect("cannot open results file"); for (param_pke, param_casting, param_fhe) in [( - PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, - PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, )] { let param_name = param_fhe.name(); @@ -183,8 +187,11 @@ fn pke_zk_verify(c: &mut Criterion, results_file: &Path) { let fhe_uint_count = bits / 64; println!("Generating CRS... "); - let crs = - CompactPkeCrs::from_shortint_params(param_pke, num_block * fhe_uint_count).unwrap(); + let crs = CompactPkeCrs::from_shortint_params( + param_pke, + LweCiphertextCount(num_block * fhe_uint_count), + ) + .unwrap(); let shortint_params: PBSParameters = param_fhe.into(); diff --git a/tfhe/docs/guides/zk-pok.md b/tfhe/docs/guides/zk-pok.md index 321719e743..2efcf4c394 100644 --- a/tfhe/docs/guides/zk-pok.md +++ b/tfhe/docs/guides/zk-pok.md @@ -87,9 +87,9 @@ pub fn main() -> Result<(), Box> { let params = tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; // Indicate which parameters to use for the Compact Public Key encryption - let cpk_params = tfhe::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let cpk_params = tfhe::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; // And parameters allowing to keyswitch/cast to the computation parameters. - let casting_params = tfhe::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let casting_params = tfhe::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; // Enable the dedicated parameters on the config let config = tfhe::ConfigBuilder::with_custom_parameters(params) .use_dedicated_compact_public_key_parameters((cpk_params, casting_params)); diff --git a/tfhe/examples/utilities/params_to_file.rs b/tfhe/examples/utilities/params_to_file.rs index 7526649da0..36856ad145 100644 --- a/tfhe/examples/utilities/params_to_file.rs +++ b/tfhe/examples/utilities/params_to_file.rs @@ -7,7 +7,7 @@ use tfhe::core_crypto::prelude::{DynamicDistribution, TUniform, UnsignedInteger} use tfhe::keycache::NamedParam; use tfhe::shortint::parameters::classic::compact_pk::ALL_PARAMETER_VEC_COMPACT_PK; use tfhe::shortint::parameters::classic::gaussian::ALL_PARAMETER_VEC_GAUSSIAN; -use tfhe::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +use tfhe::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use tfhe::shortint::parameters::multi_bit::gaussian::ALL_MULTI_BIT_PARAMETER_VEC; use tfhe::shortint::parameters::{ CompactPublicKeyEncryptionParameters, CompressionParameters, ShortintParameterSet, @@ -285,7 +285,7 @@ fn main() { write_all_params_in_file( "shortint_cpke_parameters_lattice_estimator.sage", - &[PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64], + &[V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64], ParametersFormat::Lwe, ); diff --git a/tfhe/examples/utilities/print_doc_bench_parameters.rs b/tfhe/examples/utilities/print_doc_bench_parameters.rs index 892ebe6d1d..c13b9558d8 100644 --- a/tfhe/examples/utilities/print_doc_bench_parameters.rs +++ b/tfhe/examples/utilities/print_doc_bench_parameters.rs @@ -1,7 +1,7 @@ use tfhe::keycache::NamedParam; use tfhe::shortint::parameters::classic::tuniform::p_fail_2_minus_64::ks_pbs::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; -use tfhe::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; -use tfhe::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +use tfhe::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +use tfhe::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use tfhe::shortint::parameters::multi_bit::tuniform::p_fail_2_minus_64::ks_pbs_gpu::PARAM_GPU_MULTI_BIT_GROUP_3_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; pub fn main() { @@ -43,9 +43,9 @@ pub fn main() { println!("Compact Public Key parameters (encryption + ZK):"); println!( "{}", - stringify!(PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64) + stringify!(V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64) ); - println!("{PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64:?}\n"); + println!("{V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64:?}\n"); println!("Corresponding compute FHE parameters:"); println!( @@ -57,7 +57,7 @@ pub fn main() { println!("Keyswitch from encryption + ZK to compute parameters:"); println!( "{}", - stringify!(PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64) + stringify!(V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64) ); - println!("{PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64:?}"); + println!("{V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64:?}"); } diff --git a/tfhe/examples/utilities/shortint_key_sizes.rs b/tfhe/examples/utilities/shortint_key_sizes.rs index 50f25a7781..5ea02c201b 100644 --- a/tfhe/examples/utilities/shortint_key_sizes.rs +++ b/tfhe/examples/utilities/shortint_key_sizes.rs @@ -7,8 +7,8 @@ use std::io::Write; use std::path::Path; use tfhe::keycache::NamedParam; use tfhe::shortint::keycache::KEY_CACHE; -use tfhe::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; -use tfhe::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +use tfhe::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +use tfhe::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use tfhe::shortint::parameters::list_compression::COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use tfhe::shortint::parameters::{ PARAM_GPU_MULTI_BIT_GROUP_3_MESSAGE_1_CARRY_1_KS_PBS_GAUSSIAN_2M64, @@ -222,7 +222,7 @@ fn tuniform_key_set_sizes(results_file: &Path) { &mut file, ); - let param_pke = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let param_pke = V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let param_pke_name = stringify!(PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64); let compact_private_key = CompactPrivateKey::new(param_pke); let compressed_pk = CompressedCompactPublicKey::new(&compact_private_key); @@ -266,7 +266,7 @@ fn tuniform_key_set_sizes(results_file: &Path) { &mut file, ); - let param_casting = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let param_casting = V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let param_casting_name = stringify!(PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64); let compressed_casting_key = CompressedKeySwitchingKey::new( (&compact_private_key, None), diff --git a/tfhe/src/c_api/high_level_api/zk.rs b/tfhe/src/c_api/high_level_api/zk.rs index 868d025ba9..6c7cc18a03 100644 --- a/tfhe/src/c_api/high_level_api/zk.rs +++ b/tfhe/src/c_api/high_level_api/zk.rs @@ -23,13 +23,6 @@ impl From for crate::zk::ZkComputeLoad { pub struct CompactPkeCrs(pub(crate) crate::core_crypto::entities::CompactPkeCrs); impl_destroy_on_type!(CompactPkeCrs); -// Because we use a repr(transparent) for the CompactPkeCrs, cbindgen will define CompactPkeCrs as -// an alias for CompactPkePublicParams. We need to define this struct even if it will not actually -// be used in the C api. -#[allow(unused)] -pub struct CompactPkePublicParams(pub(crate) crate::core_crypto::entities::CompactPkePublicParams); -impl_destroy_on_type!(CompactPkePublicParams); - /// Serializes the CRS /// /// If compress is true, the data will be compressed (less serialized bytes), however, this makes @@ -144,7 +137,7 @@ pub unsafe extern "C" fn compact_pke_crs_deserialize_from_params( *result = std::ptr::null_mut(); - let deserialized: crate::core_crypto::entities::CompactPkePublicParams = + let deserialized: crate::core_crypto::entities::ZkCompactPkeV1PublicParams = bincode::deserialize(buffer_view.as_slice()).unwrap(); let crs = deserialized.into(); @@ -170,7 +163,7 @@ pub unsafe extern "C" fn compact_pke_crs_safe_deserialize_from_params( let buffer_view: &[u8] = buffer_view.as_slice(); - let deserialized: crate::core_crypto::entities::CompactPkePublicParams = + let deserialized: crate::core_crypto::entities::ZkCompactPkeV1PublicParams = crate::safe_serialization::DeserializationConfig::new(serialized_size_limit) .disable_conformance() .deserialize_from(buffer_view) diff --git a/tfhe/src/c_api/shortint/parameters.rs b/tfhe/src/c_api/shortint/parameters.rs index 969c16bfd4..f71bf9155c 100644 --- a/tfhe/src/c_api/shortint/parameters.rs +++ b/tfhe/src/c_api/shortint/parameters.rs @@ -2,8 +2,8 @@ pub use crate::core_crypto::commons::dispersion::StandardDev; pub use crate::core_crypto::commons::parameters::{ DecompositionBaseLog, DecompositionLevelCount, GlweDimension, LweDimension, PolynomialSize, }; -pub 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; -pub use crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +pub use crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +pub use crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; pub use crate::shortint::parameters::*; #[repr(C)] @@ -243,8 +243,8 @@ impl ShortintCompactPublicKeyEncryptionParameters { pub static SHORTINT_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: ShortintCompactPublicKeyEncryptionParameters = ShortintCompactPublicKeyEncryptionParameters::convert(( - PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, - PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, )); macro_rules! expose_as_shortint_pbs_parameters( diff --git a/tfhe/src/core_crypto/algorithms/lwe_encryption.rs b/tfhe/src/core_crypto/algorithms/lwe_encryption.rs index 0e82160348..51eb3238d9 100644 --- a/tfhe/src/core_crypto/algorithms/lwe_encryption.rs +++ b/tfhe/src/core_crypto/algorithms/lwe_encryption.rs @@ -15,8 +15,6 @@ use crate::core_crypto::commons::parameters::*; use crate::core_crypto::commons::traits::*; use crate::core_crypto::entities::*; use rayon::prelude::*; -#[cfg(feature = "zk-pok")] -use tfhe_zk_pok::proofs::pke::{commit, prove}; /// Convenience function to share the core logic of the LWE encryption between all functions needing /// it. @@ -1858,8 +1856,7 @@ where BodyDistribution: BoundedDistribution, KeyCont: Container, { - let public_params = crs.public_params(); - let exclusive_max = public_params.exclusive_max_noise(); + let exclusive_max = crs.exclusive_max_noise(); if Scalar::BITS < 64 && (1u64 << Scalar::BITS) >= exclusive_max { return Err( "The given random distribution would create random values out \ @@ -1893,28 +1890,23 @@ where return Err("Zero knowledge proof do not support moduli greater than 2**64".into()); } - let expected_q = if Scalar::BITS == 64 { - 0u64 - } else { - 164 << Scalar::BITS - }; - - if expected_q != public_params.q { + if ciphertext_modulus != crs.ciphertext_modulus() { return Err("Mismatched modulus between CRS and ciphertexts".into()); } - if ciphertext_count.0 > public_params.k { + if ciphertext_count > crs.max_num_messages() { return Err(format!( "CRS allows at most {} ciphertexts to be proven at once, {} contained in the list", - public_params.k, ciphertext_count.0 + crs.max_num_messages().0, + ciphertext_count.0 ) .into()); } - if lwe_compact_public_key.lwe_dimension().0 > public_params.d { + if lwe_compact_public_key.lwe_dimension() > crs.lwe_dimension() { return Err(format!( "CRS allows a LweDimension of at most {}, current dimension: {}", - public_params.d, + crs.lwe_dimension().0, lwe_compact_public_key.lwe_dimension().0 ) .into()); @@ -1922,10 +1914,10 @@ where // 2**64 /delta == ((2**63) / delta) *2 let plaintext_modulus = ((1u64 << (u64::BITS - 1) as usize) / u64::cast_from(delta)) * 2; - if plaintext_modulus != public_params.t { + if plaintext_modulus != crs.plaintext_modulus() { return Err(format!( "Mismatched plaintext modulus: CRS expects {}, requested modulus: {plaintext_modulus:?}", - public_params.t + crs.plaintext_modulus() ).into()); } @@ -2174,7 +2166,7 @@ pub fn encrypt_lwe_ciphertext_with_compact_public_key< /// /// let crs = CompactPkeCrs::new( /// lwe_dimension, -/// 1, +/// LweCiphertextCount(1), /// glwe_noise_distribution, /// ciphertext_modulus, /// zk_plaintext_modulus, @@ -2291,52 +2283,18 @@ where encryption_generator, ); - let (c1, c2) = output.get_mask_and_body(); - - let (public_commit, private_commit) = commit( - lwe_compact_public_key - .get_mask() - .as_ref() - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - lwe_compact_public_key - .get_body() - .as_ref() - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - c1.as_ref() - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - vec![i64::cast_from(*c2.data)], - binary_random_vector - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - mask_noise - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - vec![i64::cast_from(message.0)], - body_noise - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - crs.public_params(), - random_generator, - ); - - Ok(prove( - (crs.public_params(), &public_commit), - &private_commit, + Ok(crs.prove( + lwe_compact_public_key, + &vec![message.0], + &LweCompactCiphertextList::from_container( + output.as_ref(), + output.lwe_size(), + LweCiphertextCount(1), + output.ciphertext_modulus(), + ), + &binary_random_vector, + &mask_noise, + &body_noise, metadata, load, random_generator, @@ -2652,7 +2610,7 @@ pub fn encrypt_lwe_compact_ciphertext_list_with_compact_public_key< /// /// let crs = CompactPkeCrs::new( /// lwe_dimension, -/// lwe_ciphertext_count.0, +/// lwe_ciphertext_count, /// glwe_noise_distribution, /// ciphertext_modulus, /// zk_plaintext_modulus, @@ -2807,61 +2765,13 @@ where encryption_generator, ); - let (c1, c2) = output.get_mask_and_body_list(); - - let (public_commit, private_commit) = commit( - lwe_compact_public_key - .get_mask() - .as_ref() - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - lwe_compact_public_key - .get_body() - .as_ref() - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - c1.as_ref() - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - c2.as_ref() - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - binary_random_vector - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - mask_noise - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - messages - .as_ref() - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - body_noise - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - crs.public_params(), - random_generator, - ); - - Ok(prove( - (crs.public_params(), &public_commit), - &private_commit, + Ok(crs.prove( + lwe_compact_public_key, + messages, + output, + &binary_random_vector, + &mask_noise, + &body_noise, metadata, load, random_generator, @@ -3186,7 +3096,7 @@ pub fn par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key< /// /// let crs = CompactPkeCrs::new( /// lwe_dimension, -/// lwe_ciphertext_count.0, +/// lwe_ciphertext_count, /// glwe_noise_distribution, /// ciphertext_modulus, /// zk_plaintext_modulus, @@ -3341,61 +3251,13 @@ where encryption_generator, ); - let (c1, c2) = output.get_mask_and_body_list(); - - let (public_commit, private_commit) = commit( - lwe_compact_public_key - .get_mask() - .as_ref() - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - lwe_compact_public_key - .get_body() - .as_ref() - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - c1.as_ref() - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - c2.as_ref() - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - binary_random_vector - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - mask_noise - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - messages - .as_ref() - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - body_noise - .iter() - .copied() - .map(CastFrom::cast_from) - .collect::>(), - crs.public_params(), - random_generator, - ); - - Ok(prove( - (crs.public_params(), &public_commit), - &private_commit, + Ok(crs.prove( + lwe_compact_public_key, + messages, + output, + &binary_random_vector, + &mask_noise, + &body_noise, metadata, load, random_generator, diff --git a/tfhe/src/core_crypto/algorithms/lwe_zero_knowledge_verification.rs b/tfhe/src/core_crypto/algorithms/lwe_zero_knowledge_verification.rs index 529ad681ae..54545b24f7 100644 --- a/tfhe/src/core_crypto/algorithms/lwe_zero_knowledge_verification.rs +++ b/tfhe/src/core_crypto/algorithms/lwe_zero_knowledge_verification.rs @@ -1,7 +1,8 @@ use crate::core_crypto::entities::{LweCompactCiphertextList, LweCompactPublicKey}; -use crate::core_crypto::prelude::{CastFrom, Container, LweCiphertext, UnsignedInteger}; -use crate::zk::{CompactPkeCrs, CompactPkeProof, ZkVerificationOutCome}; -use tfhe_zk_pok::proofs::pke::{verify, PublicCommit}; +use crate::core_crypto::prelude::{ + CastFrom, Container, LweCiphertext, LweCiphertextCount, UnsignedInteger, +}; +use crate::zk::{CompactPkeCrs, CompactPkeProof, ZkVerificationOutcome}; /// Verifies with the given proof that a [`LweCompactCiphertextList`] /// is valid. @@ -11,94 +12,39 @@ pub fn verify_lwe_compact_ciphertext_list( proof: &CompactPkeProof, crs: &CompactPkeCrs, metadata: &[u8], -) -> ZkVerificationOutCome +) -> ZkVerificationOutcome where Scalar: UnsignedInteger, i64: CastFrom, ListCont: Container, KeyCont: Container, { - if Scalar::BITS > 64 { - return ZkVerificationOutCome::Invalid; - } - let public_commit = PublicCommit::new( - compact_public_key - .get_mask() - .as_ref() - .iter() - .copied() - .map(|x| i64::cast_from(x)) - .collect(), - compact_public_key - .get_body() - .as_ref() - .iter() - .copied() - .map(|x| i64::cast_from(x)) - .collect(), - lwe_compact_list - .get_mask_list() - .as_ref() - .iter() - .copied() - .map(|x| i64::cast_from(x)) - .collect(), - lwe_compact_list - .get_body_list() - .as_ref() - .iter() - .copied() - .map(|x| i64::cast_from(x)) - .collect(), - ); - match verify(proof, (crs.public_params(), &public_commit), metadata) { - Ok(_) => ZkVerificationOutCome::Valid, - Err(_) => ZkVerificationOutCome::Invalid, - } + crs.verify(lwe_compact_list, compact_public_key, proof, metadata) } +/// Verifies with the given proof that a single [`LweCiphertext`] is valid. pub fn verify_lwe_ciphertext( lwe_ciphertext: &LweCiphertext, compact_public_key: &LweCompactPublicKey, proof: &CompactPkeProof, crs: &CompactPkeCrs, metadata: &[u8], -) -> ZkVerificationOutCome +) -> ZkVerificationOutcome where Scalar: UnsignedInteger, i64: CastFrom, Cont: Container, KeyCont: Container, { - if Scalar::BITS > 64 { - return ZkVerificationOutCome::Invalid; - } - let public_commit = PublicCommit::new( - compact_public_key - .get_mask() - .as_ref() - .iter() - .copied() - .map(|x| i64::cast_from(x)) - .collect(), - compact_public_key - .get_body() - .as_ref() - .iter() - .copied() - .map(|x| i64::cast_from(x)) - .collect(), - lwe_ciphertext - .get_mask() - .as_ref() - .iter() - .copied() - .map(|x| i64::cast_from(x)) - .collect(), - vec![i64::cast_from(*lwe_ciphertext.get_body().data); 1], - ); - match verify(proof, (crs.public_params(), &public_commit), metadata) { - Ok(_) => ZkVerificationOutCome::Valid, - Err(_) => ZkVerificationOutCome::Invalid, - } + crs.verify( + &LweCompactCiphertextList::from_container( + lwe_ciphertext.as_ref(), + lwe_ciphertext.lwe_size(), + LweCiphertextCount(1), + lwe_ciphertext.ciphertext_modulus(), + ), + compact_public_key, + proof, + metadata, + ) } diff --git a/tfhe/src/core_crypto/algorithms/test/lwe_encryption.rs b/tfhe/src/core_crypto/algorithms/test/lwe_encryption.rs index 6b5f09adb9..51d3922607 100644 --- a/tfhe/src/core_crypto/algorithms/test/lwe_encryption.rs +++ b/tfhe/src/core_crypto/algorithms/test/lwe_encryption.rs @@ -1022,9 +1022,10 @@ fn lwe_compact_public_encrypt_prove_verify_decrypt_custom_mod( let mut msg = msg_modulus; let delta: Scalar = encoding_with_padding / msg_modulus; - let crs = CompactPkeCrs::new( + // Test zk scheme v1 and v2 + let crs_v2 = CompactPkeCrs::new( lwe_dimension, - 1, + LweCiphertextCount(1), glwe_noise_distribution, ciphertext_modulus, msg_modulus * Scalar::TWO, @@ -1033,68 +1034,81 @@ fn lwe_compact_public_encrypt_prove_verify_decrypt_custom_mod( ) .unwrap(); - while msg != Scalar::ZERO { - msg = msg.wrapping_sub(Scalar::ONE); - for _ in 0..NB_TESTS { - let lwe_sk = allocate_and_generate_new_binary_lwe_secret_key( - lwe_dimension, - &mut rsc.secret_random_generator, - ); + let crs_v1 = CompactPkeCrs::new_legacy_v1( + lwe_dimension, + LweCiphertextCount(1), + glwe_noise_distribution, + ciphertext_modulus, + msg_modulus * Scalar::TWO, + ZkMSBZeroPaddingBitCount(1), + &mut random_generator, + ) + .unwrap(); - let pk = allocate_and_generate_new_lwe_compact_public_key( - &lwe_sk, - glwe_noise_distribution, - ciphertext_modulus, - &mut rsc.encryption_random_generator, - ); + for crs in [&crs_v2, &crs_v1] { + while msg != Scalar::ZERO { + msg = msg.wrapping_sub(Scalar::ONE); + for _ in 0..NB_TESTS { + let lwe_sk = allocate_and_generate_new_binary_lwe_secret_key( + lwe_dimension, + &mut rsc.secret_random_generator, + ); - let mut ct = LweCiphertext::new( - Scalar::ZERO, - lwe_dimension.to_lwe_size(), - ciphertext_modulus, - ); + let pk = allocate_and_generate_new_lwe_compact_public_key( + &lwe_sk, + glwe_noise_distribution, + ciphertext_modulus, + &mut rsc.encryption_random_generator, + ); - let proof = encrypt_and_prove_lwe_ciphertext_with_compact_public_key( - &pk, - &mut ct, - Cleartext(msg), - delta, - glwe_noise_distribution, - glwe_noise_distribution, - &mut rsc.secret_random_generator, - &mut rsc.encryption_random_generator, - &mut random_generator, - &crs, - &metadata, - ZkComputeLoad::Proof, - ) - .unwrap(); + let mut ct = LweCiphertext::new( + Scalar::ZERO, + lwe_dimension.to_lwe_size(), + ciphertext_modulus, + ); - assert!(check_encrypted_content_respects_mod( - &ct, - ciphertext_modulus, - )); + let proof = encrypt_and_prove_lwe_ciphertext_with_compact_public_key( + &pk, + &mut ct, + Cleartext(msg), + delta, + glwe_noise_distribution, + glwe_noise_distribution, + &mut rsc.secret_random_generator, + &mut rsc.encryption_random_generator, + &mut random_generator, + crs, + &metadata, + ZkComputeLoad::Proof, + ) + .unwrap(); - let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct); + assert!(check_encrypted_content_respects_mod( + &ct, + ciphertext_modulus, + )); - let decoded = round_decode(decrypted.0, delta) % msg_modulus; + let decrypted = decrypt_lwe_ciphertext(&lwe_sk, &ct); - assert_eq!(msg, decoded); + let decoded = round_decode(decrypted.0, delta) % msg_modulus; + + assert_eq!(msg, decoded); - // Verify the proof - assert!(verify_lwe_ciphertext(&ct, &pk, &proof, &crs, &metadata).is_valid()); + // Verify the proof + assert!(verify_lwe_ciphertext(&ct, &pk, &proof, crs, &metadata).is_valid()); - // verify proof with invalid ciphertext - let index = random_generator.gen::() % ct.as_ref().len(); - let value_to_add = random_generator.gen::(); - ct.as_mut()[index] = ct.as_mut()[index].wrapping_add(value_to_add); - assert!(verify_lwe_ciphertext(&ct, &pk, &proof, &crs, &metadata).is_invalid()); - } + // verify proof with invalid ciphertext + let index = random_generator.gen::() % ct.as_ref().len(); + let value_to_add = random_generator.gen::(); + ct.as_mut()[index] = ct.as_mut()[index].wrapping_add(value_to_add); + assert!(verify_lwe_ciphertext(&ct, &pk, &proof, crs, &metadata).is_invalid()); + } - // In coverage, we break after one while loop iteration, changing message values does not - // yield higher coverage - #[cfg(tarpaulin)] - break; + // In coverage, we break after one while loop iteration, changing message values does + // not yield higher coverage + #[cfg(tarpaulin)] + break; + } } } @@ -1125,7 +1139,7 @@ fn test_par_compact_lwe_list_public_key_encryption_and_proof() { let max_num_body = 512; let crs = CompactPkeCrs::new( lwe_dimension, - max_num_body, + LweCiphertextCount(max_num_body), glwe_noise_distribution, ciphertext_modulus, plaintext_modulus, diff --git a/tfhe/src/core_crypto/commons/parameters.rs b/tfhe/src/core_crypto/commons/parameters.rs index 0eeb5cac3a..16d1b04258 100644 --- a/tfhe/src/core_crypto/commons/parameters.rs +++ b/tfhe/src/core_crypto/commons/parameters.rs @@ -25,7 +25,9 @@ pub struct CleartextCount(pub usize); pub struct CiphertextCount(pub usize); /// The number of ciphertexts in an lwe ciphertext list. -#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize, Versionize)] +#[derive( + Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug, Serialize, Deserialize, Versionize, +)] #[versionize(LweCiphertextCountVersions)] pub struct LweCiphertextCount(pub usize); diff --git a/tfhe/src/high_level_api/compact_list.rs b/tfhe/src/high_level_api/compact_list.rs index e7ff1a640c..13c040c45a 100644 --- a/tfhe/src/high_level_api/compact_list.rs +++ b/tfhe/src/high_level_api/compact_list.rs @@ -218,7 +218,7 @@ mod zk { crs: &CompactPkeCrs, pk: &CompactPublicKey, metadata: &[u8], - ) -> crate::zk::ZkVerificationOutCome { + ) -> crate::zk::ZkVerificationOutcome { self.inner.verify(crs, &pk.key.key, metadata) } @@ -350,9 +350,9 @@ mod zk { 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 cpk_params = crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_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 casting_params = crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_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)); @@ -574,16 +574,16 @@ mod tests { #[test] fn test_compact_list_with_casting() { - 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; + use crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + use crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let config = crate::ConfigBuilder::with_custom_parameters( PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, ) .use_dedicated_compact_public_key_parameters(( - PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, - PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, )) .build(); @@ -720,16 +720,16 @@ mod tests { #[cfg(feature = "zk-pok")] #[test] fn test_proven_compact_list_with_casting() { - 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; + use crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + use crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let config = crate::ConfigBuilder::with_custom_parameters( PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, ) .use_dedicated_compact_public_key_parameters(( - PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, - PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, )) .build(); 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 9fa87c8f3e..9d0a7853bb 100644 --- a/tfhe/src/high_level_api/integers/unsigned/tests/cpu.rs +++ b/tfhe/src/high_level_api/integers/unsigned/tests/cpu.rs @@ -4,8 +4,8 @@ use crate::high_level_api::{generate_keys, set_server_key, ConfigBuilder, FheUin use crate::integer::U256; use crate::safe_serialization::{DeserializationConfig, SerializationConfig}; 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; +use crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +use crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use crate::shortint::parameters::*; use crate::{ ClientKey, CompactCiphertextList, CompactCiphertextListConformanceParams, CompactPublicKey, @@ -505,9 +505,9 @@ fn test_safe_deserialize_conformant_compact_fhe_uint32() { #[test] fn test_cpk_encrypt_cast_compute_hl() { - let param_pke_only = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let param_pke_only = V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let param_fhe = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let param_ksk = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let param_ksk = V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let num_block = 4usize; @@ -554,9 +554,9 @@ fn test_cpk_encrypt_cast_compute_hl() { #[test] fn test_compressed_cpk_encrypt_cast_compute_hl() { - let param_pke_only = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let param_pke_only = V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let param_fhe = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let param_ksk = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let param_ksk = V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let num_block = 4usize; diff --git a/tfhe/src/high_level_api/keys/public.rs b/tfhe/src/high_level_api/keys/public.rs index 86298c1bab..9233c2b18c 100644 --- a/tfhe/src/high_level_api/keys/public.rs +++ b/tfhe/src/high_level_api/keys/public.rs @@ -304,9 +304,9 @@ mod 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 cpk_params = crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_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 casting_params = crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_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)); @@ -340,9 +340,9 @@ mod 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 cpk_params = crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_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 casting_params = crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_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)); diff --git a/tfhe/src/high_level_api/keys/server.rs b/tfhe/src/high_level_api/keys/server.rs index f5e5467079..9ecb81109a 100644 --- a/tfhe/src/high_level_api/keys/server.rs +++ b/tfhe/src/high_level_api/keys/server.rs @@ -437,9 +437,9 @@ mod test { { let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let cpk_params = ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let cpk_params = ks_pbs::V0_11_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 casting_params = crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_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)); @@ -495,9 +495,9 @@ mod test { { let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let mut cpk_params = ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let mut cpk_params = ks_pbs::V0_11_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 casting_params = crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_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)); @@ -562,9 +562,9 @@ mod test { { let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let cpk_params = ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let cpk_params = ks_pbs::V0_11_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 casting_params = crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_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)); @@ -620,9 +620,9 @@ mod test { { let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let mut cpk_params = ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let mut cpk_params = ks_pbs::V0_11_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 casting_params = crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_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)); diff --git a/tfhe/src/high_level_api/tests/tags_on_entities.rs b/tfhe/src/high_level_api/tests/tags_on_entities.rs index c963749694..dee88cd3dc 100644 --- a/tfhe/src/high_level_api/tests/tags_on_entities.rs +++ b/tfhe/src/high_level_api/tests/tags_on_entities.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -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; +use crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +use crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use crate::shortint::parameters::list_compression::COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use crate::shortint::parameters::*; use crate::shortint::ClassicPBSParameters; @@ -18,13 +18,13 @@ fn test_tag_propagation_cpu() { Device::Cpu, PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, Some(( - PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, - PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, )), Some(COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64), Some(( PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M64, - PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, )), ) } @@ -145,7 +145,7 @@ fn test_tag_propagation_gpu() { Some(COMP_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64), Some(( PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M64, - PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, )), ) } diff --git a/tfhe/src/high_level_api/zk.rs b/tfhe/src/high_level_api/zk.rs index 179403877b..1ec291473b 100644 --- a/tfhe/src/high_level_api/zk.rs +++ b/tfhe/src/high_level_api/zk.rs @@ -1,3 +1,4 @@ +use crate::core_crypto::prelude::LweCiphertextCount; use crate::zk::CompactPkeCrs; use crate::{Config, Error}; @@ -24,7 +25,10 @@ impl CompactPkeCrs { * compact_encryption_parameters.message_modulus.0) .ilog2() as usize; let max_num_message = max_bit_size.div_ceil(carry_and_message_bit_capacity); - let crs = Self::from_shortint_params(compact_encryption_parameters, max_num_message)?; + let crs = Self::from_shortint_params( + compact_encryption_parameters, + LweCiphertextCount(max_num_message), + )?; Ok(crs) } } diff --git a/tfhe/src/integer/backward_compatibility/ciphertext/mod.rs b/tfhe/src/integer/backward_compatibility/ciphertext/mod.rs index 1a2459e83a..3061b8a277 100644 --- a/tfhe/src/integer/backward_compatibility/ciphertext/mod.rs +++ b/tfhe/src/integer/backward_compatibility/ciphertext/mod.rs @@ -77,8 +77,8 @@ pub enum CompressedModulusSwitchedRadixCiphertextVersions { } #[derive(VersionsDispatch)] -#[allow(dead_code)] pub(crate) enum CompressedModulusSwitchedRadixCiphertextGenericVersions { + #[allow(dead_code)] V0(CompressedModulusSwitchedRadixCiphertextGeneric), } diff --git a/tfhe/src/integer/ciphertext/compact_list.rs b/tfhe/src/integer/ciphertext/compact_list.rs index 1d7d977968..2de5c812fc 100644 --- a/tfhe/src/integer/ciphertext/compact_list.rs +++ b/tfhe/src/integer/ciphertext/compact_list.rs @@ -21,7 +21,7 @@ use crate::shortint::parameters::{ }; use crate::shortint::{CarryModulus, Ciphertext, MessageModulus}; #[cfg(feature = "zk-pok")] -use crate::zk::{CompactPkeCrs, ZkComputeLoad, ZkVerificationOutCome}; +use crate::zk::{CompactPkeCrs, CompactPkeZkScheme, ZkComputeLoad, ZkVerificationOutcome}; use rayon::prelude::*; use serde::{Deserialize, Serialize}; @@ -783,7 +783,7 @@ impl ProvenCompactCiphertextList { crs: &CompactPkeCrs, public_key: &CompactPublicKey, metadata: &[u8], - ) -> ZkVerificationOutCome { + ) -> ZkVerificationOutcome { self.ct_list.verify(crs, &public_key.key, metadata) } @@ -1000,6 +1000,7 @@ pub struct IntegerProvenCompactCiphertextListConformanceParams { pub ciphertext_modulus: CiphertextModulus, pub expansion_kind: CompactCiphertextListExpansionKind, pub max_elements_per_compact_list: usize, + pub zk_scheme: CompactPkeZkScheme, } #[cfg(feature = "zk-pok")] @@ -1021,7 +1022,8 @@ impl IntegerProvenCompactCiphertextListConformanceParams { carry_modulus: value.carry_modulus, ciphertext_modulus: value.ciphertext_modulus, expansion_kind: value.expansion_kind, - max_elements_per_compact_list: crs.max_num_messages(), + max_elements_per_compact_list: crs.max_num_messages().0, + zk_scheme: crs.scheme_version(), } } } @@ -1044,6 +1046,7 @@ impl ParameterSetConformant for ProvenCompactCiphertextList { 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), + zk_scheme: parameter_set.zk_scheme, }; ct_list.is_conformant(&a) @@ -1062,6 +1065,7 @@ mod tests { } use super::{DataKind, ProvenCompactCiphertextList}; + use crate::core_crypto::prelude::LweCiphertextCount; use crate::integer::ciphertext::CompactCiphertextList; use crate::integer::key_switching_key::KeySwitchingKey; use crate::integer::parameters::IntegerCompactCiphertextListExpansionMode; @@ -1069,15 +1073,15 @@ mod tests { BooleanBlock, ClientKey, CompactPrivateKey, CompactPublicKey, RadixCiphertext, ServerKey, }; use crate::shortint::parameters::classic::tuniform::p_fail_2_minus_64::ks_pbs::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - 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; + use crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + use crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use crate::zk::{CompactPkeCrs, ZkComputeLoad}; use rand::random; #[test] fn test_zk_compact_ciphertext_list_encryption_ci_run_filter() { - let pke_params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let ksk_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let pke_params = V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let ksk_params = V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let fhe_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let metadata = [b'i', b'n', b't', b'e', b'g', b'e', b'r']; @@ -1089,7 +1093,7 @@ mod tests { .checked_pow(num_blocks as u32) .unwrap(); - let crs = CompactPkeCrs::from_shortint_params(pke_params, 512).unwrap(); + let crs = CompactPkeCrs::from_shortint_params(pke_params, LweCiphertextCount(512)).unwrap(); let cks = ClientKey::new(fhe_params); let sk = ServerKey::new_radix_server_key(&cks); let compact_private_key = CompactPrivateKey::new(pke_params); @@ -1138,8 +1142,8 @@ mod tests { #[test] fn test_several_proven_lists() { - let pke_params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let ksk_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let pke_params = V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let ksk_params = V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let fhe_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let metadata = [b'i', b'n', b't', b'e', b'g', b'e', b'r']; @@ -1148,7 +1152,11 @@ mod tests { 64 / ((pke_params.message_modulus.0 * pke_params.carry_modulus.0).ilog2() as usize); let encryption_num_blocks = 64 / (pke_params.message_modulus.0.ilog2() as usize); - let crs = CompactPkeCrs::from_shortint_params(pke_params, crs_blocks_for_64_bits).unwrap(); + let crs = CompactPkeCrs::from_shortint_params( + pke_params, + LweCiphertextCount(crs_blocks_for_64_bits), + ) + .unwrap(); let cks = ClientKey::new(fhe_params); let sk = ServerKey::new_radix_server_key(&cks); let compact_private_key = CompactPrivateKey::new(pke_params); @@ -1197,8 +1205,8 @@ mod tests { fn test_malicious_boolean_proven_lists() { use super::DataKind; - let pke_params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let ksk_params = PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let pke_params = V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let ksk_params = V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let fhe_params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let metadata = [b'i', b'n', b't', b'e', b'g', b'e', b'r']; @@ -1207,7 +1215,11 @@ mod tests { 64 / ((pke_params.message_modulus.0 * pke_params.carry_modulus.0).ilog2() as usize); let encryption_num_blocks = 64 / (pke_params.message_modulus.0.ilog2() as usize); - let crs = CompactPkeCrs::from_shortint_params(pke_params, crs_blocks_for_64_bits).unwrap(); + let crs = CompactPkeCrs::from_shortint_params( + pke_params, + LweCiphertextCount(crs_blocks_for_64_bits), + ) + .unwrap(); let cks = ClientKey::new(fhe_params); let sk = ServerKey::new_radix_server_key(&cks); let compact_private_key = CompactPrivateKey::new(pke_params); diff --git a/tfhe/src/integer/key_switching_key/test.rs b/tfhe/src/integer/key_switching_key/test.rs index 7445e2a47f..5f1e6cd372 100644 --- a/tfhe/src/integer/key_switching_key/test.rs +++ b/tfhe/src/integer/key_switching_key/test.rs @@ -5,10 +5,10 @@ use crate::integer::{ ClientKey, CompactPrivateKey, CompactPublicKey, CrtClientKey, IntegerCiphertext, IntegerKeyKind, RadixCiphertext, RadixClientKey, ServerKey, }; -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::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_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_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, - PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, }; use crate::shortint::parameters::{ ClassicPBSParameters, CompactPublicKeyEncryptionParameters, ShortintKeySwitchingParameters, @@ -243,17 +243,17 @@ fn test_case_cpk_encrypt_cast_compute( #[test] fn test_cpk_encrypt_cast_to_small_compute_big_ci_run_filter() { test_case_cpk_encrypt_cast_compute( - PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, - PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, ) } #[test] fn test_cpk_encrypt_cast_to_big_compute_big_ci_run_filter() { test_case_cpk_encrypt_cast_compute( - PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, - PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, ) } diff --git a/tfhe/src/js_on_wasm_api/js_high_level_api/zk.rs b/tfhe/src/js_on_wasm_api/js_high_level_api/zk.rs index 839afaa64f..89fe53987a 100644 --- a/tfhe/src/js_on_wasm_api/js_high_level_api/zk.rs +++ b/tfhe/src/js_on_wasm_api/js_high_level_api/zk.rs @@ -1,5 +1,6 @@ use wasm_bindgen::prelude::*; +use crate::core_crypto::prelude::LweCiphertextCount; use crate::js_on_wasm_api::js_high_level_api::config::TfheConfig; use crate::js_on_wasm_api::js_high_level_api::{catch_panic_result, into_js_error}; use crate::js_on_wasm_api::shortint::ShortintParameters; @@ -86,7 +87,7 @@ impl CompactPkeCrs { catch_panic_result(|| { crate::core_crypto::entities::CompactPkeCrs::from_shortint_params( parameters.0, - max_num_message, + LweCiphertextCount(max_num_message), ) .map(CompactPkeCrs) .map_err(into_js_error) @@ -107,7 +108,7 @@ impl CompactPkeCrs { // If buffer is compressed it is automatically detected and uncompressed. catch_panic_result(|| { bincode::deserialize(buffer) - .map(crate::zk::CompactPkePublicParams::into) + .map(crate::zk::ZkCompactPkeV1PublicParams::into) .map(CompactPkeCrs) .map_err(into_js_error) }) @@ -122,7 +123,7 @@ impl CompactPkeCrs { crate::safe_serialization::DeserializationConfig::new(serialized_size_limit) .disable_conformance() .deserialize_from(buffer) - .map(crate::zk::CompactPkePublicParams::into) + .map(crate::zk::ZkCompactPkeV1PublicParams::into) .map(CompactPkeCrs) .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 8a50fd827b..19cf3cf427 100644 --- a/tfhe/src/js_on_wasm_api/shortint.rs +++ b/tfhe/src/js_on_wasm_api/shortint.rs @@ -4,8 +4,8 @@ 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; +use crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +use crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use crate::shortint::parameters::*; use std::panic::set_hook; use wasm_bindgen::prelude::*; @@ -225,8 +225,8 @@ impl ShortintCompactPublicKeyEncryptionParameters { pub fn new(name: ShortintCompactPublicKeyEncryptionParametersName) -> Self { match name { ShortintCompactPublicKeyEncryptionParametersName::SHORTINT_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64 => Self { - compact_pke_params: PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, - casting_parameters: PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + compact_pke_params: V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + casting_parameters: V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, } } } diff --git a/tfhe/src/shortint/backward_compatibility/ciphertext/mod.rs b/tfhe/src/shortint/backward_compatibility/ciphertext/mod.rs index 2e4ce9dd17..50a31e00e2 100644 --- a/tfhe/src/shortint/backward_compatibility/ciphertext/mod.rs +++ b/tfhe/src/shortint/backward_compatibility/ciphertext/mod.rs @@ -134,8 +134,8 @@ pub enum CompressedModulusSwitchedCiphertextVersions { } #[derive(VersionsDispatch)] -#[allow(dead_code)] pub(crate) enum InternalCompressedModulusSwitchedCiphertextVersions { + #[allow(dead_code)] V0(InternalCompressedModulusSwitchedCiphertext), } diff --git a/tfhe/src/shortint/ciphertext/zk.rs b/tfhe/src/shortint/ciphertext/zk.rs index ac8e8ac000..92ebca740c 100644 --- a/tfhe/src/shortint/ciphertext/zk.rs +++ b/tfhe/src/shortint/ciphertext/zk.rs @@ -1,7 +1,7 @@ use super::Degree; use crate::conformance::{ListSizeConstraint, ParameterSetConformant}; use crate::core_crypto::algorithms::verify_lwe_compact_ciphertext_list; -use crate::core_crypto::prelude::LweCiphertextListParameters; +use crate::core_crypto::prelude::{LweCiphertextCount, LweCiphertextListParameters}; use crate::shortint::backward_compatibility::ciphertext::ProvenCompactCiphertextListVersions; use crate::shortint::ciphertext::CompactCiphertextList; use crate::shortint::parameters::{ @@ -10,7 +10,10 @@ use crate::shortint::parameters::{ MessageModulus, ShortintCompactCiphertextListCastingMode, }; use crate::shortint::{Ciphertext, CompactPublicKey}; -use crate::zk::{CompactPkeCrs, CompactPkeProof, ZkMSBZeroPaddingBitCount, ZkVerificationOutCome}; +use crate::zk::{ + CompactPkeCrs, CompactPkeProof, CompactPkeZkScheme, ZkMSBZeroPaddingBitCount, + ZkVerificationOutcome, +}; use rayon::prelude::*; use serde::{Deserialize, Serialize}; use tfhe_versionable::Versionize; @@ -19,7 +22,10 @@ impl CompactPkeCrs { /// Construct the CRS that corresponds to the given parameters /// /// max_num_message is how many message a single proof can prove - pub fn from_shortint_params(params: P, max_num_message: usize) -> crate::Result + pub fn from_shortint_params( + params: P, + max_num_message: LweCiphertextCount, + ) -> crate::Result where P: TryInto, crate::Error: From, @@ -48,6 +54,42 @@ impl CompactPkeCrs { ) }) } + + /// Construct the CRS for the legacy V1 zk scheme that corresponds to the given parameters + /// + /// max_num_message is how many message a single proof can prove + pub fn from_shortint_params_legacy_v1( + params: P, + max_num_message: LweCiphertextCount, + ) -> crate::Result + where + P: TryInto, + crate::Error: From, + { + let params: CompactPublicKeyEncryptionParameters = params.try_into()?; + let (size, noise_distribution) = ( + params.encryption_lwe_dimension, + params.encryption_noise_distribution, + ); + + let mut plaintext_modulus = params.message_modulus.0 * params.carry_modulus.0; + // Our plaintext modulus does not take into account the bit of padding + plaintext_modulus *= 2; + + // 1 padding bit for the PBS + // Note that if we want to we can prove carry bits are 0 should we need it + crate::shortint::engine::ShortintEngine::with_thread_local_mut(|engine| { + Self::new_legacy_v1( + size, + max_num_message, + noise_distribution, + params.ciphertext_modulus, + plaintext_modulus, + ZkMSBZeroPaddingBitCount(1), + &mut engine.random_generator, + ) + }) + } } /// A List of CompactCiphertext with their zero-knowledge proofs @@ -166,7 +208,7 @@ impl ProvenCompactCiphertextList { crs: &CompactPkeCrs, public_key: &CompactPublicKey, metadata: &[u8], - ) -> ZkVerificationOutCome { + ) -> ZkVerificationOutcome { let all_valid = self.proved_lists.par_iter().all(|(ct_list, proof)| { verify_lwe_compact_ciphertext_list( &ct_list.ct_list, @@ -179,9 +221,9 @@ impl ProvenCompactCiphertextList { }); if all_valid { - ZkVerificationOutCome::Valid + ZkVerificationOutcome::Valid } else { - ZkVerificationOutCome::Invalid + ZkVerificationOutcome::Invalid } } @@ -203,6 +245,7 @@ pub struct ProvenCompactCiphertextListConformanceParams { pub expansion_kind: CompactCiphertextListExpansionKind, pub max_lwe_count_per_compact_list: usize, pub total_expected_lwe_count: usize, + pub zk_scheme: CompactPkeZkScheme, } impl ParameterSetConformant for ProvenCompactCiphertextList { @@ -219,6 +262,7 @@ impl ParameterSetConformant for ProvenCompactCiphertextList { message_modulus, carry_modulus, ciphertext_modulus, + zk_scheme, } = parameter_set; let max_elements_per_compact_list = *max_lwe_count_per_compact_list; @@ -226,7 +270,7 @@ impl ParameterSetConformant for ProvenCompactCiphertextList { let mut remaining_len = *total_expected_lwe_count; for (compact_ct_list, proof) in proved_lists { - if !proof.is_conformant(&()) { + if !proof.is_conformant(zk_scheme) { return false; } @@ -272,6 +316,7 @@ impl ParameterSetConformant for ProvenCompactCiphertextList { #[cfg(test)] mod tests { + use crate::core_crypto::prelude::LweCiphertextCount; use crate::shortint::parameters::{ ShortintCompactCiphertextListCastingMode, PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, }; @@ -283,7 +328,7 @@ mod tests { fn test_zk_ciphertext_encryption_ci_run_filter() { let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let crs = CompactPkeCrs::from_shortint_params(params, 4).unwrap(); + let crs = CompactPkeCrs::from_shortint_params(params, LweCiphertextCount(4)).unwrap(); let cks = ClientKey::new(params); let pk = CompactPublicKey::new(&cks); @@ -330,7 +375,7 @@ mod tests { fn test_zk_compact_ciphertext_list_encryption_ci_run_filter() { let params = PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let crs = CompactPkeCrs::from_shortint_params(params, 512).unwrap(); + let crs = CompactPkeCrs::from_shortint_params(params, LweCiphertextCount(512)).unwrap(); let cks = ClientKey::new(params); let pk = CompactPublicKey::new(&cks); diff --git a/tfhe/src/shortint/keycache.rs b/tfhe/src/shortint/keycache.rs index f1329b0869..27d74407c7 100644 --- a/tfhe/src/shortint/keycache.rs +++ b/tfhe/src/shortint/keycache.rs @@ -4,7 +4,7 @@ use crate::keycache::utils::named_params_impl; use crate::keycache::*; use crate::shortint::parameters::classic::compact_pk::*; use crate::shortint::parameters::classic::tuniform::p_fail_2_minus_64::ks_pbs::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; -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::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; #[cfg(tarpaulin)] use crate::shortint::parameters::coverage_parameters::*; use crate::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::PARAM_KEYSWITCH_1_1_KS_PBS_TO_2_2_KS_PBS; @@ -323,14 +323,14 @@ impl NamedParam for CompressionParameters { impl NamedParam for CompactPublicKeyEncryptionParameters { fn name(&self) -> String { named_params_impl!(expose - PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64 + V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64 ); named_params_impl!( { *self; Self - } == (PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64) + } == (V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64) ); format!( diff --git a/tfhe/src/shortint/parameters/compact_public_key_only/p_fail_2_minus_64/ks_pbs.rs b/tfhe/src/shortint/parameters/compact_public_key_only/p_fail_2_minus_64/ks_pbs.rs index 02783d7a17..2dc7895d8b 100644 --- a/tfhe/src/shortint/parameters/compact_public_key_only/p_fail_2_minus_64/ks_pbs.rs +++ b/tfhe/src/shortint/parameters/compact_public_key_only/p_fail_2_minus_64/ks_pbs.rs @@ -6,13 +6,26 @@ use crate::shortint::parameters::{ MessageModulus, }; -pub const PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: CompactPublicKeyEncryptionParameters = - CompactPublicKeyEncryptionParameters { - encryption_lwe_dimension: LweDimension(1024), - encryption_noise_distribution: DynamicDistribution::new_t_uniform(42), - message_modulus: MessageModulus(4), - carry_modulus: CarryModulus(4), - ciphertext_modulus: CiphertextModulus::new_native(), - expansion_kind: CompactCiphertextListExpansionKind::RequiresCasting, - } - .validate(); +/// This parameter set should be used when doing zk proof of public key encryption +pub const V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: + CompactPublicKeyEncryptionParameters = CompactPublicKeyEncryptionParameters { + encryption_lwe_dimension: LweDimension(2048), + encryption_noise_distribution: DynamicDistribution::new_t_uniform(17), + message_modulus: MessageModulus(4), + carry_modulus: CarryModulus(4), + ciphertext_modulus: CiphertextModulus::new_native(), + expansion_kind: CompactCiphertextListExpansionKind::RequiresCasting, +} +.validate(); + +/// This legacy parameter set should be used with the v1 pke zk scheme of TFHE-rs v0.10 and lower +pub const V0_10_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: + CompactPublicKeyEncryptionParameters = CompactPublicKeyEncryptionParameters { + encryption_lwe_dimension: LweDimension(1024), + encryption_noise_distribution: DynamicDistribution::new_t_uniform(42), + message_modulus: MessageModulus(4), + carry_modulus: CarryModulus(4), + ciphertext_modulus: CiphertextModulus::new_native(), + expansion_kind: CompactCiphertextListExpansionKind::RequiresCasting, +} +.validate(); diff --git a/tfhe/src/shortint/parameters/key_switching/p_fail_2_minus_64/ks_pbs.rs b/tfhe/src/shortint/parameters/key_switching/p_fail_2_minus_64/ks_pbs.rs index af46e06804..6d16881519 100644 --- a/tfhe/src/shortint/parameters/key_switching/p_fail_2_minus_64/ks_pbs.rs +++ b/tfhe/src/shortint/parameters/key_switching/p_fail_2_minus_64/ks_pbs.rs @@ -13,12 +13,13 @@ pub const PARAM_KEYSWITCH_1_1_KS_PBS_TO_2_2_KS_PBS: ShortintKeySwitchingParamete // these parameters allow to keyswitch from one set of keys of the 2_2 TUniform parameters to // another set of keys. The ciphertext will be under the small key and a PBS with the destination // keys will be applied to finish the keyswitch. -pub const PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: ShortintKeySwitchingParameters = - PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +pub const V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: + ShortintKeySwitchingParameters = + V0_11_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; // Parameters to keyswitch from input PKE 2_2 TUniform parameters to 2_2 KS_PBS compute parameters // arriving under the small key, requires a PBS to get to the big key -pub const PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: +pub const V0_11_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: ShortintKeySwitchingParameters = ShortintKeySwitchingParameters { ks_level: DecompositionLevelCount(5), ks_base_log: DecompositionBaseLog(3), @@ -27,7 +28,26 @@ pub const PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: // Parameters to keyswitch from input PKE 2_2 TUniform parameters to 2_2 KS_PBS compute parameters // arriving under the big key, requires a PBS to get to the big key -pub const PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: +pub const V0_11_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: + ShortintKeySwitchingParameters = ShortintKeySwitchingParameters { + ks_level: DecompositionLevelCount(1), + ks_base_log: DecompositionBaseLog(23), + destination_key: EncryptionKeyChoice::Big, +}; + +// These are the same parameters as they where defined in TFHE-rs 0.10 and before +pub const V0_10_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: + ShortintKeySwitchingParameters = + V0_10_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + +pub const V0_10_PARAM_KEYSWITCH_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: + ShortintKeySwitchingParameters = ShortintKeySwitchingParameters { + ks_level: DecompositionLevelCount(5), + ks_base_log: DecompositionBaseLog(3), + destination_key: EncryptionKeyChoice::Small, +}; + +pub const V0_10_PARAM_KEYSWITCH_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64: ShortintKeySwitchingParameters = ShortintKeySwitchingParameters { ks_level: DecompositionLevelCount(1), ks_base_log: DecompositionBaseLog(27), diff --git a/tfhe/src/shortint/public_key/compact.rs b/tfhe/src/shortint/public_key/compact.rs index 6cf819febc..0ae568d620 100644 --- a/tfhe/src/shortint/public_key/compact.rs +++ b/tfhe/src/shortint/public_key/compact.rs @@ -383,7 +383,7 @@ impl CompactPublicKey { // encryption let max_ciphertext_per_bin = self.key.lwe_dimension().0; // This is the maximum of lwe message a single proof can prove - let max_num_messages = crs.max_num_messages(); + let max_num_messages = crs.max_num_messages().0; // One of the two is the limiting factor for how much we can pack messages let message_chunk_size = max_num_messages.min(max_ciphertext_per_bin); diff --git a/tfhe/src/zk.rs b/tfhe/src/zk.rs deleted file mode 100644 index 8e616f2e58..0000000000 --- a/tfhe/src/zk.rs +++ /dev/null @@ -1,417 +0,0 @@ -use crate::conformance::ParameterSetConformant; -use crate::core_crypto::commons::math::random::BoundedDistribution; -use crate::core_crypto::prelude::*; -use crate::named::Named; -#[cfg(feature = "shortint")] -use crate::shortint::parameters::CompactPublicKeyEncryptionParameters; -use rand_core::RngCore; -use serde::{Deserialize, Serialize}; -use std::cmp::Ordering; -use std::collections::Bound; -use std::fmt::Debug; -use tfhe_versionable::Versionize; -use tfhe_zk_pok::proofs::pke::crs_gen; - -pub use tfhe_zk_pok::curve_api::Compressible; -pub use tfhe_zk_pok::proofs::ComputeLoad as ZkComputeLoad; -type Curve = tfhe_zk_pok::curve_api::Bls12_446; -pub type CompactPkeProof = tfhe_zk_pok::proofs::pke::Proof; - -impl Named for CompactPkeProof { - const NAME: &'static str = "zk::CompactPkeProof"; -} - -impl ParameterSetConformant for CompactPkeProof { - type ParameterSet = (); - - fn is_conformant(&self, _parameter_set: &Self::ParameterSet) -> bool { - self.is_usable() - } -} - -pub type CompactPkePublicParams = tfhe_zk_pok::proofs::pke::PublicParams; -pub type SerializableCompactPkePublicParams = - tfhe_zk_pok::serialization::SerializablePKEv1PublicParams; - -impl Named for CompactPkePublicParams { - const NAME: &'static str = "zk::CompactPkePublicParams"; -} - -pub struct CompactPkeCrsConformanceParams { - lwe_dim: LweDimension, - max_num_message: usize, - noise_bound: u64, - ciphertext_modulus: u64, - plaintext_modulus: u64, - msbs_zero_padding_bit_count: ZkMSBZeroPaddingBitCount, -} - -#[cfg(feature = "shortint")] -impl CompactPkeCrsConformanceParams { - pub fn new>( - value: P, - max_num_message: usize, - ) -> Result - where - E: Into, - { - let params: CompactPublicKeyEncryptionParameters = - value.try_into().map_err(|e| e.into())?; - - let mut plaintext_modulus = params.message_modulus.0 * params.carry_modulus.0; - // Add 1 bit of modulus for the padding bit - plaintext_modulus *= 2; - - let (lwe_dim, max_num_message, noise_bound, ciphertext_modulus, plaintext_modulus) = - CompactPkeCrs::prepare_crs_parameters( - params.encryption_lwe_dimension, - max_num_message, - params.encryption_noise_distribution, - params.ciphertext_modulus, - plaintext_modulus, - )?; - - Ok(Self { - lwe_dim, - max_num_message, - noise_bound, - ciphertext_modulus, - plaintext_modulus, - // CRS created from shortint params have 1 MSB 0bit - msbs_zero_padding_bit_count: ZkMSBZeroPaddingBitCount(1), - }) - } -} - -impl ParameterSetConformant for CompactPkePublicParams { - type ParameterSet = CompactPkeCrsConformanceParams; - - fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { - self.k <= self.d - && self.d == parameter_set.lwe_dim.0 - && self.k == parameter_set.max_num_message - && self.b == parameter_set.noise_bound - && self.q == parameter_set.ciphertext_modulus - && self.t == parameter_set.plaintext_modulus - && self.msbs_zero_padding_bit_count == parameter_set.msbs_zero_padding_bit_count.0 - && self.is_usable() - } -} - -// If we call `CompactPkePublicParams::compress` we end up with a -// `SerializableCompactPkePublicParams` that should also impl Named to be serializable with -// `safe_serialization`. Since the `CompactPkePublicParams` is transformed into a -// `SerializableCompactPkePublicParams` anyways before serialization, their impl of `Named` should -// return the same string. -impl Named for SerializableCompactPkePublicParams { - const NAME: &'static str = CompactPkePublicParams::NAME; -} - -#[derive(Copy, Clone, Eq, PartialEq)] -pub enum ZkVerificationOutCome { - /// The proof and its entity were valid - Valid, - /// The proof and its entity were not - Invalid, -} - -impl ZkVerificationOutCome { - pub fn is_valid(self) -> bool { - self == Self::Valid - } - - pub fn is_invalid(self) -> bool { - self == Self::Invalid - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct ZkMSBZeroPaddingBitCount(pub u64); - -/// The CRS (Common Reference String) of a ZK scheme is a set of values shared between the prover -/// and the verifier. -/// -/// The same CRS should be used at the prove and verify steps. -#[derive(Clone, Debug, Serialize, Deserialize, Versionize)] -#[repr(transparent)] -pub struct CompactPkeCrs { - public_params: CompactPkePublicParams, -} - -impl Named for CompactPkeCrs { - const NAME: &'static str = "zk::CompactPkeCrs"; -} - -impl From for CompactPkeCrs { - fn from(value: CompactPkePublicParams) -> Self { - Self { - public_params: value, - } - } -} - -impl CompactPkeCrs { - /// Prepare and check the CRS parameters. - /// - /// The output of this function can be used in [tfhe_zk_pok::proofs::pke::compute_crs_params]. - pub fn prepare_crs_parameters( - lwe_dim: LweDimension, - max_num_cleartext: usize, - noise_distribution: NoiseDistribution, - ciphertext_modulus: CiphertextModulus, - plaintext_modulus: Scalar, - ) -> crate::Result<(LweDimension, usize, Scalar, u64, Scalar)> - where - Scalar: UnsignedInteger + CastInto + Debug, - NoiseDistribution: BoundedDistribution, - { - // The bound for the crs has to be a power of two, - // it is [-b, b) (non-inclusive for the high bound) - // so we may have to give a bound that is bigger than - // what the distribution generates - let high_bound = match noise_distribution.high_bound() { - Bound::Included(high_b) => { - let high_b = high_b.wrapping_abs().into_unsigned(); - if high_b.is_power_of_two() { - high_b * Scalar::TWO - } else { - high_b.next_power_of_two() - } - } - Bound::Excluded(high_b) => { - let high_b = high_b.wrapping_abs().into_unsigned(); - if high_b.is_power_of_two() { - high_b - } else { - high_b.next_power_of_two() - } - } - Bound::Unbounded => { - return Err("requires bounded distribution".into()); - } - }; - - let abs_low_bound = match noise_distribution.low_bound() { - Bound::Included(low_b) => { - let low_b = low_b.wrapping_abs().into_unsigned(); - if low_b.is_power_of_two() { - low_b * Scalar::TWO - } else { - low_b.next_power_of_two() - } - } - Bound::Excluded(low_b) => { - let low_b = low_b.wrapping_abs().into_unsigned(); - if low_b.is_power_of_two() { - low_b - } else { - low_b.next_power_of_two() - } - } - Bound::Unbounded => { - return Err("requires bounded distribution".into()); - } - }; - - let noise_bound = abs_low_bound.max(high_bound); - - if Scalar::BITS > 64 && noise_bound >= (Scalar::ONE << 64usize) { - return Err("noise bounds exceeds 64 bits modulus".into()); - } - - if Scalar::BITS > 64 && plaintext_modulus >= (Scalar::ONE << 64usize) { - return Err("Plaintext modulus exceeds 64 bits modulus".into()); - } - - let q = if ciphertext_modulus.is_native_modulus() { - match Scalar::BITS.cmp(&64) { - Ordering::Greater => Err( - "Zero Knowledge proof do not support ciphertext modulus > 64 bits".to_string(), - ), - Ordering::Equal => Ok(0u64), - Ordering::Less => Ok(1u64 << Scalar::BITS), - } - } else { - let custom_modulus = ciphertext_modulus.get_custom_modulus(); - if custom_modulus > (u64::MAX) as u128 { - Err("Zero Knowledge proof do not support ciphertext modulus > 64 bits".to_string()) - } else { - Ok(custom_modulus as u64) - } - }?; - - Ok(( - lwe_dim, - max_num_cleartext, - noise_bound, - q, - plaintext_modulus, - )) - } - - /// Generates a new zk CRS from the tfhe parameters. - pub fn new( - lwe_dim: LweDimension, - max_num_cleartext: usize, - noise_distribution: NoiseDistribution, - ciphertext_modulus: CiphertextModulus, - plaintext_modulus: Scalar, - msbs_zero_padding_bit_count: ZkMSBZeroPaddingBitCount, - rng: &mut impl RngCore, - ) -> crate::Result - where - Scalar: UnsignedInteger + CastInto + Debug, - NoiseDistribution: BoundedDistribution, - { - let (d, k, b, q, t) = Self::prepare_crs_parameters( - lwe_dim, - max_num_cleartext, - noise_distribution, - ciphertext_modulus, - plaintext_modulus, - )?; - let public_params = crs_gen( - d.0, - k, - b.cast_into(), - q, - t.cast_into(), - msbs_zero_padding_bit_count.0, - rng, - ); - - Ok(Self { public_params }) - } - - /// Maximum number of messages that can be proven in a single list using this CRS - pub fn max_num_messages(&self) -> usize { - self.public_params().k - } - - pub fn public_params(&self) -> &CompactPkePublicParams { - &self.public_params - } -} - -impl ParameterSetConformant for CompactPkeCrs { - type ParameterSet = CompactPkeCrsConformanceParams; - - fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { - self.public_params.is_conformant(parameter_set) - } -} - -/// The CRS can be compressed by only storing the `x` part of the elliptic curve coordinates. -#[derive(Serialize, Deserialize, Versionize)] -#[repr(transparent)] -pub struct CompressedCompactPkeCrs { - public_params: ::Compressed, -} - -// The NAME impl is the same as CompactPkeCrs because once serialized they are represented with the -// same object. Decompression is done automatically during deserialization. -impl Named for CompressedCompactPkeCrs { - const NAME: &'static str = CompactPkeCrs::NAME; -} - -impl Compressible for CompactPkeCrs { - type Compressed = CompressedCompactPkeCrs; - - type UncompressError = ::UncompressError; - - fn compress(&self) -> Self::Compressed { - CompressedCompactPkeCrs { - public_params: self.public_params.compress(), - } - } - - fn uncompress(compressed: Self::Compressed) -> Result { - Ok(Self { - public_params: Compressible::uncompress(compressed.public_params)?, - }) - } -} - -#[cfg(all(test, feature = "shortint"))] -mod test { - use super::*; - use crate::safe_serialization::{safe_deserialize_conformant, safe_serialize}; - 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::{CarryModulus, MessageModulus}; - - #[test] - fn test_crs_conformance() { - let params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let mut bad_params = params; - bad_params.carry_modulus = CarryModulus(8); - bad_params.message_modulus = MessageModulus(8); - - let mut rng = rand::thread_rng(); - - let crs = CompactPkeCrs::new( - params.encryption_lwe_dimension, - 4, - params.encryption_noise_distribution, - params.ciphertext_modulus, - params.message_modulus.0 * params.carry_modulus.0 * 2, - ZkMSBZeroPaddingBitCount(1), - &mut rng, - ) - .unwrap(); - - let conformance_params = CompactPkeCrsConformanceParams::new(params, 4).unwrap(); - - assert!(crs.is_conformant(&conformance_params)); - - let conformance_params = CompactPkeCrsConformanceParams::new(bad_params, 4).unwrap(); - - assert!(!crs.is_conformant(&conformance_params)); - - let conformance_params = CompactPkeCrsConformanceParams::new(params, 2).unwrap(); - - assert!(!crs.is_conformant(&conformance_params)); - } - - #[test] - fn test_crs_serialization() { - let params = PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - - let mut rng = rand::thread_rng(); - - let crs = CompactPkeCrs::new( - params.encryption_lwe_dimension, - 4, - params.encryption_noise_distribution, - params.ciphertext_modulus, - params.message_modulus.0 * params.carry_modulus.0 * 2, - ZkMSBZeroPaddingBitCount(1), - &mut rng, - ) - .unwrap(); - - let conformance_params = CompactPkeCrsConformanceParams::new(params, 4).unwrap(); - - let mut serialized = Vec::new(); - safe_serialize(&crs, &mut serialized, 1 << 30).unwrap(); - - let _crs_deser: CompactPkeCrs = - safe_deserialize_conformant(serialized.as_slice(), 1 << 30, &conformance_params) - .unwrap(); - - // Check that we are able to load public params - let mut serialized = Vec::new(); - safe_serialize(crs.public_params(), &mut serialized, 1 << 30).unwrap(); - - let _params_deser: CompactPkePublicParams = - safe_deserialize_conformant(serialized.as_slice(), 1 << 30, &conformance_params) - .unwrap(); - - // Check with compression - let mut serialized = Vec::new(); - safe_serialize(&crs.compress(), &mut serialized, 1 << 30).unwrap(); - - let _crs_deser: CompactPkeCrs = - safe_deserialize_conformant(serialized.as_slice(), 1 << 30, &conformance_params) - .unwrap(); - } -} diff --git a/tfhe/src/zk/backward_compatibility.rs b/tfhe/src/zk/backward_compatibility.rs new file mode 100644 index 0000000000..a07dcaea45 --- /dev/null +++ b/tfhe/src/zk/backward_compatibility.rs @@ -0,0 +1,82 @@ +use std::convert::Infallible; + +use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; +use tfhe_zk_pok::backward_compatibility::pke::ProofV0; +use tfhe_zk_pok::backward_compatibility::IncompleteProof; +use tfhe_zk_pok::proofs::pke::Proof; +use tfhe_zk_pok::serialization::InvalidSerializedPublicParamsError; + +type Curve = tfhe_zk_pok::curve_api::Bls12_446; + +use super::{ + CompactPkeCrs, CompactPkeProof, CompressedCompactPkeCrs, SerializableCompactPkePublicParams, +}; + +#[derive(Version)] +#[repr(transparent)] +pub struct CompactPkeCrsV0(SerializableCompactPkePublicParams); + +impl Upgrade for CompactPkeCrsV0 { + type Error = InvalidSerializedPublicParamsError; + + fn upgrade(self) -> Result { + Ok(CompactPkeCrs::PkeV1(self.0.try_into()?)) + } +} + +#[derive(VersionsDispatch)] +#[allow(clippy::large_enum_variant)] +pub enum CompactPkeCrsVersions { + V0(CompactPkeCrsV0), + V1(CompactPkeCrs), +} + +#[derive(Version)] +#[repr(transparent)] +pub struct CompressedCompactPkeCrsV0(SerializableCompactPkePublicParams); + +impl Upgrade for CompressedCompactPkeCrsV0 { + type Error = Infallible; + + fn upgrade(self) -> Result { + Ok(CompressedCompactPkeCrs::PkeV1(self.0)) + } +} + +#[derive(VersionsDispatch)] +pub enum CompressedCompactPkeCrsVersions { + V0(CompressedCompactPkeCrsV0), + V1(CompressedCompactPkeCrs), +} + +#[derive(Version)] +#[repr(transparent)] +pub struct CompactPkeProofV0(ProofV0); + +impl Upgrade for CompactPkeProofV0 { + type Error = IncompleteProof; + + fn upgrade(self) -> Result { + Ok(CompactPkeProofV1(self.0.upgrade()?)) + } +} + +#[derive(Version)] +#[repr(transparent)] +pub struct CompactPkeProofV1(Proof); + +impl Upgrade for CompactPkeProofV1 { + type Error = Infallible; + + fn upgrade(self) -> Result { + Ok(CompactPkeProof::PkeV1(self.0)) + } +} + +#[derive(VersionsDispatch)] +#[allow(clippy::large_enum_variant)] +pub enum CompactPkeProofVersions { + V0(CompactPkeProofV0), + V1(CompactPkeProofV1), + V2(CompactPkeProof), +} diff --git a/tfhe/src/zk/mod.rs b/tfhe/src/zk/mod.rs new file mode 100644 index 0000000000..760489d6e7 --- /dev/null +++ b/tfhe/src/zk/mod.rs @@ -0,0 +1,791 @@ +pub mod backward_compatibility; + +use crate::conformance::ParameterSetConformant; +use crate::core_crypto::commons::math::random::{ + BoundedDistribution, ByteRandomGenerator, RandomGenerator, +}; +use crate::core_crypto::prelude::*; +use crate::named::Named; +#[cfg(feature = "shortint")] +use crate::shortint::parameters::CompactPublicKeyEncryptionParameters; +use backward_compatibility::*; +use rand_core::RngCore; +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::collections::Bound; +use std::fmt::Debug; +use tfhe_versionable::Versionize; + +use tfhe_zk_pok::proofs::pke::{ + commit as commit_v1, crs_gen as crs_gen_v1, prove as prove_v1, verify as verify_v1, + Proof as ProofV1, PublicCommit as PublicCommitV1, +}; +use tfhe_zk_pok::proofs::pke_v2::{ + commit as commit_v2, crs_gen as crs_gen_v2, prove as prove_v2, verify as verify_v2, + Proof as ProofV2, PublicCommit as PublicCommitV2, +}; + +pub use tfhe_zk_pok::curve_api::Compressible; +pub use tfhe_zk_pok::proofs::ComputeLoad as ZkComputeLoad; +type Curve = tfhe_zk_pok::curve_api::Bls12_446; + +#[derive(Clone, Debug, Serialize, Deserialize, Versionize)] +#[versionize(CompactPkeProofVersions)] +#[allow(clippy::large_enum_variant)] +pub enum CompactPkeProof { + PkeV1(ProofV1), + PkeV2(ProofV2), +} + +impl Named for CompactPkeProof { + const NAME: &'static str = "zk::CompactPkeProof"; +} + +impl ParameterSetConformant for CompactPkeProof { + type ParameterSet = CompactPkeZkScheme; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + match (self, parameter_set) { + (Self::PkeV1(proof), CompactPkeZkScheme::V1) => proof.is_usable(), + (Self::PkeV2(proof), CompactPkeZkScheme::V2) => proof.is_usable(), + (Self::PkeV1(_), CompactPkeZkScheme::V2) | (Self::PkeV2(_), CompactPkeZkScheme::V1) => { + false + } + } + } +} + +pub type ZkCompactPkeV1PublicParams = tfhe_zk_pok::proofs::pke::PublicParams; +pub type ZkCompactPkeV2PublicParams = tfhe_zk_pok::proofs::pke_v2::PublicParams; + +// Keep this to be able to deserialize CRS that were serialized as "CompactPkePublicParams" (TFHE-rs +// 0.10 and before) +pub type SerializableCompactPkePublicParams = + tfhe_zk_pok::serialization::SerializablePKEv1PublicParams; + +impl Named for ZkCompactPkeV1PublicParams { + const NAME: &'static str = "zk::CompactPkePublicParams"; +} + +pub struct CompactPkeCrsConformanceParams { + lwe_dim: LweDimension, + max_num_message: LweCiphertextCount, + noise_bound: u64, + ciphertext_modulus: u64, + plaintext_modulus: u64, + msbs_zero_padding_bit_count: ZkMSBZeroPaddingBitCount, +} + +#[cfg(feature = "shortint")] +impl CompactPkeCrsConformanceParams { + pub fn new>( + value: P, + max_num_message: LweCiphertextCount, + ) -> Result + where + E: Into, + { + let params: CompactPublicKeyEncryptionParameters = + value.try_into().map_err(|e| e.into())?; + + let mut plaintext_modulus = params.message_modulus.0 * params.carry_modulus.0; + // Add 1 bit of modulus for the padding bit + plaintext_modulus *= 2; + + let (lwe_dim, max_num_message, noise_bound, ciphertext_modulus, plaintext_modulus) = + CompactPkeCrs::prepare_crs_parameters( + params.encryption_lwe_dimension, + max_num_message, + params.encryption_noise_distribution, + params.ciphertext_modulus, + plaintext_modulus, + CompactPkeZkScheme::V2, + )?; + + Ok(Self { + lwe_dim, + max_num_message, + noise_bound, + ciphertext_modulus, + plaintext_modulus, + // CRS created from shortint params have 1 MSB 0bit + msbs_zero_padding_bit_count: ZkMSBZeroPaddingBitCount(1), + }) + } +} + +impl ParameterSetConformant for ZkCompactPkeV1PublicParams { + type ParameterSet = CompactPkeCrsConformanceParams; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + self.k <= self.d + && self.d == parameter_set.lwe_dim.0 + && self.k == parameter_set.max_num_message.0 + && self.b == parameter_set.noise_bound + && self.q == parameter_set.ciphertext_modulus + && self.t == parameter_set.plaintext_modulus + && self.msbs_zero_padding_bit_count == parameter_set.msbs_zero_padding_bit_count.0 + && self.is_usable() + } +} + +impl ParameterSetConformant for ZkCompactPkeV2PublicParams { + type ParameterSet = CompactPkeCrsConformanceParams; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + self.k <= self.d + && self.d == parameter_set.lwe_dim.0 + && self.k == parameter_set.max_num_message.0 + && self.B_inf == parameter_set.noise_bound + && self.q == parameter_set.ciphertext_modulus + && self.t == parameter_set.plaintext_modulus + && self.msbs_zero_padding_bit_count == parameter_set.msbs_zero_padding_bit_count.0 + && self.is_usable() + } +} + +// If we call `CompactPkePublicParams::compress` we end up with a +// `SerializableCompactPkePublicParams` that should also impl Named to be serializable with +// `safe_serialization`. Since the `CompactPkePublicParams` is transformed into a +// `SerializableCompactPkePublicParams` anyways before serialization, their impl of `Named` should +// return the same string. +impl Named for SerializableCompactPkePublicParams { + const NAME: &'static str = ZkCompactPkeV1PublicParams::NAME; +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum ZkVerificationOutcome { + /// The proof and its entity were valid + Valid, + /// The proof and its entity were not + Invalid, +} + +impl ZkVerificationOutcome { + pub fn is_valid(self) -> bool { + self == Self::Valid + } + + pub fn is_invalid(self) -> bool { + self == Self::Invalid + } +} + +/// The Zk Scheme for compact private key encryption is available in 2 versions. In case of doubt, +/// you should prefer the V2 which is more efficient. +#[derive(Clone, Copy)] +pub enum CompactPkeZkScheme { + V1, + V2, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ZkMSBZeroPaddingBitCount(pub u64); + +/// The CRS (Common Reference String) of a ZK scheme is a set of values shared between the prover +/// and the verifier. +/// +/// The same CRS should be used at the prove and verify steps. +#[derive(Clone, Debug, Serialize, Deserialize, Versionize)] +#[versionize(CompactPkeCrsVersions)] +#[allow(clippy::large_enum_variant)] +pub enum CompactPkeCrs { + PkeV1(ZkCompactPkeV1PublicParams), + PkeV2(ZkCompactPkeV2PublicParams), +} + +impl Named for CompactPkeCrs { + const NAME: &'static str = "zk::CompactPkeCrs"; +} + +impl From for CompactPkeCrs { + fn from(value: ZkCompactPkeV1PublicParams) -> Self { + Self::PkeV1(value) + } +} + +impl From for CompactPkeCrs { + fn from(value: ZkCompactPkeV2PublicParams) -> Self { + Self::PkeV2(value) + } +} + +impl CompactPkeCrs { + /// Compute the bound used by the V1 Scheme from the noise distribution + fn compute_bound_v1( + noise_distribution: NoiseDistribution, + ) -> Result + where + Scalar: UnsignedInteger + CastInto + Debug, + NoiseDistribution: BoundedDistribution, + { + let high_bound = match noise_distribution.high_bound() { + Bound::Included(high_b) => high_b, + Bound::Excluded(high_b) => high_b - Scalar::Signed::ONE, + Bound::Unbounded => { + return Err("requires bounded distribution".into()); + } + }; + + let low_bound = match noise_distribution.low_bound() { + Bound::Included(low_b) => low_b, + Bound::Excluded(low_b) => low_b + Scalar::Signed::ONE, + Bound::Unbounded => { + return Err("requires bounded distribution".into()); + } + }; + + if high_bound != -low_bound { + return Err("requires a distribution centered around 0".into()); + } + + // The bound for the crs has to be a power of two, + // it is [-b, b) (non-inclusive for the high bound) + // so we may have to give a bound that is bigger than + // what the distribution generates + let high_bound = high_bound.wrapping_abs().into_unsigned(); + if high_bound.is_power_of_two() { + Ok(high_bound * Scalar::TWO) + } else { + Ok(high_bound.next_power_of_two()) + } + } + + /// Compute the bound used by the V2 Scheme from the noise distribution + fn compute_bound_v2( + noise_distribution: NoiseDistribution, + ) -> Result + where + Scalar: UnsignedInteger + CastInto + Debug, + NoiseDistribution: BoundedDistribution, + { + // For zk v2 scheme, the proof is valid for an inclusive range on the noise bound + let high_bound = match noise_distribution.high_bound() { + Bound::Included(high_b) => high_b, + Bound::Excluded(high_b) => high_b - Scalar::Signed::ONE, + Bound::Unbounded => { + return Err("requires bounded distribution".into()); + } + }; + + let low_bound = match noise_distribution.low_bound() { + Bound::Included(low_b) => low_b, + Bound::Excluded(low_b) => low_b + Scalar::Signed::ONE, + Bound::Unbounded => { + return Err("requires bounded distribution".into()); + } + }; + + if high_bound != -low_bound { + return Err("requires a distribution centered around 0".into()); + } + + Ok(high_bound.wrapping_abs().into_unsigned()) + } + + /// Prepare and check the CRS parameters. + /// + /// The output of this function can be used in [tfhe_zk_pok::proofs::pke::compute_crs_params]. + pub fn prepare_crs_parameters( + lwe_dim: LweDimension, + max_num_cleartext: LweCiphertextCount, + noise_distribution: NoiseDistribution, + ciphertext_modulus: CiphertextModulus, + plaintext_modulus: Scalar, + zk_scheme: CompactPkeZkScheme, + ) -> crate::Result<(LweDimension, LweCiphertextCount, Scalar, u64, Scalar)> + where + Scalar: UnsignedInteger + CastInto + Debug, + NoiseDistribution: BoundedDistribution, + { + if max_num_cleartext.0 > lwe_dim.0 { + return Err("Maximum number of cleartexts is greater than the lwe dimension".into()); + } + + let noise_bound = match zk_scheme { + CompactPkeZkScheme::V1 => Self::compute_bound_v1(noise_distribution)?, + CompactPkeZkScheme::V2 => Self::compute_bound_v2(noise_distribution)?, + }; + + if Scalar::BITS > 64 && noise_bound >= (Scalar::ONE << 64usize) { + return Err("noise bounds exceeds 64 bits modulus".into()); + } + + if Scalar::BITS > 64 && plaintext_modulus >= (Scalar::ONE << 64usize) { + return Err("Plaintext modulus exceeds 64 bits modulus".into()); + } + + let q = if ciphertext_modulus.is_native_modulus() { + match Scalar::BITS.cmp(&64) { + Ordering::Greater => Err( + "Zero Knowledge proof do not support ciphertext modulus > 64 bits".to_string(), + ), + Ordering::Equal => Ok(0u64), + Ordering::Less => Ok(1u64 << Scalar::BITS), + } + } else { + let custom_modulus = ciphertext_modulus.get_custom_modulus(); + if custom_modulus > (u64::MAX) as u128 { + Err("Zero Knowledge proof do not support ciphertext modulus > 64 bits".to_string()) + } else { + Ok(custom_modulus as u64) + } + }?; + + Ok(( + lwe_dim, + max_num_cleartext, + noise_bound, + q, + plaintext_modulus, + )) + } + + /// Generates a new zk CRS from the tfhe parameters. This the v1 Zk PKE scheme which is less + /// efficient. + pub fn new_legacy_v1( + lwe_dim: LweDimension, + max_num_cleartext: LweCiphertextCount, + noise_distribution: NoiseDistribution, + ciphertext_modulus: CiphertextModulus, + plaintext_modulus: Scalar, + msbs_zero_padding_bit_count: ZkMSBZeroPaddingBitCount, + rng: &mut impl RngCore, + ) -> crate::Result + where + Scalar: UnsignedInteger + CastInto + Debug, + NoiseDistribution: BoundedDistribution, + { + let (d, k, b, q, t) = Self::prepare_crs_parameters( + lwe_dim, + max_num_cleartext, + noise_distribution, + ciphertext_modulus, + plaintext_modulus, + CompactPkeZkScheme::V1, + )?; + let public_params = crs_gen_v1( + d.0, + k.0, + b.cast_into(), + q, + t.cast_into(), + msbs_zero_padding_bit_count.0, + rng, + ); + + Ok(Self::PkeV1(public_params)) + } + + /// Generates a new zk CRS from the tfhe parameters. + pub fn new( + lwe_dim: LweDimension, + max_num_cleartext: LweCiphertextCount, + noise_distribution: NoiseDistribution, + ciphertext_modulus: CiphertextModulus, + plaintext_modulus: Scalar, + msbs_zero_padding_bit_count: ZkMSBZeroPaddingBitCount, + rng: &mut impl RngCore, + ) -> crate::Result + where + Scalar: UnsignedInteger + CastInto + Debug, + NoiseDistribution: BoundedDistribution, + { + let (d, k, b, q, t) = Self::prepare_crs_parameters( + lwe_dim, + max_num_cleartext, + noise_distribution, + ciphertext_modulus, + plaintext_modulus, + CompactPkeZkScheme::V2, + )?; + let public_params = crs_gen_v2( + d.0, + k.0, + b.cast_into(), + q, + t.cast_into(), + msbs_zero_padding_bit_count.0, + rng, + ); + + Ok(Self::PkeV2(public_params)) + } + + /// Maximum number of messages that can be proven in a single list using this CRS + pub fn max_num_messages(&self) -> LweCiphertextCount { + match self { + Self::PkeV1(public_params) => LweCiphertextCount(public_params.k), + Self::PkeV2(public_params) => LweCiphertextCount(public_params.k), + } + } + + /// Lwe dimension supported by this CRS + pub fn lwe_dimension(&self) -> LweDimension { + match self { + Self::PkeV1(public_params) => LweDimension(public_params.d), + Self::PkeV2(public_params) => LweDimension(public_params.d), + } + } + + /// Modulus of the ciphertexts supported by this CRS + pub fn ciphertext_modulus(&self) -> CiphertextModulus { + match self { + Self::PkeV1(public_params) => CiphertextModulus::new(public_params.q as u128), + Self::PkeV2(public_params) => CiphertextModulus::new(public_params.q as u128), + } + } + + /// Modulus of the plaintexts supported by this CRS + pub fn plaintext_modulus(&self) -> u64 { + match self { + Self::PkeV1(public_params) => public_params.t, + Self::PkeV2(public_params) => public_params.t, + } + } + + /// Upper bound on the noise accepted by this CRS + pub fn exclusive_max_noise(&self) -> u64 { + match self { + Self::PkeV1(public_params) => public_params.exclusive_max_noise(), + Self::PkeV2(public_params) => public_params.exclusive_max_noise(), + } + } + + /// Return the version of the zk scheme used by this CRS + pub fn scheme_version(&self) -> CompactPkeZkScheme { + match self { + Self::PkeV1(_) => CompactPkeZkScheme::V1, + Self::PkeV2(_) => CompactPkeZkScheme::V2, + } + } + + /// Prove a ciphertext list encryption using this CRS + #[allow(clippy::too_many_arguments)] + pub fn prove( + &self, + compact_public_key: &LweCompactPublicKey, + messages: &InputCont, + lwe_compact_list: &LweCompactCiphertextList, + binary_random_vector: &[Scalar], + mask_noise: &[Scalar], + body_noise: &[Scalar], + metadata: &[u8], + load: ZkComputeLoad, + random_generator: &mut RandomGenerator, + ) -> CompactPkeProof + where + Scalar: UnsignedInteger, + i64: CastFrom, + KeyCont: Container, + InputCont: Container, + ListCont: Container, + G: ByteRandomGenerator, + { + let key_mask = compact_public_key + .get_mask() + .as_ref() + .iter() + .copied() + .map(|x| i64::cast_from(x)) + .collect(); + let key_body = compact_public_key + .get_body() + .as_ref() + .iter() + .copied() + .map(|x| i64::cast_from(x)) + .collect(); + + let ct_mask = lwe_compact_list + .get_mask_list() + .as_ref() + .iter() + .copied() + .map(|x| i64::cast_from(x)) + .collect(); + let ct_body = lwe_compact_list + .get_body_list() + .as_ref() + .iter() + .copied() + .map(|x| i64::cast_from(x)) + .collect(); + + let binary_random_vector = binary_random_vector + .iter() + .copied() + .map(CastFrom::cast_from) + .collect::>(); + + let mask_noise = mask_noise + .iter() + .copied() + .map(CastFrom::cast_from) + .collect::>(); + + let messages = messages + .as_ref() + .iter() + .copied() + .map(CastFrom::cast_from) + .collect::>(); + + let body_noise = body_noise + .iter() + .copied() + .map(CastFrom::cast_from) + .collect::>(); + + match self { + Self::PkeV1(public_params) => { + let (public_commit, private_commit) = commit_v1( + key_mask, + key_body, + ct_mask, + ct_body, + binary_random_vector, + mask_noise, + messages, + body_noise, + public_params, + random_generator, + ); + + let proof = prove_v1( + (public_params, &public_commit), + &private_commit, + metadata, + load, + random_generator, + ); + + CompactPkeProof::PkeV1(proof) + } + Self::PkeV2(public_params) => { + let (public_commit, private_commit) = commit_v2( + key_mask, + key_body, + ct_mask, + ct_body, + binary_random_vector, + mask_noise, + messages, + body_noise, + public_params, + random_generator, + ); + + let proof = prove_v2( + (public_params, &public_commit), + &private_commit, + metadata, + load, + random_generator, + ); + + CompactPkeProof::PkeV2(proof) + } + } + } + + /// Verify the validity of a proof using this CRS + pub fn verify( + &self, + lwe_compact_list: &LweCompactCiphertextList, + compact_public_key: &LweCompactPublicKey, + proof: &CompactPkeProof, + metadata: &[u8], + ) -> ZkVerificationOutcome + where + Scalar: UnsignedInteger, + i64: CastFrom, + ListCont: Container, + KeyCont: Container, + { + if Scalar::BITS > 64 { + return ZkVerificationOutcome::Invalid; + } + + let key_mask = compact_public_key + .get_mask() + .as_ref() + .iter() + .copied() + .map(|x| i64::cast_from(x)) + .collect(); + let key_body = compact_public_key + .get_body() + .as_ref() + .iter() + .copied() + .map(|x| i64::cast_from(x)) + .collect(); + + let ct_mask = lwe_compact_list + .get_mask_list() + .as_ref() + .iter() + .copied() + .map(|x| i64::cast_from(x)) + .collect(); + let ct_body = lwe_compact_list + .get_body_list() + .as_ref() + .iter() + .copied() + .map(|x| i64::cast_from(x)) + .collect(); + + let res = match (self, proof) { + (Self::PkeV1(public_params), CompactPkeProof::PkeV1(proof)) => { + let public_commit = PublicCommitV1::new(key_mask, key_body, ct_mask, ct_body); + verify_v1(proof, (public_params, &public_commit), metadata) + } + (Self::PkeV2(public_params), CompactPkeProof::PkeV2(proof)) => { + let public_commit = PublicCommitV2::new(key_mask, key_body, ct_mask, ct_body); + verify_v2(proof, (public_params, &public_commit), metadata) + } + + (Self::PkeV1(_), CompactPkeProof::PkeV2(_)) + | (Self::PkeV2(_), CompactPkeProof::PkeV1(_)) => { + // Proof is not compatible with the CRS, so we refuse it right there + Err(()) + } + }; + + match res { + Ok(_) => ZkVerificationOutcome::Valid, + Err(_) => ZkVerificationOutcome::Invalid, + } + } +} + +impl ParameterSetConformant for CompactPkeCrs { + type ParameterSet = CompactPkeCrsConformanceParams; + + fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool { + match self { + Self::PkeV1(public_params) => public_params.is_conformant(parameter_set), + Self::PkeV2(public_params) => public_params.is_conformant(parameter_set), + } + } +} + +/// The CRS can be compressed by only storing the `x` part of the elliptic curve coordinates. +#[derive(Serialize, Deserialize, Versionize)] +#[versionize(CompressedCompactPkeCrsVersions)] +pub enum CompressedCompactPkeCrs { + PkeV1(::Compressed), + PkeV2(::Compressed), +} + +// The NAME impl is the same as CompactPkeCrs because once serialized they are represented with the +// same object. Decompression is done automatically during deserialization. +impl Named for CompressedCompactPkeCrs { + const NAME: &'static str = CompactPkeCrs::NAME; +} + +impl Compressible for CompactPkeCrs { + type Compressed = CompressedCompactPkeCrs; + + type UncompressError = ::UncompressError; + + fn compress(&self) -> Self::Compressed { + match self { + Self::PkeV1(public_params) => CompressedCompactPkeCrs::PkeV1(public_params.compress()), + Self::PkeV2(public_params) => CompressedCompactPkeCrs::PkeV2(public_params.compress()), + } + } + + fn uncompress(compressed: Self::Compressed) -> Result { + Ok(match compressed { + CompressedCompactPkeCrs::PkeV1(compressed_params) => { + Self::PkeV1(Compressible::uncompress(compressed_params)?) + } + CompressedCompactPkeCrs::PkeV2(compressed_params) => { + Self::PkeV2(Compressible::uncompress(compressed_params)?) + } + }) + } +} + +#[cfg(all(test, feature = "shortint"))] +mod test { + use super::*; + use crate::safe_serialization::{safe_deserialize_conformant, safe_serialize}; + use crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + use crate::shortint::{CarryModulus, MessageModulus}; + + #[test] + fn test_crs_conformance() { + let params = V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + let mut bad_params = params; + bad_params.carry_modulus = CarryModulus(8); + bad_params.message_modulus = MessageModulus(8); + + let mut rng = rand::thread_rng(); + + let crs = CompactPkeCrs::new( + params.encryption_lwe_dimension, + LweCiphertextCount(4), + params.encryption_noise_distribution, + params.ciphertext_modulus, + params.message_modulus.0 * params.carry_modulus.0 * 2, + ZkMSBZeroPaddingBitCount(1), + &mut rng, + ) + .unwrap(); + + let conformance_params = + CompactPkeCrsConformanceParams::new(params, LweCiphertextCount(4)).unwrap(); + + assert!(crs.is_conformant(&conformance_params)); + + let conformance_params = + CompactPkeCrsConformanceParams::new(bad_params, LweCiphertextCount(4)).unwrap(); + + assert!(!crs.is_conformant(&conformance_params)); + + let conformance_params = + CompactPkeCrsConformanceParams::new(params, LweCiphertextCount(2)).unwrap(); + + assert!(!crs.is_conformant(&conformance_params)); + } + + #[test] + fn test_crs_serialization() { + let params = V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + + let mut rng = rand::thread_rng(); + + let crs = CompactPkeCrs::new( + params.encryption_lwe_dimension, + LweCiphertextCount(4), + params.encryption_noise_distribution, + params.ciphertext_modulus, + params.message_modulus.0 * params.carry_modulus.0 * 2, + ZkMSBZeroPaddingBitCount(1), + &mut rng, + ) + .unwrap(); + + let conformance_params = + CompactPkeCrsConformanceParams::new(params, LweCiphertextCount(4)).unwrap(); + + let mut serialized = Vec::new(); + safe_serialize(&crs, &mut serialized, 1 << 30).unwrap(); + + let _crs_deser: CompactPkeCrs = + safe_deserialize_conformant(serialized.as_slice(), 1 << 30, &conformance_params) + .unwrap(); + + // Check with compression + let mut serialized = Vec::new(); + safe_serialize(&crs.compress(), &mut serialized, 1 << 30).unwrap(); + + let _crs_deser: CompactPkeCrs = + safe_deserialize_conformant(serialized.as_slice(), 1 << 30, &conformance_params) + .unwrap(); + } +} diff --git a/tfhe/tests/backward_compatibility/high_level_api.rs b/tfhe/tests/backward_compatibility/high_level_api.rs index d74ef85d5a..5b4b41de00 100644 --- a/tfhe/tests/backward_compatibility/high_level_api.rs +++ b/tfhe/tests/backward_compatibility/high_level_api.rs @@ -5,8 +5,6 @@ use tfhe::prelude::{CiphertextList, FheDecrypt, FheEncrypt}; use tfhe::shortint::PBSParameters; #[cfg(feature = "zk-pok")] use tfhe::zk::CompactPkeCrs; -#[cfg(feature = "zk-pok")] -use tfhe::zk::CompactPkePublicParams; use tfhe::{ set_server_key, ClientKey, CompactCiphertextList, CompressedCiphertextList, CompressedCompactPublicKey, CompressedFheBool, CompressedFheInt8, CompressedFheUint8, @@ -150,11 +148,6 @@ pub fn test_zk_params( test: &ZkPkePublicParamsTest, format: DataFormat, ) -> Result { - // Since CompactPkeCrs is a repr(transparent) of a CompactPkePublicParams, both can be loaded - // from the same data, so we check this. - #[cfg(feature = "zk-pok")] - let _loaded_params: CompactPkePublicParams = load_and_unversionize(dir, test, format)?; - #[cfg(feature = "zk-pok")] let _loaded_crs: CompactPkeCrs = load_and_unversionize(dir, test, format)?; @@ -187,11 +180,10 @@ pub fn test_hl_heterogeneous_ciphertext_list( #[cfg(feature = "zk-pok")] { let crs_file = dir.join(&*zk_info.params_filename); - let public_params = CompactPkePublicParams::unversionize( + let crs = CompactPkeCrs::unversionize( load_versioned_auxiliary(crs_file).map_err(|e| test.failure(e, format))?, ) .map_err(|e| test.failure(e, format))?; - let crs = CompactPkeCrs::from(public_params); let pubkey_file = dir.join(&*zk_info.public_key_filename); let pubkey = CompactPublicKey::unversionize( diff --git a/tfhe/tests/zk_wasm_x86_test.rs b/tfhe/tests/zk_wasm_x86_test.rs index 6d48a4274b..d341a97c16 100644 --- a/tfhe/tests/zk_wasm_x86_test.rs +++ b/tfhe/tests/zk_wasm_x86_test.rs @@ -11,8 +11,8 @@ use std::fs::File; use std::path::{Path, PathBuf}; use std::process::Command; use tfhe::safe_serialization::{safe_deserialize, safe_serialize}; -use tfhe::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; -use tfhe::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +use tfhe::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; +use tfhe::shortint::parameters::key_switching::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; use tfhe::zk::CompactPkeCrs; use tfhe::{ClientKey, CompactPublicKey, ConfigBuilder, ProvenCompactCiphertextList}; @@ -25,8 +25,8 @@ fn gen_key_and_crs() -> (CompactPublicKey, CompactPkeCrs) { let config = crate::ConfigBuilder::with_custom_parameters(PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64) .use_dedicated_compact_public_key_parameters(( - PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, - PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + V0_11_PARAM_KEYSWITCH_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, )) .build(); @@ -64,10 +64,10 @@ fn verify_proof( ) { println!("Verifying proof"); match proven_ct.verify(crs, public_key, &METADATA) { - tfhe::zk::ZkVerificationOutCome::Valid => { + tfhe::zk::ZkVerificationOutcome::Valid => { println!("proof verification succeeded"); } - tfhe::zk::ZkVerificationOutCome::Invalid => { + tfhe::zk::ZkVerificationOutcome::Invalid => { panic!("proof verification failed!!!") } } diff --git a/utils/tfhe-versionable-derive/src/associated.rs b/utils/tfhe-versionable-derive/src/associated.rs index d9e86e3238..a07842e008 100644 --- a/utils/tfhe-versionable-derive/src/associated.rs +++ b/utils/tfhe-versionable-derive/src/associated.rs @@ -94,9 +94,9 @@ pub(crate) enum AssociatedTypeKind { /// [`VersionType`]: crate::dispatch_type::VersionType pub(crate) trait AssociatedType: Sized { /// Bounds that will be added on the fields of the ref type definition - const REF_BOUNDS: &'static [&'static str]; + fn ref_bounds(&self) -> &'static [&'static str]; /// Bounds that will be added on the fields of the owned type definition - const OWNED_BOUNDS: &'static [&'static str]; + fn owned_bounds(&self) -> &'static [&'static str]; /// This will create the alternative of the type that holds a reference to the underlying data fn new_ref(orig_type: &DeriveInput) -> syn::Result; @@ -109,6 +109,10 @@ pub(crate) trait AssociatedType: Sized { /// Returns the kind of associated type, a ref or an owned type fn kind(&self) -> &AssociatedTypeKind; + /// Returns true if the type is transparent and trait implementation is actually deferred to the + /// inner type + fn is_transparent(&self) -> bool; + /// Returns the generics found in the original type definition fn orig_type_generics(&self) -> &Generics; @@ -119,9 +123,9 @@ pub(crate) trait AssociatedType: Sized { if let Some(lifetime) = opt_lifetime { add_lifetime_param(&mut generics, lifetime); } - add_trait_where_clause(&mut generics, self.inner_types()?, Self::REF_BOUNDS)?; + add_trait_where_clause(&mut generics, self.inner_types()?, self.ref_bounds())?; } else { - add_trait_where_clause(&mut generics, self.inner_types()?, Self::OWNED_BOUNDS)?; + add_trait_where_clause(&mut generics, self.inner_types()?, self.owned_bounds())?; } Ok(generics) @@ -254,14 +258,27 @@ impl AssociatingTrait { ) ]}; + let owned_attributes = if self.owned_type.is_transparent() { + quote! { + #[derive(#serialize_trait, #deserialize_trait)] + #[repr(transparent)] + #[serde(bound = "")] + #ignored_lints + } + } else { + quote! { + #[derive(#serialize_trait, #deserialize_trait)] + #[serde(bound = "")] + #ignored_lints + } + }; + // Creates the type declaration. These types are the output of the versioning process, so // they should be serializable. Serde might try to add automatic bounds on the type generics // even if we don't need them, so we use `#[serde(bound = "")]` to disable this. The bounds // on the generated types should be sufficient. let owned_tokens = quote! { - #[derive(#serialize_trait, #deserialize_trait)] - #[serde(bound = "")] - #ignored_lints + #owned_attributes #owned_decla #(#owned_conversion)* @@ -271,10 +288,23 @@ impl AssociatingTrait { let ref_conversion = self.ref_type.generate_conversion()?; + let ref_attributes = if self.ref_type.is_transparent() { + quote! { + #[derive(#serialize_trait)] + #[repr(transparent)] + #[serde(bound = "")] + #ignored_lints + } + } else { + quote! { + #[derive(#serialize_trait)] + #[serde(bound = "")] + #ignored_lints + } + }; + let ref_tokens = quote! { - #[derive(#serialize_trait)] - #[serde(bound = "")] - #ignored_lints + #ref_attributes #ref_decla #(#ref_conversion)* diff --git a/utils/tfhe-versionable-derive/src/dispatch_type.rs b/utils/tfhe-versionable-derive/src/dispatch_type.rs index b2f9363625..c47572afba 100644 --- a/utils/tfhe-versionable-derive/src/dispatch_type.rs +++ b/utils/tfhe-versionable-derive/src/dispatch_type.rs @@ -47,9 +47,13 @@ fn derive_input_to_enum(input: &DeriveInput) -> syn::Result { } impl AssociatedType for DispatchType { - const REF_BOUNDS: &'static [&'static str] = &[VERSION_TRAIT_NAME]; + fn ref_bounds(&self) -> &'static [&'static str] { + &[VERSION_TRAIT_NAME] + } - const OWNED_BOUNDS: &'static [&'static str] = &[VERSION_TRAIT_NAME]; + fn owned_bounds(&self) -> &'static [&'static str] { + &[VERSION_TRAIT_NAME] + } fn new_ref(orig_type: &DeriveInput) -> syn::Result { for lt in orig_type.generics.lifetimes() { @@ -109,6 +113,10 @@ impl AssociatedType for DispatchType { &self.kind } + fn is_transparent(&self) -> bool { + false + } + fn orig_type_generics(&self) -> &Generics { &self.orig_type.generics } diff --git a/utils/tfhe-versionable-derive/src/version_type.rs b/utils/tfhe-versionable-derive/src/version_type.rs index 6725878863..dc4d745b9a 100644 --- a/utils/tfhe-versionable-derive/src/version_type.rs +++ b/utils/tfhe-versionable-derive/src/version_type.rs @@ -15,10 +15,12 @@ use crate::associated::{ generate_from_trait_impl, generate_try_from_trait_impl, AssociatedType, AssociatedTypeKind, ConversionDirection, }; +use crate::versionize_attribute::is_transparent; use crate::{ add_trait_where_clause, parse_const_str, parse_trait_bound, punctuated_from_iter_result, - LIFETIME_NAME, UNVERSIONIZE_ERROR_NAME, UNVERSIONIZE_TRAIT_NAME, VERSIONIZE_OWNED_TRAIT_NAME, - VERSIONIZE_TRAIT_NAME, + INTO_TRAIT_NAME, LIFETIME_NAME, TRY_INTO_TRAIT_NAME, UNVERSIONIZE_ERROR_NAME, + UNVERSIONIZE_TRAIT_NAME, VERSIONIZE_OWNED_TRAIT_NAME, VERSIONIZE_TRAIT_NAME, + VERSION_TRAIT_NAME, }; /// The types generated for a specific version of a given exposed type. These types are identical to @@ -27,13 +29,29 @@ use crate::{ pub(crate) struct VersionType { orig_type: DeriveInput, kind: AssociatedTypeKind, + is_transparent: bool, } impl AssociatedType for VersionType { - const REF_BOUNDS: &'static [&'static str] = &[VERSIONIZE_TRAIT_NAME]; - const OWNED_BOUNDS: &'static [&'static str] = &[VERSIONIZE_OWNED_TRAIT_NAME]; + fn ref_bounds(&self) -> &'static [&'static str] { + if self.is_transparent { + &[VERSION_TRAIT_NAME] + } else { + &[VERSIONIZE_TRAIT_NAME] + } + } + + fn owned_bounds(&self) -> &'static [&'static str] { + if self.is_transparent { + &[VERSION_TRAIT_NAME] + } else { + &[VERSIONIZE_OWNED_TRAIT_NAME] + } + } fn new_ref(orig_type: &DeriveInput) -> syn::Result { + let is_transparent = is_transparent(&orig_type.attrs)?; + let lifetime = if is_unit(orig_type) { None } else { @@ -54,13 +72,17 @@ impl AssociatedType for VersionType { Ok(Self { orig_type: orig_type.clone(), kind: AssociatedTypeKind::Ref(lifetime), + is_transparent, }) } fn new_owned(orig_type: &DeriveInput) -> syn::Result { + let is_transparent = is_transparent(&orig_type.attrs)?; + Ok(Self { orig_type: orig_type.clone(), kind: AssociatedTypeKind::Owned, + is_transparent, }) } @@ -191,6 +213,10 @@ impl AssociatedType for VersionType { &self.kind } + fn is_transparent(&self) -> bool { + self.is_transparent + } + fn orig_type_generics(&self) -> &Generics { &self.orig_type.generics } @@ -198,13 +224,15 @@ impl AssociatedType for VersionType { fn conversion_generics(&self, direction: ConversionDirection) -> syn::Result { let mut generics = self.type_generics()?; - if let ConversionDirection::AssociatedToOrig = direction { - if let AssociatedTypeKind::Owned = &self.kind { - add_trait_where_clause( - &mut generics, - self.inner_types()?, - &[UNVERSIONIZE_TRAIT_NAME], - )?; + if !self.is_transparent { + if let ConversionDirection::AssociatedToOrig = direction { + if let AssociatedTypeKind::Owned = &self.kind { + add_trait_where_clause( + &mut generics, + self.inner_types()?, + &[UNVERSIONIZE_TRAIT_NAME], + )?; + } } } @@ -323,25 +351,46 @@ impl VersionType { fields_iter: I, ) -> impl IntoIterator> + 'a { let kind = self.kind.clone(); + let is_transparent = self.is_transparent; + fields_iter.into_iter().map(move |field| { let unver_ty = field.ty.clone(); - let versionize_trait = parse_trait_bound(VERSIONIZE_TRAIT_NAME)?; - let versionize_owned_trait = parse_trait_bound(VERSIONIZE_OWNED_TRAIT_NAME)?; + if is_transparent { + // If the type is transparent, we reuse the "Version" impl of the inner type + let version_trait = parse_trait_bound(VERSION_TRAIT_NAME)?; - let ty: Type = match &kind { - AssociatedTypeKind::Ref(lifetime) => parse_quote! { - <#unver_ty as #versionize_trait>::Versioned<#lifetime> - }, - AssociatedTypeKind::Owned => parse_quote! { - <#unver_ty as #versionize_owned_trait>::VersionedOwned - }, - }; + let ty: Type = match &kind { + AssociatedTypeKind::Ref(lifetime) => parse_quote! { + <#unver_ty as #version_trait>::Ref<#lifetime> + }, + AssociatedTypeKind::Owned => parse_quote! { + <#unver_ty as #version_trait>::Owned + }, + }; - Ok(Field { - ty, - ..field.clone() - }) + Ok(Field { + ty, + ..field.clone() + }) + } else { + let versionize_trait = parse_trait_bound(VERSIONIZE_TRAIT_NAME)?; + let versionize_owned_trait = parse_trait_bound(VERSIONIZE_OWNED_TRAIT_NAME)?; + + let ty: Type = match &kind { + AssociatedTypeKind::Ref(lifetime) => parse_quote! { + <#unver_ty as #versionize_trait>::Versioned<#lifetime> + }, + AssociatedTypeKind::Owned => parse_quote! { + <#unver_ty as #versionize_owned_trait>::VersionedOwned + }, + }; + + Ok(Field { + ty, + ..field.clone() + }) + } }) } @@ -520,7 +569,11 @@ impl VersionType { let ty = &field.ty; let param = quote! { #arg_ident.#field_ident }; - let rhs = self.generate_constructor_field_rhs(ty, param, false, direction)?; + let rhs = if self.is_transparent() { + self.generate_constructor_transparent_rhs(param, direction)? + } else { + self.generate_constructor_field_rhs(ty, param, false, direction)? + }; Ok(quote! { #field_ident: #rhs @@ -542,12 +595,16 @@ impl VersionType { .map(move |(arg_name, field)| { // Ok to unwrap because the field is named so field.ident is Some let field_ident = field.ident.as_ref().unwrap(); - let rhs = self.generate_constructor_field_rhs( - &field.ty, - quote! {#arg_name}, - true, - direction, - )?; + let rhs = if self.is_transparent() { + self.generate_constructor_transparent_rhs(quote! {#arg_name}, direction)? + } else { + self.generate_constructor_field_rhs( + &field.ty, + quote! {#arg_name}, + true, + direction, + )? + }; Ok(quote! { #field_ident: #rhs }) @@ -596,7 +653,11 @@ impl VersionType { let ty = &field.ty; let param = quote! { #arg_ident.#idx }; - self.generate_constructor_field_rhs(ty, param, false, direction) + if self.is_transparent { + self.generate_constructor_transparent_rhs(param, direction) + } else { + self.generate_constructor_field_rhs(ty, param, false, direction) + } } /// Generates the constructor for the fields of an unnamed enum variant. @@ -612,7 +673,16 @@ impl VersionType { ) -> syn::Result { let fields: syn::Result> = zip(arg_names, fields) .map(move |(arg_name, field)| { - self.generate_constructor_field_rhs(&field.ty, quote! {#arg_name}, true, direction) + if self.is_transparent { + self.generate_constructor_transparent_rhs(quote! {#arg_name}, direction) + } else { + self.generate_constructor_field_rhs( + &field.ty, + quote! {#arg_name}, + true, + direction, + ) + } }) .collect(); let fields = fields?; @@ -664,6 +734,41 @@ panic!("No conversion should be generated between associated ref type to origina }; Ok(field_constructor) } + + fn generate_constructor_transparent_rhs( + &self, + field_param: TokenStream, + direction: ConversionDirection, + ) -> syn::Result { + let into_trait: Path = parse_const_str(INTO_TRAIT_NAME); + let try_into_trait: Path = parse_const_str(TRY_INTO_TRAIT_NAME); + + let field_constructor = match direction { + ConversionDirection::OrigToAssociated => match self.kind { + AssociatedTypeKind::Ref(_) => { + quote! { + #into_trait::into(&#field_param) + } + } + AssociatedTypeKind::Owned => { + quote! { + #into_trait::into(#field_param) + } + } + }, + ConversionDirection::AssociatedToOrig => match self.kind { + AssociatedTypeKind::Ref(_) => { + panic!("No conversion should be generated between associated ref type to original type"); + } + AssociatedTypeKind::Owned => { + quote! { + #try_into_trait::try_into(#field_param)? + } + } + }, + }; + Ok(field_constructor) + } } /// Generates a list of argument names. This is used to create a pattern matching of a diff --git a/utils/tfhe-versionable-derive/src/versionize_attribute.rs b/utils/tfhe-versionable-derive/src/versionize_attribute.rs index f077f3b27b..e89f72da5c 100644 --- a/utils/tfhe-versionable-derive/src/versionize_attribute.rs +++ b/utils/tfhe-versionable-derive/src/versionize_attribute.rs @@ -11,7 +11,7 @@ use syn::{Attribute, Expr, Lit, Meta, Path, Token}; const VERSIONIZE_ATTR_NAME: &str = "versionize"; /// Transparent mode can also be activated using `#[repr(transparent)]` -const REPR_ATTR_NAME: &str = "repr"; +pub(crate) const REPR_ATTR_NAME: &str = "repr"; /// Represent the parsed `#[versionize(...)]` attribute pub(crate) enum VersionizeAttribute { @@ -167,16 +167,14 @@ impl VersionizeAttribute { .filter(|attr| attr.path().is_ident(VERSIONIZE_ATTR_NAME)) .collect(); - let repr_attributes: Vec<&Attribute> = attributes - .iter() - .filter(|attr| attr.path().is_ident(REPR_ATTR_NAME)) - .collect(); + // Check if transparent mode is enabled via repr(transparent). It can also be enabled with + // the versionize attribute. + let type_is_transparent = is_transparent(attributes)?; match version_attributes.as_slice() { [] => { - // transparent mode can also be enabled via `#[repr(transparent)]` - if let Some(attr) = repr_attributes.first() { - Self::parse_from_attribute(attr) + if type_is_transparent { + Ok(Self::Transparent) } else { Err(syn::Error::new( Span::call_site(), @@ -298,3 +296,23 @@ fn parse_path_ignore_quotes(value: &Expr) -> syn::Result { )), } } + +/// Check if the target type has the `#[repr(transparent)]` attribute in its attributes list +pub(crate) fn is_transparent(attributes: &[Attribute]) -> syn::Result { + if let Some(attr) = attributes + .iter() + .find(|attr| attr.path().is_ident(REPR_ATTR_NAME)) + { + let nested = attr.parse_args_with(Punctuated::::parse_terminated)?; + + for meta in nested.iter() { + if let Meta::Path(path) = meta { + if path.is_ident("transparent") { + return Ok(true); + } + } + } + } + + Ok(false) +} diff --git a/utils/tfhe-versionable/examples/manual_impl.rs b/utils/tfhe-versionable/examples/manual_impl.rs index efc3323218..58b043fd2a 100644 --- a/utils/tfhe-versionable/examples/manual_impl.rs +++ b/utils/tfhe-versionable/examples/manual_impl.rs @@ -84,9 +84,10 @@ impl Deserialize<'de> + Default> Unversio // Since MyStructV0 is only composed of built-in types, it does not need recursive versioning and // can be used as its own "version type". #[derive(Serialize)] -#[allow(dead_code)] enum MyStructVersionsDispatch<'vers, T: 'vers + Versionize> { + #[allow(dead_code)] V0(MyStructV0), + #[allow(dead_code)] V1(MyStructVersion<'vers, T>), } diff --git a/utils/tfhe-versionable/examples/transparent.rs b/utils/tfhe-versionable/examples/transparent.rs index 007b355399..f2869dc9c5 100644 --- a/utils/tfhe-versionable/examples/transparent.rs +++ b/utils/tfhe-versionable/examples/transparent.rs @@ -48,9 +48,9 @@ enum MyStructVersions { mod v0 { use tfhe_versionable::{Versionize, VersionsDispatch}; - // This struct cannot change as it is not itself versioned. If you ever make a change that - // should impact the serialized layout of the data, you need to update all the types that use - // it. + // If you ever change the layout of this struct to make it "not transparent", you should create + // a MyStructWrapperVersions enum where the first versions are the same than the ones of + // MyStructVersions. See `transparent_then_not.rs` for a full example. #[derive(Versionize)] #[versionize(transparent)] pub(super) struct MyStructWrapper(pub(super) MyStruct); diff --git a/utils/tfhe-versionable/examples/transparent_then_not.rs b/utils/tfhe-versionable/examples/transparent_then_not.rs new file mode 100644 index 0000000000..758409086e --- /dev/null +++ b/utils/tfhe-versionable/examples/transparent_then_not.rs @@ -0,0 +1,173 @@ +//! This example is similar to the "transparent" one, except that the wrapper type is transparent at +//! a point in time, then converted into its own type that is not transparent. +//! +//! Here we have a type, `MyStructWrapper`, that was a transparent wrapper for `MyStruct` in the v0 +//! and v1 of the application. `MyStruct` has been upgraded between v0 and v1. In v2, +//! `MyStructWrapper` was transformed into an enum. Since it was transparent before, it has no +//! history (dispatch enum) before v2. +//! +//! To make this work, we consider that the inner and the wrapper type share the same history up to +//! the version where the transparent attribute has been removed. + +use std::convert::Infallible; + +use tfhe_versionable::{Unversionize, Upgrade, Version, Versionize, VersionsDispatch}; + +// This type was transparent before, but it has now been transformed to a full type, for example by +// adding a new kind of metadata. +#[derive(Versionize)] +#[versionize(MyStructWrapperVersions)] +struct MyStructWrapper { + inner: MyStruct, + count: u64, +} + +// We need to create a dispatch enum that follows the version numbers of the inner type, until the +// point where the wrapper is not transparent anymore. +#[derive(VersionsDispatch)] +#[allow(unused)] +enum MyStructWrapperVersions { + V0(MyStructWrapperV0), + V1(MyStructWrapperV1), + V2(MyStructWrapper), +} + +// We copy the upgrade path of the internal struct for the wrapper for the first 2 versions. To do +// that, we recreate the "transparent" `MyStructWrapper` from v0 and v1 and upgrade them by calling +// the upgrade method of the inner type. +#[derive(Version)] +#[repr(transparent)] +struct MyStructWrapperV0(MyStructV0); + +impl Upgrade> for MyStructWrapperV0 { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { + Ok(MyStructWrapperV1(self.0.upgrade()?)) + } +} + +// Then we define the upgrade from the last transparent version to the first "full" version +#[derive(Version)] +#[repr(transparent)] +struct MyStructWrapperV1(MyStruct); + +impl Upgrade> for MyStructWrapperV1 { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { + Ok(MyStructWrapper { + inner: self.0, + count: 0, + }) + } +} + +#[derive(Versionize)] +#[versionize(MyStructVersions)] +struct MyStruct { + attr: T, + builtin: u32, +} + +#[derive(Version)] +struct MyStructV0 { + builtin: u32, +} + +impl Upgrade> for MyStructV0 { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { + Ok(MyStruct { + attr: T::default(), + builtin: self.builtin, + }) + } +} + +#[derive(VersionsDispatch)] +#[allow(unused)] +enum MyStructVersions { + V0(MyStructV0), + V1(MyStruct), +} + +// v0 of the app defined the type as a transparent wrapper +mod v0 { + use tfhe_versionable::{Versionize, VersionsDispatch}; + + #[derive(Versionize)] + #[versionize(transparent)] + pub(super) struct MyStructWrapper(pub(super) MyStruct); + + #[derive(Versionize)] + #[versionize(MyStructVersions)] + pub(super) struct MyStruct { + pub(super) builtin: u32, + } + + #[derive(VersionsDispatch)] + #[allow(unused)] + pub(super) enum MyStructVersions { + V0(MyStruct), + } +} + +// In v1, MyStructWrapper is still transparent but MyStruct got an upgrade compared to v0. +mod v1 { + use std::convert::Infallible; + + use tfhe_versionable::{Upgrade, Version, Versionize, VersionsDispatch}; + + #[derive(Versionize)] + #[repr(transparent)] + struct MyStructWrapper(MyStruct); + + #[derive(Versionize)] + #[versionize(MyStructVersions)] + struct MyStruct { + attr: T, + builtin: u32, + } + + #[derive(Version)] + struct MyStructV0 { + builtin: u32, + } + + impl Upgrade> for MyStructV0 { + type Error = Infallible; + + fn upgrade(self) -> Result, Self::Error> { + Ok(MyStruct { + attr: T::default(), + builtin: self.builtin, + }) + } + } + + #[derive(VersionsDispatch)] + #[allow(unused)] + enum MyStructVersions { + V0(MyStructV0), + V1(MyStruct), + } +} + +fn main() { + let value = 1234; + let ms = v0::MyStructWrapper(v0::MyStruct { builtin: value }); + + let serialized = bincode::serialize(&ms.versionize()).unwrap(); + + let unserialized = + MyStructWrapper::::unversionize(bincode::deserialize(&serialized).unwrap()).unwrap(); + + assert_eq!(unserialized.inner.builtin, value) +} + +#[test] +fn test() { + main() +} diff --git a/utils/tfhe-versionable/tests/bounds_private_in_public.rs b/utils/tfhe-versionable/tests/bounds_private_in_public.rs index 489d6ad0fc..2168272bd0 100644 --- a/utils/tfhe-versionable/tests/bounds_private_in_public.rs +++ b/utils/tfhe-versionable/tests/bounds_private_in_public.rs @@ -30,8 +30,8 @@ mod mymod { struct Private(T); #[derive(VersionsDispatch)] - #[allow(dead_code)] enum PrivateVersions { + #[allow(dead_code)] V0(Private), } }