Skip to content

fix(crypto,cache,search): safety-number session invalidation, out-of-…#396

Open
devchant wants to merge 1 commit into
codebestia:mainfrom
devchant:fix/issues-359-360-357-358
Open

fix(crypto,cache,search): safety-number session invalidation, out-of-…#396
devchant wants to merge 1 commit into
codebestia:mainfrom
devchant:fix/issues-359-360-357-358

Conversation

@devchant

Copy link
Copy Markdown
Contributor

Problem Description

This PR addresses four related security and cryptographic issues across the web client:

  1. Safety-number verification UI reconciled onto the surviving route #359 (Safety-number verification UI session invalidation): Re-verification triggers (such as key changes or safety number updates) updated UI state but failed to invalidate active in-memory session keys.
  2. Out-of-order + skipped-message key handling #360 (Out-of-order & skipped-message key handling): Out-of-order message delivery could not be decrypted when messages arrived out of sequence, and skipped message key storage lacked strict capacity bounding.
  3. Local encrypted message cache (replace the plaintext IndexedDB store) #357 (Local encrypted message cache): The live search local cache (lib/search/db.ts) stored full decrypted message objects as raw plaintext in IndexedDB, while a competing module (lib/messageCache.ts) was unused.
  4. Client-side encrypted search: real decryption implementation #358 (Client-side encrypted search decryption): The search indexer relied on decryptMessageText(), an explicit stub returning ciphertext verbatim, causing raw ciphertext or undecryptable messages to leak into the index.

Implemented Solution

1. Safety-Number UI & Active Session Invalidation (#359)

  • Updated apps/web/src/app/app/conversations/[id]/page.tsx handleKeyChange() handler for key-change events (key_set_changed, key_changed, contact_key_changed, safety_number_changed, system_event) to invoke clearSessionKeys() and sessionStore.clear().
  • Added session key removal helpers to apps/web/src/lib/crypto/sessionStore.ts.

2. Double Ratchet Out-of-Order Delivery & Bounded Skipped Keys (#360)

  • Created apps/web/src/lib/crypto/doubleRatchet.ts implementing Signal Double Ratchet with symmetric and Diffie-Hellman ratcheting steps.
  • Maintained a MKSKIPPED store keyed by (DHr, N) for out-of-order message decryption.
  • Bounded skipped message key storage using MAX_SKIPPED_KEYS = 1000 and MAX_SKIP = 100, purging the oldest skipped keys when capacity is reached.

3. Encrypted-at-Rest Local IndexedDB Cache (#357)

  • Updated apps/web/src/lib/cryptoStore.ts to persist private key JWK material and added getIdentityPrivateKeyJwk().
  • Refactored apps/web/src/lib/search/db.ts to encrypt message content with AES-256-GCM using a PBKDF2 key derived (100,000 iterations, SHA-256) from non-public private key material before writing to IndexedDB.
  • Removed unused duplicate module apps/web/src/lib/messageCache.ts.

4. Real Search Index Decryption Pipeline (#358)

  • Updated apps/web/src/lib/crypto/messageCrypto.ts decryptMessageText() to invoke the real envelope decryption pipeline (decryptAndVerifyEnvelope()).
  • Returns null when decryption fails so undecryptable messages are excluded from search indexing instead of indexing raw ciphertext noise.
  • Updated useMessageSearchIndex hook to handle decryption parameters and filter out plaintext === null messages.

Files and Components Changed

  • apps/web/src/app/app/conversations/[id]/page.tsx
  • apps/web/src/lib/crypto/sessionStore.ts
  • apps/web/src/lib/crypto/doubleRatchet.ts
  • apps/web/src/lib/cryptoStore.ts
  • apps/web/src/lib/search/db.ts
  • apps/web/src/lib/messageCache.ts (Deleted)
  • apps/web/src/lib/crypto/messageCrypto.ts
  • apps/web/src/hooks/useMessageSearchIndex.ts
  • Test suites: doubleRatchet.test.ts, db.test.ts, searchIntegration.test.ts, safetyNumber.test.ts, ProposalCard.test.tsx

Technical Decisions Made

  • Derived cache key via PBKDF2 with SHA-256 from identity private key material rather than public key alone to ensure at-rest confidentiality against unauthorized origin scripts.
  • Encrypted message payloads prior to opening IndexedDB transactions to prevent IndexedDB transaction auto-commit timeouts during async WebCrypto operations.
  • Pruned oldest skipped message keys FIFO when MKSKIPPED exceeds MAX_SKIPPED_KEYS = 1000 to bound memory consumption.

Validation Performed

  • Unit Tests: Ran npx vitest run — 16/16 tests passed across 5 test suites.
  • Type Checking: Ran npx tsc --noEmit — 0 errors.
  • Production Build: Ran npm run build — Next.js production build succeeded.

Closes #359
Closes #360
Closes #357
Closes #358

@drips-wave

drips-wave Bot commented Jul 26, 2026

Copy link
Copy Markdown

@devchant Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant