Skip to content

baseid-sd-jwt

Implements SD-JWT (Selective Disclosure JWT) and the SD-JWT-VC profile for verifiable credentials. Issuers create credentials with selectively-disclosable claims, and holders choose which claims to reveal during presentation.

  • Selective Disclosure — Mark individual claims as disclosable; holders choose what to reveal per presentation
  • SdJwtIssuer Builder — Fluent API separating plain (always-visible) and SD (selectively-disclosable) claims
  • Compact Serialization — Standard jwt~disc1~disc2~[kb_jwt] format with parse/serialize roundtrip
  • Signature VerificationSdJwtVerifier validates JWT signature, matches disclosure digests, and merges claims
  • Key Binding JWT — Optional holder key binding for proof of possession
  • IETF Spec Compliance — Tested against IETF SD-JWT draft test vectors
use baseid_sd_jwt::{SdJwt, issuer::SdJwtIssuer, verifier::SdJwtVerifier};
use baseid_crypto::KeyPair;
use baseid_core::types::KeyType;
let kp = KeyPair::generate(KeyType::Ed25519)?;
// Issue an SD-JWT with plain and selectively-disclosable claims
let sd_jwt = SdJwtIssuer::new(&kp, "did:key:issuer#key-0")
.add_plain_claim("iss", serde_json::json!("did:key:issuer"))
.add_sd_claim("name", serde_json::json!("Alice"))
.add_sd_claim("age", serde_json::json!(30))
.add_sd_claim("email", serde_json::json!("alice@example.com"))
.build()?;
// Compact serialization for transport
let compact = sd_jwt.serialize(); // "jwt~disc1~disc2~disc3~"
let parsed = SdJwt::parse(&compact)?;
// Verify with all disclosures
let verifier = SdJwtVerifier::new(&kp.public);
let claims = verifier.verify(&sd_jwt)?;
assert_eq!(claims["name"], "Alice");
assert_eq!(claims["age"], 30);

Holders can withhold disclosures to reveal only specific claims:

// Present only name and age, withhold email
let mut presented = sd_jwt.clone();
presented.disclosures.pop(); // remove email disclosure
let claims = verifier.verify(&presented)?;
assert_eq!(claims["name"], "Alice");
assert_eq!(claims["age"], 30);
assert!(claims.get("email").is_none()); // not revealed
OperationDescription
Disclosure::new(name, value)Create with random 128-bit salt
disclosure.encode()Base64url of [salt, name, value] JSON array
disclosure.decode(encoded)Parse from base64url string
disclosure.digest()SHA-256 hash (base64url) for _sd array matching

The SdJwtVerifier::verify() method performs these steps:

  1. Verify the JWT signature against the issuer’s public key
  2. Decode each disclosure and compute its SHA-256 digest
  3. Match digests against the _sd array in the JWT claims
  4. Merge disclosed claims into the result object
  5. Remove _sd and _sd_alg metadata from the output
TypeDescription
SdJwtContainer for issuer JWT, disclosures, and optional key binding JWT
SdJwtIssuerBuilder for creating SD-JWTs with plain and SD claims
SdJwtVerifierVerifies SD-JWT presentations and returns merged claims
DisclosureDecoded disclosure with salt, claim name, and value