feat: at-rest encryption for all sensitive data + vault unlock flow

All sensitive data (identity, mappings, activity log) is now AES-256
encrypted in browser.storage.local when sync encryption is enabled.
TOTP secret is also encrypted at rest using the derived key.

Vault unlock flow:
- On browser restart, extension detects locked state (encrypted data,
  no cached CryptoKey) and shows LOCK badge in red
- Popup shows a full-screen unlock prompt with password field,
  optional TOTP, and biometric button
- After unlock, background decrypts and broadcasts data to all tabs
- Content scripts start with empty config when locked; receive
  decrypted config via vault:unlocked message after unlock
- Injector skips encrypted blobs in storage change events

Storage module changes:
- _readSecure / _writeSecure transparently encrypt/decrypt
- encryptExistingData() migrates plaintext → encrypted on setup
- decryptAllData() restores plaintext when encryption is disabled
- isLocked() checks for encrypted data + missing key

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
This commit is contained in:
Claude
2026-03-26 19:03:54 +00:00
parent 39e609a14e
commit a22ba549a2
7 changed files with 418 additions and 41 deletions
+3 -2
View File
@@ -1002,7 +1002,8 @@ async function showSyncAuthPrompt(mode = 'decrypt') {
}
// First-device: show TOTP alongside password if configured
if (!isReverify && config?.totpSecret) {
const hasTOTP = config?.totpSecret || config?._totpEncrypted;
if (!isReverify && hasTOTP) {
$('#syncAuthTOTPForPassword').style.display = '';
} else {
$('#syncAuthTOTPForPassword').style.display = 'none';
@@ -1023,7 +1024,7 @@ async function showSyncAuthPrompt(mode = 'decrypt') {
// TOTP re-verify option
const totpGroup = $('#totpReverifyGroup');
if (config?.totpSecret) {
if (hasTOTP) {
totpGroup.style.display = 'flex';
} else {
totpGroup.style.display = 'none';