Skip to content

tech-debt(android): drop Facebook Conceal dependency from react-native-keychain to remove libconceal.so #30592

@andrepimenta

Description

@andrepimenta

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)

  1. Patch node_modules/react-native-keychain/android/build.gradle to remove the line:

    implementation "com.facebook.conceal:conceal:1.1.3@aar"
  2. 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.

  3. 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.

  4. 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

  • react-native-keychain no longer transitively pulls in com.facebook.conceal:conceal. Confirm via ./gradlew app:dependencies --configuration releaseRuntimeClasspath | grep conceal → empty.
  • Production AAB contains no libconceal.so under any ABI.
  • All keychain-dependent flows still work on Android 7+ (API 24+):
    • Wallet creation and unlock (password, biometric, PIN).
    • Vault backup write/read via app/core/BackupVault/backupVault.ts.
    • Card token vault, deposit provider token vault, rewards multi-subscription vault.
    • Settings → biometric toggle on/off cycle.
  • Wrong-password / cancelled-biometric UX still surfaces the right error strings (Option B only — Option A doesn't change error paths).
  • Dead STORAGE_TYPE.FB / 'FacebookConceal' references cleaned up from app/core/BackupVault/backupVault.test.ts and any other test mocks.
  • Dead ANDROID_WRONG_PASSWORD_2: 'error in DoCipher, status: 2' constant removed from app/core/Authentication/constants.ts.
  • CHANGELOG entry added.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    INVALID-ISSUE-TEMPLATEIssue's body doesn't match any issue template.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions