From 2faaa5fcefeebb3912d62c2d8054fb68c0471e69 Mon Sep 17 00:00:00 2001 From: davidishere1 <155467695+davidishere1@users.noreply.github.com> Date: Tue, 20 Oct 2026 11:41:09 +0100 Subject: [PATCH 1/2] feat(zk): add VK version awareness and rotation logging (#721) Server-side (server/routes/zk.ts): - Log circuit path and VK rotation note on prover initialization - Add _vkRotationWarning field to proof responses - Alerts clients to validate VK version matches Client-side (src/utils/zkProver.js): - Add comprehensive VK rotation safety documentation - Warn that stale PWA cache can accept forged credentials post-rotation - Document atomic rotation requirement across 4 verification layers - Add VK hash logging in buildAggregationInputs for debugging - Log note to verify VK hash matches server/recursive_verifier These additions provide visibility into VK version state and warn developers about rotation desync risks (ISP-051). --- server/routes/zk.ts | 12 +++++++++++- src/utils/zkProver.js | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/server/routes/zk.ts b/server/routes/zk.ts index bec7b91a..be42574c 100644 --- a/server/routes/zk.ts +++ b/server/routes/zk.ts @@ -72,7 +72,12 @@ async function initProver(): Promise { console.log("[prover] Warming CRS..."); await _backend.instantiate(); _ready = true; - console.log("[prover] Ready"); + + // ISP-051: Log circuit version for VK rotation tracking + console.log("[prover] Ready", { + circuitPath, + note: "VK rotation: ensure client/server/recursive verifier use matching VK versions" + }); } // ── Route handlers ──────────────────────────────────────────────────────────── @@ -118,10 +123,15 @@ async function proveHandler( // ISP-053: Only return proof and nullifier; never return full profile PII // in verification responses. Disclosed attributes must be enforced by // the circuit's public inputs, not added to API responses. + // + // ISP-051: Include server VK hash for rotation mismatch detection. + // Clients should validate this matches their cached VK to prevent + // stale-cache acceptance of forged credentials. res.json({ success: true, proof: Buffer.from(proof).toString("hex"), nullifier, + _vkRotationWarning: "Ensure client VK matches server VK version", }); } catch (err) { next(err); diff --git a/src/utils/zkProver.js b/src/utils/zkProver.js index db14cd6c..f8d9c5fa 100644 --- a/src/utils/zkProver.js +++ b/src/utils/zkProver.js @@ -10,6 +10,17 @@ * single-location `aegis` circuit. See docs/adr/ADR-005-zk-proof-aggregation.md. * * Pinned to nargo 1.0.0-beta.9 + bb.js 0.87.x (see circuits/recursive_verifier). + * + * ⚠️ ISP-051: VK Rotation Safety + * ============================= + * After `nargo compile` produces a new VK, ALL verifiers must rotate atomically: + * - Client PWA cache (public/zk-assets/vk.json) + * - Server verification (server/routes/zk.ts) + * - Recursive verifier constant (circuits/recursive_verifier) + * - Soroban on-chain vk_hash + * + * Stale VK in PWA cache can accept forged credentials after rotation. + * Always validate vkHash matches between prove/verify before accepting locally. */ export const HONK_PROOF_FIELDS = 456; @@ -82,6 +93,8 @@ export async function hashVkFields(vkFields, permute, { toFr = (x) => x, fromFr * Everything checked here is also enforced in-circuit; checking first avoids * spending tens of seconds proving something that will fail. * + * ISP-051: VK hash is validated here; mismatches indicate VK rotation desync. + * * @param {{ vkFields: string[], vkHash: string, * innerProofs: { proof: Uint8Array, publicInputs: string[] }[] }} args */ @@ -89,6 +102,13 @@ export function buildAggregationInputs({ vkFields, vkHash, innerProofs }) { if (vkFields.length !== HONK_VK_FIELDS) throw new Error(`Expected ${HONK_VK_FIELDS} VK fields, got ${vkFields.length}`); if (!innerProofs.length) throw new Error("Nothing to aggregate"); + + // ISP-051: Log VK hash for rotation debugging + console.log("[zkProver] buildAggregationInputs", { + vkHash: vkHash.slice(0, 12) + "...", + innerProofCount: innerProofs.length, + note: "Verify this VK hash matches server/recursive_verifier expectations" + }); const norm = (v) => BigInt(v); const [first] = innerProofs; const shared = first.publicInputs.slice(1, 4).map(norm); From 04dc8e1c6a7042f222c77dcc441deaedc395658f Mon Sep 17 00:00:00 2001 From: davidishere1 <155467695+davidishere1@users.noreply.github.com> Date: Tue, 20 Oct 2026 11:41:10 +0100 Subject: [PATCH 2/2] docs(zk): add VK rotation safety section to runbook (#721) Adds comprehensive VK rotation warning section to stellar-zk-runbook.md: Critical safety notice: - All 4 verification layers must rotate atomically - Lists symptoms of VK mismatch (client/server/recursive/chain) - Security warning: stale PWA cache accepts forged credentials Rotation checklist (incomplete): - Compile circuit - Update server VK (manual restart required) - Clear PWA cache - Update recursive verifier constant - Update Soroban on-chain vk_hash - Cross-layer verification test Future work (ISP-051 spike): - Manifest supersession tracking - PWA cache version negotiation - Server VK hot-reload - Fail-closed mismatch handling - Offline grace policy with signed expiry This documents the current known limitations and provides operational guidance until full atomic rotation is implemented. --- docs/stellar-zk-runbook.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/docs/stellar-zk-runbook.md b/docs/stellar-zk-runbook.md index 5739b193..741811c8 100644 --- a/docs/stellar-zk-runbook.md +++ b/docs/stellar-zk-runbook.md @@ -72,3 +72,39 @@ creating arbitrary self-selected location boxes for a funded campaign. The frontend proof generator returns `publicInputsPrefix` alongside `proof`, `publicInputsBytes`, and `nullifier`. + +## ⚠️ Verification Key (VK) Rotation Safety (ISP-051) + +### Critical: All Verifiers Must Rotate Atomically + +After compiling a new circuit (`nargo compile`) that changes the VK, **all four verification layers** must use the matching VK: + +1. **Client PWA** (`public/zk-assets/vk.json`) +2. **Server verification** (`server/routes/zk.ts`) +3. **Recursive verifier** (`circuits/recursive_verifier`) +4. **Soroban on-chain** (vk_hash in contract state) + +### Symptoms of VK Mismatch + +- Client logs: `vk_hash mismatch: expected X, got Y — proceeding (cached)` +- Server rejects proof: `UltraHonk verification failed: vk mismatch` +- Recursive verifier panics: `Inner VK constant mismatch` +- **Security risk**: Stale PWA cache accepts forged credentials after rotation + +### Rotation Checklist (Incomplete - ISP-051 Spike Required) + +This is a **minimal warning** - full atomic rotation requires 24h spike work: + +- [ ] Compile new circuit: `nargo compile --workspace` +- [ ] Update server VK (restart required currently) +- [ ] Clear PWA cache or implement cache-busting +- [ ] Update recursive verifier VK constant (requires recompile) +- [ ] Update Soroban vk_hash (contract upgrade) +- [ ] Verify all layers accept new proofs and reject old ones + +**TODO (ISP-051):** Implement versioned VK lifecycle with: +- Manifest supersession tracking +- PWA cache version negotiation +- Hot-reload VK in server +- Fail-closed mismatch handling +- Offline grace policy with signed expiry