Skip to main content

identity_kit/sd_jwt_vc/
error.rs

1//! Errors of SD-JWT parsing, processing and building.
2
3use super::ClaimPath;
4use crate::cause::cause;
5use crate::jose::JwsError;
6
7/// Why an SD-JWT could not be parsed, processed, selected from or built.
8#[derive(Debug, thiserror::Error)]
9#[non_exhaustive]
10pub enum SdJwtError {
11    /// The issuer-signed JWT or the key binding JWT is not a compact JWS this crate decodes.
12    #[error("invalid JWS: {0}")]
13    Jws(#[from] JwsError),
14    /// The compact serialization is malformed: no `~`, an empty disclosure.
15    #[error("invalid SD-JWT: {0}")]
16    Format(String),
17    /// A disclosure is not base64url of a JSON array of the right shape.
18    #[error("invalid disclosure {disclosure:?}: {reason}")]
19    InvalidDisclosure {
20        /// The encoded disclosure.
21        disclosure: String,
22        /// What is wrong with it.
23        reason: String,
24    },
25    /// The same disclosure is given twice.
26    #[error("disclosure {0:?} is given twice")]
27    DuplicateDisclosure(String),
28    /// The issuer-signed JWT payload is not a JSON object, or `_sd`, `...` or `_sd_alg` is
29    /// malformed. The source is the JSON error, when there is one.
30    #[error("invalid SD-JWT payload: {reason}{}", cause(.source.as_ref()))]
31    Payload {
32        /// What is wrong with it.
33        reason: String,
34        /// The JSON error.
35        source: Option<serde_json::Error>,
36    },
37    /// The `_sd_alg` names a hash algorithm this crate does not implement.
38    #[error("unsupported hash algorithm {0:?}")]
39    UnsupportedHashAlgorithm(String),
40    /// A digest appears more than once in the payload, directly or through disclosures.
41    #[error("digest {0:?} appears more than once")]
42    DuplicateDigest(String),
43    /// A disclosure matches no digest in the payload or in another disclosure.
44    #[error("disclosure {0:?} is referenced by no digest")]
45    UnusedDisclosure(String),
46    /// A disclosure of the wrong kind matches a digest: an array element disclosure in `_sd`,
47    /// or an object property disclosure in an array.
48    #[error("disclosure {disclosure:?} is not {expected}")]
49    WrongDisclosureKind {
50        /// The encoded disclosure.
51        disclosure: String,
52        /// `an object property disclosure` or `an array element disclosure`.
53        expected: &'static str,
54    },
55    /// A disclosure names the reserved claim `_sd` or `...`.
56    #[error("disclosure claim name {0:?} is reserved")]
57    ReservedClaimName(String),
58    /// A disclosure names a claim that already exists at the level of its `_sd`.
59    #[error("claim {name:?} at {path} is both disclosed and present")]
60    DuplicateClaim {
61        /// The claim name.
62        name: String,
63        /// The object it is duplicated in.
64        path: ClaimPath,
65    },
66    /// A disclosure was selected without the disclosure whose value references it.
67    #[error("disclosure {disclosure:?} needs the disclosure {parent:?} it is nested in")]
68    MissingParentDisclosure {
69        /// The encoded disclosure selected.
70        disclosure: String,
71        /// The encoded disclosure it needs.
72        parent: String,
73    },
74    /// A path to conceal or disclose selects no claim.
75    #[error("no claim at {0}")]
76    NoSuchClaim(ClaimPath),
77    /// A claim cannot be made selectively disclosable: it is `_sd` or `...`, or a profile
78    /// forbids it.
79    #[error("claim at {path} cannot be selectively disclosable: {reason}")]
80    NotConcealable {
81        /// The claim.
82        path: ClaimPath,
83        /// Why.
84        reason: String,
85    },
86    /// The claims nest deeper than [`SdJwtBuilder::MAX_DEPTH`](super::SdJwtBuilder::MAX_DEPTH)
87    /// containers: claims to issue, which no parser of this crate would read back, or claims
88    /// being processed, through the payload and the disclosures nested in it.
89    #[error("the claims nest deeper than {max} containers")]
90    TooDeep {
91        /// The deepest nesting accepted.
92        max: usize,
93    },
94    /// The key binding JWT is present where an SD-JWT without key binding was expected, or
95    /// missing.
96    #[error("{0}")]
97    KeyBinding(String),
98    /// The operating system's random number generator could not provide a salt: unavailable
99    /// on some WebAssembly targets, and in sandboxes or early boot. The source is its error.
100    #[error("cannot generate a salt: {0}")]
101    Randomness(#[source] Box<dyn std::error::Error + Send + Sync>),
102}
103
104impl SdJwtError {
105    /// A [`SdJwtError::Payload`] for `reason`, with no source.
106    pub(crate) fn payload(reason: impl Into<String>) -> Self {
107        Self::Payload {
108            reason: reason.into(),
109            source: None,
110        }
111    }
112}