Skip to content
Tamga Wallet

Standards

SD-JWT VC: the format behind selective disclosure

SD-JWT VC is the credential format that lets a wallet send only some fields of a signed document. A short, plain explanation with a simplified example.

By: Tamga Wallet team4 min read

SD-JWT VC is the format every Tamga document uses. It is a signed JSON document in which every field can be hidden or revealed on its own: the institution signs the whole document once, and when you show it, your wallet sends only the fields you approved, while the signature still checks out. This is what makes selective disclosure possible.

The name has two parts. SD-JWT (Selective Disclosure for JSON Web Tokens) is the mechanism, published by the IETF as RFC 9901 in November 2025. SD-JWT VC (SD-JWT-based Verifiable Digital Credentials) is the profile that turns it into a credential with a type, an issuer and a holder key; it is an IETF draft (version 19, August 2026) that has gone to the IESG for publication.

How can a signature stay valid when fields are missing?

The trick is that the institution never signs the field values directly. For every field, it builds a small array of a random salt, the field name and the value, encodes it, and hashes it. The signed document carries only these hashes, in a list called _sd. The encoded arrays, called disclosures, are handed to your wallet next to the signed part.

When you show the document, the wallet attaches the disclosures for the fields you approved and leaves out the rest. The verifier hashes each disclosure it received and finds the result in the _sd list. A field that was not sent stays a hash, and thanks to the random salt nobody can guess what is behind it, even for a short value such as a birth date.

The three parts of an SD-JWT VC and what is sent

On the wire, the parts are joined with a tilde: <signed JWT>~<disclosure>~<disclosure>~<KB-JWT>. Hiding a field means removing its disclosure from the string. Nothing is added and the signed part does not change.

What does it look like?

Here is the signed part of a student certificate after decoding:

Simplified example: decoded payload of a student certificateJSON
{
  "iss": "https://issuer.example.edu",
  "vct": "urn:tamga:edu:StudentCredential:1",
  "iat": 1791417600,
  "exp": 1803772800,
  "_sd_alg": "sha-256",
  "_sd": [
    "2l60GCkDykrIj0RNY7DFIWRL6ku9DweCmBUpcHSSDGc",
    "Ah9zbMv904G2xsaDrXkT7qj_7b00R0h7fgJeumqk7cA",
    "F7SV1UscA8u8blXRZwlwhqT_AE2mByqMexF2hFCLKLA"
  ],
  "cnf": {
    "jwk": { "kty": "EC", "crv": "P-256", "x": "…", "y": "…" }
  }
}

Only the fields needed to check the document are in the clear: who issued it (iss), its type (vct), when it was issued and when it expires, and the public part of your phone's key (cnf). Your name, programme and enrolment status are among the three hashes.

And here is one disclosure, the one a student-discount check would ask for:

Simplified example: one disclosure, encoded and decodedText
WyJxVjNfdUZ0Ml9IZVA5a1J4Wm1XN0JBIiwgImlzX2Vucm9sbGVkIiwgdHJ1ZV0

→ ["qV3_uFt2_HeP9kRxZmW7BA", "is_enrolled", true]

SHA-256 of the encoded string, base64url:
2l60GCkDykrIj0RNY7DFIWRL6ku9DweCmBUpcHSSDGc   ← first entry in _sd

A website that asks "are you a student?" receives the signed part, this one disclosure and a fresh device signature. It learns is_enrolled: true and which institution says so. Your name and programme stay hashes it cannot read.

What stops someone from replaying your document?

The last part of the string is the KB-JWT (key binding JWT). Your wallet creates it for every presentation and signs it with the key in your phone's secure hardware, the key whose public half is in cnf. It contains the verifier's one-time nonce, the verifier's identifier, the time, and sd_hash, a hash of exactly the parts being presented.

A copied presentation fails at a second verifier because the audience and nonce don't match. A middleman who removes a disclosure breaks sd_hash. And without the phone's key, nobody can produce a new KB-JWT (why a copied document is useless). The standard leaves key binding optional; in Tamga it is mandatory for every document.

What does Tamga add on top of the standard?

The standard leaves options open. Tamga's profile closes them:

TopicTamga rule
SignatureES256 (P-256) only
Hashsha-256 only
typ headerdc+sd-jwt; the older vc+sd-jwt is rejected
Salt128 bits, new for every disclosure
Hidden fieldsthe institution hides every field that is allowed to be hidden
Key bindingmandatory, no exceptions
Decoy hashesnot used

Tamga's identity credential is also issued in a second format, ISO mdoc, for in-person checks and browsers that accept only mdoc: ISO 18013-5 and mdoc. How documents travel between institution, wallet and verifier: OpenID4VCI and OpenID4VP.

Go deeper

The full byte-level profile, with worked examples, the verification algorithm and the common implementation mistakes, is in the Tamga Network developer docs: SD-JWT VC profile (SPEC-CRED-0002). The format choice is explained on Credential formats.

Frequently asked questions

Is SD-JWT VC the same as a JWT?

It builds on one. The signed part is an ordinary JWT; what SD-JWT adds is the salted hashes, the disclosures next to the JWT and the key binding signature at the end.

Can a verifier see how many fields I hid?

It sees the number of hashes in _sd, so it can tell that hidden fields exist. It cannot read them. The field list of each document type is public in the schema catalogue anyway.

Which Tamga documents use SD-JWT VC?

All of them: student certificates, diplomas, tickets, identity, driving licence information, and email and phone credentials. The identity credential is issued as mdoc as well.

Sources

Step-by-step help is in the Guide

Everything from setup to a lost phone, with the app’s own screens.

Go to the Guide

Get Tamga Wallet

On the App Store and Google Play. Carry your diploma, student card and tickets on your phone.

 All articles