#![allow(non_snake_case)]
use super::{
com_eq::{ComEq, ComEqSecret, CommittedMessages, Response as ComEqResponse},
common::*,
dlog::*,
};
use crate::{
common::*,
curve_arithmetic::{multiexp, Curve, Field},
elgamal::ChunkSize,
encrypted_transfers::types::CHUNK_SIZE,
pedersen_commitment::{Randomness as PedersenRandomness, Value},
random_oracle::{Challenge, RandomOracle},
};
use itertools::izip;
use std::rc::Rc;
pub struct ElgDec<C: Curve> {
pub public: C,
pub coeff: [C; 2],
}
impl<C: Curve> ElgDec<C> {
fn public(&self, ro: &mut RandomOracle) {
ro.append_message(b"public", &self.public);
ro.extend_from(b"coeff", &self.coeff)
}
}
pub struct EncTrans<C: Curve> {
pub dlog: Dlog<C>,
pub elg_dec: ElgDec<C>,
pub encexp1: Vec<ComEq<C, C>>,
pub encexp2: Vec<ComEq<C, C>>,
}
#[derive(Debug, Serialize, Clone)]
pub struct EncTransResponse<C: Curve> {
response_common: C::Scalar,
#[size_length = 4]
response_encexp1: Vec<ComEqResponse<C>>,
#[size_length = 4]
response_encexp2: Vec<ComEqResponse<C>>,
}
pub struct EncTransSecret<C: Curve> {
pub dlog_secret: Rc<C::Scalar>,
pub encexp1_secrets: Vec<ComEqSecret<C>>,
pub encexp2_secrets: Vec<ComEqSecret<C>>,
}
#[derive(Debug, Serialize)]
pub struct EncTransCommit<C: Curve> {
dlog: C,
elg_dec: C,
#[size_length = 4]
encexp1: Vec<CommittedMessages<C, C>>,
#[size_length = 4]
encexp2: Vec<CommittedMessages<C, C>>,
}
#[derive(Debug, Serialize)]
pub struct EncTransState<C: Curve> {
dlog: C::Scalar,
#[size_length = 4]
encexp1: Vec<(Value<C>, PedersenRandomness<C>)>,
#[size_length = 4]
encexp2: Vec<(Value<C>, PedersenRandomness<C>)>,
}
fn linear_combination_with_powers_of_two<C: Curve>(
scalars: &[C::Scalar],
chunk_size: ChunkSize,
) -> C::Scalar {
let u8_chunk_size = u8::from(chunk_size);
let two_chunksize = C::scalar_from_u64(1 << u8_chunk_size);
let mut power_of_two = C::Scalar::one();
let mut sum = C::Scalar::zero();
for term in scalars.iter() {
let mut term = *term;
term.mul_assign(&power_of_two);
sum.add_assign(&term);
power_of_two.mul_assign(&two_chunksize);
}
sum
}
impl<C: Curve> SigmaProtocol for EncTrans<C> {
type CommitMessage = EncTransCommit<C>;
type ProtocolChallenge = C::Scalar;
type ProverState = EncTransState<C>;
type Response = EncTransResponse<C>;
type SecretData = EncTransSecret<C>;
fn public(&self, ro: &mut RandomOracle) {
self.elg_dec.public(ro);
self.encexp1.iter().for_each(|p| p.public(ro));
self.encexp2.iter().for_each(|p| p.public(ro));
self.dlog.public(ro)
}
fn get_challenge(&self, challenge: &Challenge) -> Self::ProtocolChallenge {
C::scalar_from_bytes(challenge)
}
fn compute_commit_message<R: rand::Rng>(
&self,
csprng: &mut R,
) -> Option<(Self::CommitMessage, Self::ProverState)> {
let mut commit_encexp_1 = Vec::with_capacity(self.encexp1.len());
let mut rands_encexp_1 = Vec::with_capacity(self.encexp1.len());
let mut commit_encexp_2 = Vec::with_capacity(self.encexp2.len());
let mut rands_encexp_2 = Vec::with_capacity(self.encexp2.len());
let mut Rs_a = vec![];
let mut Rs_s_prime = vec![];
for comeq in &self.encexp1 {
match comeq.compute_commit_message(csprng) {
Some((comm_point, (alpha, R_i))) => {
rands_encexp_1.push((alpha, R_i.clone()));
commit_encexp_1.push(comm_point);
Rs_a.push(*R_i);
}
None => return None,
};
}
for comeq in &self.encexp2 {
match comeq.compute_commit_message(csprng) {
Some((comm_point, (alpha, R_s))) => {
rands_encexp_2.push((alpha, R_s.clone()));
commit_encexp_2.push(comm_point);
Rs_s_prime.push(*R_s);
}
None => return None,
};
}
let rand_scalar_common = C::generate_non_zero_scalar(csprng);
let commit_dlog = self.dlog.coeff.mul_by_scalar(&rand_scalar_common);
let rand_lin_a = linear_combination_with_powers_of_two::<C>(&Rs_a, CHUNK_SIZE);
let rand_lin_s_prime = linear_combination_with_powers_of_two::<C>(&Rs_s_prime, CHUNK_SIZE);
let mut rand_lin = rand_lin_a;
rand_lin.add_assign(&rand_lin_s_prime);
let rands = [rand_scalar_common, rand_lin];
let point = multiexp(&self.elg_dec.coeff, &rands);
let commit = EncTransCommit {
dlog: commit_dlog,
elg_dec: point,
encexp1: commit_encexp_1,
encexp2: commit_encexp_2,
};
let rand = EncTransState {
dlog: rand_scalar_common,
encexp1: rands_encexp_1,
encexp2: rands_encexp_2,
};
Some((commit, rand))
}
fn compute_response(
&self,
secret: Self::SecretData,
state: Self::ProverState,
challenge: &Self::ProtocolChallenge,
) -> Option<Self::Response> {
let mut response_common = *challenge;
response_common.mul_assign(&secret.dlog_secret);
response_common.negate();
response_common.add_assign(&state.dlog);
let mut response_encexp1 = Vec::with_capacity(secret.encexp1_secrets.len());
let mut response_encexp2 = Vec::with_capacity(secret.encexp2_secrets.len());
if secret.encexp1_secrets.len() != state.encexp1.len() {
return None;
}
for (sec, encexp1, comeq1) in izip!(
secret.encexp1_secrets,
state.encexp1.iter(),
self.encexp1.iter()
) {
match comeq1.compute_response(sec, (*encexp1).clone(), challenge) {
Some(w) => response_encexp1.push(w),
None => return None,
}
}
if secret.encexp2_secrets.len() != state.encexp2.len() {
return None;
}
for (sec, encexp2, comeq2) in izip!(
secret.encexp2_secrets,
state.encexp2.iter(),
self.encexp2.iter()
) {
match comeq2.compute_response(sec, (*encexp2).clone(), challenge) {
Some(w) => response_encexp2.push(w),
None => return None,
}
}
Some(EncTransResponse {
response_common,
response_encexp1,
response_encexp2,
})
}
fn extract_commit_message(
&self,
challenge: &Self::ProtocolChallenge,
response: &Self::Response,
) -> Option<Self::CommitMessage> {
if self.encexp1.len() != response.response_encexp1.len() {
return None;
}
if self.encexp2.len() != response.response_encexp2.len() {
return None;
}
let mut commit_encexp1 = Vec::with_capacity(self.encexp1.len());
let mut commit_encexp2 = Vec::with_capacity(self.encexp2.len());
let mut w_a_vec = Vec::with_capacity(self.encexp1.len());
let mut w_s_prime_vec = Vec::with_capacity(self.encexp2.len());
for (comeq, response) in izip!(&self.encexp1, &response.response_encexp1) {
match comeq.extract_commit_message(challenge, response) {
Some(m) => {
commit_encexp1.push(m);
w_a_vec.push(response.response.1);
}
None => return None,
}
}
for (comeq, response) in izip!(&self.encexp2, &response.response_encexp2) {
match comeq.extract_commit_message(challenge, response) {
Some(m) => {
commit_encexp2.push(m);
w_s_prime_vec.push(response.response.1);
}
None => return None,
}
}
let w_lin_a = linear_combination_with_powers_of_two::<C>(&w_a_vec, CHUNK_SIZE);
let w_lin_s_prime = linear_combination_with_powers_of_two::<C>(&w_s_prime_vec, CHUNK_SIZE);
let mut w_lin = w_lin_a;
w_lin.add_assign(&w_lin_s_prime);
let dlog_point = self
.dlog
.coeff
.mul_by_scalar(&response.response_common)
.plus_point(&self.dlog.public.mul_by_scalar(challenge));
let mut point = self.elg_dec.public.mul_by_scalar(challenge);
let exps = vec![response.response_common, w_lin];
let product = multiexp(&self.elg_dec.coeff, &exps);
point = point.plus_point(&product);
Some(EncTransCommit {
dlog: dlog_point,
elg_dec: point,
encexp1: commit_encexp1,
encexp2: commit_encexp2,
})
}
#[cfg(test)]
fn with_valid_data<R: rand::Rng>(
_data_size: usize,
_csprng: &mut R,
_f: impl FnOnce(Self, Self::SecretData, &mut R),
) {
unimplemented!("Tested in a different way.")
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
curve_arithmetic::arkworks_instances::ArkGroup,
elgamal::{PublicKey, Randomness, SecretKey},
pedersen_commitment::{Commitment, CommitmentKey},
};
use ark_bls12_381::G1Projective;
use rand::Rng;
type G1 = ArkGroup<G1Projective>;
impl<C: Curve> EncTrans<C> {
fn with_valid_data<R: Rng>(
rng: &mut R,
f: impl FnOnce(Self, <Self as SigmaProtocol>::SecretData, &mut R),
) {
let sk = SecretKey::generate_all(rng);
let pk = PublicKey::from(&sk);
let s = rng.gen::<u64>();
let dlog = Dlog {
public: pk.key,
coeff: sk.generator,
};
let encr_exp_base = C::generate(rng); let S = pk.encrypt_exponent_given_generator(&Value::from(s), &encr_exp_base, rng);
let elgdec = ElgDec {
public: S.1,
coeff: [S.0, encr_exp_base],
};
let a = rng.gen_range(0..s); let s_prime = s - a;
let a_chunks = CHUNK_SIZE.u64_to_chunks(a);
let s_prime_chunks = CHUNK_SIZE.u64_to_chunks(s_prime);
let sk2 = SecretKey::generate(&pk.generator, rng);
let pk2 = PublicKey::from(&sk2);
let a_chunks_as_values: Vec<Value<C>> =
a_chunks.iter().map(|v| Value::from(*v)).collect();
let A_enc_randomness = pk2.encrypt_exponent_vec_given_generator(
a_chunks_as_values.iter(),
&encr_exp_base,
rng,
);
let (A, A_rand): (Vec<_>, Vec<_>) = A_enc_randomness.iter().cloned().unzip();
let s_prime_chunks_as_values: Vec<Value<C>> =
s_prime_chunks.iter().map(|v| Value::from(*v)).collect();
let S_prime_enc_randomness = pk.encrypt_exponent_vec_given_generator(
s_prime_chunks_as_values.iter(),
&encr_exp_base,
rng,
);
let (S_prime, S_prime_rand): (Vec<_>, Vec<_>) =
S_prime_enc_randomness.iter().cloned().unzip();
let a_secrets: Vec<ComEqSecret<C>> = izip!(a_chunks.iter(), A_rand.iter())
.map(|(a_i, r_i)| ComEqSecret::<C> {
r: PedersenRandomness::from_u64(*a_i),
a: Randomness::to_value(r_i),
})
.collect();
let s_prime_secrets: Vec<ComEqSecret<C>> =
izip!(s_prime_chunks.iter(), S_prime_rand.iter())
.map(|(a_i, r_i)| ComEqSecret::<C> {
r: PedersenRandomness::from_u64(*a_i),
a: Randomness::to_value(r_i),
})
.collect();
let mut a_com_eqs = vec![];
for a_chunk in A.iter() {
a_com_eqs.push(ComEq {
commitment: Commitment(a_chunk.1),
y: a_chunk.0,
cmm_key: CommitmentKey {
g: pk2.key,
h: encr_exp_base,
},
g: pk.generator,
});
}
let mut s_prime_com_eqs = vec![];
for s_prime_chunk in S_prime.iter() {
s_prime_com_eqs.push(ComEq {
commitment: Commitment(s_prime_chunk.1),
y: s_prime_chunk.0,
cmm_key: CommitmentKey {
g: pk.key,
h: encr_exp_base,
},
g: pk.generator,
});
}
let secret = EncTransSecret {
dlog_secret: Rc::new(sk.scalar),
encexp1_secrets: a_secrets,
encexp2_secrets: s_prime_secrets,
};
let enc_trans = EncTrans {
dlog,
elg_dec: elgdec,
encexp1: a_com_eqs,
encexp2: s_prime_com_eqs,
};
f(enc_trans, secret, rng)
}
}
fn generate_challenge_prefix<R: rand::Rng>(csprng: &mut R) -> Vec<u8> {
let l = csprng.gen_range(0..1000);
let mut challenge_prefix = vec![0; l];
for v in challenge_prefix.iter_mut() {
*v = csprng.gen();
}
challenge_prefix
}
#[test]
fn enctrans_correctness() {
let mut rng = rand::thread_rng();
for _i in 1..20 {
EncTrans::<G1>::with_valid_data(&mut rng, |enc_trans, secret, rng| {
let challenge_prefix = generate_challenge_prefix(rng);
let mut ro = RandomOracle::domain(challenge_prefix);
let mut ro_copy = ro.split();
let proof =
prove(&mut ro_copy, &enc_trans, secret, rng).expect("Proving should succeed.");
let res = verify(&mut ro, &enc_trans, &proof);
assert!(res, "Verification of produced proof.");
})
}
}
#[test]
fn enctrans_soundness() {
let mut rng = rand::thread_rng();
for _ in 1..20 {
EncTrans::<G1>::with_valid_data(&mut rng, |enc_trans, secret, rng| {
let challenge_prefix = generate_challenge_prefix(rng);
let ro = RandomOracle::domain(&challenge_prefix);
let mut ro_split = ro.split();
let proof =
prove(&mut ro_split, &enc_trans, secret, rng).expect("Proving should succeed.");
let mut wrong_ro = RandomOracle::domain(generate_challenge_prefix(rng));
if verify(&mut wrong_ro, &enc_trans, &proof) {
assert_eq!(wrong_ro, ro);
}
let mut wrong_enc_trans = enc_trans;
{
let tmp = wrong_enc_trans.dlog;
wrong_enc_trans.dlog = Dlog {
public: G1::generate(rng),
coeff: tmp.coeff,
};
let mut ro_split = ro.split();
assert!(!verify(&mut ro_split, &wrong_enc_trans, &proof));
wrong_enc_trans.dlog = Dlog {
public: tmp.public,
coeff: G1::generate(rng),
};
let mut ro_split = ro.split();
assert!(!verify(&mut ro_split, &wrong_enc_trans, &proof));
wrong_enc_trans.dlog = tmp;
}
{
let tmp = wrong_enc_trans.elg_dec;
wrong_enc_trans.elg_dec = ElgDec {
public: G1::generate(rng),
coeff: tmp.coeff,
};
let mut ro_split = ro.split();
assert!(!verify(&mut ro_split, &wrong_enc_trans, &proof));
wrong_enc_trans.elg_dec = ElgDec {
public: tmp.public,
coeff: [G1::generate(rng), tmp.coeff[1]],
};
let mut ro_split = ro.split();
assert!(!verify(&mut ro_split, &wrong_enc_trans, &proof));
wrong_enc_trans.elg_dec = ElgDec {
public: tmp.public,
coeff: [tmp.coeff[1], G1::generate(rng)],
};
let mut ro_split = ro.split();
assert!(!verify(&mut ro_split, &wrong_enc_trans, &proof));
wrong_enc_trans.elg_dec = tmp;
}
for i in 0..wrong_enc_trans.encexp1.len() {
let com_eq = &wrong_enc_trans.encexp1[i];
let tmp = ComEq {
commitment: com_eq.commitment,
y: com_eq.y,
cmm_key: com_eq.cmm_key,
g: com_eq.g,
};
let v = Value::<G1>::generate(rng);
let wrong_commit = com_eq.cmm_key.commit(&v, rng).0;
wrong_enc_trans.encexp1[i] = ComEq {
commitment: wrong_commit,
y: tmp.y,
cmm_key: tmp.cmm_key,
g: tmp.g,
};
let mut ro_split = ro.split();
assert!(!verify(&mut ro_split, &wrong_enc_trans, &proof));
wrong_enc_trans.encexp1[i] = ComEq {
commitment: tmp.commitment,
y: G1::generate(rng),
cmm_key: tmp.cmm_key,
g: tmp.g,
};
let mut ro_split = ro.split();
assert!(!verify(&mut ro_split, &wrong_enc_trans, &proof));
wrong_enc_trans.encexp1[i] = ComEq {
commitment: tmp.commitment,
y: tmp.y,
cmm_key: CommitmentKey::generate(rng),
g: tmp.g,
};
let mut ro_split = ro.split();
assert!(!verify(&mut ro_split, &wrong_enc_trans, &proof));
wrong_enc_trans.encexp1[i] = ComEq {
commitment: tmp.commitment,
y: tmp.y,
cmm_key: tmp.cmm_key,
g: G1::generate(rng),
};
let mut ro_split = ro.split();
assert!(!verify(&mut ro_split, &wrong_enc_trans, &proof));
wrong_enc_trans.encexp1[i] = tmp;
}
for i in 0..wrong_enc_trans.encexp2.len() {
let com_eq = &wrong_enc_trans.encexp2[i];
let tmp = ComEq {
commitment: com_eq.commitment,
y: com_eq.y,
cmm_key: com_eq.cmm_key,
g: com_eq.g,
};
let v = Value::<G1>::generate(rng);
let wrong_commit = com_eq.cmm_key.commit(&v, rng).0;
wrong_enc_trans.encexp2[i] = ComEq {
commitment: wrong_commit,
y: tmp.y,
cmm_key: tmp.cmm_key,
g: tmp.g,
};
let mut ro_split = ro.split();
assert!(!verify(&mut ro_split, &wrong_enc_trans, &proof));
wrong_enc_trans.encexp2[i] = ComEq {
commitment: tmp.commitment,
y: G1::generate(rng),
cmm_key: tmp.cmm_key,
g: tmp.g,
};
let mut ro_split = ro.split();
assert!(!verify(&mut ro_split, &wrong_enc_trans, &proof));
wrong_enc_trans.encexp2[i] = ComEq {
commitment: tmp.commitment,
y: tmp.y,
cmm_key: CommitmentKey::generate(rng),
g: tmp.g,
};
let mut ro_split = ro.split();
assert!(!verify(&mut ro_split, &wrong_enc_trans, &proof));
wrong_enc_trans.encexp2[i] = ComEq {
commitment: tmp.commitment,
y: tmp.y,
cmm_key: tmp.cmm_key,
g: G1::generate(rng),
};
let mut ro_split = ro.split();
assert!(!verify(&mut ro_split, &wrong_enc_trans, &proof));
wrong_enc_trans.encexp2[i] = tmp;
}
})
}
}
}