SolidusIdentity
Create Your DID

Why Each Disclosed Field Carries Its Own Salt

And read the scope section before the mechanism section: the honest claim is narrower than the page title suggests.

What a disclosure actually looks like

Split a compact selectively-disclosable credential on ~. Every segment between the leading signed token and the optional trailing key-binding token is base64url-encoded JSON, and decoding one gives you a three-element array:

[ salt, claimName, claimValue ]

Any base64url decoder plus JSON.parse reads it. No Solidus tooling is required, and the plaintext value and its salt are both right there, which is correct: a disclosure is meant to be readable by whoever you sent it to.

What the salt is for, which is the part usually skipped

Without a salt, the digest of a low-entropy value is guessable.

Consider a hidden field whose value is a country, or a boolean, or an age bracket. The verifier holds its digest. The value space is tiny. Hash every candidate, compare, and the "hidden" field is recovered in microseconds. Hiding a value behind an unsalted hash hides nothing when there are only a few possible values.

The salt is what makes that attack fail. Each disclosure carries its own fresh random salt, so the digest depends on a value the attacker cannot enumerate. Guessing the claim is no longer enough: you would have to guess the salt too.

Two consequences worth internalising:

  • The salt must be per-disclosure, not per-credential. One salt reused across fields would let someone who learns it attack every field at once.
  • The salt is not a secret you protect. It ships in the clear inside every disclosure you choose to reveal. Its job is to defeat pre-computation against undisclosed fields, not to protect disclosed ones.

The scope narrowing, which is the honest core of this page

"Salted-hash disclosure" names a pattern with two standardised instances. Only one of them exists in Solidus.

  • The ISO mdoc / Mobile Security Object variant is not implemented anywhere in our stack.

That second one is not a nuance. Checked against the published package rather than our own repo: zero files reference CBOR, zero reference COSE. The encoding and signature layers the ISO variant is built on. A control grep for sdjwt matched thirty files, so the search was working.

The package says so itself. Its own header comment describes SD-JWT VC as one of the two formats accepted by the EU wallet architecture and names ISO mDoc as "the other": the one this module does not do.

So if you read "Solidus supports salted-hash disclosure" and pictured mobile driving licences, that is the wrong picture. We support one specific standardised instance of the pattern, not the pattern generally, and a relying party or wallet expecting the ISO variant gets nothing from us.

What the salt does not fix

It does not stop the digest travelling. An undisclosed field's digest is still in the signed payload and still goes out with every presentation: a stable handle that colluding verifiers can compare. The salt makes the value unguessable; it does nothing about the digest's presence.

It does not make a disclosed field private. Once you reveal a field, the verifier has the value. Salting concerns what stays hidden.

It does not authenticate anything. Salted hashes bind values to a signed payload. Who is presenting them is a separate mechanism, holder binding.

Keep reading

Why Each Disclosed Field Carries Its Own Salt · Solidus — Solidus Identity