Integrate Android biometrics with the shared Rust lease (#98)
This commit is contained in:
1 parent
5c933a99a3
commit
647dc1bb4b
21 files changed
+1473
-118
No files matched your search
@@ -0,0 +1,77 @@
|
||||
# Android biometric authentication (#98)
|
||||
|
||||
Passwords and Preferences use one core facade over the private runtime's shared
|
||||
Rust authentication owner. Rust validates the key, chooses protected operations,
|
||||
owns the lease generation and inactivity deadline, and revokes native entry and
|
||||
editor handles. Kotlin publishes that typed state and reports actual user
|
||||
interaction; polling never extends the lease. Moving between tabs or browsing
|
||||
folders retains a valid lease without requesting another biometric operation.
|
||||
Backgrounding, manual Lock and Cancel immediately mask protected presentation
|
||||
and interrupt a pending OS prompt before waiting for Rust to revoke its handles.
|
||||
Obsolete requests cannot publish plaintext or overwrite newer authentication state.
|
||||
|
||||
Preferences can explicitly enroll biometric unlock even in an empty store. A
|
||||
secure Compose dialog collects the GPG passphrase without saved state, disables
|
||||
keyboard correction, masks input and clears it on submission, dismissal,
|
||||
backgrounding or disposal. Rust proves the configured secret key's passphrase,
|
||||
then persists the passphrase through the existing current-biometric-set secure
|
||||
store and commits the setting only after the OS-authenticated cipher succeeds.
|
||||
Disabling deletes the protected passphrases and revokes the shared lease.
|
||||
|
||||
AndroidX BiometricPrompt authenticates the Keystore cipher supplied by the
|
||||
platform adapter with BIOMETRIC_STRONG. Current-set records never request device
|
||||
credential fallback. The explicit Unlock action restores and validates the
|
||||
configured key's protected passphrase; password-entry Unlock retains Rust's
|
||||
recipient selection. Missing or unusable records offer the manual dialog.
|
||||
Manual recovery while biometric unlock is enabled explicitly renews the protected
|
||||
record; if strong biometrics are unavailable, disable biometric unlock in
|
||||
Preferences before using manual password-entry unlock. Kotlin stores neither the
|
||||
passphrase nor a duplicate authentication deadline in preferences, saved state,
|
||||
files or WorkManager.
|
||||
|
||||
Capability/no-enrollment, denied access, cancellation, temporary/permanent
|
||||
lockout, missing or invalidated keys and storage failures retain typed Rust
|
||||
outcomes. Lockout maps to BiometryUnavailable with manual recovery; cancellation
|
||||
remains Cancelled. Process recreation starts locked and does not resurrect a
|
||||
lease. Protected fields are rendered only while the shared lease is unlocked,
|
||||
even if an older page result is still present. Ordinary folders and cached
|
||||
non-secret TOTP discovery do not invoke an authentication prompt.
|
||||
|
||||
## Validation
|
||||
|
||||
Rust tests use a fake monotonic clock and injected secure-store outcomes to check
|
||||
wrong passphrases, failed/cancelled enrollment without setting publication,
|
||||
immediate lease revocation after cancelled manual biometric renewal,
|
||||
current-set protection, one lease without repeated prompts, typed lockout and
|
||||
invalidation, renewed enrollment, process recreation, timeout/activity touches,
|
||||
editor-handle revocation and interruption of a blocked platform prompt. An empty
|
||||
store regression proves enrollment does not require an existing password entry.
|
||||
|
||||
Compose tests check explicit dialog submission, typed enrollment success,
|
||||
manual recovery, lease masking, enable/disable/lock actions and clearing input
|
||||
on cancellation, backgrounding and remount. The opt-in app instrumentation test
|
||||
uses the production core facades, actual Rust and Android Keystore with isolated
|
||||
Rust directories and namespaced OS aliases. It exercises authenticated cipher
|
||||
enrollment/unlock, cancellation, background revocation, changed fingerprint
|
||||
invalidation, removal of device protection, rejection of unsupported enrollment
|
||||
and recovery after restoring a synthetic test PIN/fingerprint. Its arguments are
|
||||
`-e class de.rfc1437.ironstorage.AuthenticationLeaseTest -e biometricLease true`.
|
||||
The fixture stages are driven by emulator fingerprint controls/test automation;
|
||||
only public disposable credentials are used. This test deliberately changes
|
||||
emulator enrollment and lock-screen protection, so use a disposable test device
|
||||
and restore its synthetic setup before subsequent tests.
|
||||
|
||||
Validation scope is ARM64 API 26/4 KB, API 37/4 KB and strict API 37/16 KB.
|
||||
ARMv7 is compiled and package-audited; no ARMv7 system image is available locally.
|
||||
Physical devices are excluded by the milestone's explicit emulator-only scope.
|
||||
No Apple project or frontend changes are required.
|
||||
|
||||
Final verification passed all four required root Rust gates (477 workspace tests),
|
||||
the complete Android debug/release/AAB, lint, JVM, style, architecture, ELF/ZIP
|
||||
16 KB, manifest/backup, dependency/license and SBOM gates, and app plus native
|
||||
instrumentation on all three configurations. Each configuration passed five
|
||||
native cold loads and five application cold starts. The real biometric lifecycle
|
||||
and capability/recovery test passed on API 37/4 KB with synthetic credentials.
|
||||
Computer use verified the active OS prompt, production Preferences and secure
|
||||
manual enrollment dialog in Android Studio's embedded emulator; approved CLI
|
||||
and test automation drove fingerprint events and disposable fixture actions.
|
||||
Reference in new issue
Block a user