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,
|
LUser, LCode, LCodeHash, LIP, LStoredHash, LKdfSalt, LWrappedKey, LWrappedIv,
|
||||||
LSalt, LToken, LCSRF, LAlgo: string;
|
LSalt, LToken, LCSRF, LAlgo: string;
|
||||||
LUserId, LKdfIters, LCurrentUses, LNewUses: Integer;
|
LUserId, LKdfIters, LCurrentUses, LNewUses: Integer;
|
||||||
|
LArgM, LArgT, LArgP: Integer;
|
||||||
|
LArg: TJSONObject;
|
||||||
LQ: TFDQuery;
|
LQ: TFDQuery;
|
||||||
begin
|
begin
|
||||||
LIP := GetClientIP(ARequest);
|
LIP := GetClientIP(ARequest);
|
||||||
@@ -314,6 +316,7 @@ begin
|
|||||||
// Join to users to look up by username + verify the code in one shot.
|
// Join to users to look up by username + verify the code in one shot.
|
||||||
LQ.SQL.Text :=
|
LQ.SQL.Text :=
|
||||||
'SELECT u.id, u.salt, u.kdf_iterations, u.hash_algo, ' +
|
'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 ' +
|
' rk.code_hash, rk.kdf_salt, rk.wrapped_key, rk.wrapped_iv, rk.remaining_uses ' +
|
||||||
'FROM users u ' +
|
'FROM users u ' +
|
||||||
'LEFT JOIN recovery_keys rk ON rk.user_id = u.id ' +
|
'LEFT JOIN recovery_keys rk ON rk.user_id = u.id ' +
|
||||||
@@ -331,6 +334,9 @@ begin
|
|||||||
LSalt := LQ.FieldByName('salt').AsString;
|
LSalt := LQ.FieldByName('salt').AsString;
|
||||||
LKdfIters := LQ.FieldByName('kdf_iterations').AsInteger;
|
LKdfIters := LQ.FieldByName('kdf_iterations').AsInteger;
|
||||||
LAlgo := LQ.FieldByName('hash_algo').AsString;
|
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;
|
LStoredHash := LQ.FieldByName('code_hash').AsString;
|
||||||
LKdfSalt := LQ.FieldByName('kdf_salt').AsString;
|
LKdfSalt := LQ.FieldByName('kdf_salt').AsString;
|
||||||
LWrappedKey := LQ.FieldByName('wrapped_key').AsString;
|
LWrappedKey := LQ.FieldByName('wrapped_key').AsString;
|
||||||
@@ -407,6 +413,18 @@ begin
|
|||||||
LObj.AddPair('salt', LSalt);
|
LObj.AddPair('salt', LSalt);
|
||||||
LObj.AddPair('kdfIterations', TJSONNumber.Create(LKdfIters));
|
LObj.AddPair('kdfIterations', TJSONNumber.Create(LKdfIters));
|
||||||
LObj.AddPair('hashAlgo', LAlgo);
|
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('wrappedKey', LWrappedKey);
|
||||||
LObj.AddPair('wrappedIv', LWrappedIv);
|
LObj.AddPair('wrappedIv', LWrappedIv);
|
||||||
LObj.AddPair('kdfSalt', LKdfSalt);
|
LObj.AddPair('kdfSalt', LKdfSalt);
|
||||||
|
|||||||
Reference in New Issue
Block a user