chore(zk)!: store inside the pke params the supported zk scheme

BREAKING_CHANGE:
- Zk for compact PKE now requires dedicated encryption parameters
This commit is contained in:
Nicolas Sarlin
2025-01-03 17:35:52 +01:00
committed by Nicolas Sarlin
parent c19683a320
commit f06b04fd83
16 changed files with 232 additions and 281 deletions

View File

@@ -11,6 +11,8 @@ 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;
@@ -18,6 +20,8 @@ int main(void) {
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;
status = config_builder_build(builder, &config);

View File

@@ -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<dyn std::error::Error>> {
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::<u64>();
let clear_b = rng.gen::<u64>();
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<dyn std::error::Error>> {
}
```
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.

View File

@@ -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);

View File

@@ -163,6 +163,38 @@ impl ShortintCompactCiphertextListCastingParameters {
}
}
#[repr(C)]
#[derive(Copy, Clone)]
pub enum SupportedCompactPkeZkScheme {
ZkNotSupported,
V1,
V2,
}
impl From<SupportedCompactPkeZkScheme>
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<ShortintCompactPublicKeyEncryptionParameters>
@@ -196,6 +229,7 @@ impl TryFrom<ShortintCompactPublicKeyEncryptionParameters>
)?,
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),
}
}
}

View File

@@ -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::<u64>();
let clear_b = rng.gen::<bool>();
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(&params));
}
#[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::<FheBool>(4).unwrap().is_none());
}
{
// Incorrect type
assert!(expander.get::<FheInt64>(0).is_err());
// Correct type but wrong number of bits
assert!(expander.get::<FheUint16>(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::<FheBool>(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;

View File

@@ -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'];

View File

@@ -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);

View File

@@ -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<CompactPkeCrs, JsError> {
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<CompactPkeCrs, JsError> {
catch_panic_result(|| {

View File

@@ -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 =

View File

@@ -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<u64>,
pub message_modulus: MessageModulus,
pub carry_modulus: CarryModulus,
pub ciphertext_modulus: CiphertextModulus,
pub expansion_kind: CompactCiphertextListExpansionKind,
}
impl Upgrade<CompactPublicKeyEncryptionParameters> for CompactPublicKeyEncryptionParametersV0 {
type Error = Infallible;
fn upgrade(self) -> Result<CompactPublicKeyEncryptionParameters, Self::Error> {
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),
}

View File

@@ -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),
}

View File

@@ -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<P, E>(
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<P, E>(
params: P,
max_num_message: LweCiphertextCount,
) -> crate::Result<Self>
where
P: TryInto<CompactPublicKeyEncryptionParameters, Error = E>,
crate::Error: From<E>,
{
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(params, LweCiphertextCount(4)).unwrap();
let cks = ClientKey::new(params);
let pk = CompactPublicKey::new(&cks);
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 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::<u64>() % params.message_modulus.0;
let msg = random::<u64>() % 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(params, LweCiphertextCount(512)).unwrap();
let cks = ClientKey::new(params);
let pk = CompactPublicKey::new(&cks);
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 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::<u64>() % params.message_modulus.0)
.collect::<Vec<_>>();
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::<Vec<_>>();
assert_eq!(msgs, decrypted);
}

View File

@@ -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<Self, Error> {
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<ShortintParameterSet> for CompactPublicKeyEncryptionParameters {
carry_modulus,
ciphertext_modulus,
output_ciphertext_kind,
// Zk needs specific pke parameters
SupportedCompactPkeZkScheme::ZkNotSupported,
)
}
}

View File

@@ -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();

View File

@@ -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<SupportedCompactPkeZkScheme> for CompactPkeZkScheme {
type Error = ();
fn try_from(value: SupportedCompactPkeZkScheme) -> Result<Self, Self::Error> {
match value {
SupportedCompactPkeZkScheme::ZkNotSupported => Err(()),
SupportedCompactPkeZkScheme::V1 => Ok(Self::V1),
SupportedCompactPkeZkScheme::V2 => Ok(Self::V2),
}
}
}

View File

@@ -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();