Enforce Android lifecycle and presentation security (#100)
This commit is contained in:
1 parent
c0ba6d43d8
commit
a06f39af23
37 files changed
+1129
-110
No files matched your search
@@ -14,9 +14,10 @@ by Release shrinking. Certificate validation uses the operating system trust sto
|
||||
Local setup generates or imports a private GPG key, verifies its passphrase, creates
|
||||
Git and the upstream `.gpg-id`, and commits the configuration. No server, camera,
|
||||
or biometric permission is needed. Encrypted prepared keys survive process death;
|
||||
passphrases and tokens remain in memory only. Form secrets survive rotation through
|
||||
a ViewModel and are excluded from saved-state Bundles. After process death transport
|
||||
and passphrase inputs must be entered again.
|
||||
passphrases and tokens remain in memory only and are excluded from saved-state
|
||||
Bundles. Presentation revocation clears form secrets synchronously, including
|
||||
backgrounding, rotation and memory pressure. Pending transport requests are cancelled
|
||||
and released; transport and passphrase inputs must be entered again after returning.
|
||||
|
||||
HTTPS setup discovers branches with a temporary Rust repository, selects the branch,
|
||||
and checks the selected private key against the cloned root recipient policy. Reusing
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
# 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.
|
||||
Reference in new issue
Block a user