feat(android): implement private Rust storage runtime (#90)
Dependency security audit / rustsec (push) Failing after 23s
Dependency security audit / rustsec (push) Failing after 23s
This commit is contained in:
1 parent
180851087b
commit
874c8139a4
36 files changed
+2848
-39
No files matched your search
@@ -31,8 +31,10 @@ selected variants belong to the native UI. Actions already use typed enums.
|
||||
|
||||
- `MobilePlatformPaths` accepts absolute private data and cache roots, rejects
|
||||
traversal and overlapping roots, and lets storage derive config/vault/key
|
||||
paths. Validation does not create or migrate files. #90 owns initialization,
|
||||
permissions, relocation, backup and cache policy.
|
||||
paths. This lightweight validator does not create or migrate files. #90's
|
||||
`MobilePrivateRuntime` opens the three canonical OS directory capabilities,
|
||||
activates the shared configuration context, and owns initialization,
|
||||
permissions, relocation, backup and cache policy; see [private storage](private-storage.md).
|
||||
- `MobileSecretBackend` performs create/retrieve/replace/delete/lock/unlock on
|
||||
a typed locator and protection supplied by storage. Values are opaque encoded
|
||||
storage records. OS errors are enums. It cannot select accounts, protection,
|
||||
|
||||
@@ -6,10 +6,12 @@ apple/Sources/AutoFill/CredentialProviderViewController.swift, crates/apple/src/
|
||||
and crates/storage/src/mobile*.rs. See [architecture.md](architecture.md) for
|
||||
support, dependency, security and update policy.
|
||||
|
||||
**Status:** all Android implementation rows below are pending. No Android app
|
||||
exists at this decision. The table is a complete assignment of work, not a
|
||||
claim of completed parity. #111 must attach screen/component/test evidence to
|
||||
each row. Every issue linked here belongs to milestone 23.
|
||||
**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.
|
||||
|
||||
## Current iPhone and shared-mobile capabilities
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# Android private storage (#90)
|
||||
|
||||
The platform supplies canonical `Context.filesDir`, `noBackupFilesDir` and
|
||||
`cacheDir` facts off the UI thread. `PrivateStorage` validates existing, distinct
|
||||
roots and opens directory capabilities. It derives all subordinate paths;
|
||||
Kotlin never lists, reads or mutates repository contents. `activate()` installs
|
||||
one immutable process path context so existing Rust configuration/onboarding,
|
||||
Git, key-transfer and TOTP APIs use these roots. Android calls made before
|
||||
activation fail instead of consulting HOME, cwd, XDG or public storage.
|
||||
|
||||
| Object | Placement | Update / cleanup policy |
|
||||
| --- | --- | --- |
|
||||
| pass-format repository | `noBackupFilesDir/ironstorage/vault` | Retained; existing storage/Git atomic mutation rules |
|
||||
| GPG key material | `noBackupFilesDir/ironstorage/keys` | Retained; Rust crypto import/export only |
|
||||
| secret-free configuration | `noBackupFilesDir/ironstorage/config.toml` | Atomic Rust writes; relative vault/key paths survive relocation |
|
||||
| TOTP discovery catalog | `noBackupFilesDir/ironstorage/totp-catalog.toml` | Valid catalogs survive updates and relocation; invalid/oversized/wrong-store catalogs are removed and rebuilt by discovery |
|
||||
| version / device binding | `noBackupFilesDir/ironstorage/layout.toml` | Atomic version 1 marker; unknown versions require a compatible migration |
|
||||
| transactional scratch | `noBackupFilesDir/ironstorage/scratch` | Per-owner file locks; startup removes orphaned transactions and skips live owners |
|
||||
| disposable cache | `cacheDir/ironstorage` | Only this capability is cleared by `clearDisposableCache`; vault/key/catalog data is retained |
|
||||
| protected binding and credentials | OS secure backend / no-backup ciphertext (#93) | No serialized token or passphrase in configuration or layout metadata |
|
||||
| `filesDir` | OS root fact only | No durable password-store object is placed here |
|
||||
|
||||
Directory opens use safe Rust `rustix` handles with `NOFOLLOW`; names are single
|
||||
components. Existing vault/key trees reject links and special files. Directories
|
||||
are 0700; files are 0600, preserving an existing owner executable bit. These are
|
||||
Unix permission checks inside the Android UID/SELinux sandbox, not promises
|
||||
about physical media erasure or an untrusted OS. No application process or
|
||||
handwritten unsafe FFI is introduced.
|
||||
|
||||
Configuration must refer to `vault` and key material below `keys`. A corrupt or
|
||||
missing configuration with remaining data produces typed recovery, never a new
|
||||
store or automatic repository deletion. Interrupted `.local-setup-*`
|
||||
transactions are retained because they can contain the previous repository.
|
||||
Owned orphan `.ironstorage-config-*` and `.ironstorage-atomic-*` files are removed only after taking their
|
||||
file lock; active atomic writers are left alone. A directory gate protects the
|
||||
creation-to-owner-lock interval from concurrent startup cleanup. Android atomic files use
|
||||
private named capabilities and rename because SELinux denies anonymous-file
|
||||
linking via `/proc`; safe `rustix` locks supply the semantics absent from the
|
||||
pinned Rust standard library on Android. Missing layout metadata with existing
|
||||
data is a recovery error, so a device binding cannot be silently discarded. Scratch/cache cleanup unlinks
|
||||
links without following their targets and cannot descend into the vault.
|
||||
|
||||
`verifySecureStorage` uses the OS-mechanics callback from #91 and a storage-owned
|
||||
`ApplicationBinding` locator. Rust creates an opaque 32-byte device record and
|
||||
persists only its SHA-256 in layout metadata. A missing or mismatched protected
|
||||
record for an already bound layout reports `RestoredWithoutKeystore` or
|
||||
`InvalidSecureBinding`; it never creates a replacement that would conceal key
|
||||
loss. Locked/unavailable storage has separate recovery kinds. #93 supplies the
|
||||
real Android Keystore adapter; the contract already executes through the
|
||||
injected backend and is tested with loss/mismatch fixtures.
|
||||
|
||||
Errors distinguish full storage, read-only storage, access denial, missing
|
||||
roots, concurrent initialization, corrupt configuration/layout and unsupported
|
||||
layout versions. Failed initialization or writes leave existing repository,
|
||||
key and configuration data intact. Free space or repair the reported condition
|
||||
and retry; destructive reset is not an update/recovery operation.
|
||||
|
||||
Normal APK/Play updates and process recreation retain no-backup app data.
|
||||
Uninstall deletes the sandbox and Keystore entries; recovery then requires the
|
||||
upstream Git repository and GPG key/passphrase or an explicit encrypted export.
|
||||
Backup/transfer is not an alternative recovery path. The manifest disables
|
||||
backup, and both legacy backup rules and cloud/device-transfer rules exclude
|
||||
all nine credential- and device-protected domains. No-backup and cache roots
|
||||
are intrinsically excluded by Android. See the [Android backup contract](https://developer.android.com/identity/data/autobackup).
|
||||
|
||||
Validation includes real GPG fixture byte preservation through updates and
|
||||
directory relocation, valid/corrupt TOTP catalogs, interrupted writes/setup,
|
||||
concurrent scratch ownership, link-safe cleanup, device-binding loss/mismatch,
|
||||
and typed storage-pressure/read-only I/O faults. A separate integration test
|
||||
activates isolated roots and uses existing local onboarding to create an actual
|
||||
Git repository and protected GPG key without environment/path overrides.
|
||||
Instrumented tests inspect installed backup XML/manifest resources and execute
|
||||
private initialization/recreation/cache clearing on persistent 4 KB and strict
|
||||
16 KB Android emulators. No physical-device testing is required.
|
||||
|
||||
Delivery verification on 2026-10-08: all four required workspace gates passed
|
||||
(431 Rust tests), Android ktlint/unit/lint gates passed, and each persistent
|
||||
API 37 emulator (4 KB and strict 16 KB) passed eight native tests and two app
|
||||
tests covering all 25 shell state/tab combinations and installed backup policy.
|
||||
The native fixture creates a real protected GPG key and Git repository through
|
||||
Rust, persists a preference atomically, and survives subsequent APK/test-process
|
||||
updates. Debug and release install-over/startup tests preserve the layout on both
|
||||
emulators. All three ABI payloads pass the APK/AAB native audit. Swift private
|
||||
runtime/redaction tests and 588 shared contract entries pass; iPhone, Watch and
|
||||
AutoFill simulator builds pass. The persistent iPhone app also launches with its
|
||||
existing store intact. Backup verification reads installed policy and package
|
||||
flags; it does not export sandbox data to a transport.
|
||||
Reference in new issue
Block a user