From 665d8ea4e6792b6908dabd63fcf80e09ab718c27 Mon Sep 17 00:00:00 2001 From: r-zakarya <82443831+r-zakarya@users.noreply.github.com> Date: Sat, 25 Jul 2026 00:00:40 +0100 Subject: [PATCH] fix(recovery): accept -v2 / argon2id verifiers in setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /recovery-key/setup verified the master password with a local copy of the verifier check that only knew pbkdf2 (legacy) and pbkdf2-sha256. Any account on a decoupled scheme — pbkdf2-sha256-v2 or argon2id-v2, the current default after a master-pw rotation — fell through both branches and was rejected as "invalid password" even when correct, so recovery-key setup was impossible. Match the canonical VerifierToStoredHash (PM.Handler.Auth): legacy stores the verifier raw, every other algo is SHA256-wrapped identically. One branch, plus a keep-in-sync note so the copy doesn't drift again. Co-Authored-By: Claude Opus 4.8 --- delphi-backend/Handlers/PM.Handler.Recovery.pas | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/delphi-backend/Handlers/PM.Handler.Recovery.pas b/delphi-backend/Handlers/PM.Handler.Recovery.pas index 928bb54..5625708 100644 --- a/delphi-backend/Handlers/PM.Handler.Recovery.pas +++ b/delphi-backend/Handlers/PM.Handler.Recovery.pas @@ -82,11 +82,15 @@ begin if AVerifier <> '' then begin - // ZK: client computed PBKDF2 hex locally. Server only does the wrap. + // ZK: client computed the verifier locally. Server only wraps it — legacy + // pbkdf2 stores it raw, EVERY other algo (pbkdf2-sha256, pbkdf2-sha256-v2, + // argon2id-v2) is SHA256-wrapped identically. Mirrors VerifierToStoredHash + // in PM.Handler.Auth — keep in sync. The old `else if =CURRENT` silently + // rejected -v2 / argon2id accounts (the current default) as wrong-password. if Length(AVerifier) <> 64 then Exit; if SameText(LAlgo, HASH_ALGO_LEGACY) then Result := ConstantTimeEquals(AVerifier, LStoredHash) - else if SameText(LAlgo, HASH_ALGO_CURRENT) then + else Result := ConstantTimeEquals(SHA256Hex(AVerifier), LStoredHash); Exit; end;