Lion Cipher Lab

Verify a Verifiable Credential with SD-JWT (build issuer, holder, and verifier from scratch)

Why it matters

Digital identity is moving into wallets. The EU Digital Identity Wallet, and the OpenID for Verifiable Presentations (OpenID4VP) flows built around it, need a credential format that lets a person prove ONE fact -- 'I am over 18', 'I am a licensed pharmacist', 'I live in this canton' -- without dumping their entire identity document at a bar, a website, or a border kiosk. A plain signed JWT cannot do that: it is all-or-nothing, so the moment you show it to prove your age you have also revealed your name, full birthdate, document number and everything else in it. SD-JWT (Selective Disclosure…

What you'll learn

Steps in this lab

  1. The one idea: prove a claim without revealing the data behind it
  2. Set up: a clean folder and two packages
  3. Build the machinery from scratch (sdjwt.py)
  4. The issuer: turn claims into salted Disclosures + a signed _sd digest list
  5. The holder: choose what to reveal, then bind it with a KB-JWT
  6. The verifier: check the signature, re-hash every Disclosure, verify the binding
  7. Run the happy path: prove 'over 18' without revealing the birthdate
  8. Failure mode A -- no key binding: the credential becomes a bearer token
  9. Failure mode B -- skipping the _sd digest check: forged claims accepted
  10. Failure mode C -- weak or reused salt: linkability and value-guessing
  11. Failure mode D -- ignoring aud/nonce: a presentation replays across verifiers
  12. Judgment: when SD-JWT VC, when an mdoc/mDL, when just a signed JWT
  13. Clean up

Open the full interactive lab →