diff --git a/src/data_types/w3c/credential_attributes.rs b/src/data_types/w3c/credential_attributes.rs index 52767752..70129473 100644 --- a/src/data_types/w3c/credential_attributes.rs +++ b/src/data_types/w3c/credential_attributes.rs @@ -1,4 +1,3 @@ -use crate::data_types::w3c::presentation::PredicateAttribute; use crate::error::ValidationError; use crate::types::{CredentialValues, MakeCredentialValues}; use crate::utils::validation::Validatable; @@ -59,23 +58,20 @@ impl CredentialAttributes { self.0.insert(attribute, value); } - pub(crate) fn add_predicate( - &mut self, - attribute: String, - predicate: PredicateAttribute, - ) -> crate::Result<()> { - match self.0.get_mut(&attribute) { + pub(crate) fn add_predicate(&mut self, attribute: String) -> crate::Result<()> { + match self.0.get(&attribute) { Some(value) => match value { CredentialAttributeValue::Attribute(_) => { return Err(err_msg!("Predicate cannot be added for revealed attribute")); } - CredentialAttributeValue::Predicate(predicates) => predicates.push(predicate), + CredentialAttributeValue::Predicate(_) => { + // predicate already exists + return Ok(()); + } }, None => { - self.0.insert( - attribute, - CredentialAttributeValue::Predicate(vec![predicate]), - ); + self.0 + .insert(attribute, CredentialAttributeValue::Predicate(true)); } } Ok(()) @@ -104,5 +100,5 @@ impl CredentialAttributes { #[serde(untagged)] pub enum CredentialAttributeValue { Attribute(String), - Predicate(Vec), + Predicate(bool), } diff --git a/src/data_types/w3c/presentation.rs b/src/data_types/w3c/presentation.rs index 4521749a..aed3679d 100644 --- a/src/data_types/w3c/presentation.rs +++ b/src/data_types/w3c/presentation.rs @@ -1,4 +1,3 @@ -use crate::data_types::pres_request::{PredicateInfo, PredicateTypes}; use serde::{Deserialize, Serialize}; use crate::data_types::w3c::constants::ANONCREDS_PRESENTATION_TYPES; @@ -63,33 +62,3 @@ impl W3CPresentation { Ok(()) } } - -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] -pub struct PredicateAttribute { - #[serde(rename = "type")] - pub type_: PredicateAttributeType, - pub predicate: PredicateTypes, - pub value: i32, -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub enum PredicateAttributeType { - #[serde(rename = "AnonCredsPredicate")] - AnonCredsPredicate, -} - -impl Default for PredicateAttributeType { - fn default() -> Self { - PredicateAttributeType::AnonCredsPredicate - } -} - -impl From for PredicateAttribute { - fn from(info: PredicateInfo) -> Self { - PredicateAttribute { - type_: PredicateAttributeType::AnonCredsPredicate, - predicate: info.p_type, - value: info.p_value, - } - } -} diff --git a/src/data_types/w3c/proof.rs b/src/data_types/w3c/proof.rs index 8fe31d1d..690cb87a 100644 --- a/src/data_types/w3c/proof.rs +++ b/src/data_types/w3c/proof.rs @@ -10,6 +10,7 @@ use anoncreds_clsignatures::{ use serde::de::DeserializeOwned; use serde::Serialize; use serde_json::json; +use std::collections::HashSet; use std::fmt::Debug; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -205,9 +206,22 @@ pub struct CredentialPresentationProofValue { pub rev_reg_id: Option, #[serde(skip_serializing_if = "Option::is_none")] pub timestamp: Option, + pub mapping: CredentialAttributesMapping, pub sub_proof: SubProof, } +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CredentialAttributesMapping { + #[serde(default)] + pub revealed_attributes: HashSet, + pub revealed_attribute_groups: HashSet, + #[serde(default)] + pub unrevealed_attributes: HashSet, + #[serde(default)] + pub predicates: HashSet, +} + #[derive(Debug, Serialize, Deserialize)] pub struct PresentationProofValue { pub aggregated: AggregatedProof, @@ -335,6 +349,7 @@ pub(crate) mod tests { cred_def_id: cred_def_id(), rev_reg_id: Some(verifier::tests::revocation_id()), timestamp: Some(PROOF_TIMESTAMP), + mapping: Default::default(), sub_proof: credential_sub_proof(), } } diff --git a/src/services/helpers.rs b/src/services/helpers.rs index 2ef44a0f..93fa226e 100644 --- a/src/services/helpers.rs +++ b/src/services/helpers.rs @@ -2,7 +2,7 @@ use crate::cl::{ bn::BigNumber, CredentialSchema, CredentialValues as CLCredentialValues, Issuer, NonCredentialSchema, SubProofRequest, Verifier, }; -use crate::data_types::pres_request::{PredicateTypes, PredicateValue}; +use crate::data_types::pres_request::PredicateInfo; use crate::data_types::presentation::RequestedProof; use crate::data_types::rev_reg_def::RevocationRegistryDefinitionId; use crate::data_types::schema::Schema; @@ -89,7 +89,7 @@ pub fn encode_credential_attribute(raw_value: &str) -> Result { pub fn build_sub_proof_request( attrs_for_credential: &[String], - predicates_for_credential: &HashMap, + predicates_for_credential: &[PredicateInfo], ) -> Result { trace!( "build_sub_proof_request >>> attrs_for_credential: {:?}, predicates_for_credential: {:?}", @@ -103,10 +103,14 @@ pub fn build_sub_proof_request( sub_proof_request_builder.add_revealed_attr(&attr_common_view(attr))?; } - for (name, (p_type, p_value)) in predicates_for_credential { - let p_type = format!("{}", p_type); + for predicate in predicates_for_credential { + let p_type = format!("{}", predicate.p_type); - sub_proof_request_builder.add_predicate(&attr_common_view(name), &p_type, *p_value)?; + sub_proof_request_builder.add_predicate( + &attr_common_view(&predicate.name), + &p_type, + predicate.p_value, + )?; } let res = sub_proof_request_builder.finalize()?; @@ -231,18 +235,14 @@ impl PresentationRequestPayload { Ok((attributes, non_revoked_interval)) } - #[allow(clippy::type_complexity)] pub(crate) fn get_requested_predicates( &self, referents: &HashSet, - ) -> Result<( - HashMap, - Option, - )> { + ) -> Result<(Vec, Option)> { trace!("get_requested_predicates >>> referents: {:?}", referents); let mut non_revoked_interval: Option = None; - let mut predicates: HashMap = - HashMap::with_capacity(self.requested_predicates.len()); + let mut predicates: Vec = + Vec::with_capacity(self.requested_predicates.len()); for referent in referents { let requested = self @@ -259,7 +259,7 @@ impl PresentationRequestPayload { None => non_revoked_interval = Some(int.clone()), } } - predicates.insert(requested.name, (requested.p_type, requested.p_value)); + predicates.push(requested); } trace!( diff --git a/src/services/verifier.rs b/src/services/verifier.rs index 2e93cb9e..c27682cb 100644 --- a/src/services/verifier.rs +++ b/src/services/verifier.rs @@ -9,8 +9,8 @@ use crate::data_types::cred_def::CredentialDefinition; use crate::data_types::cred_def::CredentialDefinitionId; use crate::data_types::issuer_id::IssuerId; use crate::data_types::nonce::Nonce; +use crate::data_types::pres_request::PresentationRequestPayload; use crate::data_types::pres_request::{AttributeInfo, NonRevokedInterval}; -use crate::data_types::pres_request::{PredicateTypes, PredicateValue, PresentationRequestPayload}; use crate::data_types::presentation::{Identifier, RequestedProof}; use crate::data_types::rev_reg_def::RevocationRegistryDefinitionId; use crate::data_types::schema::Schema; @@ -105,29 +105,13 @@ pub fn verify_presentation( .requested_proof .get_predicates_for_credential(sub_proof_index as u32); - let (attributes, attrs_nonrevoked_interval) = - pres_req.get_requested_attributes(&attributes)?; - let (predicates, pred_nonrevoked_interval) = - pres_req.get_requested_predicates(&predicates)?; - - { - check_non_revoked_interval( - proof_verifier.get_credential_definition(&identifier.cred_def_id)?, - attrs_nonrevoked_interval, - pred_nonrevoked_interval, - pres_req, - identifier.rev_reg_id.as_ref(), - nonrevoke_interval_override, - identifier.timestamp, - )?; - } - proof_verifier.add_sub_proof( &attributes, &predicates, &identifier.schema_id, &identifier.cred_def_id, identifier.rev_reg_id.as_ref(), + nonrevoke_interval_override, identifier.timestamp, )?; } @@ -851,11 +835,14 @@ impl<'a> CLProofVerifier<'a> { #[allow(clippy::too_many_arguments)] pub(crate) fn add_sub_proof( &mut self, - attributes: &[String], - predicates: &HashMap, + attributes: &HashSet, + predicates: &HashSet, schema_id: &SchemaId, cred_def_id: &CredentialDefinitionId, rev_reg_def_id: Option<&RevocationRegistryDefinitionId>, + nonrevoke_interval_override: Option< + &HashMap>, + >, timestamp: Option, ) -> Result<()> { let schema = self.get_schema(schema_id)?; @@ -867,7 +854,25 @@ impl<'a> CLProofVerifier<'a> { &cred_def.value.primary, cred_def.value.revocation.as_ref(), )?; - let sub_pres_request = build_sub_proof_request(attributes, predicates)?; + + let (attributes, attrs_nonrevoked_interval) = self + .presentation_request + .get_requested_attributes(attributes)?; + let (predicates, pred_nonrevoked_interval) = self + .presentation_request + .get_requested_predicates(predicates)?; + + check_non_revoked_interval( + self.get_credential_definition(cred_def_id)?, + attrs_nonrevoked_interval, + pred_nonrevoked_interval, + self.presentation_request, + rev_reg_def_id, + nonrevoke_interval_override, + timestamp, + )?; + + let sub_pres_request = build_sub_proof_request(&attributes, &predicates)?; let rev_key_pub = rev_reg_def.map(|d| &d.value.public_keys.accum_key); self.proof_verifier.add_sub_proof_request( &sub_pres_request, diff --git a/src/services/w3c/helpers.rs b/src/services/w3c/helpers.rs index becfd34d..81c791b9 100644 --- a/src/services/w3c/helpers.rs +++ b/src/services/w3c/helpers.rs @@ -1,5 +1,3 @@ -use crate::data_types::pres_request::{PredicateInfo, PredicateTypes, PredicateValue}; -use crate::data_types::schema::Schema; use crate::data_types::w3c::credential::W3CCredential; use crate::data_types::w3c::credential_attributes::CredentialAttributeValue; use crate::error::Result; @@ -31,21 +29,12 @@ impl W3CCredential { matches!(value, CredentialAttributeValue::Attribute(_)) } - pub(crate) fn has_predicate(&self, predicate: &PredicateInfo) -> bool { - let (_, value) = match self.get_case_insensitive_attribute(&predicate.name) { + pub(crate) fn has_predicate(&self, requested_predicate: &str) -> bool { + let (_, value) = match self.get_case_insensitive_attribute(requested_predicate) { Ok(value) => value, Err(_) => return false, }; - - match value { - CredentialAttributeValue::Predicate(ref predicates) => { - predicates.iter().any(|shared_predicate| { - shared_predicate.predicate == predicate.p_type - && shared_predicate.value == predicate.p_value - }) - } - _ => false, - } + matches!(value, CredentialAttributeValue::Predicate(_)) } pub(crate) fn get_attributes(&self) -> HashMap { @@ -57,29 +46,4 @@ impl W3CCredential { } attributes } - - pub(crate) fn get_predicates(&self) -> HashMap { - let mut predicates: HashMap = HashMap::new(); - for (name, attribute) in self.credential_subject.attributes.0.iter() { - if let CredentialAttributeValue::Predicate(predicate_list) = attribute { - predicate_list.iter().for_each(|predicate| { - predicates.insert( - name.to_string(), - (predicate.predicate.to_owned(), predicate.value), - ); - }); - } - } - predicates - } -} - -impl Schema { - pub(crate) fn has_case_insensitive_attribute(&self, requested_attribute: &str) -> bool { - let requested_attribute = attr_common_view(requested_attribute); - self.attr_names - .0 - .iter() - .any(|attribute| attr_common_view(attribute) == requested_attribute) - } } diff --git a/src/services/w3c/prover.rs b/src/services/w3c/prover.rs index 2c29bb9a..d5abfec0 100644 --- a/src/services/w3c/prover.rs +++ b/src/services/w3c/prover.rs @@ -11,9 +11,9 @@ use crate::types::{ use crate::utils::validation::Validatable; use crate::data_types::w3c::credential_attributes::CredentialAttributes; -use crate::data_types::w3c::presentation::PredicateAttribute; use crate::data_types::w3c::proof::{ - CredentialPresentationProofValue, DataIntegrityProof, PresentationProofValue, + CredentialAttributesMapping, CredentialPresentationProofValue, DataIntegrityProof, + PresentationProofValue, }; use crate::data_types::w3c::VerifiableCredentialSpecVersion; use crate::prover::{CLCredentialProver, CLProofBuilder}; @@ -191,6 +191,7 @@ pub fn create_presentation( cred_def_id: credential_proof.cred_def_id.to_owned(), rev_reg_id: credential_proof.rev_reg_id.to_owned(), timestamp: present.timestamp, + mapping: build_mapping(presentation_request, present)?, sub_proof, }; let proof = DataIntegrityProof::new_credential_presentation_proof(&proof); @@ -253,9 +254,41 @@ fn build_credential_attributes<'p>( let (attribute, _) = credentials .cred .get_case_insensitive_attribute(&predicate_info.name)?; - let predicate = PredicateAttribute::from(predicate_info); - attributes.add_predicate(attribute, predicate)?; + attributes.add_predicate(attribute)?; } Ok(attributes) } + +fn build_mapping<'p>( + pres_req: &PresentationRequestPayload, + credential: &PresentCredential<'p, W3CCredential>, +) -> Result { + let mut mapping = CredentialAttributesMapping::default(); + + for (referent, reveal) in credential.requested_attributes.iter() { + let requested_attribute = pres_req.requested_attributes.get(referent).ok_or_else(|| { + err_msg!( + "Attribute with referent \"{}\" not found in ProofRequest", + referent + ) + })?; + if requested_attribute.name.is_some() { + if *reveal { + mapping.revealed_attributes.insert(referent.to_string()); + } else { + mapping.unrevealed_attributes.insert(referent.to_string()); + } + } + if requested_attribute.names.is_some() { + mapping + .revealed_attribute_groups + .insert(referent.to_string()); + } + } + for referent in credential.requested_predicates.iter() { + mapping.predicates.insert(referent.to_string()); + } + + Ok(mapping) +} diff --git a/src/services/w3c/verifier.rs b/src/services/w3c/verifier.rs index 9e8c212e..8f91bf04 100644 --- a/src/services/w3c/verifier.rs +++ b/src/services/w3c/verifier.rs @@ -1,7 +1,7 @@ use crate::data_types::cred_def::CredentialDefinition; use crate::data_types::cred_def::CredentialDefinitionId; -use crate::data_types::pres_request::PresentationRequestPayload; -use crate::data_types::pres_request::{NonRevokedInterval, PredicateInfo}; +use crate::data_types::pres_request::PredicateInfo; +use crate::data_types::pres_request::{AttributeInfo, PresentationRequestPayload}; use crate::data_types::presentation::Identifier; use crate::data_types::rev_reg_def::RevocationRegistryDefinitionId; use crate::data_types::schema::Schema; @@ -11,13 +11,13 @@ use crate::data_types::w3c::credential_attributes::CredentialAttributeValue; use crate::data_types::w3c::presentation::W3CPresentation; use crate::data_types::w3c::proof::CredentialPresentationProofValue; use crate::error::Result; -use crate::services::helpers::{encode_credential_attribute, get_requested_non_revoked_interval}; +use crate::services::helpers::encode_credential_attribute; use crate::types::{PresentationRequest, RevocationRegistryDefinition, RevocationStatusList}; use crate::utils::query::Query; use crate::verifier::{gather_filter_info, process_operator}; use crate::verifier::{verify_revealed_attribute_value, CLProofVerifier}; use anoncreds_clsignatures::{Proof, SubProof}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; /// Verify an incoming presentation in W3C form pub fn verify_presentation( @@ -52,10 +52,9 @@ pub fn verify_presentation( check_request_data( presentation_request, presentation, + &credential_proofs, schemas, cred_defs, - nonrevoke_interval_override, - &credential_proofs, )?; let presentation_proof = presentation.get_presentation_proof()?; @@ -78,17 +77,19 @@ pub fn verify_presentation( for (verifiable_credential, credential_proof) in iter { let attributes = verifiable_credential.get_attributes(); - let predicates = verifiable_credential.get_predicates(); - let attribute_names: Vec = attributes.keys().cloned().collect(); - verify_revealed_attributes(&attributes, &credential_proof)?; + let mut revealed_attribute: HashSet = + credential_proof.mapping.revealed_attributes.clone(); + revealed_attribute.extend(credential_proof.mapping.revealed_attribute_groups.clone()); + proof_verifier.add_sub_proof( - &attribute_names, - &predicates, + &revealed_attribute, + &credential_proof.mapping.predicates, &credential_proof.schema_id, &credential_proof.cred_def_id, credential_proof.rev_reg_id.as_ref(), + nonrevoke_interval_override, credential_proof.timestamp, )?; @@ -135,154 +136,106 @@ fn check_credential_restrictions( Ok(()) } -fn check_credential_non_revoked_interval( - presentation_request: &PresentationRequestPayload, - nonrevoke_interval: Option<&NonRevokedInterval>, - nonrevoke_interval_override: Option< - &HashMap>, - >, - proof: &CredentialPresentationProofValue, -) -> Result<()> { - if let Some(ref rev_reg_id) = proof.rev_reg_id { - let non_revoked_interval = get_requested_non_revoked_interval( - Some(rev_reg_id), - nonrevoke_interval, - presentation_request.non_revoked.as_ref(), - nonrevoke_interval_override, - ); - - if let Some(non_revoked_interval) = non_revoked_interval { - let timestamp = proof - .timestamp - .ok_or_else(|| err_msg!("Credential timestamp not found for revocation check"))?; - non_revoked_interval.is_valid(timestamp)?; - } - } - Ok(()) -} - -#[allow(clippy::too_many_arguments)] -fn check_credential_conditions( - credential: &W3CCredential, - presentation_request: &PresentationRequestPayload, - restrictions: Option<&Query>, - schemas: &HashMap, - cred_defs: &HashMap, - nonrevoke_interval: Option<&NonRevokedInterval>, - nonrevoke_interval_override: Option< - &HashMap>, - >, - proof: &CredentialPresentationProofValue, -) -> Result<()> { - check_credential_restrictions(credential, restrictions, schemas, cred_defs, proof)?; - check_credential_non_revoked_interval( - presentation_request, - nonrevoke_interval, - nonrevoke_interval_override, - proof, - )?; - Ok(()) -} - #[allow(clippy::too_many_arguments)] fn check_requested_attribute<'a>( - presentation_request: &PresentationRequestPayload, presentation: &'a W3CPresentation, - attribute: &str, - restrictions: Option<&Query>, - nonrevoke_interval: Option<&NonRevokedInterval>, + credential_proofs: &[CredentialPresentationProofValue], + referent: &str, + attribute: &AttributeInfo, schemas: &HashMap, cred_defs: &HashMap, - nonrevoke_interval_override: Option< - &HashMap>, - >, - credential_proofs: &[CredentialPresentationProofValue], ) -> Result<&'a W3CCredential> { - for (index, credential) in presentation.verifiable_credential.iter().enumerate() { - let proof = credential_proofs - .get(index) - .ok_or_else(|| err_msg!("Unable to get credential proof for index {}", index))?; - let valid_credential = credential.has_attribute(attribute) - && check_credential_conditions( - credential, - presentation_request, - restrictions, - schemas, - cred_defs, - nonrevoke_interval, - nonrevoke_interval_override, - proof, - ) - .is_ok(); + let iter = presentation + .verifiable_credential + .iter() + .zip(credential_proofs); - if valid_credential { - return Ok(credential); + for (credential, proof) in iter { + if let Some(ref name) = attribute.name { + if proof.mapping.revealed_attributes.contains(referent) { + if !credential.has_attribute(name) { + return Err(err_msg!( + "Credential does not contain revealed attribute {}", + name + )); + } + check_credential_restrictions( + credential, + attribute.restrictions.as_ref(), + schemas, + cred_defs, + proof, + )?; + return Ok(credential); + } + if proof.mapping.unrevealed_attributes.contains(referent) { + check_credential_restrictions( + credential, + attribute.restrictions.as_ref(), + schemas, + cred_defs, + proof, + )?; + return Ok(credential); + } } - } - - // else consider attribute as unrevealed and try to find credential which schema includes requested attribute - for (index, credential) in presentation.verifiable_credential.iter().enumerate() { - let proof = credential_proofs - .get(index) - .ok_or_else(|| err_msg!("Unable to get credential proof for index {}", index))?; - let schema = schemas - .get(&proof.schema_id) - .ok_or_else(|| err_msg!("Credential schema not found {}", proof.schema_id))?; - - let valid_credential = schema.has_case_insensitive_attribute(attribute) - && check_credential_conditions( - credential, - presentation_request, - restrictions, - schemas, - cred_defs, - nonrevoke_interval, - nonrevoke_interval_override, - proof, - ) - .is_ok(); - - if valid_credential { - return Ok(credential); + if let Some(ref names) = attribute.names { + if proof.mapping.revealed_attribute_groups.contains(referent) { + for name in names { + if !credential.has_attribute(name) { + return Err(err_msg!( + "Credential does not contain revealed attribute {}", + name + )); + } + check_credential_restrictions( + credential, + attribute.restrictions.as_ref(), + schemas, + cred_defs, + proof, + )?; + } + return Ok(credential); + } } } Err(err_msg!( "Presentation does not contain attribute {}", - attribute + referent )) } #[allow(clippy::too_many_arguments)] fn check_requested_predicate<'a>( - presentation_request: &PresentationRequestPayload, presentation: &'a W3CPresentation, + credential_proofs: &[CredentialPresentationProofValue], + referent: &str, predicate: &PredicateInfo, schemas: &HashMap, cred_defs: &HashMap, - nonrevoke_interval_override: Option< - &HashMap>, - >, - credential_proofs: &[CredentialPresentationProofValue], ) -> Result<&'a W3CCredential> { - for (index, credential) in presentation.verifiable_credential.iter().enumerate() { - let proof = credential_proofs - .get(index) - .ok_or_else(|| err_msg!("Unable to get credential proof for index {}", index))?; - let valid_credential = credential.has_predicate(predicate) - && check_credential_conditions( + let iter = presentation + .verifiable_credential + .iter() + .zip(credential_proofs); + + for (credential, proof) in iter { + if proof.mapping.predicates.contains(referent) { + if !credential.has_predicate(&predicate.name) { + return Err(err_msg!( + "Credential does not contain revealed attribute {}", + predicate.name + )); + } + check_credential_restrictions( credential, - presentation_request, predicate.restrictions.as_ref(), schemas, cred_defs, - predicate.non_revoked.as_ref(), - nonrevoke_interval_override, proof, - ) - .is_ok(); - - if valid_credential { + )?; return Ok(credential); } } @@ -296,52 +249,28 @@ fn check_requested_predicate<'a>( fn check_request_data( presentation_request: &PresentationRequestPayload, presentation: &W3CPresentation, + credential_proofs: &[CredentialPresentationProofValue], schemas: &HashMap, cred_defs: &HashMap, - nonrevoke_interval_override: Option< - &HashMap>, - >, - credential_proofs: &[CredentialPresentationProofValue], ) -> Result<()> { - for (_, attribute) in presentation_request.requested_attributes.iter() { - if let Some(ref name) = attribute.name { - check_requested_attribute( - presentation_request, - presentation, - name, - attribute.restrictions.as_ref(), - attribute.non_revoked.as_ref(), - schemas, - cred_defs, - nonrevoke_interval_override, - credential_proofs, - )?; - } - if let Some(ref names) = attribute.names { - for name in names { - check_requested_attribute( - presentation_request, - presentation, - name, - attribute.restrictions.as_ref(), - attribute.non_revoked.as_ref(), - schemas, - cred_defs, - nonrevoke_interval_override, - credential_proofs, - )?; - } - } + for (referent, attribute) in presentation_request.requested_attributes.iter() { + check_requested_attribute( + presentation, + credential_proofs, + referent, + attribute, + schemas, + cred_defs, + )?; } - for (_, predicate) in presentation_request.requested_predicates.iter() { + for (referent, predicate) in presentation_request.requested_predicates.iter() { check_requested_predicate( - presentation_request, presentation, + credential_proofs, + referent, predicate, schemas, cred_defs, - nonrevoke_interval_override, - credential_proofs, )?; } @@ -367,13 +296,12 @@ fn verify_revealed_attributes( pub(crate) mod tests { use super::*; use crate::data_types::nonce::Nonce; - use crate::data_types::pres_request::{AttributeInfo, PredicateTypes}; + use crate::data_types::pres_request::{AttributeInfo, NonRevokedInterval, PredicateTypes}; use crate::data_types::w3c::credential_attributes::CredentialAttributes; - use crate::data_types::w3c::presentation::PredicateAttribute; use crate::data_types::w3c::proof::tests::{ credential_pres_proof_value, presentation_proof_value, }; - use crate::data_types::w3c::proof::DataIntegrityProof; + use crate::data_types::w3c::proof::{CredentialAttributesMapping, DataIntegrityProof}; use crate::w3c::credential_conversion::tests::{ cred_def_id, credential_definition, issuer_id, schema, schema_id, }; @@ -382,7 +310,6 @@ pub(crate) mod tests { const PROOF_TIMESTAMP_FROM: u64 = 40; const PROOF_TIMESTAMP_TO: u64 = 50; - const PROOF_TIMESTAMP: u64 = 50; fn credential_attributes() -> CredentialAttributes { CredentialAttributes(HashMap::from([ @@ -394,14 +321,7 @@ pub(crate) mod tests { "height".to_string(), CredentialAttributeValue::Attribute("178".to_string()), ), - ( - "age".to_string(), - CredentialAttributeValue::Predicate(vec![PredicateAttribute { - type_: Default::default(), - predicate: _predicate().p_type, - value: _predicate().p_value, - }]), - ), + ("age".to_string(), CredentialAttributeValue::Predicate(true)), ])) } @@ -513,7 +433,7 @@ pub(crate) mod tests { } #[fixture] - fn _presentation_request_with_attribute_names() -> PresentationRequestPayload { + fn _presentation_request_with_attribute_group() -> PresentationRequestPayload { PresentationRequestPayload { requested_attributes: HashMap::from([( "attr1_referent".to_string(), @@ -552,22 +472,13 @@ pub(crate) mod tests { fn _presentation_request_with_case_insensitive_attribute_and_predicate( ) -> PresentationRequestPayload { PresentationRequestPayload { - requested_attributes: HashMap::from([ - ( - "attr1_referent".to_string(), - AttributeInfo { - name: Some("NAME".to_string()), - .._attribute() - }, - ), - ( - "attr2_referent".to_string(), - AttributeInfo { - name: Some("Height".to_string()), - .._attribute() - }, - ), - ]), + requested_attributes: HashMap::from([( + "attr1_referent".to_string(), + AttributeInfo { + name: Some("NAME".to_string()), + .._attribute() + }, + )]), requested_predicates: HashMap::from([( "predicate1_referent".to_string(), PredicateInfo { @@ -599,15 +510,17 @@ pub(crate) mod tests { requested_attributes: HashMap::from([( "attr1_referent".to_string(), AttributeInfo { + name: None, names: Some(vec![ "name".to_string(), "height".to_string(), "missing".to_string(), ]), - .._attribute() + restrictions: None, + non_revoked: None, }, )]), - .._presentation_request_with_single_attribute() + .._base_presentation_request() } } @@ -689,6 +602,14 @@ pub(crate) mod tests { } } + #[fixture] + fn _presentation_request_with_unrevealed_attribute() -> PresentationRequestPayload { + PresentationRequestPayload { + requested_attributes: HashMap::from([("attr4_referent".to_string(), _attribute())]), + .._base_presentation_request() + } + } + #[fixture] fn schemas() -> HashMap { HashMap::from([(schema_id(), schema())]) @@ -699,9 +620,22 @@ pub(crate) mod tests { HashMap::from([(cred_def_id(), credential_definition())]) } - #[fixture] - fn presentation() -> W3CPresentation { - _w3_presentation() + fn presentation(mapping: CredentialAttributesMapping) -> W3CPresentation { + let credential_pres_proof_value = CredentialPresentationProofValue { + mapping, + ..credential_pres_proof_value() + }; + + let proof = + DataIntegrityProof::new_credential_presentation_proof(&credential_pres_proof_value); + let credential = W3CCredential::new(issuer_id(), credential_attributes(), proof, None); + + let proof = DataIntegrityProof::new_presentation_proof( + &presentation_proof_value(), + "1".to_string(), + cred_def_id().to_string(), + ); + W3CPresentation::new(vec![credential], proof, None) } impl W3CPresentation { @@ -716,95 +650,180 @@ pub(crate) mod tests { } } + #[fixture] + fn _mapping_empty() -> CredentialAttributesMapping { + CredentialAttributesMapping { + ..Default::default() + } + } + + #[fixture] + fn _mapping_single_revealed_attribute() -> CredentialAttributesMapping { + CredentialAttributesMapping { + revealed_attributes: HashSet::from(["attr1_referent".to_string()]), + ..Default::default() + } + } + + #[fixture] + fn _mapping_multiple_revealed_attribute() -> CredentialAttributesMapping { + CredentialAttributesMapping { + revealed_attributes: HashSet::from([ + "attr1_referent".to_string(), + "attr2_referent".to_string(), + ]), + ..Default::default() + } + } + + #[fixture] + fn _mapping_single_revealed_attribute_group() -> CredentialAttributesMapping { + CredentialAttributesMapping { + revealed_attribute_groups: HashSet::from(["attr1_referent".to_string()]), + ..Default::default() + } + } + + #[fixture] + fn _mapping_single_unrevealed_attribute_group() -> CredentialAttributesMapping { + CredentialAttributesMapping { + unrevealed_attributes: HashSet::from(["attr1_referent".to_string()]), + ..Default::default() + } + } + + #[fixture] + fn _mapping_revealed_attribute_and_predicate() -> CredentialAttributesMapping { + CredentialAttributesMapping { + revealed_attributes: HashSet::from(["attr1_referent".to_string()]), + predicates: HashSet::from(["predicate1_referent".to_string()]), + ..Default::default() + } + } + + #[fixture] + fn _mapping_predicate() -> CredentialAttributesMapping { + CredentialAttributesMapping { + predicates: HashSet::from(["predicate1_referent".to_string()]), + ..Default::default() + } + } + + #[fixture] + fn _mapping_revealed_attribute_and_group() -> CredentialAttributesMapping { + CredentialAttributesMapping { + revealed_attributes: HashSet::from(["attr1_referent".to_string()]), + revealed_attribute_groups: HashSet::from(["attr2_referent".to_string()]), + ..Default::default() + } + } + #[rstest] - #[case(_presentation_request_with_single_attribute())] - #[case(_presentation_request_with_attribute_and_predicate())] - #[case(_presentation_request_with_multiple_attributes())] - #[case(_presentation_request_with_attribute_names())] - #[case(_presentation_request_with_predicate())] - #[case(_presentation_request_with_attribute_restrictions())] - #[case(_presentation_request_with_case_insensitive_attribute_and_predicate())] - #[case(_presentation_request_with_non_revoke_interval())] + #[case( + _presentation_request_with_single_attribute(), + _mapping_single_revealed_attribute() + )] + #[case( + _presentation_request_with_attribute_and_predicate(), + _mapping_revealed_attribute_and_predicate() + )] + #[case( + _presentation_request_with_multiple_attributes(), + _mapping_multiple_revealed_attribute() + )] + #[case( + _presentation_request_with_attribute_group(), + _mapping_single_revealed_attribute_group() + )] + #[case(_presentation_request_with_predicate(), _mapping_predicate())] + #[case( + _presentation_request_with_attribute_restrictions(), + _mapping_single_revealed_attribute() + )] + #[case( + _presentation_request_with_case_insensitive_attribute_and_predicate(), + _mapping_revealed_attribute_and_predicate() + )] + #[case( + _presentation_request_with_non_revoke_interval(), + _mapping_single_revealed_attribute() + )] + #[case( + _presentation_request_with_single_attribute(), + _mapping_single_unrevealed_attribute_group() + )] fn test_check_request_data_works_for_positive_cases( schemas: HashMap, cred_defs: HashMap, - presentation: W3CPresentation, #[case] presentation_request: PresentationRequestPayload, + #[case] mapping: CredentialAttributesMapping, ) { + let presentation_ = presentation(mapping); check_request_data( &presentation_request, - &presentation, + &presentation_, + &presentation_.credential_proofs(), &schemas, &cred_defs, - None, - &presentation.credential_proofs(), ) .unwrap(); } #[rstest] - #[case(_presentation_request_with_missing_attribute())] - #[case(_presentation_request_with_missing_predicate())] - #[case(_presentation_request_with_missing_attribute_group())] - #[case(_presentation_request_with_invalid_predicate_restrictions())] - #[case(_presentation_request_with_invalid_attribute_restrictions())] - #[case(_presentation_request_with_different_predicate())] - #[case(_presentation_request_with_invalid_non_revoke_interval())] + #[case( + _presentation_request_with_missing_attribute(), + _mapping_single_revealed_attribute() + )] + #[rstest] + #[case(_presentation_request_with_single_attribute(), _mapping_empty())] + #[case(_presentation_request_with_missing_predicate(), _mapping_predicate())] + #[case(_presentation_request_with_predicate(), _mapping_empty())] + #[case(_presentation_request_with_unrevealed_attribute(), _mapping_empty())] + #[case( + _presentation_request_with_missing_attribute_group(), + _mapping_single_revealed_attribute_group() + )] + #[case(_presentation_request_with_attribute_group(), _mapping_empty())] + #[case( + _presentation_request_with_invalid_predicate_restrictions(), + _mapping_predicate() + )] + #[case( + _presentation_request_with_invalid_attribute_restrictions(), + _mapping_single_revealed_attribute() + )] fn test_check_request_data_works_for_negative_cases( schemas: HashMap, cred_defs: HashMap, - presentation: W3CPresentation, #[case] presentation_request: PresentationRequestPayload, + #[case] mapping: CredentialAttributesMapping, ) { + let presentation_ = presentation(mapping); let err = check_request_data( &presentation_request, - &presentation, + &presentation_, + &presentation_.credential_proofs(), &schemas, &cred_defs, - None, - &presentation.credential_proofs(), ) .unwrap_err(); assert_eq!(ErrorKind::Input, err.kind()); } - #[rstest] - fn test_check_request_data_works_for_unrevealed_attributes( - schemas: HashMap, - cred_defs: HashMap, - mut presentation: W3CPresentation, - ) { - // empty credential_subject means there is no revealed attributes - only unrevealed - presentation.verifiable_credential[0] - .credential_subject - .attributes = CredentialAttributes::default(); - - check_request_data( - &_presentation_request_with_single_attribute(), - &presentation, - &schemas, - &cred_defs, - None, - &presentation.credential_proofs(), - ) - .unwrap(); - } - #[rstest] fn test_check_request_data_fails_for_presentation_with_empty_credential_list( schemas: HashMap, cred_defs: HashMap, - mut presentation: W3CPresentation, ) { - presentation.verifiable_credential = Vec::default(); + let mut presentation_ = presentation(_mapping_single_revealed_attribute()); + presentation_.verifiable_credential = Vec::default(); let err = check_request_data( &_presentation_request_with_single_attribute(), - &presentation, + &presentation_, + &presentation_.credential_proofs(), &schemas, &cred_defs, - None, - &presentation.credential_proofs(), ) .unwrap_err(); assert_eq!(ErrorKind::Input, err.kind()); @@ -813,81 +832,32 @@ pub(crate) mod tests { #[rstest] fn test_check_request_data_fails_for_empty_schema( cred_defs: HashMap, - presentation: W3CPresentation, ) { let schemas = HashMap::new(); + let presentation_ = &presentation(_mapping_single_revealed_attribute()); let err = check_request_data( &_presentation_request_with_attribute_restrictions(), - &presentation, + &presentation_, + &presentation_.credential_proofs(), &schemas, &cred_defs, - None, - &presentation.credential_proofs(), ) .unwrap_err(); assert_eq!(ErrorKind::Input, err.kind()); } #[rstest] - fn test_check_request_data_fails_for_empty_cred_defs( - schemas: HashMap, - presentation: W3CPresentation, - ) { + fn test_check_request_data_fails_for_empty_cred_defs(schemas: HashMap) { let cred_defs = HashMap::new(); + let presentation_ = &presentation(_mapping_single_revealed_attribute()); let err = check_request_data( &_presentation_request_with_attribute_restrictions(), - &presentation, - &schemas, - &cred_defs, - None, - &presentation.credential_proofs(), - ) - .unwrap_err(); - assert_eq!(ErrorKind::Input, err.kind()); - } - - #[rstest] - #[case(_presentation_request_with_non_revoke_interval())] - fn test_check_request_data_works_for_valid_non_revoke_interval_override( - schemas: HashMap, - cred_defs: HashMap, - presentation: W3CPresentation, - #[case] presentation_request: PresentationRequestPayload, - ) { - let interval_override = - _non_revoke_override_interval(PROOF_TIMESTAMP_FROM, PROOF_TIMESTAMP_FROM + 1); - - check_request_data( - &presentation_request, - &presentation, - &schemas, - &cred_defs, - Some(&interval_override), - &presentation.credential_proofs(), - ) - .unwrap(); - } - - #[rstest] - #[case(_presentation_request_with_non_revoke_interval())] - fn test_check_request_data_fails_for_invalid_non_revoke_interval_override( - schemas: HashMap, - cred_defs: HashMap, - presentation: W3CPresentation, - #[case] presentation_request: PresentationRequestPayload, - ) { - let interval_override = - _non_revoke_override_interval(PROOF_TIMESTAMP_FROM, PROOF_TIMESTAMP + 1); - - let err = check_request_data( - &presentation_request, - &presentation, + &presentation_, + &presentation_.credential_proofs(), &schemas, &cred_defs, - Some(&interval_override), - &presentation.credential_proofs(), ) .unwrap_err(); assert_eq!(ErrorKind::Input, err.kind()); diff --git a/tests/anoncreds_demos.rs b/tests/anoncreds_demos.rs index 8f29be2d..e1ffb130 100644 --- a/tests/anoncreds_demos.rs +++ b/tests/anoncreds_demos.rs @@ -1,6 +1,4 @@ -use anoncreds::data_types::pres_request::PredicateTypes; use anoncreds::data_types::w3c::credential_attributes::CredentialAttributeValue; -use anoncreds::data_types::w3c::presentation::{PredicateAttribute, PredicateAttributeType}; use anoncreds::data_types::w3c::VerifiableCredentialSpecVersion; use anoncreds::verifier; use rstest::rstest; @@ -2976,11 +2974,7 @@ fn anoncreds_demo_works_for_issue_legacy_credential_convert_into_w3c_and_present ); assert_eq!( - CredentialAttributeValue::Predicate(vec![PredicateAttribute { - type_: PredicateAttributeType::AnonCredsPredicate, - predicate: PredicateTypes::GE, - value: 18, - }]), + CredentialAttributeValue::Predicate(true), presentation.verifiable_credential[0] .credential_subject .attributes