From 68485f8998e8465fa9f19c15ffaa855f6caa7550 Mon Sep 17 00:00:00 2001 From: aniampio Date: Tue, 10 May 2022 13:21:53 +0100 Subject: [PATCH] Add spend and spend verification functions; fix breaking test for proof of withdrawal --- .../src/proofs/mod.rs | 1 + .../src/proofs/proof_spend.rs | 32 ++++++- .../src/proofs/proof_withdrawal.rs | 10 +- .../src/scheme/mod.rs | 93 ++++++++++++++++--- .../src/scheme/setup.rs | 57 +++++++----- .../scheme/structure_preserving_signature.rs | 26 +++--- .../src/scheme/withdrawal.rs | 12 +-- 7 files changed, 169 insertions(+), 62 deletions(-) diff --git a/common/nym_offline_divisible_ecash/src/proofs/mod.rs b/common/nym_offline_divisible_ecash/src/proofs/mod.rs index b54452411e..224ea3f377 100644 --- a/common/nym_offline_divisible_ecash/src/proofs/mod.rs +++ b/common/nym_offline_divisible_ecash/src/proofs/mod.rs @@ -14,6 +14,7 @@ use crate::scheme::setup::GroupParameters; use crate::utils::try_deserialize_g1_projective; pub mod proof_withdrawal; +pub mod proof_spend; type ChallengeDigest = Sha256; diff --git a/common/nym_offline_divisible_ecash/src/proofs/proof_spend.rs b/common/nym_offline_divisible_ecash/src/proofs/proof_spend.rs index 68d27595d4..5c70e8b922 100644 --- a/common/nym_offline_divisible_ecash/src/proofs/proof_spend.rs +++ b/common/nym_offline_divisible_ecash/src/proofs/proof_spend.rs @@ -1,4 +1,32 @@ -pub struct SpendInstance {} +use bls12_381::{G1Projective, G2Projective, Scalar}; + +use crate::scheme::{Phi, VarPhi}; +use crate::scheme::keygen::{SecretKeyUser, VerificationKeyAuth}; +use crate::scheme::setup::Parameters; + +pub struct SpendInstance { + pub kappa: G2Projective, + pub phi: Phi, + pub var_phi: VarPhi, + pub rr: G1Projective, + pub ss: G1Projective, + pub tt: G1Projective, +} + +pub struct SpendWitness { + sk_u: SecretKeyUser, + v: Scalar, + r: Scalar, + r1: Scalar, + r2: Scalar, +} + +#[derive(Debug, Clone)] +pub struct SpendProof {} + +impl SpendProof { + pub fn construct() {} +} + -pub struct SpendWitness {} diff --git a/common/nym_offline_divisible_ecash/src/proofs/proof_withdrawal.rs b/common/nym_offline_divisible_ecash/src/proofs/proof_withdrawal.rs index a7ebd52dae..0a9c2d4d95 100644 --- a/common/nym_offline_divisible_ecash/src/proofs/proof_withdrawal.rs +++ b/common/nym_offline_divisible_ecash/src/proofs/proof_withdrawal.rs @@ -130,7 +130,7 @@ impl WithdrawalReqProof { ) -> Self { let grp = params.get_grp(); let g1 = grp.gen1(); - let paramsU = params.get_paramsUser(); + let params_u = params.get_params_u(); // generate random values to replace the witnesses let r_com_opening = grp.random_scalar(); @@ -141,7 +141,7 @@ impl WithdrawalReqProof { let zkcm_com = g1 * r_com_opening + r_attributes .iter() - .zip(paramsU.get_gammas().iter()) + .zip(params_u.get_gammas().iter()) .map(|(rm_i, gamma_i)| gamma_i * rm_i) .sum::(); @@ -154,7 +154,7 @@ impl WithdrawalReqProof { let zkcm_user_sk = g1 * r_attributes[0]; // covert to bytes - let gammas_bytes = paramsU + let gammas_bytes = params_u .get_gammas() .iter() .map(|gamma| gamma.to_bytes()) @@ -203,7 +203,7 @@ impl WithdrawalReqProof { ) -> bool { let grp = params.get_grp(); let g1 = grp.gen1(); - let paramsU = params.get_paramsUser(); + let paramsU = params.get_params_u(); // recompute zk commitments for each instance let zkcm_com = instance.com * self.challenge @@ -305,7 +305,7 @@ mod tests { let com = grp.gen1() * com_opening + attr .iter() - .zip(params.get_paramsUser().get_gammas()) + .zip(params.get_params_u().get_gammas()) .map(|(&m, gamma)| gamma * m) .sum::(); let h = hash_g1(com.to_bytes()); diff --git a/common/nym_offline_divisible_ecash/src/scheme/mod.rs b/common/nym_offline_divisible_ecash/src/scheme/mod.rs index 5af94f915f..5a2c8bf665 100644 --- a/common/nym_offline_divisible_ecash/src/scheme/mod.rs +++ b/common/nym_offline_divisible_ecash/src/scheme/mod.rs @@ -1,13 +1,15 @@ use std::cell::Cell; -use bls12_381::{G2Projective, Scalar}; +use bls12_381::{G1Projective, G2Prepared, G2Projective, Scalar}; +use group::Curve; use crate::Attribute; use crate::constants::L; use crate::error::{DivisibleEcashError, Result}; +use crate::proofs::proof_spend::SpendProof; use crate::scheme::keygen::{SecretKeyUser, VerificationKeyAuth}; use crate::scheme::setup::{GroupParameters, Parameters}; -use crate::utils::{hash_to_scalar, Signature, SignerIndex}; +use crate::utils::{check_bilinear_pairing, hash_to_scalar, Signature, SignerIndex}; pub mod aggregation; pub mod identify; @@ -31,12 +33,27 @@ pub fn compute_kappa( .sum::() } +#[derive(Debug, Clone)] +pub struct Phi(pub(crate) G1Projective, pub(crate) G1Projective); + +#[derive(Debug, Clone)] +pub struct VarPhi(pub(crate) G1Projective, pub(crate) G1Projective); + + pub struct PayInfo { pub info: [u8; 32], } #[derive(Debug, Clone)] -pub struct Payment {} +pub struct Payment { + kappa: G2Projective, + sig: Signature, + phi: Phi, + varphi: VarPhi, + rr: Scalar, + zkproof: SpendProof, + vv: u64, +} pub struct PartialWallet { sig: Signature, @@ -77,16 +94,16 @@ impl Wallet { verification_key: &VerificationKeyAuth, sk_user: &SecretKeyUser, pay_info: &PayInfo, - V: u64, + vv: u64, ) -> Result<(Payment, &Self)> { - if self.l() + V > L { + if self.l() + vv >= L { return Err(DivisibleEcashError::Spend( "The counter l is higher than max L".to_string(), )); } let grp = params.get_grp(); - let paramsU = params.get_paramsUser(); + let params_u = params.get_params_u(); // randomize signature in the wallet let (signature_prime, sign_blinding_factor) = self.signature().randomise(grp); // construct kappa i.e., blinded attributes for show @@ -101,19 +118,71 @@ impl Wallet { let r1 = grp.random_scalar(); let r2 = grp.random_scalar(); - let phi1 = grp.gen1() * r1; - let phi2 = paramsU.get_ith_sigma(self.l.get() as usize) * self.v + paramsU.get_ith_eta(V as usize) * r1; + let phi = Phi(grp.gen1() * r1, params_u.get_ith_sigma(self.l.get() as usize) * self.v + params_u.get_ith_eta(vv as usize) * r1); // compute hash of the payment info let rr = hash_to_scalar(pay_info.info); - let rho1 = grp.gen1() * r2; - let rho2 = (grp.gen1() * rr) * sk_user.sk + paramsU.get_ith_theta(self.l.get() as usize) * self.v + paramsU.get_ith_eta(V as usize) * r2; - + let varphi = VarPhi(grp.gen1() * r2, (grp.gen1() * rr) * sk_user.sk + params_u.get_ith_theta(self.l.get() as usize) * self.v + params_u.get_ith_eta(vv as usize) * r2); // compute the zk proof + let zkproof = SpendProof {}; // output pay and updated wallet - let pay = Payment {}; + let pay = Payment { + kappa, + sig: signature_prime, + phi, + varphi, + rr, + zkproof, + vv, + }; + self.l.set(self.l.get() + vv); Ok((pay, self)) } +} + +impl Payment { + pub fn spend_verify( + &self, + params: &Parameters, + verification_key: &VerificationKeyAuth, + pay_info: &PayInfo) -> Result { + if bool::from(self.sig.0.is_identity()) { + return Err(DivisibleEcashError::Spend( + "The element h of the signature equals the identity".to_string(), + )); + } + let grp = params.get_grp(); + + if !check_bilinear_pairing( + &self.sig.0.to_affine(), + &G2Prepared::from(self.kappa.to_affine()), + &self.sig.1.to_affine(), + grp.prepared_miller_g2(), + ) { + return Err(DivisibleEcashError::Spend( + "The bilinear check for kappa failed".to_string(), + )); + } + + if bool::from(self.sig.0.is_identity()) { + return Err(DivisibleEcashError::Spend( + "The element h of the signature on l equals the identity".to_string(), + )); + } + + // verify integrity of R + if !(self.rr == hash_to_scalar(pay_info.info)) { + return Err(DivisibleEcashError::Spend( + "Integrity of R does not hold".to_string(), + )); + } + + //TODO: verify whether payinfo contains merchent's identifier + + // TODO: Add zk proof verification + + Ok(true) + } } \ No newline at end of file diff --git a/common/nym_offline_divisible_ecash/src/scheme/setup.rs b/common/nym_offline_divisible_ecash/src/scheme/setup.rs index c7ba8f4e7a..def5f2de32 100644 --- a/common/nym_offline_divisible_ecash/src/scheme/setup.rs +++ b/common/nym_offline_divisible_ecash/src/scheme/setup.rs @@ -52,20 +52,22 @@ impl GroupParameters { #[derive(Debug, Clone)] pub struct Parameters { grp: GroupParameters, - paramsUser: ParametersUser, - paramsAuth: ParametersAuthority, + params_u: ParametersUser, + params_a: ParametersAuthority, } impl Parameters { pub(crate) fn get_grp(&self) -> &GroupParameters { &self.grp } - pub(crate) fn get_paramsUser(&self) -> &ParametersUser { &self.paramsUser } + pub(crate) fn get_params_u(&self) -> &ParametersUser { &self.params_u } - pub(crate) fn get_paramsAuth(&self) -> &ParametersAuthority { &self.paramsAuth } + pub(crate) fn get_params_a(&self) -> &ParametersAuthority { &self.params_a } pub fn new(grp: GroupParameters) -> Parameters { let g1 = grp.gen1(); let g2 = grp.gen2(); + let psi1 = hash_g1("psi1"); + let psi2 = hash_g1("psi2"); let gamma1 = hash_g1("gamma1"); let gamma2 = hash_g1("gamma2"); let eta = hash_g1("eta"); @@ -79,55 +81,59 @@ impl Parameters { let sigma = g1 * z; let theta = eta * z; - let sigmasUser: Vec = (1..=L) + let sigmas_u: Vec = (1..=L) .map(|i| sigma * (y * Scalar::from(i))) .collect(); - let thetasUser: Vec = (1..=L) + + let thetas_u: Vec = (1..=L) .map(|i| theta * (y * Scalar::from(i))) .collect(); - let deltasAuth: Vec = (0..=L - 1) + let deltas_a: Vec = (0..=L - 1) .map(|i| g2 * (y * Scalar::from(i))) .collect(); - let etasUser: Vec = vec_a.iter().map(|x| g1 * x).collect(); + let etas_u: Vec = vec_a.iter().map(|x| g1 * x).collect(); - let mut etasAuth: Vec = Default::default(); - for l in 1..=L + 1 { + let mut etas_a: Vec = Default::default(); + for l in 1..=L { + println!("l = {:?}", l); for k in 0..=l - 1 { - etasAuth.push(g2 * (vec_a[l as usize].neg() * (y * Scalar::from(k)))); + println!("k = {:?}", k); + etas_a.push(g2 * (vec_a[l as usize - 1].neg() * (y * Scalar::from(k)))); } } let sps_keypair = SPSKeyPair::new(grp.clone(), 2, 0); - let messagesA = vec![sigma, theta]; + let messages_a = vec![sigma, theta]; - let sps_signatures: Vec = sigmasUser + let sps_signatures: Vec = sigmas_u .iter() - .zip(thetasUser.iter()) - .map(|(sigma, theta)| sps_keypair.sps_sk.sign(grp.clone(), Some(&messagesA), None)) + .zip(thetas_u.iter()) + .map(|(sigma, theta)| sps_keypair.sps_sk.sign(grp.clone(), Some(&messages_a), None)) .collect(); // Compute signature for each pair sigma, theta - let paramsUser = ParametersUser { + let params_u = ParametersUser { gammas: vec![gamma1, gamma2], + psi: vec![psi1, psi2], eta, omega, - etas: etasUser, - sigmas: sigmasUser, - thetas: thetasUser, + etas: etas_u, + sigmas: sigmas_u, + thetas: thetas_u, sps_signatures, sps_pk: sps_keypair.sps_vk, }; - let paramsAuth = ParametersAuthority { - deltas: deltasAuth, - etas: etasAuth, + let params_a = ParametersAuthority { + deltas: deltas_a, + etas: etas_a, }; return Parameters { grp, - paramsUser, - paramsAuth, + params_u, + params_a, }; } } @@ -135,6 +141,7 @@ impl Parameters { #[derive(Debug, Clone)] pub struct ParametersUser { gammas: Vec, + psi: Vec, eta: G1Projective, omega: G1Projective, etas: Vec, @@ -147,6 +154,8 @@ pub struct ParametersUser { impl ParametersUser { pub(crate) fn get_gammas(&self) -> &Vec { &self.gammas } + pub(crate) fn get_psi(&self) -> &Vec { &self.psi } + pub(crate) fn get_eta(&self) -> &G1Projective { &self.eta } pub(crate) fn get_omega(&self) -> &G1Projective { &self.omega } diff --git a/common/nym_offline_divisible_ecash/src/scheme/structure_preserving_signature.rs b/common/nym_offline_divisible_ecash/src/scheme/structure_preserving_signature.rs index 55956c937b..e7f1012e7c 100644 --- a/common/nym_offline_divisible_ecash/src/scheme/structure_preserving_signature.rs +++ b/common/nym_offline_divisible_ecash/src/scheme/structure_preserving_signature.rs @@ -9,7 +9,7 @@ use crate::scheme::setup::GroupParameters; #[derive(Debug, Clone)] pub struct SPSVerificationKey { - pub grparams: GroupParameters, + pub grp: GroupParameters, pub uus: Vec, pub wws: Vec, pub yy: G2Projective, @@ -17,7 +17,7 @@ pub struct SPSVerificationKey { } pub struct SPSSecretKey { - spsVK: SPSVerificationKey, + sps_vk: SPSVerificationKey, us: Vec, ws: Vec, y: Scalar, @@ -33,30 +33,30 @@ impl SPSSecretKey { self.y } - pub fn sign(&self, grparams: GroupParameters, messagesA: Option<&[G1Projective]>, messagesB: Option<&[G2Projective]>) -> SPSSignature { - let r = grparams.random_scalar(); - let rr = grparams.gen1() * r; - let ss: G1Projective = match messagesA { + pub fn sign(&self, grp: GroupParameters, messages_a: Option<&[G1Projective]>, messages_b: Option<&[G2Projective]>) -> SPSSignature { + let r = grp.random_scalar(); + let rr = grp.gen1() * r; + let ss: G1Projective = match messages_a { Some(msgsA) => { let prodS: Vec = msgsA .iter() .zip(self.ws.iter()) .map(|(m_i, w_i)| m_i * w_i.neg()) .collect(); - grparams.gen1() * (self.z() - r * self.y()) + prodS.iter().fold(G1Projective::identity(), |acc, elem| acc + elem) + grp.gen1() * (self.z() - r * self.y()) + prodS.iter().fold(G1Projective::identity(), |acc, elem| acc + elem) } - None => grparams.gen1() * (self.z() - r * self.y()) + None => grp.gen1() * (self.z() - r * self.y()) }; - let tt = match messagesB { + let tt = match messages_b { Some(msgsB) => { let prodT: Vec = msgsB .iter() .zip(self.us.iter()) .map(|(m_i, u_i)| m_i * u_i.neg()) .collect(); - (grparams.gen2() + prodT.iter().fold(G2Projective::identity(), |acc, elem| acc + elem)) * r.invert().unwrap() + (grp.gen2() + prodT.iter().fold(G2Projective::identity(), |acc, elem| acc + elem)) * r.invert().unwrap() } - None => grparams.gen2() * r.invert().unwrap() + None => grp.gen2() * r.invert().unwrap() }; SPSSignature @@ -91,14 +91,14 @@ impl SPSKeyPair { let zz = grparams.gen2() * z; let sps_vk = SPSVerificationKey { - grparams: grparams.clone(), + grp: grparams.clone(), uus, wws, yy, zz, }; let sps_sk = SPSSecretKey { - spsVK: sps_vk.clone(), + sps_vk: sps_vk.clone(), us, ws, y, diff --git a/common/nym_offline_divisible_ecash/src/scheme/withdrawal.rs b/common/nym_offline_divisible_ecash/src/scheme/withdrawal.rs index 316f2c049c..179b5699cb 100644 --- a/common/nym_offline_divisible_ecash/src/scheme/withdrawal.rs +++ b/common/nym_offline_divisible_ecash/src/scheme/withdrawal.rs @@ -24,14 +24,14 @@ pub struct RequestInfo { pub fn withdrawal_request(params: Parameters, sk_user: SecretKeyUser) -> Result<(WithdrawalRequest, RequestInfo)> { let grp = params.get_grp(); let g1 = grp.gen1(); - let paramsU = params.get_paramsUser(); + let params_u = params.get_params_u(); let v = grp.random_scalar(); let attributes = vec![sk_user.sk, v]; let com_opening = grp.random_scalar(); let commitment = g1 * com_opening + attributes .iter() - .zip(paramsU.get_gammas()) + .zip(params_u.get_gammas()) .map(|(&m, gamma)| gamma * m) .sum::(); @@ -78,7 +78,7 @@ pub fn withdrawal_request(params: Parameters, sk_user: SecretKeyUser) -> Result< Ok((req, req_info)) } -pub(crate) fn issue(params: &Parameters, req: WithdrawalRequest, pkU: PublicKeyUser, skA: SecretKeyAuth) -> Result { +pub(crate) fn issue(params: &Parameters, req: WithdrawalRequest, pk_u: PublicKeyUser, sk_a: SecretKeyAuth) -> Result { let h = hash_g1(req.com.to_bytes()); if !(h == req.com_hash) { return Err(DivisibleEcashError::WithdrawalRequestVerification( @@ -91,7 +91,7 @@ pub(crate) fn issue(params: &Parameters, req: WithdrawalRequest, pkU: PublicKeyU com: req.com, h: req.com_hash, pc_coms: req.pc_coms.clone(), - pk_user: pkU, + pk_user: pk_u, }; if !req.zk_proof.verify(¶ms, &instance) { return Err(DivisibleEcashError::WithdrawalRequestVerification( @@ -102,9 +102,9 @@ pub(crate) fn issue(params: &Parameters, req: WithdrawalRequest, pkU: PublicKeyU let sig = req .pc_coms .iter() - .zip(skA.ys.iter()) + .zip(sk_a.ys.iter()) .map(|(pc, yi)| pc * yi) - .chain(std::iter::once(h * skA.x)) + .chain(std::iter::once(h * sk_a.x)) .sum(); Ok(BlindedSignature(h, sig))