identity_credential/validator/jwt_credential_validation/error.rs
1// Copyright 2020-2023 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4// This module is basically an adaptation of the old credential::validator::error module.
5use std::fmt::Display;
6
7use itertools;
8
9/// An error associated with validating credentials and presentations.
10#[derive(Debug, thiserror::Error, strum::IntoStaticStr)]
11#[non_exhaustive]
12pub enum JwtValidationError {
13 /// Indicates that the JWS representation of an issued credential or presentation could not be decoded.
14 #[error("could not decode jws")]
15 JwsDecodingError(#[source] identity_verification::jose::error::Error),
16
17 /// Indicates error while verifying the JWS of a presentation.
18 #[error("could not verify jws")]
19 PresentationJwsError(#[source] identity_document::error::Error),
20
21 /// Indicates that a verification method that both matches the DID Url specified by
22 /// the `kid` value and contains a public key in the JWK format could not be found.
23 #[error("could not find verification material")]
24 MethodDataLookupError {
25 /// The source of the error if set
26 #[source]
27 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
28 /// A message providing more context
29 message: &'static str,
30 /// Specifies whether the error occurred when trying to verify the signature of a presentation holder or
31 /// of a credential issuer.
32 signer_ctx: SignerContext,
33 },
34
35 /// The DID part parsed from the `kid` does not match the identifier of the issuer (resp. holder) property
36 /// of the credential (resp. presentation).
37 #[error("identifier mismatch")]
38 IdentifierMismatch {
39 /// The context in which the error occurred.
40 signer_ctx: SignerContext,
41 },
42
43 /// Indicates that the expiration date of the credential or presentation is not considered valid.
44 #[error("the expiration date is in the past or earlier than required")]
45 ExpirationDate,
46 /// Indicates that the issuance date of the credential or presentation is not considered valid.
47 #[error("issuance date is in the future or later than required")]
48 IssuanceDate,
49 /// Indicates that the credential's (resp. presentation's) signature could not be verified using
50 /// the issuer's (resp. holder's) DID Document.
51 #[error("could not verify the {signer_ctx}'s signature; {source}")]
52 #[non_exhaustive]
53 Signature {
54 /// Signature verification error.
55 source: identity_verification::jose::error::Error,
56 /// Specifies whether the error was from the DID Document of a credential issuer
57 /// or the presentation holder.
58 signer_ctx: SignerContext,
59 },
60
61 /// Indicates that the credential's (resp. presentation's) issuer's (resp. holder's) URL could
62 /// not be parsed as a valid DID.
63 #[error("{signer_ctx} URL is not a valid DID")]
64 #[non_exhaustive]
65 SignerUrl {
66 /// DID parsing error.
67 source: Box<dyn std::error::Error + Send + Sync + 'static>,
68 /// Specifies whether the error relates to the DID of a credential issuer
69 /// or the presentation holder.
70 signer_ctx: SignerContext,
71 },
72
73 /// Indicates an attempt to verify a signature of a credential (resp. presentation) using a
74 /// DID Document not matching the issuer's (resp. holder's) id.
75 #[error("the {0}'s id does not match the provided DID Document(s)")]
76 #[non_exhaustive]
77 DocumentMismatch(SignerContext),
78
79 /// Indicates that the structure of the [Credential](crate::credential::Credential) is not semantically
80 /// correct.
81 #[error("the credential's structure is not semantically correct")]
82 CredentialStructure(#[source] crate::Error),
83 /// Indicates that the structure of the [Presentation](crate::presentation::Presentation) is not
84 /// semantically correct.
85 #[error("the presentation's structure is not semantically correct")]
86 PresentationStructure(#[source] crate::Error),
87 /// Indicates that the relationship between the presentation holder and one of the credential subjects is not valid.
88 #[error("expected holder = subject of the credential")]
89 #[non_exhaustive]
90 SubjectHolderRelationship,
91 /// Indicates that the presentation does not have a holder.
92 #[error("the presentation has an empty holder property")]
93 MissingPresentationHolder,
94 /// Indicates that the credential's status is invalid.
95 #[error("invalid credential status")]
96 InvalidStatus(#[source] crate::Error),
97 /// Indicates that the the credential's service is invalid.
98 #[error("service lookup error")]
99 #[non_exhaustive]
100 ServiceLookupError,
101 /// Indicates that the credential has been revoked.
102 #[error("credential has been revoked")]
103 Revoked,
104 /// Indicates that the credential has been suspended.
105 #[error("credential has been suspended")]
106 Suspended,
107 /// Indicates that the credential's timeframe interval is not valid
108 #[cfg(feature = "jpt-bbs-plus")]
109 #[error("timeframe interval not valid")]
110 OutsideTimeframe,
111 /// Indicates that the JWP representation of an issued credential or presentation could not be decoded.
112 #[cfg(feature = "jpt-bbs-plus")]
113 #[error("could not decode jwp")]
114 JwpDecodingError(#[source] jsonprooftoken::errors::CustomError),
115 /// Indicates that the verification of the JWP has failed
116 #[cfg(feature = "jpt-bbs-plus")]
117 #[error("could not verify jwp")]
118 JwpProofVerificationError(#[source] jsonprooftoken::errors::CustomError),
119}
120
121/// Specifies whether an error is related to a credential issuer or the presentation holder.
122#[derive(Debug)]
123#[non_exhaustive]
124pub enum SignerContext {
125 /// Credential issuer.
126 Issuer,
127 /// Presentation holder.
128 Holder,
129}
130
131impl Display for SignerContext {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 let context = match *self {
134 Self::Issuer => "issuer",
135 Self::Holder => "holder",
136 };
137 write!(f, "{context}")
138 }
139}
140
141/// Errors caused by a failure to validate a [`Credential`](crate::credential::Credential).
142#[derive(Debug)]
143pub struct CompoundCredentialValidationError {
144 /// List of credential validation errors.
145 pub validation_errors: Vec<JwtValidationError>,
146}
147
148impl Display for CompoundCredentialValidationError {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 // intersperse might become available in the standard library soon: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.intersperse
151 let detailed_information: String = itertools::intersperse(
152 self.validation_errors.iter().map(|err| err.to_string()),
153 "; ".to_string(),
154 )
155 .collect();
156 write!(f, "[{detailed_information}]")
157 }
158}
159
160impl std::error::Error for CompoundCredentialValidationError {}