Skip to content

Commit

Permalink
chore(zk)!: store inside the pke params the supported zk scheme
Browse files Browse the repository at this point in the history
BREAKING_CHANGE:
- Zk for compact PKE now requires dedicated encryption parameters
  • Loading branch information
nsarlin-zama committed Jan 6, 2025
1 parent f1eb675 commit 6cd25a6
Show file tree
Hide file tree
Showing 16 changed files with 232 additions and 281 deletions.
4 changes: 4 additions & 0 deletions tfhe/c_api_tests/test_high_level_zk.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
77 changes: 12 additions & 65 deletions tfhe/docs/guides/zk-pok.md
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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.
7 changes: 6 additions & 1 deletion tfhe/js_on_wasm_tests/test-hlapi-signed.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
35 changes: 35 additions & 0 deletions tfhe/src/c_api/shortint/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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>
Expand All @@ -196,6 +229,7 @@ impl TryFrom<ShortintCompactPublicKeyEncryptionParameters>
)?,
expansion_kind:
crate::shortint::parameters::CompactCiphertextListExpansionKind::RequiresCasting,
zk_scheme: c_params.zk_scheme.into(),
})
}
}
Expand Down Expand Up @@ -233,6 +267,7 @@ impl ShortintCompactPublicKeyEncryptionParameters {
casting_parameters: ShortintCompactCiphertextListCastingParameters::convert(
casting_parameters,
),
zk_scheme: SupportedCompactPkeZkScheme::convert(compact_pke_params.zk_scheme),
}
}
}
Expand Down
116 changes: 0 additions & 116 deletions tfhe/src/high_level_api/compact_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 6 additions & 1 deletion tfhe/src/high_level_api/tests/tags_on_entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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'];
Expand Down
4 changes: 1 addition & 3 deletions tfhe/src/integer/ciphertext/compact_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
17 changes: 0 additions & 17 deletions tfhe/src/js_on_wasm_api/js_high_level_api/zk.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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(|| {
Expand Down
3 changes: 2 additions & 1 deletion tfhe/src/js_on_wasm_api/shortint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
Loading

0 comments on commit 6cd25a6

Please sign in to comment.