diff --git a/.yarn/patches/react-native-test-app-npm-4.2.1-866b46545f.patch b/.yarn/patches/react-native-test-app-npm-4.2.1-866b46545f.patch new file mode 100644 index 00000000..737f94a4 --- /dev/null +++ b/.yarn/patches/react-native-test-app-npm-4.2.1-866b46545f.patch @@ -0,0 +1,14 @@ +diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml +index f0e3634e693478c417f5d0edf18c1fa9f6398c21..5c2191ee6aec6e7e7cae404897cce03504396436 100644 +--- a/android/app/src/main/AndroidManifest.xml ++++ b/android/app/src/main/AndroidManifest.xml +@@ -12,7 +12,8 @@ + + >() - - /** - * Gets or creates a Cipher instance for the specified transformation. - * This method is thread-safe and caches Cipher instances per thread. - * - * @param transformation The name of the transformation, e.g., "AES/CBC/PKCS7Padding" - * @return A Cipher instance for the requested transformation - * @throws NoSuchAlgorithmException if the transformation algorithm is not available - * @throws NoSuchPaddingException if the padding scheme is not available - */ - @Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class) - fun getCipher(transformation: String): Cipher { - return synchronized(this) { - (cipherCache.get() ?: mutableMapOf().also { cipherCache.set(it) }) - .getOrPut(transformation) { Cipher.getInstance(transformation) } - } - } - - /** - * Clears the cipher cache for the current thread. - * This should be called when the ciphers are no longer needed to free up resources. - */ - fun clearCache() { - try { - cipherCache.remove() - } catch (e: Exception) { - Log.w(LOG_TAG, "Failed to clear cipher cache: ${e.message}") - } - } -} diff --git a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageBase.kt b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageBase.kt index adc9c007..703c9ace 100644 --- a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageBase.kt +++ b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageBase.kt @@ -7,7 +7,6 @@ import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyInfo import android.security.keystore.UserNotAuthenticatedException import android.util.Log -import androidx.annotation.VisibleForTesting import com.oblador.keychain.DeviceAvailability import com.oblador.keychain.SecurityLevel import com.oblador.keychain.cipherStorage.CipherStorageBase.DecryptBytesHandler @@ -159,8 +158,8 @@ abstract class CipherStorageBase(protected val applicationContext: Context) : Ci /** Get cipher instance and cache it for any next call. */ @Throws(NoSuchAlgorithmException::class, NoSuchPaddingException::class) - fun getCachedInstance(): Cipher { - return CipherCache.getCipher(getEncryptionTransformation()) + fun getCipher(): Cipher { + return Cipher.getInstance(getEncryptionTransformation()) } /** Check requirements to the security level. */ @@ -302,7 +301,7 @@ abstract class CipherStorageBase(protected val applicationContext: Context) : Ci value: String, handler: EncryptStringHandler? ): ByteArray { - val cipher = getCachedInstance() + val cipher = getCipher() try { ByteArrayOutputStream().use { output -> if (handler != null) { @@ -334,7 +333,7 @@ abstract class CipherStorageBase(protected val applicationContext: Context) : Ci bytes: ByteArray, handler: DecryptBytesHandler? ): String { - val cipher = getCachedInstance() + val cipher = getCipher() try { ByteArrayInputStream(bytes).use { input -> ByteArrayOutputStream().use { output -> @@ -353,8 +352,8 @@ abstract class CipherStorageBase(protected val applicationContext: Context) : Ci e is javax.crypto.AEADBadTagException -> { throw CryptoFailedException( "Decryption failed: Authentication tag verification failed. " + - "This usually indicates that the encrypted data was modified, corrupted, " + - "or is being decrypted with the wrong key.", + "This usually indicates that the encrypted data was modified, corrupted, " + + "or is being decrypted with the wrong key.", e ) } 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 98c399fd..bea11502 100644 --- a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAesCbc.kt +++ b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAesCbc.kt @@ -184,7 +184,7 @@ class CipherStorageKeystoreAesCbc(reactContext: ReactApplicationContext) : bytes: ByteArray, handler: DecryptBytesHandler? ): String { - val cipher = getCachedInstance() + val cipher = getCipher() return try { // read the initialization vector from bytes array diff --git a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAesGcm.kt b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAesGcm.kt index 6b6e6261..e8fa83ed 100644 --- a/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAesGcm.kt +++ b/android/src/main/java/com/oblador/keychain/cipherStorage/CipherStorageKeystoreAesGcm.kt @@ -217,9 +217,12 @@ class CipherStorageKeystoreAesGcm( /** Save Initialization vector to output stream. */ val encrypt = EncryptStringHandler { cipher, key, output -> - cipher.init(Cipher.ENCRYPT_MODE, key) - val iv = cipher.iv - output.write(iv, 0, iv.size) + cipher.init(Cipher.ENCRYPT_MODE, key) + val iv = cipher.iv + if (iv.size != IV_LENGTH) { + throw CryptoFailedException("IV length mismatch: expected ${IV_LENGTH}, got ${iv.size}") + } + output.write(iv, 0, iv.size) } /** Read initialization vector from input stream and configure cipher by it. */ diff --git a/package.json b/package.json index 009f7386..196cab54 100644 --- a/package.json +++ b/package.json @@ -104,5 +104,8 @@ } ] ] + }, + "resolutions": { + "react-native-test-app@4.2.1": "patch:react-native-test-app@npm%3A4.2.1#./.yarn/patches/react-native-test-app-npm-4.2.1-866b46545f.patch" } } diff --git a/yarn.lock b/yarn.lock index 0716aa52..b6b9d701 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16172,6 +16172,43 @@ __metadata: languageName: node linkType: hard +"react-native-test-app@patch:react-native-test-app@npm%3A4.2.1#./.yarn/patches/react-native-test-app-npm-4.2.1-866b46545f.patch::locator=react-native-keychain%40workspace%3A.": + version: 4.2.1 + resolution: "react-native-test-app@patch:react-native-test-app@npm%3A4.2.1#./.yarn/patches/react-native-test-app-npm-4.2.1-866b46545f.patch::version=4.2.1&hash=faaa1b&locator=react-native-keychain%40workspace%3A." + dependencies: + "@rnx-kit/react-native-host": ^0.5.4 + "@rnx-kit/tools-react-native": ^2.1.0 + ajv: ^8.0.0 + cliui: ^8.0.0 + fast-xml-parser: ^4.0.0 + prompts: ^2.4.0 + semver: ^7.3.5 + uuid: ^11.0.0 + peerDependencies: + "@callstack/react-native-visionos": 0.73 - 0.77 + "@expo/config-plugins": ">=5.0" + react: 18.1 - 19.0 + react-native: 0.70 - 0.78 || >=0.79.0-0 <0.79.0 + react-native-macos: ^0.0.0-0 || 0.71 - 0.77 + react-native-windows: ^0.0.0-0 || 0.70 - 0.77 + peerDependenciesMeta: + "@callstack/react-native-visionos": + optional: true + "@expo/config-plugins": + optional: true + react-native-macos: + optional: true + react-native-windows: + optional: true + bin: + configure-test-app: scripts/configure.mjs + init: scripts/init.mjs + init-test-app: scripts/init.mjs + install-windows-test-app: windows/test-app.mjs + checksum: 26f6bfcfcf0646afadb73582895bc1a1b8ed9c4ba445dd61cb06942f9c880aec33197cf24290fc164e568f4a392c8953666ab49a263c34eb5dcdfe7ccd8878a2 + languageName: node + linkType: hard + "react-native@npm:0.77.1, react-native@npm:^0.77.1": version: 0.77.1 resolution: "react-native@npm:0.77.1"