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.
Key Features
Section titled “Key Features”- 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 Verification —
SdJwtVerifiervalidates 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
Quick Start
Section titled “Quick Start”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 claimslet 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 transportlet compact = sd_jwt.serialize(); // "jwt~disc1~disc2~disc3~"let parsed = SdJwt::parse(&compact)?;
// Verify with all disclosureslet verifier = SdJwtVerifier::new(&kp.public);let claims = verifier.verify(&sd_jwt)?;assert_eq!(claims["name"], "Alice");assert_eq!(claims["age"], 30);Selective Presentation
Section titled “Selective Presentation”Holders can withhold disclosures to reveal only specific claims:
// Present only name and age, withhold emaillet 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 revealedDisclosure Internals
Section titled “Disclosure Internals”| Operation | Description |
|---|---|
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 |
Verification Steps
Section titled “Verification Steps”The SdJwtVerifier::verify() method performs these steps:
- Verify the JWT signature against the issuer’s public key
- Decode each disclosure and compute its SHA-256 digest
- Match digests against the
_sdarray in the JWT claims - Merge disclosed claims into the result object
- Remove
_sdand_sd_algmetadata from the output
Key Types
Section titled “Key Types”| Type | Description |
|---|---|
SdJwt | Container for issuer JWT, disclosures, and optional key binding JWT |
SdJwtIssuer | Builder for creating SD-JWTs with plain and SD claims |
SdJwtVerifier | Verifies SD-JWT presentations and returns merged claims |
Disclosure | Decoded disclosure with salt, claim name, and value |
Related Crates
Section titled “Related Crates”baseid-crypto— JWT signing and verification primitivesbaseid-issuer-core—issue_sd_jwt_vc()for SD-JWT VC issuancebaseid-vc— W3C VC data model (SD-JWT-VC is the privacy-preserving alternative)baseid-oid4vci— OID4VCI supportsvc+sd-jwtformat