Merge pull request #15 from mrteye/feat-recovery-sss-matrix-task-4186155517714950179

Add task file for 2-of-3 SSS Wasm recovery matrix
This commit is contained in:
Tyler Gillispie 2026-08-23 22:52:36 -07:00 committed by GitHub
commit 792ed141d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -0,0 +1,43 @@
# TASK METADATA
- **Target Files:** `ui/components/RecoveryPage.tsx`, `server/main.ts`, `server/recovery.ts` (new), `wasm/sss_recovery/` (new Rust module)
- **Core Objective:** Implement constant-time 2-of-3 Shamir's Secret Sharing (SSS) key splitting and reconstruction in WebAssembly/Rust for the client-side zero-downgrade recovery portal, with mandatory in-place memory zeroization.
- **Dependencies:** Deno WebCrypto API, SimpleWebAuthn (client & server), Rust/Wasm toolchain (`wasm-pack`), IndexedDB.
- **Additional Important Notes:** Share choreography uses a Device Share (IndexedDB via WebAuthn PRF), Hot Server Share (PostgreSQL via PIN), and Cold Voucher (BIP-39 mnemonic). The execution sandbox must use Web Workers or strict in-memory client modules with mandatory `Uint8Array.fill(0)` zeroization; isolated iframes are rejected to prevent `postMessage` memory leakage.
---
## Architectural Considerations & Risks
- **Risks:**
- **Garbage Collection Leaks:** Transferring ArrayBuffers between JavaScript and Wasm can leave un-zeroed memory in V8. Strict lifecycle management and immediate `Uint8Array.fill(0)` on all JS-side buffers is mandatory before losing references.
- **Side-Channel Attacks:** Polynomial interpolation in Rust over GF(256) must be constant-time to avoid timing attacks when processing recovery shares.
- **WebAuthn PRF Extension Support:** The Device Share in IndexedDB relies on the WebAuthn PRF extension. A fallback or clear UX flow must be designed if the user's authenticator lacks PRF support.
- **Brute-Forcing Server Share:** The Hot Server Share is gated by a recovery PIN/code. Robust rate-limiting on the `/api/recovery/challenge` endpoint is critical to prevent brute-forcing the server share.
- **Alternatives:**
- **Execution Context:** We explicitly rejected using an isolated sandbox iframe. `postMessage` serializes data, creating uncontrollable memory copies in the DOM that cannot be deterministically zeroed. We will use a Web Worker or direct WebAssembly instantiation in the main thread with explicit TypedArray zeroization.
- **Implementation Language:** Pure TypeScript SSS was rejected due to lack of constant-time execution guarantees and poor low-level memory control compared to Rust/Wasm.
## Proposed Implementation
### Phase 1: Wasm Core Engine (Rust)
1. Scaffold a new Rust crate (e.g., `wasm/sss_recovery`) compiling to `wasm32-unknown-unknown`.
2. Implement a constant-time 2-of-3 Shamir's Secret Sharing reconstruction algorithm over GF(256).
3. Expose FFI boundaries that accept two share buffers and output the reconstructed master secret.
4. Utilize `zeroize` crate in Rust to ensure Wasm linear memory is purged of intermediate polynomial data before returning control to JavaScript.
### Phase 2: Client-Side Choreography (`ui/components/RecoveryPage.tsx`)
1. Implement the UI flow for the two recovery scenarios:
- **Scenario A (Lost Key):** Fetch Device Share (IndexedDB + WebAuthn PRF) + Server Share (via PIN).
- **Scenario B (Lost Device):** Prompt for Cold Voucher (12-word BIP-39) + Server Share (via PIN).
2. Instantiate the Wasm SSS module.
3. Pass the two gathered shares to the Wasm module to reconstruct the master secret.
4. Import the reconstructed master secret directly into WebCrypto as an `extractable: false` `CryptoKey`.
5. **Memory Purge:** Immediately execute `Uint8Array.fill(0)` on the share inputs, intermediate buffers, and the raw reconstructed byte array.
6. Use the WebCrypto key to derive the ephemeral recovery token and sign the challenge for the new passkey registration.
### Phase 3: Server-Side Share Gating (`server/main.ts`, `server/recovery.ts`)
1. Implement backend storage for the Hot Server Share within the `recovery_shares` table (or similar schema extension).
2. Update `/api/recovery/challenge` to validate the recovery PIN and release the Hot Server Share only upon success, enforcing strict rate-limiting.
3. Update `/api/recovery/verify` to validate the ephemeral token signature derived from the reconstructed master secret.
4. Complete the recovery cycle by binding the new WebAuthn passkey, revoking the old credentials, and generating a new 2-of-3 share matrix for the new passkey.