Chapter VIII · Part III — Applications

Selective Disclosure 🪪

Everything in this course converges here: prove you're old enough, solvent enough, citizen enough — enough being the operative word. Selective disclosure is zero knowledge doing paperwork: reveal the predicate, hide the data.

The oversharing crisis

To buy a beer you prove you're over 18. The tool: hand over your entire identity — name, exact birthdate, address, document number, signature, sometimes your photo gets scanned and stored. The bouncer needed one bit; he received everything. Multiply by every bar, website, rental car desk and crypto exchange, and you get a permanent trail of identity dumps waiting to be breached, sold, or subpoenaed.

What we actually want is a predicate proof: convince the verifier that some statement about your attributes is true, while the attributes themselves stay in your pocket.

Verifier wants to know…PredicateWhat you keep private
"old enough to enter"age ≥ 18birth date, name, address
"EU resident"country ∈ EU_SETwhich country exactly
"can afford this loan"salary ≥ kexact salary, employer
"account in good standing"status = active ∧ expiry > todayaccount number, history
"human, one-per-person"holds valid passport credentialpassport number, nationality, name

🧪 The Bouncer: two ways to prove your age

Same club, same night, same you — two different technologies. Watch what the club (and its CCTV partner, and its data broker) actually captures.

🪪Nightclub Verifier — ID card vs ZK credential
Your credential (on your phone)
nameAiko Tanaka
birthdate1999-04-17
address12 Rue Oberkampf, Paris
citizenshipJapan
doc numberXN4471982
issuer: 🏛️ GovID — signed, tamper-proof
📷 What the club captured tonight
  • nothing yet…
> bouncer online. state your proof method…

The triangle: issuer · holder · verifier

1 · issues signed credential (all attributes) 2 · presents ZK predicate proof ("age ≥ 18") 3 · checks proof against issuer's public keys ISSUER gov · bank · exchange HOLDER (YOU) wallet holds credentials VERIFIER club · website · dApp

The magic of step 2: the proof convinces the verifier that (a) a valid issuer signed the underlying attributes, (b) the attributes satisfy the predicate — all without opening the envelope. The issuer can be offline forever; the verifier needs only the issuer's public key. This triangle is exactly how AnonCreds (Hyperledger), BBS+ credentials (W3C), and the EU's digital identity wallets work.

🔍 Deeper dive · unlinkability — the metadata war Even a perfect zero-knowledge proof leaks metadata: when, from where, and the fact you proved something at all. Worse, presentations can correlate — if every proof embeds the same unique credential serial, two verifiers can compare notes and reconstruct your evening ("wallet #4471 entered a bar at 22:14, bought wine at 22:51, booked a hotel at 23:30"). The defenses are structural: derived proofs (BBS+ presentations are statistically unlinkable across verifiers) and pairwise link secrets (AnonCreds gives each relationship its own pseudonym). The tension is permanent — revocation and audit want stable handles, privacy wants disposable ones — and real systems navigate it explicitly. Rule of thumb: a predicate proof is only as private as the set it hides you in.

How "age ≥ 18" becomes math: sets and trees

A clean way to prove a range without revealing the value: the issuer publishes a Merkle tree of approved values — one leaf per allowed age, root signed. Your proof: "I hold a path through this tree for some leaf." A plain Merkle path reveals which leaf (the value itself); a ZK membership proof shows the path validates without showing it. Chapter 3's hashes + Chapter 5's commitments + Chapter 6's algebra, assembled:

🌲Merkle Membership — the approved-ages tree (18…25)
your age: root = …
> pick an age and present a proof…
The subtlety you just saw In plain reveal mode the verifier learns your exact age (the leaf must be shown to re-hash the path). In ZK mode the same tree backs a zero-knowledge membership proof: the verifier learns age ∈ {18,…,25} → TRUE and nothing else — not which leaf, not your age, not even a per-visit identifier. Real systems (AnonCreds) use the same shape with hidden-linkability accumulators instead of a visible tree.
🔍 Deeper dive · revocation without doxxing Credentials expire or get revoked, and checking "still valid?" naively means asking the issuer about your credential — instant tracking. The classic fix is a cryptographic accumulator: one small number that summarizes an entire set, with (non-)membership proofs that don't reveal which element you hold. The issuer adds revoked IDs to the accumulator; unrevoked holders carry an update "witness" and prove "not in that set" in zero knowledge at each presentation. It's exactly the Merkle-membership game you just played, upgraded to hide the leaf and support cheap updates as the set evolves. (Smaller sets — the EU list, the 18+ range — work fine as plain signed Merkle roots; accumulators earn their keep on millions of credentials.)

🧪 Predicate Console

You hold one credential with six attributes. Choose what the verifier demands, run the session, and audit exactly what crossed the line. Redacted bars = never transmitted, not even hashed-then-guessed.

🎛️Predicate Console — you control the disclosure leak score: 0 attributes
your wallet's view — what was sent
    > verifier terminal ready…\n> select a predicate and RUN
    🔍 Deeper dive · replay attacks & holder binding A proof that convinced the bar must not convince a data broker who recorded it. Two standard stitches, both built from your Chapter 6 toolkit. First, freshness: the verifier issues a random nonce and the proof consumes it (Fiat–Shamir over the nonce) — a recorded proof hashes a stale nonce and fails instantly. Second, holder binding: the credential is issued to a key on your device, and every proof attests "the presenter holds the private key that owns this credential" — a Schnorr-style ID proof embedded in the predicate proof. Together they say: only you, only now. A stolen wallet file without the key is dead paper; a recording without a live nonce is history.

    Where this ships today

    🔍 Deeper dive · choosing your disclosure architecture If you're picking a stack for a product, the trade space reduces to three families:
    ArchitectureIssuer effortUnlinkable?Holder costShips as
    Full-ZK credentials
    (AnonCreds, BBS+)
    new credential infrayes — derived/pairwise proofsmillisecondsHyperledger Aries, W3C VC + BBS+
    SD-JWTminimal — any JWT issuerno — colluding verifiers link sessionstrivialIETF draft, OpenID ecosystems
    SNARK over documentsnone — reuse existing signaturesyesseconds (phone-class)passport ZK, anon Aadhaar
    The pattern: the less the issuer cooperates, the more mathematics the holder pays — and privacy from the issuer itself (who never learns what you prove, or to whom) is only on the table in the third family.
    Honest limitations — read before deploying utopia
    • Linkability: naive credentials let verifiers collude and correlate your sessions. Fix: per-verifierunlinkable presentations (blind issuance, pseudonyms) — AnonCreds and BBS+ handle it; roll your own and you won't.
    • Holder binding: a stolen credential mustn't verify — presentations must bind to an authenticated holder key.
    • Revocation: "not revoked" needs proving non-membership in a revocation registry without revealing your credential ID (accumulators again).
    • Issuer trust: ZK proves the math; it can't prove the birthdate on file was true. Garbage in, zero-knowledge garbage out.
    Full circle Chapter 1 promised proofs that convince but don't reveal. You've now seen every layer: the clock universe (2) that hides exponents, the hashes (3) that seal envelopes, the commitments (4) that lock choices, the interactive dances (5) and their sigma-protocol compression (6), the circuits that swallow entire programs (7) — and finally a citizen proving enough to a bouncer while their identity stays home. That's zero knowledge, end to end.

    📌 Week 8 — key takeaways

    • Selective disclosure = prove predicates (age ≥ 18, country ∈ EU, score ≥ 600) while values stay in your wallet.
    • The triangle: issuer signs once and goes offline; holder derives per-verifier proofs; verifier checks against public keys.
    • Set membership (Merkle trees, accumulators) turns ranges into "is your value in the approved set?" — with ZK hiding even the leaf.
    • The three engineering dragons — unlinkability, revocation, replay/holder binding — each have standard, battle-tested slayers (derived proofs, accumulators, nonce challenges + device keys).
    • Architecture rule of thumb: less issuer cooperation ⇒ more holder-side math (AnonCreds/BBS+ → SD-JWT → passport SNARKs).
    • ZK proves the math, never the truth of the issuer's records — garbage in, zero-knowledge garbage out.
    🎯

    Final Checkpoint: Reveal Less, Prove More

    The verifier needs "age ≥ 18: TRUE". With a well-designed ZK credential, what does the holder transmit?
    • The birthdate, encrypted with the verifier's key.
    • A proof that a signed attribute satisfies the predicate — no attribute values, no signature material.
    • A hash of the birthdate the verifier brute-forces against 30,000 dates.
    • The credential, plus a promise not to store it.
    Predicates travel, values stay. (Answer 3 is a real vulnerability of naive designs — a 30,000-entry dictionary defeats any raw hash of a birthdate; proper schemes use ZK proofs or salted commitments.)
    In the Merkle set-membership scheme, what stops a 25-year-old from proving "age ≥ 18" if the tree only contains {18,…,25}?
    • Nothing — they can forge a path.
    • The bouncer eyeballs them.
    • Every valid path leads to a leaf in the approved set; there is no path for a non-member value, and the root is the issuer's signed anchor.
    • The tree is secret.
    Membership is hash-verifiable from the signed root; forging a path = finding a SHA-256 collision (Chapter 3's grind, ~2²⁵⁶).
    Why does "prove country ∈ EU" leak less than sending your country code, even though the verifier learns the predicate is true?
    • The verifier learns one bit of a disjunction, not which of the 27 disjuncts is true — the proof is witness-indistinguishable over the whole set.
    • Because the country code is transmitted twice-hashed.
    • Because EU countries share a database.
    • It doesn't — same leakage.
    Set membership collapses 27 possibilities into one TRUE. Correlation across sessions drops accordingly — though a singleton set ("country = FR") would leak everything; predicate granularity is a design choice.
    Which real limitation of selective disclosure does ZK math NOT solve by itself?
    • Forging membership paths.
    • Tampering with signed attributes.
    • Replay of a presentation to a second verifier.
    • Whether the issuer's records were accurate in the first place.
    ZK proves consistency with issuer signatures — not the truth of the underlying facts. (Replays are handled by challenge/response or fresh holder binding, also outside pure ZK.)