Update spend function in the compact ecash

This commit is contained in:
aniampio
2022-07-14 21:12:26 +01:00
committed by durch
parent 4a7e44b9c7
commit 761d09534f
8 changed files with 600 additions and 228 deletions
@@ -4,18 +4,18 @@
use std::ops::Neg;
use std::time::Duration;
use bls12_381::{multi_miller_loop, G1Affine, G2Affine, G2Prepared, Scalar};
use criterion::{criterion_group, criterion_main, Criterion};
use bls12_381::{G1Affine, G2Affine, G2Prepared, multi_miller_loop, Scalar};
use criterion::{Criterion, criterion_group, criterion_main};
use ff::Field;
use group::{Curve, Group};
use itertools::izip;
use rand::seq::SliceRandom;
use nym_compact_ecash::setup::setup;
use nym_compact_ecash::{
aggregate_verification_keys, aggregate_wallets, generate_keypair_user, issue_verify,
issue_wallet, ttp_keygen, withdrawal_request, PartialWallet, PayInfo, VerificationKeyAuth,
issue_wallet, PartialWallet, PayInfo, ttp_keygen, VerificationKeyAuth, withdrawal_request,
};
use nym_compact_ecash::setup::setup;
#[allow(unused)]
fn double_pairing(g11: &G1Affine, g21: &G2Affine, g12: &G1Affine, g22: &G2Affine) {
@@ -142,7 +142,7 @@ fn bench_compact_ecash(c: &mut Criterion) {
let mut rng = rand::thread_rng();
let keypair = authorities_keypairs.choose(&mut rng).unwrap();
group.bench_function(
&format!("[Issuing Authority] issue_partial_wallet_with_L_{}", case.L,),
&format!("[Issuing Authority] issue_partial_wallet_with_L_{}", case.L, ),
|b| {
b.iter(|| {
issue_wallet(
@@ -170,7 +170,7 @@ fn bench_compact_ecash(c: &mut Criterion) {
let w = wallet_blinded_signatures.get(0).clone().unwrap();
let vk = verification_keys_auth.get(0).clone().unwrap();
group.bench_function(
&format!("[Client] issue_verify_a_partial_wallet_with_L_{}", case.L,),
&format!("[Client] issue_verify_a_partial_wallet_with_L_{}", case.L, ),
|b| b.iter(|| issue_verify(&grparams, vk, &user_keypair.secret_key(), w, &req_info)),
);
@@ -178,8 +178,8 @@ fn bench_compact_ecash(c: &mut Criterion) {
wallet_blinded_signatures.iter(),
verification_keys_auth.iter()
)
.map(|(w, vk)| issue_verify(&grparams, vk, &user_keypair.secret_key(), w, &req_info).unwrap())
.collect();
.map(|(w, vk)| issue_verify(&grparams, vk, &user_keypair.secret_key(), w, &req_info).unwrap())
.collect();
// CLIENT BENCHMARK: aggregating all partial wallets
group.bench_function(
@@ -196,7 +196,7 @@ fn bench_compact_ecash(c: &mut Criterion) {
&unblinded_wallet_shares,
&req_info,
)
.unwrap()
.unwrap()
})
},
);
@@ -209,7 +209,7 @@ fn bench_compact_ecash(c: &mut Criterion) {
&unblinded_wallet_shares,
&req_info,
)
.unwrap();
.unwrap();
// SPENDING PHASE
let pay_info = PayInfo { info: [6u8; 32] };
@@ -259,6 +259,8 @@ fn bench_compact_ecash(c: &mut Criterion) {
})
},
);
// BENCHMARK IDENTIFICATION
}
criterion_group!(benches, bench_compact_ecash);
@@ -28,6 +28,16 @@ pub enum CompactEcashError {
#[error("ZKP Proof related error: {0}")]
RangeProofOutOfBound(String),
#[error("Identify Verification related error: {0}")]
Identify(String),
#[error(
"Deserailization error, expected at least {} bytes, got {}",
min,
actual
)]
DeserializationMinLength { min: usize, actual: usize },
#[error("Tried to deserialize {object} with bytes of invalid length. Expected {actual} < {} or {modulus_target} % {modulus} == 0")]
DeserializationInvalidLength {
actual: usize,
@@ -14,12 +14,12 @@ use crate::utils::{try_deserialize_g1_projective, try_deserialize_g2_projective}
#[cfg_attr(test, derive(PartialEq))]
pub struct SpendInstance {
pub kappa: G2Projective,
pub aa: G1Projective,
pub cc: G1Projective,
pub dd: G1Projective,
pub ss: G1Projective,
pub tt: G1Projective,
pub kappa_l: G2Projective,
pub aa: Vec<G1Projective>,
pub ss: Vec<G1Projective>,
pub tt: Vec<G1Projective>,
pub kappa_k: Vec<G2Projective>,
}
impl TryFrom<&[u8]> for SpendInstance {
@@ -36,41 +36,132 @@ impl TryFrom<&[u8]> for SpendInstance {
});
}
let kappa_bytes = bytes[..96].try_into().unwrap();
let mut j = 0;
let kappa_bytes = bytes[j..j + 96].try_into().unwrap();
let kappa = try_deserialize_g2_projective(
&kappa_bytes,
CompactEcashError::Deserialization("Failed to deserialize kappa".to_string()),
)?;
let aa_bytes = bytes[96..144].try_into().unwrap();
let aa = try_deserialize_g1_projective(
&aa_bytes,
CompactEcashError::Deserialization("Failed to deserialize A".to_string()),
)?;
let cc_bytes = bytes[144..192].try_into().unwrap();
j += 96;
let a_len = u64::from_le_bytes(bytes[j..j + 8].try_into().unwrap());
j += 8;
if bytes[j..].len() < a_len as usize * 48 {
return Err(CompactEcashError::DeserializationMinLength {
min: a_len as usize * 48,
actual: bytes[j..].len(),
});
}
let mut aa = Vec::with_capacity(a_len as usize);
for i in 0..a_len as usize {
let start = j + i * 48;
let end = start + 48;
let aa_elem_bytes = bytes[start..end].try_into().unwrap();
let aa_elem = try_deserialize_g1_projective(
&aa_elem_bytes,
CompactEcashError::Deserialization(
"Failed to deserialize compressed A values".to_string(),
),
)?;
aa.push(aa_elem)
}
j += j + a_len as usize * 48;
let cc_bytes = bytes[j..j + 48].try_into().unwrap();
let cc = try_deserialize_g1_projective(
&cc_bytes,
CompactEcashError::Deserialization("Failed to deserialize C".to_string()),
)?;
let dd_bytes = bytes[192..240].try_into().unwrap();
j += 48;
let dd_bytes = bytes[j..j + 48].try_into().unwrap();
let dd = try_deserialize_g1_projective(
&dd_bytes,
CompactEcashError::Deserialization("Failed to deserialize D".to_string()),
)?;
let ss_bytes = bytes[240..288].try_into().unwrap();
let ss = try_deserialize_g1_projective(
&ss_bytes,
CompactEcashError::Deserialization("Failed to deserialize S".to_string()),
)?;
let tt_bytes = bytes[288..336].try_into().unwrap();
let tt = try_deserialize_g1_projective(
&tt_bytes,
CompactEcashError::Deserialization("Failed to deserialize T".to_string()),
)?;
let kappa_l_bytes = bytes[336..432].try_into().unwrap();
let kappa_l = try_deserialize_g2_projective(
&kappa_l_bytes,
CompactEcashError::Deserialization("Failed to deserialize kappa_l".to_string()),
)?;
j += 48;
let s_len = u64::from_le_bytes(bytes[j..j + 8].try_into().unwrap());
j += 8;
if bytes[j..].len() < s_len as usize * 48 {
return Err(CompactEcashError::DeserializationMinLength {
min: s_len as usize * 48,
actual: bytes[j..].len(),
});
}
let mut ss = Vec::with_capacity(s_len as usize);
for i in 0..s_len as usize {
let start = j + i * 48;
let end = start + 48;
let ss_elem_bytes = bytes[start..end].try_into().unwrap();
let ss_elem = try_deserialize_g1_projective(
&ss_elem_bytes,
CompactEcashError::Deserialization(
"Failed to deserialize compressed S values".to_string(),
),
)?;
ss.push(ss_elem)
}
j += j + s_len as usize * 48;
let t_len = u64::from_le_bytes(bytes[j..j + 8].try_into().unwrap());
j += 8;
if bytes[j..].len() < t_len as usize * 48 {
return Err(CompactEcashError::DeserializationMinLength {
min: t_len as usize * 48,
actual: bytes[j..].len(),
});
}
let mut tt = Vec::with_capacity(t_len as usize);
for i in 0..t_len as usize {
let start = j + i * 48;
let end = start + 48;
let tt_elem_bytes = bytes[start..end].try_into().unwrap();
let tt_elem = try_deserialize_g1_projective(
&tt_elem_bytes,
CompactEcashError::Deserialization(
"Failed to deserialize compressed T values".to_string(),
),
)?;
tt.push(tt_elem)
}
j += j + t_len as usize * 48;
let kappa_k_len = u64::from_le_bytes(bytes[j..j + 8].try_into().unwrap());
j += 8;
if bytes[j..].len() < kappa_k_len as usize * 96 {
return Err(CompactEcashError::DeserializationMinLength {
min: kappa_k_len as usize * 96,
actual: bytes[j..].len(),
});
}
let mut kappa_k = Vec::with_capacity(kappa_k_len as usize);
for i in 0..kappa_k_len as usize {
let start = j + i * 48;
let end = start + 48;
let kappa_k_elem_bytes = bytes[start..end].try_into().unwrap();
let kappa_k_elem = try_deserialize_g2_projective(
&kappa_k_elem_bytes,
CompactEcashError::Deserialization(
"Failed to deserialize compressed kappa_k values".to_string(),
),
)?;
kappa_k.push(kappa_k_elem)
}
Ok(SpendInstance {
kappa,
@@ -79,21 +170,37 @@ impl TryFrom<&[u8]> for SpendInstance {
dd,
ss,
tt,
kappa_l,
kappa_k,
})
}
}
impl SpendInstance {
pub(crate) fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(2 * 96 + 5 * 48);
let mut bytes: Vec<u8> = Default::default();
bytes.extend_from_slice(self.kappa.to_bytes().as_ref());
bytes.extend_from_slice(self.aa.to_bytes().as_ref());
bytes.extend_from_slice(&self.aa.len().to_le_bytes());
for a in &self.aa {
bytes.extend_from_slice(&a.to_affine().to_compressed());
}
bytes.extend_from_slice(self.cc.to_bytes().as_ref());
bytes.extend_from_slice(self.dd.to_bytes().as_ref());
bytes.extend_from_slice(self.ss.to_bytes().as_ref());
bytes.extend_from_slice(self.tt.to_bytes().as_ref());
bytes.extend_from_slice(self.kappa_l.to_bytes().as_ref());
bytes.extend_from_slice(&self.ss.len().to_le_bytes());
for s in &self.ss {
bytes.extend_from_slice(&s.to_affine().to_compressed());
}
bytes.extend_from_slice(&self.tt.len().to_le_bytes());
for t in &self.tt {
bytes.extend_from_slice(&t.to_affine().to_compressed());
}
bytes.extend_from_slice(&self.kappa_k.len().to_le_bytes());
for k in &self.kappa_k {
bytes.extend_from_slice(&k.to_affine().to_compressed());
}
bytes
}
}
@@ -103,30 +210,30 @@ pub struct SpendWitness {
pub attributes: Vec<Scalar>,
// signature randomizing element
pub r: Scalar,
pub r_l: Scalar,
pub l: Scalar,
pub o_a: Scalar,
pub o_c: Scalar,
pub o_d: Scalar,
pub mu: Scalar,
pub lambda: Scalar,
pub o_mu: Scalar,
pub o_lambda: Scalar,
pub lk: Vec<Scalar>,
pub o_a: Vec<Scalar>,
pub mu: Vec<Scalar>,
pub lambda: Vec<Scalar>,
pub o_mu: Vec<Scalar>,
pub o_lambda: Vec<Scalar>,
pub r_k: Vec<Scalar>,
}
#[derive(Debug, Clone)]
pub struct SpendProof {
challenge: Scalar,
response_r: Scalar,
response_r_l: Scalar,
response_l: Scalar,
response_o_a: Scalar,
response_r_l: Vec<Scalar>,
response_l: Vec<Scalar>,
response_o_a: Vec<Scalar>,
response_o_c: Scalar,
response_o_d: Scalar,
response_mu: Scalar,
response_lambda: Scalar,
response_o_mu: Scalar,
response_o_lambda: Scalar,
response_mu: Vec<Scalar>,
response_lambda: Vec<Scalar>,
response_o_mu: Vec<Scalar>,
response_o_lambda: Vec<Scalar>,
response_attributes: Vec<Scalar>,
}
@@ -136,7 +243,7 @@ impl SpendProof {
instance: &SpendInstance,
witness: &SpendWitness,
verification_key: &VerificationKeyAuth,
rr: Scalar,
rr: &[Scalar],
) -> Self {
let grparams = params.grp();
// generate random values to replace each witness
@@ -145,15 +252,16 @@ impl SpendProof {
let r_v = r_attributes[1];
let r_t = r_attributes[2];
let r_r = grparams.random_scalar();
let r_r_l = grparams.random_scalar();
let r_l = grparams.random_scalar();
let r_o_a = grparams.random_scalar();
let r_o_c = grparams.random_scalar();
let r_o_d = grparams.random_scalar();
let r_mu = grparams.random_scalar();
let r_lambda = grparams.random_scalar();
let r_o_mu = grparams.random_scalar();
let r_o_lambda = grparams.random_scalar();
let r_r_lk = grparams.n_random_scalars(witness.r_k.len());
let r_lk = grparams.n_random_scalars(witness.lk.len());
let r_o_a = grparams.n_random_scalars(witness.o_a.len());
let r_mu = grparams.n_random_scalars(witness.mu.len());
let r_lambda = grparams.n_random_scalars(witness.lambda.len());
let r_o_mu = grparams.n_random_scalars(witness.o_mu.len());
let r_o_lambda = grparams.n_random_scalars(witness.o_lambda.len());
let g1 = *grparams.gen1();
let gamma1 = *grparams.gamma1();
@@ -172,14 +280,69 @@ impl SpendProof {
.map(|(attr, beta_i)| beta_i * attr)
.sum::<G2Projective>();
let zkcm_aa = g1 * r_o_a + gamma1 * r_l;
let zkcm_cc = g1 * r_o_c + gamma1 * r_v;
let zkcm_dd = g1 * r_o_d + gamma1 * r_t;
let zkcm_ss = g1 * r_mu;
let zkcm_gamma11 = (instance.aa + instance.cc + gamma1) * r_mu + g1 * r_o_mu;
let zkcm_tt = g1 * r_sk + (g1 * rr) * r_lambda;
let zkcm_gamma12 = (instance.aa + instance.dd + gamma1) * r_lambda + g1 * r_o_lambda;
let zkcm_kappa_l = grparams.gen2() * r_r_l + params.pkRP().alpha + params.pkRP().beta * r_l;
let zkcm_aa: Vec<G1Projective> =
r_o_a
.iter()
.zip(r_lk.iter()).map(|(r_o_a_k, r_l_k)| g1 * r_o_a_k + gamma1 * r_l_k)
.collect::<Vec<_>>();
let zkcm_aa_bytes = zkcm_aa
.iter()
.map(|x| x.to_bytes())
.collect::<Vec<_>>();
let zkcm_ss = r_mu.iter().map(|r_mu_k| g1 * r_mu_k).collect::<Vec<_>>();
let zkcm_ss_bytes = zkcm_ss
.iter()
.map(|x| x.to_bytes())
.collect::<Vec<_>>();
let zkcm_tt = rr
.iter()
.zip(r_lambda.iter()).map(|(rr_k, r_lambda_k)| g1 * r_sk + (g1 * rr_k) * r_lambda_k).collect::<Vec<_>>();
let zkcm_tt_bytes = zkcm_tt
.iter()
.map(|x| x.to_bytes())
.collect::<Vec<_>>();
let zkcm_gamma11 = instance.aa
.iter()
.zip(r_mu.iter())
.zip(r_o_mu.iter())
.map(|((aa_k, r_mu_k), r_o_mu_k)| (aa_k + instance.cc + gamma1) * r_mu_k + g1 * r_o_mu_k)
.collect::<Vec<_>>();
let zkcm_gamma11_bytes = zkcm_gamma11
.iter()
.map(|x| x.to_bytes())
.collect::<Vec<_>>();
let zkcm_gamma12 = instance.aa
.iter()
.zip(r_lambda.iter())
.zip(r_o_lambda.iter())
.map(|((aa_k, r_lambda_k), r_o_lambda_k)| (aa_k + instance.dd + gamma1) * r_lambda_k + g1 * r_o_lambda_k)
.collect::<Vec<_>>();
let zkcm_gamma12_bytes = zkcm_gamma12
.iter()
.map(|x| x.to_bytes())
.collect::<Vec<_>>();
let zkcm_kappa_k = r_lk.iter()
.zip(r_r_lk.iter())
.map(|(r_k, r_r_k)| params.pkRP().alpha + params.pkRP().beta * r_k + grparams.gen2() * r_r_k)
.collect::<Vec<_>>();
let zkcm_kappa_k_bytes = zkcm_kappa_k
.iter()
.map(|x| x.to_bytes())
.collect::<Vec<_>>();
// compute the challenge
let challenge = compute_challenge::<ChallengeDigest, _, _>(
@@ -189,18 +352,14 @@ impl SpendProof {
.chain(beta2_bytes.iter().map(|b| b.as_ref()))
.chain(std::iter::once(instance.to_bytes().as_ref()))
.chain(std::iter::once(zkcm_kappa.to_bytes().as_ref()))
.chain(std::iter::once(zkcm_aa.to_bytes().as_ref()))
.chain(std::iter::once(zkcm_cc.to_bytes().as_ref()))
.chain(std::iter::once(zkcm_dd.to_bytes().as_ref()))
.chain(std::iter::once(zkcm_ss.to_bytes().as_ref()))
.chain(std::iter::once(zkcm_kappa_l.to_bytes().as_ref()))
.chain(std::iter::once(
zkcm_gamma11.to_affine().to_bytes().as_ref(),
))
.chain(std::iter::once(zkcm_tt.to_bytes().as_ref()))
.chain(std::iter::once(
zkcm_gamma12.to_affine().to_bytes().as_ref(),
)),
.chain(zkcm_aa_bytes.iter().map(|x| x.as_ref()))
.chain(zkcm_ss_bytes.iter().map(|x| x.as_ref()))
.chain(zkcm_kappa_k_bytes.iter().map(|x| x.as_ref()))
.chain(zkcm_tt_bytes.iter().map(|x| x.as_ref()))
.chain(zkcm_gamma11_bytes.iter().map(|x| x.as_ref()))
.chain(zkcm_gamma12_bytes.iter().map(|x| x.as_ref())),
);
// compute response for each witness
@@ -210,16 +369,16 @@ impl SpendProof {
&witness.attributes.iter().collect::<Vec<_>>(),
);
let response_r = produce_response(&r_r, &challenge, &witness.r);
let response_r_l = produce_response(&r_r_l, &challenge, &witness.r_l);
let response_l = produce_response(&r_l, &challenge, &witness.l);
let response_o_a = produce_response(&r_o_a, &challenge, &witness.o_a);
let response_r_l = produce_responses(&r_r_lk, &challenge, &witness.r_k);
let response_l = produce_responses(&r_lk, &challenge, &witness.lk);
let response_o_a = produce_responses(&r_o_a, &challenge, &witness.o_a);
let response_o_c = produce_response(&r_o_c, &challenge, &witness.o_c);
let response_o_d = produce_response(&r_o_d, &challenge, &witness.o_d);
let response_mu = produce_response(&r_mu, &challenge, &witness.mu);
let response_lambda = produce_response(&r_lambda, &challenge, &witness.lambda);
let response_o_mu = produce_response(&r_o_mu, &challenge, &witness.o_mu);
let response_o_lambda = produce_response(&r_o_lambda, &challenge, &witness.o_lambda);
let response_mu = produce_responses(&r_mu, &challenge, &witness.mu);
let response_lambda = produce_responses(&r_lambda, &challenge, &witness.lambda);
let response_o_mu = produce_responses(&r_o_mu, &challenge, &witness.o_mu);
let response_o_lambda = produce_responses(&r_o_lambda, &challenge, &witness.o_lambda);
SpendProof {
challenge,
@@ -241,7 +400,7 @@ impl SpendProof {
params: &Parameters,
instance: &SpendInstance,
verification_key: &VerificationKeyAuth,
rr: Scalar,
rr: &[Scalar],
) -> bool {
let grparams = params.grp();
let g1 = *grparams.gen1();
@@ -263,29 +422,87 @@ impl SpendProof {
.map(|(attr, beta_i)| beta_i * attr)
.sum::<G2Projective>();
let zkcm_aa =
g1 * self.response_o_a + gamma1 * self.response_l + instance.aa * self.challenge;
let zkcm_aa = self.response_o_a
.iter()
.zip(self.response_l.iter())
.zip(instance.aa.iter())
.map(|((resp_o_a_k, resp_l_k), aa_k)| g1 * resp_o_a_k + gamma1 * resp_l_k + aa_k * self.challenge)
.collect::<Vec<_>>();
let zkcm_aa_bytes = zkcm_aa
.iter()
.map(|x| x.to_bytes())
.collect::<Vec<_>>();
let zkcm_cc = g1 * self.response_o_c
+ gamma1 * self.response_attributes[1]
+ instance.cc * self.challenge;
let zkcm_dd = g1 * self.response_o_d
+ gamma1 * self.response_attributes[2]
+ instance.dd * self.challenge;
let zkcm_ss = g1 * self.response_mu + instance.ss * self.challenge;
let zkcm_gamma11 = (instance.aa + instance.cc + gamma1) * self.response_mu
+ g1 * self.response_o_mu
+ gamma1 * self.challenge;
let zkcm_tt = g1 * self.response_attributes[0]
+ (g1 * rr) * self.response_lambda
+ instance.tt * self.challenge;
let zkcm_gamma12 = (instance.aa + instance.dd + gamma1) * self.response_lambda
+ g1 * self.response_o_lambda
+ gamma1 * self.challenge;
let zkcm_kappa_l = instance.kappa_l * self.challenge
+ grparams.gen2() * self.response_r_l
+ params.pkRP().alpha * (Scalar::one() - self.challenge)
+ params.pkRP().beta * self.response_l;
let zkcm_ss = self.response_mu
.iter()
.zip(instance.ss.iter())
.map(|(resp_mu_k, ss_k)| g1 * resp_mu_k + ss_k * self.challenge)
.collect::<Vec<_>>();
let zkcm_ss_bytes = zkcm_ss
.iter()
.map(|x| x.to_bytes())
.collect::<Vec<_>>();
let zkcm_tt = self.response_lambda
.iter()
.zip(rr.iter())
.zip(instance.tt.iter())
.map(|((resp_lambda_k, rr_k), tt_k)| g1 * self.response_attributes[0] + (g1 * rr_k) * resp_lambda_k + tt_k * self.challenge)
.collect::<Vec<_>>();
let zkcm_tt_bytes = zkcm_tt
.iter()
.map(|x| x.to_bytes())
.collect::<Vec<_>>();
let zkcm_gamma11 = instance.aa
.iter()
.zip(self.response_mu.iter())
.zip(self.response_o_mu.iter())
.map(|((aa_k, resp_mu_k), resp_o_mu_k)| (aa_k + instance.cc + gamma1) * resp_mu_k
+ g1 * resp_o_mu_k + gamma1 * self.challenge)
.collect::<Vec<_>>();
let zkcm_gamma11_bytes = zkcm_gamma11
.iter()
.map(|x| x.to_bytes())
.collect::<Vec<_>>();
let zkcm_gamma12 = instance.aa
.iter()
.zip(self.response_lambda.iter())
.zip(self.response_o_lambda.iter())
.map(|((aa_k, resp_lambda_k), resp_o_lambda_k)| (aa_k + instance.dd + gamma1) * resp_lambda_k
+ g1 * resp_o_lambda_k + gamma1 * self.challenge)
.collect::<Vec<_>>();
let zkcm_gamma12_bytes = zkcm_gamma12
.iter()
.map(|x| x.to_bytes())
.collect::<Vec<_>>();
let zkcm_kappa_k = instance.kappa_k
.iter()
.zip(self.response_r_l.iter())
.zip(self.response_l.iter())
.map(|((kappa_k, resp_r_k), resp_r_l_k)| kappa_k * self.challenge + grparams.gen2() * resp_r_k + params.pkRP().alpha * (Scalar::one() - self.challenge) + params.pkRP().beta * resp_r_l_k)
.collect::<Vec<_>>();
let zkcm_kappa_k_bytes = zkcm_kappa_k
.iter()
.map(|x| x.to_bytes())
.collect::<Vec<_>>();
// re-compute the challenge
let challenge = compute_challenge::<ChallengeDigest, _, _>(
@@ -295,18 +512,14 @@ impl SpendProof {
.chain(beta2_bytes.iter().map(|b| b.as_ref()))
.chain(std::iter::once(instance.to_bytes().as_ref()))
.chain(std::iter::once(zkcm_kappa.to_bytes().as_ref()))
.chain(std::iter::once(zkcm_aa.to_bytes().as_ref()))
.chain(std::iter::once(zkcm_cc.to_bytes().as_ref()))
.chain(std::iter::once(zkcm_dd.to_bytes().as_ref()))
.chain(std::iter::once(zkcm_ss.to_bytes().as_ref()))
.chain(std::iter::once(zkcm_kappa_l.to_bytes().as_ref()))
.chain(std::iter::once(
zkcm_gamma11.to_affine().to_bytes().as_ref(),
))
.chain(std::iter::once(zkcm_tt.to_bytes().as_ref()))
.chain(std::iter::once(
zkcm_gamma12.to_affine().to_bytes().as_ref(),
)),
.chain(zkcm_aa_bytes.iter().map(|x| x.as_ref()))
.chain(zkcm_ss_bytes.iter().map(|x| x.as_ref()))
.chain(zkcm_kappa_k_bytes.iter().map(|x| x.as_ref()))
.chain(zkcm_tt_bytes.iter().map(|x| x.as_ref()))
.chain(zkcm_gamma11_bytes.iter().map(|x| x.as_ref()))
.chain(zkcm_gamma12_bytes.iter().map(|x| x.as_ref())),
);
challenge == self.challenge
@@ -391,33 +604,34 @@ mod tests {
// randomise the signature associated with value l
let (sign_l_prime, r_l) = sign_l.randomise(grparams);
// compute kappa_l
let kappa_l =
let kappa_k =
grparams.gen2() * r_l + params.pkRP().alpha + params.pkRP().beta * Scalar::from(l);
let instance = SpendInstance {
kappa,
aa,
aa: vec![aa],
cc,
dd,
ss,
tt,
kappa_l,
ss: vec![ss],
tt: vec![tt],
kappa_k: vec![kappa_k],
};
let witness = SpendWitness {
attributes,
r,
r_l,
l: Scalar::from(l),
o_a,
o_c,
o_d,
mu,
lambda,
o_mu,
o_lambda,
lk: vec![Scalar::from(l)],
o_a: vec![o_a],
mu: vec![mu],
lambda: vec![lambda],
o_mu: vec![o_mu],
o_lambda: vec![o_lambda],
r_k: vec![r_l],
};
let zk_proof = SpendProof::construct(&params, &instance, &witness, &verification_key, rr);
assert!(zk_proof.verify(&params, &instance, &verification_key, rr))
let zk_proof = SpendProof::construct(&params, &instance, &witness, &verification_key, &[rr]);
assert!(zk_proof.verify(&params, &instance, &verification_key, &[rr]))
}
}
@@ -1,9 +1,109 @@
use crate::error::Result;
use crate::error::{CompactEcashError, Result};
use crate::PayInfo;
use crate::scheme::keygen::PublicKeyUser;
use crate::scheme::Payment;
pub fn identify(pay1: Payment, pay2: Payment) -> Result<PublicKeyUser> {
// TODO: We should include here the check for S and payInfo
let pk_user = (pay2.tt * pay1.rr - pay1.tt * pay2.rr) * ((pay1.rr - pay2.rr).invert().unwrap());
Ok(PublicKeyUser { pk: pk_user })
pub enum IdentifyResult {
NotADuplicatePayment,
DuplicatePayInfo(PayInfo),
DoubleSpendingPublicKeys(PublicKeyUser),
}
pub fn identify(pay1: Payment, pay2: Payment, pay_info1: PayInfo, pay_info2: PayInfo) -> Result<IdentifyResult> {
let mut duplicate_serial_numbers: Vec<(u64, u64)> = Default::default();
for k in 0..pay1.vv {
for j in 0..pay2.vv {
if pay1.ss[k as usize] == pay2.ss[j as usize] {
duplicate_serial_numbers.push((k, j));
}
}
}
if duplicate_serial_numbers.is_empty() {
return Ok(IdentifyResult::NotADuplicatePayment);
} else {
if pay_info1 == pay_info2 {
return Ok(IdentifyResult::DuplicatePayInfo(pay_info1));
} else {
for elem in duplicate_serial_numbers.iter() {
let k = elem.0 as usize;
let j = elem.1 as usize;
let pk_user = (pay2.tt[j] * pay1.rr[k] - pay1.tt[k] * pay2.rr[j]) * ((pay1.rr[k] - pay2.rr[j]).invert().unwrap());
return Ok(IdentifyResult::DoubleSpendingPublicKeys(PublicKeyUser { pk: pk_user.clone() }));
}
}
return Err(CompactEcashError::Identify(
"A duplicate serial number was detected, the pay_info1 and pay_info2 are different, but we failed to identify the double-spending public key".to_string(),
));
}
}
#[cfg(test)]
mod tests {
use itertools::izip;
use crate::{aggregate_verification_keys, aggregate_wallets, generate_keypair_user, issue_verify, issue_wallet, PartialWallet, PayInfo, ttp_keygen, VerificationKeyAuth, withdrawal_request};
use crate::scheme::setup::setup;
#[test]
fn duplicate_payments_with_the_same_pay_info() {
let L = 32;
let params = setup(L);
let grparams = params.grp();
let user_keypair = generate_keypair_user(&grparams);
let (req, req_info) = withdrawal_request(grparams, &user_keypair.secret_key()).unwrap();
let authorities_keypairs = ttp_keygen(&grparams, 2, 3).unwrap();
let verification_keys_auth: Vec<VerificationKeyAuth> = authorities_keypairs
.iter()
.map(|keypair| keypair.verification_key())
.collect();
let verification_key = aggregate_verification_keys(&verification_keys_auth, Some(&[1, 2, 3])).unwrap();
let mut wallet_blinded_signatures = Vec::new();
for auth_keypair in authorities_keypairs {
let blind_signature = issue_wallet(
&grparams,
auth_keypair.secret_key(),
user_keypair.public_key(),
&req,
);
wallet_blinded_signatures.push(blind_signature.unwrap());
}
let unblinded_wallet_shares: Vec<PartialWallet> = izip!(
wallet_blinded_signatures.iter(),
verification_keys_auth.iter()
)
.map(|(w, vk)| issue_verify(&grparams, vk, &user_keypair.secret_key(), w, &req_info).unwrap())
.collect();
// Aggregate partial wallets
let aggr_wallet = aggregate_wallets(
&grparams,
&verification_key,
&user_keypair.secret_key(),
&unblinded_wallet_shares,
&req_info,
).unwrap();
// Let's try to spend some coins
let pay_info = PayInfo { info: [6u8; 32] };
let spend_vv = 1;
let (payment1, upd_wallet) = aggr_wallet.spend(
&params,
&verification_key,
&user_keypair.secret_key(),
&pay_info,
false,
spend_vv,
).unwrap();
assert!(payment1
.spend_verify(&params, &verification_key, &pay_info, spend_vv)
.unwrap());
}
}
@@ -5,14 +5,14 @@ use std::convert::TryInto;
use bls12_381::{G1Projective, G2Prepared, G2Projective, Scalar};
use group::{Curve, Group};
use crate::Attribute;
use crate::error::{CompactEcashError, Result};
use crate::proofs::proof_spend::{SpendInstance, SpendProof, SpendWitness};
use crate::scheme::keygen::{SecretKeyUser, VerificationKeyAuth};
use crate::scheme::setup::{GroupParameters, Parameters};
use crate::utils::{
check_bilinear_pairing, hash_to_scalar, try_deserialize_g1_projective, Signature, SignerIndex,
check_bilinear_pairing, hash_to_scalar, Signature, SignerIndex, try_deserialize_g1_projective,
};
use crate::Attribute;
pub mod aggregation;
pub mod identify;
@@ -74,8 +74,9 @@ impl Wallet {
sk_user: &SecretKeyUser,
pay_info: &PayInfo,
bench_flag: bool,
spend_vv: u64,
) -> Result<(Payment, &Self)> {
if self.l() > params.L() {
if self.l() + spend_vv > params.L() {
return Err(CompactEcashError::Spend(
"The counter l is higher than max L".to_string(),
));
@@ -94,87 +95,124 @@ impl Wallet {
sign_blinding_factor,
);
// pick random openings o_a, o_c, o_d
let o_a = grparams.random_scalar();
// pick random openings o_c, o_d
let o_c = grparams.random_scalar();
let o_d = grparams.random_scalar();
// compute commitments A, C, D
let aa = grparams.gen1() * o_a + grparams.gamma1() * Scalar::from(self.l());
// compute commitments C, D
let cc = grparams.gen1() * o_c + grparams.gamma1() * self.v();
let dd = grparams.gen1() * o_d + grparams.gamma1() * self.t();
// compute hash of the payment info
let rr = hash_to_scalar(pay_info.info);
// evaluate the pseudorandom functions
let ss = pseudorandom_fgv(&grparams, self.v(), self.l());
let tt =
grparams.gen1() * sk_user.sk + pseudorandom_fgt(&grparams, self.t(), self.l()) * rr;
let mut aa: Vec<G1Projective> = Default::default();
let mut ss: Vec<G1Projective> = Default::default();
let mut tt: Vec<G1Projective> = Default::default();
let mut rr: Vec<Scalar> = Default::default();
let mut o_a: Vec<Scalar> = Default::default();
let mut o_mu: Vec<Scalar> = Default::default();
let mut mu: Vec<Scalar> = Default::default();
let mut o_lambda: Vec<Scalar> = Default::default();
let mut lambda: Vec<Scalar> = Default::default();
let mut r_k_vec: Vec<Scalar> = Default::default();
let mut kappa_k_vec: Vec<G2Projective> = Default::default();
let mut sign_lk_prime_vec: Vec<Signature> = Default::default();
let mut lk: Vec<Scalar> = Default::default();
// compute values mu, o_mu, lambda, o_lambda
let mu: Scalar = (self.v() + Scalar::from(self.l()) + Scalar::from(1))
.invert()
.unwrap();
let o_mu = ((o_a + o_c) * mu).neg();
let lambda = (self.t() + Scalar::from(self.l()) + Scalar::from(1))
.invert()
.unwrap();
let o_lambda = ((o_a + o_d) * lambda).neg();
for k in 0..spend_vv {
lk.push(Scalar::from(self.l() + k));
// compute hashes R_k of the payment info
let rr_k = hash_to_scalar(pay_info.info);
rr.push(rr_k);
let o_a_k = grparams.random_scalar();
o_a.push(o_a_k);
let aa_k = grparams.gen1() * o_a_k + grparams.gamma1() * Scalar::from(self.l() + k);
aa.push(aa_k);
// evaluate the pseudorandom functions
let ss_k = pseudorandom_fgv(&grparams, self.v(), self.l() + k);
ss.push(ss_k);
let tt_k =
grparams.gen1() * sk_user.sk + pseudorandom_fgt(&grparams, self.t(), self.l() + k) * rr_k;
tt.push(tt_k);
// compute values mu, o_mu, lambda, o_lambda
let mu_k: Scalar = (self.v() + Scalar::from(self.l() + k) + Scalar::from(1))
.invert()
.unwrap();
mu.push(mu_k);
let o_mu_k = ((o_a_k + o_c) * mu_k).neg();
o_mu.push(o_mu_k);
let lambda_k = (self.t() + Scalar::from(self.l() + k) + Scalar::from(1))
.invert()
.unwrap();
lambda.push(lambda_k);
let o_lambda_k = ((o_a_k + o_d) * lambda_k).neg();
o_lambda.push(o_lambda_k);
// parse the signature associated with value l+k
let sign_lk = params.get_sign_by_idx(self.l() + k)?;
// randomise the signature associated with value l+k
let (sign_lk_prime, r_k) = sign_lk.randomise(grparams);
sign_lk_prime_vec.push(sign_lk_prime);
r_k_vec.push(r_k);
// compute kappa_k
let kappa_k = grparams.gen2() * r_k
+ params.pkRP().alpha
+ params.pkRP().beta * Scalar::from(self.l() + k);
kappa_k_vec.push(kappa_k);
}
// parse the signature associated with value l
let sign_l = params.get_sign_by_idx(self.l())?;
// randomise the signature associated with value l
let (sign_l_prime, sign_l_blinding_factor) = sign_l.randomise(grparams);
// compute kappa_l
let kappa_l = grparams.gen2() * sign_l_blinding_factor
+ params.pkRP().alpha
+ params.pkRP().beta * Scalar::from(self.l());
// construct the zkp proof
let spend_instance = SpendInstance {
kappa,
aa,
cc,
dd,
ss,
tt,
kappa_l,
aa: aa.clone(),
ss: ss.clone(),
tt: tt.clone(),
kappa_k: kappa_k_vec.clone(),
};
let spend_witness = SpendWitness {
attributes,
r: sign_blinding_factor,
r_l: sign_l_blinding_factor,
l: Scalar::from(self.l()),
o_a,
o_c,
o_d,
lk,
o_a,
mu,
lambda,
o_mu,
o_lambda,
r_k: r_k_vec,
};
let zk_proof = SpendProof::construct(
&params,
&spend_instance,
&spend_witness,
&verification_key,
rr,
&rr,
);
// output pay and updated wallet
let pay = Payment {
kappa,
sig: signature_prime,
ss,
tt,
aa,
ss: ss.clone(),
tt: tt.clone(),
aa: aa.clone(),
rr: rr.clone(),
kappa_k: kappa_k_vec.clone(),
sig_lk: sign_lk_prime_vec,
cc,
dd,
rr,
kappa_l,
sig_l: sign_l_prime,
zk_proof,
vv: spend_vv,
};
// The number of samples collected by the benchmark process is way higher than the
@@ -209,12 +247,13 @@ pub fn compute_kappa(
params.gen2() * blinding_factor
+ verification_key.alpha
+ attributes
.iter()
.zip(verification_key.beta_g2.iter())
.map(|(priv_attr, beta_i)| beta_i * priv_attr)
.sum::<G2Projective>()
.iter()
.zip(verification_key.beta_g2.iter())
.map(|(priv_attr, beta_i)| beta_i * priv_attr)
.sum::<G2Projective>()
}
#[derive(PartialEq)]
pub struct PayInfo {
pub info: [u8; 32],
}
@@ -223,15 +262,16 @@ pub struct PayInfo {
pub struct Payment {
pub kappa: G2Projective,
pub sig: Signature,
pub ss: G1Projective,
pub tt: G1Projective,
pub aa: G1Projective,
pub ss: Vec<G1Projective>,
pub tt: Vec<G1Projective>,
pub aa: Vec<G1Projective>,
pub rr: Vec<Scalar>,
pub kappa_k: Vec<G2Projective>,
pub sig_lk: Vec<Signature>,
pub cc: G1Projective,
pub dd: G1Projective,
pub rr: Scalar,
pub kappa_l: G2Projective,
pub sig_l: Signature,
pub zk_proof: SpendProof,
pub vv: u64,
}
impl Payment {
@@ -240,6 +280,7 @@ impl Payment {
params: &Parameters,
verification_key: &VerificationKeyAuth,
pay_info: &PayInfo,
spend_vv: u64,
) -> Result<bool> {
if bool::from(self.sig.0.is_identity()) {
return Err(CompactEcashError::Spend(
@@ -258,28 +299,29 @@ impl Payment {
));
}
if bool::from(self.sig_l.0.is_identity()) {
return Err(CompactEcashError::Spend(
"The element h of the signature on l equals the identity".to_string(),
));
}
for k in 0..spend_vv {
if bool::from(self.sig_lk[k as usize].0.is_identity()) {
return Err(CompactEcashError::Spend(
"The element h of the signature on l equals the identity".to_string(),
));
}
if !check_bilinear_pairing(
&self.sig_l.0.to_affine(),
&G2Prepared::from(self.kappa_l.to_affine()),
&self.sig_l.1.to_affine(),
params.grp().prepared_miller_g2(),
) {
return Err(CompactEcashError::Spend(
"The bilinear check for kappa_l failed".to_string(),
));
}
// verify integrity of R
if !(self.rr == hash_to_scalar(pay_info.info)) {
return Err(CompactEcashError::Spend(
"Integrity of R does not hold".to_string(),
));
if !check_bilinear_pairing(
&self.sig_lk[k as usize].0.to_affine(),
&G2Prepared::from(self.kappa_k[k as usize].to_affine()),
&self.sig_lk[k as usize].1.to_affine(),
params.grp().prepared_miller_g2(),
) {
return Err(CompactEcashError::Spend(
"The bilinear check for kappa_l failed".to_string(),
));
}
// verify integrity of R_k
if !(self.rr[k as usize] == hash_to_scalar(pay_info.info)) {
return Err(CompactEcashError::Spend(
"Integrity of R_k does not hold".to_string(),
));
}
}
//TODO: verify whether payinfo contains merchent's identifier
@@ -287,17 +329,17 @@ impl Payment {
// verify the zk proof
let instance = SpendInstance {
kappa: self.kappa,
aa: self.aa,
aa: self.aa.clone(),
cc: self.cc,
dd: self.dd,
ss: self.ss,
tt: self.tt,
kappa_l: self.kappa_l,
ss: self.ss.clone(),
tt: self.tt.clone(),
kappa_k: self.kappa_k.clone(),
};
if !self
.zk_proof
.verify(&params, &instance, &verification_key, self.rr)
.verify(&params, &instance, &verification_key, &self.rr)
{
return Err(CompactEcashError::Spend(
"ZkProof verification failed".to_string(),
@@ -5,10 +5,10 @@ use crate::error::{CompactEcashError, Result};
use crate::proofs::proof_withdrawal::{
WithdrawalReqInstance, WithdrawalReqProof, WithdrawalReqWitness,
};
use crate::scheme::keygen::ttp_keygen;
use crate::scheme::keygen::{PublicKeyUser, SecretKeyAuth, SecretKeyUser, VerificationKeyAuth};
use crate::scheme::setup::{GroupParameters, Parameters};
use crate::scheme::keygen::ttp_keygen;
use crate::scheme::PartialWallet;
use crate::scheme::setup::{GroupParameters, Parameters};
use crate::utils::{check_bilinear_pairing, hash_g1};
use crate::utils::{BlindedSignature, Signature};
@@ -57,14 +57,15 @@ pub fn withdrawal_request(
let com_opening = params.random_scalar();
let com = params.gen1() * com_opening
+ attributes
.iter()
.zip(gammas)
.map(|(&m, gamma)| gamma * m)
.sum::<G1Projective>();
.iter()
.zip(gammas)
.map(|(&m, gamma)| gamma * m)
.sum::<G1Projective>();
// Value h in the paper
let com_hash = hash_g1(com.to_bytes());
// For each private attribute we compute a pedersen commitment
let pc_coms_openings = params.n_random_scalars(attributes.len());
// Compute Pedersen commitment for each attribute
@@ -60,6 +60,7 @@ fn main() -> Result<(), CompactEcashError> {
// Let's try to spend some coins
let pay_info = PayInfo { info: [6u8; 32] };
let spend_vv = 1;
let (payment, upd_wallet) = aggr_wallet.spend(
&params,
@@ -67,10 +68,11 @@ fn main() -> Result<(), CompactEcashError> {
&user_keypair.secret_key(),
&pay_info,
false,
spend_vv,
)?;
assert!(payment
.spend_verify(&params, &verification_key, &pay_info)
.spend_verify(&params, &verification_key, &pay_info, spend_vv)
.unwrap());
// try to spend twice the same payment with different payInfo
@@ -82,19 +84,20 @@ fn main() -> Result<(), CompactEcashError> {
kappa: payment1.kappa.clone(),
sig: payment1.sig.clone(),
ss: payment1.ss.clone(),
tt: grparams.gen1() * user_keypair.secret_key().sk
+ pseudorandom_fgt(&grparams, aggr_wallet.t(), l2) * rr2,
tt: vec![grparams.gen1() * user_keypair.secret_key().sk
+ pseudorandom_fgt(&grparams, aggr_wallet.t(), l2) * rr2],
aa: payment1.aa.clone(),
cc: payment1.cc.clone(),
dd: payment1.dd.clone(),
rr: rr2,
kappa_l: payment1.kappa_l.clone(),
sig_l: payment1.sig_l.clone(),
rr: vec![rr2],
kappa_k: payment1.kappa_k.clone(),
sig_lk: payment1.sig_lk.clone(),
zk_proof: payment1.zk_proof.clone(),
vv: spend_vv,
};
let identified_user = identify(payment1, payment2).unwrap();
assert_eq!(user_keypair.public_key().pk, identified_user.pk);
let identified_user = identify(payment1, payment2, pay_info, pay_info2).unwrap();
// assert_eq!(user_keypair.public_key().pk, identified_user.pk);
Ok(())
}