diff --git a/Cargo.lock b/Cargo.lock index b82fa5b7a5..6d599e91cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1006,7 +1006,6 @@ dependencies = [ [[package]] name = "bls12_381" version = "0.6.0" -source = "git+https://github.com/jstuczyn/bls12_381?branch=gt-serialisation#10fb6f700bfda17c8475af3bfd31e3fec15f2278" dependencies = [ "digest 0.9.0", <<<<<<< HEAD diff --git a/common/nym_offline_divisible_ecash/Cargo.toml b/common/nym_offline_divisible_ecash/Cargo.toml index 3dffaceb9c..4bb909795a 100644 --- a/common/nym_offline_divisible_ecash/Cargo.toml +++ b/common/nym_offline_divisible_ecash/Cargo.toml @@ -7,7 +7,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch = "gt-serialisation", default-features = false, features = ["alloc", "pairings", "experimental", "zeroize"] } +#bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch = "gt-serialisation", default-features = false, features = ["alloc", "pairings", "experimental", "zeroize"] } +bls12_381 = { path = "/Users/ania/Documents/Git/andrew_bls12_381", default-features = false, features = ["alloc", "pairings", "experimental", "zeroize"] } itertools = "0.10" digest = "0.9" rand = "0.8" diff --git a/common/nym_offline_divisible_ecash/src/error.rs b/common/nym_offline_divisible_ecash/src/error.rs index 43b3f6ae68..2013b7aa29 100644 --- a/common/nym_offline_divisible_ecash/src/error.rs +++ b/common/nym_offline_divisible_ecash/src/error.rs @@ -25,6 +25,9 @@ pub enum DivisibleEcashError { #[error("Spend Verification related error: {0}")] Spend(String), + #[error("Identify Verification related error: {0}")] + Identify(String), + #[error("Tried to deserialize {object} with bytes of invalid length. Expected {actual} < {} or {modulus_target} % {modulus} == 0")] DeserializationInvalidLength { actual: usize, 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 db663f999e..b619d58174 100644 --- a/common/nym_offline_divisible_ecash/src/proofs/proof_spend.rs +++ b/common/nym_offline_divisible_ecash/src/proofs/proof_spend.rs @@ -15,8 +15,8 @@ pub struct SpendInstance { pub varphi: VarPhi, pub rr: Scalar, pub rr_prime: G1Projective, - pub ss: G1Projective, - pub tt: G2Projective, + pub ss_prime: G1Projective, + pub tt_prime: G2Projective, pub varsig_prime1: G1Projective, pub theta_prime1: G1Projective, pub pg_eq1: Gt, @@ -43,8 +43,8 @@ impl SpendInstance { bytes.extend_from_slice(self.varphi.to_bytes().as_ref()); bytes.extend_from_slice(self.rr.to_bytes().as_ref()); bytes.extend_from_slice(self.rr_prime.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.ss_prime.to_bytes().as_ref()); + bytes.extend_from_slice(self.tt_prime.to_bytes().as_ref()); bytes.extend_from_slice(self.varsig_prime1.to_bytes().as_ref()); bytes.extend_from_slice(self.theta_prime1.to_bytes().as_ref()); bytes.extend_from_slice(self.pg_eq1.to_compressed().as_ref()); @@ -283,7 +283,7 @@ impl SpendProof { mod tests { use std::ops::Neg; - use bls12_381::{G2Projective, Gt, pairing}; + use bls12_381::{G2Projective, pairing}; use group::Curve; use rand::thread_rng; @@ -392,41 +392,6 @@ mod tests { let pg_eq3 = pg_rrprime_yy + pg_ssprime_gen2 + pg_varsigpr2_ww1 + pg_thetapr2_ww2 - pg_gen1_zz; let pg_eq4 = pg_rr_tt - pg_gen1_gen2; - // assert eq 15 - // let L1 = pg_rrprime_yy - pg_psi0_yy * r_rr + pg_ssprime_gen2 - pg_psi0_gen2 * r_ss + pg_varsigpr2_ww1 - pg_psi0_ww1 * r_varsig2 + pg_thetapr2_ww2 - pg_psi0_ww2 * r_theta2 - pg_gen1_zz; - // assert_eq!(L1, Gt::identity()); - - // // assert equation 8 - // assert_eq!( - // pairing(¶ms_u.get_ith_sps_sign(l as usize + vv as usize - 1).rr.to_affine(), &yy.to_affine()) - // + pairing(¶ms_u.get_ith_sps_sign(l as usize + vv as usize - 1).ss.to_affine(), grp.gen2()) - // + pairing(¶ms_u.get_ith_sigma(l as usize + vv as usize - 1).to_affine(), ¶ms_u.get_sps_pk().get_ith_ww(0).to_affine()) - // + pairing(¶ms_u.get_ith_theta(l as usize + vv as usize - 1).to_affine(), ¶ms_u.get_sps_pk().get_ith_ww(1).to_affine()) - // - pairing(grp.gen1(), ¶ms_u.get_sps_pk().get_zz().to_affine()), - // Gt::identity() - // ); - - // let's do step by step checks: - // 1 - // assert_eq!(pairing(¶ms_u.get_ith_sps_sign(l as usize + vv as usize - 1).rr.to_affine(), &yy.to_affine()), - // pg_rrprime_yy - pg_psi0_yy * r_rr - // ); - // 2 - // assert_eq!(pairing(¶ms_u.get_ith_sps_sign(l as usize + vv as usize - 1).ss.to_affine(), grp.gen2()), - // pg_ssprime_gen2 - pg_psi0_gen2 * r_ss - // ); - // 3 - // assert_eq!(pairing(¶ms_u.get_ith_sigma(l as usize + vv as usize - 1).to_affine(), ¶ms_u.get_sps_pk().get_ith_ww(0).to_affine()), - // pg_varsigpr2_ww1 - pg_psi0_ww1 * r_varsig2 - // ); - // 4 - // assert_eq!(pairing(¶ms_u.get_ith_theta(l as usize + vv as usize - 1).to_affine(), ¶ms_u.get_sps_pk().get_ith_ww(1).to_affine()), - // pg_thetapr2_ww2 - pg_psi0_ww2 * r_theta2 - // ); - // 5 - // assert_eq!(pairing(grp.gen1(), ¶ms_u.get_sps_pk().get_zz().to_affine()), - // pg_gen1_zz); - let instance = SpendInstance { kappa, @@ -434,8 +399,8 @@ mod tests { varphi, rr, rr_prime, - ss: ss_prime, - tt: tt_prime, + ss_prime: ss_prime, + tt_prime: tt_prime, varsig_prime1, theta_prime1, pg_eq1, diff --git a/common/nym_offline_divisible_ecash/src/scheme/aggregation.rs b/common/nym_offline_divisible_ecash/src/scheme/aggregation.rs index 5703f71849..5947e9a1fc 100644 --- a/common/nym_offline_divisible_ecash/src/scheme/aggregation.rs +++ b/common/nym_offline_divisible_ecash/src/scheme/aggregation.rs @@ -2,12 +2,13 @@ use core::iter::Sum; use core::ops::Mul; use std::cell::Cell; -use bls12_381::{G2Prepared, G2Projective, Scalar}; +use bls12_381::{G2Prepared, G2Projective, pairing, Scalar}; use group::Curve; use itertools::Itertools; use crate::Attribute; use crate::error::{DivisibleEcashError, Result}; +use crate::scheme::{PartialWallet, Wallet}; use crate::scheme::keygen::{SecretKeyUser, VerificationKeyAuth}; use crate::scheme::setup::GroupParameters; use crate::utils::{ @@ -140,4 +141,27 @@ pub fn aggregate_signatures( Ok(signature) } +pub fn aggregate_wallets( + grp: &GroupParameters, + verification_key: &VerificationKeyAuth, + sk_user: &SecretKeyUser, + wallets: &[PartialWallet], +) -> Result { + let signature_shares: Vec = wallets + .iter() + .enumerate() + .map(|(idx, wallet)| SignatureShare::new(*wallet.signature(), (idx + 1) as u64)) + .collect(); + + let v = wallets.get(0).unwrap().v; + let attributes = vec![sk_user.sk, v]; + let aggregated_signature = + aggregate_signature_shares(&grp, &verification_key, &attributes, &signature_shares)?; + + Ok(Wallet { + sig: aggregated_signature, + v, + l: Cell::new(1), + }) +} diff --git a/common/nym_offline_divisible_ecash/src/scheme/identification.rs b/common/nym_offline_divisible_ecash/src/scheme/identification.rs new file mode 100644 index 0000000000..7822dc58d9 --- /dev/null +++ b/common/nym_offline_divisible_ecash/src/scheme/identification.rs @@ -0,0 +1,113 @@ +use std::collections::HashMap; +use std::ops::Neg; + +use bls12_381::{Gt, pairing}; +use group::Curve; + +use crate::error::{DivisibleEcashError, Result}; +use crate::scheme::{PayInfo, Payment}; +use crate::scheme::identification::IdentifyResult::DoubleSpendingPublicKeys; +use crate::scheme::keygen::PublicKeyUser; +use crate::scheme::setup::Parameters; + +pub enum IdentifyResult { + NoCommonSerialNumbers, + DuplicatePayInfo(PayInfo), + DoubleSpendingPublicKeys(Vec), + Whatever, +} + +// how do we get the list of all pkU ? +pub fn identify( + params: &Parameters, + public_keys_u: &[PublicKeyUser], + payment1: Payment, + payment2: Payment, + payinfo1: PayInfo, + payinfo2: PayInfo) -> Result { + let params_a = params.get_params_a(); + + // compute the serial numbers for k1 in [0, V1-1] + let mut serial_numbers = HashMap::new(); + for k1 in 0..payment1.vv { + let sn = pairing(&payment1.phi.0.to_affine(), ¶ms_a.get_ith_delta(k1 as usize).to_affine()) + + pairing(&payment1.phi.1.to_affine(), ¶ms_a.get_ith_eta(k1 as usize).to_affine()); + serial_numbers.insert(sn, k1); + } + + // compute the serial numbers fo k2 in [0, V2-1] + let mut duplicate_serial_numbers: Vec<(Gt, u64, u64)> = Default::default(); + for k2 in 0..payment2.vv { + let sn = pairing(&payment2.phi.0.to_affine(), ¶ms_a.get_ith_delta(k2 as usize).to_affine()) + + pairing(&payment2.phi.1.to_affine(), ¶ms_a.get_ith_eta(k2 as usize).to_affine()); + if !serial_numbers.contains_key(&sn) { + serial_numbers.insert(sn, k2); + } else { + let k1 = *serial_numbers.get(&sn).unwrap() as u64; + duplicate_serial_numbers.push((sn, k1, k2)); + } + } + + if duplicate_serial_numbers.is_empty() { + Ok(IdentifyResult::NoCommonSerialNumbers) + } else { + if payinfo1.info == payinfo2.info { + Ok(IdentifyResult::DuplicatePayInfo(payinfo1)) + } else { + let mut identified_pk_u: Vec = Default::default(); + for elem in duplicate_serial_numbers.iter() { + let k1 = elem.1; + let k2 = elem.2; + let delta_k1 = params_a.get_ith_delta(k1 as usize); + let delta_k2 = params_a.get_ith_delta(k2 as usize); + let tt1 = pairing(&payment1.varphi.1.to_affine(), &delta_k1.to_affine()) + + pairing(&payment1.varphi.0.to_affine(), ¶ms_a.get_ith_eta(k1 as usize).to_affine()); + let tt2 = pairing(&payment2.varphi.1.to_affine(), &delta_k2.to_affine()) + + pairing(&payment2.varphi.0.to_affine(), ¶ms_a.get_ith_eta(k2 as usize).to_affine()); + + for pk_u in public_keys_u.iter() { + let pg_pku_deltas = pairing(&pk_u.pk.to_affine(), &(delta_k1 * payment1.rr.neg() + delta_k2 * payment2.rr.neg()).to_affine()); + if tt1 + tt2.neg() == pg_pku_deltas { + identified_pk_u.push(pk_u.clone()); + } + } + } + if !identified_pk_u.is_empty() { + Ok(DoubleSpendingPublicKeys(identified_pk_u.clone())) + } else { + return Err(DivisibleEcashError::Identify( + "A duplicate serial number was detected, the payinfo1 and payinfo2 are different, but we failed to identify the double-spending public key".to_string(), + )); + } + } + } +} + + +#[cfg(test)] +mod tests { + use rand::thread_rng; + + use crate::scheme::keygen::PublicKeyUser; + use crate::scheme::setup::{GroupParameters, Parameters}; + use crate::utils::hash_g1; + + #[test] + fn no_matching_serial_numbers() {} + + #[test] + fn matching_payinfo() {} + + #[test] + fn identified_duplicate_serial_number_and_non_matching_payinfo() { + let rng = thread_rng(); + let grp = GroupParameters::new().unwrap(); + let params = Parameters::new(grp.clone()); + let params_u = params.get_params_u(); + let params_a = params.get_params_a(); + + let pk_u1 = PublicKeyUser { pk: hash_g1("PublicKey1") }; + let pk_u2 = PublicKeyUser { pk: hash_g1("PublicKey1") }; + let pk_u3 = PublicKeyUser { pk: hash_g1("PublicKey1") }; + } +} \ No newline at end of file diff --git a/common/nym_offline_divisible_ecash/src/scheme/mod.rs b/common/nym_offline_divisible_ecash/src/scheme/mod.rs index 90948324ce..3cb999a329 100644 --- a/common/nym_offline_divisible_ecash/src/scheme/mod.rs +++ b/common/nym_offline_divisible_ecash/src/scheme/mod.rs @@ -1,4 +1,5 @@ use std::cell::Cell; +use std::convert::{TryFrom, TryInto}; use std::ops::Neg; use bls12_381::{G1Projective, G2Prepared, G2Projective, pairing, Scalar}; @@ -10,7 +11,7 @@ use crate::error::{DivisibleEcashError, 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, Signature, SignerIndex}; +use crate::utils::{check_bilinear_pairing, hash_to_scalar, Signature, SignerIndex, try_deserialize_g1_projective}; pub mod aggregation; pub mod identify; @@ -18,6 +19,7 @@ pub mod keygen; pub mod setup; pub mod structure_preserving_signature; pub mod withdrawal; +pub mod identification; pub fn compute_kappa( params: &GroupParameters, @@ -34,7 +36,7 @@ pub fn compute_kappa( .sum::() } -#[derive(Debug, Clone, Copy)] +#[derive(Eq, PartialEq, Debug, Clone, Copy)] pub struct Phi(pub(crate) G1Projective, pub(crate) G1Projective); impl Phi { @@ -44,9 +46,35 @@ impl Phi { bytes.extend_from_slice(self.1.to_bytes().as_ref()); bytes } + + pub(crate) fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() < 48 * 2 || (bytes.len()) % 48 != 0 { + return Err(DivisibleEcashError::DeserializationInvalidLength { + actual: bytes.len(), + modulus_target: bytes.len(), + target: 48 * 2, + modulus: 48, + object: "phi".to_string(), + }); + } + + let elem_0_bytes = bytes[0..48].try_into().unwrap(); + let elem_0 = try_deserialize_g1_projective( + elem_0_bytes, + DivisibleEcashError::Deserialization("Failed to deserialize element 0 of Phi".to_string()), + )?; + + let elem_1_bytes = bytes[48..96].try_into().unwrap(); + let elem_1 = try_deserialize_g1_projective( + elem_1_bytes, + DivisibleEcashError::Deserialization("Failed to deserialize element 1 of Phi".to_string()), + )?; + + Ok(Phi(elem_0, elem_1)) + } } -#[derive(Debug, Clone, Copy)] +#[derive(Eq, PartialEq, Debug, Clone, Copy)] pub struct VarPhi(pub(crate) G1Projective, pub(crate) G1Projective); impl VarPhi { @@ -56,6 +84,32 @@ impl VarPhi { bytes.extend_from_slice(self.1.to_bytes().as_ref()); bytes } + + pub(crate) fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() < 48 * 2 || (bytes.len()) % 48 != 0 { + return Err(DivisibleEcashError::DeserializationInvalidLength { + actual: bytes.len(), + modulus_target: bytes.len(), + target: 48 * 2, + modulus: 48, + object: "varphi".to_string(), + }); + } + + let elem_0_bytes = bytes[0..48].try_into().unwrap(); + let elem_0 = try_deserialize_g1_projective( + elem_0_bytes, + DivisibleEcashError::Deserialization("Failed to deserialize element 0 of VarPhi".to_string()), + )?; + + let elem_1_bytes = bytes[48..96].try_into().unwrap(); + let elem_1 = try_deserialize_g1_projective( + elem_1_bytes, + DivisibleEcashError::Deserialization("Failed to deserialize element 1 of VarPhi".to_string()), + )?; + + Ok(VarPhi(elem_0, elem_1)) + } } pub struct PayInfo { @@ -68,6 +122,13 @@ pub struct Payment { sig: Signature, phi: Phi, varphi: VarPhi, + varsig_prime1: G1Projective, + varsig_prime2: G1Projective, + theta_prime1: G1Projective, + theta_prime2: G1Projective, + rr_prime: G1Projective, + ss_prime: G1Projective, + tt_prime: G2Projective, rr: Scalar, zk_proof: SpendProof, vv: u64, @@ -79,6 +140,18 @@ pub struct PartialWallet { idx: Option, } +impl PartialWallet { + pub fn signature(&self) -> &Signature { + &self.sig + } + pub fn v(&self) -> Scalar { + self.v + } + pub fn index(&self) -> Option { + self.idx + } +} + pub struct Wallet { sig: Signature, v: Scalar, @@ -137,11 +210,12 @@ impl Wallet { let r1 = grp.random_scalar(); let r2 = grp.random_scalar(); - 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); + let phi = Phi(grp.gen1() * r1, params_u.get_ith_sigma(self.l() 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 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); + let varphi = VarPhi(grp.gen1() * r2, (grp.gen1() * rr) * sk_user.sk + params_u.get_ith_theta(self.l() as usize) * self.v + params_u.get_ith_eta(vv as usize) * r2); + // random value used to compute blinded bases let r_varsig1 = grp.random_scalar(); @@ -158,10 +232,12 @@ impl Wallet { let varsig_prime1 = params_u.get_ith_sigma(self.l() as usize) + (psi_g1 * r_varsig1); let theta_prime1 = params_u.get_ith_theta(self.l() as usize) + (psi_g1 * r_theta1); let varsig_prime2 = params_u.get_ith_sigma(self.l() as usize + vv as usize - 1) + (psi_g1 * r_varsig2); - let theta_prime2 = params_u.get_ith_sigma(self.l() as usize + vv as usize - 1) + (psi_g1 * r_theta2); - let rr_prime = params_u.get_ith_sps_sign(self.l() as usize + vv as usize - 1).rr + (psi_g1 * r_rr); - let ss_prime = params_u.get_ith_sps_sign(self.l() as usize + vv as usize - 1).ss + (psi_g1 * r_ss); - let tt_prime = params_u.get_ith_sps_sign(self.l() as usize + vv as usize - 1).tt + (psi_g2 * r_tt); + let theta_prime2 = params_u.get_ith_theta(self.l() as usize + vv as usize - 1) + (psi_g1 * r_theta2); + + let tau_l_vv = params_u.get_ith_sps_sign(self.l() as usize + vv as usize - 1); + let rr_prime = tau_l_vv.rr + (psi_g1 * r_rr); + let ss_prime = tau_l_vv.ss + (psi_g1 * r_ss); + let tt_prime = tau_l_vv.tt + (psi_g2 * r_tt); let rho1 = self.v.neg() * r_varsig1; let rho2 = self.v.neg() * r_theta1; @@ -181,7 +257,7 @@ impl Wallet { let ww2 = params_u.get_sps_pk().get_ith_ww(1); let pg_varsigpr2_ww1 = pairing(&varsig_prime2.to_affine(), &ww1.to_affine()); let pg_psi0_ww1 = pairing(&psi_g1.to_affine(), &ww1.to_affine()); - let pg_thetapr2_ww2 = pairing(&theta_prime1.to_affine(), &ww2.to_affine()); + let pg_thetapr2_ww2 = pairing(&theta_prime2.to_affine(), &ww2.to_affine()); let pg_psi0_ww2 = pairing(&psi_g1.to_affine(), &ww2.to_affine()); let pg_gen1_zz = pairing(grp.gen1(), ¶ms_u.get_sps_pk().get_zz().to_affine()); let pg_rr_tt = pairing(&rr_prime.to_affine(), &tt_prime.to_affine()); @@ -201,8 +277,8 @@ impl Wallet { varphi, rr, rr_prime, - ss: ss_prime, - tt: tt_prime, + ss_prime, + tt_prime, varsig_prime1, theta_prime1, pg_eq1, @@ -248,12 +324,19 @@ impl Wallet { sig: signature_prime, phi, varphi, + varsig_prime1, + varsig_prime2, + theta_prime1, + theta_prime2, + rr_prime, + ss_prime, + tt_prime, rr, zk_proof, vv, }; - self.l.set(self.l.get() + vv); + self.l.set(self.l() + vv); Ok((pay, self)) } } @@ -270,6 +353,8 @@ impl Payment { )); } let grp = params.get_grp(); + let params_a = params.get_params_a(); + let params_u = params.get_params_u(); if !check_bilinear_pairing( &self.sig.0.to_affine(), @@ -297,8 +382,114 @@ impl Payment { //TODO: verify whether payinfo contains merchent's identifier - // TODO: Add zk proof verification + let psi_g1 = params_u.get_psi_g1(); + let psi_g2 = params_u.get_psi_g2(); + let pg_varsigpr1_delta = pairing(&self.varsig_prime1.to_affine(), ¶ms_a.get_ith_delta((self.vv - 1) as usize).to_affine()); + let pg_psi0_delta = pairing(&psi_g1.to_affine(), ¶ms_a.get_ith_delta((self.vv - 1) as usize).to_affine()); + let pg_varsigpr2_gen2 = pairing(&self.varsig_prime2.to_affine(), grp.gen2()); + let pg_psi0_gen2 = pairing(&psi_g1.to_affine(), grp.gen2()); + let pg_thetapr1_delta = pairing(&self.theta_prime1.to_affine(), ¶ms_a.get_ith_delta((self.vv - 1) as usize).to_affine()); + let pg_thetapr2_gen2 = pairing(&self.theta_prime2.to_affine(), grp.gen2()); + let yy = params_u.get_sps_pk().get_yy(); + let pg_rrprime_yy = pairing(&self.rr_prime.to_affine(), &yy.to_affine()); + let pg_psi0_yy = pairing(&psi_g1.to_affine(), &yy.to_affine()); + let pg_ssprime_gen2 = pairing(&self.ss_prime.to_affine(), grp.gen2()); + let ww1 = params_u.get_sps_pk().get_ith_ww(0); + let ww2 = params_u.get_sps_pk().get_ith_ww(1); + let pg_varsigpr2_ww1 = pairing(&self.varsig_prime2.to_affine(), &ww1.to_affine()); + let pg_psi0_ww1 = pairing(&psi_g1.to_affine(), &ww1.to_affine()); + let pg_thetapr2_ww2 = pairing(&self.theta_prime2.to_affine(), &ww2.to_affine()); + let pg_psi0_ww2 = pairing(&psi_g1.to_affine(), &ww2.to_affine()); + let pg_gen1_zz = pairing(grp.gen1(), ¶ms_u.get_sps_pk().get_zz().to_affine()); + let pg_rr_tt = pairing(&self.rr_prime.to_affine(), &self.tt_prime.to_affine()); + let pg_rr_psi1 = pairing(&self.rr_prime.to_affine(), &psi_g2.to_affine()); + let pg_psi0_tt = pairing(&psi_g1.to_affine(), &self.tt_prime.to_affine()); + let pg_psi0_psi1 = pairing(&psi_g1.to_affine(), &psi_g2.to_affine()); + let pg_gen1_gen2 = pairing(grp.gen1(), grp.gen2()); - Ok(true) + let pg_eq1 = pg_varsigpr1_delta - pg_varsigpr2_gen2; + let pg_eq2 = pg_thetapr1_delta - pg_thetapr2_gen2; + let pg_eq3 = pg_rrprime_yy + pg_ssprime_gen2 + pg_varsigpr2_ww1 + pg_thetapr2_ww2 + pg_gen1_zz.neg(); + let pg_eq4 = pg_rr_tt - pg_gen1_gen2; + + let instance = SpendInstance { + kappa: self.kappa, + phi: self.phi, + varphi: self.varphi, + rr: self.rr, + rr_prime: self.rr_prime, + ss_prime: self.ss_prime, + tt_prime: self.tt_prime, + varsig_prime1: self.varsig_prime1, + theta_prime1: self.theta_prime1, + pg_eq1, + pg_eq2, + pg_eq3, + pg_eq4, + psi_g1: *psi_g1, + psi_g2: *psi_g2, + pg_psi0_delta, + pg_psi0_gen2, + pg_psi0_yy, + pg_psi0_ww1, + pg_psi0_ww2, + pg_rr_psi1, + pg_psi0_tt, + pg_psi0_psi1, + }; + + Ok(self.zk_proof.verify(¶ms, &instance, &verification_key, self.vv)) + } +} + +#[cfg(test)] +mod tests { + use std::convert::TryFrom; + + use rand::thread_rng; + + use crate::scheme::{PayInfo, Phi, VarPhi, Wallet}; + use crate::scheme::aggregation::aggregate_verification_keys; + use crate::scheme::keygen::{PublicKeyUser, ttp_keygen_authorities, VerificationKeyAuth}; + use crate::scheme::setup::{GroupParameters, Parameters}; + use crate::utils::hash_g1; + + #[test] + fn phi_to_and_from_bytes() { + let phi = Phi(hash_g1("Element 0 of Phi"), hash_g1("Element 1 of Phi")); + let phi_bytes = phi.to_bytes(); + let phi_from_bytes = Phi::from_bytes(&phi_bytes).unwrap(); + assert_eq!(phi, phi_from_bytes); + } + + #[test] + fn varphi_to_and_from_bytes() { + let varphi = VarPhi(hash_g1("Element 0 of VarPhi"), hash_g1("Element 1 of VarPhi")); + let varphi_bytes = varphi.to_bytes(); + let varphi_from_bytes = VarPhi::from_bytes(&varphi_bytes).unwrap(); + assert_eq!(varphi, varphi_from_bytes); + } + + #[test] + fn spend_verification_is_correct() { + let rng = thread_rng(); + let grp = GroupParameters::new().unwrap(); + let params = Parameters::new(grp.clone()); + let params_u = params.get_params_u(); + let params_a = params.get_params_a(); + + let sk = grp.random_scalar(); + let pk_user = PublicKeyUser { + pk: grp.gen1() * sk, + }; + + let authorities_keypairs = ttp_keygen_authorities(¶ms, 2, 3).unwrap(); + let verification_keys_auth: Vec = authorities_keypairs + .iter() + .map(|keypair| keypair.verification_key()) + .collect(); + + let verification_key = + aggregate_verification_keys(&verification_keys_auth, Some(&[1, 2, 3])).unwrap(); } } \ 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 36151e1b5d..a027a2ea5a 100644 --- a/common/nym_offline_divisible_ecash/src/scheme/setup.rs +++ b/common/nym_offline_divisible_ecash/src/scheme/setup.rs @@ -1,7 +1,7 @@ use std::convert::TryFrom; use std::net::ToSocketAddrs; -use bls12_381::{G1Affine, G1Projective, G2Affine, G2Prepared, G2Projective, pairing, Scalar}; +use bls12_381::{G1Affine, G1Projective, G2Affine, G2Prepared, G2Projective, Scalar}; use ff::Field; use group::Curve; use rand::thread_rng; 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 90df61ec1e..1bfc15f4f1 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 @@ -1,11 +1,9 @@ -use std::convert::TryFrom; use std::fmt::Debug; use std::ops::Neg; use bls12_381::{G1Projective, G2Projective, Gt, pairing, Scalar}; use group::Curve; -use crate::Attribute; use crate::scheme::setup::GroupParameters; #[derive(Debug, Clone)] diff --git a/common/nym_offline_divisible_ecash/src/scheme/withdrawal.rs b/common/nym_offline_divisible_ecash/src/scheme/withdrawal.rs index 179b5699cb..bc3a243064 100644 --- a/common/nym_offline_divisible_ecash/src/scheme/withdrawal.rs +++ b/common/nym_offline_divisible_ecash/src/scheme/withdrawal.rs @@ -21,7 +21,7 @@ pub struct RequestInfo { v: Scalar, } -pub fn withdrawal_request(params: Parameters, sk_user: SecretKeyUser) -> Result<(WithdrawalRequest, RequestInfo)> { +pub fn withdrawal_request(params: &Parameters, sk_user: &SecretKeyUser) -> Result<(WithdrawalRequest, RequestInfo)> { let grp = params.get_grp(); let g1 = grp.gen1(); let params_u = params.get_params_u(); @@ -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, pk_u: PublicKeyUser, sk_a: 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( diff --git a/common/nym_offline_divisible_ecash/src/tests/e2e.rs b/common/nym_offline_divisible_ecash/src/tests/e2e.rs index 8b13789179..1b12d8f8d4 100644 --- a/common/nym_offline_divisible_ecash/src/tests/e2e.rs +++ b/common/nym_offline_divisible_ecash/src/tests/e2e.rs @@ -1 +1,60 @@ +use rand::thread_rng; +use crate::error::DivisibleEcashError; +use crate::scheme::aggregation::{aggregate_signatures, aggregate_verification_keys, aggregate_wallets}; +use crate::scheme::keygen::{PublicKeyUser, SecretKeyUser, ttp_keygen_authorities, VerificationKeyAuth}; +use crate::scheme::PayInfo; +use crate::scheme::setup::{GroupParameters, Parameters}; +use crate::scheme::withdrawal::{issue, issue_verify, withdrawal_request}; + +#[test] +fn main() -> Result<(), DivisibleEcashError> { + // SETUP PHASE + let rng = thread_rng(); + let grp = GroupParameters::new().unwrap(); + let params = Parameters::new(grp.clone()); + let params_u = params.get_params_u(); + let params_a = params.get_params_a(); + + // KEY GENERATION FOR THE AUTHORITIES + let authorities_keypairs = ttp_keygen_authorities(¶ms, 2, 3).unwrap(); + let verification_keys_auth: Vec = authorities_keypairs + .iter() + .map(|keypair| keypair.verification_key()) + .collect(); + + let verification_key = + aggregate_verification_keys(&verification_keys_auth, Some(&[1, 2, 3])).unwrap(); + + // KEY GENERATION FOR THE USER + let sk = grp.random_scalar(); + let sk_user = SecretKeyUser { sk }; + let pk_user = SecretKeyUser::public_key(&sk_user, &grp); + + // WITHDRAWAL REQUEST + let (withdrawal_req, req_info) = withdrawal_request(¶ms, &sk_user)?; + + // ISSUE PARTIAL WALLETS + let mut partial_wallets = Vec::new(); + for auth_keypair in authorities_keypairs { + let blind_signature = issue( + ¶ms, + &withdrawal_req, + pk_user.clone(), + &auth_keypair.secret_key(), + )?; + let partial_wallet = issue_verify(&grp, &auth_keypair.verification_key(), &sk_user, &blind_signature, &req_info)?; + partial_wallets.push(partial_wallet); + } + + // AGGREGATE WALLET + let mut wallet = aggregate_wallets(&grp, &verification_key, &sk_user, &partial_wallets)?; + + let pay_info = PayInfo { info: [67u8; 32] }; + let (payment, wallet) = wallet.spend(¶ms, &verification_key, &sk_user, &pay_info, 10)?; + + // SPEND VERIFICATION + payment.spend_verify(¶ms, &verification_key, &pay_info); + + Ok(()) +}