Skip to content

chore: upgrade to 9.2.3-exodus.0 for RN 0.85#5

Open
raxodus wants to merge 7 commits into
v9.2.3from
exodus-9.2.3
Open

chore: upgrade to 9.2.3-exodus.0 for RN 0.85#5
raxodus wants to merge 7 commits into
v9.2.3from
exodus-9.2.3

Conversation

@raxodus
Copy link
Copy Markdown

@raxodus raxodus commented Apr 23, 2026

Summary

Rebase of Exodus patches onto upstream react-native-keychain v9.2.3 (from old v4.x-based fork).
Part of the RN 0.85 upgrade: ExodusMovement/exodus-mobile#38221.

Exodus Patches

Commit Type Description
bc221b8 chore @exodus/ scoping and version to 9.2.3-exodus.0
abc7e01 feature Remove Facebook Conceal encryption in favor of modern Android Keystore
a5484e0 fix Remove padding from decryptedBytes if left in by the cipher

Upstream Changelog (v4.x → v9.2.3)

The v4.x→v9.x span represents a near-complete rewrite of the library:

  • v5–v6: Added macOS support, removed deprecated kSecAttrAccessibleAlwaysThisDeviceOnly, added getAllGenericPasswordServices, migrated build config.
  • v7: Speed optimisations for Android, improved authenticationPrompt handling.
  • v8: Added visionOS support, full Java→Kotlin rewrite of Android layer, coroutine-based async model, hasGenericPassword API, builder-bob build toolchain replacing the old native module scaffolding.
  • v9.0: Public API cleaned up (TypeScript refactor splitting index into enums/types/normalizeOptions), STORAGE_TYPE.AES renamed to AES_CBC with deprecation shim, hasInternetCredentials signature changed to accept an options object, resetInternetCredentials likewise. SharedPreferences storage backend replaced with androidx.datastore:datastore-preferences:1.1.1 (with automatic SharedPreferences migration). New CipherStorageKeystoreAesGcm added (both biometric-auth and no-auth variants), a Mutex added to serialize all cipher operations, and the DecryptionResultHandler abstraction generalised to ResultHandler (covering both encrypt and decrypt flows). iOS gained cloudSync / iCloud Keychain sync support.
  • v9.1: Strongbox detection fixed (lazy property via PackageManager instead of runtime probe), key-algorithm compatibility check added to extractGeneratedKey, hasCredentialsWithSecClass helper on iOS refactored to deduplicate hasGenericPassword / hasInternetCredentials. Parallel set/get race condition fixed with the Mutex (v9.2.0 / v9.2.1). IllegalBlockSizeException on Android decrypt fixed (v9.2.1). authenticationPrompt default value added for SetOptions (v9.2.2). FacebookConceal entity prefix spacing reverted for backward-compat (v9.2.3).

Security Audit of Upstream Changes

Prototype Pollution Vectors

No prototype pollution vectors found. The JS/TS layer (normalizeOptions.ts, enums.ts, index.ts) uses plain object spread ({ ...options }) with typed inputs. No dynamic property assignment via bracket notation on prototypes. All user-supplied strings are passed to the native layer via typed React Native bridge calls, not used as property keys.

Command Injection

No shell execution in the library itself. The only cp.spawnSync('adb', ...) calls are in the Detox e2e test helpers (KeychainExample/e2e/) and are not part of the shipped library. No Runtime.exec, ProcessBuilder, or NSTask usage was found in any production Android or iOS code.

Network Requests

No outbound network requests. The library is entirely local: it reads and writes to the Android Keystore / DataStore and iOS Keychain. No HttpURLConnection, OkHttp, NSURLSession, or fetch calls are present in any production source file. The only URLs in the diff are comment references to GitHub issues and documentation links.

Binary Blobs / Non-reproducible Artifacts

The android/gradle/wrapper/gradle-wrapper.jar binary is present in the repository (as is standard for Gradle projects). A SHA-256 comparison between v9.0.0 and v9.2.3 shows the jar is identical. No new .aar, .so, .dylib, or other binary blobs were added in the v9.0.0→v9.2.3 range.

ReDoS Patterns

No complex or backtracking regexes added. The only new regex patterns in the diff are simple anchored test-assertion patterns in Detox e2e specs, which are not part of the library runtime. The Kotlin split("/")[1] call on a static transformation string in isKeyAlgorithmSupported is a fixed-input split with no regex.

Unsafe Dynamic Code Execution

No eval, new Function, ClassLoader / forName / getMethod reflection abuse, dlopen/dlsym, or JavaScript WebView eval found. All cipher operations go through the Android Keystore JCA provider and iOS Security framework—no custom crypto implementations.

New Dependencies

Android (build.gradle):

  • androidx.datastore:datastore-preferences:1.1.1 — Google's official replacement for SharedPreferences. Apache 2.0 licensed. No network access; purely local storage. Added to replace SharedPreferences as the encrypted-entry backing store, with automatic migration.

JavaScript/TypeScript: No new runtime dependencies added. devDependencies are dev-only toolchain packages unchanged between versions.

iOS: No new CocoaPods dependencies.

Findings Summary

Category Status Notes
Prototype Pollution ✅ Clear No dynamic property assignment on prototypes
Command Injection ✅ Clear No shell execution in production code
Network Requests ✅ Clear No outbound network calls anywhere
Binary Blobs ✅ Clear gradle-wrapper.jar unchanged; no new binaries
ReDoS Patterns ✅ Clear No complex regexes in production code
Unsafe Dynamic Code Execution ✅ Clear No eval/reflection/dlopen
New Dependencies ✅ Clear Only androidx.datastore:1.1.1 (well-known Google library)

Test Plan

  • Update src/package.json in exodus-mobile-upgrade worktree
  • yarn ios:base builds
  • yarn android:base builds
  • Keychain read/write functional test
  • Verify biometric authentication flow

raxodus added 3 commits April 23, 2026 16:35
Port padding fix to Kotlin implementation. Some devices/ciphers leave
PKCS7 padding in the decrypted bytes, which corrupts the output.
This adds a maybeRemovePKCS7Padding helper that validates and strips
the padding when present.

Original fix: 83a3359
Remove the deprecated Facebook Conceal cipher storage implementation.
Facebook Conceal was archived in March 2020 and is no longer maintained.

Changes:
- Remove CipherStorageFacebookConceal.kt
- Remove conceal dependency from build.gradle
- Remove FB cipher from KnownCiphers annotation and init
- Remove auto-upgrade logic for FB cipher in getGenericPassword
- Remove FB fallback in DataStorePrefsStorage
- Remove FB from STORAGE_TYPE enum in TypeScript
- Update docs and tests to remove all Facebook Conceal references
Scope package to @Exodus namespace and set version to 9.2.3-exodus.0
for use as an Exodus fork dependency.
@raxodus raxodus changed the base branch from v9 to v9.2.3 April 23, 2026 14:59
raxodus and others added 4 commits April 24, 2026 14:45
AES-GCM is a one way street, once it's been rolled out to users, there's no going back. It's potentially a high risk change, let's roll this out in stages. First all the other changes that should be compatible with our older v4.x.x release and then consider re-enabling this.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants