Files
IronStorage/docs/android/authentication.md
T
hugo 647dc1bb4b
Android ARM verification / rust (push) Failing after 47m23s
Android ARM verification / android (push) Failing after 8s
Dependency security audit / rustsec (push) Successful in 23s
Integrate Android biometrics with the shared Rust lease (#98)
2026-10-09 16:32:01 +02:00

78 lines
5.0 KiB
Markdown

# 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.