chore(zk): add tests of a proof/verify with different ct

This commit is contained in:
Nicolas Sarlin
2024-11-22 18:07:56 +01:00
committed by Nicolas Sarlin
parent 81f071c30e
commit c07fb7cbb4
3 changed files with 310 additions and 13 deletions

View File

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

View File

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

View File

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