use crate::scheme::keygen::PublicKeyUser;
use crate::scheme::{compute_pay_info_hash, Payment};
use crate::PayInfo;
#[derive(Debug, Eq, PartialEq)]
pub enum IdentifyResult {
NotADuplicatePayment,
DuplicatePayInfo(PayInfo),
DoubleSpendingPublicKeys(PublicKeyUser),
}
pub fn identify(
payment1: &Payment,
payment2: &Payment,
pay_info1: PayInfo,
pay_info2: PayInfo,
) -> IdentifyResult {
let mut k = 0;
let mut j = 0;
for (id1, pay1_ss) in payment1.ss.iter().enumerate() {
for (id2, pay2_ss) in payment2.ss.iter().enumerate() {
if pay1_ss == pay2_ss {
k = id1;
j = id2;
break;
}
}
}
if payment1
.ss
.iter()
.any(|pay1_ss| payment2.ss.contains(pay1_ss))
{
if pay_info1 == pay_info2 {
IdentifyResult::DuplicatePayInfo(pay_info1)
} else {
let rr_k_payment1 = compute_pay_info_hash(&pay_info1, k as u64);
let rr_j_payment2 = compute_pay_info_hash(&pay_info2, j as u64);
let rr_diff = rr_k_payment1 - rr_j_payment2;
let pk = (payment2.tt[j] * rr_k_payment1 - payment1.tt[k] * rr_j_payment2)
* rr_diff.invert().unwrap();
let pk_user = PublicKeyUser { pk };
IdentifyResult::DoubleSpendingPublicKeys(pk_user)
}
} else {
IdentifyResult::NotADuplicatePayment
}
}
#[cfg(test)]
mod tests {
use crate::scheme::identify::{identify, IdentifyResult};
use crate::scheme::keygen::{PublicKeyUser, SecretKeyAuth, SecretKeyUser};
use crate::setup::Parameters;
use crate::tests::helpers::{
generate_coin_indices_signatures, generate_expiration_date_signatures,
};
use crate::{
aggregate_verification_keys, aggregate_wallets, generate_keypair_user, issue, issue_verify,
ttp_keygen, withdrawal_request, PartialWallet, PayInfo, VerificationKeyAuth,
};
use itertools::izip;
#[test]
fn duplicate_payments_with_the_same_pay_info() {
let total_coins = 32;
let params = Parameters::new(total_coins);
let spend_date = 1701907200; let expiration_date = 1702166400; let t_type = 1;
let user_keypair = generate_keypair_user();
let (req, req_info) =
withdrawal_request(user_keypair.secret_key(), expiration_date, t_type).unwrap();
let authorities_keypairs = ttp_keygen(2, 3).unwrap();
let indices: [u64; 3] = [1, 2, 3];
let secret_keys_authorities: Vec<&SecretKeyAuth> = authorities_keypairs
.iter()
.map(|keypair| keypair.secret_key())
.collect();
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 dates_signatures = generate_expiration_date_signatures(
expiration_date,
&secret_keys_authorities,
&verification_keys_auth,
&verification_key,
&indices,
)
.unwrap();
let coin_indices_signatures = generate_coin_indices_signatures(
¶ms,
&secret_keys_authorities,
&verification_keys_auth,
&verification_key,
&indices,
)
.unwrap();
let mut wallet_blinded_signatures = Vec::new();
for auth_keypair in authorities_keypairs {
let blind_signature = issue(
auth_keypair.secret_key(),
user_keypair.public_key(),
&req,
expiration_date,
t_type,
);
wallet_blinded_signatures.push(blind_signature.unwrap());
}
let unblinded_wallet_shares: Vec<PartialWallet> = izip!(
wallet_blinded_signatures.iter(),
verification_keys_auth.iter()
)
.enumerate()
.map(|(idx, (w, vk))| {
issue_verify(vk, user_keypair.secret_key(), w, &req_info, idx as u64 + 1).unwrap()
})
.collect();
let mut aggr_wallet = aggregate_wallets(
&verification_key,
user_keypair.secret_key(),
&unblinded_wallet_shares,
&req_info,
)
.unwrap();
let pay_info1 = PayInfo {
pay_info_bytes: [6u8; 72],
};
let spend_vv = 1;
let payment1 = aggr_wallet
.spend(
¶ms,
&verification_key,
user_keypair.secret_key(),
&pay_info1,
spend_vv,
&dates_signatures,
&coin_indices_signatures,
spend_date,
)
.unwrap();
assert!(payment1
.spend_verify(&verification_key, &pay_info1, spend_date)
.is_ok());
let payment2 = payment1.clone();
assert!(payment2
.spend_verify(&verification_key, &pay_info1, spend_date)
.is_ok());
let identify_result = identify(&payment1, &payment2, pay_info1, pay_info1);
assert_eq!(identify_result, IdentifyResult::DuplicatePayInfo(pay_info1));
}
#[test]
fn ok_if_two_different_payments() {
let total_coins = 32;
let params = Parameters::new(total_coins);
let spend_date = 1701907200; let expiration_date = 1702166400; let t_type = 1;
let user_keypair = generate_keypair_user();
let (req, req_info) =
withdrawal_request(user_keypair.secret_key(), expiration_date, t_type).unwrap();
let authorities_keypairs = ttp_keygen(2, 3).unwrap();
let indices: [u64; 3] = [1, 2, 3];
let secret_keys_authorities: Vec<&SecretKeyAuth> = authorities_keypairs
.iter()
.map(|keypair| keypair.secret_key())
.collect();
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 dates_signatures = generate_expiration_date_signatures(
expiration_date,
&secret_keys_authorities,
&verification_keys_auth,
&verification_key,
&indices,
)
.unwrap();
let coin_indices_signatures = generate_coin_indices_signatures(
¶ms,
&secret_keys_authorities,
&verification_keys_auth,
&verification_key,
&indices,
)
.unwrap();
let mut wallet_blinded_signatures = Vec::new();
for auth_keypair in authorities_keypairs {
let blind_signature = issue(
auth_keypair.secret_key(),
user_keypair.public_key(),
&req,
expiration_date,
t_type,
);
wallet_blinded_signatures.push(blind_signature.unwrap());
}
let unblinded_wallet_shares: Vec<PartialWallet> = izip!(
wallet_blinded_signatures.iter(),
verification_keys_auth.iter()
)
.enumerate()
.map(|(idx, (w, vk))| {
issue_verify(vk, user_keypair.secret_key(), w, &req_info, idx as u64 + 1).unwrap()
})
.collect();
let mut aggr_wallet = aggregate_wallets(
&verification_key,
user_keypair.secret_key(),
&unblinded_wallet_shares,
&req_info,
)
.unwrap();
let pay_info1 = PayInfo {
pay_info_bytes: [6u8; 72],
};
let spend_vv = 1;
let payment1 = aggr_wallet
.spend(
¶ms,
&verification_key,
user_keypair.secret_key(),
&pay_info1,
spend_vv,
&dates_signatures,
&coin_indices_signatures,
spend_date,
)
.unwrap();
assert!(payment1
.spend_verify(&verification_key, &pay_info1, spend_date)
.is_ok());
let pay_info2 = PayInfo {
pay_info_bytes: [7u8; 72],
};
let payment2 = aggr_wallet
.spend(
¶ms,
&verification_key,
user_keypair.secret_key(),
&pay_info2,
spend_vv,
&dates_signatures,
&coin_indices_signatures,
spend_date,
)
.unwrap();
assert!(payment2
.spend_verify(&verification_key, &pay_info2, spend_date)
.is_ok());
let identify_result = identify(&payment1, &payment2, pay_info1, pay_info2);
assert_eq!(identify_result, IdentifyResult::NotADuplicatePayment);
}
#[test]
fn two_payments_with_one_repeating_serial_number_but_different_pay_info() {
let total_coins = 32;
let params = Parameters::new(total_coins);
let grp = params.grp();
let spend_date = 1701907200; let expiration_date = 1702166400; let t_type = 1;
let user_keypair = generate_keypair_user();
let mut public_keys: Vec<PublicKeyUser> = Default::default();
for _i in 0..50 {
let sk = grp.random_scalar();
let sk_user = SecretKeyUser { sk };
let pk_user = sk_user.public_key();
public_keys.push(pk_user);
}
public_keys.push(user_keypair.public_key());
let (req, req_info) =
withdrawal_request(user_keypair.secret_key(), expiration_date, t_type).unwrap();
let authorities_keypairs = ttp_keygen(2, 3).unwrap();
let indices: [u64; 3] = [1, 2, 3];
let secret_keys_authorities: Vec<&SecretKeyAuth> = authorities_keypairs
.iter()
.map(|keypair| keypair.secret_key())
.collect();
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 dates_signatures = generate_expiration_date_signatures(
expiration_date,
&secret_keys_authorities,
&verification_keys_auth,
&verification_key,
&indices,
)
.unwrap();
let coin_indices_signatures = generate_coin_indices_signatures(
¶ms,
&secret_keys_authorities,
&verification_keys_auth,
&verification_key,
&indices,
)
.unwrap();
let mut wallet_blinded_signatures = Vec::new();
for auth_keypair in authorities_keypairs {
let blind_signature = issue(
auth_keypair.secret_key(),
user_keypair.public_key(),
&req,
expiration_date,
t_type,
);
wallet_blinded_signatures.push(blind_signature.unwrap());
}
let unblinded_wallet_shares: Vec<PartialWallet> = izip!(
wallet_blinded_signatures.iter(),
verification_keys_auth.iter()
)
.enumerate()
.map(|(idx, (w, vk))| {
issue_verify(vk, user_keypair.secret_key(), w, &req_info, idx as u64 + 1).unwrap()
})
.collect();
let mut aggr_wallet = aggregate_wallets(
&verification_key,
user_keypair.secret_key(),
&unblinded_wallet_shares,
&req_info,
)
.unwrap();
let pay_info1 = PayInfo {
pay_info_bytes: [6u8; 72],
};
let spend_vv = 1;
let payment1 = aggr_wallet
.spend(
¶ms,
&verification_key,
user_keypair.secret_key(),
&pay_info1,
spend_vv,
&dates_signatures,
&coin_indices_signatures,
spend_date,
)
.unwrap();
assert!(payment1
.spend_verify(&verification_key, &pay_info1, spend_date)
.is_ok());
aggr_wallet.tickets_spent -= 1;
let pay_info2 = PayInfo {
pay_info_bytes: [7u8; 72],
};
let payment2 = aggr_wallet
.spend(
¶ms,
&verification_key,
user_keypair.secret_key(),
&pay_info2,
spend_vv,
&dates_signatures,
&coin_indices_signatures,
spend_date,
)
.unwrap();
assert!(payment2
.spend_verify(&verification_key, &pay_info2, spend_date)
.is_ok());
let identify_result = identify(&payment1, &payment2, pay_info1, pay_info2);
assert_eq!(
identify_result,
IdentifyResult::DoubleSpendingPublicKeys(user_keypair.public_key())
);
}
#[test]
fn two_payments_with_multiple_repeating_serial_numbers_but_different_pay_info() {
let total_coins = 32;
let params = Parameters::new(total_coins);
let grp = params.grp();
let spend_date = 1701907200; let expiration_date = 1702166400; let t_type = 1;
let user_keypair = generate_keypair_user();
let mut public_keys: Vec<PublicKeyUser> = Default::default();
for _ in 0..50 {
let sk = grp.random_scalar();
let sk_user = SecretKeyUser { sk };
let pk_user = sk_user.public_key();
public_keys.push(pk_user);
}
public_keys.push(user_keypair.public_key());
let (req, req_info) =
withdrawal_request(user_keypair.secret_key(), expiration_date, t_type).unwrap();
let authorities_keypairs = ttp_keygen(2, 3).unwrap();
let indices: [u64; 3] = [1, 2, 3];
let secret_keys_authorities: Vec<&SecretKeyAuth> = authorities_keypairs
.iter()
.map(|keypair| keypair.secret_key())
.collect();
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 dates_signatures = generate_expiration_date_signatures(
expiration_date,
&secret_keys_authorities,
&verification_keys_auth,
&verification_key,
&indices,
)
.unwrap();
let coin_indices_signatures = generate_coin_indices_signatures(
¶ms,
&secret_keys_authorities,
&verification_keys_auth,
&verification_key,
&indices,
)
.unwrap();
let mut wallet_blinded_signatures = Vec::new();
for auth_keypair in authorities_keypairs {
let blind_signature = issue(
auth_keypair.secret_key(),
user_keypair.public_key(),
&req,
expiration_date,
t_type,
);
wallet_blinded_signatures.push(blind_signature.unwrap());
}
let unblinded_wallet_shares: Vec<PartialWallet> = izip!(
wallet_blinded_signatures.iter(),
verification_keys_auth.iter()
)
.enumerate()
.map(|(idx, (w, vk))| {
issue_verify(vk, user_keypair.secret_key(), w, &req_info, idx as u64 + 1).unwrap()
})
.collect();
let mut aggr_wallet = aggregate_wallets(
&verification_key,
user_keypair.secret_key(),
&unblinded_wallet_shares,
&req_info,
)
.unwrap();
let pay_info1 = PayInfo {
pay_info_bytes: [6u8; 72],
};
let spend_vv = 10;
let payment1 = aggr_wallet
.spend(
¶ms,
&verification_key,
user_keypair.secret_key(),
&pay_info1,
spend_vv,
&dates_signatures,
&coin_indices_signatures,
spend_date,
)
.unwrap();
assert!(payment1
.spend_verify(&verification_key, &pay_info1, spend_date)
.is_ok());
aggr_wallet.tickets_spent -= 10;
let pay_info2 = PayInfo {
pay_info_bytes: [7u8; 72],
};
let payment2 = aggr_wallet
.spend(
¶ms,
&verification_key,
user_keypair.secret_key(),
&pay_info2,
spend_vv,
&dates_signatures,
&coin_indices_signatures,
spend_date,
)
.unwrap();
let identify_result = identify(&payment1, &payment2, pay_info1, pay_info2);
assert_eq!(
identify_result,
IdentifyResult::DoubleSpendingPublicKeys(user_keypair.public_key())
);
}
}