fix(keys): a device refuses Argon2id costs beyond the register's ceiling

PasswordKdf.fromJson refused costs below the floor but took any above it,
so an instance could ask a device for 100 GiB and crash the app at every
sign-in. It now refuses more than the register's own ceiling (16 passes,
1 GiB; KeyRefusal.excessivePasswordKdf), and the app reads either refusal,
before deriving or sending anything, as PasswordProblem.unsafeCosts with
a text of its own (before, costs below the floor read as keys missing).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-20 20:04:37 +02:00
commit e126e1d4fb
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -207,7 +207,14 @@ released, stays at 1), after the second reading.
part (`AccountSecuritySection`): setting up a password (anonymous people, GitHub's, an
account with only an address), finishing the setup, the authenticators (adding one
takes a code from one before), new recovery codes, changing the password, a new
phrase; GitHub's end date.
phrase (with the password and a code); GitHub's end date.
- **The device holds the costs to the register's bounds:** Argon2id's costs an instance
names (as it begins a sign-in or a change) are refused below the floor (3 passes,
64 MiB: guessing would be cheaper) and above the ceiling (16 passes, 1 GiB, the
register's own: a hostile instance could ask for any memory and crash the app), before
anything is derived or sent (`PasswordKdf.maximum`, `KeyRefusal.excessivePasswordKdf`;
the app's `unsafeCosts`, found at step 3's review, 20 Oct). The two bounds move
together with the register's.
- **The keys on the device:** unlocked after signing in, kept in the system's keyring
per instance and person (flutter_secure_storage), gone once another person signs in or
nobody, and with the instance's removal from the device. Anonymous people's device keys