Description
react-native-keychain@9.2.3 pulls in com.facebook.conceal:conceal:1.1.3@aar as a hard dependency. That AAR ships a prebuilt libconceal.so (4 KB-aligned, last released 2016, repo archived since 2020). Google Play flagged this .so as non-compliant with Android's 16 KB page-size requirement on the x86_64 ABI. As a workaround, #30590 drops the x86_64 ABI from production AABs entirely, but the same .so still ships for arm64-v8a with 4 KB alignment — eventually a Play blocker on arm64 too.
Key insight: libconceal.so is dead code in our app. react-native-keychain only selects the FacebookConcealCipherStorage backend on devices below API 23. Our minSdkVersion = 24, so every install uses Android Keystore (KeystoreAesGcmCipherStorage). Conceal is shipped but never invoked.
We don't need a data migration — Conceal-encrypted data cannot exist on any device that can install our app. We just need to stop shipping the AAR.
Technical Details
Two viable paths
Option A — Yarn patch the dependency out of 9.2.3 (cheaper, lower risk)
-
Patch node_modules/react-native-keychain/android/build.gradle to remove the line:
implementation "com.facebook.conceal:conceal:1.1.3@aar"
-
Patch react-native-keychain's Kotlin source to either remove the FacebookConcealCipherStorage class entirely or stop registering it with the CipherStorageRegistry. Without this, the module's init will class-load com.facebook.crypto.* and crash with NoClassDefFoundError on every launch.
-
Verify on Android 7+ (API 24+) emulator + real device that keychain operations still work end-to-end: set/get/reset generic password, biometric prompt, vault backup read/write via app/core/BackupVault/backupVault.ts.
-
Optional belt-and-suspenders: emit a Sentry tag with the resolved storage type from getInternetCredentials() for a release before the patch lands, to confirm 0 active users on FacebookConceal. (Should be 0 already given minSdk 24, but cheap insurance.)
Option B — Upgrade to react-native-keychain 10.x (more work, better long-term)
10.x removes the Conceal dependency upstream. It also rewrites the Android side using coroutines + thread-safe cipher caching and supports TurboModules cleanly. Trade-off: more change surface; we have ~10 files that consume the keychain API across app/core/Authentication/, app/core/BackupVault/, app/core/SecureKeychain.ts, app/components/UI/Card/util/cardTokenVault.ts, app/components/UI/Ramp/Deposit/utils/ProviderTokenVault.ts, app/core/Engine/controllers/rewards-controller/utils/multi-subscription-token-vault.ts, app/core/Engine/controllers/card-controller/CardOnboardingStore.ts, plus UI components for biometric toggles.
Important risk to validate when going to 10.x: the codebase relies on exact error-message strings in app/core/Authentication/constants.ts (UNLOCK_WALLET_ERROR_MESSAGES):
ANDROID_PIN_DENIED: 'Error: Cancel',
USER_NOT_AUTHENTICATED: 'User not authenticated',
ANDROID_WRONG_PASSWORD_2: 'error in DoCipher, status: 2', // dead with Conceal gone
These come from the native Android keychain path and 10.x rewrote it (CryptoFailedException wraps AEADBadTagException now). Wrong-password/cancelled-biometric UX needs manual QA on a real device after the upgrade.
Storage format is unchanged across 9.x → 10.x (both use EncryptedSharedPreferences for AES_GCM entries), so vault backups written by 9.x are readable by 10.x.
Recommendation
Land Option A first as a fast, low-risk 16 KB compliance win. Plan Option B as a separate ticket once we have Android device-arch telemetry (see tag/super-property work also being scoped) and a real QA plan for the error-message strings.
Verification
unzip -l app-prod-release.aab | grep libconceal returns empty across all ABIs.
- Manual QA: install on Android 7+ device, create wallet, set biometric/PIN, lock/unlock, write/read vault backup, change biometric enrollment, reinstall + restore.
- No
NoClassDefFoundError for com.facebook.crypto.* in Sentry post-rollout.
Acceptance Criteria
References
Description
react-native-keychain@9.2.3pulls incom.facebook.conceal:conceal:1.1.3@aaras a hard dependency. That AAR ships a prebuiltlibconceal.so(4 KB-aligned, last released 2016, repo archived since 2020). Google Play flagged this.soas non-compliant with Android's 16 KB page-size requirement on the x86_64 ABI. As a workaround, #30590 drops the x86_64 ABI from production AABs entirely, but the same.sostill ships forarm64-v8awith 4 KB alignment — eventually a Play blocker on arm64 too.Key insight:
libconceal.sois dead code in our app.react-native-keychainonly selects theFacebookConcealCipherStoragebackend on devices below API 23. OurminSdkVersion = 24, so every install uses Android Keystore (KeystoreAesGcmCipherStorage). Conceal is shipped but never invoked.We don't need a data migration — Conceal-encrypted data cannot exist on any device that can install our app. We just need to stop shipping the AAR.
Technical Details
Two viable paths
Option A — Yarn patch the dependency out of 9.2.3 (cheaper, lower risk)
Patch
node_modules/react-native-keychain/android/build.gradleto remove the line:implementation "com.facebook.conceal:conceal:1.1.3@aar"Patch
react-native-keychain's Kotlin source to either remove theFacebookConcealCipherStorageclass entirely or stop registering it with theCipherStorageRegistry. Without this, the module'sinitwill class-loadcom.facebook.crypto.*and crash withNoClassDefFoundErroron every launch.Verify on Android 7+ (API 24+) emulator + real device that keychain operations still work end-to-end: set/get/reset generic password, biometric prompt, vault backup read/write via
app/core/BackupVault/backupVault.ts.Optional belt-and-suspenders: emit a Sentry tag with the resolved
storagetype fromgetInternetCredentials()for a release before the patch lands, to confirm 0 active users onFacebookConceal. (Should be 0 already given minSdk 24, but cheap insurance.)Option B — Upgrade to
react-native-keychain10.x (more work, better long-term)10.x removes the Conceal dependency upstream. It also rewrites the Android side using coroutines + thread-safe cipher caching and supports TurboModules cleanly. Trade-off: more change surface; we have ~10 files that consume the keychain API across
app/core/Authentication/,app/core/BackupVault/,app/core/SecureKeychain.ts,app/components/UI/Card/util/cardTokenVault.ts,app/components/UI/Ramp/Deposit/utils/ProviderTokenVault.ts,app/core/Engine/controllers/rewards-controller/utils/multi-subscription-token-vault.ts,app/core/Engine/controllers/card-controller/CardOnboardingStore.ts, plus UI components for biometric toggles.Important risk to validate when going to 10.x: the codebase relies on exact error-message strings in
app/core/Authentication/constants.ts(UNLOCK_WALLET_ERROR_MESSAGES):These come from the native Android keychain path and 10.x rewrote it (
CryptoFailedExceptionwrapsAEADBadTagExceptionnow). Wrong-password/cancelled-biometric UX needs manual QA on a real device after the upgrade.Storage format is unchanged across 9.x → 10.x (both use
EncryptedSharedPreferencesfor AES_GCM entries), so vault backups written by 9.x are readable by 10.x.Recommendation
Land Option A first as a fast, low-risk 16 KB compliance win. Plan Option B as a separate ticket once we have Android device-arch telemetry (see tag/super-property work also being scoped) and a real QA plan for the error-message strings.
Verification
unzip -l app-prod-release.aab | grep libconcealreturns empty across all ABIs.NoClassDefFoundErrorforcom.facebook.crypto.*in Sentry post-rollout.Acceptance Criteria
react-native-keychainno longer transitively pulls incom.facebook.conceal:conceal. Confirm via./gradlew app:dependencies --configuration releaseRuntimeClasspath | grep conceal→ empty.libconceal.sounder any ABI.app/core/BackupVault/backupVault.ts.STORAGE_TYPE.FB/'FacebookConceal'references cleaned up fromapp/core/BackupVault/backupVault.test.tsand any other test mocks.ANDROID_WRONG_PASSWORD_2: 'error in DoCipher, status: 2'constant removed fromapp/core/Authentication/constants.ts.References
node_modules/react-native-keychain/android/build.gradle(line referencingcom.facebook.conceal:conceal:1.1.3@aar).app/core/Authentication/constants.tsapp/core/SecureKeychain.ts,app/core/Authentication/Authentication.ts,app/core/BackupVault/backupVault.ts