From 014374e33e42caa2c33d806fe654686751456099 Mon Sep 17 00:00:00 2001 From: aniampio Date: Tue, 28 Jun 2022 15:35:07 +0100 Subject: [PATCH] Add tests to indetify protocol --- .../src/scheme/identification.rs | 204 +++++++++++++++--- .../src/scheme/keygen.rs | 2 +- .../src/scheme/mod.rs | 4 +- .../src/tests/e2e.rs | 2 + 4 files changed, 174 insertions(+), 38 deletions(-) diff --git a/common/nym_offline_divisible_ecash/src/scheme/identification.rs b/common/nym_offline_divisible_ecash/src/scheme/identification.rs index 03714fd026..3bc6b07447 100644 --- a/common/nym_offline_divisible_ecash/src/scheme/identification.rs +++ b/common/nym_offline_divisible_ecash/src/scheme/identification.rs @@ -1,46 +1,62 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::ops::Neg; -use bls12_381::{Gt, pairing}; +use bls12_381::{Gt, pairing, Scalar}; 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::keygen::{PublicKeyUser, VerificationKeyAuth}; use crate::scheme::setup::Parameters; #[derive(Debug, Eq, PartialEq)] pub enum IdentifyResult { NotADuplicatePayment, DuplicatePayInfo(PayInfo), - DoubleSpendingPublicKeys(Vec), + DoubleSpendingPublicKeys(PublicKeyUser), Whatever, } // how do we get the list of all pkU ? pub fn identify( params: &Parameters, + verification_key: &VerificationKeyAuth, public_keys_u: &[PublicKeyUser], payment1: Payment, payment2: Payment, - payinfo1: PayInfo, - payinfo2: PayInfo) -> Result { + pay_info1: PayInfo, + pay_info2: PayInfo) -> Result { + // verify first the validaty of both payments + assert!(payment1.spend_verify(¶ms, &verification_key, &pay_info1).unwrap()); + assert!(payment2.spend_verify(¶ms, &verification_key, &pay_info2).unwrap()); + let params_a = params.get_params_a(); // compute the serial numbers for k1 in [0, V1-1] let mut serial_numbers = HashMap::new(); + + println!("Let's see, where is the difference"); + println!("Payment1 Phi1 {:?}", payment1.phi.1); + println!("Payment2 Phi1 {:?}", payment2.phi.1); + + println!("Payment1 Phi0 {:?}", payment1.phi.0); + println!("Payment2 Phi0 {:?}", payment2.phi.0); + + println!("Payment1 vv {:?}", payment1.vv); + println!("Payment2 vv {:?}", payment2.vv); + + for k1 in 0..payment1.vv { - let pg1 = pairing(&payment1.phi.0.to_affine(), ¶ms_a.get_ith_delta(k1 as usize).to_affine()); - let pg2 = pairing(&payment1.phi.1.to_affine(), ¶ms_a.get_ith_eta(k1 as usize).to_affine()); - let sn = pg1 + pg2; + let sn = pairing(&payment1.phi.1.to_affine(), ¶ms_a.get_ith_delta(k1 as usize).to_affine()) + + pairing(&payment1.phi.0.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()); + let sn = pairing(&payment2.phi.1.to_affine(), ¶ms_a.get_ith_delta(k2 as usize).to_affine()) + + pairing(&payment2.phi.0.to_affine(), ¶ms_a.get_ith_eta(k2 as usize).to_affine()); if !serial_numbers.contains_key(&sn) { serial_numbers.insert(sn, k2); } else { @@ -49,13 +65,13 @@ pub fn identify( } } + println!("HOw many duplicates: {:?}", duplicate_serial_numbers.len()); if duplicate_serial_numbers.is_empty() { Ok(IdentifyResult::NotADuplicatePayment) } else { - if payinfo1.info == payinfo2.info { - Ok(IdentifyResult::DuplicatePayInfo(payinfo1)) + if pay_info1.info == pay_info2.info { + Ok(IdentifyResult::DuplicatePayInfo(pay_info1)) } else { - let mut identified_pk_u: Vec = Default::default(); for elem in duplicate_serial_numbers.iter() { let k1 = elem.1; let k2 = elem.2; @@ -66,27 +82,25 @@ pub fn identify( 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 tt1 + tt2 * Scalar::from(1).neg() == pg_pku_deltas { + return Ok(IdentifyResult::DoubleSpendingPublicKeys(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(), - )); - } + 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 std::collections::HashSet; + use rand::thread_rng; use crate::scheme::{PayInfo, Payment}; @@ -98,24 +112,144 @@ mod tests { use crate::utils::hash_g1; #[test] - fn no_matching_serial_numbers() {} - - #[test] - fn matching_payinfo() {} - - #[test] - fn identified_duplicate_serial_number_and_non_matching_pay_info() { + fn duplicate_payments_with_the_same_pay_info() { 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") }; + // 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 USER1 + let sk1 = grp.random_scalar(); + let sk_user1 = SecretKeyUser { sk: sk1 }; + let pk_user1 = SecretKeyUser::public_key(&sk_user1, &grp); + + // KEY GENERATION FOR THE USER2 + let sk2 = grp.random_scalar(); + let sk_user2 = SecretKeyUser { sk: sk2 }; + let pk_user2 = SecretKeyUser::public_key(&sk_user2, &grp); + + + // WITHDRAWAL REQUEST FOR USER1 + let (withdrawal_req1, req_info1) = withdrawal_request(¶ms, &sk_user1).unwrap(); + + // ISSUE PARTIAL WALLETS for USER1 + let mut partial_wallets1 = Vec::new(); + for auth_keypair in authorities_keypairs.clone() { + let blind_signature = issue( + ¶ms, + &withdrawal_req1, + pk_user1.clone(), + &auth_keypair.secret_key(), + ).unwrap(); + let partial_wallet1 = issue_verify(&grp, &auth_keypair.verification_key(), &sk_user1, &blind_signature, &req_info1).unwrap(); + partial_wallets1.push(partial_wallet1); + } + + // AGGREGATE WALLET FOR USER1 + let mut wallet1 = aggregate_wallets(&grp, &verification_key, &sk_user1, &partial_wallets1).unwrap(); + + let pay_info1 = PayInfo { info: [67u8; 32] }; + let (payment1, wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info1, 10).unwrap(); + + // SPEND VERIFICATION for USER1 + assert!(payment1.spend_verify(¶ms, &verification_key, &pay_info1).unwrap()); + + let payment2 = payment1.clone(); + // SPEND VERIFICATION for the duplicate payment + assert!(payment1.spend_verify(¶ms, &verification_key, &pay_info1).unwrap()); + + let pay_info2 = pay_info1.clone(); + + let identify_result = identify(¶ms, &verification_key, &[pk_user1, pk_user2], payment1, payment2, pay_info1, pay_info2).unwrap(); + assert_eq!(identify_result, IdentifyResult::DuplicatePayInfo(pay_info1)); } + #[test] + fn two_payments_with_one_repeating_serial_number_but_different_pay_info() { + 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 USER1 + let sk1 = grp.random_scalar(); + let sk_user1 = SecretKeyUser { sk: sk1 }; + let pk_user1 = SecretKeyUser::public_key(&sk_user1, &grp); + + // KEY GENERATION FOR THE USER2 + let sk_user2 = sk_user1.clone(); + let pk_user2 = pk_user1.clone(); + + // GENERATE KEYS FOR OTHER USERS + let mut pk_all_users: Vec = Default::default(); + // for i in 0..50 { + // let sk = grp.random_scalar(); + // let sk_user = SecretKeyUser { sk }; + // let pk_user = sk_user.public_key(&grp); + // pk_all_users.push(pk_user); + // } + pk_all_users.push(pk_user1.clone()); + pk_all_users.push(pk_user2.clone()); + + // WITHDRAWAL REQUEST FOR USER1 + let (withdrawal_req1, req_info1) = withdrawal_request(¶ms, &sk_user1).unwrap(); + + // ISSUE PARTIAL WALLETS for USER1 + let mut partial_wallets1 = Vec::new(); + for auth_keypair in authorities_keypairs.clone() { + let blind_signature = issue( + ¶ms, + &withdrawal_req1, + pk_user1.clone(), + &auth_keypair.secret_key(), + ).unwrap(); + let partial_wallet1 = issue_verify(&grp, &auth_keypair.verification_key(), &sk_user1, &blind_signature, &req_info1).unwrap(); + partial_wallets1.push(partial_wallet1); + } + + // AGGREGATE WALLET FOR USER1 + let mut wallet1 = aggregate_wallets(&grp, &verification_key, &sk_user1, &partial_wallets1).unwrap(); + + let pay_info1 = PayInfo { info: [67u8; 32] }; + let (payment1, new_wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info1, 10).unwrap(); + + + // let's reverse the spending counter in the wallet to create a double spending payment + // let current_l = wallet1.l.get(); + // wallet1.l.set(current_l - 1); + + let pay_info2 = PayInfo { info: [52u8; 32] }; + let (payment2, wallet1) = wallet1.spend(¶ms, &verification_key, &sk_user1, &pay_info2, 10).unwrap(); + + + let identify_result = identify(¶ms, &verification_key, &pk_all_users, payment1, payment2, pay_info1, pay_info2).unwrap(); + + assert_eq!(identify_result, IdentifyResult::DoubleSpendingPublicKeys(pk_user1)); + } + + #[test] fn ok_if_two_different_payments() { let rng = thread_rng(); @@ -194,7 +328,7 @@ mod tests { // SPEND VERIFICATION for USER2 assert!(payment2.spend_verify(¶ms, &verification_key, &pay_info2).unwrap()); - let identify_result = identify(¶ms, &[pk_user1, pk_user2], payment1, payment2, pay_info1, pay_info2).unwrap(); + let identify_result = identify(¶ms, &verification_key, &[pk_user1, pk_user2], payment1, payment2, pay_info1, pay_info2).unwrap(); assert_eq!(identify_result, IdentifyResult::NotADuplicatePayment); } } \ No newline at end of file diff --git a/common/nym_offline_divisible_ecash/src/scheme/keygen.rs b/common/nym_offline_divisible_ecash/src/scheme/keygen.rs index b58dc21404..fbb137fb87 100644 --- a/common/nym_offline_divisible_ecash/src/scheme/keygen.rs +++ b/common/nym_offline_divisible_ecash/src/scheme/keygen.rs @@ -351,7 +351,7 @@ impl SecretKeyUser { } } -#[derive(Eq, Debug, PartialEq, Clone)] +#[derive(Hash, Eq, Debug, PartialEq, Clone)] pub struct PublicKeyUser { pub(crate) pk: G1Projective, } diff --git a/common/nym_offline_divisible_ecash/src/scheme/mod.rs b/common/nym_offline_divisible_ecash/src/scheme/mod.rs index 63d6177229..ac8a4d2c90 100644 --- a/common/nym_offline_divisible_ecash/src/scheme/mod.rs +++ b/common/nym_offline_divisible_ecash/src/scheme/mod.rs @@ -111,7 +111,7 @@ impl VarPhi { } } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct PayInfo { pub info: [u8; 32], } @@ -345,7 +345,7 @@ impl Wallet { zk_proof, vv, }; - + self.l.set(self.l() + vv); Ok((pay, self)) } diff --git a/common/nym_offline_divisible_ecash/src/tests/e2e.rs b/common/nym_offline_divisible_ecash/src/tests/e2e.rs index a61fe7dfce..89dfc2b6e4 100644 --- a/common/nym_offline_divisible_ecash/src/tests/e2e.rs +++ b/common/nym_offline_divisible_ecash/src/tests/e2e.rs @@ -9,6 +9,8 @@ use crate::scheme::setup::{GroupParameters, Parameters}; use crate::scheme::withdrawal::{issue, issue_verify, withdrawal_request}; #[test] +// Test wa full end to end flow of withdrawal request, issuance, +// and spending. fn main() -> Result<(), DivisibleEcashError> { // SETUP PHASE let rng = thread_rng();