From ea86960deecce8829626a365d1850746f95458a7 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Wed, 12 Aug 2026 15:36:48 -0400 Subject: [PATCH 1/6] fix: isolate bundled OpenSSL from other OpenSSL copies (#1059) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two libraries in one app can each bring their own OpenSSL — commonly op-sqlite with sqlcipher enabled. Today that breaks in two ways: Android: both ship libcrypto.so/libssl.so, so mergeNativeLibs fails, and pickFirst 'fixes' the build by leaving one library bound to a version it was not compiled against. iOS: QuickCrypto is source-built, so its OpenSSL references resolve at *app* link time. Anything that statically embeds OpenSSL wins first-come resolution and QuickCrypto silently runs on a foreign OpenSSL with different struct layouts (EXC_BAD_ACCESS in EVP_KEYMGMT_get0_name). Android now links OpenSSL statically and localizes those symbols with -Wl,--exclude-libs,ALL, so no libcrypto.so ships at all. iOS vendors a prebuilt static OpenSSL whose every global symbol has been renamed to rnqc_* with the originals demoted to non-external, built by scripts/build-openssl-apple.sh. The rename happens at link level rather than through a -include prefix header so that it also covers perlasm symbols, which the preprocessor cannot reach. Both changes depend on artifacts that must be published first: - io.github.ronickg:openssl-static:3.6.2-1 (ndkports) - the openssl-apple-3.6.2 release, whose checksum goes in the podspec --- .docs/troubleshooting.md | 79 ++++++++++--------- .../QuickCrypto.podspec | 56 ++++++++++++- .../android/CMakeLists.txt | 13 ++- .../android/build.gradle | 6 +- 4 files changed, 112 insertions(+), 42 deletions(-) diff --git a/.docs/troubleshooting.md b/.docs/troubleshooting.md index 53b27c5a2..b2cc92346 100644 --- a/.docs/troubleshooting.md +++ b/.docs/troubleshooting.md @@ -29,56 +29,63 @@ Fix it one of these ways: > Note: the `ndkVersion` patch that circulates for this error targets the old `react-native-quick-base64` `2.2.2` `android/build.gradle`. Version `3.0.0`+ has no `build.gradle` (it is CMake only), so that patch does not apply. -## Android build errors +## `libcrypto.so` / `libssl.so` collision on Android -If you get an error similar to this: +If a build fails like this: ``` Execution failed for task ':app:mergeDebugNativeLibs'. -> A failure occurred while executing com.android.build.gradle.internal.tasks.MergeNativeLibsTask$MergeNativeLibsTaskWorkAction - > 2 files found with path 'lib/arm64-v8a/libcrypto.so' from inputs: - - /Users/osp/Developer/mac_test/node_modules/react-native-quick-crypto/android/build/intermediates/library_jni/debug/jni/arm64-v8a/libcrypto.so - - /Users/osp/.gradle/caches/transforms-3/e13f88164840fe641a466d05cd8edac7/transformed/jetified-flipper-0.182.0/jni/arm64-v8a/libcrypto.so + > 2 files found with path 'lib/arm64-v8a/libcrypto.so' ``` -It means you have a transitive dependency where two libraries depend on OpenSSL and are generating a `libcrypto.so` file. You can get around this issue by adding the following in your `app/build.gradle`: +two libraries in your app are each shipping their own OpenSSL. The usual second +one is `@op-engineering/op-sqlite` with `sqlcipher: true`. + +Since `1.2.0`, `react-native-quick-crypto` links OpenSSL **statically** into +`libQuickCrypto.so` and keeps its symbols off the global symbol table, so it no +longer ships `libcrypto.so` or `libssl.so` at all. Upgrading is the fix. -

- React Native   -

+### On older versions -`android/app/build.gradle` file +Do **not** reach for `pickFirst` on its own: ```groovy packagingOptions { - // Should prevent clashes with other libraries that use OpenSSL - pickFirst '**/libcrypto.so' + pickFirst '**/libcrypto.so' // builds, then crashes at runtime } ``` -

- Expo   -

- -`app.json` file - -```diff -... - plugins: [ - ... -+ [ -+ 'expo-build-properties', -+ { -+ android: { -+ packagingOptions: { -+ pickFirst: ['**/libcrypto.so'], -+ }, -+ }, -+ }, -+ ], - ], +It makes the build succeed by dropping one of the two OpenSSL builds, leaving +whichever library lost the coin toss bound to a version it was not compiled +against. Expect corruption or `SIGSEGV` inside OpenSSL rather than a clean +error. + +If you cannot upgrade, force both libraries onto a single OpenSSL build first, +so that whichever copy `pickFirst` keeps is the one both sides compiled against. +Both `react-native-quick-crypto` and `@op-engineering/op-sqlite` consume the +same artifact, just at different versions: + +```groovy +// android/build.gradle +allprojects { + configurations.all { + resolutionStrategy.force 'io.github.ronickg:openssl:3.6.2-1' + } +} ``` -> This caused by flipper which also depends on OpenSSL +Pick the newest version any of your dependencies asks for — OpenSSL keeps ABI +compatibility across `3.x`, so the older consumer keeps working against the +newer build, but not the reverse. + +## `libcrypto` symbol collision on iOS + +The same problem has an iOS form: another dependency statically links its own +OpenSSL into your app binary, `ld` resolves each OpenSSL symbol first-wins, and +`react-native-quick-crypto` ends up calling into a foreign OpenSSL with +different struct layouts. It typically surfaces as `EXC_BAD_ACCESS` on the first +`subtle.*` call. -This just tells Gradle to grab whatever OpenSSL version it finds first and link against that, but as you can imagine this is not correct if the packages depend on different OpenSSL versions (quick-crypto depends on `com.android.ndk.thirdparty:openssl:1.1.1q-beta-1`). You should make sure all the OpenSSL versions match and you have no conflicts or errors. +Since `1.2.0` this cannot happen: the bundled OpenSSL's symbols are renamed to +`rnqc_*` and the original names are local to the archive, so neither copy can +see the other. Upgrade to fix it. diff --git a/packages/react-native-quick-crypto/QuickCrypto.podspec b/packages/react-native-quick-crypto/QuickCrypto.podspec index aa4306928..5241e34d3 100644 --- a/packages/react-native-quick-crypto/QuickCrypto.podspec +++ b/packages/react-native-quick-crypto/QuickCrypto.podspec @@ -1,4 +1,6 @@ require "json" +require "digest" +require "shellwords" package = JSON.parse(File.read(File.join(__dir__, "package.json"))) @@ -19,6 +21,44 @@ Pod::Spec.new do |s| s.source = { :git => "https://github.com/margelo/react-native-quick-crypto.git", :tag => "#{s.version}" } + # Prebuilt static OpenSSL whose every global symbol is renamed to rnqc_*, with + # the original names demoted to non-external. QuickCrypto is source-built, so + # its OpenSSL references are resolved when the *app* links; without the rename, + # any other library that statically embeds OpenSSL wins the first-come symbol + # resolution and QuickCrypto silently runs on a foreign OpenSSL. See + # scripts/build-openssl-apple.sh and issue #1059. + # + # Downloaded during podspec evaluation rather than in prepare_command, because + # prepare_command is skipped for :path pods. + openssl_version = "3.6.2" + openssl_sha256 = "REPLACE_WITH_RELEASE_SHA256" + openssl_dir = File.join(__dir__, "ios", "openssl") + openssl_prefix_header = File.join(openssl_dir, "quickcrypto_openssl_prefix.h") + + unless File.exist?(openssl_prefix_header) + if openssl_sha256 == "REPLACE_WITH_RELEASE_SHA256" + raise "[QuickCrypto] openssl_sha256 is unset — publish the openssl-apple-#{openssl_version} release and paste its checksum into QuickCrypto.podspec" + end + + archive = File.join(__dir__, "ios", "QuickCryptoOpenSSL.zip") + url = "https://github.com/margelo/react-native-quick-crypto/releases/download/openssl-apple-#{openssl_version}/QuickCryptoOpenSSL-#{openssl_version}.zip" + + Pod::UI.puts "[QuickCrypto] ⬇️ Downloading static OpenSSL #{openssl_version}..." + FileUtils.mkdir_p(File.join(__dir__, "ios")) + FileUtils.rm_rf(openssl_dir) + system("curl -sSfL --connect-timeout 30 --max-time 600 -o #{archive.shellescape} #{url.shellescape}") || raise("[QuickCrypto] Failed to download static OpenSSL") + + actual = Digest::SHA256.file(archive).hexdigest + unless actual == openssl_sha256 + File.delete(archive) + raise "[QuickCrypto] Static OpenSSL checksum mismatch: expected #{openssl_sha256}, got #{actual}" + end + + system("unzip -q #{archive.shellescape} -d #{openssl_dir.shellescape}") || raise("[QuickCrypto] Failed to extract static OpenSSL") + File.delete(archive) + Pod::UI.puts "[QuickCrypto] ✅ Static OpenSSL ready" + end + sodium_enabled = ENV['SODIUM_ENABLED'] == '1' Pod::UI.puts("[QuickCrypto] 🧂 has libsodium #{sodium_enabled ? "enabled" : "disabled"}!") @@ -94,6 +134,8 @@ Pod::Spec.new do |s| # These use Intel intrinsics that don't compile on ARM # Also exclude example files, TBB files, test files, and non-C directories s.exclude_files = [ + # Prebuilt OpenSSL: headers and archives, never compiled as sources + "ios/openssl/**/*", "deps/blake3/c/blake3_sse2.c", "deps/blake3/c/blake3_sse41.c", "deps/blake3/c/blake3_avx2.c", @@ -146,8 +188,19 @@ Pod::Spec.new do |s| "GCC_PREPROCESSOR_DEFINITIONS[sdk=iphonesimulator*][arch=x86_64]" => "$(inherited) BLAKE3_NO_AVX512 BLAKE3_NO_AVX2 BLAKE3_NO_SSE41 BLAKE3_NO_SSE2" } + # Every translation unit calls the renamed OpenSSL symbols. Missing a rename + # is a link error, never a silent bind to a foreign OpenSSL. + openssl_include = File.join(openssl_dir, "include") + force_include = "-include \"#{openssl_prefix_header}\"" + xcconfig["OTHER_CFLAGS"] = "$(inherited) #{force_include}" + xcconfig["OTHER_CPLUSPLUSFLAGS"] = "$(inherited) #{force_include}" + # Add cpp subdirectories to header search paths cpp_headers = [ + # Absolute path as well as the pod-relative one: in monorepos the podspec is + # reached through a symlink but sources resolve to their real paths. + "\"$(PODS_TARGET_SRCROOT)/ios/openssl/include\"", + "\"#{openssl_include}\"", "\"$(PODS_TARGET_SRCROOT)/cpp/utils\"", "\"$(PODS_TARGET_SRCROOT)/cpp/hkdf\"", "\"$(PODS_TARGET_SRCROOT)/cpp/dh\"", @@ -184,7 +237,8 @@ Pod::Spec.new do |s| load "nitrogen/generated/ios/QuickCrypto+autolinking.rb" add_nitrogen_files(s) - s.dependency "OpenSSL-Universal", "~> 3.6.2000" + s.vendored_frameworks = "ios/openssl/QuickCryptoOpenSSL.xcframework" + s.dependency "React-jsi" s.dependency "React-callinvoker" diff --git a/packages/react-native-quick-crypto/android/CMakeLists.txt b/packages/react-native-quick-crypto/android/CMakeLists.txt index 19ff94b77..76e7bb82f 100644 --- a/packages/react-native-quick-crypto/android/CMakeLists.txt +++ b/packages/react-native-quick-crypto/android/CMakeLists.txt @@ -114,17 +114,24 @@ include_directories( # Third party libraries (Prefabs) find_library(LOG_LIB log) -find_package(openssl REQUIRED CONFIG) +find_package(openssl-static REQUIRED CONFIG) # Link all libraries together target_link_libraries( ${PACKAGE_NAME} ${LOG_LIB} # <-- Logcat logger android # <-- Android core - openssl::crypto # <-- OpenSSL (Crypto) - openssl::ssl # <-- OpenSSL (SSL) + openssl-static::ssl # <-- OpenSSL (SSL) + openssl-static::crypto # <-- OpenSSL (Crypto) ) +# Keep every symbol we pull in from a static archive — i.e. all of OpenSSL — out +# of this .so's dynamic symbol table, so our OpenSSL and another library's +# OpenSSL cannot interpose on each other at runtime. Symbols from our own +# sources are unaffected, so the Nitro/JNI entry points stay exported. +# See issue #1059. +target_link_options(${PACKAGE_NAME} PRIVATE "-Wl,--exclude-libs,ALL") + if(SODIUM_ENABLED) add_definitions(-DBLSALLOC_SODIUM) find_package(sodium REQUIRED CONFIG) diff --git a/packages/react-native-quick-crypto/android/build.gradle b/packages/react-native-quick-crypto/android/build.gradle index e9ce64dd4..e2f0ce633 100644 --- a/packages/react-native-quick-crypto/android/build.gradle +++ b/packages/react-native-quick-crypto/android/build.gradle @@ -149,8 +149,10 @@ dependencies { // Add a dependency on NitroModules implementation project(":react-native-nitro-modules") - // Add a dependency on OpenSSL - implementation 'io.github.ronickg:openssl:3.6.2-1' + // Static OpenSSL: linked into libQuickCrypto.so with its symbols localized, so we + // neither ship a libcrypto.so that collides with another library's nor export + // OpenSSL symbols that could interpose. See CMakeLists.txt and issue #1059. + implementation 'io.github.ronickg:openssl-static:3.6.2-1' if (sodiumEnabled) { // Add a dependency on libsodium From d3d8d037d5289ae5116b6ff266de8d29af1d02a9 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Wed, 12 Aug 2026 18:30:22 -0400 Subject: [PATCH 2/6] fix(ios): wire in the published static OpenSSL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sets openssl_sha256 to the checksum from the openssl-apple-3.6.2 release (CI-built, verified: all 7 slices export only rnqc_* symbols, and the macOS slice runs SHA-256 and P-256 keygen through them). Also narrows the exclude_files pattern. CocoaPods runs exclude_files against vendored_frameworks as well as sources — paths_for_attribute passes spec_consumer.exclude_files as :exclude_patterns — so the previous 'ios/openssl/**/*' silently dropped the .xcframework from the pod and the library was never linked. pod install succeeded and looked clean; only the missing -l"QuickCryptoOpenSSL" in the app's OTHER_LDFLAGS gave it away. --- example/ios/Podfile.lock | 6 +----- packages/react-native-quick-crypto/QuickCrypto.podspec | 10 +++++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 265d9c7a6..b3a8e8604 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -69,7 +69,6 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - OpenSSL-Universal (3.6.2000) - QuickCrypto (1.1.6): - boost - DoubleConversion @@ -78,7 +77,6 @@ PODS: - glog - hermes-engine - NitroModules - - OpenSSL-Universal (~> 3.6.2000) - RCT-Folly - RCT-Folly/Fabric - RCTRequired @@ -2665,7 +2663,6 @@ DEPENDENCIES: SPEC REPOS: trunk: - MMKVCore - - OpenSSL-Universal - SocketRocket EXTERNAL SOURCES: @@ -2840,8 +2837,7 @@ SPEC CHECKSUMS: MMKVCore: f2dd4c9befea04277a55e84e7812f930537993df NitroMmkv: afbc5b2fbf963be567c6c545aa1efcf6a9cec68e NitroModules: 11bba9d065af151eae51e38a6425e04c3b223ff3 - OpenSSL-Universal: ecee7b138fa75a74ecf00d7ffd248fb584739b9e - QuickCrypto: 149336b1a5257bad6ee521e0de3d79cd17dcc116 + QuickCrypto: 736e5af30a4e875009b62f08139d38a19c01493b RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 RCTDeprecation: c4b9e2fd0ab200e3af72b013ed6113187c607077 RCTRequired: e97dd5dafc1db8094e63bc5031e0371f092ae92a diff --git a/packages/react-native-quick-crypto/QuickCrypto.podspec b/packages/react-native-quick-crypto/QuickCrypto.podspec index 5241e34d3..97edc87ce 100644 --- a/packages/react-native-quick-crypto/QuickCrypto.podspec +++ b/packages/react-native-quick-crypto/QuickCrypto.podspec @@ -31,7 +31,7 @@ Pod::Spec.new do |s| # Downloaded during podspec evaluation rather than in prepare_command, because # prepare_command is skipped for :path pods. openssl_version = "3.6.2" - openssl_sha256 = "REPLACE_WITH_RELEASE_SHA256" + openssl_sha256 = "27c9fe35ed82c67e4e55c222ee6e01a300a5086c0c0e2c544d44de2c73435586" openssl_dir = File.join(__dir__, "ios", "openssl") openssl_prefix_header = File.join(openssl_dir, "quickcrypto_openssl_prefix.h") @@ -134,8 +134,12 @@ Pod::Spec.new do |s| # These use Intel intrinsics that don't compile on ARM # Also exclude example files, TBB files, test files, and non-C directories s.exclude_files = [ - # Prebuilt OpenSSL: headers and archives, never compiled as sources - "ios/openssl/**/*", + # Prebuilt OpenSSL headers — reached through HEADER_SEARCH_PATHS, never + # compiled as sources. Must not glob the .xcframework alongside them: + # CocoaPods applies exclude_files to vendored_frameworks too, so a broader + # pattern here silently unlinks the library. + "ios/openssl/include/**/*", + "ios/openssl/quickcrypto_openssl_prefix.h", "deps/blake3/c/blake3_sse2.c", "deps/blake3/c/blake3_sse41.c", "deps/blake3/c/blake3_avx2.c", From 5c340278c92b22c97bddeb7f5cb63f927105eefc Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Wed, 12 Aug 2026 18:36:29 -0400 Subject: [PATCH 3/6] fix(ios): derive the OpenSSL prefix list from preprocessed headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prefix header was generated by intersecting the renamed symbols with identifiers found in the text of OpenSSL's public headers. Much of the public API never appears as literal text: DECLARE_ASN1_FUNCTIONS(X509) token-pastes X509_free/d2i_X509/i2d_X509, DECLARE_PEM_write_bio produces PEM_write_bio_X509. 1100 names were dropped — the whole ASN.1 and PEM surface — so QuickCrypto kept calling the original names, which are non-external in the archive. Collect the identifiers from clang -E output instead. 5379 -> 6479 defines, and the example app now links. Caught by building the example app; every prior check passed, since the test consumer only exercised EVP digests and keygen and never touched X509 or PEM. --- scripts/build-openssl-apple.sh | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/scripts/build-openssl-apple.sh b/scripts/build-openssl-apple.sh index 1b54d8c67..4aac83e9b 100755 --- a/scripts/build-openssl-apple.sh +++ b/scripts/build-openssl-apple.sh @@ -151,13 +151,23 @@ for slice in "${SLICES[@]}"; do done # The header QuickCrypto force-includes so its sources call the renamed symbols. -# Only symbols that appear in OpenSSL's public headers get a #define: the macros -# apply to every token in every QuickCrypto translation unit, so renaming an -# internal name like `sha256_block_data_order` would risk clobbering an +# Only symbols reachable from OpenSSL's public headers get a #define: these +# macros apply to every token in every QuickCrypto translation unit, so renaming +# an internal name like `sha256_block_data_order` would risk clobbering an # unrelated identifier of ours for no gain — internal symbols are only reached # from inside OpenSSL, where the link-level alias already covers them. sort -u "$WORK/all-symbols.txt" | sed 's/^_//' | sort -u >"$WORK/symbols.all" -grep -ohE '\b[A-Za-z_][A-Za-z0-9_]*\b' "$DIST"/include/openssl/*.h | sort -u >"$WORK/header.idents" +# Identifiers are collected from the *preprocessed* headers, not their text. A +# large part of OpenSSL's public API is generated by token pasting — +# DECLARE_ASN1_FUNCTIONS(X509) produces X509_free/d2i_X509/i2d_X509, +# DECLARE_PEM_write_bio produces PEM_write_bio_X509 — so those names never +# appear literally in a header and reading the text alone silently drops the +# entire ASN.1 and PEM surface. +for h in "$DIST"/include/openssl/*.h; do + echo "#include " +done >"$WORK/all-headers.h" +xcrun clang -E -I"$DIST/include" -x c "$WORK/all-headers.h" -o "$WORK/headers.pp" +grep -ohE '\b[A-Za-z_][A-Za-z0-9_]*\b' "$WORK/headers.pp" | sort -u >"$WORK/header.idents" # Names OpenSSL's own headers #define (EVP_des_cfb -> EVP_des_cfb64) are skipped: # that macro already redirects to a symbol we do rename, and defining both just # collides. From d0579df681fc62360cb17217fd965194e5ee9cc9 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Wed, 12 Aug 2026 21:00:33 -0400 Subject: [PATCH 4/6] fix(ios): repin to the corrected static OpenSSL artifact The openssl-apple-3.6.2 release was rebuilt from the fixed prefix-header generation; the archives were always correct, but the header ships inside the zip so the checksum moved. --- packages/react-native-quick-crypto/QuickCrypto.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-quick-crypto/QuickCrypto.podspec b/packages/react-native-quick-crypto/QuickCrypto.podspec index 97edc87ce..957934551 100644 --- a/packages/react-native-quick-crypto/QuickCrypto.podspec +++ b/packages/react-native-quick-crypto/QuickCrypto.podspec @@ -31,7 +31,7 @@ Pod::Spec.new do |s| # Downloaded during podspec evaluation rather than in prepare_command, because # prepare_command is skipped for :path pods. openssl_version = "3.6.2" - openssl_sha256 = "27c9fe35ed82c67e4e55c222ee6e01a300a5086c0c0e2c544d44de2c73435586" + openssl_sha256 = "a50e3c8473b0526b159ad8d105e97a90d1153a4a230388cc731ee23a7d0ad3a4" openssl_dir = File.join(__dir__, "ios", "openssl") openssl_prefix_header = File.join(openssl_dir, "quickcrypto_openssl_prefix.h") From a8e46ef6fe0adde6a11fdf1bb42443beaff67a94 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Fri, 14 Aug 2026 18:09:25 -0400 Subject: [PATCH 5/6] fix(android): consume the corrected static OpenSSL prefab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit openssl-static 3.6.2-1 was unusable — ndkports derives the AAR's manifest package from the prefab package name, and 'openssl-static' is not a valid Java identifier, so AGP rejected it before compiling anything. Fixed upstream in ronickg/ndkports#6 and republished as 3.6.2-2, which names the prefab package opensslstatic (the Maven coordinate is unchanged). Verified against the published artifact: builds all 4 ABIs, the APK ships no libcrypto.so or libssl.so, libQuickCrypto.so exports no OpenSSL symbols while keeping JNI_OnLoad and the Nitro entry points, and the OpenSSL code it does contain is local. --- packages/react-native-quick-crypto/android/CMakeLists.txt | 6 +++--- packages/react-native-quick-crypto/android/build.gradle | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-native-quick-crypto/android/CMakeLists.txt b/packages/react-native-quick-crypto/android/CMakeLists.txt index 76e7bb82f..2bb708e66 100644 --- a/packages/react-native-quick-crypto/android/CMakeLists.txt +++ b/packages/react-native-quick-crypto/android/CMakeLists.txt @@ -114,15 +114,15 @@ include_directories( # Third party libraries (Prefabs) find_library(LOG_LIB log) -find_package(openssl-static REQUIRED CONFIG) +find_package(opensslstatic REQUIRED CONFIG) # Link all libraries together target_link_libraries( ${PACKAGE_NAME} ${LOG_LIB} # <-- Logcat logger android # <-- Android core - openssl-static::ssl # <-- OpenSSL (SSL) - openssl-static::crypto # <-- OpenSSL (Crypto) + opensslstatic::ssl # <-- OpenSSL (SSL) + opensslstatic::crypto # <-- OpenSSL (Crypto) ) # Keep every symbol we pull in from a static archive — i.e. all of OpenSSL — out diff --git a/packages/react-native-quick-crypto/android/build.gradle b/packages/react-native-quick-crypto/android/build.gradle index e2f0ce633..4e32c5af4 100644 --- a/packages/react-native-quick-crypto/android/build.gradle +++ b/packages/react-native-quick-crypto/android/build.gradle @@ -152,7 +152,7 @@ dependencies { // Static OpenSSL: linked into libQuickCrypto.so with its symbols localized, so we // neither ship a libcrypto.so that collides with another library's nor export // OpenSSL symbols that could interpose. See CMakeLists.txt and issue #1059. - implementation 'io.github.ronickg:openssl-static:3.6.2-1' + implementation 'io.github.ronickg:openssl-static:3.6.2-2' if (sodiumEnabled) { // Add a dependency on libsodium From 0ce411f4207fe40405709d82a0f5cb170c2a7e61 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Fri, 14 Aug 2026 18:31:52 -0400 Subject: [PATCH 6/6] docs: the OpenSSL isolation fix ships in 1.1.7 --- .docs/troubleshooting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.docs/troubleshooting.md b/.docs/troubleshooting.md index b2cc92346..cfe10ad4a 100644 --- a/.docs/troubleshooting.md +++ b/.docs/troubleshooting.md @@ -41,7 +41,7 @@ Execution failed for task ':app:mergeDebugNativeLibs'. two libraries in your app are each shipping their own OpenSSL. The usual second one is `@op-engineering/op-sqlite` with `sqlcipher: true`. -Since `1.2.0`, `react-native-quick-crypto` links OpenSSL **statically** into +Since `1.1.7`, `react-native-quick-crypto` links OpenSSL **statically** into `libQuickCrypto.so` and keeps its symbols off the global symbol table, so it no longer ships `libcrypto.so` or `libssl.so` at all. Upgrading is the fix. @@ -86,6 +86,6 @@ OpenSSL into your app binary, `ld` resolves each OpenSSL symbol first-wins, and different struct layouts. It typically surfaces as `EXC_BAD_ACCESS` on the first `subtle.*` call. -Since `1.2.0` this cannot happen: the bundled OpenSSL's symbols are renamed to +Since `1.1.7` this cannot happen: the bundled OpenSSL's symbols are renamed to `rnqc_*` and the original names are local to the archive, so neither copy can see the other. Upgrade to fix it.