131 lines
8.6 KiB
Markdown
131 lines
8.6 KiB
Markdown
# 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](https://support.google.com/chrome/answer/142893?co=GENIE.Platform%3DAndroid&hl=en).
|
|
|
|
## 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](https://developer.android.com/training/package-visibility/declaring).
|
|
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](https://support.google.com/googleplay/android-developer/answer/10158779).
|
|
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](https://developer.android.com/identity/sign-in/credential-provider),
|
|
[Autofill service contract](https://developer.android.com/reference/android/service/autofill/AutofillService)
|
|
and [inline suggestion contract](https://developer.android.com/identity/autofill/ime-autofill).
|
|
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.
|