Skip to content
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
213 changes: 189 additions & 24 deletions tests/backward_compatibility/high_level_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ use tfhe_backward_compat_data::load::{
use tfhe_backward_compat_data::{
DataKind, HlBoolCiphertextTest, HlCiphertextTest, HlClientKeyTest, HlCompressedKVStoreTest,
HlCompressedSquashedNoiseCiphertextListTest, HlCompressedXofKeySetTest,
HlHeterogeneousCiphertextListTest, HlPublicKeyTest, HlServerKeyTest, HlSignedCiphertextTest,
HlSquashedNoiseBoolCiphertextTest, HlSquashedNoiseSignedCiphertextTest,
HlSquashedNoiseUnsignedCiphertextTest, TestMetadata, TestType, Testcase, ZkPkePublicParamsTest,
HlHeterogeneousCiphertextListTest, HlPublicKeyTest, HlSeededCompactCiphertextListTest,
HlServerKeyTest, HlSignedCiphertextTest, HlSquashedNoiseBoolCiphertextTest,
HlSquashedNoiseSignedCiphertextTest, HlSquashedNoiseUnsignedCiphertextTest, TestMetadata,
TestType, Testcase, ZkPkePublicParamsTest, ZkProofAuxiliaryInfo,
};
use tfhe_versionable::Unversionize;

Expand Down Expand Up @@ -249,44 +250,206 @@ pub fn test_hl_heterogeneous_ciphertext_list_elements<CtList: CiphertextList>(
key: &ClientKey,
test: &HlHeterogeneousCiphertextListTest,
) -> Result<(), String> {
for idx in 0..(list.len()) {
match test.data_kinds[idx] {
verify_expanded_values(&list, &test.clear_values, &test.data_kinds, key)
}

fn verify_expanded_values<CtList>(
list: &CtList,
clear_values: &[i64],
data_kinds: &[DataKind],
key: &ClientKey,
) -> Result<(), String>
where
CtList: CiphertextList,
{
if clear_values.len() != data_kinds.len() {
return Err(format!(
"Number of clear values ({}) is not the same as the number of data kind ({})",
clear_values.len(),
data_kinds.len(),
));
}

if clear_values.len() != list.len() {
return Err(format!(
"Number of clear values ({}) is not the same as the number of values in the expander({})",
clear_values.len(),
list.len(),
));
}

for (idx, (value, kind)) in clear_values.iter().zip(data_kinds.iter()).enumerate() {
match kind {
DataKind::Bool => {
let ct: FheBool = list.get(idx).unwrap().unwrap();
let ct: FheBool = list
.get(idx)
.map_err(|e| format!("Failed to get bool at index {idx}: {e}"))?
.ok_or_else(|| format!("No value at index {idx}"))?;
let clear = ct.decrypt(key);
if clear != (test.clear_values[idx] != 0) {
if clear != (*value != 0) {
return Err(format!(
"Invalid decrypted cleartext:\n Expected :\n{:?}\nGot:\n{:?}",
clear, test.clear_values[idx]
"Invalid decrypted bool at index {idx}: expected {:?}, got {clear:?}",
*value != 0
));
}
}
DataKind::Signed => {
let ct: FheInt8 = list.get(idx).unwrap().unwrap();
let ct: FheInt8 = list
.get(idx)
.map_err(|e| format!("Failed to get signed at index {idx}: {e}"))?
.ok_or_else(|| format!("No value at index {idx}"))?;
let clear: i8 = ct.decrypt(key);
if clear != test.clear_values[idx] as i8 {
if clear != *value as i8 {
return Err(format!(
"Invalid decrypted cleartext:\n Expected :\n{:?}\nGot:\n{:?}",
clear,
(test.clear_values[idx] as i8)
"Invalid decrypted signed at index {idx}: expected {:?}, got {clear:?}",
*value as i8
));
}
}
DataKind::Unsigned => {
let ct: FheUint8 = list.get(idx).unwrap().unwrap();
let ct: FheUint8 = list
.get(idx)
.map_err(|e| format!("Failed to get unsigned at index {idx}: {e}"))?
.ok_or_else(|| format!("No value at index {idx}"))?;
let clear: u8 = ct.decrypt(key);
if clear != test.clear_values[idx] as u8 {
if clear != *value as u8 {
return Err(format!(
"Invalid decrypted cleartext:\n Expected :\n{:?}\nGot:\n{:?}",
clear, test.clear_values[idx]
"Invalid decrypted unsigned at index {idx}: expected {:?}, got {clear:?}",
*value as u8
));
}
}
};
}
}
Ok(())
}

/// Shared core for seeded compact ciphertext list backward compat tests.
/// When `zk_proof_info` is `Some`, operates in ZK (proven) mode; otherwise plain mode.
#[allow(clippy::too_many_arguments)]
fn test_hl_seeded_compact_list_core<T: TestType>(
dir: &Path,
test: &T,
format: DataFormat,
key_filename: &str,
public_key_filename: &str,
clear_values: &[i64],
data_kinds: &[DataKind],
seed: &[u8],
zk_proof_info: Option<&ZkProofAuxiliaryInfo>,
) -> Result<TestSuccess, TestFailure> {
#[cfg(not(feature = "zk-pok"))]
if zk_proof_info.is_some() {
return Ok(test.success(format));
}

let key_file = dir.join(key_filename);
let key = ClientKey::unversionize(
load_versioned_auxiliary(key_file).map_err(|e| test.failure(e, format))?,
)
.map_err(|e| test.failure(e, format))?;

let server_key = key.generate_server_key();
set_server_key(server_key);

let pubkey_file = dir.join(public_key_filename);
let pubkey = CompactPublicKey::unversionize(
load_versioned_auxiliary(pubkey_file).map_err(|e| test.failure(e, format))?,
)
.map_err(|e| test.failure(e, format))?;

let mut builder = CompactCiphertextList::builder(&pubkey);
for (value, kind) in clear_values.iter().zip(data_kinds.iter()) {
match kind {
DataKind::Unsigned => {
builder.push(*value as u8);
}
DataKind::Signed => {
builder.push(*value as i8);
}
DataKind::Bool => {
builder.push(*value != 0);
}
}
}

if let Some(_proof_info) = zk_proof_info {
#[cfg(feature = "zk-pok")]
{
use tfhe::zk::ZkComputeLoad;

let crs_file = dir.join(&*_proof_info.params_filename);
let crs = CompactPkeCrs::unversionize(
load_versioned_auxiliary(crs_file).map_err(|e| test.failure(e, format))?,
)
.map_err(|e| test.failure(e, format))?;

let pregenerated: ProvenCompactCiphertextList =
load_and_unversionize(dir, test, format)?;

let rebuilt = builder
.build_with_proof_packed_seeded(
&crs,
_proof_info.metadata.as_bytes(),
ZkComputeLoad::Proof,
seed,
)
.map_err(|e| test.failure(e, format))?;

if pregenerated != rebuilt {
return Err(test.failure(
"Seeded proven compact list rebuilt from values/seed does not match pregenerated",
format,
));
}

let expanded = pregenerated
.verify_and_expand(&crs, &pubkey, _proof_info.metadata.as_bytes())
.map_err(|e| test.failure(e, format))?;
verify_expanded_values(&expanded, clear_values, data_kinds, &key)
.map_err(|e| test.failure(e, format))?;
}
} else {
let pregenerated: CompactCiphertextList = load_and_unversionize(dir, test, format)?;

let rebuilt = builder.build_packed_seeded(seed).unwrap();

if pregenerated != rebuilt {
return Err(test.failure(
"Seeded compact list rebuilt from values/seed does not match pregenerated",
format,
));
}

let expanded = pregenerated.expand().map_err(|e| test.failure(e, format))?;
verify_expanded_values(&expanded, clear_values, data_kinds, &key)
.map_err(|e| test.failure(e, format))?;
}

Ok(test.success(format))
}

/// Test seeded compact ciphertext list: loads pregenerated list, rebuilds from
/// stored values/seed, asserts they match via PartialEq.
pub fn test_hl_seeded_compact_ciphertext_list(
dir: &Path,
test: &HlSeededCompactCiphertextListTest,
format: DataFormat,
) -> Result<TestSuccess, TestFailure> {
test_hl_seeded_compact_list_core(
dir,
test,
format,
&test.key_filename,
&test.public_key_filename,
&test.clear_values,
&test.data_kinds,
&test.seed,
test.proof_info.as_ref(),
)
}

/// Test seeded proven compact ciphertext list
/// Test HL client key: loads the key and checks the parameters using the values stored in
/// the test metadata.
pub fn test_hl_clientkey(
Expand Down Expand Up @@ -778,21 +941,20 @@ pub fn test_hl_compressed_squashed_noise_ciphertext_list(
}

for i in 0..list.len() {
let decrypted = match test.data_kinds[i] {
let decrypted: i64 = match test.data_kinds[i] {
DataKind::Unsigned => {
let ct: SquashedNoiseFheUint = list.get(i).unwrap().unwrap();
let clear: u64 = ct.decrypt(&key);
clear
clear as i64
}
DataKind::Signed => {
let ct: SquashedNoiseFheInt = list.get(i).unwrap().unwrap();
let clear: i64 = ct.decrypt(&key);
clear as u64
ct.decrypt(&key)
}
DataKind::Bool => {
let ct: SquashedNoiseFheBool = list.get(i).unwrap().unwrap();
let clear: bool = ct.decrypt(&key);
clear as u64
clear as i64
}
};

Expand Down Expand Up @@ -953,6 +1115,9 @@ impl TestedModule for Hl {
TestMetadata::HlCompressedXofKeySet(test) => {
test_hl_compressed_xof_key_set_test(test_dir.as_ref(), test, format).into()
}
TestMetadata::HlSeededCompactCiphertextList(test) => {
test_hl_seeded_compact_ciphertext_list(test_dir.as_ref(), test, format).into()
}
_ => {
println!("WARNING: missing test: {:?}", testcase.metadata);
TestResult::Skipped(testcase.skip())
Expand Down
2 changes: 1 addition & 1 deletion tfhe-csprng/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tfhe-csprng"
version = "0.8.0"
version = "0.9.0"
edition = "2021"
license = "BSD-3-Clause-Clear"
description = "Cryptographically Secure PRNG used in the TFHE-rs library."
Expand Down
2 changes: 1 addition & 1 deletion tfhe-zk-pok/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tfhe-zk-pok"
version = "0.8.0"
version = "0.8.1"
edition = "2021"
keywords = ["zero", "knowledge", "proof", "vector-commitments"]
homepage = "https://zama.org/"
Expand Down
4 changes: 2 additions & 2 deletions tfhe-zk-pok/src/curve_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,9 +446,9 @@ impl PairingGroupOps<bls12_446::Zp, bls12_446::G1, bls12_446::G2> for bls12_446:

// These are just ZSTs that are not actually produced and are only used for their
// associated types. So it's ok to derive "NotVersioned" for them.
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, NotVersioned)]
#[derive(Debug, Copy, Clone, PartialEq, serde::Serialize, serde::Deserialize, NotVersioned)]
pub struct Bls12_381;
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, NotVersioned)]
#[derive(Debug, Copy, Clone, PartialEq, serde::Serialize, serde::Deserialize, NotVersioned)]
pub struct Bls12_446;

impl Curve for Bls12_381 {
Expand Down
4 changes: 2 additions & 2 deletions tfhe-zk-pok/src/proofs/pke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ impl<G: Curve> PublicParams<G> {
}
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Versionize)]
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Versionize)]
#[serde(bound(
deserialize = "G: Curve, G::G1: serde::Deserialize<'de>, G::G2: serde::Deserialize<'de>",
serialize = "G: Curve, G::G1: serde::Serialize, G::G2: serde::Serialize"
Expand Down Expand Up @@ -325,7 +325,7 @@ impl<G: Curve> Proof<G> {

/// These fields can be pre-computed on the prover side in the faster Verifier scheme. If that's the
/// case, they should be included in the proof.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Versionize)]
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Versionize)]
#[serde(bound(
deserialize = "G: Curve, G::G1: serde::Deserialize<'de>, G::G2: serde::Deserialize<'de>",
serialize = "G: Curve, G::G1: serde::Serialize, G::G2: serde::Serialize"
Expand Down
2 changes: 1 addition & 1 deletion tfhe-zk-pok/src/proofs/pke_v2/hashes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl PkeV2HashConfig {
/// List of hash config that were used for a given version of this crate
///
/// This is stored in the proof so that we only support a specific subset of all possible config.
#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, Versionize)]
#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Versionize)]
#[versionize(PkeV2SupportedHashConfigVersions)]
pub enum PkeV2SupportedHashConfig {
V0_4_0 = 0,
Expand Down
4 changes: 2 additions & 2 deletions tfhe-zk-pok/src/proofs/pke_v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ impl<G: Curve> PublicParams<G> {

/// This represents a proof that the given ciphertext is a valid encryptions of the input messages
/// with the provided public key.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Versionize)]
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Versionize)]
#[serde(bound(
deserialize = "G: Curve, G::G1: serde::Deserialize<'de>, G::G2: serde::Deserialize<'de>",
serialize = "G: Curve, G::G1: serde::Serialize, G::G2: serde::Serialize"
Expand Down Expand Up @@ -428,7 +428,7 @@ impl<G: Curve> Proof<G> {

/// These fields can be pre-computed on the prover side in the faster Verifier scheme. If that's the
/// case, they should be included in the proof.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Versionize)]
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Versionize)]
#[versionize(ComputeLoadProofFieldsVersions)]
pub(crate) struct ComputeLoadProofFields<G: Curve> {
pub(crate) C_hat_h3: G::G2,
Expand Down
4 changes: 2 additions & 2 deletions tfhe/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ clap-num = { version = "1.1.1" }
cbindgen = { version = "0.28", optional = true }

[dependencies]
tfhe-csprng = { version = "0.8.0", path = "../tfhe-csprng", features = [
tfhe-csprng = { version = "0.9.0", path = "../tfhe-csprng", features = [
"parallel",
] }
serde = { workspace = true, features = ["default", "derive"] }
Expand All @@ -78,7 +78,7 @@ blake3 = { version = "1.8", optional = true }
itertools = { workspace = true }
rand_core = { version = "0.6.4", features = ["std"] }
strum = { version = "0.27", features = ["derive"], optional = true }
tfhe-zk-pok = { version = "0.8.0", path = "../tfhe-zk-pok", optional = true }
tfhe-zk-pok = { version = "0.8.1", path = "../tfhe-zk-pok", optional = true }
tfhe-versionable = { version = "0.7.0", path = "../utils/tfhe-versionable" }

# wasm deps
Expand Down
Loading
Loading