fix(recovery): echo Argon2 params in the redeem response
The redeem client already reads `state.argon2Params = r.argon2 || null`, but the server never sent `argon2`, so it always fell back to null → the default params. Correct today (every account uses ARGON2_DEFAULT_PARAMS), but a latent wrong-key derivation the day per-account params diverge: same class as the setup verifier bug — an Argon2 account derived under the wrong params. Echo m/t/p from the users row like /login/challenge does; omitted for non-Argon2 accounts (m=0). No client change — it already consumes the field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -279,6 +279,8 @@ var
|
||||
LUser, LCode, LCodeHash, LIP, LStoredHash, LKdfSalt, LWrappedKey, LWrappedIv,
|
||||
LSalt, LToken, LCSRF, LAlgo: string;
|
||||
LUserId, LKdfIters, LCurrentUses, LNewUses: Integer;
|
||||
LArgM, LArgT, LArgP: Integer;
|
||||
LArg: TJSONObject;
|
||||
LQ: TFDQuery;
|
||||
begin
|
||||
LIP := GetClientIP(ARequest);
|
||||
@@ -314,6 +316,7 @@ begin
|
||||
// Join to users to look up by username + verify the code in one shot.
|
||||
LQ.SQL.Text :=
|
||||
'SELECT u.id, u.salt, u.kdf_iterations, u.hash_algo, ' +
|
||||
' u.argon2_m, u.argon2_t, u.argon2_p, ' +
|
||||
' rk.code_hash, rk.kdf_salt, rk.wrapped_key, rk.wrapped_iv, rk.remaining_uses ' +
|
||||
'FROM users u ' +
|
||||
'LEFT JOIN recovery_keys rk ON rk.user_id = u.id ' +
|
||||
@@ -331,6 +334,9 @@ begin
|
||||
LSalt := LQ.FieldByName('salt').AsString;
|
||||
LKdfIters := LQ.FieldByName('kdf_iterations').AsInteger;
|
||||
LAlgo := LQ.FieldByName('hash_algo').AsString;
|
||||
LArgM := LQ.FieldByName('argon2_m').AsInteger;
|
||||
LArgT := LQ.FieldByName('argon2_t').AsInteger;
|
||||
LArgP := LQ.FieldByName('argon2_p').AsInteger;
|
||||
LStoredHash := LQ.FieldByName('code_hash').AsString;
|
||||
LKdfSalt := LQ.FieldByName('kdf_salt').AsString;
|
||||
LWrappedKey := LQ.FieldByName('wrapped_key').AsString;
|
||||
@@ -407,6 +413,18 @@ begin
|
||||
LObj.AddPair('salt', LSalt);
|
||||
LObj.AddPair('kdfIterations', TJSONNumber.Create(LKdfIters));
|
||||
LObj.AddPair('hashAlgo', LAlgo);
|
||||
// Echo the Argon2 params (as /login/challenge does) so the client derives
|
||||
// under the account's real params, not the default fallback. Harmless today
|
||||
// (all accounts use the default) but avoids a silent wrong-key derivation if
|
||||
// per-account params are ever tuned. Omitted for non-Argon2 accounts (m=0).
|
||||
if LArgM > 0 then
|
||||
begin
|
||||
LArg := TJSONObject.Create;
|
||||
LArg.AddPair('m', TJSONNumber.Create(LArgM));
|
||||
LArg.AddPair('t', TJSONNumber.Create(LArgT));
|
||||
LArg.AddPair('p', TJSONNumber.Create(LArgP));
|
||||
LObj.AddPair('argon2', LArg);
|
||||
end;
|
||||
LObj.AddPair('wrappedKey', LWrappedKey);
|
||||
LObj.AddPair('wrappedIv', LWrappedIv);
|
||||
LObj.AddPair('kdfSalt', LKdfSalt);
|
||||
|
||||
Reference in New Issue
Block a user