Skip to main content

bh_sd_jwt/
error.rs

1// Copyright (C) 2020-2026  The Blockhouse Technology Limited (TBTL).
2//
3// This program is free software: you can redistribute it and/or modify it
4// under the terms of the GNU Affero General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or (at your
6// option) any later version.
7//
8// This program is distributed in the hope that it will be useful, but
9// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10// or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
11// License for more details.
12//
13// You should have received a copy of the GNU Affero General Public License
14// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
16use bh_jws_utils::SigningAlgorithm;
17
18use crate::{holder::HolderError, verifier::VerifierError, DecodingError};
19
20/// Top-level error type for the SD-JWT crate.
21#[derive(strum_macros::Display, Debug, PartialEq)]
22pub enum Error {
23    /// Format error, e.g. invalid SD-JWT format or non-parsable JWT.
24    #[strum(to_string = "Format error: {0}")]
25    Format(FormatError),
26
27    /// Signature error, e.g. invalid JWT signature or missing signature verifier.
28    #[strum(to_string = "Signature error: {0}")]
29    Signature(SignatureError),
30
31    /// Decoding error, e.g. issues with decoding the JWT or SD-JWT.
32    #[strum(to_string = "Decoding error: {0}")]
33    Decoding(DecodingError),
34
35    /// JWT not yet valid error, indicating the JWT's `nbf` (not before) claim is in the future.
36    #[strum(to_string = "Jwt not yet valid: current time is {0}, nbf is {1}")]
37    JwtNotYetValid(u64, u64),
38
39    /// JWT expired error, indicating the JWT's `exp` (expiration) claim is in the past.
40    #[strum(to_string = "Jwt expired, current time is {0}, expiration is {1}")]
41    JwtExpired(u64, u64),
42}
43
44impl bherror::BhError for Error {}
45
46/// Format error related to parsing and validating SD-JWTs and VCs (Verifiable Credentials).
47#[derive(strum_macros::Display, Debug, PartialEq, Clone)]
48pub enum FormatError {
49    /// Error indicating that the SD-JWT format is invalid.
50    #[strum(to_string = "Invalid SD-JWT format")]
51    InvalidSdJwtFormat,
52
53    /// Error indicating that the SD-JWT is not parsable.
54    #[strum(to_string = "Provided JWT is not parsable")]
55    NonParseableJwt,
56
57    /// Error indicating that VC schema is invalid.
58    #[strum(to_string = "Invalid VC schema")]
59    InvalidVcSchema,
60
61    /// Error indicating that the `iat` (issued at) claim is not a number.
62    #[strum(to_string = "Invalid Iat format. Iat needs to be a number")]
63    InvalidIatFormat,
64
65    /// Error indicating that the disclosure data is in an invalid format.
66    #[strum(to_string = "Invalid disclosure: {0}")]
67    InvalidDisclosure(String),
68}
69
70impl bherror::BhError for FormatError {}
71
72/// Error type for signature-related issues in SD-JWTs.
73#[derive(strum_macros::Display, Debug, PartialEq, Clone)]
74pub enum SignatureError {
75    /// Error indicating that the JWT signature is invalid.
76    #[strum(to_string = "Invalid Jwt signature")]
77    InvalidJwtSignature,
78
79    /// Error indicating that there is no signature verifier available for the
80    /// specified signing algorithm.
81    #[strum(to_string = "Missing signature verifier for algorithm {0}")]
82    MissingSignatureVerifier(SigningAlgorithm),
83
84    /// Error indicating that the public key lookup failed.
85    #[strum(to_string = "Public key lookup failed")]
86    PublicKeyLookupFailed,
87}
88
89impl bherror::BhError for SignatureError {}
90
91impl Error {
92    pub(crate) fn to_holder_error(&self) -> HolderError {
93        match self {
94            Self::Format(error) => HolderError::Format(error.clone()),
95            Self::Signature(error) => HolderError::Signature(error.clone()),
96            Self::Decoding(error) => HolderError::Decoding(error.clone()),
97            Self::JwtExpired(time, nbf) => HolderError::JwtExpired(*time, *nbf),
98            Self::JwtNotYetValid(time, exp) => HolderError::JwtNotYetValid(*time, *exp),
99        }
100    }
101
102    pub(crate) fn to_verifier_error(&self) -> VerifierError {
103        match self {
104            Self::Format(error) => VerifierError::Format(error.clone()),
105            Self::Signature(error) => VerifierError::Signature(error.clone()),
106            Self::Decoding(error) => VerifierError::Decoding(error.clone()),
107            Self::JwtExpired(time, nbf) => VerifierError::JwtExpired(*time, *nbf),
108            Self::JwtNotYetValid(time, exp) => VerifierError::JwtNotYetValid(*time, *exp),
109        }
110    }
111}
112
113/// Result type used across the crate.
114pub type Result<T, E> = bherror::Result<T, E>;