113 lines
7.4 KiB
Markdown
113 lines
7.4 KiB
Markdown
# Android presentation security
|
|
|
|
The Android client presents Rust-owned authentication state. `crates/storage`
|
|
owns lease expiry, renewal, editor disposal, and clipboard content comparison.
|
|
Kotlin forwards OS events and applies native window, input, and clipboard controls.
|
|
|
|
## Lifecycle and immediate cleanup
|
|
|
|
| Event | Behavior |
|
|
| --- | --- |
|
|
| Foreground/resume | Report foreground to Rust; remain locked until explicit unlock. |
|
|
| Explicit interaction | Report user activity while unlocked; Rust decides lease renewal. |
|
|
| Pause/stop/task switch | Immediately cover native content and clear protected presentation, then revoke the Rust lease. |
|
|
| Loss of top-resumed status | Revoke for an inactive multi-window activity, except while its own OS biometric request is pending. |
|
|
| Native window focus lost/user leaves task | Revoke even if Recents keeps the activity resumed; owned dialog/menu focus transfers remain usable. |
|
|
| Screen off/device locked | Cancel authentication and revoke the lease. |
|
|
| Trim memory/low memory | Clear protected presentation and revoke regardless of trim level. |
|
|
| Process recreation | Start locked; never restore a lease or plaintext from saved state. |
|
|
| Manual lock/expiry | Clear protected presentation, editors, OTP state, and active clipboard copies. |
|
|
|
|
`ProtectedPresentation` invokes main-thread cleanup synchronously. Its callbacks
|
|
clear setup secrets, cancel staged setup/remote operations, drop decrypted facade
|
|
sections and editor/TOTP state, clear local dialog inputs, and cover the old native
|
|
surface with an opaque overlay. Accessibility descendants are suppressed while
|
|
covered. A generation change discards the old composition; the cleaned content is
|
|
rendered before the resumed activity is uncovered. This keeps the frame clock
|
|
running while preventing a stale frame from being visible between cleanup and
|
|
recomposition. Rust revocation jobs run in order and complete before a subsequent
|
|
unlock; stale results cannot republish a protected document.
|
|
Owned dialogs and menus register their window before opening and report their own
|
|
focus loss. The activity excludes those internal focus transfers and its active
|
|
OS biometric request from external focus-loss revocation. The native cover stays
|
|
in place until the cleaned root is both resumed and focused again.
|
|
|
|
TOTP ticks, recomposition, polling, animations, clipboard waiting/cleanup, and
|
|
foreground notifications never report user activity. Rotation clears input and
|
|
cancels setup operations rather than saving passphrases, armor, or tokens.
|
|
|
|
## Windows, inputs, and diagnostics
|
|
|
|
The main activity sets `FLAG_SECURE` before Compose renders. Sensitive dialogs
|
|
use secure windows; other dialogs inherit the activity flag. API 33+ also disables
|
|
recent-task screenshots. Picture-in-picture is disabled in the manifest. An
|
|
activity launched on a secondary display is revoked and finished. Task switching
|
|
and inactive multi-window views clear protected presentation; returning does not
|
|
automatically authenticate. Screen sharing/casting to nonsecure surfaces uses the
|
|
same window policy. See Android's [secure-window contract](https://developer.android.com/reference/android/view/WindowManager.LayoutParams#FLAG_SECURE).
|
|
|
|
The activity and dialog roots exclude descendants from autofill and, on API 30+,
|
|
content capture. Input sessions request no personalized learning, fullscreen IME,
|
|
or extract UI. Password editors remain masked until an explicit reveal action.
|
|
Unlocked accessible fields remain available to assistive technology; locked
|
|
content is removed and covered. See the platform [view controls](https://developer.android.com/reference/android/view/View)
|
|
and [IME flags](https://developer.android.com/reference/android/view/inputmethod/EditorInfo).
|
|
|
|
The production Android modules have no secret logging, analytics, notification
|
|
payloads, or plaintext previews. Facade secret state and generated bridge records
|
|
have redacted diagnostic strings; errors use typed Rust messages or generic OS
|
|
failure text. Saved state contains navigation/setup choices, not protected input
|
|
or decrypted documents. Tests inspect serialized saved bundles and immediate
|
|
accessibility suppression. No memory-dump/export facility is provided.
|
|
|
|
The current manifest contains the main activity. Credential-provider components
|
|
are delivered by issue #109; they must use these protected presentation/window
|
|
controls when introduced. This issue does not install an empty credential service.
|
|
|
|
## Clipboard ownership and limits
|
|
|
|
Each explicit copy obtains a separate native clipboard capability. Rust supplies
|
|
the selected stored field and directs timeout/cancellation cleanup. Android marks
|
|
the clip sensitive, assigns a random owner identity, and records the OS timestamp.
|
|
Cleanup requires that identity, timestamp, and Rust's content comparison still
|
|
match. Another copy with identical text, unrelated user text, or a restored old
|
|
clip with a new timestamp is preserved. The adapter checks ownership again just
|
|
before clearing. A successful write publishes explicit copy feedback. Revocation
|
|
attempts cleanup synchronously while foreground clipboard access is still
|
|
available; no previous clipboard history is retained or restored.
|
|
|
|
Android's ClipboardManager does not offer an atomic compare-and-clear operation
|
|
across IPC calls. Another application can race the final check; this implementation
|
|
cannot promise atomicity that the OS API lacks. Android 10+ can deny background
|
|
clipboard reads, so cleanup cannot be guaranteed after the process has already
|
|
lost access or died. System/OEM clipboard histories may retain copies independently.
|
|
The sensitive flag suppresses supported clipboard previews; it is not encryption.
|
|
See [Android clipboard security](https://developer.android.com/privacy-and-security/risks/secure-clipboard-handling)
|
|
and [clipboard behavior](https://developer.android.com/develop/ui/views/touch-and-input/copy-paste).
|
|
|
|
`FLAG_SECURE`, autofill/content-capture exclusions, and IME requests rely on OS/OEM
|
|
and keyboard cooperation. They cannot protect against a rooted OS, debugger,
|
|
malicious keyboard, camera photographing the screen, or forensic memory access.
|
|
JVM/Compose strings cannot be reliably zeroized; cleanup drops references and
|
|
Rust zeroizes owned secret buffers, without claiming forensic JVM erasure.
|
|
|
|
## Validation
|
|
|
|
Rust tests exercise lifecycle revocation/editor disposal, foreground without
|
|
renewal, and immediate content-checked clipboard cleanup. Native facade tests
|
|
use real encrypted entries and verify synchronous clearing for background,
|
|
device lock, memory pressure, and process recreation. UI/OS tests cover native
|
|
clipboard ownership and restored metadata, sensitive clips, secure windows,
|
|
IME flags, saved bundles, rotation, and immediate accessibility masking.
|
|
|
|
The emulator matrix covers ARM64 API 26 (4 KB pages), API 37 (4 KB), and API 37
|
|
(strict 16 KB), including app/core/bridge instrumentation and five cold starts.
|
|
The existing embedded API 37 emulator additionally exercises real Keystore
|
|
biometric enrollment, unlock, cancellation, background cancellation, foreground
|
|
remaining locked, and memory-pressure revocation using disposable public fixture
|
|
credentials and its existing synthetic fingerprint. Computer use checks task
|
|
switching/recents and cleared input on return. ARMv7 receives build/package
|
|
validation; no suitable ARMv7 emulator image is available. Physical-device and
|
|
OEM-specific capture/casting coverage are excluded from this milestone's test
|
|
scope. Accessibility assertions do not claim spoken TalkBack verification.
|