identity_kit/vc/validation/
error.rs1use core::fmt;
4use std::collections::BTreeMap;
5
6use chrono::{DateTime, Utc};
7
8use crate::did::{OneOrMany, Uri};
9use crate::jose::{JwsError, VerificationError};
10use crate::resolver::{KeyReference, KeyReferenceError, KeyResolutionError};
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(transparent)]
23 KeyReference(#[from] KeyReferenceError),
24 #[error("header `typ` is {found:?}, not `{expected}`")]
26 UnexpectedTyp {
27 expected: &'static str,
29 found: String,
31 },
32 #[error("the payload is not a JWT claims set: {0}")]
34 Payload(String),
35 #[error(transparent)]
37 Credential(#[from] CredentialError),
38 #[error("the payload carries `{claim}`, so it is an SD-JWT, not a plain JWT")]
43 SdJwtPayload {
44 claim: &'static str,
46 },
47 #[error("resolving key {key}: {source}")]
49 KeyResolution {
50 key: Box<KeyReference>,
52 #[source]
54 source: KeyResolutionError,
55 },
56 #[error("signature: {0}")]
58 Signature(#[from] VerificationError),
59 #[error("issuer `{issuer}` does not control signing key {key}")]
61 IssuerMismatch {
62 issuer: Uri,
64 key: Box<KeyReference>,
66 },
67 #[error("{role} `{id}` is not a DID")]
69 NotDid {
70 role: &'static str,
72 id: String,
74 },
75 #[error("holder `{holder}` does not control signing key {key}")]
77 HolderMismatch {
78 holder: Uri,
80 key: Box<KeyReference>,
82 },
83 #[error("not valid before {} (now is {})", not_before.to_rfc3339(), now.to_rfc3339())]
85 NotYetValid {
86 not_before: DateTime<Utc>,
88 now: DateTime<Utc>,
90 },
91 #[error("expired at {} (now is {})", expired_at.to_rfc3339(), now.to_rfc3339())]
93 Expired {
94 expired_at: DateTime<Utc>,
96 now: DateTime<Utc>,
98 },
99 #[error("issued at {}, after now ({})", issued_at.to_rfc3339(), now.to_rfc3339())]
101 IssuedInFuture {
102 issued_at: DateTime<Utc>,
104 now: DateTime<Utc>,
106 },
107 #[error("audience {found:?} does not include {expected:?}")]
109 AudienceMismatch {
110 expected: String,
112 found: Option<OneOrMany<String>>,
114 },
115 #[error("nonce {found:?} is not {expected:?}")]
117 NonceMismatch {
118 expected: String,
120 found: Option<String>,
122 },
123 #[error("`credentialStatus` of type `{0}` cannot be checked")]
126 StatusUnchecked(String),
127 #[error("credential status: {0}")]
129 Status(#[from] StatusError),
130 #[error("credential subject {subject:?} is not the holder `{holder}`")]
132 SubjectHolderMismatch {
133 subject: Option<Uri>,
135 holder: Uri,
137 },
138 #[cfg(feature = "sd-jwt")]
142 #[cfg_attr(docsrs, doc(cfg(feature = "sd-jwt")))]
143 #[error(transparent)]
144 SdJwt(Box<crate::sd_jwt_vc::SdJwtValidationError>),
145}
146
147#[cfg(feature = "sd-jwt")]
148impl From<crate::sd_jwt_vc::SdJwtValidationError> for ValidationError {
149 fn from(error: crate::sd_jwt_vc::SdJwtValidationError) -> Self {
151 match error {
152 crate::sd_jwt_vc::SdJwtValidationError::Validation(error) => error,
153 other => Self::SdJwt(Box::new(other)),
154 }
155 }
156}
157
158#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
162#[non_exhaustive]
163pub enum StatusError {
164 #[error("status type `{0}` is not supported")]
166 Unsupported(String),
167 #[error("status unavailable: {0}")]
169 Unavailable(String),
170 #[error("credential is {0}")]
172 Invalid(String),
173}
174
175#[derive(Debug, thiserror::Error)]
177#[non_exhaustive]
178pub struct PresentationValidationError {
179 pub presentation: Option<Box<ValidationError>>,
181 pub credentials: BTreeMap<usize, ValidationError>,
184}
185
186impl PresentationValidationError {
187 pub fn presentation(error: ValidationError) -> Self {
189 Self {
190 presentation: Some(Box::new(error)),
191 credentials: BTreeMap::new(),
192 }
193 }
194
195 pub fn credentials(errors: BTreeMap<usize, ValidationError>) -> Self {
197 Self {
198 presentation: None,
199 credentials: errors,
200 }
201 }
202}
203
204impl fmt::Display for PresentationValidationError {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 if let Some(error) = &self.presentation {
207 return write!(f, "presentation: {error}");
208 }
209 for (i, (index, error)) in self.credentials.iter().enumerate() {
210 if i > 0 {
211 f.write_str("; ")?;
212 }
213 write!(f, "credential {index}: {error}")?;
214 }
215 Ok(())
216 }
217}