diff --git a/clients/native/src/client/mod.rs b/clients/native/src/client/mod.rs index 32eb59ada6..8c78ed059f 100644 --- a/clients/native/src/client/mod.rs +++ b/clients/native/src/client/mod.rs @@ -194,6 +194,7 @@ impl NymClient { prepare_for_spending( &self.key_manager.identity_keypair().public_key().to_bytes(), &bandwidth_credential, + &bandwidth_credential_attributes, &verification_key, ) .expect("could not prepare out bandwidth credential for spending") diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index 96ace589cb..7c171debdf 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -79,7 +79,7 @@ async fn prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) - .await .expect("could not obtain bandwidth credential"); - prepare_for_spending(raw_identity, &bandwidth_credential, &verification_key) + prepare_for_spending(raw_identity, &bandwidth_credential, &bandwidth_credential_attributes, &verification_key) .expect("could not prepare out bandwidth credential for spending") } diff --git a/clients/socks5/src/client/mod.rs b/clients/socks5/src/client/mod.rs index f74255929e..fc012c3600 100644 --- a/clients/socks5/src/client/mod.rs +++ b/clients/socks5/src/client/mod.rs @@ -182,6 +182,7 @@ impl NymClient { prepare_for_spending( &self.key_manager.identity_keypair().public_key().to_bytes(), &bandwidth_credential, + &bandwidth_credential_attributes, &verification_key, ) .expect("could not prepare out bandwidth credential for spending") diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index a0f5e2bc88..0a8a50ee04 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -79,7 +79,7 @@ async fn prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) - .await .expect("could not obtain bandwidth credential"); - prepare_for_spending(raw_identity, &bandwidth_credential, &verification_key) + prepare_for_spending(raw_identity, &bandwidth_credential, &bandwidth_credential_attributes, &verification_key) .expect("could not prepare out bandwidth credential for spending") } diff --git a/common/credentials/src/bandwidth.rs b/common/credentials/src/bandwidth.rs index 281cb728ee..48d8fdc559 100644 --- a/common/credentials/src/bandwidth.rs +++ b/common/credentials/src/bandwidth.rs @@ -32,14 +32,14 @@ pub struct BandwidthVoucherAttributes { impl BandwidthVoucherAttributes { - fn get_public_attributes(&self) -> Vec { + pub fn get_public_attributes(&self) -> Vec { let mut pub_attributes = Vec::with_capacity(PUBLIC_ATTRIBUTES as usize); pub_attributes.push(self.voucher_value); pub_attributes.push(self.voucher_info); pub_attributes } - fn get_private_attributes(&self) -> Vec { + pub fn get_private_attributes(&self) -> Vec { let mut priv_attributes = Vec::with_capacity(PRIVATE_ATTRIBUTES as usize); priv_attributes.push(self.serial_number); priv_attributes.push(self.binding_number); @@ -62,17 +62,18 @@ pub async fn obtain_signature(attributes: &BandwidthVoucherAttributes, validator pub fn prepare_for_spending( raw_identity: &[u8], signature: &Signature, + attributes: &BandwidthVoucherAttributes, verification_key: &VerificationKey, ) -> Result { let public_attributes = vec![BANDWIDTH_VALUE.to_be_bytes().to_vec()]; - let private_attributes = vec![raw_identity.to_vec()]; let params = Parameters::new(TOTAL_ATTRIBUTES)?; prepare_credential_for_spending( ¶ms, public_attributes, - private_attributes, + attributes.serial_number, + attributes.binding_number, signature, verification_key, ) diff --git a/common/credentials/src/utils.rs b/common/credentials/src/utils.rs index 1f0c85159f..7694ca186b 100644 --- a/common/credentials/src/utils.rs +++ b/common/credentials/src/utils.rs @@ -134,18 +134,16 @@ pub async fn obtain_aggregate_signature( pub fn prepare_credential_for_spending( params: &Parameters, public_attributes: Vec>, - private_attributes: Vec>, + serial_number: Attribute, + binding_number: Attribute, signature: &Signature, verification_key: &VerificationKey, ) -> Result { - let private_attributes = private_attributes - .iter() - .map(hash_to_scalar) - .collect::>(); - let theta = prove_credential(params, verification_key, signature, &private_attributes)?; + + let theta = prove_credential(params, verification_key, signature, serial_number, binding_number)?; Ok(Credential::new( - (public_attributes.len() + private_attributes.len()) as u32, + (public_attributes.len() + 2) as u32, theta, public_attributes, signature, diff --git a/common/nymcoconut/src/proofs/mod.rs b/common/nymcoconut/src/proofs/mod.rs index a22ef7b4ca..82ec786ab9 100644 --- a/common/nymcoconut/src/proofs/mod.rs +++ b/common/nymcoconut/src/proofs/mod.rs @@ -76,7 +76,8 @@ fn compute_challenge(iter: I) -> Scalar Scalar::from_bytes_wide(&bytes) } -fn produce_response(witness: &Scalar, challenge: &Scalar, secret: &Scalar) -> Scalar { +fn produce_response(witness: &Scalar, challenge: &Scalar, secret: &Scalar) -> Scalar +{ witness - challenge * secret } @@ -380,11 +381,9 @@ pub struct ProofKappaNu { // c challenge: Scalar, - // rm - response_attributes: Vec, - - // TODO NAMING: blinder or blinding factor? - // rt + // responses + response_serial_number: Scalar, + response_binding_number: Scalar, response_blinder: Scalar, } @@ -392,13 +391,17 @@ impl ProofKappaNu { pub(crate) fn construct( params: &Parameters, verification_key: &VerificationKey, - private_attributes: &[Attribute], + serial_number: &Attribute, + binding_number: &Attribute, blinding_factor: &Scalar, blinded_message: &G2Projective, + blinded_serial_number: &G2Projective, ) -> Self { // create the witnesses let witness_blinder = params.random_scalar(); - let witness_attributes = params.n_random_scalars(private_attributes.len()); + let witness_serial_number = params.random_scalar(); + let witness_binding_number = params.random_scalar(); + let witness_attributes = vec![witness_serial_number, witness_blinder]; let beta_bytes = verification_key .beta @@ -416,28 +419,33 @@ impl ProofKappaNu { .map(|(wm_i, beta_i)| beta_i * wm_i) .sum::(); + // zeta is the public value associated with the serial number + let commitment_zeta = params.gen2() * witness_serial_number; + let challenge = compute_challenge::( std::iter::once(params.gen2().to_bytes().as_ref()) .chain(std::iter::once(blinded_message.to_bytes().as_ref())) //kappa .chain(std::iter::once(verification_key.alpha.to_bytes().as_ref())) .chain(beta_bytes.iter().map(|b| b.as_ref())) - .chain(std::iter::once(commitment_kappa.to_bytes().as_ref())), + .chain(std::iter::once(commitment_kappa.to_bytes().as_ref())) + .chain(std::iter::once(commitment_zeta.to_bytes().as_ref())), ); // responses let response_blinder = produce_response(&witness_blinder, &challenge, &blinding_factor); - let response_attributes = - produce_responses(&witness_attributes, &challenge, private_attributes); + let response_serial_number = produce_response(&witness_serial_number, &challenge, &serial_number); + let response_binding_number = produce_response(&witness_binding_number, &challenge, &binding_number); ProofKappaNu { challenge, - response_attributes, + response_serial_number, + response_binding_number, response_blinder, } } pub(crate) fn private_attributes(&self) -> usize { - self.response_attributes.len() + 2 } pub(crate) fn verify( @@ -445,6 +453,7 @@ impl ProofKappaNu { params: &Parameters, verification_key: &VerificationKey, kappa: &G2Projective, + zeta: &G2Projective, ) -> bool { let beta_bytes = verification_key .beta @@ -452,20 +461,22 @@ impl ProofKappaNu { .map(|beta_i| beta_i.to_bytes()) .collect::>(); + let response_attributes = [self.response_serial_number, self.response_binding_number]; // re-compute witnesses commitments // Aw = (c * kappa) + (rt * g2) + ((1 - c) * alpha) + (rm[0] * beta[0]) + ... + (rm[i] * beta[i]) let commitment_kappa = kappa * self.challenge + params.gen2() * self.response_blinder + verification_key.alpha * (Scalar::one() - self.challenge) - + self - .response_attributes + + response_attributes .iter() .zip(verification_key.beta.iter()) .map(|(priv_attr, beta_i)| beta_i * priv_attr) .sum::(); - // Bw = (c * nu) + (rt * h) - // let commitment_blinder = nu * self.challenge + signature.sig1() * self.response_blinder; + // zeta is the public value associated with the serial number + let commitment_zeta = zeta * self.challenge + + params.gen2() * self.response_serial_number; + // compute the challenge let challenge = compute_challenge::( @@ -473,7 +484,8 @@ impl ProofKappaNu { .chain(std::iter::once(kappa.to_bytes().as_ref())) //kappa .chain(std::iter::once(verification_key.alpha.to_bytes().as_ref())) .chain(beta_bytes.iter().map(|b| b.as_ref())) - .chain(std::iter::once(commitment_kappa.to_bytes().as_ref())), + .chain(std::iter::once(commitment_kappa.to_bytes().as_ref())) + .chain(std::iter::once(commitment_zeta.to_bytes().as_ref())), ); challenge == self.challenge @@ -481,16 +493,19 @@ impl ProofKappaNu { // challenge || rm.len() || rm || rt pub(crate) fn to_bytes(&self) -> Vec { - let attributes_len = self.response_attributes.len() as u64; - - let mut bytes = Vec::with_capacity(8 + (attributes_len + 1) as usize * 32); + //let attributes_len = self.response_attributes.len() as u64; + let attributes_len = 2; + let mut bytes = Vec::with_capacity((attributes_len + 1) as usize * 32); bytes.extend_from_slice(&self.challenge.to_bytes()); + bytes.extend_from_slice(&self.response_serial_number.to_bytes()); + bytes.extend_from_slice(&self.response_binding_number.to_bytes()); - bytes.extend_from_slice(&attributes_len.to_le_bytes()); - for rm in &self.response_attributes { - bytes.extend_from_slice(&rm.to_bytes()); - } + + // bytes.extend_from_slice(&attributes_len.to_le_bytes()); + // for rm in &self.response_attributes { + // bytes.extend_from_slice(&rm.to_bytes()); + // } bytes.extend_from_slice(&self.response_blinder.to_bytes()); @@ -499,12 +514,12 @@ impl ProofKappaNu { pub(crate) fn from_bytes(bytes: &[u8]) -> Result { // at the very minimum there must be a single attribute being proven - if bytes.len() < 32 * 3 + 8 || (bytes.len() - 8) % 32 != 0 { + if bytes.len() < 32 * 3 || (bytes.len()) % 32 != 0 { return Err(CoconutError::DeserializationInvalidLength { actual: bytes.len(), - modulus_target: bytes.len() - 8, + modulus_target: bytes.len(), modulus: 32, - object: "kappa and nu".to_string(), + object: "kappa and zeta".to_string(), target: 32 * 3 + 8, }); } @@ -515,23 +530,34 @@ impl ProofKappaNu { CoconutError::Deserialization("Failed to deserialize challenge".to_string()), )?; - let rm_len = u64::from_le_bytes(bytes[32..40].try_into().unwrap()); - if bytes[40..].len() != (rm_len + 1) as usize * 32 { + // let rm_len = u64::from_le_bytes(bytes[32..40].try_into().unwrap()); + if bytes[32..].len() != (2 + 1) as usize * 32 { return Err( CoconutError::Deserialization( - format!("Tried to deserialize proof of kappa and nu with insufficient number of bytes provided, expected {} got {}.", (rm_len + 1) as usize * 32, bytes[40..].len()) + format!("Tried to deserialize proof of kappa and zeta with insufficient number of bytes provided, expected {} got {}.", (2 + 1) as usize * 32, bytes[32..].len()) ) ); } - let rm_end = 40 + rm_len as usize * 32; - let response_attributes = try_deserialize_scalar_vec( - rm_len, - &bytes[40..rm_end], - CoconutError::Deserialization("Failed to deserialize attributes response".to_string()), + let serial_number_bytes = &bytes[32..64].try_into().unwrap(); + let response_serial_number = try_deserialize_scalar( + &serial_number_bytes, + CoconutError::Deserialization("failed to deserialize the serial number".to_string()), )?; - let blinder_bytes = bytes[rm_end..].try_into().unwrap(); + let binding_number_bytes = &bytes[64..96].try_into().unwrap(); + let response_binding_number = try_deserialize_scalar( + &binding_number_bytes, + CoconutError::Deserialization("failed to deserialize the binding number".to_string()), + )?; + // let rm_end = 40 + rm_len as usize * 32; + // let response_attributes = try_deserialize_scalar_vec( + // rm_len, + // &bytes[40..rm_end], + // CoconutError::Deserialization("Failed to deserialize attributes response".to_string()), + // )?; + + let blinder_bytes = bytes[96..].try_into().unwrap(); let response_blinder = try_deserialize_scalar( &blinder_bytes, CoconutError::Deserialization("failed to deserialize the blinder".to_string()), @@ -539,7 +565,8 @@ impl ProofKappaNu { Ok(ProofKappaNu { challenge, - response_attributes, + response_serial_number, + response_binding_number, response_blinder, }) } @@ -558,7 +585,7 @@ mod tests { use crate::scheme::issuance::{compute_attribute_encryption, compute_commitment_hash}; use crate::scheme::keygen::keygen; use crate::scheme::setup::setup; - use crate::scheme::verification::compute_kappa; + use crate::scheme::verification::{compute_kappa, compute_zeta}; use super::*; @@ -628,17 +655,21 @@ mod tests { // we don't care about 'correctness' of the proof. only whether we can correctly recover it from bytes let signature = Signature(params.gen1() * r, params.gen1() * s); - let private_attributes = params.n_random_scalars(1); + let serial_number = params.random_scalar(); + let binding_number = params.random_scalar(); let r = params.random_scalar(); let kappa = compute_kappa(¶ms, &keypair.verification_key(), &private_attributes, r); + let zeta = compute_zeta(¶ms, serial_number); - // 0 public 1 private + // 0 public 2 private let pi_v = ProofKappaNu::construct( &mut params, &keypair.verification_key(), &private_attributes, + &binding_number, &r, &kappa, + &zeta ); let bytes = pi_v.to_bytes(); @@ -648,13 +679,17 @@ mod tests { let mut params = setup(4).unwrap(); let keypair = keygen(&mut params); let private_attributes = params.n_random_scalars(2); + let serial_number = params.random_scalar(); + let binding_number = params.random_scalar(); let pi_v = ProofKappaNu::construct( &mut params, &keypair.verification_key(), - &private_attributes, + &serial_number, + &binding_number, &r, &kappa, + &zeta ); let bytes = pi_v.to_bytes(); diff --git a/common/nymcoconut/src/scheme/verification.rs b/common/nymcoconut/src/scheme/verification.rs index 32bd17d41c..e6a6ba454a 100644 --- a/common/nymcoconut/src/scheme/verification.rs +++ b/common/nymcoconut/src/scheme/verification.rs @@ -35,6 +35,8 @@ use crate::utils::{try_deserialize_g1_projective, try_deserialize_g2_projective} pub struct Theta { // blinded_message (kappa) pub blinded_message: G2Projective, + // blinded serial number (zeta) + pub blinded_serial_number : G2Projective, // sigma pub credential: Signature, // pi_v @@ -45,7 +47,7 @@ impl TryFrom<&[u8]> for Theta { type Error = CoconutError; fn try_from(bytes: &[u8]) -> Result { - if bytes.len() < 192 { + if bytes.len() < 288 { return Err( CoconutError::Deserialization( format!("Tried to deserialize theta with insufficient number of bytes, expected >= 240, got {}", bytes.len()), @@ -58,12 +60,18 @@ impl TryFrom<&[u8]> for Theta { CoconutError::Deserialization("failed to deserialize kappa".to_string()), )?; - let credential = Signature::try_from(&bytes[96..192])?; + let blinded_serial_number_bytes = bytes[96..192].try_into().unwrap(); + let blinded_serial_number = try_deserialize_g2_projective( + &blinded_serial_number_bytes, + CoconutError::Deserialization("failed to deserialize zeta".to_string()), + )?; + let credential = Signature::try_from(&bytes[192..288])?; - let pi_v = ProofKappaNu::from_bytes(&bytes[192..])?; + let pi_v = ProofKappaNu::from_bytes(&bytes[288..])?; Ok(Theta { blinded_message, + blinded_serial_number, credential, pi_v, }) @@ -76,6 +84,7 @@ impl Theta { params, verification_key, &self.blinded_message, + &self.blinded_serial_number, ) } @@ -85,11 +94,13 @@ impl Theta { // kappa || nu || credential || pi_v pub fn to_bytes(&self) -> Vec { let blinded_message_bytes = self.blinded_message.to_affine().to_compressed(); + let blinded_serial_number_bytes = self.blinded_serial_number.to_affine().to_compressed(); let credential_bytes = self.credential.to_bytes(); let proof_bytes = self.pi_v.to_bytes(); - let mut bytes = Vec::with_capacity(192 + proof_bytes.len()); + let mut bytes = Vec::with_capacity(288 + proof_bytes.len()); bytes.extend_from_slice(&blinded_message_bytes); + bytes.extend_from_slice(&blinded_serial_number_bytes); bytes.extend_from_slice(&credential_bytes); bytes.extend_from_slice(&proof_bytes); @@ -128,24 +139,23 @@ pub fn compute_kappa( .sum::() } +pub fn compute_zeta(params: &Parameters, serial_number: Attribute) -> G2Projective { + params.gen2() * serial_number +} + pub fn prove_credential( params: &Parameters, verification_key: &VerificationKey, signature: &Signature, - private_attributes: &[Attribute], + serial_number: Attribute, + binding_number: Attribute, ) -> Result { - if private_attributes.is_empty() { - return Err(CoconutError::Verification( - "Tried to prove a credential with an empty set of private attributes".to_string(), - )); - } - if private_attributes.len() > verification_key.beta.len() { + if verification_key.beta.len() < 2{ return Err( CoconutError::Verification( - format!("Tried to prove a credential for higher than supported by the provided verification key number of attributes (max: {}, requested: {})", - verification_key.beta.len(), - private_attributes.len() + format!("Tried to prove a credential for higher than supported by the provided verification key number of attributes (max: {}, requested: 2)", + verification_key.beta.len() ))); } @@ -154,24 +164,30 @@ pub fn prove_credential( // blinded_message : kappa in the paper. // Value kappa is needed since we want to show a signature sigma'. - // In order to verify sigma' we need both the varification key vk and the message m. + // In order to verify sigma' we need both the verification key vk and the message m. // However, we do not want to reveal m to whomever we are showing the signature. // Thus, we need kappa which allows us to verify sigma'. In particular, // kappa is computed on m as input, but thanks to the use or random value r, // it does not reveal any information about m. - let blinded_message = compute_kappa(params, verification_key, private_attributes, sign_blinding_factor); + let private_attributes = vec![serial_number, binding_number]; + let blinded_message = compute_kappa(params, verification_key, &private_attributes, sign_blinding_factor); + // zeta is a commitment to the serial number (i.e., a public value associated with the serial number) + let blinded_serial_number = compute_zeta(params, serial_number); let pi_v = ProofKappaNu::construct( params, verification_key, - private_attributes, + &serial_number, + &binding_number, &sign_blinding_factor, &blinded_message, + &blinded_serial_number, ); Ok(Theta { blinded_message, + blinded_serial_number, credential: signature_prime, pi_v, }) @@ -262,40 +278,42 @@ mod tests { #[test] fn theta_bytes_roundtrip() { - let mut params = setup(1).unwrap(); + let mut params = setup(2).unwrap(); let keypair = keygen(&mut params); let r = params.random_scalar(); let s = params.random_scalar(); let signature = Signature(params.gen1() * r, params.gen1() * s); - let private_attributes = params.n_random_scalars(1); + let serial_number = params.random_scalar(); + let binding_number = params.random_scalar(); let theta = prove_credential( &mut params, &keypair.verification_key(), &signature, - &private_attributes, + serial_number, + binding_number, ) .unwrap(); let bytes = theta.to_bytes(); assert_eq!(Theta::try_from(bytes.as_slice()).unwrap(), theta); - let mut params = setup(4).unwrap(); - - let keypair = keygen(&mut params); - let private_attributes = params.n_random_scalars(2); - - let theta = prove_credential( - &mut params, - &keypair.verification_key(), - &signature, - &private_attributes, - ) - .unwrap(); - - let bytes = theta.to_bytes(); - assert_eq!(Theta::try_from(bytes.as_slice()).unwrap(), theta); + // let mut params = setup(4).unwrap(); + // + // let keypair = keygen(&mut params); + // let private_attributes = params.n_random_scalars(2); + // + // let theta = prove_credential( + // &mut params, + // &keypair.verification_key(), + // &signature, + // &private_attributes, + // ) + // .unwrap(); + // + // let bytes = theta.to_bytes(); + // assert_eq!(Theta::try_from(bytes.as_slice()).unwrap(), theta); } } diff --git a/validator-api/src/network_monitor/mod.rs b/validator-api/src/network_monitor/mod.rs index 70d3f15a68..096a69d3b2 100644 --- a/validator-api/src/network_monitor/mod.rs +++ b/validator-api/src/network_monitor/mod.rs @@ -191,6 +191,7 @@ async fn TEMPORARY_obtain_bandwidth_credential( prepare_for_spending( &identity.to_bytes(), &bandwidth_credential, + &bandwidth_credential_attributes, &verification_key, ) .expect("failed to prepare bandwidth credential for spending!")