dpp_crypto/sd_jwt/error.rs
1//! How reading an SD-JWT fails.
2//!
3//! Both enums live here rather than beside the types they belong to because the
4//! two are a single contract: a caller handed a credential from outside gets one
5//! or the other, and should be able to see every way that can go without reading
6//! the mechanism.
7
8/// Anything that can go wrong reading a disclosure someone else produced.
9#[derive(Debug, PartialEq, Eq, thiserror::Error)]
10#[non_exhaustive]
11pub enum DisclosureError {
12 /// The string is not base64url.
13 #[error("disclosure is not base64url")]
14 NotBase64,
15 /// The decoded bytes are not JSON.
16 #[error("disclosure does not decode to JSON")]
17 NotJson,
18 /// The JSON is not the three-element array clause 4.2.1 requires.
19 ///
20 /// The two-element form (array-element disclosures, clause 4.2.2) is
21 /// deliberately not accepted — see the module documentation.
22 #[error("disclosure is not a [salt, name, value] triple")]
23 NotATriple,
24 /// The claim name is one clause 4.2.1 forbids: `_sd` or `...`.
25 #[error("disclosure names a reserved claim")]
26 ReservedClaimName,
27}
28
29/// Anything that can go wrong reading an SD-JWT.
30#[derive(Debug, PartialEq, Eq, thiserror::Error)]
31#[non_exhaustive]
32pub enum SdJwtError {
33 /// Fewer than two `~`-separated segments, so not an SD-JWT at all.
34 #[error("not an SD-JWT: fewer than two segments")]
35 NotAnSdJwt,
36 /// A disclosure segment could not be read.
37 #[error(transparent)]
38 Disclosure(#[from] DisclosureError),
39 /// The issuer-signed JWT is not three dot-separated parts.
40 #[error("issuer-signed JWT is malformed")]
41 MalformedJwt,
42 /// The JWT payload is not a JSON object.
43 #[error("JWT payload is not a JSON object")]
44 PayloadNotAnObject,
45 /// `_sd_alg` names a hash function this does not implement.
46 ///
47 /// Carried rather than defaulted: clause 4.1.1 permits the claim to be
48 /// absent and to mean `sha-256`, but a *present* value naming something else
49 /// must not be read as if it said `sha-256`.
50 #[error("unsupported _sd_alg: {0}")]
51 UnsupportedHashAlg(String),
52 /// A disclosure was supplied whose digest appears nowhere in the token.
53 ///
54 /// Clause 7.1 step 4: every disclosure must be used. The count is reported
55 /// rather than the disclosure itself, which carries a claim value.
56 #[error("{0} disclosure(s) matched nothing in the token")]
57 UnusedDisclosures(usize),
58 /// The same digest appeared more than once.
59 ///
60 /// RFC 9901 clause 4.1: *"The same digest value MUST NOT appear more than
61 /// once in the SD-JWT."* Repetition is how a token smuggles a second
62 /// meaning past a reader that de-duplicates, so it is refused rather than
63 /// collapsed.
64 #[error("digest {0} appears more than once")]
65 DuplicateDigest(String),
66 /// A disclosure would overwrite a claim already present in cleartext.
67 ///
68 /// Clause 7.1 makes this a rejection condition: the token would otherwise
69 /// have two values for one claim and the reader would pick one.
70 #[error("disclosure for '{0}' collides with a cleartext claim")]
71 ClaimCollision(String),
72}