fix(keys): new passwords have at least 12 characters

The register can't check a password it never sees, and should its
database leak, the verifier and the keys wrapped under the password fall
to guessing at Argon2id's cost (the second reading's M8). Devices now
refuse a new password shorter than 12 characters, counted as code points
once normalised (halkyon_keys' checkNewPassword), at sign-up, a reset
keeping the keys and a change alike, before anything is derived; the new
password's field says so, and the refusal has its own text (en, de).
Signing in takes any length, so a password set before keeps working.

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 21:25:47 +02:00
commit b33b99a50a
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -259,8 +259,8 @@ void main() {
await tester.pumpAndSettle();
await tester.enterText(field('Email address'), 'anna@example.org');
await tester.enterText(field('Recovery phrase'), fakePhrase);
await tester.enterText(field('New password'), 'remembered');
await tester.enterText(field('New password again'), 'remembered');
await tester.enterText(field('New password'), 'remembered at last');
await tester.enterText(field('New password again'), 'remembered at last');
await tester.enterText(
field('Code from your authenticator app'),
fakeTotpCode,
@ -268,7 +268,7 @@ void main() {
await tapButton(tester, 'Sign in');
expect(find.text('App of ${anna.person}'), findsOneWidget);
expect(anna.password, 'remembered');
expect(anna.password, 'remembered at last');
});
/// Signs in as far as the code: the sign-in sheet, [address] and
@ -429,8 +429,8 @@ void main() {
);
await tapButton(tester, 'Continue');
await tester.enterText(field('New password'), 'a new start');
await tester.enterText(field('New password again'), 'a new start');
await tester.enterText(field('New password'), 'a new start again');
await tester.enterText(field('New password again'), 'a new start again');
await tapButton(tester, 'Continue');
expect(find.text('Your recovery phrase'), findsOneWidget);
await tapButton(tester, "I've written it down");
@ -449,7 +449,7 @@ void main() {
await tapButton(tester, "I've kept them");
expect(find.text('App of ${anna.person}'), findsOneWidget);
expect(anna.password, 'a new start');
expect(anna.password, 'a new start again');
expect(anna.passwordKeys, isNot(same(keysBefore)));
expect(anna.accountReset, isNull);
expect(anna.finished, isTrue);