diff --git a/tfhe/c_api_tests/test_high_level_zk.c b/tfhe/c_api_tests/test_high_level_zk.c index 53dfbabd23..4487629486 100644 --- a/tfhe/c_api_tests/test_high_level_zk.c +++ b/tfhe/c_api_tests/test_high_level_zk.c @@ -11,12 +11,16 @@ int main(void) { ShortintPBSParameters params = SHORTINT_PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; assert(params.encryption_key_choice == ShortintEncryptionKeyChoiceBig); + ShortintCompactPublicKeyEncryptionParameters pke_params = SHORTINT_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + int status; ConfigBuilder *builder; status = config_builder_default(&builder); assert(status == 0); status = config_builder_use_custom_parameters(&builder, params); + assert(status == 0); + status = use_dedicated_compact_public_key_parameters(&builder, pke_params); assert(status == 0); Config *config; diff --git a/tfhe/docs/guides/zk-pok.md b/tfhe/docs/guides/zk-pok.md index 2efcf4c394..f0cd2b8a44 100644 --- a/tfhe/docs/guides/zk-pok.md +++ b/tfhe/docs/guides/zk-pok.md @@ -10,71 +10,7 @@ You can enable this feature using the flag: `--features=zk-pok` when building ** Using this feature is straightforward: during encryption, the client generates the proof, and the server validates it before conducting any homomorphic computations. The following example demonstrates how a client can encrypt and prove a ciphertext, and how a server can verify the ciphertext and compute it: -```rust -use rand::prelude::*; -use tfhe::prelude::*; -use tfhe::set_server_key; -use tfhe::zk::{CompactPkeCrs, ZkComputeLoad}; - -pub fn main() -> Result<(), Box> { - let mut rng = thread_rng(); - - let params = tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let config = tfhe::ConfigBuilder::with_custom_parameters(params); - - let client_key = tfhe::ClientKey::generate(config.clone()); - // This is done in an offline phase and the CRS is shared to all clients and the server - let crs = CompactPkeCrs::from_config(config.into(), 64).unwrap(); - let server_key = tfhe::ServerKey::new(&client_key); - let public_key = tfhe::CompactPublicKey::try_new(&client_key).unwrap(); - // This can be left empty, but if provided allows to tie the proof to arbitrary data - let metadata = [b'T', b'F', b'H', b'E', b'-', b'r', b's']; - - let clear_a = rng.gen::(); - let clear_b = rng.gen::(); - - let proven_compact_list = tfhe::ProvenCompactCiphertextList::builder(&public_key) - .push(clear_a) - .push(clear_b) - .build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof)?; - - // Server side - let result = { - set_server_key(server_key); - - // Verify the ciphertexts - let expander = proven_compact_list.verify_and_expand(&crs, &public_key, &metadata)?; - let a: tfhe::FheUint64 = expander.get(0)?.unwrap(); - let b: tfhe::FheUint64 = expander.get(1)?.unwrap(); - - a + b - }; - - // Back on the client side - let a_plus_b: u64 = result.decrypt(&client_key); - assert_eq!(a_plus_b, clear_a.wrapping_add(clear_b)); - - Ok(()) -} -``` - -Performance can be improved by setting `lto="fat"` in `Cargo.toml` -```toml -[profile.release] -lto = "fat" -``` -and by building the code for the native CPU architecture and in release mode, e.g. by calling `RUSTFLAGS="-C target-cpu=native" cargo run --release`. - -{% hint style="info" %} -You can choose a more costly proof with `ZkComputeLoad::Proof`, which has a faster verification time. Alternatively, you can select `ZkComputeLoad::Verify` for a faster proof and slower verification. -{% endhint %} - -## Using dedicated Compact Public Key parameters - -### A first example -You can use dedicated parameters for the compact public key encryption to reduce the size of encrypted data and speed up the zero-knowledge proof computation. - -This works essentially in the same way as before. Additionally, you need to indicate the dedicated parameters to use: +Note that you need to use dedicated parameters for the compact public key encryption. This helps to reduce the size of encrypted data and speed up the zero-knowledge proof computation. ```rust use rand::prelude::*; @@ -132,5 +68,16 @@ pub fn main() -> Result<(), Box> { } ``` +Performance can be improved by setting `lto="fat"` in `Cargo.toml` +```toml +[profile.release] +lto = "fat" +``` +and by building the code for the native CPU architecture and in release mode, e.g. by calling `RUSTFLAGS="-C target-cpu=native" cargo run --release`. + +{% hint style="info" %} +You can choose a more costly proof with `ZkComputeLoad::Proof`, which has a faster verification time. Alternatively, you can select `ZkComputeLoad::Verify` for a faster proof and slower verification. +{% endhint %} + ## Benchmark Please refer to the [Zero-knowledge proof benchmarks](../getting_started/benchmarks/zk_proof_benchmarks.md) for detailed performance benchmark results. diff --git a/tfhe/js_on_wasm_tests/test-hlapi-signed.js b/tfhe/js_on_wasm_tests/test-hlapi-signed.js index 224e73af08..8efd054349 100644 --- a/tfhe/js_on_wasm_tests/test-hlapi-signed.js +++ b/tfhe/js_on_wasm_tests/test-hlapi-signed.js @@ -500,14 +500,19 @@ test('hlapi_compact_ciphertext_list', (t) => { test('hlapi_compact_ciphertext_list_with_proof', (t) => { const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64); + let publicKeyParams = new ShortintCompactPublicKeyEncryptionParameters( + ShortintCompactPublicKeyEncryptionParametersName.SHORTINT_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, + ); + let config = TfheConfigBuilder.default() .use_custom_parameters(block_params) + .use_dedicated_compact_public_key_parameters(publicKeyParams) .build(); let clientKey = TfheClientKey.generate(config); let publicKey = TfheCompactPublicKey.new(clientKey); - let crs = CompactPkeCrs.from_parameters(block_params, 2 + 32 + 1 + 256); + let crs = CompactPkeCrs.from_config(config, 2 + 32 + 1 + 256); const compress = false; // We don't compress as it's too slow on wasm let serialized_pke_crs = crs.serialize(compress); diff --git a/tfhe/src/c_api/shortint/parameters.rs b/tfhe/src/c_api/shortint/parameters.rs index 7be86247aa..82fc43cee5 100644 --- a/tfhe/src/c_api/shortint/parameters.rs +++ b/tfhe/src/c_api/shortint/parameters.rs @@ -163,6 +163,38 @@ impl ShortintCompactCiphertextListCastingParameters { } } +#[repr(C)] +#[derive(Copy, Clone)] +pub enum SupportedCompactPkeZkScheme { + ZkNotSupported, + V1, + V2, +} + +impl From + for crate::shortint::parameters::SupportedCompactPkeZkScheme +{ + fn from(value: SupportedCompactPkeZkScheme) -> Self { + match value { + SupportedCompactPkeZkScheme::ZkNotSupported => Self::ZkNotSupported, + SupportedCompactPkeZkScheme::V1 => Self::V1, + SupportedCompactPkeZkScheme::V2 => Self::V2, + } + } +} + +impl SupportedCompactPkeZkScheme { + const fn convert(value: crate::shortint::parameters::SupportedCompactPkeZkScheme) -> Self { + match value { + crate::shortint::parameters::SupportedCompactPkeZkScheme::ZkNotSupported => { + Self::ZkNotSupported + } + crate::shortint::parameters::SupportedCompactPkeZkScheme::V1 => Self::V1, + crate::shortint::parameters::SupportedCompactPkeZkScheme::V2 => Self::V2, + } + } +} + #[repr(C)] #[derive(Copy, Clone)] pub struct ShortintCompactPublicKeyEncryptionParameters { @@ -176,6 +208,7 @@ pub struct ShortintCompactPublicKeyEncryptionParameters { // these parameters will always require casting, as they always require casting we add a field // for the casting parameters here. pub casting_parameters: ShortintCompactCiphertextListCastingParameters, + pub zk_scheme: SupportedCompactPkeZkScheme, } impl TryFrom @@ -196,6 +229,7 @@ impl TryFrom )?, expansion_kind: crate::shortint::parameters::CompactCiphertextListExpansionKind::RequiresCasting, + zk_scheme: c_params.zk_scheme.into(), }) } } @@ -233,6 +267,7 @@ impl ShortintCompactPublicKeyEncryptionParameters { casting_parameters: ShortintCompactCiphertextListCastingParameters::convert( casting_parameters, ), + zk_scheme: SupportedCompactPkeZkScheme::convert(compact_pke_params.zk_scheme), } } } diff --git a/tfhe/src/high_level_api/compact_list.rs b/tfhe/src/high_level_api/compact_list.rs index 13c040c45a..836865de8d 100644 --- a/tfhe/src/high_level_api/compact_list.rs +++ b/tfhe/src/high_level_api/compact_list.rs @@ -315,39 +315,6 @@ mod zk { fn conformance_zk_compact_ciphertext_list() { let mut rng = thread_rng(); - let params: crate::shortint::ClassicPBSParameters = - crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; - let config = crate::ConfigBuilder::with_custom_parameters(params); - - let client_key = crate::ClientKey::generate(config.clone()); - // This is done in an offline phase and the CRS is shared to all clients and the server - let crs = CompactPkeCrs::from_config(config.into(), 64).unwrap(); - let public_key = crate::CompactPublicKey::try_new(&client_key).unwrap(); - // This can be left empty, but if provided allows to tie the proof to arbitrary data - let metadata = [b'T', b'F', b'H', b'E', b'-', b'r', b's']; - - let clear_a = rng.gen::(); - let clear_b = rng.gen::(); - - let proven_compact_list = crate::ProvenCompactCiphertextList::builder(&public_key) - .push(clear_a) - .push(clear_b) - .build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof) - .unwrap(); - - let params = - IntegerProvenCompactCiphertextListConformanceParams::from_crs_and_parameters( - params.try_into().unwrap(), - &crs, - ); - - assert!(proven_compact_list.is_conformant(¶ms)); - } - - #[test] - fn conformance_zk_compact_ciphertext_list_casting() { - let mut rng = thread_rng(); - let params = crate::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; let cpk_params = crate::shortint::parameters::compact_public_key_only::p_fail_2_minus_64::ks_pbs::V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; @@ -637,89 +604,6 @@ mod tests { #[cfg(feature = "zk-pok")] #[test] fn test_proven_compact_list() { - use crate::shortint::parameters::classic::tuniform::p_fail_2_minus_64::ks_pbs::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, - ) - .build(); - - let ck = crate::ClientKey::generate(config); - let pk = crate::CompactPublicKey::new(&ck); - let sks = crate::ServerKey::new(&ck); - - set_server_key(sks); - - // Intentionally low to that we test when multiple lists and proofs are needed - let crs = CompactPkeCrs::from_config(config, 32).unwrap(); - - let metadata = [b'h', b'l', b'a', b'p', b'i']; - - let compact_list = ProvenCompactCiphertextList::builder(&pk) - .push(17u32) - .push(-1i64) - .push(false) - .push_with_num_bits(3u32, 2) - .unwrap() - .build_with_proof_packed(&crs, &metadata, ZkComputeLoad::Proof) - .unwrap(); - - let serialized = bincode::serialize(&compact_list).unwrap(); - let compact_list: ProvenCompactCiphertextList = bincode::deserialize(&serialized).unwrap(); - let expander = compact_list - .verify_and_expand(&crs, &pk, &metadata) - .unwrap(); - - { - let a: FheUint32 = expander.get(0).unwrap().unwrap(); - let b: FheInt64 = expander.get(1).unwrap().unwrap(); - let c: FheBool = expander.get(2).unwrap().unwrap(); - let d: FheUint2 = expander.get(3).unwrap().unwrap(); - - let a: u32 = a.decrypt(&ck); - assert_eq!(a, 17); - let b: i64 = b.decrypt(&ck); - assert_eq!(b, -1); - let c = c.decrypt(&ck); - assert!(!c); - let d: u8 = d.decrypt(&ck); - assert_eq!(d, 3); - - assert!(expander.get::(4).unwrap().is_none()); - } - - { - // Incorrect type - assert!(expander.get::(0).is_err()); - - // Correct type but wrong number of bits - assert!(expander.get::(0).is_err()); - } - - let unverified_expander = compact_list.expand_without_verification().unwrap(); - - { - let a: FheUint32 = unverified_expander.get(0).unwrap().unwrap(); - let b: FheInt64 = unverified_expander.get(1).unwrap().unwrap(); - let c: FheBool = unverified_expander.get(2).unwrap().unwrap(); - let d: FheUint2 = unverified_expander.get(3).unwrap().unwrap(); - - let a: u32 = a.decrypt(&ck); - assert_eq!(a, 17); - let b: i64 = b.decrypt(&ck); - assert_eq!(b, -1); - let c = c.decrypt(&ck); - assert!(!c); - let d: u8 = d.decrypt(&ck); - assert_eq!(d, 3); - - assert!(unverified_expander.get::(4).unwrap().is_none()); - } - } - - #[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::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; 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 62dfa384c1..ce81958608 100644 --- a/tfhe/src/high_level_api/tests/tags_on_entities.rs +++ b/tfhe/src/high_level_api/tests/tags_on_entities.rs @@ -36,7 +36,12 @@ fn test_tag_propagation_zk_pok() { use crate::ProvenCompactCiphertextList; let config = - ConfigBuilder::with_custom_parameters(PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64).build(); + ConfigBuilder::with_custom_parameters(PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64) + .use_dedicated_compact_public_key_parameters(( + 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(); let crs = crate::zk::CompactPkeCrs::from_config(config, (2 * 32) + (2 * 64) + 2).unwrap(); let metadata = [b'h', b'l', b'a', b'p', b'i']; diff --git a/tfhe/src/integer/ciphertext/compact_list.rs b/tfhe/src/integer/ciphertext/compact_list.rs index b73085b359..b7910ff567 100644 --- a/tfhe/src/integer/ciphertext/compact_list.rs +++ b/tfhe/src/integer/ciphertext/compact_list.rs @@ -1164,9 +1164,7 @@ mod tests { .checked_pow(num_blocks as u32) .unwrap(); - let crs = - CompactPkeCrs::from_shortint_params_legacy_v1(pke_params, LweCiphertextCount(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); 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 89fe53987a..5c40d5caa3 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,9 +1,7 @@ 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; use crate::zk::Compressible; @@ -79,21 +77,6 @@ impl CompactPkeCrs { }) } - #[wasm_bindgen] - pub fn from_parameters( - parameters: &ShortintParameters, - max_num_message: usize, - ) -> Result { - catch_panic_result(|| { - crate::core_crypto::entities::CompactPkeCrs::from_shortint_params( - parameters.0, - LweCiphertextCount(max_num_message), - ) - .map(CompactPkeCrs) - .map_err(into_js_error) - }) - } - #[wasm_bindgen] pub fn from_config(config: &TfheConfig, max_num_bits: usize) -> Result { catch_panic_result(|| { diff --git a/tfhe/src/js_on_wasm_api/shortint.rs b/tfhe/src/js_on_wasm_api/shortint.rs index 3436c6d6f7..8d9fa6d05b 100644 --- a/tfhe/src/js_on_wasm_api/shortint.rs +++ b/tfhe/src/js_on_wasm_api/shortint.rs @@ -258,7 +258,8 @@ impl ShortintCompactPublicKeyEncryptionParameters { CarryModulus(carry_modulus), ciphertext_modulus, // These parameters always requires casting - crate::shortint::parameters::CompactCiphertextListExpansionKind::RequiresCasting + crate::shortint::parameters::CompactCiphertextListExpansionKind::RequiresCasting, + crate::shortint::parameters::SupportedCompactPkeZkScheme::ZkNotSupported ).map_err(into_js_error)?; let casting_parameters = diff --git a/tfhe/src/shortint/backward_compatibility/parameters/compact_public_key_only.rs b/tfhe/src/shortint/backward_compatibility/parameters/compact_public_key_only.rs index 86c850eb50..4ab5382f15 100644 --- a/tfhe/src/shortint/backward_compatibility/parameters/compact_public_key_only.rs +++ b/tfhe/src/shortint/backward_compatibility/parameters/compact_public_key_only.rs @@ -1,14 +1,47 @@ -use tfhe_versionable::VersionsDispatch; +use std::convert::Infallible; + +use tfhe_versionable::{Upgrade, Version, VersionsDispatch}; use super::parameters::compact_public_key_only::CompactPublicKeyEncryptionParameters; -use super::parameters::CompactCiphertextListExpansionKind; +use super::parameters::{ + CompactCiphertextListExpansionKind, DynamicDistribution, SupportedCompactPkeZkScheme, +}; +use super::prelude::*; #[derive(VersionsDispatch)] pub enum CompactCiphertextListExpansionKindVersions { V0(CompactCiphertextListExpansionKind), } +#[derive(Version)] +pub struct CompactPublicKeyEncryptionParametersV0 { + pub encryption_lwe_dimension: LweDimension, + pub encryption_noise_distribution: DynamicDistribution, + pub message_modulus: MessageModulus, + pub carry_modulus: CarryModulus, + pub ciphertext_modulus: CiphertextModulus, + pub expansion_kind: CompactCiphertextListExpansionKind, +} + +impl Upgrade for CompactPublicKeyEncryptionParametersV0 { + type Error = Infallible; + + fn upgrade(self) -> Result { + Ok(CompactPublicKeyEncryptionParameters { + encryption_lwe_dimension: self.encryption_lwe_dimension, + encryption_noise_distribution: self.encryption_noise_distribution, + message_modulus: self.message_modulus, + carry_modulus: self.carry_modulus, + ciphertext_modulus: self.ciphertext_modulus, + expansion_kind: self.expansion_kind, + // TFHE-rs v0.10 and before used only the v1 zk scheme + zk_scheme: SupportedCompactPkeZkScheme::V1, + }) + } +} + #[derive(VersionsDispatch)] pub enum CompactPublicKeyEncryptionParametersVersions { - V0(CompactPublicKeyEncryptionParameters), + V0(CompactPublicKeyEncryptionParametersV0), + V1(CompactPublicKeyEncryptionParameters), } diff --git a/tfhe/src/shortint/backward_compatibility/parameters/mod.rs b/tfhe/src/shortint/backward_compatibility/parameters/mod.rs index 62bc632c79..9d1cc537bf 100644 --- a/tfhe/src/shortint/backward_compatibility/parameters/mod.rs +++ b/tfhe/src/shortint/backward_compatibility/parameters/mod.rs @@ -4,7 +4,7 @@ pub mod list_compression; use tfhe_versionable::VersionsDispatch; -use crate::shortint::parameters::ShortintParameterSetInner; +use crate::shortint::parameters::*; use crate::shortint::*; #[derive(VersionsDispatch)] @@ -47,3 +47,8 @@ pub enum MultiBitPBSParametersVersions { pub enum WopbsParametersVersions { V0(WopbsParameters), } + +#[derive(VersionsDispatch)] +pub enum SupportedCompactPkeZkSchemeVersions { + V0(SupportedCompactPkeZkScheme), +} diff --git a/tfhe/src/shortint/ciphertext/zk.rs b/tfhe/src/shortint/ciphertext/zk.rs index 92ebca740c..3a335a0259 100644 --- a/tfhe/src/shortint/ciphertext/zk.rs +++ b/tfhe/src/shortint/ciphertext/zk.rs @@ -7,13 +7,14 @@ use crate::shortint::ciphertext::CompactCiphertextList; use crate::shortint::parameters::{ CarryModulus, CiphertextListConformanceParams, CiphertextModulus, CompactCiphertextListExpansionKind, CompactPublicKeyEncryptionParameters, LweDimension, - MessageModulus, ShortintCompactCiphertextListCastingMode, + MessageModulus, ShortintCompactCiphertextListCastingMode, SupportedCompactPkeZkScheme, }; use crate::shortint::{Ciphertext, CompactPublicKey}; use crate::zk::{ CompactPkeCrs, CompactPkeProof, CompactPkeZkScheme, ZkMSBZeroPaddingBitCount, ZkVerificationOutcome, }; + use rayon::prelude::*; use serde::{Deserialize, Serialize}; use tfhe_versionable::Versionize; @@ -21,7 +22,8 @@ use tfhe_versionable::Versionize; impl CompactPkeCrs { /// Construct the CRS that corresponds to the given parameters /// - /// max_num_message is how many message a single proof can prove + /// max_num_message is how many message a single proof can prove. + /// The version of the zk scheme is based on the [`CompactPkeZkScheme`] value in the params. pub fn from_shortint_params( params: P, max_num_message: LweCiphertextCount, @@ -43,51 +45,29 @@ impl CompactPkeCrs { // 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( - size, - max_num_message, - noise_distribution, - params.ciphertext_modulus, - plaintext_modulus, - ZkMSBZeroPaddingBitCount(1), - &mut engine.random_generator, - ) - }) - } - - /// 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, - ) + match params.zk_scheme { + SupportedCompactPkeZkScheme::V1 => Self::new_legacy_v1( + size, + max_num_message, + noise_distribution, + params.ciphertext_modulus, + plaintext_modulus, + ZkMSBZeroPaddingBitCount(1), + &mut engine.random_generator, + ), + SupportedCompactPkeZkScheme::V2 => Self::new( + size, + max_num_message, + noise_distribution, + params.ciphertext_modulus, + plaintext_modulus, + ZkMSBZeroPaddingBitCount(1), + &mut engine.random_generator, + ), + SupportedCompactPkeZkScheme::ZkNotSupported => { + Err("Zk proof of encryption is not supported by the provided parameters".into()) + } + } }) } } @@ -317,28 +297,42 @@ impl ParameterSetConformant for ProvenCompactCiphertextList { #[cfg(test)] mod tests { use crate::core_crypto::prelude::LweCiphertextCount; + 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::{ ShortintCompactCiphertextListCastingMode, PARAM_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64, }; - use crate::shortint::{ClientKey, CompactPublicKey}; + use crate::shortint::{ + ClientKey, CompactPrivateKey, CompactPublicKey, KeySwitchingKey, ServerKey, + }; use crate::zk::{CompactPkeCrs, ZkComputeLoad}; use rand::random; #[test] fn test_zk_ciphertext_encryption_ci_run_filter() { let params = PARAM_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 crs = CompactPkeCrs::from_shortint_params(pke_params, LweCiphertextCount(4)).unwrap(); + let priv_key = CompactPrivateKey::new(pke_params); + let pub_key = CompactPublicKey::new(&priv_key); + let ck = ClientKey::new(params); + let sk = ServerKey::new(&ck); + let ksk = KeySwitchingKey::new((&priv_key, None), (&ck, &sk), ksk_params); + + let id = |x: u64| x; + let dyn_id: &(dyn Fn(u64) -> u64 + Sync) = &id; - let crs = CompactPkeCrs::from_shortint_params(params, LweCiphertextCount(4)).unwrap(); - let cks = ClientKey::new(params); - let pk = CompactPublicKey::new(&cks); + let functions = vec![Some(vec![dyn_id; 1]); 1]; let metadata = [b's', b'h', b'o', b'r', b't', b'i', b'n', b't']; - let msg = random::() % params.message_modulus.0; + let msg = random::() % pke_params.message_modulus.0; // No packing - let encryption_modulus = params.message_modulus.0; + let encryption_modulus = pke_params.message_modulus.0; - let proven_ct = pk + let proven_ct = pub_key .encrypt_and_prove( msg, &crs, @@ -349,35 +343,50 @@ mod tests { .unwrap(); { - let unproven_ct = proven_ct - .expand_without_verification(ShortintCompactCiphertextListCastingMode::NoCasting); - assert!(unproven_ct.is_ok()); + let unproven_ct = proven_ct.expand_without_verification( + ShortintCompactCiphertextListCastingMode::CastIfNecessary { + casting_key: ksk.as_view(), + functions: Some(functions.as_slice()), + }, + ); let unproven_ct = unproven_ct.unwrap(); - let decrypted = cks.decrypt(&unproven_ct[0]); + let decrypted = ck.decrypt(&unproven_ct[0]); assert_eq!(msg, decrypted); } let proven_ct = proven_ct.verify_and_expand( &crs, - &pk, + &pub_key, &metadata, - ShortintCompactCiphertextListCastingMode::NoCasting, + ShortintCompactCiphertextListCastingMode::CastIfNecessary { + casting_key: ksk.as_view(), + functions: Some(functions.as_slice()), + }, ); - assert!(proven_ct.is_ok()); let proven_ct = proven_ct.unwrap(); - let decrypted = cks.decrypt(&proven_ct[0]); + let decrypted = ck.decrypt(&proven_ct[0]); assert_eq!(msg, decrypted); } #[test] fn test_zk_compact_ciphertext_list_encryption_ci_run_filter() { let params = PARAM_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 crs = CompactPkeCrs::from_shortint_params(pke_params, LweCiphertextCount(4)).unwrap(); + let priv_key = CompactPrivateKey::new(pke_params); + let pub_key = CompactPublicKey::new(&priv_key); + let ck = ClientKey::new(params); + let sk = ServerKey::new(&ck); + let ksk = KeySwitchingKey::new((&priv_key, None), (&ck, &sk), ksk_params); - let crs = CompactPkeCrs::from_shortint_params(params, LweCiphertextCount(512)).unwrap(); - let cks = ClientKey::new(params); - let pk = CompactPublicKey::new(&cks); + let id = |x: u64| x; + let dyn_id: &(dyn Fn(u64) -> u64 + Sync) = &id; + + let functions = vec![Some(vec![dyn_id; 1]); 512]; let metadata = [b's', b'h', b'o', b'r', b't', b'i', b'n', b't']; @@ -385,7 +394,7 @@ mod tests { .map(|_| random::() % params.message_modulus.0) .collect::>(); - let proven_ct = pk + let proven_ct = pub_key .encrypt_and_prove_slice( &msgs, &crs, @@ -394,19 +403,22 @@ mod tests { params.message_modulus.0, ) .unwrap(); - assert!(proven_ct.verify(&crs, &pk, &metadata).is_valid()); + assert!(proven_ct.verify(&crs, &pub_key, &metadata).is_valid()); let expanded = proven_ct .verify_and_expand( &crs, - &pk, + &pub_key, &metadata, - ShortintCompactCiphertextListCastingMode::NoCasting, + ShortintCompactCiphertextListCastingMode::CastIfNecessary { + casting_key: ksk.as_view(), + functions: Some(functions.as_slice()), + }, ) .unwrap(); let decrypted = expanded .iter() - .map(|ciphertext| cks.decrypt(ciphertext)) + .map(|ciphertext| ck.decrypt(ciphertext)) .collect::>(); assert_eq!(msgs, decrypted); } diff --git a/tfhe/src/shortint/parameters/compact_public_key_only/mod.rs b/tfhe/src/shortint/parameters/compact_public_key_only/mod.rs index 115bb1b8e3..282a11a1e0 100644 --- a/tfhe/src/shortint/parameters/compact_public_key_only/mod.rs +++ b/tfhe/src/shortint/parameters/compact_public_key_only/mod.rs @@ -7,9 +7,10 @@ use crate::shortint::backward_compatibility::parameters::compact_public_key_only }; use crate::shortint::parameters::{ CarryModulus, ClassicPBSParameters, MessageModulus, MultiBitPBSParameters, PBSParameters, - ShortintParameterSet, + ShortintParameterSet, SupportedCompactPkeZkScheme, }; use crate::shortint::KeySwitchingKeyView; + use crate::Error; use serde::{Deserialize, Serialize}; use tfhe_versionable::Versionize; @@ -50,6 +51,8 @@ pub struct CompactPublicKeyEncryptionParameters { pub carry_modulus: CarryModulus, pub ciphertext_modulus: CiphertextModulus, pub expansion_kind: CompactCiphertextListExpansionKind, + // Version of the PKE zk scheme compatible with these parameters + pub zk_scheme: SupportedCompactPkeZkScheme, } impl CompactPublicKeyEncryptionParameters { @@ -60,6 +63,7 @@ impl CompactPublicKeyEncryptionParameters { carry_modulus: CarryModulus, ciphertext_modulus: CiphertextModulus, output_ciphertext_kind: CompactCiphertextListExpansionKind, + zk_scheme: SupportedCompactPkeZkScheme, ) -> Result { let parameters = Self { encryption_lwe_dimension, @@ -68,6 +72,7 @@ impl CompactPublicKeyEncryptionParameters { carry_modulus, ciphertext_modulus, expansion_kind: output_ciphertext_kind, + zk_scheme, }; if !parameters.is_valid() { @@ -126,6 +131,8 @@ impl TryFrom for CompactPublicKeyEncryptionParameters { carry_modulus, ciphertext_modulus, output_ciphertext_kind, + // Zk needs specific pke parameters + SupportedCompactPkeZkScheme::ZkNotSupported, ) } } 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 d5fe19103d..6ba5847aa7 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 @@ -3,7 +3,7 @@ use crate::core_crypto::commons::parameters::{ }; use crate::shortint::parameters::{ CarryModulus, CompactCiphertextListExpansionKind, CompactPublicKeyEncryptionParameters, - MessageModulus, + MessageModulus, SupportedCompactPkeZkScheme, }; /// This parameter set should be used when doing zk proof of public key encryption @@ -19,6 +19,7 @@ pub const V0_11_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV2: carry_modulus: CarryModulus(4), ciphertext_modulus: CiphertextModulus::new_native(), expansion_kind: CompactCiphertextListExpansionKind::RequiresCasting, + zk_scheme: SupportedCompactPkeZkScheme::V2, } .validate(); @@ -33,6 +34,7 @@ pub const V0_11_PARAM_PKE_TO_SMALL_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV1: carry_modulus: CarryModulus(4), ciphertext_modulus: CiphertextModulus::new_native(), expansion_kind: CompactCiphertextListExpansionKind::RequiresCasting, + zk_scheme: SupportedCompactPkeZkScheme::V1, } .validate(); @@ -46,5 +48,6 @@ pub const V0_11_PARAM_PKE_TO_BIG_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV1: carry_modulus: CarryModulus(4), ciphertext_modulus: CiphertextModulus::new_native(), expansion_kind: CompactCiphertextListExpansionKind::RequiresCasting, + zk_scheme: SupportedCompactPkeZkScheme::V1, } .validate(); diff --git a/tfhe/src/shortint/parameters/mod.rs b/tfhe/src/shortint/parameters/mod.rs index 33b34fd7a1..8824a5f9ac 100644 --- a/tfhe/src/shortint/parameters/mod.rs +++ b/tfhe/src/shortint/parameters/mod.rs @@ -17,6 +17,8 @@ use crate::core_crypto::prelude::{ LweCiphertextListParameters, LweCiphertextParameters, MsDecompressionType, }; use crate::shortint::backward_compatibility::parameters::*; +#[cfg(feature = "zk-pok")] +use crate::zk::CompactPkeZkScheme; use serde::{Deserialize, Serialize}; use tfhe_versionable::Versionize; @@ -793,3 +795,29 @@ pub const COMP_PARAM_MESSAGE_2_CARRY_2: CompressionParameters = COMP_PARAM_MESSA // GPU pub const PARAM_GPU_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_3_KS_PBS: MultiBitPBSParameters = PARAM_GPU_MULTI_BIT_GROUP_3_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64; + +/// The Zk scheme for compact private key encryption supported by these parameters. +/// +/// The Zk Scheme is available in 2 versions. In case of doubt, you should prefer the V2 which is +/// more efficient. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Versionize)] +#[versionize(SupportedCompactPkeZkSchemeVersions)] +pub enum SupportedCompactPkeZkScheme { + /// The given parameters do not support zk proof of encryption + ZkNotSupported, + V1, + V2, +} + +#[cfg(feature = "zk-pok")] +impl TryFrom for CompactPkeZkScheme { + type Error = (); + + fn try_from(value: SupportedCompactPkeZkScheme) -> Result { + match value { + SupportedCompactPkeZkScheme::ZkNotSupported => Err(()), + SupportedCompactPkeZkScheme::V1 => Ok(Self::V1), + SupportedCompactPkeZkScheme::V2 => Ok(Self::V2), + } + } +} diff --git a/tfhe/src/shortint/parameters/v0_10/compact_public_key_only/p_fail_2_minus_64/ks_pbs.rs b/tfhe/src/shortint/parameters/v0_10/compact_public_key_only/p_fail_2_minus_64/ks_pbs.rs index d83f0e3aef..86fd66971e 100644 --- a/tfhe/src/shortint/parameters/v0_10/compact_public_key_only/p_fail_2_minus_64/ks_pbs.rs +++ b/tfhe/src/shortint/parameters/v0_10/compact_public_key_only/p_fail_2_minus_64/ks_pbs.rs @@ -3,7 +3,7 @@ use crate::core_crypto::commons::parameters::{ }; use crate::shortint::parameters::{ CarryModulus, CompactCiphertextListExpansionKind, CompactPublicKeyEncryptionParameters, - MessageModulus, + MessageModulus, SupportedCompactPkeZkScheme, }; /// This legacy parameter set were used with the v1 pke zk scheme on TFHE-rs v0.10 and lower @@ -15,5 +15,6 @@ pub const V0_10_PARAM_PKE_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M64_ZKV1: carry_modulus: CarryModulus(4), ciphertext_modulus: CiphertextModulus::new_native(), expansion_kind: CompactCiphertextListExpansionKind::RequiresCasting, + zk_scheme: SupportedCompactPkeZkScheme::V1, } .validate();