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/zk test bad ct #1809

Merged
merged 2 commits into from
Nov 25, 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
57 changes: 47 additions & 10 deletions tfhe-zk-pok/src/proofs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ impl<G: Curve> GroupElements<G> {
}

/// Allows to compute proof with bad inputs for tests
#[derive(PartialEq, Eq)]
#[derive(Copy, Clone, PartialEq, Eq)]
enum ProofSanityCheckMode {
Panic,
#[cfg(test)]
Expand Down 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
151 changes: 138 additions & 13 deletions tfhe-zk-pok/src/proofs/pke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1262,6 +1262,8 @@ mod tests {
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};

type Curve = curve_api::Bls12_446;

/// Compact key params used with pkev1
pub(super) const PKEV1_TEST_PARAMS: PkeTestParameters = PkeTestParameters {
d: 1024,
Expand All @@ -1272,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 @@ -1310,8 +1322,6 @@ mod tests {
let mut fake_metadata = [255u8; METADATA_LEN];
fake_metadata.fill_with(|| rng.gen::<u8>());

type Curve = curve_api::Bls12_446;

// To check management of bigger k_max from CRS during test
let crs_k = k + 1 + (rng.gen::<usize>() % (d - k));

Expand Down Expand Up @@ -1397,9 +1407,9 @@ mod tests {
}
}

fn prove_and_verify<G: Curve>(
fn prove_and_verify(
testcase: &PkeTestcase,
crs: &PublicParams<G>,
crs: &PublicParams<Curve>,
load: ComputeLoad,
rng: &mut StdRng,
) -> VerificationResult {
Expand Down Expand Up @@ -1434,10 +1444,10 @@ mod tests {
}
}

fn assert_prove_and_verify<G: Curve>(
fn assert_prove_and_verify(
testcase: &PkeTestcase,
testcase_name: &str,
crs: &PublicParams<G>,
crs: &PublicParams<Curve>,
rng: &mut StdRng,
expected_result: VerificationResult,
) {
Expand Down Expand Up @@ -1466,8 +1476,6 @@ mod tests {

let testcase = PkeTestcase::gen(rng, PKEV1_TEST_PARAMS);

type Curve = curve_api::Bls12_446;

// A CRS where the number of slots = the number of messages to encrypt
let crs = crs_gen::<Curve>(d, k, B, q, t, msbs_zero_padding_bit_count, rng);

Expand Down Expand Up @@ -1630,7 +1638,6 @@ mod tests {
};

let ct = testcase.encrypt(PKEV1_TEST_PARAMS);
type Curve = curve_api::Bls12_446;

// To check management of bigger k_max from CRS during test
let crs_k = k + 1 + (rng.gen::<usize>() % (d - k));
Expand Down Expand Up @@ -1676,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 @@ -1693,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 @@ -1746,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
Loading