MetaMask / MetaMask/metamask-mobile

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

Open
#30,592 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

INVALID-ISSUE-TEMPLATE
Dominant language
TypeScript
Stars
3k
Forks
1.7k
Avg merge
1d 14h
Merged PRs (30d)
669

Description

## **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, https://github.com/MetaMask/metamask-mobile/pull/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:

```groovy
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`):

```ts
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**

- Related PR (workaround that drops x86_64 ABI): https://github.com/MetaMask/metamask-mobile/pull/30590
- Conceal dependency declaration: `node_modules/react-native-keychain/android/build.gradle` (line referencing `com.facebook.conceal:conceal:1.1.3@aar`).
- Conceal upstream (archived): https://github.com/facebook/conceal
- react-native-keychain v10 release notes (Conceal removal): https://github.com/oblador/react-native-keychain/releases/tag/v10.0.0
- Android 16 KB page sizes: https://developer.android.com/guide/practices/page-sizes
- App-level error-string coupling: `app/core/Authentication/constants.ts`
- Top-level consumers: `app/core/SecureKeychain.ts`, `app/core/Authentication/Authentication.ts`, `app/core/BackupVault/backupVault.ts`

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the dependency declaration in react-native-keychain/android/build.gradle and the keychain consumers in app/core/SecureKeychain.ts, app/core/BackupVault/backupVault.ts, and app/core/Authentication/constants.ts. Check the release dependency tree and inspect backupVault.test.ts for Facebook Conceal references. Done means no conceal dependency or libconceal.so in the AAB, updated tests and changelog, and verified keychain flows on Android 7+.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin, react-native, typescript
Domain
build-system, mobile, security
Issue type
Refactor
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.