From 7fcd246089433253be1f7500deee5a8a07b2e970 Mon Sep 17 00:00:00 2001 From: aniampio Date: Fri, 25 Mar 2022 13:25:33 +0000 Subject: [PATCH] Start the spend function and zkproof for spend --- common/nym-compact-ecash/src/error.rs | 3 + common/nym-compact-ecash/src/proofs/mod.rs | 324 +----------------- .../src/proofs/proof_spend.rs | 40 +++ .../src/proofs/proof_withdrawal.rs | 319 +++++++++++++++++ common/nym-compact-ecash/src/scheme/mod.rs | 1 + common/nym-compact-ecash/src/scheme/setup.rs | 18 +- common/nym-compact-ecash/src/scheme/spend.rs | 102 +++++- .../src/scheme/withdrawal.rs | 2 +- common/nymcoconut/src/scheme/verification.rs | 24 +- 9 files changed, 498 insertions(+), 335 deletions(-) create mode 100644 common/nym-compact-ecash/src/proofs/proof_spend.rs create mode 100644 common/nym-compact-ecash/src/proofs/proof_withdrawal.rs diff --git a/common/nym-compact-ecash/src/error.rs b/common/nym-compact-ecash/src/error.rs index 6a63548a91..04ce046cbd 100644 --- a/common/nym-compact-ecash/src/error.rs +++ b/common/nym-compact-ecash/src/error.rs @@ -22,6 +22,9 @@ pub enum CompactEcashError { #[error("Issuance Verification related error: {0}")] IssuanceVfy(String), + #[error("Spend Verification related error: {0}")] + Spend(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-compact-ecash/src/proofs/mod.rs b/common/nym-compact-ecash/src/proofs/mod.rs index 1efe6199ec..76b273dae1 100644 --- a/common/nym-compact-ecash/src/proofs/mod.rs +++ b/common/nym-compact-ecash/src/proofs/mod.rs @@ -3,10 +3,9 @@ use std::convert::TryFrom; use std::convert::TryInto; use bls12_381::{G1Affine, G1Projective, Scalar}; -use digest::generic_array::typenum::Unsigned; use digest::Digest; +use digest::generic_array::typenum::Unsigned; use group::GroupEncoding; -use itertools::izip; use sha2::Sha256; use crate::error::{CompactEcashError, Result}; @@ -14,14 +13,17 @@ use crate::scheme::keygen::PublicKeyUser; use crate::scheme::setup::Parameters; use crate::utils::try_deserialize_g1_projective; +pub mod proof_withdrawal; +pub mod proof_spend; + type ChallengeDigest = Sha256; /// Generates a Scalar [or Fp] challenge by hashing a number of elliptic curve points. fn compute_challenge(iter: I) -> Scalar -where - D: Digest, - I: Iterator, - B: AsRef<[u8]>, + where + D: Digest, + I: Iterator, + B: AsRef<[u8]>, { let mut h = D::new(); for point_representation in iter { @@ -49,8 +51,8 @@ fn produce_response(witness_replacement: &Scalar, challenge: &Scalar, secret: &S // note: it's caller's responsibility to ensure witnesses.len() = secrets.len() fn produce_responses(witnesses: &[Scalar], challenge: &Scalar, secrets: &[S]) -> Vec -where - S: Borrow, + where + S: Borrow, { debug_assert_eq!(witnesses.len(), secrets.len()); @@ -61,310 +63,4 @@ where .collect() } -#[derive(Debug)] -#[cfg_attr(test, derive(PartialEq))] -// instance: g, gamma1, gamma2, gamma3, com, h, com1, com2, com3, pkUser -pub struct WithdrawalReqInstance { - // Joined commitment to all attributes - pub com: G1Projective, - // Hash of the joined commitment com - pub h: G1Projective, - // Pedersen commitments to each attribute - pub pc_coms: Vec, - // Public key of a user - pub pk_user: PublicKeyUser, -} -impl TryFrom<&[u8]> for WithdrawalReqInstance { - type Error = CompactEcashError; - - fn try_from(bytes: &[u8]) -> Result { - if bytes.len() < 48 * 4 + 8 || (bytes.len() - 8) % 48 != 0 { - return Err(CompactEcashError::DeserializationInvalidLength { - actual: bytes.len(), - modulus_target: bytes.len() - 8, - target: 48 * 4 + 8, - modulus: 48, - object: "secret key".to_string(), - }); - } - let com_bytes: [u8; 48] = bytes[..48].try_into().unwrap(); - let com = try_deserialize_g1_projective( - &com_bytes, - CompactEcashError::Deserialization("Failed to deserialize com".to_string()), - )?; - let h_bytes: [u8; 48] = bytes[48..96].try_into().unwrap(); - let h = try_deserialize_g1_projective( - &h_bytes, - CompactEcashError::Deserialization("Failed to deserialize h".to_string()), - )?; - let pc_coms_len = u64::from_le_bytes(bytes[96..104].try_into().unwrap()); - let actual_pc_coms_len = (bytes.len() - 152) / 48; - if pc_coms_len as usize != actual_pc_coms_len { - return Err(CompactEcashError::Deserialization(format!( - "Tried to deserialize pedersen commitments with inconsistent pc_coms_len (expected {}, got {})", - pc_coms_len, actual_pc_coms_len - ))); - } - let mut pc_coms = Vec::new(); - let mut pc_coms_end: usize = 0; - for i in 0..pc_coms_len { - let start = (104 + i * 48) as usize; - let end = (start + 48) as usize; - let pc_i_bytes = bytes[start..end].try_into().unwrap(); - let pc_i = try_deserialize_g1_projective( - &pc_i_bytes, - CompactEcashError::Deserialization( - "Failed to deserialize pedersen commitment".to_string(), - ), - )?; - pc_coms_end = end; - pc_coms.push(pc_i); - } - let pk_bytes = bytes[pc_coms_end..].try_into().unwrap(); - let pk = try_deserialize_g1_projective( - &pk_bytes, - CompactEcashError::Deserialization( - "Failed to deserialize user's public key".to_string(), - ), - )?; - - Ok(WithdrawalReqInstance { - com, - h, - pc_coms, - pk_user: PublicKeyUser { pk }, - }) - } -} - -impl WithdrawalReqInstance { - pub(crate) fn to_bytes(&self) -> Vec { - let pc_coms_len = self.pc_coms.len(); - let mut bytes = Vec::with_capacity(8 + (pc_coms_len + 3) as usize * 48); - bytes.extend_from_slice(self.com.to_bytes().as_ref()); - bytes.extend_from_slice(self.h.to_bytes().as_ref()); - bytes.extend_from_slice(&pc_coms_len.to_le_bytes()); - for pc in self.pc_coms.iter() { - bytes.extend_from_slice((pc.to_bytes()).as_ref()); - } - bytes.extend_from_slice(self.pk_user.pk.to_bytes().as_ref()); - bytes - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - WithdrawalReqInstance::try_from(bytes) - } -} - -// witness: m1, m2, m3, o, o1, o2, o3, -pub struct WithdrawalReqWitness { - pub attributes: Vec, - // Opening for the joined commitment com - pub com_opening: Scalar, - // Openings for the pedersen commitments - pub pc_coms_openings: Vec, -} - -pub struct WithdrawalReqProof { - challenge: Scalar, - response_opening: Scalar, - response_openings: Vec, - response_attributes: Vec, -} - -impl WithdrawalReqProof { - pub(crate) fn construct( - params: &Parameters, - instance: &WithdrawalReqInstance, - witness: &WithdrawalReqWitness, - ) -> Self { - // generate random values to replace the witnesses - let r_com_opening = params.random_scalar(); - let r_pedcom_openings = params.n_random_scalars(witness.pc_coms_openings.len()); - let r_attributes = params.n_random_scalars(witness.attributes.len()); - - // compute zkp commitments for each instance - let zkcm_com = params.gen1() * r_com_opening - + r_attributes - .iter() - .zip(params.gammas().iter()) - .map(|(rm_i, gamma_i)| gamma_i * rm_i) - .sum::(); - - let zkcm_pedcom = r_pedcom_openings - .iter() - .zip(r_attributes.iter()) - .map(|(o_j, m_j)| params.gen1() * o_j + instance.h * m_j) - .collect::>(); - - let zkcm_user_sk = params.gen1() * r_attributes[0]; - - // covert to bytes - let gammas_bytes = params - .gammas() - .iter() - .map(|gamma| gamma.to_bytes()) - .collect::>(); - - let zkcm_pedcom_bytes = zkcm_pedcom - .iter() - .map(|cm| cm.to_bytes()) - .collect::>(); - - // compute zkp challenge using g1, gammas, c, h, c1, c2, c3, zk commitments - let challenge = compute_challenge::( - std::iter::once(params.gen1().to_bytes().as_ref()) - .chain(gammas_bytes.iter().map(|gamma| gamma.as_ref())) - .chain(std::iter::once(instance.to_bytes().as_ref())) - .chain(std::iter::once(zkcm_com.to_bytes().as_ref())) - .chain(zkcm_pedcom_bytes.iter().map(|c| c.as_ref())) - .chain(std::iter::once(zkcm_user_sk.to_bytes().as_ref())), - ); - - // compute response - let response_opening = produce_response(&r_com_opening, &challenge, &witness.com_opening); - let response_openings = produce_responses( - &r_pedcom_openings, - &challenge, - &witness.pc_coms_openings.iter().collect::>(), - ); - let response_attributes = produce_responses( - &r_attributes, - &challenge, - &witness.attributes.iter().collect::>(), - ); - - WithdrawalReqProof { - challenge, - response_opening, - response_openings, - response_attributes, - } - } - - pub(crate) fn verify(&self, params: &Parameters, instance: &WithdrawalReqInstance) -> bool { - // recompute zk commitments for each instance - let zkcm_com = instance.com * self.challenge - + params.gen1() * self.response_opening - + self - .response_attributes - .iter() - .zip(params.gammas().iter()) - .map(|(m_i, gamma_i)| gamma_i * m_i) - .sum::(); - - let zkcm_pedcom = izip!( - instance.pc_coms.iter(), - self.response_openings.iter(), - self.response_attributes.iter() - ) - .map(|(cm_j, resp_o_j, resp_m_j)| { - cm_j * self.challenge + params.gen1() * resp_o_j + instance.h * resp_m_j - }) - .collect::>(); - - let zk_commitment_user_sk = - instance.pk_user.pk * self.challenge + params.gen1() * self.response_attributes[0]; - - // covert to bytes - let gammas_bytes = params - .gammas() - .iter() - .map(|gamma| gamma.to_bytes()) - .collect::>(); - - let zkcm_pedcom_bytes = zkcm_pedcom - .iter() - .map(|cm| cm.to_bytes()) - .collect::>(); - - // recompute zkp challenge - let challenge = compute_challenge::( - std::iter::once(params.gen1().to_bytes().as_ref()) - .chain(gammas_bytes.iter().map(|hs| hs.as_ref())) - .chain(std::iter::once(instance.to_bytes().as_ref())) - .chain(std::iter::once(zkcm_com.to_bytes().as_ref())) - .chain(zkcm_pedcom_bytes.iter().map(|c| c.as_ref())) - .chain(std::iter::once(zk_commitment_user_sk.to_bytes().as_ref())), - ); - - challenge == self.challenge - } -} - -#[cfg(test)] -mod tests { - use group::Group; - use rand::thread_rng; - - use crate::utils::hash_g1; - - use super::*; - - #[test] - fn withdrawal_request_instance_roundtrip() { - let mut rng = thread_rng(); - let params = Parameters::new().unwrap(); - let instance = WithdrawalReqInstance { - com: G1Projective::random(&mut rng), - h: G1Projective::random(&mut rng), - pc_coms: vec![ - G1Projective::random(&mut rng), - G1Projective::random(&mut rng), - G1Projective::random(&mut rng), - ], - pk_user: PublicKeyUser { - pk: params.gen1() * params.random_scalar(), - }, - }; - - let instance_bytes = instance.to_bytes(); - let instance_p = WithdrawalReqInstance::from_bytes(&instance_bytes).unwrap(); - assert_eq!(instance, instance_p) - } - - #[test] - fn withdrawal_proof_construct_and_verify() { - let mut rng = thread_rng(); - let params = Parameters::new().unwrap(); - let sk = params.random_scalar(); - let pk_user = PublicKeyUser { - pk: params.gen1() * sk, - }; - let v = params.random_scalar(); - let t = params.random_scalar(); - let attr = vec![sk, v, t]; - - let com_opening = params.random_scalar(); - let com = params.gen1() * com_opening - + attr - .iter() - .zip(params.gammas()) - .map(|(&m, gamma)| gamma * m) - .sum::(); - let h = hash_g1(com.to_bytes()); - - let pc_openings = params.n_random_scalars(attr.len()); - let pc_coms = pc_openings - .iter() - .zip(attr.iter()) - .map(|(o_j, m_j)| params.gen1() * o_j + h * m_j) - .collect::>(); - - let instance = WithdrawalReqInstance { - com, - h, - pc_coms, - pk_user, - }; - - let witness = WithdrawalReqWitness { - attributes: attr, - com_opening, - pc_coms_openings: pc_openings, - }; - let zk_proof = WithdrawalReqProof::construct(¶ms, &instance, &witness); - assert!(zk_proof.verify(¶ms, &instance)) - } -} diff --git a/common/nym-compact-ecash/src/proofs/proof_spend.rs b/common/nym-compact-ecash/src/proofs/proof_spend.rs new file mode 100644 index 0000000000..f6b9a44d09 --- /dev/null +++ b/common/nym-compact-ecash/src/proofs/proof_spend.rs @@ -0,0 +1,40 @@ +use bls12_381::{G1Projective, G2Projective, Scalar}; + +use crate::scheme::keygen::SecretKeyUser; +use crate::scheme::setup::Parameters; + +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] +pub struct SpendInstance { + pub kappa: G2Projective, + pub A: G1Projective, + pub C: G1Projective, + pub D: G1Projective, + pub S: G1Projective, + pub T: G1Projective, +} + +pub struct SpendWitness { + // includes skUser, v, t + pub attributes: Vec, + // signature randomizing element + pub r: Scalar, + pub l: u64, + pub o_a: Scalar, + pub o_c: Scalar, + pub o_d: Scalar, + pub mu: Scalar, + pub lambda: Scalar, + pub o_mu: Scalar, + pub o_lambda: Scalar, + +} + +pub struct SpendProof {} + +impl SpendProof { + pub fn construct(params: &Parameters, + instance: &SpendInstance, + witness: &SpendWitness, ) {} + pub fn verify() {} +} \ No newline at end of file diff --git a/common/nym-compact-ecash/src/proofs/proof_withdrawal.rs b/common/nym-compact-ecash/src/proofs/proof_withdrawal.rs new file mode 100644 index 0000000000..d0a61d032b --- /dev/null +++ b/common/nym-compact-ecash/src/proofs/proof_withdrawal.rs @@ -0,0 +1,319 @@ +use std::convert::{TryFrom, TryInto}; + +use bls12_381::{G1Projective, Scalar}; +use group::GroupEncoding; +use itertools::izip; + +use crate::error::{CompactEcashError, Result}; +use crate::proofs::{ChallengeDigest, compute_challenge, produce_response, produce_responses}; +use crate::scheme::keygen::PublicKeyUser; +use crate::scheme::setup::Parameters; +use crate::utils::try_deserialize_g1_projective; + +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] +// instance: g, gamma1, gamma2, gamma3, com, h, com1, com2, com3, pkUser +pub struct WithdrawalReqInstance { + // Joined commitment to all attributes + pub com: G1Projective, + // Hash of the joined commitment com + pub h: G1Projective, + // Pedersen commitments to each attribute + pub pc_coms: Vec, + // Public key of a user + pub pk_user: PublicKeyUser, +} + +impl TryFrom<&[u8]> for WithdrawalReqInstance { + type Error = CompactEcashError; + + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() < 48 * 4 + 8 || (bytes.len() - 8) % 48 != 0 { + return Err(CompactEcashError::DeserializationInvalidLength { + actual: bytes.len(), + modulus_target: bytes.len() - 8, + target: 48 * 4 + 8, + modulus: 48, + object: "secret key".to_string(), + }); + } + let com_bytes: [u8; 48] = bytes[..48].try_into().unwrap(); + let com = try_deserialize_g1_projective( + &com_bytes, + CompactEcashError::Deserialization("Failed to deserialize com".to_string()), + )?; + let h_bytes: [u8; 48] = bytes[48..96].try_into().unwrap(); + let h = try_deserialize_g1_projective( + &h_bytes, + CompactEcashError::Deserialization("Failed to deserialize h".to_string()), + )?; + let pc_coms_len = u64::from_le_bytes(bytes[96..104].try_into().unwrap()); + let actual_pc_coms_len = (bytes.len() - 152) / 48; + if pc_coms_len as usize != actual_pc_coms_len { + return Err(CompactEcashError::Deserialization(format!( + "Tried to deserialize pedersen commitments with inconsistent pc_coms_len (expected {}, got {})", + pc_coms_len, actual_pc_coms_len + ))); + } + let mut pc_coms = Vec::new(); + let mut pc_coms_end: usize = 0; + for i in 0..pc_coms_len { + let start = (104 + i * 48) as usize; + let end = (start + 48) as usize; + let pc_i_bytes = bytes[start..end].try_into().unwrap(); + let pc_i = try_deserialize_g1_projective( + &pc_i_bytes, + CompactEcashError::Deserialization( + "Failed to deserialize pedersen commitment".to_string(), + ), + )?; + pc_coms_end = end; + pc_coms.push(pc_i); + } + let pk_bytes = bytes[pc_coms_end..].try_into().unwrap(); + let pk = try_deserialize_g1_projective( + &pk_bytes, + CompactEcashError::Deserialization( + "Failed to deserialize user's public key".to_string(), + ), + )?; + + Ok(WithdrawalReqInstance { + com, + h, + pc_coms, + pk_user: PublicKeyUser { pk }, + }) + } +} + +impl WithdrawalReqInstance { + pub(crate) fn to_bytes(&self) -> Vec { + let pc_coms_len = self.pc_coms.len(); + let mut bytes = Vec::with_capacity(8 + (pc_coms_len + 3) as usize * 48); + bytes.extend_from_slice(self.com.to_bytes().as_ref()); + bytes.extend_from_slice(self.h.to_bytes().as_ref()); + bytes.extend_from_slice(&pc_coms_len.to_le_bytes()); + for pc in self.pc_coms.iter() { + bytes.extend_from_slice((pc.to_bytes()).as_ref()); + } + bytes.extend_from_slice(self.pk_user.pk.to_bytes().as_ref()); + bytes + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + WithdrawalReqInstance::try_from(bytes) + } +} + +// witness: m1, m2, m3, o, o1, o2, o3, +pub struct WithdrawalReqWitness { + pub attributes: Vec, + // Opening for the joined commitment com + pub com_opening: Scalar, + // Openings for the pedersen commitments + pub pc_coms_openings: Vec, +} + +pub struct WithdrawalReqProof { + challenge: Scalar, + response_opening: Scalar, + response_openings: Vec, + response_attributes: Vec, +} + +impl WithdrawalReqProof { + pub(crate) fn construct( + params: &Parameters, + instance: &WithdrawalReqInstance, + witness: &WithdrawalReqWitness, + ) -> Self { + // generate random values to replace the witnesses + let r_com_opening = params.random_scalar(); + let r_pedcom_openings = params.n_random_scalars(witness.pc_coms_openings.len()); + let r_attributes = params.n_random_scalars(witness.attributes.len()); + + // compute zkp commitments for each instance + let zkcm_com = params.gen1() * r_com_opening + + r_attributes + .iter() + .zip(params.gammas().iter()) + .map(|(rm_i, gamma_i)| gamma_i * rm_i) + .sum::(); + + let zkcm_pedcom = r_pedcom_openings + .iter() + .zip(r_attributes.iter()) + .map(|(o_j, m_j)| params.gen1() * o_j + instance.h * m_j) + .collect::>(); + + let zkcm_user_sk = params.gen1() * r_attributes[0]; + + // covert to bytes + let gammas_bytes = params + .gammas() + .iter() + .map(|gamma| gamma.to_bytes()) + .collect::>(); + + let zkcm_pedcom_bytes = zkcm_pedcom + .iter() + .map(|cm| cm.to_bytes()) + .collect::>(); + + // compute zkp challenge using g1, gammas, c, h, c1, c2, c3, zk commitments + let challenge = compute_challenge::( + std::iter::once(params.gen1().to_bytes().as_ref()) + .chain(gammas_bytes.iter().map(|gamma| gamma.as_ref())) + .chain(std::iter::once(instance.to_bytes().as_ref())) + .chain(std::iter::once(zkcm_com.to_bytes().as_ref())) + .chain(zkcm_pedcom_bytes.iter().map(|c| c.as_ref())) + .chain(std::iter::once(zkcm_user_sk.to_bytes().as_ref())), + ); + + // compute response + let response_opening = produce_response(&r_com_opening, &challenge, &witness.com_opening); + let response_openings = produce_responses( + &r_pedcom_openings, + &challenge, + &witness.pc_coms_openings.iter().collect::>(), + ); + let response_attributes = produce_responses( + &r_attributes, + &challenge, + &witness.attributes.iter().collect::>(), + ); + + WithdrawalReqProof { + challenge, + response_opening, + response_openings, + response_attributes, + } + } + + pub(crate) fn verify(&self, params: &Parameters, instance: &WithdrawalReqInstance) -> bool { + // recompute zk commitments for each instance + let zkcm_com = instance.com * self.challenge + + params.gen1() * self.response_opening + + self + .response_attributes + .iter() + .zip(params.gammas().iter()) + .map(|(m_i, gamma_i)| gamma_i * m_i) + .sum::(); + + let zkcm_pedcom = izip!( + instance.pc_coms.iter(), + self.response_openings.iter(), + self.response_attributes.iter() + ) + .map(|(cm_j, resp_o_j, resp_m_j)| { + cm_j * self.challenge + params.gen1() * resp_o_j + instance.h * resp_m_j + }) + .collect::>(); + + let zk_commitment_user_sk = + instance.pk_user.pk * self.challenge + params.gen1() * self.response_attributes[0]; + + // covert to bytes + let gammas_bytes = params + .gammas() + .iter() + .map(|gamma| gamma.to_bytes()) + .collect::>(); + + let zkcm_pedcom_bytes = zkcm_pedcom + .iter() + .map(|cm| cm.to_bytes()) + .collect::>(); + + // recompute zkp challenge + let challenge = compute_challenge::( + std::iter::once(params.gen1().to_bytes().as_ref()) + .chain(gammas_bytes.iter().map(|hs| hs.as_ref())) + .chain(std::iter::once(instance.to_bytes().as_ref())) + .chain(std::iter::once(zkcm_com.to_bytes().as_ref())) + .chain(zkcm_pedcom_bytes.iter().map(|c| c.as_ref())) + .chain(std::iter::once(zk_commitment_user_sk.to_bytes().as_ref())), + ); + + challenge == self.challenge + } +} + +#[cfg(test)] +mod tests { + use group::Group; + use rand::thread_rng; + + use crate::utils::hash_g1; + + use super::*; + + #[test] + fn withdrawal_request_instance_roundtrip() { + let mut rng = thread_rng(); + let params = Parameters::new().unwrap(); + let instance = WithdrawalReqInstance { + com: G1Projective::random(&mut rng), + h: G1Projective::random(&mut rng), + pc_coms: vec![ + G1Projective::random(&mut rng), + G1Projective::random(&mut rng), + G1Projective::random(&mut rng), + ], + pk_user: PublicKeyUser { + pk: params.gen1() * params.random_scalar(), + }, + }; + + let instance_bytes = instance.to_bytes(); + let instance_p = WithdrawalReqInstance::from_bytes(&instance_bytes).unwrap(); + assert_eq!(instance, instance_p) + } + + #[test] + fn withdrawal_proof_construct_and_verify() { + let rng = thread_rng(); + let params = Parameters::new().unwrap(); + let sk = params.random_scalar(); + let pk_user = PublicKeyUser { + pk: params.gen1() * sk, + }; + let v = params.random_scalar(); + let t = params.random_scalar(); + let attr = vec![sk, v, t]; + + let com_opening = params.random_scalar(); + let com = params.gen1() * com_opening + + attr + .iter() + .zip(params.gammas()) + .map(|(&m, gamma)| gamma * m) + .sum::(); + let h = hash_g1(com.to_bytes()); + + let pc_openings = params.n_random_scalars(attr.len()); + let pc_coms = pc_openings + .iter() + .zip(attr.iter()) + .map(|(o_j, m_j)| params.gen1() * o_j + h * m_j) + .collect::>(); + + let instance = WithdrawalReqInstance { + com, + h, + pc_coms, + pk_user, + }; + + let witness = WithdrawalReqWitness { + attributes: attr, + com_opening, + pc_coms_openings: pc_openings, + }; + let zk_proof = WithdrawalReqProof::construct(¶ms, &instance, &witness); + assert!(zk_proof.verify(¶ms, &instance)) + } +} \ No newline at end of file diff --git a/common/nym-compact-ecash/src/scheme/mod.rs b/common/nym-compact-ecash/src/scheme/mod.rs index 0c5f6bed92..2d68770d2f 100644 --- a/common/nym-compact-ecash/src/scheme/mod.rs +++ b/common/nym-compact-ecash/src/scheme/mod.rs @@ -144,6 +144,7 @@ impl Wallet { pub fn signature(&self) -> &Signature { &self.sig } pub fn v(&self) -> Scalar { self.v } pub fn t(&self) -> Scalar { self.t } + pub fn l(&self) -> u64 { self.l.get() } fn up(&self) { self.l.set(self.l.get() + 1); } diff --git a/common/nym-compact-ecash/src/scheme/setup.rs b/common/nym-compact-ecash/src/scheme/setup.rs index d9219e6c40..7d76a87aab 100644 --- a/common/nym-compact-ecash/src/scheme/setup.rs +++ b/common/nym-compact-ecash/src/scheme/setup.rs @@ -7,7 +7,7 @@ use crate::error::Result; use crate::utils::hash_g1; const ATTRIBUTES_LEN: usize = 3; -const MAX_COIN_VALUE: usize = 32; +const MAX_COIN_VALUE: u64 = 32; pub struct Parameters { /// Generator of the G1 group @@ -15,9 +15,9 @@ pub struct Parameters { /// Generator of the G2 group g2: G2Affine, /// Additional generators of the G1 group - gammas: Vec, + gammas: Vec, /// Value of wallet - L: usize, + L: u64, /// Precomputed G2 generator used for the miller loop _g2_prepared_miller: G2Prepared, } @@ -25,7 +25,7 @@ pub struct Parameters { impl Parameters { pub fn new() -> Result { let gammas = (1..=ATTRIBUTES_LEN) - .map(|i| hash_g1(format!("gamma{}", i)).to_affine()) + .map(|i| hash_g1(format!("gamma{}", i))) .collect(); Ok(Parameters { g1: G1Affine::generator(), @@ -44,10 +44,18 @@ impl Parameters { &self.g2 } - pub(crate) fn gammas(&self) -> &Vec { + pub(crate) fn gammas(&self) -> &Vec { &self.gammas } + pub(crate) fn gamma1(&self) -> Option<&G1Projective> { self.gammas.get(1) } + + pub(crate) fn gamma2(&self) -> Option<&G1Projective> { self.gammas.get(2) } + + pub(crate) fn gamma3(&self) -> Option<&G1Projective> { self.gammas.get(3) } + + pub(crate) fn L(&self) -> u64 { self.L } + pub fn random_scalar(&self) -> Scalar { // lazily-initialized thread-local random number generator, seeded by the system let mut rng = thread_rng(); diff --git a/common/nym-compact-ecash/src/scheme/spend.rs b/common/nym-compact-ecash/src/scheme/spend.rs index ed6da623be..4071c4dabc 100644 --- a/common/nym-compact-ecash/src/scheme/spend.rs +++ b/common/nym-compact-ecash/src/scheme/spend.rs @@ -1,10 +1,106 @@ +use std::convert::TryInto; + +use bls12_381::{G1Projective, G2Projective, Scalar}; + +use crate::Attribute; +use crate::error::{CompactEcashError, Result}; +use crate::proofs::proof_spend::{SpendInstance, SpendProof, SpendWitness}; use crate::scheme::keygen::{SecretKeyUser, VerificationKeyAuth}; +use crate::scheme::setup::Parameters; use crate::scheme::Wallet; +use crate::utils::hash_to_scalar; -pub struct PayInfo {} +pub struct PayInfo { + info: [u8; 32], +} -pub fn spend(wallet: &Wallet, verification_key: &VerificationKeyAuth, skUser: &SecretKeyUser, payInfo: &PayInfo) { - // +pub fn pseudorandom_fgv(params: &Parameters, v: Scalar, l: u64) -> G1Projective { + let pow = (v + Scalar::from(l) + Scalar::from(1)).neg(); + params.gen1() * pow +} + +pub fn pseudorandom_fgt(params: &Parameters, t: Scalar, l: u64) -> G1Projective { + let pow = (t + Scalar::from(l) + Scalar::from(1)).neg(); + params.gen1() * pow +} + +pub fn compute_kappa( + params: &Parameters, + verification_key: &VerificationKeyAuth, + attributes: &[Attribute], + blinding_factor: Scalar, +) -> G2Projective { + params.gen2() * blinding_factor + + verification_key.alpha + + attributes + .iter() + .zip(verification_key.beta_g2.iter()) + .map(|(priv_attr, beta_i)| beta_i * priv_attr) + .sum::() +} + +pub fn spend(params: &Parameters, wallet: &Wallet, verification_key: &VerificationKeyAuth, skUser: &SecretKeyUser, payInfo: &PayInfo) -> Result<()> { + if wallet.l() > params.L() { + return Err(CompactEcashError::Spend( + "The counter l is higher than max L".to_string(), + )); + } + + // randomize signature in the wallet + let (signature_prime, sign_blinding_factor) = wallet.signature().randomise(params); + // construct kappa i.e., blinded attributes for show + let attributes = vec![skUser.sk, wallet.v(), wallet.t()]; + let kappa = compute_kappa(¶ms, &verification_key, &attributes, sign_blinding_factor); + + // pick random openings o_a, o_c, o_d + let o_a = params.random_scalar(); + let o_c = params.random_scalar(); + let o_d = params.random_scalar(); + + // compute commitments A, C, D + let A = params.gen1() * o_a + params.gamma1().unwrap() * Scalar::from(wallet.l()); + let C = params.gen1() * o_c + params.gamma1().unwrap() * wallet.v(); + let D = params.gen1() * o_d + params.gamma1().unwrap() * wallet.t(); + + // compute hash of the payment info + let R = hash_to_scalar(payInfo.info); + + // evaluate the pseudorandom functions + let S = pseudorandom_fgv(¶ms, wallet.v(), wallet.l()); + let T = params.gen1() * skUser.sk + pseudorandom_fgt(¶ms, wallet.t(), wallet.l()) * R; + + // compute values mu, o_mu, lambda, o_lambda + let mu: Scalar = (wallet.v() + Scalar::from(wallet.l()) + Scalar::from(1)).neg(); + let o_mu = ((o_a + o_c) * mu).neg(); + let lambda = (wallet.t() + Scalar::from(wallet.l()) + Scalar::from(1)).neg(); + let o_lambda = ((o_a + o_d) * lambda).neg(); + + // construct the zkp proof + let spendInstance = SpendInstance { + kappa, + A, + C, + D, + S, + T, + }; + let spendWitness = SpendWitness { + attributes, + r: sign_blinding_factor, + l: wallet.l(), + o_a, + o_c, + o_d, + mu, + lambda, + o_mu, + o_lambda, + }; + let zkp = SpendProof::construct(¶ms, &spendInstance, &spendWitness); + + // output pay and updated wallet + + Ok(()) } pub fn spend_verify() {} diff --git a/common/nym-compact-ecash/src/scheme/withdrawal.rs b/common/nym-compact-ecash/src/scheme/withdrawal.rs index 4dcc8f56b0..0d9821aaae 100644 --- a/common/nym-compact-ecash/src/scheme/withdrawal.rs +++ b/common/nym-compact-ecash/src/scheme/withdrawal.rs @@ -2,7 +2,7 @@ use bls12_381::{G1Projective, G2Prepared, G2Projective, Scalar}; use group::{Curve, GroupEncoding}; use crate::error::{CompactEcashError, Result}; -use crate::proofs::{WithdrawalReqInstance, WithdrawalReqProof, WithdrawalReqWitness}; +use crate::proofs::proof_withdrawal::{WithdrawalReqInstance, WithdrawalReqProof, WithdrawalReqWitness}; use crate::scheme::{BlindedSignature, PartialWallet, Signature}; use crate::scheme::keygen::{PublicKeyUser, SecretKeyAuth, SecretKeyUser, VerificationKeyAuth}; use crate::scheme::keygen::ttp_keygen; diff --git a/common/nymcoconut/src/scheme/verification.rs b/common/nymcoconut/src/scheme/verification.rs index 96ef28e9ca..dfbd891524 100644 --- a/common/nymcoconut/src/scheme/verification.rs +++ b/common/nymcoconut/src/scheme/verification.rs @@ -5,9 +5,10 @@ use core::ops::Neg; use std::convert::TryFrom; use std::convert::TryInto; -use bls12_381::{multi_miller_loop, G1Affine, G2Prepared, G2Projective, Scalar}; +use bls12_381::{G1Affine, G2Prepared, G2Projective, multi_miller_loop, Scalar}; use group::{Curve, Group}; +use crate::Attribute; use crate::error::{CoconutError, Result}; use crate::proofs::ProofKappaZeta; use crate::scheme::double_use::BlindedSerialNumber; @@ -16,7 +17,6 @@ use crate::scheme::Signature; use crate::scheme::VerificationKey; use crate::traits::{Base58, Bytable}; use crate::utils::try_deserialize_g2_projective; -use crate::Attribute; // TODO NAMING: this whole thing // Theta @@ -136,10 +136,10 @@ pub fn compute_kappa( params.gen2() * blinding_factor + verification_key.alpha + private_attributes - .iter() - .zip(verification_key.beta_g2.iter()) - .map(|(priv_attr, beta_i)| beta_i * priv_attr) - .sum::() + .iter() + .zip(verification_key.beta_g2.iter()) + .map(|(priv_attr, beta_i)| beta_i * priv_attr) + .sum::() } pub fn compute_zeta(params: &Parameters, serial_number: Attribute) -> G2Projective { @@ -296,11 +296,11 @@ pub fn verify( ) -> bool { let kappa = (verification_key.alpha + public_attributes - .iter() - .zip(verification_key.beta_g2.iter()) - .map(|(m_i, b_i)| b_i * m_i) - .sum::()) - .to_affine(); + .iter() + .zip(verification_key.beta_g2.iter()) + .map(|(m_i, b_i)| b_i * m_i) + .sum::()) + .to_affine(); check_bilinear_pairing( &sig.0.to_affine(), @@ -345,7 +345,7 @@ mod tests { serial_number, binding_number, ) - .unwrap(); + .unwrap(); let bytes = theta.to_bytes(); assert_eq!(Theta::try_from(bytes.as_slice()).unwrap(), theta);