Skip to main content

Module sd_jwt

Module sd_jwt 

Source
Expand description

SD-JWT (Selective Disclosure for JWTs) issuance and verification, per draft-ietf-oauth-selective-disclosure-jwt.

An SD-JWT lets an issuer mint a single signed token that carries some claims in the clear and others only as digests (_sd[]), plus a separate list of Disclosures[salt, claim_name, claim_value] triples — that reveal what each digest stands for. A holder decides, per presentation, which Disclosures to forward alongside the JWT; a verifier can only recover the claims for the Disclosures it was handed, and can cryptographically prove every disclosed value was actually vouched for by the issuer (its digest is in _sd[], which is inside the signed payload) without the issuer needing to mint one token per disclosure combination.

§What this module does and does not implement

In scope: issuing and verifying flat, top-level, object-property Disclosures, serialized in SD-JWT compact form (<jwt>~<d1>~<d2>~).

Deliberately out of scope (spec features this module does not touch):

  • Key Binding JWT (KB-JWT) — holder proof-of-possession. This module verifies the issuer’s signature and the Disclosure digests only; it has no notion of a holder key or a ~<kb-jwt> suffix.
  • Array-element and recursive/nested Disclosures — only flat top-level object properties are supported, matching every consumer this crate has today.
  • SD-JWT VC (vc+sd-jwt) — no vct/type metadata handling.

None of these are hard to misuse into thinking they’re covered — there is simply no code path for them. A caller needing KB-JWT or nested disclosures needs to build that on top, not assume it’s already here.

§Security properties this module enforces (and why)

  • _sd_alg is never silently defaulted to sha-256 when present and unrecognized. A verifier that treats an unknown digest algorithm as “must mean sha-256” is an algorithm-confusion bug: an attacker who controls (or can influence) the claimed _sd_alg could otherwise coax a verifier into hashing Disclosures with a weaker/attacker- favorable function while the verifier’s logic still believes it’s checking sha-256 digests. This module fails closed instead: an absent _sd_alg defaults to sha-256 (per spec, the assumed default), but a present-and-different value is rejected outright.
  • A presented Disclosure whose digest is not found in _sd[] fails the whole verification, not just that one claim. Accepting it would let a holder (or a network attacker who can append to the compact form) inject arbitrary claims the issuer never signed for — the entire point of _sd[] living inside the signed JWT payload is that only digests the issuer actually put there are trustworthy.
  • Duplicate digests in _sd[] are rejected. They serve no legitimate purpose (each Disclosure is independently salted, so two honestly-generated Disclosures never collide) and are a cheap way to smuggle a second, attacker-chosen Disclosure past the “digest found” check above once one legitimate Disclosure’s digest becomes known.
  • A disclosed claim can never shadow a registered top-level JWT claim (iss, sub, aud, exp, iat, nbf, jti, scope) or an already-present extra claim. Selective disclosure is additive by design; letting a Disclosure silently overwrite aud or exp would let a holder forge the very claims the issuer’s signature is supposed to pin down.

Structs§

DisclosableClaim
A claim an issuer wants to make selectively disclosable, instead of stamping it directly onto the JWT payload.
IssuedSdJwt
The result of issuing an SD-JWT: the signed JWT, the SD-JWT compact serialization ready to hand to a holder, and the raw Disclosure strings (in case the caller wants to persist or selectively re-forward a subset later, e.g. to build a holder-controlled presentation).
VerifiedSdJwt
The result of verifying a presented SD-JWT compact form: the validated JWT claims (signature, iss/aud/exp already checked by TokenManager::validate_token) plus whatever claims the presented Disclosures actually proved out.