Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ns/bench zk cs ghl #1825

Merged
merged 2 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 46 additions & 46 deletions tfhe-zk-pok/benches/pke_v2.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use criterion::{criterion_group, criterion_main, Criterion};
use tfhe_zk_pok::proofs::pke_v2::{prove, verify};
use tfhe_zk_pok::proofs::pke_v2::{prove, verify, Bound};
use tfhe_zk_pok::proofs::ComputeLoad;
use utils::{init_params_v2, write_to_json, PKEV1_TEST_PARAMS, PKEV2_TEST_PARAMS};

Expand All @@ -16,35 +16,33 @@ fn bench_pke_v2_prove(c: &mut Criterion) {

let rng = &mut rand::thread_rng();

for (params, param_name) in [
(PKEV1_TEST_PARAMS, "PKEV1_TEST_PARAMS"),
(PKEV2_TEST_PARAMS, "PKEV2_TEST_PARAMS"),
] {
let (public_param, public_commit, private_commit, metadata) = init_params_v2(params);
for ((params, param_name), load, bound) in itertools::iproduct!(
[
(PKEV1_TEST_PARAMS, "PKEV1_TEST_PARAMS"),
(PKEV2_TEST_PARAMS, "PKEV2_TEST_PARAMS"),
],
[ComputeLoad::Proof, ComputeLoad::Verify],
[Bound::CS, Bound::GHL]
) {
let (public_param, public_commit, private_commit, metadata) = init_params_v2(params, bound);
let effective_t = params.t >> 1;
let bits = (params.k as u32) * effective_t.ilog2();

for load in [ComputeLoad::Proof, ComputeLoad::Verify] {
let zk_load = match load {
ComputeLoad::Proof => "compute_load_proof",
ComputeLoad::Verify => "compute_load_verify",
};
let bench_id = format!("{bench_name}::{param_name}_{bits}_bits_packed_{zk_load}");
let bench_id = format!("{bench_name}::{param_name}_{bits}_bits_packed_{load}_{bound:?}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debug formatting of bounds is ok for our format ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will print "GHL" or "CS", which is ok as David confirmed


bench_group.bench_function(&bench_id, |b| {
b.iter(|| {
prove(
(&public_param, &public_commit),
&private_commit,
&metadata,
load,
rng,
)
})
});
bench_group.bench_function(&bench_id, |b| {
b.iter(|| {
prove(
(&public_param, &public_commit),
&private_commit,
&metadata,
load,
rng,
)
})
});

write_to_json(&bench_id, params, param_name, bench_shortname);
}
write_to_json(&bench_id, params, param_name, bench_shortname);
}
}

Expand All @@ -58,33 +56,35 @@ fn bench_pke_v2_verify(c: &mut Criterion) {

let rng = &mut rand::thread_rng();

for (params, param_name) in [
(PKEV1_TEST_PARAMS, "PKEV1_TEST_PARAMS"),
(PKEV2_TEST_PARAMS, "PKEV2_TEST_PARAMS"),
] {
let (public_param, public_commit, private_commit, metadata) = init_params_v2(params);
for ((params, param_name), load, bound) in itertools::iproduct!(
[
(PKEV1_TEST_PARAMS, "PKEV1_TEST_PARAMS"),
(PKEV2_TEST_PARAMS, "PKEV2_TEST_PARAMS"),
],
[ComputeLoad::Proof, ComputeLoad::Verify],
[Bound::CS, Bound::GHL]
) {
let (public_param, public_commit, private_commit, metadata) = init_params_v2(params, bound);
let effective_t = params.t >> 1;
let bits = (params.k as u32) * effective_t.ilog2();

for load in [ComputeLoad::Proof, ComputeLoad::Verify] {
let bench_id = format!("{bench_name}::{param_name}_{bits}_bits_packed_{load}");
let bench_id = format!("{bench_name}::{param_name}_{bits}_bits_packed_{load}_{bound:?}");

let proof = prove(
(&public_param, &public_commit),
&private_commit,
&metadata,
load,
rng,
);
let proof = prove(
(&public_param, &public_commit),
&private_commit,
&metadata,
load,
rng,
);

bench_group.bench_function(&bench_id, |b| {
b.iter(|| {
verify(&proof, (&public_param, &public_commit), &metadata).unwrap();
})
});
bench_group.bench_function(&bench_id, |b| {
b.iter(|| {
verify(&proof, (&public_param, &public_commit), &metadata).unwrap();
})
});

write_to_json(&bench_id, params, param_name, bench_shortname);
}
write_to_json(&bench_id, params, param_name, bench_shortname);
}
}

Expand Down
11 changes: 8 additions & 3 deletions tfhe-zk-pok/benches/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ use serde::Serialize;
use tfhe_zk_pok::proofs::pke::{commit, crs_gen, PrivateCommit, PublicCommit, PublicParams};

use tfhe_zk_pok::proofs::pke_v2::{
commit as commitv2, crs_gen as crs_genv2, PrivateCommit as PrivateCommitv2,
PublicCommit as PublicCommitv2, PublicParams as PublicParamsv2,
commit as commitv2, crs_gen_cs as crs_genv2_cs, crs_gen_ghl as crs_genv2_ghl, Bound,
PrivateCommit as PrivateCommitv2, PublicCommit as PublicCommitv2,
PublicParams as PublicParamsv2,
};

// One of our usecases uses 320 bits of additional metadata
Expand Down Expand Up @@ -414,6 +415,7 @@ pub fn init_params_v1(
#[allow(unused)]
pub fn init_params_v2(
test_params: PkeTestParameters,
bound: Bound,
) -> (
PublicParamsv2<Curve>,
PublicCommitv2<Curve>,
Expand All @@ -435,7 +437,10 @@ pub fn init_params_v2(

let ct = testcase.encrypt(test_params);

let public_param = crs_genv2::<Curve>(d, k, B, q, t, msbs_zero_padding_bit_count, rng);
let public_param = match bound {
Bound::GHL => crs_genv2_ghl::<Curve>(d, k, B, q, t, msbs_zero_padding_bit_count, rng),
Bound::CS => crs_genv2_cs::<Curve>(d, k, B, q, t, msbs_zero_padding_bit_count, rng),
};

let (public_commit, private_commit) = commitv2(
testcase.a.clone(),
Expand Down
6 changes: 5 additions & 1 deletion tfhe-zk-pok/src/four_squares.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use ark_ff::biginteger::arithmetic::widening_mul;
use rand::prelude::*;

pub fn sqr<T: Copy + core::ops::Mul>(x: T) -> T::Output {
pub fn sqr(x: u128) -> u128 {
x * x
}

pub fn checked_sqr(x: u128) -> Option<u128> {
x.checked_mul(x)
}

// copied from the standard library
// since isqrt is unstable at the moment
pub fn isqrt(this: u128) -> u128 {
Expand Down
97 changes: 89 additions & 8 deletions tfhe-zk-pok/src/proofs/pke_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,10 +499,18 @@ pub fn compute_crs_params(
let mut B_bound_squared = {
(match bound_type {
// GHL factor is 9.75, 9.75**2 = 95.0625
// Result is multiplied and divided by 10000 to avoid floating point operations
// Result is multiplied and divided by 10000 to avoid floating point operations.
// This could be avoided if one day we need to support bigger params.
Bound::GHL => 950625,
Bound::CS => (2 * (d + k) + 4) as u128,
}) * (B_squared + (sqr(d + 2) * (d + k)) as u128 / 4)
Bound::CS => 2 * (d as u128 + k as u128) + 4,
})
.checked_mul(B_squared + (sqr((d + 2) as u128) * (d + k) as u128) / 4)
.unwrap_or_else(|| {
panic!(
"Invalid parameters for zk_pok, B_squared: {B_squared}, d: {d}, k: {k}. \
Please select a smaller B, d and/or k"
)
})
};

if bound_type == Bound::GHL {
Expand All @@ -513,6 +521,14 @@ pub fn compute_crs_params(
// Since we use B_bound_square, the log is divided by 2
let m_bound = 1 + ceil_ilog2(B_bound_squared).div_ceil(2) as usize;

// m_bound is used to do the bit decomposition of a u64 integer, so we check that it can be
// safely used for this
assert!(
m_bound <= 64,
"Invalid parameters for zk_pok, w e only support 64 bits integer. \
The computed m parameter is {m_bound} > 64. Please select a smaller B, d and/or k"
);

// This is also the effective t for encryption
let effective_t_for_decomposition = t >> msbs_zero_padding_bit_count;

Expand All @@ -529,9 +545,9 @@ pub fn compute_crs_params(
/// Use the relationship: `||x||_2 <= sqrt(dim)*||x||_inf`. Since we are only interested in the
/// squared bound, we avoid the sqrt by returning dim*(||x||_inf)^2.
fn inf_norm_bound_to_euclidean_squared(B_inf: u64, dim: usize) -> u128 {
let norm_squared = sqr(B_inf) as u128;

norm_squared * dim as u128
checked_sqr(B_inf as u128)
.and_then(|norm_squared| norm_squared.checked_mul(dim as u128))
.unwrap_or_else(|| panic!("Invalid parameters for zk_pok, B_inf: {B_inf}, d+k: {dim}"))
}

/// Generates a CRS based on the bound the heuristic provided by the lemma 2 of the paper.
Expand Down Expand Up @@ -914,7 +930,11 @@ fn prove_impl<G: Curve>(
_ => unreachable!(),
});
if sanity_check_mode == ProofSanityCheckMode::Panic {
assert!(sqr(acc) as u128 <= B_bound_squared);
assert!(
checked_sqr(acc.unsigned_abs()).unwrap() <= B_bound_squared,
"sqr(acc) ({}) > B_bound_squared ({B_bound_squared})",
sqr(acc as u128)
);
}
acc as i64
})
Expand Down Expand Up @@ -2434,6 +2454,27 @@ mod tests {
msbs_zero_padding_bit_count: 1,
};

/// Compact key params with limits values to test that there is no overflow, using a GHL bound
pub(super) const BIG_TEST_PARAMS_CS: PkeTestParameters = PkeTestParameters {
d: 2048,
k: 2048,
B: 1125899906842624, // 2**50
q: 0,
t: 4, // 1b message, 1b padding
msbs_zero_padding_bit_count: 1,
};

/// Compact key params with limits values to test that there is no overflow, using a
/// Cauchy-Schwarz bound
pub(super) const BIG_TEST_PARAMS_GHL: PkeTestParameters = PkeTestParameters {
d: 2048,
k: 2048,
B: 281474976710656, // 2**48
q: 0,
t: 4, // 1b message, 1b padding
msbs_zero_padding_bit_count: 1,
};

/// Test that the proof is rejected if we use a different value between encryption and proof
#[test]
fn test_pke() {
Expand Down Expand Up @@ -2606,7 +2647,7 @@ mod tests {
assert_eq!(
prove_and_verify(testcase, ct, crs, load, sanity_check_mode, rng),
expected_result,
"Testcase {testcase_name} failed"
"Testcase {testcase_name} with load {load} failed"
)
}
}
Expand Down Expand Up @@ -3139,6 +3180,46 @@ mod tests {
);
}

/// Test encryption of a message with params that are at the limits of what is supported
#[test]
fn test_big_params() {
let rng = &mut StdRng::seed_from_u64(0);

for bound in [Bound::CS, Bound::GHL] {
let params = match bound {
Bound::GHL => BIG_TEST_PARAMS_GHL,
Bound::CS => BIG_TEST_PARAMS_CS,
};
let PkeTestParameters {
d,
k,
B,
q,
t,
msbs_zero_padding_bit_count,
} = params;

let testcase = PkeTestcase::gen(rng, params);
let ct = testcase.encrypt(params);

// Check that there is no overflow with both bounds
let crs = match bound {
Bound::GHL => crs_gen_ghl::<Curve>(d, k, B, q, t, msbs_zero_padding_bit_count, rng),
Bound::CS => crs_gen_cs::<Curve>(d, k, B, q, t, msbs_zero_padding_bit_count, rng),
};

assert_prove_and_verify(
&testcase,
&ct,
&format!("testcase_big_params_{bound:?}"),
&crs,
ProofSanityCheckMode::Panic,
VerificationResult::Accept,
rng,
);
}
}

/// Test compression of proofs
#[test]
fn test_proof_compression() {
Expand Down
Loading