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 {}