Skip to content

Commit

Permalink
chore(zk): add tests of a proof/verify with different ct
Browse files Browse the repository at this point in the history
  • Loading branch information
nsarlin-zama committed Nov 22, 2024
1 parent 276e186 commit b75533a
Show file tree
Hide file tree
Showing 3 changed files with 310 additions and 13 deletions.
55 changes: 46 additions & 9 deletions tfhe-zk-pok/src/proofs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ mod test {
use serde::{Deserialize, Serialize};

use crate::curve_api::Compressible;
use crate::proofs::decode_q;

// One of our usecases uses 320 bits of additional metadata
pub(super) const METADATA_LEN: usize = (320 / u8::BITS) as usize;
Expand Down Expand Up @@ -438,8 +439,36 @@ mod test {
}
}

/// Encrypt using compact pke, the encryption is validated by doing a decryption
pub(super) fn encrypt(&self, params: PkeTestParameters) -> PkeTestCiphertext {
pub(super) fn sk_encrypt_zero(
&self,
params: PkeTestParameters,
rng: &mut StdRng,
) -> Vec<i64> {
let PkeTestParameters {
d,
k: _,
B,
q: _,
t: _,
msbs_zero_padding_bit_count: _msbs_zero_padding_bit_count,
} = params;

let e = (rng.gen::<u64>() % (2 * B)) as i64 - B as i64;

let mut a = (0..d).map(|_| rng.gen::<i64>()).collect::<Vec<_>>();

let b = a.iter().zip(&self.s).map(|(ai, si)| ai * si).sum::<i64>() + e;

a.push(b);
a
}

/// Decrypt a ciphertext list
pub(super) fn decrypt(
&self,
ct: &PkeTestCiphertext,
params: PkeTestParameters,
) -> Vec<i64> {
let PkeTestParameters {
d,
k,
Expand All @@ -449,8 +478,6 @@ mod test {
msbs_zero_padding_bit_count: _msbs_zero_padding_bit_count,
} = params;

let ct = self.encrypt_unchecked(params);

// Check decryption
let mut m_decrypted = vec![0i64; k];
for (i, decrypted) in m_decrypted.iter_mut().enumerate() {
Expand All @@ -465,15 +492,25 @@ mod test {
dot += self.s[d - j - 1] as i128 * c as i128;
}

let q = if q == 0 { 1i128 << 64 } else { q as i128 };
let decoded_q = decode_q(q) as i128;
let val = ((ct.c2[i] as i128).wrapping_sub(dot)) * t as i128;
let div = val.div_euclid(q);
let rem = val.rem_euclid(q);
let result = div as i64 + (rem > (q / 2)) as i64;
let div = val.div_euclid(decoded_q);
let rem = val.rem_euclid(decoded_q);
let result = div as i64 + (rem > (decoded_q / 2)) as i64;
let result = result.rem_euclid(params.t as i64);
*decrypted = result;
}

m_decrypted
}

/// Encrypt using compact pke, the encryption is validated by doing a decryption
pub(super) fn encrypt(&self, params: PkeTestParameters) -> PkeTestCiphertext {
let ct = self.encrypt_unchecked(params);

// Check decryption
let m_decrypted = self.decrypt(&ct, params);

assert_eq!(self.m, m_decrypted);

ct
Expand All @@ -491,7 +528,7 @@ mod test {
} = params;

let delta = {
let q = if q == 0 { 1i128 << 64 } else { q as i128 };
let q = decode_q(q) as i128;
// delta takes the encoding with the padding bit
(q / t as i128) as u64
};
Expand Down
136 changes: 132 additions & 4 deletions tfhe-zk-pok/src/proofs/pke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,16 @@ mod tests {
msbs_zero_padding_bit_count: 1,
};

/// Compact key params used with pkve1 to encrypt a single message
pub(super) const PKEV1_TEST_PARAMS_SINGLE: PkeTestParameters = PkeTestParameters {
d: 1024,
k: 1,
B: 4398046511104, // 2**42
q: 0,
t: 32, // 2b msg, 2b carry, 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 @@ -1673,6 +1683,128 @@ mod tests {
}
}

/// Test verification with modified ciphertexts
#[test]
fn test_bad_ct() {
let PkeTestParameters {
d,
k,
B,
q,
t,
msbs_zero_padding_bit_count,
} = PKEV1_TEST_PARAMS;

let effective_cleartext_t = t >> msbs_zero_padding_bit_count;

let rng = &mut StdRng::seed_from_u64(0);

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

let ct_zero = testcase.sk_encrypt_zero(PKEV1_TEST_PARAMS_SINGLE, rng);

let c1_plus_zero = ct
.c1
.iter()
.zip(ct_zero.iter())
.map(|(a1, az)| a1.wrapping_add(*az))
.collect();
let c2_plus_zero = vec![ct.c2[0].wrapping_add(*ct_zero.last().unwrap())];

let ct_plus_zero = PkeTestCiphertext {
c1: c1_plus_zero,
c2: c2_plus_zero,
};

let m_plus_zero = testcase.decrypt(&ct_plus_zero, PKEV1_TEST_PARAMS_SINGLE);
assert_eq!(testcase.m, m_plus_zero);

let delta = {
let q = decode_q(q) as i128;
// delta takes the encoding with the padding bit
(q / t as i128) as u64
};

let trivial = rng.gen::<u64>() % effective_cleartext_t;
let trivial_pt = trivial * delta;
let c2_plus_trivial = vec![ct.c2[0].wrapping_add(trivial_pt as i64)];

let ct_plus_trivial = PkeTestCiphertext {
c1: ct.c1.clone(),
c2: c2_plus_trivial,
};

let m_plus_trivial = testcase.decrypt(&ct_plus_trivial, PKEV1_TEST_PARAMS_SINGLE);
assert_eq!(testcase.m[0] + trivial as i64, m_plus_trivial[0]);

let crs = crs_gen::<Curve>(d, k, B, q, t, msbs_zero_padding_bit_count, rng);

// Test proving with one ct and verifying another
let (public_commit_proof, private_commit) = commit(
testcase.a.clone(),
testcase.b.clone(),
ct.c1.clone(),
ct.c2.clone(),
testcase.r.clone(),
testcase.e1.clone(),
testcase.m.clone(),
testcase.e2.clone(),
&crs,
rng,
);

let (public_commit_verify_zero, _) = commit(
testcase.a.clone(),
testcase.b.clone(),
ct_plus_zero.c1.clone(),
ct_plus_zero.c2.clone(),
testcase.r.clone(),
testcase.e1.clone(),
testcase.m.clone(),
testcase.e2.clone(),
&crs,
rng,
);

let (public_commit_verify_trivial, _) = commit(
testcase.a.clone(),
testcase.b.clone(),
ct_plus_trivial.c1.clone(),
ct_plus_trivial.c2.clone(),
testcase.r.clone(),
testcase.e1.clone(),
testcase.m.clone(),
testcase.e2.clone(),
&crs,
rng,
);

for load in [ComputeLoad::Proof, ComputeLoad::Verify] {
let proof = prove(
(&crs, &public_commit_proof),
&private_commit,
&testcase.metadata,
load,
rng,
);

assert!(verify(
&proof,
(&crs, &public_commit_verify_zero),
&testcase.metadata
)
.is_err());

assert!(verify(
&proof,
(&crs, &public_commit_verify_trivial),
&testcase.metadata
)
.is_err());
}
}

/// Test compression of proofs
#[test]
fn test_proof_compression() {
Expand All @@ -1690,8 +1822,6 @@ mod tests {
let testcase = PkeTestcase::gen(rng, PKEV1_TEST_PARAMS);
let ct = testcase.encrypt(PKEV1_TEST_PARAMS);

type Curve = curve_api::Bls12_446;

let crs_k = k + 1 + (rng.gen::<usize>() % (d - k));

let public_param = crs_gen::<Curve>(d, crs_k, B, q, t, msbs_zero_padding_bit_count, rng);
Expand Down Expand Up @@ -1743,8 +1873,6 @@ mod tests {
let testcase = PkeTestcase::gen(rng, PKEV1_TEST_PARAMS);
let ct = testcase.encrypt(PKEV1_TEST_PARAMS);

type Curve = curve_api::Bls12_446;

let crs_k = k + 1 + (rng.gen::<usize>() % (d - k));

let public_param = crs_gen::<Curve>(d, crs_k, B, q, t, msbs_zero_padding_bit_count, rng);
Expand Down
Loading

0 comments on commit b75533a

Please sign in to comment.