diff --git a/tasks/new/2026-0824.02.jul.feat.recovery.sss-wasm-matrix-1200.md b/tasks/new/2026-0824.02.jul.feat.recovery.sss-wasm-matrix-1200.md new file mode 100644 index 0000000..521dbdc --- /dev/null +++ b/tasks/new/2026-0824.02.jul.feat.recovery.sss-wasm-matrix-1200.md @@ -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.