Complete Android parity and security audit (#111)
This commit is contained in:
1 parent
45d78aa5b3
commit
b54636b7bc
14 files changed
+498
-75
No files matched your search
@@ -80,7 +80,7 @@ At #89 pin a mutually supported stable AGP/Gradle/Kotlin/KSP/Hilt set, JDK,
|
||||
SDK build tools and NDK, plus wrapper checksum. The current [AGP 9.4 contract](https://developer.android.com/build/releases/agp-9-4-0-release-notes)
|
||||
requires Gradle 9.6.0 and JDK 17 and supports API 37. This is a candidate build
|
||||
baseline, subject to actual artifact availability and plugin compatibility.
|
||||
The #89 scaffold pins AGP 9.4.1, Gradle 9.8.1, Kotlin 2.4.20, KSP 2.3.12,
|
||||
The #89 scaffold pins AGP 9.4.1, Gradle 9.8.1, Kotlin 2.4.21, KSP 2.3.12,
|
||||
Hilt 2.60.1 and JDK 25 (JVM output 17); its catalog and build instructions live
|
||||
in [android/README.md](../../android/README.md). These pins are verified by the
|
||||
scaffold build gates rather than inferred from minimum-version requirements.
|
||||
@@ -198,9 +198,11 @@ a remote clone. Do not seed app data or commit credentials to prove recovery.
|
||||
## Verification and acceptance
|
||||
|
||||
The [parity matrix](parity.md) assigns every current non-Watch mobile capability
|
||||
and every Android issue to a Rust owner and Android API. Its pending rows are
|
||||
implementation obligations, not accepted gaps. #111 must replace pending status
|
||||
with evidence and have no unexplained gap before #112 distribution.
|
||||
and every Android issue to a Rust owner, current Android component and executable
|
||||
checks. The [#111 audit](audit.md) records current results, runtime limits and the
|
||||
unpatched/deprecated upstream dependency findings tracked outside this milestone
|
||||
in [#125](https://git.rfc1437.de/hugo/IronStorage/issues/125). This traceability does
|
||||
not claim that cargo-audit is clean or grant unrestricted release approval.
|
||||
|
||||
Every issue runs the AGENTS.md Rust gates before commit. Android changes also
|
||||
run build/lint/unit/instrumented checks appropriate to the issue and computer-use
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
# Android parity, architecture and security audit (#111)
|
||||
|
||||
Audit date: 2026-10-15. Scope is milestone 23, the current non-Watch iPhone
|
||||
surface and the Android release graph. [parity.md](parity.md) maps every
|
||||
capability to its Rust owner, concrete Android component and executable checks.
|
||||
[architecture.md](architecture.md) remains the support/threat-model contract.
|
||||
A test listed as a coverage pointer is not evidence that an opt-in journey ran.
|
||||
The current iPhone AutoFill extension controller has no implemented selection
|
||||
flow. The Android provider requirement is nevertheless fully implemented through
|
||||
Rust and the native OS services; the matrix does not attribute that capability
|
||||
to the empty iPhone controller.
|
||||
|
||||
## Findings corrected in this audit
|
||||
|
||||
- First-run Preferences previously claimed that configuration had been lost while
|
||||
repository/key data remained. `PrivateStorage::authentication` now distinguishes
|
||||
the validated empty configuration from actual `ConfigurationMissingWithData`.
|
||||
Genuine recovery still retains its typed error and ciphertext. Rust regressions
|
||||
first failed against the old implementation and now pass for both cases.
|
||||
- Startup onboarding, binding verification and Home independently used the same
|
||||
nonblocking runtime gate. Brief legitimate overlap could display a storage
|
||||
failure on a cold start. Rust now tolerates up to one second of contention,
|
||||
checking the same capability-validated lock every 10 ms. Longer operations
|
||||
still fail with retry guidance. Scratch-owner locks remain nonblocking so
|
||||
startup cleanup never deletes active scratch. Tests cover transient release,
|
||||
a retained long-lived lock, unchanged encrypted bytes and successful retry.
|
||||
- Busy authentication is a typed `Conflict` titled `Private Storage Busy`, rather
|
||||
than a claim of corruption/recovery. The Home facade preserves the typed
|
||||
conflict. Setup also reports contention as `Private Storage Busy` while genuine
|
||||
setup corruption retains `Setup Recovery Required`. No frontend derives state
|
||||
from error strings.
|
||||
- API 26 exposed a test synchronization race in the TOTP disposal check: the
|
||||
baseline was read immediately after a click, before Compose removed the code
|
||||
view, and one legitimate pre-disposal tick could land afterward. The regression
|
||||
now asserts that the code node is absent and reads/checks the counter on the UI
|
||||
thread. It retains the full 1.2-second no-further-ticks assertion; no production
|
||||
timer or assertion was disabled to obtain a pass. The corrected focused check
|
||||
passed three isolated API 26 runs, then passed in the full 53-case app runner.
|
||||
The failed original attempt is not counted as passing evidence.
|
||||
- The release artifact gate now rejects `debuggable`, `testOnly` and `profileable`
|
||||
access in the packaged production manifest. Debug/profile/benchmark packages
|
||||
remain separately identifiable test artifacts. Manifest regression tests prove
|
||||
rejection and the release CI path invokes the gate on decoded APK contents.
|
||||
- Updated Android-reachable Rustls 0.23.45 to 0.23.46 (and raised its minimum),
|
||||
faster-hex 0.10.0 to 0.10.1, rqrr 0.10.1 to 0.11.0 / lru 0.18.2, and the yanked
|
||||
chacha20 0.10.1 to 0.10.2. QR fixtures and native decode tests exercise the
|
||||
decoder change; both release ABIs were rebuilt from the resulting lockfile.
|
||||
|
||||
## Boundary and lifetime review
|
||||
|
||||
Reviewed the connected `crates/storage` mobile/authentication/onboarding/Git/QR/
|
||||
credential/secret-store implementations, `crates/mobile-bridge`, `crates/android`,
|
||||
and Android app/core/features/platform/native-loader modules. Rust owns directory
|
||||
capabilities, repository operations, parsing, generation, OTP, QR transfer/decode,
|
||||
secret records, authentication, endpoint and caller/site policy. UniFFI exports
|
||||
mechanical values and OS callbacks. Kotlin maps typed state, schedules blocking
|
||||
calls off the main thread, collects input and presents Android UI/OS services.
|
||||
|
||||
No Android-reachable application component launches a process. Project-authored
|
||||
Rust forbids unsafe code; generated/dependency JNI/JNA/Keystore FFI is outside that
|
||||
claim. `std::process::id()` obtains a transaction owner ID and does not launch a
|
||||
process. Build/release tools legitimately invoke host commands. The desktop CLI's
|
||||
external-editor support is outside the Android runtime graph and this audit; this
|
||||
report does not claim that every unrelated workspace executable avoids processes.
|
||||
|
||||
NativeRuntime's `File`/`ZipFile` checks inspect installed APK/native-library facts,
|
||||
not the repository. Provider package-certificate hashing obtains OS identity facts;
|
||||
Rust compares caller/site identities and decides grants/matches. Kotlin never
|
||||
parses pass documents, `.gpg-id`, GPG packets, OTP payloads or Git transport errors.
|
||||
The source/dependency gate complements this connected-code review.
|
||||
|
||||
Git endpoints are validated typed HTTPS on Android (SSH feature disabled), without
|
||||
embedded account/token userinfo. Repository remote configuration is authoritative.
|
||||
Tokens stay in Rust-owned sealed records with OS wrapping keys. TLS uses the
|
||||
Android platform verifier and redirects cannot forward credentials. There is no
|
||||
cleartext transport or certificate-validation bypass to make fixtures pass.
|
||||
|
||||
Ordinary persistent state contains encrypted entries, encrypted private keys,
|
||||
configuration, encrypted secret envelopes and private TOTP metadata, not decrypted
|
||||
entry fields, passphrases, tokens or OTP seeds. Editor recovery is encrypted and
|
||||
revision/generation-bound. Android saved state retains non-secret selection/query
|
||||
facts only. Logs and reports use bounded typed diagnostics and sanitized metrics;
|
||||
there is no app crash-upload service. Kotlin/Java memory cannot promise complete
|
||||
erasure, and accessibility/IME/clipboard consumers can observe deliberately
|
||||
revealed or handed-off data under OS control.
|
||||
|
||||
## Threat-test coverage and limits
|
||||
|
||||
| Threat | Enforcement and executable evidence | Interpretation |
|
||||
| --- | --- | --- |
|
||||
| Keystore record tamper, missing keys, restore/device binding | Rust Android backend/private-storage tests; real `AndroidKeystoreTest.deviceUnlockedRoundtripReplaceDeleteAndTamper` | Failure retains existing encrypted repository; no silent identity/key recreation for bound data |
|
||||
| Biometric cancellation, rejection, invalidation/lock removal | Rust authentication/backend tests; opt-in platform `AndroidKeystoreTest` and `AuthenticationLeaseTest` | OS-interactive checks require separate recorded runs; do not count standard-suite exclusions as passes or change enrollment on the retained app phone |
|
||||
| Backup/transfer and update | Packaged XML reference and all-domain exclusion gates, `BackupPolicyTest`, private-context/update Rust tests | Install over the same ID/signing lineage; emulator data retained. OEM transfer tools remain outside available emulator evidence |
|
||||
| Screen capture/recents, focus loss, logs and plaintext saved state | Secure-window/recents platform wiring; `PresentationSecurityTest`, source audit, generation/lease Rust tests | Computer-use visibility is test observation, not proof that an arbitrary compromised OS cannot capture pixels |
|
||||
| Clipboard and accessibility | OS ownership/expiry adapter, Rust copy policy, `PresentationSecurityTest`; manual hardening evidence | Deliberate reveal/copy is observable by authorized platform consumers. TalkBack focus/activation is verified; speech output and actual external switch scanning are not claimed |
|
||||
| Background, process death, dirty edits and stale callbacks | `AuthenticationLeaseTest`, `PresentationSecurityTest`, `NativeEntryViewerTest`; Rust lease/editor checkpoint/cancellation tests | No stored lease, no plaintext draft restore, no replay of a completed mutation |
|
||||
| Git dirty tree/conflict, TLS/auth/offline/cancellation | Rust `mobile_home::android_tests`, Git transport/embedded tests, Compose Home tests | Injected transports prove failure ordering without sending fixture tokens to external servers; runtime local Git journey is separate from public-server availability |
|
||||
| Camera interruption, malicious/oversized QR/image | `AndroidQrCameraTest`, Rust QR/key-transfer tests and bounded Y-plane adapter | Camera restart and real frame delivery are distinct from decoding known QR vectors in Rust |
|
||||
| Malicious providers, forged/stale/cancelled intents, preauth disclosure | Rust `mobile_credentials`/authentication tests, `ProviderIntentSecurityTest`, `NativeCredentialProviderTest`, system `provider-client` journeys | Paths/candidates may be listed while locked; secret fill requires exact live request, explicit consent/selection and valid lease |
|
||||
| Offline, doze/battery, memory/storage pressure | Rust fault/rollback tests; native environment/hardening checks | No physical disk filling or physical-device coverage is implied by the low-storage broadcast |
|
||||
| Root/debug assumptions | Non-debuggable packaged release gate, private capabilities/backup/Keystore policy | Rooted/fully compromised devices and local hostile timing observers are outside the accepted sandbox threat model; a debug APK is not the production security boundary |
|
||||
|
||||
## Dependency exceptions
|
||||
|
||||
RustSec database: 1,329 advisories, commit
|
||||
`edb58f5974b83eb8302332821cb6d389777aa351`, updated 2026-10-14T22:18:32+02:00.
|
||||
After the updates, the Android normal/build dependency graph has two remaining
|
||||
findings, explicitly tracked outside this milestone in
|
||||
[#125](https://git.rfc1437.de/hugo/IronStorage/issues/125):
|
||||
|
||||
- `pgp -> rsa 0.9.10`: [RUSTSEC-2023-0071](https://rustsec.org/advisories/RUSTSEC-2023-0071.html)
|
||||
has **no patched release**. OpenPGP decryption is local and follows explicit
|
||||
authorization; Git/HTTPS does not expose a remote RSA-decryption endpoint.
|
||||
This limits the ordinary remote oracle but does not establish constant-time
|
||||
RSA or eliminate local timing risk. Retaining existing pass RSA-key compatibility
|
||||
does not justify claiming the advisory fixed. Reassess an upstream-reviewed,
|
||||
compatible pure-Rust mitigation under #125.
|
||||
- `gix-protocol -> bisync 0.3.0`: yanked/deprecated, with no replacement release.
|
||||
Its [upstream notice](https://github.com/JM4ier/bisync) says the author retired
|
||||
the tiny utility to avoid maintenance/supply-chain responsibility. The existing
|
||||
lockfile checksum remains required; track a reviewed transport update removing
|
||||
it under #125 rather than hiding the warning or adding an unreviewed fork.
|
||||
|
||||
Workspace `cargo audit` still exits nonzero: besides RSA it reports host/desktop
|
||||
packages outside the Android graph (including quinn-proto and ruzstd). They were
|
||||
not silently updated as Android work. No clean whole-workspace audit or unrestricted
|
||||
release approval is claimed. License/SBOM policy and vulnerability status are
|
||||
separate checks.
|
||||
|
||||
## Run record
|
||||
|
||||
Required Rust gates passed: formatting, warnings-as-errors workspace/all-target
|
||||
check, Clippy with warnings denied, and the full workspace test run (524 passed,
|
||||
zero failed). The final Android verification/build passed debug/release/AAB,
|
||||
Lint including release, ktlint, JVM tests, both ARM native builds/metadata/16-KB
|
||||
package audits, decoded backup/transfer/manifest checks, separate native symbols,
|
||||
R8 mapping and deterministic CycloneDX/license policy (1,839 inventory components).
|
||||
The CI orchestration self-test run passed all 11 tests.
|
||||
|
||||
The final API 37 / 4-KB ARM64 run passed app/core/bridge/platform instrumentation
|
||||
and five cold starts. The native core suite exercised the new 12-pair concurrent
|
||||
onboarding/Home regression along with real encrypted edit/mutation/OTP/provider
|
||||
and catalog/recovery tests. Native QR analysis was 84 ms, authenticated fill
|
||||
1,249 ms, and 10,000-entry navigation/search were 90/273 ms. Debug cold starts
|
||||
were 840–1,220 ms; five native-load measurements were 40.8–58.4 ms.
|
||||
|
||||
The final API 26 / 4-KB ARM64 run likewise passed all four instrumentation suites
|
||||
and five cold starts after the TOTP test synchronization fix. Native QR analysis
|
||||
was 80 ms, authenticated fill 1,210 ms, and 10,000-entry navigation/search were
|
||||
76/227 ms. Debug cold starts were 210–249 ms. Computer use opened actual first-run
|
||||
Preferences and verified `Set Up Password Store` with local/HTTPS setup guidance,
|
||||
replacing the old false data-recovery claim without creating or deleting a store.
|
||||
|
||||
API 26 release-equivalent Macrobenchmark passed five cold and five warm starts:
|
||||
initial display 149.8–171.0 ms cold and 44.1–95.4 ms warm; full cold display
|
||||
189.7–219.5 ms. The measured artifact retained its app data and baseline profile.
|
||||
|
||||
The explicit `biometricSecurity=true` real-OS fixture passed on the existing
|
||||
API 37 phone fingerprint enrollment. Computer use observed the CryptoObject
|
||||
prompts and cancellation result; emulator sensor events supplied matching/nonmatching
|
||||
synthetic fingerprints. OS counters confirmed one rejected crypto attempt before
|
||||
a matching unlock. The test asserted enrollment, shared reload while unlocked,
|
||||
manual lock, cancellation, real activity background interruption, fresh post-background
|
||||
unlock, immediate memory-pressure relock and biometric disable. Its namespace,
|
||||
public passphrase and generated store were isolated from the retained app. Fixture
|
||||
aliases and runner were cleaned up; device enrollment and app data were retained.
|
||||
|
||||
Strict API 37 / 16-KB ARM64 passed all four suites and five cold starts with
|
||||
both compatibility paths disabled. Native QR analysis was 82 ms, authenticated
|
||||
fill 1,216 ms, and 10,000-entry navigation/search were 82/251 ms. Cached TOTP,
|
||||
warm refresh and provider preparation p95 were 5,349/8,241/4,195 us. Five debug
|
||||
cold starts were 832–971 ms and native loads 66.4–75.8 ms. Computer use verified
|
||||
the actual retained six-commit store and clean worktree loaded after the final
|
||||
cold starts, with no startup busy/recovery failure.
|
||||
|
||||
Strict API 37 release-equivalent Macrobenchmark also passed five cold and five
|
||||
warm starts: initial display 192.3–335.9 ms cold and 77.7–134.5 ms warm; full cold
|
||||
display 399.0–510.1 ms. Computer use completed real framework Autofill and Android
|
||||
Credential Manager selection/consent/unlock against the separate provider client;
|
||||
both returned the exact expected public fixture username/password. The initial
|
||||
requests expired during slow inspection; fresh requests passed within the
|
||||
unchanged request lifetime. Pre-authentication selectors exposed entry paths,
|
||||
not the secret fields.
|
||||
|
||||
Real API 35 ARM64 Keystore enrollment-change invalidation passed in an isolated
|
||||
platform test app: the current-set biometric key became `Invalidated`, while a
|
||||
separate DeviceUnlocked key still decrypted its public token. The first attempt
|
||||
timed out while driving OS setup and is not counted as a pass; the fresh test
|
||||
completed in 124.4 seconds, within the unchanged 180-second fixture window.
|
||||
Computer use observed the encryption prompt, OS enrollment and return to the
|
||||
fixture. Only synthetic enrollment on that dedicated device changed; no ordinary
|
||||
IronStorage app was installed there. Test keys were deleted by the fixture.
|
||||
The device was shut down gracefully afterward. Its existing synthetic PIN was
|
||||
retained; no data or enrollment reset was used to recover the cold boot.
|
||||
|
||||
The updated fold emulator also loaded real first-run setup and Preferences via
|
||||
computer use, with readable adaptive navigation and the corrected setup guidance.
|
||||
The final connected-code review checked the diff, callers, error mappings,
|
||||
fixtures and acceptance matrix. No issue is closed on an expired/failed run.
|
||||
|
||||
After the final setup-contention title correction, all four Rust gates passed
|
||||
again (524 tests) and the full Android build/audit passed again for both ARM ABIs.
|
||||
The final native viewer regression, including concurrent onboarding/Home, passed
|
||||
on API 37 in 54.9 seconds. A fresh app cold launch took 743 ms and computer use
|
||||
verified the retained three-commit vault and clean worktree. ARM64 emulator runtime evidence stays distinct from
|
||||
ARMv7 packaging and historical per-issue evidence.
|
||||
+30
-30
@@ -1,43 +1,43 @@
|
||||
# Android parity and issue ownership
|
||||
|
||||
Contract for [#88](https://git.rfc1437.de/hugo/IronStorage/issues/88), reviewed
|
||||
2026-10-08 against apple/Sources/App/IronStorageApp.swift,
|
||||
2026-10-15 against apple/Sources/App/IronStorageApp.swift,
|
||||
apple/Sources/AutoFill/CredentialProviderViewController.swift, crates/apple/src/lib.rs,
|
||||
and crates/storage/src/mobile*.rs. See [architecture.md](architecture.md) for
|
||||
support, dependency, security and update policy.
|
||||
|
||||
**Status:** #88 records the architecture baseline. The Android shell (#89),
|
||||
native packaging (#92), shared contract (#91), and private runtime (#90) are
|
||||
implemented; feature presentation rows remain assigned below. Issue comments
|
||||
record per-issue validation and outstanding test gaps. #111 must attach the
|
||||
final screen/component/test evidence to each row before claiming full parity.
|
||||
Every issue linked here belongs to milestone 23.
|
||||
**Status:** implementation issues #89–#110 are delivered. The #111 audit
|
||||
maps each capability to current Android components and executable checks below.
|
||||
[audit.md](audit.md) records current results, dependency decisions and explicit
|
||||
runtime limits. Test names identify coverage; opt-in tests need separate runtime
|
||||
evidence and are not counted as exercised by a normal suite.
|
||||
Every implementation issue linked here belongs to milestone 23.
|
||||
|
||||
## Current iPhone and shared-mobile capabilities
|
||||
|
||||
| Existing capability / source | Owning Rust API/module | Android presentation / approved API | Implementation and required evidence |
|
||||
| --- | --- | --- | --- |
|
||||
| Five destinations: Home, Passwords, Search, TOTP, Preferences; loading/empty/ready/locked/error fixtures (AppContext, ShellViewController) | mobile::MobileShell, MobileTab, MobileShellState; config selected tab | Single Activity, Compose Material 3 Adaptive, Navigation 3, ViewModel/StateFlow | #89/#91/#102; shell fixtures, startup, back/navigation and recreation |
|
||||
| Local-first setup, generated/imported key, passphrase confirmation, staged QR import (SetupChoiceViewController, LocalSetupViewController) | mobile_onboarding::MobileOnboardingOperation, crypto; mobile_key_transfer import | Compose forms and core facade; OS input/camera adapters | #95/#106; real local init, key export/import, canceled/failed setup rollback |
|
||||
| Optional HTTPS clone, server/account/token, repository/branch discovery; attach remote to local store (OnboardingViewController) | mobile_onboarding::{MobileOnboardingRequest, MobileOnboardingOperation}; git/config/secret_store | Forms, typed incremental progress and cancel | #95; TLS/auth/branch/cancel/offline errors; repository remote authority |
|
||||
| Home history, ahead/behind, local/remote change status, commit activity (ShellViewController, MobileCommitActivityViewController) | mobile_home::MobileHomePage/Operation and git | Compose Home/list/detail | #97; summaries and stale/error state, Git identity, author/history |
|
||||
| Commit, Fetch, Pull, Push and pull-then-push Sync | mobile_home::MobileHomeOperation; git sync/merge/conflicts/signing | Typed actions, progress/cancel, conflict presentation | #97; pull failure prevents push, conflict/data-loss cases and signing |
|
||||
| Directory hierarchy, breadcrumbs, folder and entry rows (PasswordDirectoryViewController) | mobile_passwords::MobilePasswordPage/Row/RowKind; repository/read | Adaptive list/detail with stable IDs | #96; deep tree, empty/error, keyboard and size changes |
|
||||
| Password-store name search (PasswordSearchViewController) | mobile_passwords::MobilePasswordPage::search | Search ViewModel, Flow latest-query-wins | #101; large-store responsiveness and canceled/stale results |
|
||||
| Unlock entry, explicit user activity, timeout, manual/background lock; shared entry/TOTP/editor lease (LockedPasswordViewController, AppContext) | mobile_authentication::MobileAuthentication; authentication generation/leases | AndroidX BiometricPrompt/CryptoObject, KeyguardManager, lifecycle | #93/#98/#100; wrong passphrase, cancel, expiry, generation race, changed enrollment and process death |
|
||||
| Structured fields/sections, masked secrets, explicit reveal/select/copy; OTP field diagnostics (MobileEntryFieldCell) | mobile_entry::MobileEntryPage/Field; mobile_authentication::{entry_page,reveal_entry_field,copy_entry_field} | Compose viewer, sensitive controls and accessibility semantics | #99/#100; no pre-unlock values, relock clears all views, copy expiry/ownership |
|
||||
| Create entry/folder, structured multiline editing, add/remove/reorder fields, dirty discard confirmation (MobileEntryEditorViewController) | mobile_entry::MobileEntryDraft; mobile_authentication editor session/create/save/discard; write | Compose editor/forms, stable field IDs | #103; actual encrypted saves, conflict tokens, interrupted saves and dirty navigation |
|
||||
| Password length/generation and replacement | generate; mobile_entry::MobileEntryDraft::generate_password | Generator controls call Rust | #103; Rust validation and unbiased generated value; no Kotlin generator |
|
||||
| OTP URI input, camera/image import, review before Save (OtpImportViewController; TOTP Add action) | mobile_entry::MobileEntryDraft::add_otp_image; document/otp/presentation | CameraX frames or Android photo picker bytes; Rust decode and field construction | #103/#106/#107; URI/image/scan parity, invalid/oversized input, cancel; no payload parsing in Kotlin |
|
||||
| Move/copy/delete entries and folders, destination browse, overwrite and dirty-editor handling (PasswordMutationCoordinator) | mobile_mutation::MobileMutationService/Plan/Request; mutation | Compose dialogs/destination picker | #108; revisions, destination conflicts, recipient reencryption and rollback |
|
||||
| TOTP discovery catalog, incremental progress, warm cache, refresh, cancel/resume, cached search (TotpListViewController) | mobile_totp discovery/catalog/operation; mobile_authentication cached/search APIs | Compose list/progress/search; private no-backup catalog | #90/#110; [discovery behavior and evidence](totp-discovery.md), cold/warm/change/add/remove/corrupt/wrong-store/concurrent-change tests |
|
||||
| TOTP rows, large detail code, countdown/expiry, copying (TotpDetailViewController, TotpCodeView) | mobile_totp::MobileOtpCodePresentation; otp; mobile_authentication::{presented_otp_code,copy_presented_otp_code,advance_presented_hotp} | Compose code/detail; OS clock facts, Rust timing | #107/#100; [OTP behavior and evidence](otp.md), RFC vectors, boundary countdown, stale callback/relock and clipboard |
|
||||
| Repository/server identity, local-only/add-remote Settings, token replace/remove (PreferencesViewController, TokenUpdateViewController) | mobile_authentication::MobilePreferences; mobile_onboarding::replace_configured_application_token; config/secret_store | Compose Settings/facade | #104/#95; no token display; protected replacement/removal and offline operation |
|
||||
| Key identity/fingerprint, GPG recovery import and public/private QR export (KeyImportScannerViewController, KeyExportListViewController, KeyQrExportViewController) | mobile_key_transfer::{MobileKeyTransferService,MobileKeyTransferImport}; crypto/presentation | CameraX Preview/ImageAnalysis; animated Rust-provided matrices | #106/#104; multi-frame order/duplicates/interruption, bounded frames, private export authorization and camera denial |
|
||||
| Inactivity timeout, biometric enable/disable, Lock Now, appearance and Git commit name/email | mobile_authentication preference setters, git_identity; config | Compose Settings, BiometricPrompt, system/light/dark theme | #98/#104; persistent settings, validation, Keystore enrollment/recovery and lease revocation |
|
||||
| iPhone AutoFill credential selection (CredentialProviderViewController) and shared identity/access domain | desktop/read/document/secret_store today; storage-owned provider contract to add | AndroidX CredentialProviderService (API 34+) and framework AutofillService (API 26+) | #109; typed caller/site identity, non-secret candidates, explicit selection/auth, forged/stale requests, permissions and cross-process lifetime |
|
||||
| Camera permissions, private paths, secret-store mechanics, clipboard, scene/process events, screen privacy | Rust repository/config/secret_store/authentication/presentation; platform callbacks contain facts only | Android framework adapters, FLAG_SECURE, backup rules, ClipboardManager | #90/#93/#98/#100; backup inspection, sandbox paths, pressure/update, screen/clipboard/lifecycle tests |
|
||||
| Accessible labels/actions, large text, keyboard, responsive layout and localization | Typed Rust state/actions; UI accessibility is Kotlin | Compose semantics, TalkBack, adaptive layout, resources | #105; measured keyboard/TalkBack/large-font/multi-window/RTL/rotation coverage |
|
||||
| Five destinations: Home, Passwords, Search, TOTP, Preferences; loading/empty/ready/locked/error fixtures (AppContext, ShellViewController) | mobile::MobileShell, MobileTab, MobileShellState; config selected tab | `ShellScreen / ShellViewModel / MainActivity` | #89/#91/#102; shell fixtures, startup, back/navigation and recreation; **Checks:** ShellScreenTest, AdaptiveShellTest, Tier2WindowTest, HingeWindowTest |
|
||||
| Local-first setup, generated/imported key, passphrase confirmation, staged QR import (SetupChoiceViewController, LocalSetupViewController) | mobile_onboarding::MobileOnboardingOperation, crypto; mobile_key_transfer import | `OnboardingScreen / NativeOnboardingRepository` | #95/#106; real local init, key export/import, canceled/failed setup rollback; **Checks:** OnboardingScreenTest; Rust mobile_onboarding::android::tests |
|
||||
| Optional HTTPS clone, server/account/token, repository/branch discovery; attach remote to local store (OnboardingViewController) | mobile_onboarding::{MobileOnboardingRequest, MobileOnboardingOperation}; git/config/secret_store | `OnboardingScreen and SettingsScreen HTTPS actions` | #95; TLS/auth/branch/cancel/offline errors; repository remote authority; **Checks:** OnboardingScreenTest; Rust onboarding and git TLS/auth/remote-authority tests |
|
||||
| Home history, ahead/behind, local/remote change status, commit activity (ShellViewController, MobileCommitActivityViewController) | mobile_home::MobileHomePage/Operation and git | `HomeScreen / NativeHomeRepository` | #97; summaries and stale/error state, Git identity, author/history; **Checks:** HomeScreenTest; Rust mobile_home and tests/git_embedded.rs |
|
||||
| Commit, Fetch, Pull, Push and pull-then-push Sync | mobile_home::MobileHomeOperation; git sync/merge/conflicts/signing | `HomeScreen typed actions and progress` | #97; pull failure prevents push, conflict/data-loss cases and signing; **Checks:** HomeScreenTest; Rust mobile_home::android_tests and Git conflict/cancellation tests |
|
||||
| Directory hierarchy, breadcrumbs, folder and entry rows (PasswordDirectoryViewController) | mobile_passwords::MobilePasswordPage/Row/RowKind; repository/read | `PasswordScreen / NativePasswordRepository` | #96; deep tree, empty/error, keyboard and size changes; **Checks:** PasswordScreenTest, NativeEntryViewerTest, NativeTreeMutationTest |
|
||||
| Password-store name search (PasswordSearchViewController) | mobile_passwords::MobilePasswordPage::search | `SearchScreen / NativeSearchRepository` | #101; large-store responsiveness and canceled/stale results; **Checks:** SearchScreenTest; Rust mobile_passwords and large-store native instrumentation |
|
||||
| Unlock entry, explicit user activity, timeout, manual/background lock; shared entry/TOTP/editor lease (LockedPasswordViewController, AppContext) | mobile_authentication::MobileAuthentication; authentication generation/leases | `AuthenticationControls / AndroidKeystoreAdapter / MainActivity lifecycle` | #93/#98/#100; wrong passphrase, cancel, expiry, generation race, changed enrollment and process death; **Checks:** AuthenticationScreenTest, opt-in AuthenticationLeaseTest, AndroidKeystoreTest; tests/authentication_leases.rs |
|
||||
| Structured fields/sections, masked secrets, explicit reveal/select/copy; OTP field diagnostics (MobileEntryFieldCell) | mobile_entry::MobileEntryPage/Field; mobile_authentication::{entry_page,reveal_entry_field,copy_entry_field} | `EntryViewer / NativePasswordRepository` | #99/#100; no pre-unlock values, relock clears all views, copy expiry/ownership; **Checks:** EntryViewerTest, NativeEntryViewerTest, NativeOtpPresentationTest; tests/entry_documents.rs |
|
||||
| Create entry/folder, structured multiline editing, add/remove/reorder fields, dirty discard confirmation (MobileEntryEditorViewController) | mobile_entry::MobileEntryDraft; mobile_authentication editor session/create/save/discard; write | `EntryEditorScreen / NativeEditorRepository` | #103; actual encrypted saves, conflict tokens, interrupted saves and dirty navigation; **Checks:** EntryEditorScreenTest, NativeEntryViewerTest; Rust editor conflict/checkpoint/atomic-save tests |
|
||||
| Password length/generation and replacement | generate; mobile_entry::MobileEntryDraft::generate_password | `EntryEditorScreen generator controls` | #103; Rust validation and unbiased generated value; no Kotlin generator; **Checks:** EntryEditorScreenTest; tests/password_generation.rs |
|
||||
| OTP URI input, camera/image import, review before Save (OtpImportViewController; TOTP Add action) | mobile_entry::MobileEntryDraft::add_otp_image; document/otp/presentation | `EntryEditorScreen / QrCameraPane / AndroidQrCamera` | #103/#106/#107; URI/image/scan parity, invalid/oversized input, cancel; no payload parsing in Kotlin; **Checks:** EntryEditorScreenTest, AndroidQrCameraTest; Rust qr and tests/otp.rs |
|
||||
| Move/copy/delete entries and folders, destination browse, overwrite and dirty-editor handling (PasswordMutationCoordinator) | mobile_mutation::MobileMutationService/Plan/Request; mutation | `MutationDialog / NativePasswordRepository` | #108; revisions, destination conflicts, recipient reencryption and rollback; **Checks:** MutationWorkflowTest, NativeTreeMutationTest; tests/tree_mutations.rs and recipient_policy.rs |
|
||||
| TOTP discovery catalog, incremental progress, warm cache, refresh, cancel/resume, cached search (TotpListViewController) | mobile_totp discovery/catalog/operation; mobile_authentication cached/search APIs | `TotpScreen / NativeTotpRepository` | #90/#110; [discovery behavior and evidence](totp-discovery.md), cold/warm/change/add/remove/corrupt/wrong-store/concurrent-change tests; **Checks:** TotpDiscoveryFixtureTest, NativeTotpDiscoveryTest, NativeTotpPerformanceTest; tests/mobile_totp.rs |
|
||||
| TOTP rows, large detail code, countdown/expiry, copying (TotpDetailViewController, TotpCodeView) | mobile_totp::MobileOtpCodePresentation; otp; mobile_authentication::{presented_otp_code,copy_presented_otp_code,advance_presented_hotp} | `TotpScreen / OtpCodeView / EntryViewer` | #107/#100; [OTP behavior and evidence](otp.md), RFC vectors, boundary countdown, stale callback/relock and clipboard; **Checks:** TotpScreenTest, NativeOtpPresentationTest; Rust OTP boundary/relock/HOTP tests |
|
||||
| Repository/server identity, local-only/add-remote Settings, token replace/remove (PreferencesViewController, TokenUpdateViewController) | mobile_authentication::MobilePreferences; mobile_onboarding::replace_configured_application_token; config/secret_store | `SettingsScreen / NativeSettingsRepository` | #104/#95; no token display; protected replacement/removal and offline operation; **Checks:** SettingsScreenTest; Rust preference/token and Android onboarding tests |
|
||||
| Key identity/fingerprint, GPG recovery import and public/private QR export (KeyImportScannerViewController, KeyExportListViewController, KeyQrExportViewController) | mobile_key_transfer::{MobileKeyTransferService,MobileKeyTransferImport}; crypto/presentation | `SettingsScreen key transfer / QrCameraPane / AndroidQrCamera` | #106/#104; multi-frame order/duplicates/interruption, bounded frames, private export authorization and camera denial; **Checks:** SettingsScreenTest, AndroidQrCameraTest; Rust mobile_key_transfer frame/order/cancel/authorization tests |
|
||||
| Inactivity timeout, biometric enable/disable, Lock Now, appearance and Git commit name/email | mobile_authentication preference setters, git_identity; config | `SettingsScreen / AuthenticationControls / ApplicationTheme` | #98/#104; persistent settings, validation, Keystore enrollment/recovery and lease revocation; **Checks:** SettingsScreenTest, NativeEntryViewerTest, opt-in AuthenticationLeaseTest; Rust authenticated preference tests |
|
||||
| AutoFill extension boundary (current iPhone controller is empty), shared identity/access domain and Android provider requirement | mobile_credentials; mobile_authentication provider request/select/fill/save APIs | `IronCredentialProviderService / IronAutofillService / CredentialSelectionActivity` | #109; typed caller/site identity, non-secret candidates, explicit selection/auth, forged/stale requests, permissions and cross-process lifetime; **Checks:** ProviderIntentSecurityTest, CredentialSelectionScreenTest, NativeCredentialProviderTest; provider-client OS journeys |
|
||||
| Camera permissions, private paths, secret-store mechanics, clipboard, scene/process events, screen privacy | Rust repository/config/secret_store/authentication/presentation; platform callbacks contain facts only | `AndroidSecretClipboard / ProtectedInput / ProtectedPresentation / MainActivity` | #90/#93/#98/#100; backup inspection, sandbox paths, pressure/update, screen/clipboard/lifecycle tests; **Checks:** PresentationSecurityTest, BackupPolicyTest, AndroidQrCameraTest; Rust secret-store and editor recovery tests |
|
||||
| Accessible labels/actions, large text, keyboard, responsive layout and localization | Typed Rust state/actions; UI accessibility is Kotlin | `Compose semantics/resources / HingeSafeLayout / HingeSafeAlertDialog` | #105; measured keyboard/TalkBack/large-font/multi-window/RTL/rotation coverage; **Checks:** Tier2WindowTest, HingeWindowTest; hardening.md records manual accessibility and exact performance evidence |
|
||||
| Watch pairing/install state in Settings; TOTP Watch selection, snapshots, receipts, transfer and watchOS detail | mobile_watch; shared bridge's explicit `watch` feature enabled only by crates/apple | Absent from Android records and UI (#91) | Deliberate Watch-only exclusion; no unsupported placeholder controls or Wear implementation |
|
||||
|
||||
Name search matches current mobile behavior; CLI/TUI decrypted grep is not an
|
||||
@@ -49,8 +49,8 @@ an Android workflow invokes them; platform presentation may not replace them.
|
||||
Current iPhone camera adapters pass OS-decoded QR bytes. Android #106 supplies bounded Y-plane/stride/orientation facts through the
|
||||
shared native contract; storage decodes and reassembles the transfer. See
|
||||
[key-transfer.md](key-transfer.md) for interruption/security behavior and evidence.
|
||||
The external-credential matching/grant API required by #109 must be implemented
|
||||
in storage, rather than copying frontend selection assumptions from AutoFill.
|
||||
The external-credential matching/grant API is implemented in `mobile_credentials`
|
||||
and `mobile_authentication`; both Android providers delegate to it.
|
||||
|
||||
## Complete milestone issue-to-layer map
|
||||
|
||||
|
||||
Reference in new issue
Block a user