|
129 | 129 | | **Mitigation** | All inline handlers were removed; forms now use `addEventListener` from a single `<script>` block. CSP is enforced. | |
130 | 130 | | **Test status** | Live `curl … | grep onsubmit=` returns 0 in CI. Could be promoted to a real test. | |
131 | 131 |
|
132 | | -<!-- ─── W3: QR-proof pairing (Android phone → desktop) ─────────────── --> |
133 | | - |
134 | 132 | ### A-11 — Pairing-nonce replay across two desktop sessions |
135 | 133 |
|
136 | 134 | | | | |
137 | 135 | |---|---| |
138 | 136 | | **Class** | Spoofing (STRIDE: S) | |
139 | 137 | | **Surface** | `POST /v1/proof-pairing/sessions/:id/submit` | |
140 | 138 | | **Description** | A passively-recorded `{proof, publicSignals, did}` from session S1 is replayed against a freshly-issued session S2 whose nonce the attacker observes (shoulder-surf of the desktop QR). Without an explicit nonce ↔ proof binding the same proof verifies for any session because `identityBinding = Poseidon(2)([biometricSecret, didHash])` is per-user, not per-session. | |
141 | | -| **Mitigation** | [ADR-0009](../adr/0009-qr-proof-pairing-protocol.md) Option B′: phone computes `didHashSession = Poseidon(2)([didHash, sessionNonce])` and uses it as the circuit's `didHash` input. Server re-derives `Poseidon(2)([user.didHash, session.nonce])` from its own records and `crypto.timingSafeEqual`-compares to `publicSignals[1]`. Mismatch → 400 `pairing_nonce_mismatch`. | |
| 139 | +| **Mitigation** | [ADR-0009](https://github.com/zeroauth-dev/ZeroAuth/blob/main/adr/0009-qr-proof-pairing-protocol.md) Option B′: phone computes `didHashSession = Poseidon(2)([didHash, sessionNonce])` and uses it as the circuit's `didHash` input. Server re-derives `Poseidon(2)([user.didHash, session.nonce])` from its own records and `crypto.timingSafeEqual`-compares to `publicSignals[1]`. Mismatch → 400 `pairing_nonce_mismatch`. | |
142 | 140 | | **Test status** | **Required before merge.** `tests/proof-pairing.test.ts` cases: (i) valid first submit succeeds; (ii) replay against same session returns 409; (iii) replay against fresh session with different nonce returns 400 with `pairing_nonce_mismatch`. | |
143 | 141 | | **Audit signal** | `audit_events.action = 'pairing.replay_blocked'` with `metadata.session_id`. | |
144 | 142 |
|
|
204 | 202 | | **Class** | Tampering (STRIDE: T) | |
205 | 203 | | **Surface** | The Android app's WebView running snarkjs | |
206 | 204 | | **Description** | If snarkjs is loaded over HTTPS at runtime, a CDN/network compromise swaps it for a build that exfiltrates `biometricSecret` (the only private witness) or produces proofs against attacker-chosen commitments. Same class as `event-stream` / `ua-parser-js`. | |
207 | | -| **Mitigation** | [ADR-0010](../adr/0010-android-webview-snarkjs-bundling.md): snarkjs is bundled in the APK at `android/app/src/main/assets/prover/`, SHA-256 pinned in the ADR, build fails on mismatch. WebView CSP: `default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'none'`. WebView in a separate renderer process. No `file://`, no `content://`, no DOM storage. Play Integrity verdict travels in `clientMeta.playIntegrityVerdict` for W4 server-side enforcement. | |
| 205 | +| **Mitigation** | [ADR-0010](https://github.com/zeroauth-dev/ZeroAuth/blob/main/adr/0010-android-webview-snarkjs-bundling.md): snarkjs is bundled in the APK at `android/app/src/main/assets/prover/`, SHA-256 pinned in the ADR, build fails on mismatch. WebView CSP: `default-src 'none'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'none'`. WebView in a separate renderer process. No `file://`, no `content://`, no DOM storage. Play Integrity verdict travels in `clientMeta.playIntegrityVerdict` for W4 server-side enforcement. | |
208 | 206 | | **Test status** | **Required before Android app merge.** CI step diffs `assets/prover/*.sha256` against the ADR-pinned table; build fails on mismatch. | |
209 | 207 | | **Audit signal** | If `clientMeta.snarkjsHash` ever travels in the submit, log mismatches as `audit_events.action = 'pairing.unexpected_prover_hash'`. | |
210 | 208 |
|
|
0 commit comments