Public Access
Give the phone a way to enrol the fingerprint it already unlocks with
The Android device key store, the biometric gate and the lock screen's UNLOCK WITH FINGERPRINT button have all shipped since this head was written, and none of them could ever run: that button appears only when a device key exists, and nothing on the phone could create one. `CanUnlockWithDevice` was false on every launch of every phone. This is the missing half. **The offer is on PREFERENCES**, which held a PendingScreen until it had a setting on it. It is there rather than beside the button it turns on because registering needs an unlocked keychain and a reachable server — the vault has to be open to seal the bundle, and the wrap has to reach the account or a phone somebody has lost could never be revoked. Neither is true on the lock screen. One card, and exactly one of its three blocks is ever drawn: the offer, the withdrawal, or the sentence saying this phone has nowhere to keep a key. That is `CanRegisterDevice` / `CanForgetDevice` / `HasNoDeviceKeyOption`, which are two flags and not one and its negation for the reason written where they are set — a phone with no screen lock and a phone already registered are both "cannot register", and only the second has anything to take back. The withdrawal has no confirmation, deliberately, and the sentence above it carries what the desktop puts in a tooltip this head has no room for. `StatusMessage` is on the screen because it is the only feedback this head has once the system's own dialogue has gone. **Two things would have been wrong in the feature the moment it worked.** `Environment.MachineName` answers `localhost` on Android, and registering names the device — so every phone would have arrived in the account's device list as another identical row, on the very screen a lost handset is revoked from. `PhoneEnvironment.DeviceName` was already written and never called; the shell now takes it as an optional constructor argument that the desktop does not pass, and it reaches enrollment, registration and every connection log entry. That was gap §7 of docs/android-port.md, and it is now closed. And the status line said "Waiting for Windows…" over an Android biometric prompt. `GestureWait` picks the sentence from the platform rather than from a head, unlike the device name beside it: a device name is a fact about one handset only the head can read, and which dialogue appears is a fact about the operating system this assembly is running on. Two tests cover the seam — the injected name reaching the account, and the default still being this machine's own name — and `FakeVaultServer` records what each device called itself, because the name is the only part of a registration a person ever reads. The gesture itself is unreachable from any test process, so Phase 13 of docs/manual-checks.md carries five checks, including that enrolling a new fingerprint in Android's own Settings destroys the key. That one is the property that makes this a fast path rather than a weakening of the passphrase.
This commit is contained in:
@@ -1166,3 +1166,71 @@ unchanged and there is nowhere to change it. Nothing claims to know *when* it wa
|
||||
**Failure means:** a rename that moved the slug could take one an archived team is still holding, and that
|
||||
archived team could then never be brought back. An "edited" timestamp anywhere on the screen is invented
|
||||
data — `team` has no updated-at column, so there is nothing behind it.
|
||||
|
||||
---
|
||||
|
||||
## Phase 13 — Unlocking the phone with a fingerprint
|
||||
|
||||
Every check here needs a real Android device or emulator with a screen lock and a fingerprint enrolled on
|
||||
the phone itself, and none has a headless equivalent: the whole feature is a keystore key the platform will
|
||||
not release without a gesture, and there is no gesture in a test process. What *is* covered automatically is
|
||||
the shell's half — `ShellFlowTests` registers, relaunches, unlocks and withdraws against a fake keystore, so
|
||||
what is left here is the platform half plus the one thing only a person can see, which is which dialogue
|
||||
comes up.
|
||||
|
||||
### 13.1 The offer is on PREFERENCES, and only when there is something to offer
|
||||
|
||||
Unlock the keychain, go to SETTINGS → Preferences on a phone with a screen lock.
|
||||
|
||||
**Pass:** REGISTER THIS PHONE is there under THIS PHONE. On a phone with **no** screen lock at all, neither
|
||||
button is drawn and the paragraph saying this phone has nowhere to keep a device key is.
|
||||
|
||||
**Failure means:** if the button is drawn on a phone with no screen lock, `AndroidDeviceKeyStore`
|
||||
`IsAvailableAsync` is no longer asking the keyguard — and registering there would put a wrap on the account
|
||||
that nothing can ever open, on a phone whose key no gesture can release.
|
||||
|
||||
### 13.2 Registering asks for the fingerprint, and says which phone it registered
|
||||
|
||||
Press REGISTER THIS PHONE while signed in.
|
||||
|
||||
**Pass:** the system's own biometric prompt appears, titled "Register this phone". Confirm it, and the status
|
||||
line names **this phone** — the name from Android's Settings, or the model — rather than `localhost`. The
|
||||
button is replaced by STOP UNLOCKING HERE. Cancel the prompt instead and nothing changes but the message.
|
||||
|
||||
**Failure means:** a status line reading `localhost` means the head is no longer passing
|
||||
`PhoneEnvironment.DeviceName`, and the account's device list is about to fill with rows nobody can tell
|
||||
apart. No prompt at all means the cipher is not being bound to it — see `BiometricGate`, where binding is
|
||||
the entire point.
|
||||
|
||||
### 13.3 The lock screen then opens without the passphrase
|
||||
|
||||
Lock the keychain, close the app, and launch it again.
|
||||
|
||||
**Pass:** the prompt is raised on arrival, and confirming it opens the keychain with nothing typed. UNLOCK
|
||||
WITH FINGERPRINT is on the screen behind it. Lock from inside the running app instead and **no** prompt is
|
||||
raised — that rule is deliberate; see `PhoneShell.TryOfferDeviceUnlock`.
|
||||
|
||||
**Failure means:** a button that is absent after a successful registration is the wrap not reaching the local
|
||||
cache. A prompt raised after an in-app lock trains the reflex of authenticating at a prompt nobody asked for.
|
||||
|
||||
### 13.4 Enrolling a new fingerprint on the phone destroys the key · **the security property**
|
||||
|
||||
With DodoSSH registered, add another fingerprint in Android's own Settings. Then launch DodoSSH.
|
||||
|
||||
**Pass:** no fingerprint button, and the passphrase opens the vault as it always did. Registering again from
|
||||
PREFERENCES restores it.
|
||||
|
||||
**Failure means:** `setInvalidatedByBiometricEnrollment` has been dropped, and anybody who can add their own
|
||||
fingerprint to an unlocked phone has inherited the vault. This is the check that says the phone's fast path
|
||||
is not a downgrade of the passphrase.
|
||||
|
||||
### 13.5 Withdrawing stops this phone, and clears the account
|
||||
|
||||
Press STOP UNLOCKING HERE.
|
||||
|
||||
**Pass:** it goes back to offering REGISTER, a relaunch asks for the passphrase, and the device is gone from
|
||||
the account — check from the desktop head, or by registering the same phone again and seeing one device
|
||||
rather than two. There is no confirmation prompt, deliberately.
|
||||
|
||||
**Failure means:** a phone that still unlocks itself after this is the local half not happening, which is the
|
||||
half that matters when the handset is the thing that was lost.
|
||||
|
||||
Reference in New Issue
Block a user