Skip to main content

indy_credx/services/
verifier.rs

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
50/// Temporary method for verifying presentations with unlinked revocation proofs
51pub 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(&timestamp))
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                &reg_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) => &reg_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            // start with the predicate requested attribute, which is un-revealed
652            let mut attr_value_map = HashMap::new();
653            attr_value_map.insert(info.name.to_string(), None);
654
655            // include any revealed attributes for the same credential (based on sub_proof_index)
656            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}