mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-10 07:08:03 -05:00
chore(zk): add tests of a proof/verify with different ct
This commit is contained in:
committed by
Nicolas Sarlin
parent
81f071c30e
commit
c07fb7cbb4
@@ -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;
|
||||
@@ -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,
|
||||
@@ -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() {
|
||||
@@ -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
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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() {
|
||||
@@ -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() {
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -2424,6 +2424,16 @@ mod tests {
|
||||
msbs_zero_padding_bit_count: 1,
|
||||
};
|
||||
|
||||
/// Compact key params used with pkve2 to encrypt a single message
|
||||
pub(super) const PKEV2_TEST_PARAMS_SINGLE: PkeTestParameters = PkeTestParameters {
|
||||
d: 2048,
|
||||
k: 1,
|
||||
B: 131072, // 2**17
|
||||
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() {
|
||||
@@ -2961,6 +2971,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,
|
||||
} = PKEV2_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, PKEV2_TEST_PARAMS_SINGLE);
|
||||
let ct = testcase.encrypt(PKEV2_TEST_PARAMS_SINGLE);
|
||||
|
||||
let ct_zero = testcase.sk_encrypt_zero(PKEV2_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, PKEV2_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, PKEV2_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() {
|
||||
|
||||
Reference in New Issue
Block a user