feat(#20): ZK-lite Selective Disclosure for IdentityVector#93
feat(#20): ZK-lite Selective Disclosure for IdentityVector#93Nicolas0315 wants to merge 1 commit intomainfrom
Conversation
Implements a lightweight commitment-based selective disclosure mechanism that lets a prover share specific IdentityVector attributes without exposing the full vector. Protocol (per field): - Prover commits with SHA-256(salt:JSON(value)), keeps salt private - Creates DisclosureEnvelope: revealed fields include salt+value, blinded fields include only the commitment hash - Verifier recomputes commitment and confirms match for each revealed field API surface: - buildCommitments(vector): ProverSecrets — generate per-field salts - createEnvelope(secrets, revealKeys): DisclosureEnvelope — selective reveal - verifyEnvelope(envelope): VerificationResult — per-field verification - extractRevealedAttributes(envelope): Partial<IdentityVector> — extract values Tests: 19 tests covering commitment randomness, blinding invariants, tamper detection (value + salt), full E2E prover/verifier simulation. All 153 existing tests continue to pass. Closes #20
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Nicolas0315
left a comment
There was a problem hiding this comment.
RALPH Review — PR #93: ZK-lite Selective Disclosure for IdentityVector
Verdict: APPROVE ✅
Correctness ✅
The commitment scheme is sound: SHA-256(salt:JSON(value)) with a per-field random 16-byte salt provides proper binding. The prover/verifier protocol correctly separates what gets shared (DisclosureEnvelope) from what stays private (ProverSecrets). Type system enforces the invariant at compile time — CommittedField structurally cannot carry salt or value.
One minor observation: JSON.stringify(value) ordering is non-deterministic for objects with unordered keys. For the current IdentityVector shape this is fine (prover and verifier both use the same object reference), but worth noting if cross-language or cross-serialization verification is ever needed in the future.
Security ✅
- CSPRNG:
randomBytes(16)— 128-bit entropy, appropriate - SHA-256 via Node native
crypto— no third-party deps, no supply chain risk - Salt prevents preimage and rainbow table attacks on the committed values
- Blinded fields expose only the commitment hash — verifier cannot reverse-compute the value
- No timing oracle exposure (Node's
===on hex strings is not constant-time, but this is low-risk for this ZK-lite scope; a future hardening pass could usetimingSafeEqualfor the commitment comparison inverifyEnvelope)
Test Coverage ✅
19 tests covering all critical paths:
- Salt randomness / non-determinism
- Blinding invariants (CommittedField has no salt/value)
- Tamper detection: value swap ✓, salt corruption ✓
- Full E2E prover/verifier simulation
- Edge cases: all-blinded, all-revealed, empty key list, default vector
Style / Conventions ✅
- Conventional Commits:
feat(#20): ...✓ - Follows existing vitest/TypeScript conventions ✓
- Self-contained — zero mutations to existing files ✓
Minor Suggestions (non-blocking)
- Future hardening:
verifyEnvelopeuses===for hex string comparison. Considercrypto.timingSafeEqualfor the commitment check if this ever gates sensitive access decisions. - JSON serialization note: Add a JSDoc note that
JSON.stringifykey ordering must be consistent between prover and verifier; currently guaranteed by same-process usage, but worth documenting for future cross-lang compatibility.
Risk: Low — pure additive change, no existing files modified, no new runtime dependencies. Ready to merge.
Summary
Implements ZK-lite Selective Disclosure for
IdentityVectorattributes (Issue #20).Lets a prover share specific profile fields with a verifier without revealing the full IdentityVector. Uses a commitment scheme (SHA-256 + random salt) that provides the same disclosure-without-exposure guarantees as ZK proofs at this stage of the trust infrastructure.
Protocol
New API (
packages/katala/core/SelectiveDisclosure.ts)buildCommitments(vector)createEnvelope(secrets, revealKeys)verifyEnvelope(envelope)extractRevealedAttributes(envelope)Partial<IdentityVector>from verified envelopeTests
19 new tests covering:
All 153 existing tests pass.
RALPH Self-Review (Opus)
Security ✅
randomBytes(16)— 128-bit entropycrypto— no third-party depssalt/value— compiler-enforced privacySHA-256(salt:JSON(value))— salt prevents preimage attacks; acceptable for ZK-lite scopeQuality ✅
CommittedFieldcannot structurally carryvalueorsaltStyle ✅
Risk: Low — additive change only, no new runtime dependencies
Closes #20