8.6 KiB
Android password providers
IronStorage supplies password credentials through AndroidX Credential Provider on
Android 14 and newer and through framework Autofill on Android 8 and newer. Both
use the same NativeCredentialRepository facade and storage-owned request,
matching, search, authentication, credential-field and save APIs. Services and
selection run inside the private application UID and share one Rust owner;
requesting applications run outside that boundary. There is no second secret
cache or service-specific authentication lease.
Enable IronStorage in its Preferences → Autofill and Credential Manager providers section. The two status rows open Android's provider settings. For current Chrome, select the provider in Android settings, then open Chrome Settings → Autofill and passwords → Autofill settings, choose Autofill using another service, and restart Chrome. See Chrome's instructions.
Requests and identity
Candidate responses contain entry paths only. Rust ranks exact path components matching the verified application package or website host. Manual search is also Rust-owned and returns up to 100 matching paths; refine the search to find another entry. Listing candidates never decrypts an entry or restores a passphrase.
The OS adapter supplies installed package signing fingerprints.
The Android 11+ package-visibility permission is required to recheck the current
signers of arbitrary requesting apps, including clients with no launcher intent.
IronStorage queries only the package supplied by the OS request and never
enumerates installed apps. See Android's package visibility contract.
The manifest suppresses only the lint recommendation to replace this permission
with static queries: arbitrary OS-mediated clients have no common discoverable
intent filter. A Google Play release must declare and justify this use under
Play's package visibility policy.
Credential Manager's supplied signing identity must agree with PackageManager. Rust's
reviewed release-signature list is passed to AndroidX CallingAppInfo.getOrigin;
Rust then validates and canonicalizes the verified HTTPS origin. The list covers
release Chrome and Firefox. Unsupported or untrusted origin claims remain bound
to the verified application identity. Framework Autofill never treats raw
AssistStructure webDomain assertions as a website permission.
Kotlin translates AssistStructure identifiers, hints, HTML attributes and input facts. Rust chooses exactly one username/email and one password role and rejects ambiguous roles, duplicate identifiers and oversized facts. It uses the lossless pass document's typed username/email/password fields after explicit selection; it rejects ambiguous entries and filters any client-specified account constraint.
Requests have random 256-bit, process-local identifiers, a two-minute monotonic deadline, an exact caller binding and the repository mutation revision. At most 16 Rust requests are outstanding. The final secret read checks identity, revision, deadline and the shared authentication lease again and consumes the request. Cancellation, relock, device lock, memory pressure and process recreation revoke requests. Secret reads and metadata refreshes do not renew the lease.
The selection activity is not exported. PendingIntents have an explicit component, a fixed action and unique immutable base data; mutability exists only so Android can attach its complete request. The activity verifies that request against its original caller and form. It uses secure-window and recents protections, rejects obscured touches and clears its passphrase when focus or foreground ownership is lost. It revokes the lease after successful handoff as well as cancellation.
Filling and saving
Users select an entry, explicitly consent to the named recipient receiving its username and password, and authenticate with a valid lease, biometrics or the GPG passphrase. Only then does Rust release the selected fields to the OS response. Autofill supplies an authentication suggestion, including an inline presentation when the IME advertises the supported AndroidX style. Its attribution PendingIntent is immutable and opens the ordinary app; the private authentication PendingIntent is not exposed as IME attribution. Responses contain no other entry fields. Credentials are never written to logs, saved activity state, notifications or an application dataset cache. Android 12+ authentication results use the framework's ephemeral-dataset flag. Older Android versions retain the handed-off values in their current framework autofill session; IronStorage does not keep a dataset cache.
Save offers use the same request binding. Submitted values are immediately sealed with the installed encryption-capable private-key identities and retained in bounded process memory only. They expire with the request. Users choose an entry path, confirm saving and separately authorize updating an existing destination. Rust authenticates the chosen destination's recipient policy, including a different key than the default, and validates the repository and authentication again, preserves unrelated fields, rejects an open editor of the target and uses the recoverable structured-editor/Git transaction. Its commit guard refuses cancelled, expired or relocked requests. There is no silent capture and no competing password-store format. An explicitly confirmed update containing identical fields succeeds without rewriting the ciphertext, after rechecking the original destination and request.
Verification
Rust tests use real encrypted stores to cover selection, missing consent, missing lease, account constraints, one-use consumption, cancellation, changed signatures, untrusted and invalid origins, stale repositories, expiry, ambiguous forms and explicit create/update preservation. Native instrumentation exercises the same production facade with real GPG and repository transactions on API 26 ARM64 and strict 16-KB API 37 ARM64.
android/provider-client builds a standalone fixture APK with synthetic fields and actual
Credential Manager client calls. Its public HTML asset can be served on host loopback port 8765 and reached with emulator adb reverse tcp:8765 tcp:8765; the form verifies synthetic values locally and submits nothing.
The opt-in ProviderFixtureSetupTest seeds an
unconfigured retained emulator through production onboarding and refuses to
replace existing keys or a configured store. Its debug-only Hilt access hook is
absent from release builds. The fixture passphrase and credentials are public
synthetic values in the test source, not user credentials.
Computer-use validation on the retained strict 16-KB API 37 ARM64 emulator confirmed real Credential Manager and native framework Autofill fills, plus a Chrome fill whose local form checked both exact synthetic values. Chrome 145.0.7632.218 was used as explicitly requested by the user; no browser update was installed. On this fixture Chrome offered Autofill when its password field was focused. Before authentication, only the unlock/search suggestion and entry paths were shown. Every handoff relocked the provider, and interrupted requests expired without filling.
Credential Manager also saved fixture-bob at an explicitly selected new
synthetic destination after consent and authentication. The opt-in
providerSavedFixtureValidation=true instrumentation read the resulting username
and password through the production Rust credential facade and verified both
submitted fields.
Framework Autofill's actual system save offer also created a second explicitly
selected destination; the same readback test verified both submitted fields there.
Overwrite without its separate confirmation was rejected; a confirmed identical
update completed successfully through the actual system dialog.
The separate providerFixtureValidation=true regression checks
installed caller visibility and verifies that normal UI/cache trim callbacks
preserve locked metadata requests while genuine memory pressure revokes them.
The integration follows the official Credential Provider guide, Autofill service contract and inline suggestion contract. AndroidX Credentials 1.6.0 and Autofill 1.3.0 are pinned with locks, checksums and Apache-2.0 license inventory entries. Emulator evidence is ARM-only; no physical or x86_64 runtime coverage is claimed.