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:
parent
5fb28bd817
commit
e126e1d4fb
8 changed files with 126 additions and 4 deletions
|
|
@ -205,5 +205,6 @@
|
|||
"passwordUnusable": "Dieses Passwort geht nicht: Es ist leer oder enthält Zeichen, die kein Text sind.",
|
||||
"passwordInstanceUnproven": "Die Instanz konnte nicht beweisen, dass sie dein Passwort kennt, daher vertraut dieses Gerät ihr nicht. Versuch es noch einmal; passiert es wieder, ist die Instanz nicht die, die sie zu sein behauptet.",
|
||||
"passwordKeysMissing": "Die Schlüssel deines Kontos sind nicht auf diesem Gerät. Melde dich hier ab und mit deinem Passwort wieder an.",
|
||||
"passwordUnsafeCosts": "Die Instanz wollte dein Passwort auf eine Weise prüfen lassen, die kein Konto nutzt, daher wurde nichts gesendet. Passiert es wieder, ist die Instanz nicht die, die sie zu sein behauptet.",
|
||||
"passwordFailed": "Die Instanz war nicht erreichbar. Prüf die Verbindung und versuch es noch einmal."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -431,5 +431,6 @@
|
|||
"passwordUnusable": "This password can't be used: it's empty, or holds characters that aren't text.",
|
||||
"passwordInstanceUnproven": "The instance couldn't prove it knows your password, so this device trusts nothing it says. Try again; if it happens again, the instance isn't the one it claims to be.",
|
||||
"passwordKeysMissing": "Your account's keys aren't on this device. Sign out and in again with your password here.",
|
||||
"passwordUnsafeCosts": "The instance asked for your password to be checked in a way no account uses, so nothing was sent. If it happens again, the instance isn't the one it claims to be.",
|
||||
"passwordFailed": "Couldn't reach the instance. Check the connection and try again."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1333,6 +1333,12 @@ abstract class HalkyonAppLocalizations {
|
|||
/// **'Your account\'s keys aren\'t on this device. Sign out and in again with your password here.'**
|
||||
String get passwordKeysMissing;
|
||||
|
||||
/// No description provided for @passwordUnsafeCosts.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'The instance asked for your password to be checked in a way no account uses, so nothing was sent. If it happens again, the instance isn\'t the one it claims to be.'**
|
||||
String get passwordUnsafeCosts;
|
||||
|
||||
/// No description provided for @passwordFailed.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
|
|
@ -766,6 +766,10 @@ class HalkyonAppLocalizationsDe extends HalkyonAppLocalizations {
|
|||
String get passwordKeysMissing =>
|
||||
'Die Schlüssel deines Kontos sind nicht auf diesem Gerät. Melde dich hier ab und mit deinem Passwort wieder an.';
|
||||
|
||||
@override
|
||||
String get passwordUnsafeCosts =>
|
||||
'Die Instanz wollte dein Passwort auf eine Weise prüfen lassen, die kein Konto nutzt, daher wurde nichts gesendet. Passiert es wieder, ist die Instanz nicht die, die sie zu sein behauptet.';
|
||||
|
||||
@override
|
||||
String get passwordFailed =>
|
||||
'Die Instanz war nicht erreichbar. Prüf die Verbindung und versuch es noch einmal.';
|
||||
|
|
|
|||
|
|
@ -749,6 +749,10 @@ class HalkyonAppLocalizationsEn extends HalkyonAppLocalizations {
|
|||
String get passwordKeysMissing =>
|
||||
'Your account\'s keys aren\'t on this device. Sign out and in again with your password here.';
|
||||
|
||||
@override
|
||||
String get passwordUnsafeCosts =>
|
||||
'The instance asked for your password to be checked in a way no account uses, so nothing was sent. If it happens again, the instance isn\'t the one it claims to be.';
|
||||
|
||||
@override
|
||||
String get passwordFailed =>
|
||||
'Couldn\'t reach the instance. Check the connection and try again.';
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ class SodiumAccountCrypto() implements AccountCrypto {
|
|||
}) => Isolate.run(
|
||||
() => _guarded(() async {
|
||||
final sodium = await SodiumSumoInit.init();
|
||||
final kdf = PasswordKdf.fromJson(costs.toJson());
|
||||
final kdf = _kdfOf(costs);
|
||||
final unlocked = derivePasswordKeys(
|
||||
sodium,
|
||||
password: password,
|
||||
|
|
@ -250,6 +250,18 @@ class SodiumAccountCrypto() implements AccountCrypto {
|
|||
);
|
||||
}
|
||||
|
||||
/// Argon2id's [costs] as an instance named them, refused
|
||||
/// ([PasswordProblem.unsafeCosts]) outside the bounds every account is
|
||||
/// within, before anything is derived: below them guessing the password
|
||||
/// would be cheaper, above them the device would run out of memory.
|
||||
PasswordKdf _kdfOf(protocol.PasswordCosts costs) {
|
||||
try {
|
||||
return PasswordKdf.fromJson(costs.toJson());
|
||||
} on KeyRefused {
|
||||
throw const PasswordFailed(PasswordProblem.unsafeCosts);
|
||||
}
|
||||
}
|
||||
|
||||
/// What [step] gives, a password libsodium can't take ([PasswordRefused])
|
||||
/// refused as [PasswordProblem.passwordUnusable] and keys that don't
|
||||
/// rebuild as [PasswordProblem.keysMissing]: on the isolate, so the
|
||||
|
|
|
|||
|
|
@ -91,6 +91,11 @@ enum PasswordProblem() {
|
|||
/// keys don't open: nothing it said is trusted.
|
||||
instanceUnproven,
|
||||
|
||||
/// The instance named costs for the password no account may have (below
|
||||
/// the floor, which would make guessing it cheaper, or beyond what a
|
||||
/// device can spare): nothing was derived or sent.
|
||||
unsafeCosts,
|
||||
|
||||
/// The account's keys aren't on this device: sign in again here.
|
||||
keysMissing,
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ String passwordProblemText(BuildContext context, PasswordFailed failed) {
|
|||
PasswordProblem.phraseDoesNotFit => l10n.passwordPhraseDoesNotFit,
|
||||
PasswordProblem.passwordUnusable => l10n.passwordUnusable,
|
||||
PasswordProblem.instanceUnproven => l10n.passwordInstanceUnproven,
|
||||
PasswordProblem.unsafeCosts => l10n.passwordUnsafeCosts,
|
||||
PasswordProblem.keysMissing => l10n.passwordKeysMissing,
|
||||
PasswordProblem.failed => l10n.passwordFailed,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue