Skip to content

fix: upgrade encryption to AES-GCM, fix silent decrypt failures#214

Merged
d0x2f merged 4 commits into
masterfrom
worktree-bridge-cse_01Qwfsst9RP8FZGM5DvKNdbW
May 4, 2026
Merged

fix: upgrade encryption to AES-GCM, fix silent decrypt failures#214
d0x2f merged 4 commits into
masterfrom
worktree-bridge-cse_01Qwfsst9RP8FZGM5DvKNdbW

Conversation

@d0x2f
Copy link
Copy Markdown
Owner

@d0x2f d0x2f commented May 4, 2026

Summary

  • S1 (Critical): Replace crypto-js (MD5-based KDF, unauthenticated AES-CBC) with the Web Crypto API — PBKDF2 (200k iterations, SHA-256) for key derivation and AES-GCM for authenticated encryption. Existing boards encrypted with the old format are detected by their OpenSSL Salted__ prefix and continue to decrypt via a read-only legacy path; crypto-js is kept as a dependency only for this.
  • S2 (Medium): decrypt() now throws on failure instead of silently returning '?'. All six call sites updated — components abort edits gracefully, CSV export falls back to '?' per field, checkBoardPassword catches and returns false.
  • Removes IMPROVEMENTS.md now that all items (Q1–Q4, T1–T4, B1–B8, S1–S2) have been addressed.

Test plan

  • Existing Encryption.cy.js tests pass (create board with password, unlock, see plaintext)
  • New test: wrong password leaves the password wall visible
  • New test: CSV export after unlock contains decrypted card content
  • Manually verify: create a new encrypted board → cards use AES-GCM format (no U2FsdGVkX1 prefix in Firestore)
  • Manually verify: old-format boards (if any exist) still decrypt correctly

🤖 Generated with Claude Code

d0x2f and others added 4 commits May 4, 2026 22:09
…, S2)

Replace crypto-js (MD5-based KDF, unauthenticated AES-CBC) with the Web
Crypto API using PBKDF2 (200k iterations, SHA-256) and AES-GCM. Boards
encrypted with the old crypto-js format are detected by their OpenSSL
prefix and continue to decrypt via the legacy path.

Also fixes the silent-failure bug where decrypt() returned '?' on error —
it now throws, with all callers updated to handle failures explicitly.

Closes S1 and S2 from IMPROVEMENTS.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cy.readFile was picking up the previous run's file before the new
download landed. Delete the file first so the assertion waits for
a fresh write.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Stubs URL.createObjectURL to intercept the blob before it lands on
disk, eliminating the stale-file race and the date-in-filename race.
Cypress retries the wrap assertion until blob.text() resolves.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
$cards was empty when downloadCSV fired because Firestore hadn't
delivered the cards yet. Gate on the card being visible first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@d0x2f d0x2f merged commit d7eb580 into master May 4, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant