The screens and the rules behind the choice. The storage and the
migration landed in the previous commit.
The state machine lives in the ViewModel and only a verified outcome
advances it. That is not tidiness: the old screen advanced its own step in
the same breath as asking, without waiting for the answer, and any four
digits reached "choose a PIN". Keeping the stage where the verification
happens removes the class of bug rather than one instance of it.
Three rules, each with a test:
Any change authenticates with the method that is on NOW -- otherwise
whoever is holding the unlocked phone simply switches the lock off, which
is the adversary SECURITY.md names first. Turning it on from nothing needs
no proof, because there is nothing yet to prove.
A fingerprint is never turned on without one working on this phone.
Consent, then a successful scan, and only then is anything written. A
cancelled prompt after a PIN has been committed leaves a working PIN lock
rather than nothing, and says so.
Moving to fingerprint-only drops the PIN only once a scan has worked, and
then drops it properly -- the verifier and the Keystore key both go, so a
stale secret is not left in the file.
The lock screen in fingerprint-only mode is one line, one button and a
notice, with no PIN field: a disabled text box on a screen that will never
accept a PIN is an invitation to try. An unavailable sensor there must NOT
switch the lock off -- that would let anyone who knows the phone's own PIN
remove the enrolled fingerprints and walk in -- so the notice names the way
back instead: enrol again, or erase.
All of it says what it means. The PIN option states that it belongs to
this app and not the phone, because somebody who assumes otherwise will
assume they can reset it the way they reset a phone PIN. The fingerprint
option says who it lets in: anyone enrolled on this phone, now or later.
LockSettingsCopyTest holds both of those, and holds the whole surface to
the same no-sensitive-words, no-"secure" rules the lock screen has.
Proved: a failed scan committing nothing reddens exactly one test.
Removing the authenticate-first rule or the scan-before-commit rule
reddens several, which is that rule's whole surface rather than a
coincidence.
Part of #63
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>