fix(auth)!: a session alone never gets a code checked
The third reading found a stolen session guessing TOTP codes without end: nine wrong codes for new recovery codes, then removing an unconfirmed authenticator of its own, a success that cleared the session's count. As Proton has it, every signed-in change of how an account is secured now takes the owner's proof (OwnerProof): the password's SRP proof for a change's challenge and a TOTP or recovery code. Adding an authenticator once one is confirmed, removing a confirmed one, new recovery codes, a new phrase, a new password and deleting take it; only the first authenticator is confirmed with its own code alone, and removing an unconfirmed one takes nothing. A proof given where none is taken is refused, and a confirmed authenticator's code is never checked for a session alone, so no success passes a proof unchecked. A session's proofs count for it alone, before they are checked, cleared only by one accepted; the tenth wrong in a row ends the session. They never count toward the account's waits nor wait for them, so whoever knows the password no longer holds a signed-in owner out of changing it (the second finding), and a new password forgets those waits. Beginning a change counts nothing against the account (the third finding): 30 an hour per session, holding back that session alone. Deleting takes a TOTP code wherever an authenticator is confirmed, a code by mail where no second factor works (the fourth finding), which SignInWays now tells (secondFactorWorks). Signed-in calls no longer take the address: nothing there mails. wrongExistingCode goes, a wrong existing code being a wrong proof. The app follows in the next commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
70b434bb84
commit
a9459bb875
1 changed files with 1461 additions and 1026 deletions
|
|
@ -155,6 +155,27 @@ final class PasswordDevice {
|
|||
);
|
||||
}
|
||||
|
||||
/// The owner's proof for [challenge] (a change's): the device's answer
|
||||
/// with its password, or the [privateValue] of another, and a
|
||||
/// [totpCode] or [recoveryCode].
|
||||
OwnerProof ownerProof(
|
||||
PasswordChallenge challenge, {
|
||||
String? totpCode,
|
||||
String? recoveryCode,
|
||||
BigInt? privateValue,
|
||||
String host = testHost,
|
||||
}) {
|
||||
final reply = answer(challenge, privateValue: privateValue, host: host);
|
||||
return OwnerProof(
|
||||
challenge: challenge.challenge,
|
||||
clientValue: reply.clientValue,
|
||||
clientProof: reply.clientProof,
|
||||
host: host,
|
||||
totpCode: totpCode,
|
||||
recoveryCode: recoveryCode,
|
||||
);
|
||||
}
|
||||
|
||||
/// The signature proving the recovery phrase for [challenge] on [host]:
|
||||
/// for the new [password] it sets, or for an authenticator's reset
|
||||
/// without one; by the phrase's proof key, or [keys]' where given.
|
||||
|
|
|
|||
Loading…
Reference in a new issue