From a5484e0f09647547c1d8057b5d9dddf72bb82440 Mon Sep 17 00:00:00 2001 From: raxodus <251626932+raxodus@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:04:55 +0200 Subject: [PATCH 1/7] fix: remove padding from decryptedBytes if left in by the cipher 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: 83a33596d0238a7c93b11175b2f5b10d209382ad --- .../CipherStorageKeystoreAesCbc.kt | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAesCbc.kt b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAesCbc.kt index 99ae741e..6f16a87c 100644 --- a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAesCbc.kt +++ b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAesCbc.kt @@ -221,7 +221,12 @@ class CipherStorageKeystoreAesCbc(reactContext: ReactApplicationContext) : cipher.init(Cipher.DECRYPT_MODE, key, spec) // Decrypt the bytes using cipher.doFinal() - val decryptedBytes = cipher.doFinal(bytes, IV.IV_LENGTH, bytes.size - IV.IV_LENGTH) + // Using a CipherInputStream for decryption has historically led to issues on the Pixel family of devices + // see https://github.com/oblador/react-native-keychain/issues/383 + val _decryptedBytes = cipher.doFinal(bytes, IV.IV_LENGTH, bytes.size - IV.IV_LENGTH) + + val decryptedBytes = maybeRemovePKCS7Padding(_decryptedBytes, IV.IV_LENGTH) + String(decryptedBytes, UTF8) } catch (fail: Throwable) { Log.w(LOG_TAG, fail.message, fail) @@ -270,4 +275,22 @@ class CipherStorageKeystoreAesCbc(reactContext: ReactApplicationContext) : decryptBytes(key, bytes, IV.decrypt) // endregion + + private fun maybeRemovePKCS7Padding(paddedBytes: ByteArray, maxPaddingLength: Int): ByteArray { + val paddingLength = paddedBytes.last().toInt() + + // Validate the padding + if (paddingLength < 1 || paddingLength > paddedBytes.size || paddingLength > maxPaddingLength) { + return paddedBytes + } + + for (i in paddedBytes.size - paddingLength until paddedBytes.size) { + if (paddedBytes[i] != paddingLength.toByte()) { + return paddedBytes + } + } + + // Remove the padding + return paddedBytes.copyOfRange(0, paddedBytes.size - paddingLength) + } } From abc7e0138d1f89b993220f015d19cd0ee3845d03 Mon Sep 17 00:00:00 2001 From: raxodus <251626932+raxodus@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:07:16 +0200 Subject: [PATCH 2/7] feat: remove facebook conceal 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 --- .../e2e/testCases/storageTypesTest.spec.js | 1 - android/build.gradle | 4 - .../oblador/keychain/DataStorePrefsStorage.kt | 7 +- .../com/oblador/keychain/KeychainModule.kt | 19 +- .../CipherStorageFacebookConceal.kt | 178 ------------------ src/enums.ts | 12 +- website/docs/choosing-storage-type.md | 5 - website/docs/faq.md | 14 +- website/docs/index.md | 4 +- website/docs/jest.md | 1 - website/docs/usage.md | 1 - 11 files changed, 8 insertions(+), 238 deletions(-) delete mode 100644 android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageFacebookConceal.kt diff --git a/KeychainExample/e2e/testCases/storageTypesTest.spec.js b/KeychainExample/e2e/testCases/storageTypesTest.spec.js index 736d1d7b..3c38e8cc 100644 --- a/KeychainExample/e2e/testCases/storageTypesTest.spec.js +++ b/KeychainExample/e2e/testCases/storageTypesTest.spec.js @@ -29,7 +29,6 @@ describe(':android:Storage Types', () => { await matchLoadInfo( 'testUsernameFB', 'testPasswordFB', - 'FacebookConceal', type === 'internetCredentials' ? 'https://example.com' : undefined ); await element(by.text('Automatic upgrade')).tap(); diff --git a/android/build.gradle b/android/build.gradle index 4f2ab553..3d9985d2 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -79,10 +79,6 @@ dependencies { // Needed for BiometricPrompt in androidx.biometric implementation "androidx.fragment:fragment:1.3.2@aar" - /* version higher 1.1.3 has problems with included soloader packages, - https://github.com/facebook/conceal/releases */ - implementation "com.facebook.conceal:conceal:1.1.3@aar" - // Used to store encrypted data implementation("androidx.datastore:datastore-preferences:1.1.1") } diff --git a/android/src/main/java/com/oblador/keychain/DataStorePrefsStorage.kt b/android/src/main/java/com/oblador/keychain/DataStorePrefsStorage.kt index 7227ba18..06164b76 100644 --- a/android/src/main/java/com/oblador/keychain/DataStorePrefsStorage.kt +++ b/android/src/main/java/com/oblador/keychain/DataStorePrefsStorage.kt @@ -45,12 +45,7 @@ class DataStorePrefsStorage( var cipherStorageName = getCipherStorageName(service) // in case of wrong password or username - if (bytesForUsername == null || bytesForPassword == null) return null - if (cipherStorageName == null) { - // If the CipherStorage name is not found, we assume it is because the entry was written by an - // older version of this library which used Facebook Conceal, so we default to that. - cipherStorageName = KnownCiphers.FB - } + if (bytesForUsername == null || bytesForPassword == null || cipherStorageName == null) return null return ResultSet(cipherStorageName, bytesForUsername, bytesForPassword) } diff --git a/android/src/main/java/com/oblador/keychain/KeychainModule.kt b/android/src/main/java/com/oblador/keychain/KeychainModule.kt index 7a2c6793..be5eb615 100644 --- a/android/src/main/java/com/oblador/keychain/KeychainModule.kt +++ b/android/src/main/java/com/oblador/keychain/KeychainModule.kt @@ -16,7 +16,6 @@ import com.facebook.react.module.annotations.ReactModule import com.oblador.keychain.cipherStorage.CipherStorage import com.oblador.keychain.cipherStorage.CipherStorage.DecryptionResult import com.oblador.keychain.cipherStorage.CipherStorageBase -import com.oblador.keychain.cipherStorage.CipherStorageFacebookConceal import com.oblador.keychain.cipherStorage.CipherStorageKeystoreAesCbc import com.oblador.keychain.cipherStorage.CipherStorageKeystoreAesGcm import com.oblador.keychain.cipherStorage.CipherStorageKeystoreRsaEcb @@ -103,12 +102,9 @@ class KeychainModule(reactContext: ReactApplicationContext) : } /** Supported ciphers. */ - @StringDef(KnownCiphers.FB, KnownCiphers.AES_CBC, KnownCiphers.AES_GCM, KnownCiphers.RSA) + @StringDef(KnownCiphers.AES_CBC, KnownCiphers.AES_GCM, KnownCiphers.RSA) annotation class KnownCiphers { companion object { - /** Facebook conceal compatibility lib in use. */ - const val FB = "FacebookConceal" - /** AES CBC encryption. */ const val AES_CBC = "KeystoreAESCBC" @@ -151,7 +147,6 @@ class KeychainModule(reactContext: ReactApplicationContext) : /** Default constructor. */ init { prefsStorage = DataStorePrefsStorage(reactContext, coroutineScope) - addCipherStorageToMap(CipherStorageFacebookConceal(reactContext)) addCipherStorageToMap(CipherStorageKeystoreAesCbc(reactContext)) addCipherStorageToMap(CipherStorageKeystoreAesGcm(reactContext, false)) addCipherStorageToMap(CipherStorageKeystoreAesGcm(reactContext, true)) @@ -289,17 +284,7 @@ class KeychainModule(reactContext: ReactApplicationContext) : val promptInfo = getPromptInfo(options) var cipher: CipherStorage? = null - // Only check for upgradable ciphers for FacebookConseal as that - // is the only cipher that can be upgraded - cipher = - if (rules == Rules.AUTOMATIC_UPGRADE && storageName == KnownCiphers.FB) { - // get the best storage - val accessControl = getAccessControlOrDefault(options) - val useBiometry = getUseBiometry(accessControl) - getCipherStorageForCurrentAPILevel(useBiometry) - } else { - getCipherStorageByName(storageName) - } + cipher = getCipherStorageByName(storageName) val decryptionResult = decryptCredentials(alias, cipher!!, resultSet, rules, promptInfo) val credentials = Arguments.createMap() credentials.putString(Maps.SERVICE, alias) diff --git a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageFacebookConceal.kt b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageFacebookConceal.kt deleted file mode 100644 index ce419736..00000000 --- a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageFacebookConceal.kt +++ /dev/null @@ -1,178 +0,0 @@ -package com.oblador.keychain.cipherStorage - -import android.os.Build -import android.security.keystore.KeyGenParameterSpec -import android.security.keystore.KeyInfo -import android.util.Log -import com.facebook.android.crypto.keychain.AndroidConceal -import com.facebook.android.crypto.keychain.SharedPrefsBackedKeyChain -import com.facebook.crypto.Crypto -import com.facebook.crypto.CryptoConfig -import com.facebook.crypto.Entity -import com.facebook.react.bridge.AssertionException -import com.facebook.react.bridge.ReactApplicationContext -import com.oblador.keychain.KeychainModule.KnownCiphers -import com.oblador.keychain.SecurityLevel -import com.oblador.keychain.resultHandler.ResultHandler -import com.oblador.keychain.exceptions.CryptoFailedException -import java.security.GeneralSecurityException -import java.security.Key - -/** - * @see [Conceal Project](https://github.com/facebook/conceal) - * @see - * [Fast Cryptographics](https://medium.com/@ssaurel/make-fast-cryptographic-operations-on-android-with-conceal-77a751e89b8e) - */ -@Suppress("unused", "MemberVisibilityCanBePrivate") -class CipherStorageFacebookConceal(reactContext: ReactApplicationContext) : - CipherStorageBase(reactContext) { - - companion object { - const val KEYCHAIN_DATA = "RN_KEYCHAIN" - } - - private val crypto: Crypto - - init { - val keyChain = SharedPrefsBackedKeyChain(reactContext, CryptoConfig.KEY_256) - crypto = AndroidConceal.get().createDefaultCrypto(keyChain) - } - - // region Configuration - override fun getCipherStorageName(): String = KnownCiphers.FB - - override fun getMinSupportedApiLevel(): Int = Build.VERSION_CODES.JELLY_BEAN - - override fun securityLevel(): SecurityLevel = SecurityLevel.ANY - - override fun supportsSecureHardware(): Boolean = false - - override fun isBiometrySupported(): Boolean = false - - // endregion - - // region Overrides - - @Throws(CryptoFailedException::class) - override fun encrypt( - handler: ResultHandler, - alias: String, - username: String, - password: String, - level: SecurityLevel - ) { - - throwIfInsufficientLevel(level) - throwIfNoCryptoAvailable() - - val usernameEntity = createUsernameEntity(alias) - val passwordEntity = createPasswordEntity(alias) - - try { - val encryptedUsername = crypto.encrypt(username.toByteArray(UTF8), usernameEntity) - val encryptedPassword = crypto.encrypt(password.toByteArray(UTF8), passwordEntity) - - val result = CipherStorage.EncryptionResult(encryptedUsername, encryptedPassword, this) - handler.onEncrypt(result, null) - } catch (fail: Throwable) { - throw CryptoFailedException("Encryption failed for alias: $alias", fail) - } - } - - /** Redirect call to default [decrypt] method. */ - override fun decrypt( - handler: ResultHandler, - alias: String, - username: ByteArray, - password: ByteArray, - level: SecurityLevel - ) { - throwIfInsufficientLevel(level) - throwIfNoCryptoAvailable() - - val usernameEntity = createUsernameEntity(alias) - val passwordEntity = createPasswordEntity(alias) - - try { - val decryptedUsername = crypto.decrypt(username, usernameEntity) - val decryptedPassword = crypto.decrypt(password, passwordEntity) - - val results = CipherStorage.DecryptionResult( - String(decryptedUsername, UTF8), String(decryptedPassword, UTF8), SecurityLevel.ANY - ) - handler.onDecrypt(results, null) - } catch (fail: Throwable) { - handler.onDecrypt(null, fail) - } - } - - override fun removeKey(alias: String) { - // Facebook Conceal stores only one key across all services, so we cannot - // delete the key (otherwise decryption will fail for encrypted data of other services). - Log.w(LOG_TAG, "CipherStorageFacebookConceal removeKey called. alias: $alias") - } - - - @Throws(GeneralSecurityException::class) - override fun getKeyGenSpecBuilder(alias: String): KeyGenParameterSpec.Builder { - throw CryptoFailedException("Not designed for a call") - } - - - @Throws(GeneralSecurityException::class) - override fun getKeyGenSpecBuilder( - alias: String, - isForTesting: Boolean - ): KeyGenParameterSpec.Builder { - throw CryptoFailedException("Not designed for a call") - } - - - @Throws(GeneralSecurityException::class) - override fun getKeyInfo(key: Key): KeyInfo { - throw CryptoFailedException("Not designed for a call") - } - - - @Throws(GeneralSecurityException::class) - override fun generateKey(spec: KeyGenParameterSpec): Key { - throw CryptoFailedException("Not designed for a call") - } - - - override fun getEncryptionAlgorithm(): String { - throw AssertionException("Not designed for a call") - } - - - override fun getEncryptionTransformation(): String { - throw AssertionException("Not designed for a call") - } - - /** Verify availability of the Crypto API. */ - @Throws(CryptoFailedException::class) - private fun throwIfNoCryptoAvailable() { - if (!crypto.isAvailable) { - throw CryptoFailedException("Crypto is missing") - } - } - - // endregion - - // region Helper methods - - private fun createUsernameEntity(alias: String): Entity { - val prefix = getEntityPrefix(alias) - return Entity.create(prefix + "user") - } - - - private fun createPasswordEntity(alias: String): Entity { - val prefix = getEntityPrefix(alias) - return Entity.create(prefix + "pass") - } - - - private fun getEntityPrefix(alias: String): String = "$KEYCHAIN_DATA:$alias" - // endregion -} diff --git a/src/enums.ts b/src/enums.ts index 39bd4157..71c9d549 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -63,7 +63,7 @@ export enum SECURITY_LEVEL { * */ SECURE_HARDWARE = RNKeychainManager && RNKeychainManager.SECURITY_LEVEL_SECURE_HARDWARE, - /** No security guarantees needed (default value). Credentials can be stored in FB Secure Storage. */ + /** No security guarantees needed (default value). */ ANY = RNKeychainManager && RNKeychainManager.SECURITY_LEVEL_ANY, } @@ -109,17 +109,9 @@ export enum BIOMETRY_TYPE { * 2. Medium Security (No Authentication): * - AES_GCM_NO_AUTH: For app-level secrets and cached data * - * 3. Legacy/Deprecated: - * - AES_CBC: Outdated, use AES_GCM_NO_AUTH instead - * - FB: Archived Facebook Conceal implementation - * * @platform Android */ export enum STORAGE_TYPE { - /** Facebook compatibility cipher. - * @deprecated Facebook Conceal was deprecated and archived in Mar 3, 2020. https://github.com/facebookarchive/conceal - */ - FB = 'FacebookConceal', /** Encryptions without human interaction. * @deprecated Use AES_GCM_NO_AUTH instead. */ @@ -154,6 +146,6 @@ export enum STORAGE_TYPE { export enum SECURITY_RULES { /** No special security rules applied. */ NONE = 'none', - /** Upgrade secret to the best available storage as soon as it is available and user request secret extraction. Upgrade not applied till we request the secret. This rule only applies to secrets stored with FacebookConseal. */ + /** Upgrade secret to the best available storage as soon as it is available and user request secret extraction. Upgrade not applied till we request the secret. */ AUTOMATIC_UPGRADE = 'automaticUpgradeToMoreSecuredStorage', } diff --git a/website/docs/choosing-storage-type.md b/website/docs/choosing-storage-type.md index c5fa0983..e0c96278 100644 --- a/website/docs/choosing-storage-type.md +++ b/website/docs/choosing-storage-type.md @@ -20,11 +20,6 @@ We offer three security levels for data storage: - **AES_GCM_NO_AUTH**: Symmetric encryption without biometric requirements - Best for: Cached data, non-sensitive encrypted data -### 3. Legacy/Deprecated - -- **AES_CBC**, **FB** (Facebook Conceal) -- ⚠️ Not recommended for new implementations - ## Storage Type Selection Guide ### Use AES_GCM (High Security) for: diff --git a/website/docs/faq.md b/website/docs/faq.md index 695a710b..5413632b 100644 --- a/website/docs/faq.md +++ b/website/docs/faq.md @@ -5,7 +5,7 @@ title: Frequently Asked Questions **Q: How does the library handle encryption when storing secrets, and can it upgrade the encryption?** -**A:** The library automatically applies the highest possible encryption when storing secrets. However, once a secret is stored, it does not attempt to upgrade the encryption unless **Facebook Conceal** was used and the `SECURITY_RULES` option is set to `AUTOMATIC_UPGRADE`. +**A:** The library automatically applies the highest possible encryption when storing secrets. However, once a secret is stored, it does not attempt to upgrade the encryption. --- @@ -23,18 +23,6 @@ title: Frequently Asked Questions --- -**Q: How do I enable automatic upgrades for Facebook Conceal?** - -**A:** Use the following call: - -```tsx -getGenericPassword({ ...otherProps, rules: "AUTOMATIC_UPGRADE" }); -``` - -Ensure the `rules` property is set to the string value `AUTOMATIC_UPGRADE`. - ---- - **Q: How do I force a specific level of encryption when saving a secret?** **A:** To force a specific encryption level, call: diff --git a/website/docs/index.md b/website/docs/index.md index 06b49608..21046c10 100644 --- a/website/docs/index.md +++ b/website/docs/index.md @@ -15,12 +15,12 @@ This library supports various security features such as biometric authentication ## Support This library supports both iOS and Android platforms. Additionally, it has support for macOS Catalyst and visionOS. -For iOS, the library uses the Keychain Services API, while on Android, it uses Facebook Conceal or the Android Keystore depending on the API level. +For iOS, the library uses the Keychain Services API, while on Android, it uses the Android Keystore. Supported platforms and versions: - **iOS**: Requires iOS 9.0+ -- **Android**: API 16+ (uses Facebook Conceal for API levels 16-22, Android Keystore for API 23+) +- **Android**: API 23+ - **macOS Catalyst**: Supported - **visionOS**: Supported diff --git a/website/docs/jest.md b/website/docs/jest.md index e2d814c3..05a51e32 100644 --- a/website/docs/jest.md +++ b/website/docs/jest.md @@ -40,7 +40,6 @@ const keychainMock = { BIOMETRICS: 'MOCK_AuthenticationWithBiometrics', }, STORAGE_TYPE: { - FB: 'MOCK_FacebookConceal', AES: 'MOCK_KeystoreAESCBC', RSA: 'MOCK_KeystoreRSAECB', KC: 'MOCK_keychain', diff --git a/website/docs/usage.md b/website/docs/usage.md index cedb8904..2fcc3b62 100644 --- a/website/docs/usage.md +++ b/website/docs/usage.md @@ -46,7 +46,6 @@ See the `KeychainExample` for a fully working project example. The module automatically selects the appropriate `CipherStorage` implementation based on the device's API level: -- **API levels 16-22**: Uses Facebook Conceal for encryption/decryption. - **API level 23+**: Uses Android Keystore for encryption/decryption. Encrypted data is stored in `Jetpack DataStore`. From bc221b8e1cc3b5a9302ecb3a64b199d601cb6fcd Mon Sep 17 00:00:00 2001 From: raxodus <251626932+raxodus@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:07:38 +0200 Subject: [PATCH 3/7] chore: exodus v9.2.3-exodus.0 Scope package to @exodus namespace and set version to 9.2.3-exodus.0 for use as an Exodus fork dependency. --- package.json | 4 ++-- yarn.lock | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index dc89107f..1e22a768 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "react-native-keychain", - "version": "9.2.3", + "name": "@exodus/react-native-keychain", + "version": "9.2.3-exodus.0", "description": "Keychain Access for React Native", "main": "./lib/commonjs/index.js", "module": "./lib/module/index", diff --git a/yarn.lock b/yarn.lock index 6a9eea66..c6503e14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16734,9 +16734,9 @@ __metadata: languageName: node linkType: hard -"react-native-keychain@workspace:.": +"@exodus/react-native-keychain@workspace:.": version: 0.0.0-use.local - resolution: "react-native-keychain@workspace:." + resolution: "@exodus/react-native-keychain@workspace:." dependencies: "@react-native/eslint-config": ^0.74.84 "@react-native/typescript-config": ^0.74.84 From 3c9f460b7bd98219d4a324744d1178a58f484300 Mon Sep 17 00:00:00 2001 From: raxodus <251626932+raxodus@users.noreply.github.com> Date: Fri, 24 Apr 2026 14:45:13 +0200 Subject: [PATCH 4/7] chore: regenerate lockfile --- yarn.lock | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/yarn.lock b/yarn.lock index c6503e14..1bb9e3b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3573,6 +3573,22 @@ __metadata: languageName: node linkType: hard +"@exodus/react-native-keychain@workspace:.": + version: 0.0.0-use.local + resolution: "@exodus/react-native-keychain@workspace:." + dependencies: + "@react-native/eslint-config": ^0.74.84 + "@react-native/typescript-config": ^0.74.84 + eslint: ^8.46.0 + eslint-plugin-prettier: ^5.1.3 + prettier: ^3.0.3 + react: 18.2.0 + react-native: 0.74.5 + react-native-builder-bob: ^0.30.0 + typescript: ^5.2.2 + languageName: unknown + linkType: soft + "@flatten-js/interval-tree@npm:^1.1.2": version: 1.1.3 resolution: "@flatten-js/interval-tree@npm:1.1.3" @@ -16734,22 +16750,6 @@ __metadata: languageName: node linkType: hard -"@exodus/react-native-keychain@workspace:.": - version: 0.0.0-use.local - resolution: "@exodus/react-native-keychain@workspace:." - dependencies: - "@react-native/eslint-config": ^0.74.84 - "@react-native/typescript-config": ^0.74.84 - eslint: ^8.46.0 - eslint-plugin-prettier: ^5.1.3 - prettier: ^3.0.3 - react: 18.2.0 - react-native: 0.74.5 - react-native-builder-bob: ^0.30.0 - typescript: ^5.2.2 - languageName: unknown - linkType: soft - "react-native-segmented-control-tab@npm:^4.0.0": version: 4.0.0 resolution: "react-native-segmented-control-tab@npm:4.0.0" From 8f9d82dcc13c634302ac91e58e3d553dcd1ed0ca Mon Sep 17 00:00:00 2001 From: kewdex Date: Thu, 7 May 2026 16:19:29 +0200 Subject: [PATCH 5/7] feat: disable AES-GCM temporarily 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. --- android/src/main/java/com/oblador/keychain/KeychainModule.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/oblador/keychain/KeychainModule.kt b/android/src/main/java/com/oblador/keychain/KeychainModule.kt index be5eb615..1866b18f 100644 --- a/android/src/main/java/com/oblador/keychain/KeychainModule.kt +++ b/android/src/main/java/com/oblador/keychain/KeychainModule.kt @@ -148,8 +148,8 @@ class KeychainModule(reactContext: ReactApplicationContext) : init { prefsStorage = DataStorePrefsStorage(reactContext, coroutineScope) addCipherStorageToMap(CipherStorageKeystoreAesCbc(reactContext)) - addCipherStorageToMap(CipherStorageKeystoreAesGcm(reactContext, false)) - addCipherStorageToMap(CipherStorageKeystoreAesGcm(reactContext, true)) + // addCipherStorageToMap(CipherStorageKeystoreAesGcm(reactContext, false)) + // addCipherStorageToMap(CipherStorageKeystoreAesGcm(reactContext, true)) // we have a references to newer api that will fail load of app classes in old androids OS if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { From 8750c7295b8036f40c9f98eb16484e48bd5a2f1f Mon Sep 17 00:00:00 2001 From: kewdex Date: Thu, 7 May 2026 21:50:08 +0200 Subject: [PATCH 6/7] chore: release v9.2.3-exodus.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1e22a768..f3db2286 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@exodus/react-native-keychain", - "version": "9.2.3-exodus.0", + "version": "9.2.3-exodus.1", "description": "Keychain Access for React Native", "main": "./lib/commonjs/index.js", "module": "./lib/module/index", From f864cbbc260e07791887b5908b7cc2f162217e5a Mon Sep 17 00:00:00 2001 From: kewdex Date: Tue, 12 May 2026 18:34:19 +0200 Subject: [PATCH 7/7] chore: release v9.2.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f3db2286..2838e130 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@exodus/react-native-keychain", - "version": "9.2.3-exodus.1", + "version": "9.2.4", "description": "Keychain Access for React Native", "main": "./lib/commonjs/index.js", "module": "./lib/module/index",