mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-09 22:57:59 -05:00
465 lines
12 KiB
Rust
465 lines
12 KiB
Rust
#![allow(non_snake_case)]
|
|
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
|
|
use rand::rngs::StdRng;
|
|
use rand::{Rng, SeedableRng};
|
|
use serde::Serialize;
|
|
use tfhe_zk_pok::proofs::pke::{commit, crs_gen, PrivateCommit, PublicCommit, PublicParams};
|
|
|
|
use tfhe_zk_pok::proofs::pke_v2::{
|
|
commit as commitv2, crs_gen_cs as crs_genv2_cs, crs_gen_ghl as crs_genv2_ghl, Bound,
|
|
PrivateCommit as PrivateCommitv2, PublicCommit as PublicCommitv2,
|
|
PublicParams as PublicParamsv2,
|
|
};
|
|
|
|
// One of our usecases uses 320 bits of additional metadata
|
|
pub const METADATA_LEN: usize = (320 / u8::BITS) as usize;
|
|
|
|
pub fn polymul_rev(a: &[i64], b: &[i64]) -> Vec<i64> {
|
|
assert_eq!(a.len(), b.len());
|
|
let d = a.len();
|
|
let mut c = vec![0i64; d];
|
|
|
|
for i in 0..d {
|
|
for j in 0..d {
|
|
if i + j < d {
|
|
c[i + j] = c[i + j].wrapping_add(a[i].wrapping_mul(b[d - j - 1]));
|
|
} else {
|
|
c[i + j - d] = c[i + j - d].wrapping_sub(a[i].wrapping_mul(b[d - j - 1]));
|
|
}
|
|
}
|
|
}
|
|
|
|
c
|
|
}
|
|
|
|
#[derive(Clone, Copy, Default, Serialize)]
|
|
pub struct CryptoParametersRecord {
|
|
pub lwe_dimension: usize,
|
|
#[serde(serialize_with = "CryptoParametersRecord::serialize_distribution")]
|
|
pub lwe_noise_distribution: u64,
|
|
pub message_modulus: u64,
|
|
pub carry_modulus: u64,
|
|
pub ciphertext_modulus: u64,
|
|
}
|
|
|
|
impl CryptoParametersRecord {
|
|
pub fn noise_distribution_as_string(bound: u64) -> String {
|
|
format!("TUniform({})", bound.ilog2())
|
|
}
|
|
|
|
pub fn serialize_distribution<S>(
|
|
noise_distribution: &u64,
|
|
serializer: S,
|
|
) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: serde::Serializer,
|
|
{
|
|
serializer.serialize_str(&Self::noise_distribution_as_string(*noise_distribution))
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
enum PolynomialMultiplication {
|
|
Fft,
|
|
// Ntt,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
enum IntegerRepresentation {
|
|
Radix,
|
|
// Crt,
|
|
// Hybrid,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
enum ExecutionType {
|
|
Sequential,
|
|
Parallel,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
enum KeySetType {
|
|
Single,
|
|
// Multi,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
enum OperandType {
|
|
CipherText,
|
|
PlainText,
|
|
}
|
|
|
|
#[derive(Clone, Serialize)]
|
|
pub enum OperatorType {
|
|
Atomic,
|
|
// AtomicPattern,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct BenchmarkParametersRecord {
|
|
display_name: String,
|
|
crypto_parameters_alias: String,
|
|
crypto_parameters: CryptoParametersRecord,
|
|
message_modulus: Option<usize>,
|
|
carry_modulus: Option<usize>,
|
|
ciphertext_modulus: usize,
|
|
bit_size: u32,
|
|
polynomial_multiplication: PolynomialMultiplication,
|
|
precision: u32,
|
|
error_probability: f64,
|
|
integer_representation: IntegerRepresentation,
|
|
decomposition_basis: Vec<u32>,
|
|
pbs_algorithm: Option<String>,
|
|
execution_type: ExecutionType,
|
|
key_set_type: KeySetType,
|
|
operand_type: OperandType,
|
|
operator_type: OperatorType,
|
|
}
|
|
|
|
/// Writes benchmarks parameters to disk in JSON format.
|
|
pub fn write_to_json<T: Into<CryptoParametersRecord>>(
|
|
bench_id: &str,
|
|
params: T,
|
|
params_alias: impl Into<String>,
|
|
display_name: impl Into<String>,
|
|
) {
|
|
let params = params.into();
|
|
|
|
let execution_type = match bench_id.contains("parallelized") {
|
|
true => ExecutionType::Parallel,
|
|
false => ExecutionType::Sequential,
|
|
};
|
|
let operand_type = match bench_id.contains("scalar") {
|
|
true => OperandType::PlainText,
|
|
false => OperandType::CipherText,
|
|
};
|
|
|
|
let record = BenchmarkParametersRecord {
|
|
display_name: display_name.into(),
|
|
crypto_parameters_alias: params_alias.into(),
|
|
crypto_parameters: params,
|
|
message_modulus: Some(params.message_modulus as usize),
|
|
carry_modulus: Some(params.carry_modulus as usize),
|
|
ciphertext_modulus: 64,
|
|
bit_size: params.message_modulus as u32,
|
|
polynomial_multiplication: PolynomialMultiplication::Fft,
|
|
precision: (params.message_modulus as u32).ilog2(),
|
|
error_probability: 2f64.powf(-41.0),
|
|
integer_representation: IntegerRepresentation::Radix,
|
|
decomposition_basis: Vec::new(),
|
|
pbs_algorithm: None, // To be added in future version
|
|
execution_type,
|
|
key_set_type: KeySetType::Single,
|
|
operand_type,
|
|
operator_type: OperatorType::Atomic,
|
|
};
|
|
|
|
let mut params_directory = ["benchmarks_parameters", bench_id]
|
|
.iter()
|
|
.collect::<PathBuf>();
|
|
fs::create_dir_all(¶ms_directory).unwrap();
|
|
params_directory.push("parameters.json");
|
|
|
|
fs::write(params_directory, serde_json::to_string(&record).unwrap()).unwrap();
|
|
}
|
|
|
|
impl From<PkeTestParameters> for CryptoParametersRecord {
|
|
fn from(value: PkeTestParameters) -> Self {
|
|
let effective = value.t / 2; // Remove padding bit
|
|
let (message_modulus, carry_modulus) = match effective.ilog2() {
|
|
2 => (2, 2),
|
|
4 => (4, 4),
|
|
6 => (8, 8),
|
|
8 => (16, 16),
|
|
_ => panic!("Unsupported parameters for tfhe-zk-pok bench"),
|
|
};
|
|
|
|
Self {
|
|
lwe_dimension: value.d,
|
|
lwe_noise_distribution: value.B,
|
|
message_modulus,
|
|
carry_modulus,
|
|
ciphertext_modulus: value.q,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// parameters needed for a PKE zk proof test
|
|
#[derive(Copy, Clone)]
|
|
pub struct PkeTestParameters {
|
|
pub d: usize,
|
|
pub k: usize,
|
|
pub B: u64,
|
|
pub q: u64,
|
|
pub t: u64,
|
|
pub msbs_zero_padding_bit_count: u64,
|
|
}
|
|
|
|
/// An encrypted PKE ciphertext
|
|
pub struct PkeTestCiphertext {
|
|
pub c1: Vec<i64>,
|
|
pub c2: Vec<i64>,
|
|
}
|
|
|
|
/// A randomly generated testcase of pke encryption
|
|
pub struct PkeTestcase {
|
|
pub a: Vec<i64>,
|
|
pub e1: Vec<i64>,
|
|
pub e2: Vec<i64>,
|
|
pub r: Vec<i64>,
|
|
pub m: Vec<i64>,
|
|
pub b: Vec<i64>,
|
|
pub metadata: [u8; METADATA_LEN],
|
|
s: Vec<i64>,
|
|
}
|
|
|
|
impl PkeTestcase {
|
|
pub fn gen(rng: &mut StdRng, params: PkeTestParameters) -> Self {
|
|
let PkeTestParameters {
|
|
d,
|
|
k,
|
|
B,
|
|
q: _q,
|
|
t,
|
|
msbs_zero_padding_bit_count,
|
|
} = params;
|
|
|
|
let effective_cleartext_t = t >> msbs_zero_padding_bit_count;
|
|
|
|
let a = (0..d).map(|_| rng.gen::<i64>()).collect::<Vec<_>>();
|
|
|
|
let s = (0..d)
|
|
.map(|_| (rng.gen::<u64>() % 2) as i64)
|
|
.collect::<Vec<_>>();
|
|
|
|
let e = (0..d)
|
|
.map(|_| (rng.gen::<u64>() % (2 * B)) as i64 - B as i64)
|
|
.collect::<Vec<_>>();
|
|
let e1 = (0..d)
|
|
.map(|_| (rng.gen::<u64>() % (2 * B)) as i64 - B as i64)
|
|
.collect::<Vec<_>>();
|
|
let e2 = (0..k)
|
|
.map(|_| (rng.gen::<u64>() % (2 * B)) as i64 - B as i64)
|
|
.collect::<Vec<_>>();
|
|
|
|
let r = (0..d)
|
|
.map(|_| (rng.gen::<u64>() % 2) as i64)
|
|
.collect::<Vec<_>>();
|
|
let m = (0..k)
|
|
.map(|_| (rng.gen::<u64>() % effective_cleartext_t) as i64)
|
|
.collect::<Vec<_>>();
|
|
let b = polymul_rev(&a, &s)
|
|
.into_iter()
|
|
.zip(e.iter())
|
|
.map(|(x, e)| x.wrapping_add(*e))
|
|
.collect::<Vec<_>>();
|
|
|
|
let mut metadata = [0u8; METADATA_LEN];
|
|
metadata.fill_with(|| rng.gen::<u8>());
|
|
|
|
Self {
|
|
a,
|
|
e1,
|
|
e2,
|
|
r,
|
|
m,
|
|
b,
|
|
metadata,
|
|
s,
|
|
}
|
|
}
|
|
|
|
/// Encrypt using compact pke
|
|
pub fn encrypt(&self, params: PkeTestParameters) -> PkeTestCiphertext {
|
|
let PkeTestParameters {
|
|
d,
|
|
k,
|
|
B: _B,
|
|
q,
|
|
t,
|
|
msbs_zero_padding_bit_count: _msbs_zero_padding_bit_count,
|
|
} = params;
|
|
|
|
let delta = {
|
|
let q = if q == 0 { 1i128 << 64 } else { q as i128 };
|
|
// delta takes the encoding with the padding bit
|
|
(q / t as i128) as u64
|
|
};
|
|
|
|
let c1 = polymul_rev(&self.a, &self.r)
|
|
.into_iter()
|
|
.zip(self.e1.iter())
|
|
.map(|(x, e1)| x.wrapping_add(*e1))
|
|
.collect::<Vec<_>>();
|
|
|
|
let mut c2 = vec![0i64; k];
|
|
|
|
for (i, c2) in c2.iter_mut().enumerate() {
|
|
let mut dot = 0i64;
|
|
for j in 0..d {
|
|
let b = if i + j < d {
|
|
self.b[d - j - i - 1]
|
|
} else {
|
|
self.b[2 * d - j - i - 1].wrapping_neg()
|
|
};
|
|
|
|
dot = dot.wrapping_add(self.r[d - j - 1].wrapping_mul(b));
|
|
}
|
|
|
|
*c2 = dot
|
|
.wrapping_add(self.e2[i])
|
|
.wrapping_add((delta * self.m[i] as u64) as i64);
|
|
}
|
|
|
|
// Check decryption
|
|
let mut m_roundtrip = vec![0i64; k];
|
|
for i in 0..k {
|
|
let mut dot = 0i128;
|
|
for j in 0..d {
|
|
let c = if i + j < d {
|
|
c1[d - j - i - 1]
|
|
} else {
|
|
c1[2 * d - j - i - 1].wrapping_neg()
|
|
};
|
|
|
|
dot += self.s[d - j - 1] as i128 * c as i128;
|
|
}
|
|
|
|
let q = if q == 0 { 1i128 << 64 } else { q as i128 };
|
|
let val = ((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 result = result.rem_euclid(params.t as i64);
|
|
m_roundtrip[i] = result;
|
|
}
|
|
|
|
assert_eq!(self.m, m_roundtrip);
|
|
|
|
PkeTestCiphertext { c1, c2 }
|
|
}
|
|
}
|
|
|
|
/// Compact key params used with pkev2
|
|
pub const PKEV2_TEST_PARAMS: PkeTestParameters = PkeTestParameters {
|
|
d: 2048,
|
|
k: 1024,
|
|
B: 131072, // 2**17
|
|
q: 0,
|
|
t: 32, // 2b msg, 2b carry, 1b padding
|
|
msbs_zero_padding_bit_count: 1,
|
|
};
|
|
|
|
/// Compact key params used with pkev1
|
|
pub const PKEV1_TEST_PARAMS: PkeTestParameters = PkeTestParameters {
|
|
d: 1024,
|
|
k: 1024,
|
|
B: 4398046511104, // 2**42
|
|
q: 0,
|
|
t: 32, // 2b msg, 2b carry, 1b padding
|
|
msbs_zero_padding_bit_count: 1,
|
|
};
|
|
|
|
type Curve = tfhe_zk_pok::curve_api::Bls12_446;
|
|
|
|
#[allow(unused)]
|
|
pub fn init_params_v1(
|
|
test_params: PkeTestParameters,
|
|
) -> (
|
|
PublicParams<Curve>,
|
|
PublicCommit<Curve>,
|
|
PrivateCommit<Curve>,
|
|
[u8; METADATA_LEN],
|
|
) {
|
|
let PkeTestParameters {
|
|
d,
|
|
k,
|
|
B,
|
|
q,
|
|
t,
|
|
msbs_zero_padding_bit_count,
|
|
} = test_params;
|
|
|
|
let rng = &mut StdRng::seed_from_u64(0);
|
|
|
|
let testcase = PkeTestcase::gen(rng, test_params);
|
|
|
|
let ct = testcase.encrypt(test_params);
|
|
|
|
let public_param = crs_gen::<Curve>(d, k, B, q, t, msbs_zero_padding_bit_count, rng);
|
|
|
|
let (public_commit, 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(),
|
|
&public_param,
|
|
rng,
|
|
);
|
|
|
|
(
|
|
public_param,
|
|
public_commit,
|
|
private_commit,
|
|
testcase.metadata,
|
|
)
|
|
}
|
|
|
|
#[allow(unused)]
|
|
pub fn init_params_v2(
|
|
test_params: PkeTestParameters,
|
|
bound: Bound,
|
|
) -> (
|
|
PublicParamsv2<Curve>,
|
|
PublicCommitv2<Curve>,
|
|
PrivateCommitv2<Curve>,
|
|
[u8; METADATA_LEN],
|
|
) {
|
|
let PkeTestParameters {
|
|
d,
|
|
k,
|
|
B,
|
|
q,
|
|
t,
|
|
msbs_zero_padding_bit_count,
|
|
} = test_params;
|
|
|
|
let rng = &mut StdRng::seed_from_u64(0);
|
|
|
|
let testcase = PkeTestcase::gen(rng, test_params);
|
|
|
|
let ct = testcase.encrypt(test_params);
|
|
|
|
let public_param = match bound {
|
|
Bound::GHL => crs_genv2_ghl::<Curve>(d, k, B, q, t, msbs_zero_padding_bit_count, rng),
|
|
Bound::CS => crs_genv2_cs::<Curve>(d, k, B, q, t, msbs_zero_padding_bit_count, rng),
|
|
};
|
|
|
|
let (public_commit, private_commit) = commitv2(
|
|
testcase.a.clone(),
|
|
testcase.b.clone(),
|
|
ct.c1.clone(),
|
|
ct.c2.clone(),
|
|
testcase.r.clone(),
|
|
testcase.e1.clone(),
|
|
testcase.m.clone(),
|
|
testcase.e2.clone(),
|
|
&public_param,
|
|
rng,
|
|
);
|
|
|
|
(
|
|
public_param,
|
|
public_commit,
|
|
private_commit,
|
|
testcase.metadata,
|
|
)
|
|
}
|