1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//! SD-JWT — selective disclosure for JSON Web Tokens, IETF RFC 9901
//! (Standards Track, November 2025).
//!
//! An issuer replaces a claim's value with a salted hash and hands the cleartext
//! over separately as a *disclosure*. A holder forwards the signed token plus
//! only the disclosures it chooses. A verifier recomputes each disclosure's
//! digest and looks it up in the token; claims it was not given remain digests —
//! present, provably untampered, unreadable.
//!
//! # Why this is a primitive and not a credential
//!
//! Nothing here knows what a passport is, which fields are sensitive, or who may
//! see them. This module hides *whatever it is told to hide*. Deciding what to
//! hide is a disclosure-policy question and lives in `dpp-domain`; wrapping the
//! result in a credential is `dpp-vc`. The same split already separates
//! [`crate::jws`] from the things that sign with it.
//!
//! # What this module does not do
//!
//! - **It does not verify the issuer's signature.** [`SdJwt::parse`] checks the
//! disclosure mechanism only. The JWS check is [`crate::jws::verifier`], and a
//! caller must do both — a credential whose digests all match but whose
//! signature is forged is worthless, and this module cannot tell.
//! - **`parse` does not authenticate the disclosures either**, which is the
//! sharper half of the same point. It reads the serialisation; it does not
//! compare what arrived against the signed `_sd` digests. A validly signed,
//! entirely untouched JWT can be re-serialised with an extra forged
//! disclosure, and [`SdJwt::disclosures`] will hand it back — the signature
//! still verifies, because it never covered that list. Only
//! [`SdJwt::disclosed_payload`] recomputes the digests, and it **refuses the
//! whole token** rather than filtering: a disclosure matching no `_sd` digest
//! is [`SdJwtError::UnusedDisclosures`], per clause 7.1 step 4. So **read
//! claims from its output, never from `disclosures()`** — and read a failure
//! from it as a tampered credential, not as a claim that was dropped.
//! - **It does not implement key binding.** RFC 9901 clause 4.3's KB-JWT proves
//! the presenter holds a key the credential names. That needs holders to have
//! keys. The parser tolerates a trailing KB-JWT segment so that a presentation
//! carrying one is not misread as malformed, and otherwise ignores it.
//! - **It does not implement array-element disclosures** (clause 4.2.2's `...`
//! form) or decoy digests (clause 4.2.5). Both are optional. Array elements
//! are not how any disclosure class here is expressed — a class attaches to a
//! named field, and the whole array travels with it — and decoys buy
//! concealment of *how many* claims were withheld, which the digest count
//! already reveals only in aggregate.
//!
//! # Two requirements that are easy to miss and are tested
//!
//! - **Digest order must not follow claim order.** Clause 4.2.4.1: *"The Issuer
//! MUST hide the original order of the claims in the array."* Pushing digests
//! in the order the fields were walked leaks the source structure, and a naive
//! implementation does exactly that. [`conceal`] sorts.
//! - **An unmatched disclosure is an error, not an omission.** Clause 7.1
//! requires every disclosure to be used; one whose digest is absent from the
//! token means the credential and the disclosures disagree, and the safe
//! reading is refusal. [`SdJwt::disclosed_payload`] refuses.
//! - **A repeated digest is an error.** Clause 4.1: *"The same digest value MUST
//! NOT appear more than once in the SD-JWT."* A map keyed by digest quietly
//! satisfies a count-based check while collapsing the repeat, so the check is
//! made against every occurrence rather than against what survived a
//! deduplicating collection.
//! - **A presentation selects disclosures by digest, not by claim name.** One
//! name can belong to several disclosures, so selecting by name reveals values
//! the holder did not choose. See [`SdJwt::present`].
pub use ;
pub use ;
pub use ;