05/22 Enhance codes and fix bugs 2

This commit is contained in:
2026-05-22 10:46:43 -04:00
parent 3231c8eb56
commit f5dc6660c5
7 changed files with 80 additions and 29 deletions
+22 -10
View File
@@ -103,9 +103,10 @@ const Recover = (() => {
/**
* Derive an AES-256-GCM key from the recovery code using PBKDF2.
* Salt is fixed to 'passkeeper-recovery' — the recovery code itself is the secret.
* `salt` should be the user's email (per-user uniqueness).
* Falls back to the legacy fixed salt for backward compatibility.
*/
async function deriveRecoveryKey(recoveryCode) {
async function deriveRecoveryKey(recoveryCode, salt = "passkeeper-recovery") {
const baseKey = await subtle.importKey(
"raw",
strToBytes(recoveryCode),
@@ -116,7 +117,7 @@ const Recover = (() => {
return subtle.deriveKey(
{
name: "PBKDF2",
salt: strToBytes("passkeeper-recovery"),
salt: strToBytes(salt),
iterations: 200_000,
hash: "SHA-256",
},
@@ -216,15 +217,26 @@ const Recover = (() => {
}
const data = await res.json();
// Attempt to decrypt enc_key_salt using the recovery code
const recoveryKey = await deriveRecoveryKey(rawCode);
// Attempt to decrypt enc_key_salt using the recovery code.
// Try email-as-salt first (new format), fall back to the legacy fixed salt
// so that recovery codes generated before this fix still work.
let decryptedEncKeySalt;
try {
decryptedEncKeySalt = await decryptEncKeySalt(
recoveryKey,
data.recovery_enc_salt,
data.recovery_iv,
);
const keyWithEmail = await deriveRecoveryKey(rawCode, email);
try {
decryptedEncKeySalt = await decryptEncKeySalt(
keyWithEmail,
data.recovery_enc_salt,
data.recovery_iv,
);
} catch {
const keyLegacy = await deriveRecoveryKey(rawCode);
decryptedEncKeySalt = await decryptEncKeySalt(
keyLegacy,
data.recovery_enc_salt,
data.recovery_iv,
);
}
} catch {
showError(
"recover-error-1",