Files
orca/mobile/src
Brennan Benson 7db0101bcb fix(mobile): recover pairing saves from Android SecureStore failures (#11430)
* fix(mobile): recover pairing save when the Android keystore alias is unusable

Orca Mobile could reach a state where pairing succeeded but the host could
never be saved, with every attempt failing identically:

  Could not encrypt the value for key 'orca.host-token.host-...'
  under keychain 'key_v1'. Caused by: unknown

expo-secure-store derives ONE Android keystore alias from the keychain
service (`<service>:unauthenticated`) and shares it across every host token,
so a single unusable alias rejects all writes. Its built-in self-heal only
covers KeyPermanentlyInvalidatedException, and a null-message
GeneralSecurityException takes the unrecoverable branch instead — leaving
onboarding permanently blocked, which a reinstall does not clear.

Route host-token persistence through a keychain generation that rotates to a
fresh service (and therefore a fresh alias) only after a write has already
failed. Generation 0 keeps expo's default service so tokens written by
earlier builds stay readable, reads walk back through retired services, and
deletes clear every generation so a rotation cannot strand a live credential.

Refs #6600

* fix(mobile): record a keychain rotation before storing the token under it

Greptile flagged that a token could be stored under a generation the
generation record never captured. `commitGeneration` swallowed the
AsyncStorage failure and cached the new generation in memory, so the write
succeeded for the rest of the session — but the next launch re-read the old
record, and because reads only walk back from the recorded generation they
never probed the newer service. The host silently vanished and the user had
to re-pair, which is the same class of loss this change set out to fix.

Record the rotation first and let a storage failure propagate, so a token is
never written under a generation reads won't reach. Advancing the record
before the write is safe because reads walk back through every older service;
the worst case is one spent generation and one extra probe per miss.

* fix(mobile): harden pairing keychain recovery

* fix(mobile): harden pairing keychain recovery state

* fix(mobile): fail closed on unreadable pairing credentials
2026-07-30 17:29:41 -07:00
..