From 40ddaa2c92e18b29283c1a8d6eb277b79f5d44cf Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sat, 11 Jul 2026 20:16:14 +0200 Subject: [PATCH] fix(e2ee): stop reusing the AES-GCM nonce in setup; drop redundant key_check oracle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setup() encrypted BOTH the wrapped private key AND the key_check under the same wrapKey with the SAME wrapIv — a GCM nonce-reuse bug (leaks keystream + enables GHASH/tag forgery, which matters under E2EE's malicious-server threat model). Fixes: - setup() gives key_check its own independent nonce (checkIv). - unlock() no longer decrypts key_check to verify the passphrase — that was redundant (a wrong passphrase already fails the wrapped_private_key GCM auth tag) AND was the second consumer of the reused nonce. Backward-compatible: the uploaded bundle shape is unchanged, and existing users' bundles still unlock (unlock only presence-checks key_check now; the private-key decrypt path with wrap_iv is untouched). Also removed the unused, architecturally ineffective lock() export (re-unlocks from IndexedDB on next mount; the meaningful primitive is clearDevice). Re-review clean; tsc green. --- frontend/src/lib/e2ee.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/frontend/src/lib/e2ee.ts b/frontend/src/lib/e2ee.ts index 763858a..f0d5c0f 100644 --- a/frontend/src/lib/e2ee.ts +++ b/frontend/src/lib/e2ee.ts @@ -46,12 +46,6 @@ export function isUnlocked(): boolean { return !!privKey; } -export function lock(): void { - privKey = null; - convKeys.clear(); - emitUnlock(); -} - // --- IndexedDB (per-device persistence of the unlocked private key) --- const DB_NAME = "siftlode-e2ee"; const STORE = "privkeys"; @@ -127,7 +121,11 @@ export async function setup(userId: number, passphrase: string): Promise