auth-yes/tasks/complete/2026-0824.02.jul.feat.recovery.sss-wasm-matrix-1200.md
google-labs-jules[bot] f63d0c9afe feat: implement 2-of-3 SSS recovery matrix using Wasm/Rust
- Scaffolds a new Rust crate `wasm/sss_recovery` for constant-time Shamir's Secret Sharing over GF(256) with strict Wasm `zeroize`
- Implements purely typed BIP-39 fallback mapped via Deno WebCrypto in `ui/utils/bip39.ts`
- Migrates `server/recovery.ts` logic mapping Device/Voucher + Server shares with Valkey rate-limiting
- Applies mandatory in-memory JS zeroization on all reconstructed buffers

Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
2026-08-24 07:49:34 +00:00

85 lines
4.3 KiB
Markdown

# 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.