identity_kit/vc/validation/
error.rs1use core::fmt;
4use std::collections::BTreeMap;
5use std::error::Error;
6
7use chrono::{DateTime, Utc};
8
9use crate::did::{DidError, DidUrl, OneOrMany, Uri};
10use crate::jose::{JwsError, VerificationError};
11use crate::vc::CredentialError;
12
13#[derive(Debug, thiserror::Error)]
15#[non_exhaustive]
16pub enum ValidationError {
17 #[error("invalid JWS: {0}")]
19 Jws(#[from] JwsError),
20 #[error("the JWS header has no `kid`")]
22 MissingKid,
23 #[error("`kid` {kid:?} is not a DID URL: {source}")]
25 InvalidKid {
26 kid: String,
28 #[source]
30 source: DidError,
31 },
32 #[error("`kid` `{0}` carries a path or DID parameters")]
36 KidWithParameters(DidUrl),
37 #[error("header `typ` is {found:?}, not `{expected}`")]
39 UnexpectedTyp {
40 expected: &'static str,
42 found: String,
44 },
45 #[error("the payload is not a JWT claims set: {0}")]
47 Payload(String),
48 #[error(transparent)]
50 Credential(#[from] CredentialError),
51 #[error("resolving key `{kid}`: {source}")]
53 KeyResolution {
54 kid: DidUrl,
56 #[source]
58 source: KeyResolutionError,
59 },
60 #[error("signature: {0}")]
62 Signature(#[from] VerificationError),
63 #[error("issuer `{issuer}` does not control signing key `{kid}`")]
65 IssuerMismatch {
66 issuer: Uri,
68 kid: DidUrl,
70 },
71 #[error("{role} `{id}` is not a DID")]
73 NotDid {
74 role: &'static str,
76 id: String,
78 },
79 #[error("holder `{holder}` does not control signing key `{kid}`")]
81 HolderMismatch {
82 holder: Uri,
84 kid: DidUrl,
86 },
87 #[error("not valid before {} (now is {})", not_before.to_rfc3339(), now.to_rfc3339())]
89 NotYetValid {
90 not_before: DateTime<Utc>,
92 now: DateTime<Utc>,
94 },
95 #[error("expired at {} (now is {})", expired_at.to_rfc3339(), now.to_rfc3339())]
97 Expired {
98 expired_at: DateTime<Utc>,
100 now: DateTime<Utc>,
102 },
103 #[error("issued at {}, after now ({})", issued_at.to_rfc3339(), now.to_rfc3339())]
105 IssuedInFuture {
106 issued_at: DateTime<Utc>,
108 now: DateTime<Utc>,
110 },
111 #[error("audience {found:?} does not include {expected:?}")]
113 AudienceMismatch {
114 expected: String,
116 found: Option<OneOrMany<String>>,
118 },
119 #[error("nonce {found:?} is not {expected:?}")]
121 NonceMismatch {
122 expected: String,
124 found: Option<String>,
126 },
127 #[error("`credentialStatus` of type `{0}` cannot be checked")]
130 StatusUnchecked(String),
131 #[error("credential status: {0}")]
133 Status(#[from] StatusError),
134 #[error("credential subject {subject:?} is not the holder `{holder}`")]
136 SubjectHolderMismatch {
137 subject: Option<Uri>,
139 holder: Uri,
141 },
142}
143
144#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
148#[non_exhaustive]
149pub enum StatusError {
150 #[error("status type `{0}` is not supported")]
152 Unsupported(String),
153 #[error("status unavailable: {0}")]
155 Unavailable(String),
156 #[error("credential is {0}")]
158 Invalid(String),
159}
160
161#[derive(Debug)]
163pub struct KeyResolutionError(Box<dyn Error + Send + Sync + 'static>);
164
165impl KeyResolutionError {
166 pub fn new<E: Error + Send + Sync + 'static>(error: E) -> Self {
168 Self(Box::new(error))
169 }
170
171 pub fn msg<M: fmt::Display>(message: M) -> Self {
173 Self(message.to_string().into())
174 }
175
176 pub fn inner(&self) -> &(dyn Error + Send + Sync + 'static) {
178 &*self.0
179 }
180}
181
182impl fmt::Display for KeyResolutionError {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 fmt::Display::fmt(&self.0, f)
185 }
186}
187
188impl Error for KeyResolutionError {
189 fn source(&self) -> Option<&(dyn Error + 'static)> {
190 self.0.source()
191 }
192}
193
194impl From<crate::resolver::DereferenceError> for KeyResolutionError {
195 fn from(error: crate::resolver::DereferenceError) -> Self {
196 Self::new(error)
197 }
198}
199
200#[derive(Debug, thiserror::Error)]
202#[non_exhaustive]
203pub struct PresentationValidationError {
204 pub presentation: Option<Box<ValidationError>>,
206 pub credentials: BTreeMap<usize, ValidationError>,
209}
210
211impl PresentationValidationError {
212 pub fn presentation(error: ValidationError) -> Self {
214 Self {
215 presentation: Some(Box::new(error)),
216 credentials: BTreeMap::new(),
217 }
218 }
219
220 pub fn credentials(errors: BTreeMap<usize, ValidationError>) -> Self {
222 Self {
223 presentation: None,
224 credentials: errors,
225 }
226 }
227}
228
229impl fmt::Display for PresentationValidationError {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 if let Some(error) = &self.presentation {
232 return write!(f, "presentation: {error}");
233 }
234 for (i, (index, error)) in self.credentials.iter().enumerate() {
235 if i > 0 {
236 f.write_str("; ")?;
237 }
238 write!(f, "credential {index}: {error}")?;
239 }
240 Ok(())
241 }
242}