4.1 KiB
4.1 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 preventpostMessagememory 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/challengeendpoint is critical to prevent brute-forcing the server share.
- Garbage Collection Leaks: Transferring ArrayBuffers between JavaScript and Wasm can leave un-zeroed memory in V8. Strict lifecycle management and immediate
- Alternatives:
- Execution Context: We explicitly rejected using an isolated sandbox iframe.
postMessageserializes 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.
- Execution Context: We explicitly rejected using an isolated sandbox iframe.
Proposed Implementation
Phase 1: Wasm Core Engine (Rust)
- Scaffold a new Rust crate (e.g.,
wasm/sss_recovery) compiling towasm32-unknown-unknown. - Implement a constant-time 2-of-3 Shamir's Secret Sharing reconstruction algorithm over GF(256).
- Expose FFI boundaries that accept two share buffers and output the reconstructed master secret.
- Utilize
zeroizecrate 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)
- 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).
- Instantiate the Wasm SSS module.
- Pass the two gathered shares to the Wasm module to reconstruct the master secret.
- Import the reconstructed master secret directly into WebCrypto as an
extractable: falseCryptoKey. - Memory Purge: Immediately execute
Uint8Array.fill(0)on the share inputs, intermediate buffers, and the raw reconstructed byte array. - 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)
- Implement backend storage for the Hot Server Share within the
recovery_sharestable (or similar schema extension). - Update
/api/recovery/challengeto validate the recovery PIN and release the Hot Server Share only upon success, enforcing strict rate-limiting. - Update
/api/recovery/verifyto validate the ephemeral token signature derived from the reconstructed master secret. - 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.