Verifiable credentials with selective disclosure let users prove specific claims without revealing full credentials. ZK proofs make this cryptographically secure. This guide covers practical implementation -- from credential structure and proof generation to real-world privacy concerns that traditional identity systems consistently fail to address.
Why Selective Disclosure Matters
Every time you hand a bartender your driver's license, you reveal your full name, home address, date of birth, organ donor status, and license number -- all to prove a single fact: you are over 21. This is the fundamental problem with traditional credential systems. They operate on an all-or-nothing model that leaks far more information than any verifier actually needs.
ZK credential verification eliminates this structural privacy failure. With a zero-knowledge proof, you can demonstrate that a signed credential satisfies a specific condition without revealing any data beyond the condition itself. The verifier learns the answer -- "yes, this person is over 21" -- and nothing more. Not the birth date, not the name, not the address.
Centralized identity databases are honeypots. Equifax (147M records), OPM (21.5M records), and Optus (9.8M records) proved that storing credentials in cleartext creates catastrophic breach surfaces. ZK credentials eliminate the need for verifiers to store or even see sensitive data in the first place. Learn more about computing on encrypted data to keep credentials private end-to-end.
Credential Structure
A verifiable credential contains three core components that together form a cryptographically binding assertion about a subject:
- Claims: Statements about the subject (name, age, address, etc.)
- Issuer signature: Cryptographic proof of issuance
- Metadata: Issuance date, expiration, schema
{
"subject": "did:example:user123",
"claims": {
"name": "Alice Smith",
"birthDate": "1990-05-15",
"citizenship": "US",
"employerVerified": true
},
"issuer": "did:example:government",
"signature": "..."
}
Each claim is individually committed using a Merkle tree or Pedersen commitment scheme. This structure is what enables selective disclosure -- the holder can open individual leaf commitments without exposing sibling nodes. When paired with SHA3-256 hashing, these commitments are post-quantum resistant at the hash layer, which matters for long-lived credentials like government IDs or professional certifications.
Selective Disclosure
With ZK, users control what to reveal. There are four distinct disclosure modes, each providing a different privacy-utility tradeoff:
Disclosure Options
Full disclosure: Reveal claim value
Predicate proof: Prove condition (age > 18)
Existence proof: Prove claim exists
Non-disclosure: Prove credential valid without revealing claim
Predicate proofs are the most powerful mode for everyday use. Rather than disclosing an exact salary to a mortgage lender, a borrower can prove "income exceeds $75,000" without revealing the precise figure. The ZK circuit encodes the comparison as an arithmetic constraint and produces a proof that the committed value satisfies it -- verifiable in nanoseconds, forgeable in never.
ZK Credential Proof
The proof generation and verification flow is straightforward from the developer's perspective, even though the underlying cryptographic machinery is substantial:
// Generate selective disclosure proof
const proof = await zkCredentials.prove({
credential: myCredential,
disclose: ['citizenship'], // Reveal this
predicates: [
{ claim: 'birthDate', predicate: 'olderThan', value: 21 }
],
hide: ['name', 'employerVerified'] // Don't reveal
});
// Verifier learns:
// - citizenship is "US" (disclosed)
// - user is over 21 (predicate satisfied)
// - credential is valid (signature verified)
// Verifier does NOT learn: name, exact birth date, employer status
Under the hood, the prover constructs a STARK or SNARK circuit that takes the credential's Merkle root, the issuer's public key, and the individual claim values as private witness inputs. The circuit verifies the issuer signature, checks the Merkle inclusion proof for each disclosed or predicated claim, and evaluates the predicate constraints -- all without exposing any private input to the verifier.
Credential Schemas
Define credential structure for interoperability. Schemas ensure that issuers, holders, and verifiers all agree on the claim types, required fields, and supported predicates for a given credential class:
{
"type": "IdentityCredential",
"version": "1.0",
"claims": {
"name": { "type": "string", "required": true },
"birthDate": { "type": "date", "required": true },
"citizenship": { "type": "string", "required": false }
},
"predicates": {
"birthDate": ["olderThan", "youngerThan", "between"]
}
}
Issuer Implementation
Credential issuers must follow a strict protocol to ensure that the credentials they produce are compatible with ZK selective disclosure:
- Verify subject identity before issuance using out-of-band verification (in-person, biometric, or multi-factor)
- Sign credentials with ZK-compatible signatures -- BBS+ signatures are ideal because they natively support selective disclosure without wrapping in an additional ZK layer
- Publish public keys for verification through a decentralized registry or well-known DID document
- Maintain revocation registry using a cryptographic accumulator that supports ZK membership proofs
In H33's production pipeline, the issuer signature step uses Dilithium (ML-DSA) for post-quantum security. Signing and verification together take approximately 244 microseconds, and the entire authentication stack -- including BFV fully homomorphic encryption for biometric matching -- runs at 2,172,518 auths/sec on Graviton4 hardware with a per-auth latency of roughly 42 microseconds.
Verifier Implementation
Verifiers specify exactly what they need to see and nothing more. This is the minimal-disclosure principle in action:
// Verifier requests proof
const request = {
required: [
{ claim: 'citizenship', disclose: true },
{ claim: 'birthDate', predicate: 'olderThan', value: 18 }
],
trustedIssuers: ['did:example:government'],
purpose: "Age verification for alcohol purchase"
};
// User generates proof matching request
const proof = await wallet.generateProof(request);
// Verifier validates
const valid = await verifier.check(proof, request);
The verifier never touches the raw credential. It receives only the ZK proof, checks it against the request specification, and gets a boolean result. No PII is transmitted, stored, or logged. This is not just a privacy improvement -- it eliminates an entire category of regulatory liability under GDPR, CCPA, and similar data-protection frameworks.
Privacy Considerations
A ZK credential system must defend against three correlation attacks that traditional systems ignore entirely:
- Issuer unlinkability: Issuers should not learn when or where credentials are used. If a government issuer can observe every verification event, it has a real-time surveillance feed. Blind issuance protocols prevent this.
- Verifier unlinkability: Different verifiers must not be able to correlate proofs to the same holder. Each proof should be cryptographically fresh -- randomized so that two proofs from the same credential appear unrelated.
- Minimal disclosure: Only prove what is asked. The system must reject verifier requests that demand more information than the stated purpose requires.
A credential system that achieves all three unlinkability properties gives users the digital equivalent of paying with cash -- the transaction is valid, but it does not create a traceable record.
Revocation
Handle credential revocation privately. This is one of the hardest problems in ZK credential design, because naive revocation (publishing a revocation list) destroys holder privacy by letting anyone check whether a specific credential was revoked:
- Accumulators allow ZK membership proofs -- the holder proves their credential is still in the "valid" set without revealing which credential it is
- Non-revocation witness: User proves credential not revoked without revealing which credential, using an updated accumulator witness
- Update efficiency: When revoking, only the accumulator and affected witnesses change. H33 uses in-process DashMap caching for witness lookups at 0.085 microseconds per lookup, keeping revocation checks off the critical path
Performance at Scale
ZK credential verification is only practical if it runs fast enough for real-time use. Proof generation is the bottleneck -- SNARK provers are computationally expensive. But verification is the operation that matters at scale, because every relying party must verify every proof it receives.
| Operation | Latency | Notes |
|---|---|---|
| Proof verification (STARK lookup) | 67 ns | SHA3-256 hash, post-quantum |
| Credential attestation (Dilithium sign + verify) | ~244 µs | ML-DSA, FIPS 204 compliant |
| FHE biometric batch (32 users) | ~1,109 µs | BFV inner product, N=4096 |
| Full-stack per-auth | ~42 µs | FHE + ZKP + attestation combined |
| Cache lookup (DashMap) | 0.085 µs | In-process, zero contention |
At these latencies, ZK credential verification adds negligible overhead to any authentication flow. The entire H33 stack processes 1.595 million authentications per second on a single Graviton4 instance -- proof that privacy and performance are not in conflict.
ZK credentials are the foundation of privacy-respecting identity. Users control their data while verifiers get cryptographic assurance. The technology is no longer theoretical -- it runs in production, at scale, with post-quantum security built in from the ground up.
Ready to Go Quantum-Secure?
Start protecting your users with post-quantum authentication today. 1,000 free auths, no credit card required.
Get Free API Key →