- 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>
4.3 KiB
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 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.