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

4.3 KiB

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.