1use std::borrow::Cow;
2use std::collections::{HashMap, HashSet};
3
4use once_cell::sync::Lazy;
5use regex::Regex;
6
7use super::helpers::*;
8use super::types::*;
9use crate::anoncreds_clsignatures::{CredentialPublicKey, Verifier as ClVerifier};
10use crate::error::Result;
11use indy_data_types::anoncreds::{
12 nonce::Nonce,
13 pres_request::{AttributeInfo, NonRevocedInterval, PredicateInfo, PresentationRequestPayload},
14 presentation::{Identifier, RequestedProof, RevealedAttributeInfo},
15 wql::Query,
16};
17
18#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
19pub struct Filter {
20 schema_id: String,
21 schema_issuer_did: String,
22 schema_name: String,
23 schema_version: String,
24 issuer_did: String,
25 cred_def_id: String,
26}
27
28static INTERNAL_TAG_MATCHER: Lazy<Regex> =
29 Lazy::new(|| Regex::new("^attr::([^:]+)::(value|marker)$").unwrap());
30
31pub fn verify_presentation(
32 presentation: &Presentation,
33 pres_req: &PresentationRequest,
34 schemas: &HashMap<SchemaId, &Schema>,
35 cred_defs: &HashMap<CredentialDefinitionId, &CredentialDefinition>,
36 rev_reg_defs: Option<&HashMap<RevocationRegistryId, &RevocationRegistryDefinition>>,
37 rev_regs: Option<&HashMap<RevocationRegistryId, HashMap<u64, &RevocationRegistry>>>,
38) -> Result<bool> {
39 _verify_presentation(
40 presentation,
41 pres_req,
42 schemas,
43 cred_defs,
44 rev_reg_defs,
45 rev_regs,
46 false,
47 )
48}
49
50pub fn verify_presentation_legacy(
52 presentation: &Presentation,
53 pres_req: &PresentationRequest,
54 schemas: &HashMap<SchemaId, &Schema>,
55 cred_defs: &HashMap<CredentialDefinitionId, &CredentialDefinition>,
56 rev_reg_defs: Option<&HashMap<RevocationRegistryId, &RevocationRegistryDefinition>>,
57 rev_regs: Option<&HashMap<RevocationRegistryId, HashMap<u64, &RevocationRegistry>>>,
58) -> Result<bool> {
59 _verify_presentation(
60 presentation,
61 pres_req,
62 schemas,
63 cred_defs,
64 rev_reg_defs,
65 rev_regs,
66 true,
67 )
68}
69
70pub(crate) fn _verify_presentation(
71 presentation: &Presentation,
72 pres_req: &PresentationRequest,
73 schemas: &HashMap<SchemaId, &Schema>,
74 cred_defs: &HashMap<CredentialDefinitionId, &CredentialDefinition>,
75 rev_reg_defs: Option<&HashMap<RevocationRegistryId, &RevocationRegistryDefinition>>,
76 rev_regs: Option<&HashMap<RevocationRegistryId, HashMap<u64, &RevocationRegistry>>>,
77 accept_legacy_revocation: bool,
78) -> Result<bool> {
79 trace!("verify >>> presentation: {:?}, pres_req: {:?}, schemas: {:?}, cred_defs: {:?}, rev_reg_defs: {:?} rev_regs: {:?}",
80 presentation, pres_req, schemas, cred_defs, rev_reg_defs, rev_regs);
81
82 let pres_req = pres_req.value();
83 let received_revealed_attrs: HashMap<String, Identifier> =
84 received_revealed_attrs(presentation)?;
85 let received_unrevealed_attrs: HashMap<String, Identifier> =
86 received_unrevealed_attrs(presentation)?;
87 let received_predicates: HashMap<String, Identifier> = received_predicates(presentation)?;
88 let received_self_attested_attrs: HashSet<String> = received_self_attested_attrs(presentation);
89
90 compare_attr_from_proof_and_request(
91 pres_req,
92 &received_revealed_attrs,
93 &received_unrevealed_attrs,
94 &received_self_attested_attrs,
95 &received_predicates,
96 )?;
97
98 verify_revealed_attribute_values(pres_req, presentation)?;
99
100 verify_requested_restrictions(
101 pres_req,
102 &presentation.requested_proof,
103 &received_revealed_attrs,
104 &received_unrevealed_attrs,
105 &received_predicates,
106 &received_self_attested_attrs,
107 )?;
108
109 compare_timestamps_from_proof_and_request(
110 pres_req,
111 &received_revealed_attrs,
112 &received_unrevealed_attrs,
113 &received_self_attested_attrs,
114 &received_predicates,
115 )?;
116
117 let mut proof_verifier = ClVerifier::new_proof_verifier()?;
118 proof_verifier.accept_legacy_revocation(accept_legacy_revocation);
119 let non_credential_schema = build_non_credential_schema()?;
120
121 for sub_proof_index in 0..presentation.identifiers.len() {
122 let identifier = presentation.identifiers[sub_proof_index].clone();
123
124 let Schema::SchemaV1(schema) = schemas
125 .get(&identifier.schema_id)
126 .ok_or_else(|| err_msg!("Schema not provided for ID: {:?}", identifier.schema_id))?;
127
128 let CredentialDefinition::CredentialDefinitionV1(cred_def) =
129 cred_defs.get(&identifier.cred_def_id).ok_or_else(|| {
130 err_msg!(
131 "Credential Definition not provided for ID: {:?}",
132 identifier.cred_def_id
133 )
134 })?;
135
136 let (rev_reg_def, rev_reg) = if let Some(timestamp) = identifier.timestamp {
137 let rev_reg_id = identifier.rev_reg_id.clone().ok_or_else(|| {
138 err_msg!("Timestamp provided but Revocation Registry Id not found")
139 })?;
140 if rev_reg_defs.is_none() {
141 return Err(err_msg!(
142 "Timestamp provided but no Revocation Registry Definitions found"
143 ));
144 }
145 if rev_regs.is_none() {
146 return Err(err_msg!(
147 "Timestamp provided but no Revocation Registries found"
148 ));
149 }
150
151 let rev_reg_def = Some(rev_reg_defs.as_ref().unwrap().get(&rev_reg_id).ok_or_else(
152 || {
153 err_msg!(
154 "Revocation Registry Definition not provided for ID: {:?}",
155 rev_reg_id
156 )
157 },
158 )?);
159
160 let rev_reg = Some(
161 rev_regs
162 .as_ref()
163 .unwrap()
164 .get(&rev_reg_id)
165 .and_then(|regs| regs.get(×tamp))
166 .ok_or_else(|| {
167 err_msg!(
168 "Revocation Registry not provided for ID and timestamp: {:?}, {:?}",
169 rev_reg_id,
170 timestamp
171 )
172 })?,
173 );
174
175 (rev_reg_def, rev_reg)
176 } else {
177 (None, None)
178 };
179
180 let attrs_for_credential = get_revealed_attributes_for_credential(
181 sub_proof_index,
182 &presentation.requested_proof,
183 pres_req,
184 )?;
185 let predicates_for_credential = get_predicates_for_credential(
186 sub_proof_index,
187 &presentation.requested_proof,
188 pres_req,
189 )?;
190
191 let credential_schema = build_credential_schema(&schema.attr_names.0)?;
192 let sub_pres_request =
193 build_sub_proof_request(&attrs_for_credential, &predicates_for_credential)?;
194
195 let credential_pub_key = CredentialPublicKey::build_from_parts(
196 &cred_def.value.primary,
197 cred_def.value.revocation.as_ref(),
198 )?;
199
200 let rev_key_pub = rev_reg_def.as_ref().map(|r_reg_def| match r_reg_def {
201 RevocationRegistryDefinition::RevocationRegistryDefinitionV1(reg_def) => {
202 ®_def.value.public_keys.accum_key
203 }
204 });
205 let rev_reg = rev_reg.as_ref().map(|r_reg| match r_reg {
206 RevocationRegistry::RevocationRegistryV1(reg_def) => ®_def.value,
207 });
208
209 proof_verifier.add_sub_proof_request(
210 &sub_pres_request,
211 &credential_schema,
212 &non_credential_schema,
213 &credential_pub_key,
214 rev_key_pub,
215 rev_reg,
216 )?;
217 }
218
219 let valid = proof_verifier.verify(&presentation.proof, pres_req.nonce.as_native())?;
220
221 trace!("verify <<< valid: {:?}", valid);
222
223 Ok(valid)
224}
225
226pub fn generate_nonce() -> Result<Nonce> {
227 new_nonce()
228}
229
230fn get_revealed_attributes_for_credential(
231 sub_proof_index: usize,
232 requested_proof: &RequestedProof,
233 pres_req: &PresentationRequestPayload,
234) -> Result<Vec<AttributeInfo>> {
235 trace!("_get_revealed_attributes_for_credential >>> sub_proof_index: {:?}, requested_credentials: {:?}, pres_req: {:?}",
236 sub_proof_index, requested_proof, pres_req);
237
238 let mut revealed_attrs_for_credential = requested_proof
239 .revealed_attrs
240 .iter()
241 .filter(|&(attr_referent, revealed_attr_info)| {
242 sub_proof_index == revealed_attr_info.sub_proof_index as usize
243 && pres_req.requested_attributes.contains_key(attr_referent)
244 })
245 .map(|(attr_referent, _)| pres_req.requested_attributes[attr_referent].clone())
246 .collect::<Vec<AttributeInfo>>();
247
248 revealed_attrs_for_credential.append(
249 &mut requested_proof
250 .revealed_attr_groups
251 .iter()
252 .filter(|&(attr_referent, revealed_attr_info)| {
253 sub_proof_index == revealed_attr_info.sub_proof_index as usize
254 && pres_req.requested_attributes.contains_key(attr_referent)
255 })
256 .map(|(attr_referent, _)| pres_req.requested_attributes[attr_referent].clone())
257 .collect::<Vec<AttributeInfo>>(),
258 );
259
260 trace!(
261 "_get_revealed_attributes_for_credential <<< revealed_attrs_for_credential: {:?}",
262 revealed_attrs_for_credential
263 );
264
265 Ok(revealed_attrs_for_credential)
266}
267
268fn get_predicates_for_credential(
269 sub_proof_index: usize,
270 requested_proof: &RequestedProof,
271 pres_req: &PresentationRequestPayload,
272) -> Result<Vec<PredicateInfo>> {
273 trace!("_get_predicates_for_credential >>> sub_proof_index: {:?}, requested_credentials: {:?}, pres_req: {:?}",
274 sub_proof_index, requested_proof, pres_req);
275
276 let predicates_for_credential = requested_proof
277 .predicates
278 .iter()
279 .filter(|&(predicate_referent, requested_referent)| {
280 sub_proof_index == requested_referent.sub_proof_index as usize
281 && pres_req
282 .requested_predicates
283 .contains_key(predicate_referent)
284 })
285 .map(|(predicate_referent, _)| pres_req.requested_predicates[predicate_referent].clone())
286 .collect::<Vec<PredicateInfo>>();
287
288 trace!(
289 "_get_predicates_for_credential <<< predicates_for_credential: {:?}",
290 predicates_for_credential
291 );
292
293 Ok(predicates_for_credential)
294}
295
296fn compare_attr_from_proof_and_request(
297 pres_req: &PresentationRequestPayload,
298 received_revealed_attrs: &HashMap<String, Identifier>,
299 received_unrevealed_attrs: &HashMap<String, Identifier>,
300 received_self_attested_attrs: &HashSet<String>,
301 received_predicates: &HashMap<String, Identifier>,
302) -> Result<()> {
303 let requested_attrs: HashSet<String> = pres_req.requested_attributes.keys().cloned().collect();
304
305 let received_attrs: HashSet<String> = received_revealed_attrs
306 .iter()
307 .chain(received_unrevealed_attrs)
308 .map(|(r, _)| r.to_string())
309 .collect::<HashSet<String>>()
310 .union(received_self_attested_attrs)
311 .cloned()
312 .collect();
313
314 if requested_attrs != received_attrs {
315 return Err(err_msg!(
316 "Requested attributes {:?} do not correspond to received {:?}",
317 requested_attrs,
318 received_attrs
319 ));
320 }
321
322 let requested_predicates: HashSet<&String> = pres_req.requested_predicates.keys().collect();
323
324 let received_predicates_: HashSet<&String> = received_predicates.keys().collect();
325
326 if requested_predicates != received_predicates_ {
327 return Err(err_msg!(
328 "Requested predicates {:?} do not correspond to received {:?}",
329 requested_predicates,
330 received_predicates
331 ));
332 }
333
334 Ok(())
335}
336
337fn compare_timestamps_from_proof_and_request(
338 pres_req: &PresentationRequestPayload,
339 received_revealed_attrs: &HashMap<String, Identifier>,
340 received_unrevealed_attrs: &HashMap<String, Identifier>,
341 received_self_attested_attrs: &HashSet<String>,
342 received_predicates: &HashMap<String, Identifier>,
343) -> Result<()> {
344 pres_req
345 .requested_attributes
346 .iter()
347 .map(|(referent, info)| {
348 validate_timestamp(
349 received_revealed_attrs,
350 referent,
351 &pres_req.non_revoked,
352 &info.non_revoked,
353 )
354 .or_else(|_| {
355 validate_timestamp(
356 received_unrevealed_attrs,
357 referent,
358 &pres_req.non_revoked,
359 &info.non_revoked,
360 )
361 })
362 .or_else(|_| {
363 received_self_attested_attrs
364 .get(referent)
365 .map(|_| ())
366 .ok_or_else(|| err_msg!("Missing referent"))
367 })
368 })
369 .collect::<Result<Vec<()>>>()?;
370
371 pres_req
372 .requested_predicates
373 .iter()
374 .map(|(referent, info)| {
375 validate_timestamp(
376 received_predicates,
377 referent,
378 &pres_req.non_revoked,
379 &info.non_revoked,
380 )
381 })
382 .collect::<Result<Vec<()>>>()?;
383
384 Ok(())
385}
386
387fn validate_timestamp(
388 received_: &HashMap<String, Identifier>,
389 referent: &str,
390 global_interval: &Option<NonRevocedInterval>,
391 local_interval: &Option<NonRevocedInterval>,
392) -> Result<()> {
393 if get_non_revoc_interval(global_interval, local_interval).is_none() {
394 return Ok(());
395 }
396
397 if !received_
398 .get(referent)
399 .map(|attr| attr.timestamp.is_some())
400 .unwrap_or(false)
401 {
402 return Err(err_msg!("Missing timestamp"));
403 }
404
405 Ok(())
406}
407
408fn received_revealed_attrs(proof: &Presentation) -> Result<HashMap<String, Identifier>> {
409 let mut revealed_identifiers: HashMap<String, Identifier> = HashMap::new();
410 for (referent, info) in proof.requested_proof.revealed_attrs.iter() {
411 revealed_identifiers.insert(
412 referent.to_string(),
413 get_proof_identifier(proof, info.sub_proof_index)?,
414 );
415 }
416 for (referent, infos) in proof.requested_proof.revealed_attr_groups.iter() {
417 revealed_identifiers.insert(
418 referent.to_string(),
419 get_proof_identifier(proof, infos.sub_proof_index)?,
420 );
421 }
422 Ok(revealed_identifiers)
423}
424
425fn received_unrevealed_attrs(proof: &Presentation) -> Result<HashMap<String, Identifier>> {
426 let mut unrevealed_identifiers: HashMap<String, Identifier> = HashMap::new();
427 for (referent, info) in proof.requested_proof.unrevealed_attrs.iter() {
428 unrevealed_identifiers.insert(
429 referent.to_string(),
430 get_proof_identifier(proof, info.sub_proof_index)?,
431 );
432 }
433 Ok(unrevealed_identifiers)
434}
435
436fn received_predicates(proof: &Presentation) -> Result<HashMap<String, Identifier>> {
437 let mut predicate_identifiers: HashMap<String, Identifier> = HashMap::new();
438 for (referent, info) in proof.requested_proof.predicates.iter() {
439 predicate_identifiers.insert(
440 referent.to_string(),
441 get_proof_identifier(proof, info.sub_proof_index)?,
442 );
443 }
444 Ok(predicate_identifiers)
445}
446
447fn received_self_attested_attrs(proof: &Presentation) -> HashSet<String> {
448 proof
449 .requested_proof
450 .self_attested_attrs
451 .keys()
452 .cloned()
453 .collect()
454}
455
456fn get_proof_identifier(proof: &Presentation, index: u32) -> Result<Identifier> {
457 proof
458 .identifiers
459 .get(index as usize)
460 .cloned()
461 .ok_or_else(|| err_msg!("Identifier not found for index: {}", index))
462}
463
464fn verify_revealed_attribute_values(
465 pres_req: &PresentationRequestPayload,
466 proof: &Presentation,
467) -> Result<()> {
468 for (attr_referent, attr_info) in proof.requested_proof.revealed_attrs.iter() {
469 let attr_name = pres_req
470 .requested_attributes
471 .get(attr_referent)
472 .as_ref()
473 .ok_or_else(|| {
474 err_msg!(
475 ProofRejected,
476 "Attribute with referent \"{}\" not found in ProofRequests",
477 attr_referent
478 )
479 })?
480 .name
481 .as_ref()
482 .ok_or_else(|| {
483 err_msg!(
484 ProofRejected,
485 "Attribute with referent \"{}\" not found in ProofRequests",
486 attr_referent,
487 )
488 })?;
489 verify_revealed_attribute_value(attr_name.as_str(), proof, attr_info)?;
490 }
491
492 for (attr_referent, attr_infos) in proof.requested_proof.revealed_attr_groups.iter() {
493 let attr_names = pres_req
494 .requested_attributes
495 .get(attr_referent)
496 .as_ref()
497 .ok_or_else(|| {
498 err_msg!(
499 ProofRejected,
500 "Attribute with referent \"{}\" not found in ProofRequests",
501 attr_referent,
502 )
503 })?
504 .names
505 .as_ref()
506 .ok_or_else(|| {
507 err_msg!(
508 ProofRejected,
509 "Attribute with referent \"{}\" not found in ProofRequests",
510 attr_referent,
511 )
512 })?;
513 if attr_infos.values.len() != attr_names.len() {
514 error!("Proof Revealed Attr Group does not match Proof Request Attribute Group, proof request attrs: {:?}, referent: {:?}, attr_infos: {:?}", pres_req.requested_attributes, attr_referent, attr_infos);
515 return Err(err_msg!(
516 "Proof Revealed Attr Group does not match Proof Request Attribute Group",
517 ));
518 }
519 for attr_name in attr_names {
520 let attr_info = &attr_infos.values.get(attr_name).ok_or_else(|| {
521 err_msg!("Proof Revealed Attr Group does not match Proof Request Attribute Group",)
522 })?;
523 verify_revealed_attribute_value(
524 attr_name,
525 proof,
526 &RevealedAttributeInfo {
527 sub_proof_index: attr_infos.sub_proof_index,
528 raw: attr_info.raw.clone(),
529 encoded: attr_info.encoded.clone(),
530 },
531 )?;
532 }
533 }
534 Ok(())
535}
536
537fn normalize_encoded_attr(attr: &str) -> Cow<'_, str> {
538 attr.parse::<i32>()
539 .map(|iattr| Cow::Owned(iattr.to_string()))
540 .unwrap_or_else(|_| Cow::Borrowed(attr))
541}
542
543fn verify_revealed_attribute_value(
544 attr_name: &str,
545 proof: &Presentation,
546 attr_info: &RevealedAttributeInfo,
547) -> Result<()> {
548 let reveal_attr_encoded = normalize_encoded_attr(&attr_info.encoded);
549 let sub_proof_index = attr_info.sub_proof_index as usize;
550
551 let crypto_proof_encoded = proof
552 .proof
553 .proofs
554 .get(sub_proof_index)
555 .ok_or_else(|| {
556 err_msg!(
557 ProofRejected,
558 "CryptoProof not found by index \"{}\"",
559 sub_proof_index,
560 )
561 })?
562 .revealed_attrs()?
563 .iter()
564 .find(|(key, _)| attr_common_view(attr_name) == attr_common_view(key))
565 .map(|(_, val)| val.to_string())
566 .ok_or_else(|| {
567 err_msg!(
568 ProofRejected,
569 "Attribute with name \"{}\" not found in CryptoProof",
570 attr_name,
571 )
572 })?;
573
574 if reveal_attr_encoded != crypto_proof_encoded {
575 return Err(err_msg!(ProofRejected,
576 "Encoded Values for \"{}\" are different in RequestedProof \"{}\" and CryptoProof \"{}\"", attr_name, reveal_attr_encoded, crypto_proof_encoded));
577 }
578
579 Ok(())
580}
581
582fn verify_requested_restrictions(
583 pres_req: &PresentationRequestPayload,
584 requested_proof: &RequestedProof,
585 received_revealed_attrs: &HashMap<String, Identifier>,
586 received_unrevealed_attrs: &HashMap<String, Identifier>,
587 received_predicates: &HashMap<String, Identifier>,
588 self_attested_attrs: &HashSet<String>,
589) -> Result<()> {
590 let proof_attr_identifiers: HashMap<String, Identifier> = received_revealed_attrs
591 .iter()
592 .chain(received_unrevealed_attrs)
593 .map(|(r, id)| (r.to_string(), id.clone()))
594 .collect();
595
596 let requested_attrs: HashMap<String, AttributeInfo> = pres_req
597 .requested_attributes
598 .iter()
599 .filter(|&(referent, info)| !is_self_attested(referent, info, self_attested_attrs))
600 .map(|(referent, info)| (referent.to_string(), info.clone()))
601 .collect();
602
603 for (referent, info) in requested_attrs.iter() {
604 if let Some(ref query) = info.restrictions {
605 let filter = gather_filter_info(referent, &proof_attr_identifiers)?;
606
607 let attr_value_map: HashMap<String, Option<&str>> = if let Some(name) =
608 info.name.as_ref()
609 {
610 let mut map = HashMap::new();
611 map.insert(
612 name.clone(),
613 requested_proof
614 .revealed_attrs
615 .get(referent)
616 .map(|attr| attr.raw.as_str()),
617 );
618 map
619 } else if let Some(names) = info.names.as_ref() {
620 let mut map = HashMap::new();
621 let attrs = requested_proof
622 .revealed_attr_groups
623 .get(referent)
624 .ok_or_else(|| err_msg!("Proof does not have referent from proof request"))?;
625 for name in names {
626 let val = attrs.values.get(name).map(|attr| attr.raw.as_str());
627 map.insert(name.clone(), val);
628 }
629 map
630 } else {
631 error!(
632 r#"Proof Request attribute restriction should contain "name" or "names" param. Current proof request: {:?}"#,
633 pres_req
634 );
635 return Err(err_msg!(
636 r#"Proof Request attribute restriction should contain "name" or "names" param"#,
637 ));
638 };
639
640 process_operator(&attr_value_map, query, &filter).map_err(err_map!(
641 "Requested restriction validation failed for \"{:?}\" attributes",
642 &attr_value_map
643 ))?;
644 }
645 }
646
647 for (referent, info) in pres_req.requested_predicates.iter() {
648 if let Some(ref query) = info.restrictions {
649 let filter = gather_filter_info(referent, received_predicates)?;
650
651 let mut attr_value_map = HashMap::new();
653 attr_value_map.insert(info.name.to_string(), None);
654
655 let pred_sub_proof_index = requested_proof
657 .predicates
658 .get(referent)
659 .unwrap()
660 .sub_proof_index;
661 for attr_referent in requested_proof.revealed_attrs.keys() {
662 let attr_info = requested_proof.revealed_attrs.get(attr_referent).unwrap();
663 let attr_sub_proof_index = attr_info.sub_proof_index;
664 if pred_sub_proof_index == attr_sub_proof_index {
665 let attr_name = requested_attrs.get(attr_referent).unwrap().name.clone();
666 if let Some(name) = attr_name {
667 attr_value_map.insert(name, Some(attr_info.raw.as_str()));
668 }
669 }
670 }
671 for attr_referent in requested_proof.revealed_attr_groups.keys() {
672 let attr_info = requested_proof
673 .revealed_attr_groups
674 .get(attr_referent)
675 .unwrap();
676 let attr_sub_proof_index = attr_info.sub_proof_index;
677 if pred_sub_proof_index == attr_sub_proof_index {
678 for name in attr_info.values.keys() {
679 let raw_val = attr_info.values.get(name).unwrap().raw.as_str();
680 attr_value_map.insert(name.clone(), Some(raw_val));
681 }
682 }
683 }
684
685 process_operator(&attr_value_map, query, &filter).map_err(err_map!(
686 "Requested restriction validation failed for \"{}\" predicate",
687 &info.name
688 ))?;
689 }
690 }
691
692 Ok(())
693}
694
695fn is_self_attested(
696 referent: &str,
697 info: &AttributeInfo,
698 self_attested_attrs: &HashSet<String>,
699) -> bool {
700 match info.restrictions.as_ref() {
701 Some(&Query::And(ref array)) | Some(&Query::Or(ref array)) if array.is_empty() => {
702 self_attested_attrs.contains(referent)
703 }
704 None => self_attested_attrs.contains(referent),
705 Some(_) => false,
706 }
707}
708
709fn gather_filter_info(referent: &str, identifiers: &HashMap<String, Identifier>) -> Result<Filter> {
710 let identifier = identifiers.get(referent).ok_or_else(|| {
711 err_msg!(
712 InvalidState,
713 "Identifier not found for referent: {}",
714 referent
715 )
716 })?;
717
718 let (_, schema_issuer_did, schema_name, schema_version) =
719 identifier.schema_id.parts().ok_or_else(|| {
720 err_msg!(
721 "Invalid Schema ID `{}`: wrong number of parts",
722 identifier.schema_id.0
723 )
724 })?;
725
726 let issuer_did = identifier.cred_def_id.issuer_did().ok_or_else(|| {
727 err_msg!(
728 "Invalid Credential Definition ID `{}`: wrong number of parts",
729 identifier.cred_def_id.0
730 )
731 })?;
732
733 Ok(Filter {
734 schema_id: identifier.schema_id.0.to_string(),
735 schema_name,
736 schema_issuer_did: schema_issuer_did.0,
737 schema_version,
738 cred_def_id: identifier.cred_def_id.0.to_string(),
739 issuer_did: issuer_did.0,
740 })
741}
742
743fn process_operator(
744 attr_value_map: &HashMap<String, Option<&str>>,
745 restriction_op: &Query,
746 filter: &Filter,
747) -> Result<()> {
748 match restriction_op {
749 Query::Eq(ref tag_name, ref tag_value) => {
750 process_filter(attr_value_map, tag_name, tag_value, filter).map_err(err_map!(
751 "$eq operator validation failed for tag: \"{}\", value: \"{}\"",
752 tag_name,
753 tag_value
754 ))
755 }
756 Query::Neq(ref tag_name, ref tag_value) => {
757 if process_filter(attr_value_map, tag_name, tag_value, filter).is_err() {
758 Ok(())
759 } else {
760 Err(err_msg!(ProofRejected,
761 "$neq operator validation failed for tag: \"{}\", value: \"{}\". Condition was passed.", tag_name, tag_value))
762 }
763 }
764 Query::In(ref tag_name, ref tag_values) => {
765 let res = tag_values
766 .iter()
767 .any(|val| process_filter(attr_value_map, tag_name, val, filter).is_ok());
768 if res {
769 Ok(())
770 } else {
771 Err(err_msg!(
772 ProofRejected,
773 "$in operator validation failed for tag: \"{}\", values \"{:?}\".",
774 tag_name,
775 tag_values,
776 ))
777 }
778 }
779 Query::And(ref operators) => operators
780 .iter()
781 .map(|op| process_operator(attr_value_map, op, filter))
782 .collect::<Result<Vec<()>>>()
783 .map(|_| ())
784 .map_err(err_map!("$and operator validation failed.")),
785 Query::Or(ref operators) => {
786 let res = operators
787 .iter()
788 .any(|op| process_operator(attr_value_map, op, filter).is_ok());
789 if res {
790 Ok(())
791 } else {
792 Err(err_msg!(
793 ProofRejected,
794 "$or operator validation failed. All conditions were failed.",
795 ))
796 }
797 }
798 Query::Not(ref operator) => {
799 if process_operator(attr_value_map, operator, filter).is_err() {
800 Ok(())
801 } else {
802 Err(err_msg!(
803 ProofRejected,
804 "$not operator validation failed. All conditions were passed.",
805 ))
806 }
807 }
808 _ => Err(err_msg!(ProofRejected, "unsupported operator",)),
809 }
810}
811
812fn process_filter(
813 attr_value_map: &HashMap<String, Option<&str>>,
814 tag: &str,
815 tag_value: &str,
816 filter: &Filter,
817) -> Result<()> {
818 trace!(
819 "_process_filter: attr_value_map: {:?}, tag: {}, tag_value: {}, filter: {:?}",
820 attr_value_map,
821 tag,
822 tag_value,
823 filter
824 );
825 match tag {
826 tag_ @ "schema_id" => precess_filed(tag_, &filter.schema_id, tag_value),
827 tag_ @ "schema_issuer_did" => precess_filed(tag_, &filter.schema_issuer_did, tag_value),
828 tag_ @ "schema_name" => precess_filed(tag_, &filter.schema_name, tag_value),
829 tag_ @ "schema_version" => precess_filed(tag_, &filter.schema_version, tag_value),
830 tag_ @ "cred_def_id" => precess_filed(tag_, &filter.cred_def_id, tag_value),
831 tag_ @ "issuer_did" => precess_filed(tag_, &filter.issuer_did, tag_value),
832 x if is_attr_internal_tag(x, attr_value_map) => {
833 check_internal_tag_revealed_value(x, tag_value, attr_value_map)
834 }
835 x if is_attr_operator(x) => Ok(()),
836 _ => Err(err_msg!("Unknown Filter Type")),
837 }
838}
839
840fn precess_filed(filed: &str, filter_value: &str, tag_value: &str) -> Result<()> {
841 if filter_value == tag_value {
842 Ok(())
843 } else {
844 Err(err_msg!(
845 ProofRejected,
846 "\"{}\" values are different: expected: \"{}\", actual: \"{}\"",
847 filed,
848 tag_value,
849 filter_value,
850 ))
851 }
852}
853
854fn is_attr_internal_tag(key: &str, attr_value_map: &HashMap<String, Option<&str>>) -> bool {
855 INTERNAL_TAG_MATCHER
856 .captures(key)
857 .map(|caps| {
858 caps.get(1)
859 .map(|s| attr_value_map.contains_key(&s.as_str().to_string()))
860 .unwrap_or(false)
861 })
862 .unwrap_or(false)
863}
864
865fn check_internal_tag_revealed_value(
866 key: &str,
867 tag_value: &str,
868 attr_value_map: &HashMap<String, Option<&str>>,
869) -> Result<()> {
870 let attr_name = INTERNAL_TAG_MATCHER
871 .captures(key)
872 .ok_or_else(|| err_msg!(InvalidState, "Attribute name became unparseable",))?
873 .get(1)
874 .ok_or_else(|| err_msg!(InvalidState, "No name has been parsed",))?
875 .as_str();
876 if let Some(Some(revealed_value)) = attr_value_map.get(attr_name) {
877 if *revealed_value != tag_value {
878 return Err(err_msg!(
879 ProofRejected,
880 "\"{}\" values are different: expected: \"{}\", actual: \"{}\"",
881 key,
882 tag_value,
883 revealed_value
884 ));
885 }
886 }
887 Ok(())
888}
889
890fn is_attr_operator(key: &str) -> bool {
891 key.starts_with("attr::") && key.ends_with("::marker")
892}
893
894#[cfg(test)]
895mod tests {
896 use super::*;
897
898 pub const SCHEMA_ID: &str = "123";
899 pub const SCHEMA_NAME: &str = "Schema Name";
900 pub const SCHEMA_ISSUER_DID: &str = "234";
901 pub const SCHEMA_VERSION: &str = "1.2.3";
902 pub const CRED_DEF_ID: &str = "345";
903 pub const ISSUER_DID: &str = "456";
904
905 fn schema_id_tag() -> String {
906 "schema_id".to_string()
907 }
908
909 fn schema_name_tag() -> String {
910 "schema_name".to_string()
911 }
912
913 fn schema_issuer_did_tag() -> String {
914 "schema_issuer_did".to_string()
915 }
916
917 fn schema_version_tag() -> String {
918 "schema_version".to_string()
919 }
920
921 fn cred_def_id_tag() -> String {
922 "cred_def_id".to_string()
923 }
924
925 fn issuer_did_tag() -> String {
926 "issuer_did".to_string()
927 }
928
929 fn attr_tag() -> String {
930 "attr::zip::marker".to_string()
931 }
932
933 fn attr_tag_value() -> String {
934 "attr::zip::value".to_string()
935 }
936
937 fn bad_attr_tag() -> String {
938 "bad::zip::marker".to_string()
939 }
940
941 fn filter() -> Filter {
942 Filter {
943 schema_id: SCHEMA_ID.to_string(),
944 schema_name: SCHEMA_NAME.to_string(),
945 schema_issuer_did: SCHEMA_ISSUER_DID.to_string(),
946 schema_version: SCHEMA_VERSION.to_string(),
947 cred_def_id: CRED_DEF_ID.to_string(),
948 issuer_did: ISSUER_DID.to_string(),
949 }
950 }
951
952 fn _process_operator(
953 attr: &str,
954 restriction_op: &Query,
955 filter: &Filter,
956 revealed_value: Option<&str>,
957 ) -> Result<()> {
958 let mut attr_value_map = HashMap::new();
959 attr_value_map.insert(attr.to_string(), revealed_value);
960 process_operator(&attr_value_map, restriction_op, filter)
961 }
962
963 #[test]
964 fn test_process_op_eq() {
965 let filter = filter();
966
967 let mut op = Query::Eq(schema_id_tag(), SCHEMA_ID.to_string());
968 _process_operator("zip", &op, &filter, None).unwrap();
969
970 op = Query::And(vec![
971 Query::Eq(attr_tag(), "1".to_string()),
972 Query::Eq(schema_id_tag(), SCHEMA_ID.to_string()),
973 ]);
974 _process_operator("zip", &op, &filter, None).unwrap();
975
976 op = Query::And(vec![
977 Query::Eq(bad_attr_tag(), "1".to_string()),
978 Query::Eq(schema_id_tag(), SCHEMA_ID.to_string()),
979 ]);
980 assert!(_process_operator("zip", &op, &filter, None).is_err());
981
982 op = Query::Eq(schema_id_tag(), "NOT HERE".to_string());
983 assert!(_process_operator("zip", &op, &filter, None).is_err());
984 }
985
986 #[test]
987 fn test_process_op_ne() {
988 let filter = filter();
989 let mut op = Query::Neq(schema_id_tag(), SCHEMA_ID.to_string());
990 assert!(_process_operator("zip", &op, &filter, None).is_err());
991
992 op = Query::Neq(schema_id_tag(), "NOT HERE".to_string());
993 _process_operator("zip", &op, &filter, None).unwrap()
994 }
995
996 #[test]
997 fn test_process_op_in() {
998 let filter = filter();
999 let mut cred_def_ids = vec!["Not Here".to_string()];
1000
1001 let mut op = Query::In(cred_def_id_tag(), cred_def_ids.clone());
1002 assert!(_process_operator("zip", &op, &filter, None).is_err());
1003
1004 cred_def_ids.push(CRED_DEF_ID.to_string());
1005 op = Query::In(cred_def_id_tag(), cred_def_ids.clone());
1006 _process_operator("zip", &op, &filter, None).unwrap()
1007 }
1008
1009 #[test]
1010 fn test_process_op_or() {
1011 let filter = filter();
1012 let mut op = Query::Or(vec![
1013 Query::Eq(schema_id_tag(), "Not Here".to_string()),
1014 Query::Eq(cred_def_id_tag(), "Not Here".to_string()),
1015 ]);
1016 assert!(_process_operator("zip", &op, &filter, None).is_err());
1017
1018 op = Query::Or(vec![
1019 Query::Eq(schema_id_tag(), SCHEMA_ID.to_string()),
1020 Query::Eq(cred_def_id_tag(), "Not Here".to_string()),
1021 ]);
1022 _process_operator("zip", &op, &filter, None).unwrap()
1023 }
1024
1025 #[test]
1026 fn test_process_op_and() {
1027 let filter = filter();
1028 let mut op = Query::And(vec![
1029 Query::Eq(schema_id_tag(), "Not Here".to_string()),
1030 Query::Eq(cred_def_id_tag(), "Not Here".to_string()),
1031 ]);
1032 assert!(_process_operator("zip", &op, &filter, None).is_err());
1033
1034 op = Query::And(vec![
1035 Query::Eq(schema_id_tag(), SCHEMA_ID.to_string()),
1036 Query::Eq(cred_def_id_tag(), "Not Here".to_string()),
1037 ]);
1038 assert!(_process_operator("zip", &op, &filter, None).is_err());
1039
1040 op = Query::And(vec![
1041 Query::Eq(schema_id_tag(), SCHEMA_ID.to_string()),
1042 Query::Eq(cred_def_id_tag(), CRED_DEF_ID.to_string()),
1043 ]);
1044 _process_operator("zip", &op, &filter, None).unwrap()
1045 }
1046
1047 #[test]
1048 fn test_process_op_not() {
1049 let filter = filter();
1050 let mut op = Query::Not(Box::new(Query::And(vec![
1051 Query::Eq(schema_id_tag(), SCHEMA_ID.to_string()),
1052 Query::Eq(cred_def_id_tag(), CRED_DEF_ID.to_string()),
1053 ])));
1054 assert!(_process_operator("zip", &op, &filter, None).is_err());
1055
1056 op = Query::Not(Box::new(Query::And(vec![
1057 Query::Eq(schema_id_tag(), "Not Here".to_string()),
1058 Query::Eq(cred_def_id_tag(), "Not Here".to_string()),
1059 ])));
1060 _process_operator("zip", &op, &filter, None).unwrap()
1061 }
1062
1063 #[test]
1064 fn test_proccess_op_or_with_nested_and() {
1065 let filter = filter();
1066 let mut op = Query::Or(vec![
1067 Query::And(vec![
1068 Query::Eq(schema_id_tag(), "Not Here".to_string()),
1069 Query::Eq(cred_def_id_tag(), "Not Here".to_string()),
1070 ]),
1071 Query::And(vec![
1072 Query::Eq(schema_issuer_did_tag(), "Not Here".to_string()),
1073 Query::Eq(schema_name_tag(), "Not Here".to_string()),
1074 ]),
1075 Query::And(vec![
1076 Query::Eq(schema_name_tag(), "Not Here".to_string()),
1077 Query::Eq(issuer_did_tag(), "Not Here".to_string()),
1078 ]),
1079 ]);
1080 assert!(_process_operator("zip", &op, &filter, None).is_err());
1081
1082 op = Query::Or(vec![
1083 Query::And(vec![
1084 Query::Eq(schema_id_tag(), SCHEMA_ID.to_string()),
1085 Query::Eq(cred_def_id_tag(), "Not Here".to_string()),
1086 ]),
1087 Query::And(vec![
1088 Query::Eq(schema_issuer_did_tag(), "Not Here".to_string()),
1089 Query::Eq(schema_name_tag(), "Not Here".to_string()),
1090 ]),
1091 Query::And(vec![
1092 Query::Eq(schema_name_tag(), "Not Here".to_string()),
1093 Query::Eq(issuer_did_tag(), "Not Here".to_string()),
1094 ]),
1095 ]);
1096 assert!(_process_operator("zip", &op, &filter, None).is_err());
1097
1098 op = Query::Or(vec![
1099 Query::And(vec![
1100 Query::Eq(schema_id_tag(), SCHEMA_ID.to_string()),
1101 Query::Eq(cred_def_id_tag(), CRED_DEF_ID.to_string()),
1102 ]),
1103 Query::And(vec![
1104 Query::Eq(schema_issuer_did_tag(), "Not Here".to_string()),
1105 Query::Eq(schema_name_tag(), "Not Here".to_string()),
1106 ]),
1107 Query::And(vec![
1108 Query::Eq(schema_name_tag(), "Not Here".to_string()),
1109 Query::Eq(issuer_did_tag(), "Not Here".to_string()),
1110 ]),
1111 ]);
1112 _process_operator("zip", &op, &filter, None).unwrap()
1113 }
1114
1115 #[test]
1116 fn test_verify_op_complex_nested() {
1117 let filter = filter();
1118 let mut op = Query::And(vec![
1119 Query::And(vec![
1120 Query::Or(vec![
1121 Query::Eq(schema_name_tag(), "Not Here".to_string()),
1122 Query::Eq(issuer_did_tag(), "Not Here".to_string()),
1123 ]),
1124 Query::Eq(schema_id_tag(), SCHEMA_ID.to_string()),
1125 Query::Eq(cred_def_id_tag(), CRED_DEF_ID.to_string()),
1126 ]),
1127 Query::And(vec![
1128 Query::Eq(schema_issuer_did_tag(), SCHEMA_ISSUER_DID.to_string()),
1129 Query::Eq(schema_name_tag(), SCHEMA_NAME.to_string()),
1130 ]),
1131 Query::And(vec![
1132 Query::Eq(schema_version_tag(), SCHEMA_VERSION.to_string()),
1133 Query::Eq(issuer_did_tag(), ISSUER_DID.to_string()),
1134 ]),
1135 ]);
1136 assert!(_process_operator("zip", &op, &filter, None).is_err());
1137
1138 op = Query::And(vec![
1139 Query::And(vec![
1140 Query::Or(vec![
1141 Query::Eq(schema_name_tag(), SCHEMA_NAME.to_string()),
1142 Query::Eq(issuer_did_tag(), "Not Here".to_string()),
1143 ]),
1144 Query::Eq(schema_id_tag(), SCHEMA_ID.to_string()),
1145 Query::Eq(cred_def_id_tag(), CRED_DEF_ID.to_string()),
1146 ]),
1147 Query::And(vec![
1148 Query::Eq(schema_issuer_did_tag(), SCHEMA_ISSUER_DID.to_string()),
1149 Query::Eq(schema_name_tag(), SCHEMA_NAME.to_string()),
1150 ]),
1151 Query::And(vec![
1152 Query::Eq(schema_version_tag(), SCHEMA_VERSION.to_string()),
1153 Query::Eq(issuer_did_tag(), ISSUER_DID.to_string()),
1154 ]),
1155 Query::Not(Box::new(Query::Eq(
1156 schema_version_tag(),
1157 "NOT HERE".to_string(),
1158 ))),
1159 ]);
1160 _process_operator("zip", &op, &filter, None).unwrap();
1161
1162 op = Query::And(vec![
1163 Query::And(vec![
1164 Query::Or(vec![
1165 Query::Eq(schema_name_tag(), SCHEMA_NAME.to_string()),
1166 Query::Eq(issuer_did_tag(), "Not Here".to_string()),
1167 ]),
1168 Query::Eq(schema_id_tag(), SCHEMA_ID.to_string()),
1169 Query::Eq(cred_def_id_tag(), CRED_DEF_ID.to_string()),
1170 ]),
1171 Query::And(vec![
1172 Query::Eq(schema_issuer_did_tag(), SCHEMA_ISSUER_DID.to_string()),
1173 Query::Eq(schema_name_tag(), SCHEMA_NAME.to_string()),
1174 ]),
1175 Query::And(vec![
1176 Query::Eq(schema_version_tag(), SCHEMA_VERSION.to_string()),
1177 Query::Eq(issuer_did_tag(), ISSUER_DID.to_string()),
1178 ]),
1179 Query::Not(Box::new(Query::Eq(
1180 schema_version_tag(),
1181 SCHEMA_VERSION.to_string(),
1182 ))),
1183 ]);
1184 assert!(_process_operator("zip", &op, &filter, None).is_err());
1185 }
1186
1187 #[test]
1188 fn test_process_op_eq_revealed_value() {
1189 let filter = filter();
1190 let value = "value";
1191
1192 let mut op = Query::Eq(attr_tag_value(), value.to_string());
1193 _process_operator("zip", &op, &filter, Some(value)).unwrap();
1194
1195 op = Query::And(vec![
1196 Query::Eq(attr_tag_value(), value.to_string()),
1197 Query::Eq(schema_issuer_did_tag(), SCHEMA_ISSUER_DID.to_string()),
1198 ]);
1199 _process_operator("zip", &op, &filter, Some(value)).unwrap();
1200
1201 op = Query::Eq(attr_tag_value(), value.to_string());
1202 assert!(_process_operator("zip", &op, &filter, Some("NOT HERE")).is_err());
1203 }
1204
1205 fn _received() -> HashMap<String, Identifier> {
1206 let mut res: HashMap<String, Identifier> = HashMap::new();
1207 res.insert(
1208 "referent_1".to_string(),
1209 Identifier {
1210 timestamp: Some(1234),
1211 schema_id: SchemaId(String::new()),
1212 cred_def_id: CredentialDefinitionId(String::new()),
1213 rev_reg_id: Some(RevocationRegistryId(String::new())),
1214 },
1215 );
1216 res.insert(
1217 "referent_2".to_string(),
1218 Identifier {
1219 timestamp: None,
1220 schema_id: SchemaId(String::new()),
1221 cred_def_id: CredentialDefinitionId(String::new()),
1222 rev_reg_id: Some(RevocationRegistryId(String::new())),
1223 },
1224 );
1225 res
1226 }
1227
1228 fn _interval() -> NonRevocedInterval {
1229 NonRevocedInterval {
1230 from: None,
1231 to: Some(1234),
1232 }
1233 }
1234
1235 #[test]
1236 fn validate_timestamp_works() {
1237 validate_timestamp(&_received(), "referent_1", &None, &None).unwrap();
1238 validate_timestamp(&_received(), "referent_1", &Some(_interval()), &None).unwrap();
1239 validate_timestamp(&_received(), "referent_1", &None, &Some(_interval())).unwrap();
1240 }
1241
1242 #[test]
1243 fn validate_timestamp_not_work() {
1244 validate_timestamp(&_received(), "referent_2", &Some(_interval()), &None).unwrap_err();
1245 validate_timestamp(&_received(), "referent_2", &None, &Some(_interval())).unwrap_err();
1246 validate_timestamp(&_received(), "referent_3", &None, &Some(_interval())).unwrap_err();
1247 }
1248
1249 #[test]
1250 fn format_attribute() {
1251 assert_eq!(normalize_encoded_attr(""), "");
1252 assert_eq!(normalize_encoded_attr("abc"), "abc");
1253 assert_eq!(normalize_encoded_attr("0"), "0");
1254 assert_eq!(normalize_encoded_attr("000"), "0");
1255 assert_eq!(normalize_encoded_attr("01"), "1");
1256 assert_eq!(normalize_encoded_attr("01.0"), "01.0");
1257 assert_eq!(normalize_encoded_attr("0abc"), "0abc");
1258 assert_eq!(normalize_encoded_attr("-100"), "-100");
1259 assert_eq!(normalize_encoded_attr("-0100"), "-100");
1260 }
1261}