peerbadge_protocol/
verifier.rs1use std::collections::{BTreeMap, BTreeSet};
4
5use crate::{
6 canonicalize_pbrsa_blind_msg, canonicalize_pbrsa_info, holder::current_unix_timestamp,
7 CredentialDigest, CredentialsError, HolderAuthorization, HolderId, IssuerAuthority, IssuerId,
8 PbrsaPublicKey, ProtocolV1, Revocation, SignedCredential, SignedRevocation, Timestamp,
9};
10
11#[derive(Clone, Default)]
13pub struct VerificationContext {
14 issuers: BTreeMap<IssuerId, PbrsaPublicKey>,
15 revocations: BTreeSet<(IssuerId, Revocation)>,
16}
17
18impl VerificationContext {
19 pub fn new() -> Self {
21 Self::default()
22 }
23
24 pub fn add_issuer_authority(
26 &mut self,
27 authority: &IssuerAuthority,
28 ) -> Result<(), CredentialsError> {
29 let issuer = authority.verify()?;
30 self.issuers
31 .insert(issuer.issuer_id_pubkey.clone(), issuer.issuance_key.clone());
32
33 Ok(())
34 }
35
36 pub fn add_revocation(
38 &mut self,
39 signed_revocation: &SignedRevocation,
40 ) -> Result<(), CredentialsError> {
41 let revocation = signed_revocation.verify()?;
42 if !self
43 .issuers
44 .contains_key(&signed_revocation.proof.issuer_id_pubkey)
45 {
46 return Err(CredentialsError::UnknownIssuer);
47 }
48
49 self.revocations
50 .insert((signed_revocation.proof.issuer_id_pubkey.clone(), revocation));
51 Ok(())
52 }
53
54 pub fn verify_credential(&self, credential: &SignedCredential) -> Result<(), CredentialsError> {
56 let issuer_public_key = self
57 .issuers
58 .get(&credential.credential.issuer_id_pubkey)
59 .ok_or(CredentialsError::UnknownIssuer)?;
60
61 verify_credential_with_key(issuer_public_key, credential)?;
62
63 let revocation = Revocation {
64 credential_digest: CredentialDigest(credential.credential.digest()?),
65 };
66
67 if self
68 .revocations
69 .contains(&(credential.credential.issuer_id_pubkey.clone(), revocation))
70 {
71 return Err(CredentialsError::CredentialRevoked);
72 }
73
74 Ok(())
75 }
76
77 pub fn verify_credential_authorization(
84 &self,
85 credential: &SignedCredential,
86 authorization: &HolderAuthorization,
87 ) -> Result<(), CredentialsError> {
88 self.verify_credential_authorization_at_time(
89 credential,
90 authorization,
91 current_unix_timestamp()?,
92 )
93 }
94
95 pub fn verify_credential_authorization_at_time(
97 &self,
98 credential: &SignedCredential,
99 authorization: &HolderAuthorization,
100 now: impl Into<Timestamp>,
101 ) -> Result<(), CredentialsError> {
102 self.verify_credential(credential)?;
103
104 let authorization = authorization.verify()?;
105 let now = now.into();
106 let credential_holder_id = credential_holder_id(credential)?;
107
108 if credential_holder_id != authorization.holder_id_pubkey {
109 return Err(CredentialsError::HolderIdMismatch);
110 }
111
112 if now < authorization.issued_at {
113 return Err(CredentialsError::AuthorizationNotYetValid);
114 }
115
116 let expected_credential_digest = CredentialDigest(credential.credential.digest()?);
117
118 if authorization.credential_digest != expected_credential_digest {
119 return Err(CredentialsError::AuthorizationCredentialDigestMismatch);
120 }
121
122 Ok(())
123 }
124}
125
126pub(crate) fn credential_holder_id(
127 credential: &SignedCredential,
128) -> Result<HolderId, CredentialsError> {
129 let Some(holder_id) = credential.credential.blind_msg.as_str() else {
130 return Err(CredentialsError::VerificationFailed);
131 };
132
133 holder_id
134 .parse::<HolderId>()
135 .map_err(|_| CredentialsError::VerificationFailed)
136}
137
138pub(crate) fn verify_credential_with_key(
139 issuer_public_key: &PbrsaPublicKey,
140 credential: &SignedCredential,
141) -> Result<(), CredentialsError> {
142 let metadata = canonicalize_pbrsa_info(
143 ProtocolV1,
144 &credential.credential.issuer_id_pubkey,
145 &credential.credential.info,
146 )?;
147 let message = canonicalize_pbrsa_blind_msg(ProtocolV1, &credential.credential.blind_msg)?;
148 let public_key = issuer_public_key.derive_public_key_for_metadata(&metadata)?;
149 public_key.verify(&credential.proof.signature, None, &message, Some(&metadata))?;
150 Ok(())
151}