use core::fmt;
use std::collections::BTreeMap;
use std::error::Error;
use chrono::{DateTime, Utc};
use crate::did::{DidError, DidUrl, OneOrMany, Uri};
use crate::jose::{JwsError, VerificationError};
use crate::vc::CredentialError;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ValidationError {
#[error("invalid JWS: {0}")]
Jws(#[from] JwsError),
#[error("the JWS header has no `kid`")]
MissingKid,
#[error("`kid` {kid:?} is not a DID URL: {source}")]
InvalidKid {
kid: String,
#[source]
source: DidError,
},
#[error("`kid` `{0}` carries a path or DID parameters")]
KidWithParameters(DidUrl),
#[error("header `typ` is {found:?}, not `{expected}`")]
UnexpectedTyp {
expected: &'static str,
found: String,
},
#[error("the payload is not a JWT claims set: {0}")]
Payload(String),
#[error(transparent)]
Credential(#[from] CredentialError),
#[error("resolving key `{kid}`: {source}")]
KeyResolution {
kid: DidUrl,
#[source]
source: KeyResolutionError,
},
#[error("signature: {0}")]
Signature(#[from] VerificationError),
#[error("issuer `{issuer}` does not control signing key `{kid}`")]
IssuerMismatch {
issuer: Uri,
kid: DidUrl,
},
#[error("{role} `{id}` is not a DID")]
NotDid {
role: &'static str,
id: String,
},
#[error("holder `{holder}` does not control signing key `{kid}`")]
HolderMismatch {
holder: Uri,
kid: DidUrl,
},
#[error("not valid before {} (now is {})", not_before.to_rfc3339(), now.to_rfc3339())]
NotYetValid {
not_before: DateTime<Utc>,
now: DateTime<Utc>,
},
#[error("expired at {} (now is {})", expired_at.to_rfc3339(), now.to_rfc3339())]
Expired {
expired_at: DateTime<Utc>,
now: DateTime<Utc>,
},
#[error("issued at {}, after now ({})", issued_at.to_rfc3339(), now.to_rfc3339())]
IssuedInFuture {
issued_at: DateTime<Utc>,
now: DateTime<Utc>,
},
#[error("audience {found:?} does not include {expected:?}")]
AudienceMismatch {
expected: String,
found: Option<OneOrMany<String>>,
},
#[error("nonce {found:?} is not {expected:?}")]
NonceMismatch {
expected: String,
found: Option<String>,
},
#[error("`credentialStatus` of type `{0}` cannot be checked")]
StatusUnchecked(String),
#[error("credential status: {0}")]
Status(#[from] StatusError),
#[error("credential subject {subject:?} is not the holder `{holder}`")]
SubjectHolderMismatch {
subject: Option<Uri>,
holder: Uri,
},
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum StatusError {
#[error("status type `{0}` is not supported")]
Unsupported(String),
#[error("status unavailable: {0}")]
Unavailable(String),
#[error("credential is {0}")]
Invalid(String),
}
#[derive(Debug)]
pub struct KeyResolutionError(Box<dyn Error + Send + Sync + 'static>);
impl KeyResolutionError {
pub fn new<E: Error + Send + Sync + 'static>(error: E) -> Self {
Self(Box::new(error))
}
pub fn msg<M: fmt::Display>(message: M) -> Self {
Self(message.to_string().into())
}
pub fn inner(&self) -> &(dyn Error + Send + Sync + 'static) {
&*self.0
}
}
impl fmt::Display for KeyResolutionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl Error for KeyResolutionError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.0.source()
}
}
impl From<crate::resolver::DereferenceError> for KeyResolutionError {
fn from(error: crate::resolver::DereferenceError) -> Self {
Self::new(error)
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub struct PresentationValidationError {
pub presentation: Option<Box<ValidationError>>,
pub credentials: BTreeMap<usize, ValidationError>,
}
impl PresentationValidationError {
pub fn presentation(error: ValidationError) -> Self {
Self {
presentation: Some(Box::new(error)),
credentials: BTreeMap::new(),
}
}
pub fn credentials(errors: BTreeMap<usize, ValidationError>) -> Self {
Self {
presentation: None,
credentials: errors,
}
}
}
impl fmt::Display for PresentationValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(error) = &self.presentation {
return write!(f, "presentation: {error}");
}
for (i, (index, error)) in self.credentials.iter().enumerate() {
if i > 0 {
f.write_str("; ")?;
}
write!(f, "credential {index}: {error}")?;
}
Ok(())
}
}