Add type attribute

This commit is contained in:
aniampio
2024-07-24 22:41:36 +01:00
committed by Jędrzej Stuczyński
parent 86fe955592
commit 42efff83da
8 changed files with 119 additions and 26 deletions
@@ -5,7 +5,7 @@ use bls12_381::Scalar;
use nym_network_defaults::ecash::TICKETBOOK_VALIDITY_DAYS;
use nym_network_defaults::TICKETBOOK_SIZE;
pub const PUBLIC_ATTRIBUTES_LEN: usize = 1; //expiration date
pub const PUBLIC_ATTRIBUTES_LEN: usize = 2; //expiration date and ticket type
pub const PRIVATE_ATTRIBUTES_LEN: usize = 2; //user and wallet secret
pub const ATTRIBUTES_LEN: usize = PUBLIC_ATTRIBUTES_LEN + PRIVATE_ATTRIBUTES_LEN; // number of attributes encoded in a single zk-nym credential
@@ -11,6 +11,7 @@ use itertools::Itertools;
use crate::common_types::{PartialSignature, Signature, SignatureShare, SignerIndex};
use crate::error::{CompactEcashError, Result};
use crate::scheme::expiration_date_signatures::scalar_date;
use crate::scheme::expiration_date_signatures::scalar_type;
use crate::scheme::keygen::{SecretKeyUser, VerificationKeyAuth};
use crate::scheme::withdrawal::RequestInfo;
use crate::scheme::{PartialWallet, Wallet, WalletSignatures};
@@ -144,17 +145,20 @@ pub fn aggregate_wallets(
sk_user.sk,
*req_info.get_v(),
*req_info.get_expiration_date(),
*req_info.get_t_type(),
];
let aggregated_signature =
aggregate_signature_shares(verification_key, &attributes, &signature_shares)?;
let expiration_date_timestamp = req_info.get_expiration_date();
let t_type = req_info.get_t_type();
Ok(Wallet {
signatures: WalletSignatures {
sig: aggregated_signature,
v: *req_info.get_v(),
expiration_date_timestamp: scalar_date(expiration_date_timestamp),
t_type: scalar_type(t_type),
},
tickets_spent: 0,
})
@@ -411,6 +411,15 @@ pub fn scalar_date(scalar: &Scalar) -> u64 {
u64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]])
}
pub fn type_scalar(t_type: u64) -> Scalar {
Scalar::from(t_type)
}
pub fn scalar_type(scalar: &Scalar) -> u64 {
let b = scalar.to_bytes();
u64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]])
}
#[cfg(test)]
mod tests {
use super::*;
@@ -74,11 +74,12 @@ mod tests {
// NOTE: Make sure that the date timestamp are calculated at 00:00:00!!
let spend_date = 1701907200; // Dec 07 2023 00:00:00
let expiration_date = 1702166400; // Dec 10 2023 00:00:00
let t_type = 1;
let user_keypair = generate_keypair_user();
let (req, req_info) =
withdrawal_request(user_keypair.secret_key(), expiration_date).unwrap();
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
@@ -120,6 +121,7 @@ mod tests {
user_keypair.public_key(),
&req,
expiration_date,
t_type,
);
wallet_blinded_signatures.push(blind_signature.unwrap());
}
@@ -182,11 +184,12 @@ mod tests {
// NOTE: Make sure that the date timestamp are calculated at 00:00:00!!
let spend_date = 1701907200; // Dec 07 2023 00:00:00
let expiration_date = 1702166400; // Dec 10 2023 00:00:00
let t_type = 1;
let user_keypair = generate_keypair_user();
let (req, req_info) =
withdrawal_request(user_keypair.secret_key(), expiration_date).unwrap();
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
@@ -228,6 +231,7 @@ mod tests {
user_keypair.public_key(),
&req,
expiration_date,
t_type,
);
wallet_blinded_signatures.push(blind_signature.unwrap());
}
@@ -306,6 +310,7 @@ mod tests {
// NOTE: Make sure that the date timestamp are calculated at 00:00:00!!
let spend_date = 1701907200; // Dec 07 2023 00:00:00
let expiration_date = 1702166400; // Dec 10 2023 00:00:00
let t_type = 1;
let user_keypair = generate_keypair_user();
@@ -320,7 +325,7 @@ mod tests {
public_keys.push(user_keypair.public_key().clone());
let (req, req_info) =
withdrawal_request(user_keypair.secret_key(), expiration_date).unwrap();
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
@@ -362,6 +367,7 @@ mod tests {
user_keypair.public_key(),
&req,
expiration_date,
t_type,
);
wallet_blinded_signatures.push(blind_signature.unwrap());
}
@@ -447,6 +453,7 @@ mod tests {
// NOTE: Make sure that the date timestamp are calculated at 00:00:00!!
let spend_date = 1701907200; // Dec 07 2023 00:00:00
let expiration_date = 1702166400; // Dec 10 2023 00:00:00
let t_type = 1;
let user_keypair = generate_keypair_user();
@@ -461,7 +468,7 @@ mod tests {
public_keys.push(user_keypair.public_key().clone());
let (req, req_info) =
withdrawal_request(user_keypair.secret_key(), expiration_date).unwrap();
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
@@ -504,6 +511,7 @@ mod tests {
user_keypair.public_key(),
&req,
expiration_date,
t_type,
);
wallet_blinded_signatures.push(blind_signature.unwrap());
}
@@ -41,6 +41,7 @@ pub struct PartialWallet {
v: Scalar,
idx: SignerIndex,
expiration_date: Scalar,
t_type: Scalar,
}
impl PartialWallet {
@@ -54,23 +55,27 @@ impl PartialWallet {
pub fn expiration_date(&self) -> Scalar {
self.expiration_date
}
pub fn t_type(&self) -> Scalar {
self.t_type
}
/// Converts the `PartialWallet` to a fixed-size byte array.
///
/// The resulting byte array has a length of 168 bytes and contains serialized
/// The resulting byte array has a length of 200 bytes and contains serialized
/// representations of the `Signature` (`sig`), scalar value (`v`),
/// expiration date (`expiration_date`), and `idx` fields of the `PartialWallet` struct.
///
/// # Returns
///
/// A fixed-size byte array (`[u8; 168]`) representing the serialized form of the `PartialWallet`.
/// A fixed-size byte array (`[u8; 200]`) representing the serialized form of the `PartialWallet`.
///
pub fn to_bytes(&self) -> [u8; 168] {
let mut bytes = [0u8; 168];
pub fn to_bytes(&self) -> [u8; 200] {
let mut bytes = [0u8; 200];
bytes[0..96].copy_from_slice(&self.sig.to_bytes());
bytes[96..128].copy_from_slice(&self.v.to_bytes());
bytes[128..160].copy_from_slice(&self.expiration_date.to_bytes());
bytes[160..168].copy_from_slice(&self.idx.to_le_bytes());
bytes[160..192].copy_from_slice(&self.t_type.to_bytes());
bytes[192..200].copy_from_slice(&self.idx.to_le_bytes());
bytes
}
@@ -91,9 +96,10 @@ impl PartialWallet {
const SIGNATURE_BYTES: usize = 96;
const V_BYTES: usize = 32;
const EXPIRATION_DATE_BYTES: usize = 32;
const T_TYPE_BYTES: usize = 32;
const IDX_BYTES: usize = 8;
const EXPECTED_LENGTH: usize =
SIGNATURE_BYTES + V_BYTES + EXPIRATION_DATE_BYTES + IDX_BYTES;
SIGNATURE_BYTES + V_BYTES + EXPIRATION_DATE_BYTES + T_TYPE_BYTES + IDX_BYTES;
if bytes.len() != EXPECTED_LENGTH {
return Err(CompactEcashError::DeserializationLengthMismatch {
@@ -119,6 +125,11 @@ impl PartialWallet {
let expiration_date_bytes = bytes[j..j + EXPIRATION_DATE_BYTES].try_into().unwrap();
let expiration_date = try_deserialize_scalar(expiration_date_bytes)?;
j += EXPIRATION_DATE_BYTES;
//SAFETY: slice to array after length check
#[allow(clippy::unwrap_used)]
let t_type_bytes = bytes[j..j + T_TYPE_BYTES].try_into().unwrap();
let t_type = try_deserialize_scalar(t_type_bytes)?;
j += T_TYPE_BYTES;
//SAFETY: slice to array after length check
#[allow(clippy::unwrap_used)]
@@ -130,6 +141,7 @@ impl PartialWallet {
v,
idx,
expiration_date,
t_type,
})
}
}
@@ -258,6 +270,7 @@ pub struct WalletSignatures {
sig: Signature,
v: Scalar,
expiration_date_timestamp: u64,
t_type: u64,
}
impl WalletSignatures {
@@ -301,8 +314,8 @@ pub fn compute_pay_info_hash(pay_info: &PayInfo, k: u64) -> Scalar {
}
impl WalletSignatures {
// signature size (96) + secret size (32) + expiration size (8)
pub const SERIALISED_SIZE: usize = 136;
// signature size (96) + secret size (32) + expiration size (8) + t_type (8)
pub const SERIALISED_SIZE: usize = 144;
pub fn signature(&self) -> &Signature {
&self.sig
@@ -323,6 +336,7 @@ impl WalletSignatures {
bytes[0..96].copy_from_slice(&self.sig.to_bytes());
bytes[96..128].copy_from_slice(&self.v.to_bytes());
bytes[128..136].copy_from_slice(&self.expiration_date_timestamp.to_be_bytes());
bytes[136..144].copy_from_slice(&self.t_type.to_be_bytes());
bytes
}
@@ -342,16 +356,21 @@ impl WalletSignatures {
let v_bytes: &[u8; 32] = &bytes[96..128].try_into().unwrap();
#[allow(clippy::unwrap_used)]
let expiration_date_bytes = bytes[128..].try_into().unwrap();
let expiration_date_bytes = bytes[128..136].try_into().unwrap();
#[allow(clippy::unwrap_used)]
let t_type_bytes = bytes[136..].try_into().unwrap();
let sig = Signature::try_from(sig_bytes.as_slice())?;
let v = Scalar::from_bytes(v_bytes).unwrap();
let expiration_date_timestamp = u64::from_be_bytes(expiration_date_bytes);
let t_type = u64::from_be_bytes(t_type_bytes);
Ok(WalletSignatures {
sig,
v,
expiration_date_timestamp,
t_type,
})
}
@@ -550,6 +569,7 @@ impl WalletSignatures {
aa: aa.clone(),
spend_value,
cc,
t_type: self.t_type,
zk_proof,
};
@@ -669,6 +689,7 @@ pub struct Payment {
pub aa: Vec<G1Projective>,
pub spend_value: u64,
pub cc: G1Projective,
pub t_type: u64,
pub zk_proof: SpendProof,
}
@@ -693,15 +714,16 @@ impl Payment {
/// - The element `h` of the payment signature equals the identity.
/// - The bilinear pairing check for `kappa` fails.
///
pub fn check_signature_validity(&self) -> Result<()> {
pub fn check_signature_validity(&self, verification_key: &VerificationKeyAuth) -> Result<()> {
let params = ecash_group_parameters();
if bool::from(self.sig.h.is_identity()) {
return Err(CompactEcashError::SpendSignaturesValidity);
}
let kappa_type = self.kappa + verification_key.beta_g2[3] * Scalar::from(self.t_type);
if !check_bilinear_pairing(
&self.sig.h.to_affine(),
&G2Prepared::from(self.kappa.to_affine()),
&G2Prepared::from(kappa_type.to_affine()),
&self.sig.s.to_affine(),
params.prepared_miller_g2(),
) {
@@ -935,7 +957,7 @@ impl Payment {
// verify the zk proof
self.verify_spend_proof(verification_key, pay_info)?;
// Verify whether the payment signature and kappa are correct
self.check_signature_validity()?;
self.check_signature_validity(verification_key)?;
// Verify whether the expiration date signature and kappa_e are correct
self.check_exp_signature_validity(verification_key, spend_date)?;
// Verify whether the coin indices signatures and kappa_k are correct
@@ -57,6 +57,7 @@ pub struct RequestInfo {
private_attributes_openings: Vec<Scalar>,
wallet_secret: Scalar,
expiration_date: Scalar,
t_type: Scalar,
}
impl RequestInfo {
@@ -75,6 +76,9 @@ impl RequestInfo {
pub fn get_expiration_date(&self) -> &Scalar {
&self.expiration_date
}
pub fn get_t_type(&self) -> &Scalar {
&self.t_type
}
}
/// Computes Pedersen commitments for private attributes.
@@ -117,6 +121,7 @@ fn compute_private_attribute_commitments(
///
/// * `sk_user` - A reference to the user's secret key.
/// * `expiration_date` - The expiration date for the withdrawal request.
/// * `t_type` - The type of the ticket book
///
/// # Returns
///
@@ -137,6 +142,7 @@ fn compute_private_attribute_commitments(
pub fn withdrawal_request(
sk_user: &SecretKeyUser,
expiration_date: u64,
t_type: u64,
) -> Result<(WithdrawalRequest, RequestInfo)> {
let params = ecash_group_parameters();
// Generate random and unique wallet secret
@@ -152,8 +158,9 @@ pub fn withdrawal_request(
// Compute commitment hash h
#[allow(clippy::unwrap_used)]
let joined_commitment_hash = hash_g1(
(joined_commitment + params.gamma_idx(2).unwrap() * Scalar::from(expiration_date))
.to_bytes(),
(joined_commitment
+ params.gamma_idx(2).unwrap() * Scalar::from(expiration_date)
+ params.gamma_idx(3).unwrap() * Scalar::from(t_type)).to_bytes(),
);
// Compute Pedersen commitments for private attributes (wallet secret and user's secret)
@@ -192,6 +199,7 @@ pub fn withdrawal_request(
private_attributes_openings: private_attributes_openings.clone(),
wallet_secret: v,
expiration_date: Scalar::from(expiration_date),
t_type: Scalar::from(t_type),
},
))
}
@@ -203,7 +211,8 @@ pub fn withdrawal_request(
///
/// * `req` - The withdrawal request to be verified.
/// * `pk_user` - Public key of the user associated with the withdrawal request.
/// * `expiration_date` - Expiration date for the withdrawal request.
/// * `expiration_date` - Expiration date for the ticket book.
/// * `t_type` - The type of the ticket book
///
/// # Returns
///
@@ -213,13 +222,16 @@ pub fn request_verify(
req: &WithdrawalRequest,
pk_user: PublicKeyUser,
expiration_date: u64,
t_type: u64,
) -> Result<()> {
let params = ecash_group_parameters();
// Verify the joined commitment hash
//SAFETY: params is static with length 3
#[allow(clippy::unwrap_used)]
let expected_commitment_hash = hash_g1(
(req.joined_commitment + params.gamma_idx(2).unwrap() * Scalar::from(expiration_date))
(req.joined_commitment
+ params.gamma_idx(2).unwrap() * Scalar::from(expiration_date)
+ params.gamma_idx(3).unwrap() * Scalar::from(t_type))
.to_bytes(),
);
if req.joined_commitment_hash != expected_commitment_hash {
@@ -266,6 +278,32 @@ fn sign_expiration_date(
joined_commitment_hash * (yi * Scalar::from(expiration_date))
}
/// Signs a transaction type using a joined commitment hash and a secret key.
///
/// Given a joined commitment hash (`joined_commitment_hash`), a ticket type (`t_type`),
/// and a secret key for authentication (`sk_auth`), this function computes the signature of the
/// ticket type.
///
/// # Arguments
///
/// * `joined_commitment_hash` - The G1Projective point representing the joined commitment hash.
/// * `t_type` - The ticket type identifier to be signed.
/// * `sk_auth` - The secret key of the signing authority.
///
/// # Returns
///
/// The resulting G1Projective point representing the signed ticket type.
fn sign_t_type(
joined_commitment_hash: &G1Projective,
t_type: u64,
sk_auth: &SecretKeyAuth,
) -> G1Projective {
//SAFETY : this fn assumes a long enough key
#[allow(clippy::unwrap_used)]
let yi = sk_auth.get_y_by_idx(3).unwrap();
joined_commitment_hash * (yi * Scalar::from(t_type))
}
/// Issues a blinded signature for a withdrawal request, after verifying its integrity.
///
/// This function first verifies the withdrawal request using the provided group parameters,
@@ -289,9 +327,10 @@ pub fn issue(
pk_user: PublicKeyUser,
withdrawal_req: &WithdrawalRequest,
expiration_date: u64,
t_type: u64,
) -> Result<BlindedSignature> {
// Verify the withdrawal request
request_verify(withdrawal_req, pk_user, expiration_date)?;
request_verify(withdrawal_req, pk_user, expiration_date, t_type)?;
// Verify `sk_auth` is long enough
if sk_auth.ys.len() < constants::ATTRIBUTES_LEN {
return Err(CompactEcashError::KeyTooShort);
@@ -310,9 +349,15 @@ pub fn issue(
expiration_date,
sk_auth,
);
// Sign the type
let t_type_sign = sign_t_type(
&withdrawal_req.joined_commitment_hash,
t_type,
sk_auth,
);
// Combine both signatures
let signature =
blind_signatures + withdrawal_req.joined_commitment_hash * sk_auth.x + expiration_date_sign;
blind_signatures + withdrawal_req.joined_commitment_hash * sk_auth.x + expiration_date_sign + t_type_sign;
Ok(BlindedSignature {
h: withdrawal_req.joined_commitment_hash,
@@ -361,7 +406,7 @@ pub fn issue_verify(
.sum::<G1Projective>();
let unblinded_c = blind_signature.c - blinding_removers;
let attr = [sk_user.sk, req_info.wallet_secret, req_info.expiration_date];
let attr = [sk_user.sk, req_info.wallet_secret, req_info.expiration_date, req_info.t_type];
let signed_attributes = attr
.iter()
@@ -387,6 +432,7 @@ pub fn issue_verify(
v: req_info.wallet_secret,
idx: signer_index,
expiration_date: req_info.expiration_date,
t_type: req_info.t_type,
})
}
@@ -24,6 +24,7 @@ mod tests {
// NOTE: Make sure that the date timestamp are calculated at 00:00:00!!
let spend_date = 1701907200; // Dec 07 2023 00:00:00
let expiration_date = 1702166400; // Dec 10 2023 00:00:00
let t_type = 1;
let user_keypair = generate_keypair_user();
// generate authorities keys
@@ -61,7 +62,7 @@ mod tests {
// request a wallet
let (req, req_info) =
withdrawal_request(user_keypair.secret_key(), expiration_date).unwrap();
withdrawal_request(user_keypair.secret_key(), expiration_date, t_type).unwrap();
let req_bytes = req.to_bytes();
let req2 = WithdrawalRequest::try_from(req_bytes.as_slice()).unwrap();
assert_eq!(req, req2);
@@ -74,6 +75,7 @@ mod tests {
user_keypair.public_key(),
&req,
expiration_date,
t_type,
);
wallet_blinded_signatures.push(blind_signature.unwrap());
}
@@ -83,6 +83,7 @@ pub fn payment_from_keys_and_expiration_date(
ecash_keypairs: &Vec<KeyPairAuth>,
indices: &[SignerIndex],
expiration_date: u64,
t_type: u64,
) -> Result<(Payment, PayInfo)> {
let total_coins = 32;
let params = Parameters::new(total_coins);
@@ -121,7 +122,7 @@ pub fn payment_from_keys_and_expiration_date(
//SAFETY : method intended for test only
#[allow(clippy::unwrap_used)]
// request a wallet
let (req, req_info) = withdrawal_request(user_keypair.secret_key(), expiration_date).unwrap();
let (req, req_info) = withdrawal_request(user_keypair.secret_key(), expiration_date, t_type).unwrap();
// generate blinded signatures
let mut wallet_blinded_signatures = Vec::new();
@@ -132,6 +133,7 @@ pub fn payment_from_keys_and_expiration_date(
user_keypair.public_key(),
&req,
expiration_date,
t_type,
)?;
wallet_blinded_signatures.push(blinded_signature)
}