Description
react-native-fast-crypto@2.2.0 ships a prebuilt libsecp256k1.so (4 KB-aligned) inside its package's android/jni/libs/<ABI>/ directory. Google Play flagged this .so as non-compliant with Android's 16 KB page-size requirement on the x86_64 ABI. As a workaround, #30590 drops the x86_64 ABI from production AABs entirely, but the same .so still ships for arm64-v8a with 4 KB alignment — which will eventually trigger the same Play warning on arm64, and is already non-compliant for Android 15+ devices running in 16 KB mode.
We only consume scrypt from react-native-fast-crypto:
// app/core/Engine/controllers/identity/user-storage-controller-init.ts
import { scrypt } from 'react-native-fast-crypto';
// ...
nativeScryptCrypto: scrypt,
react-native-quick-crypto@1.x (Nitro Modules rewrite) ships a Node-compatible crypto.scrypt(password, salt, keylen[, options], callback) API that uses OpenSSL underneath, is 16 KB-aligned, and is actively maintained by Margelo. We already depend on react-native-quick-crypto@0.7.15 so upgrading consolidates two crypto deps into one.
Technical Details
Scope of change
-
Bump react-native-quick-crypto 0.7.15 → 1.x
- Requires
react-native ≥ 0.75 and New Architecture enabled (✅ we already have newArchEnabled=true).
- Adds
react-native-nitro-modules as a peer dep.
- Re-evaluate the existing yarn patch (
.yarn/patches/react-native-quick-crypto-npm-0.7.15-85c4f4892e.patch) against 1.x — rewrite or drop.
-
Add a fast-crypto-shaped adapter at the single call site (app/core/Engine/controllers/identity/user-storage-controller-init.ts):
import QuickCrypto from 'react-native-quick-crypto';
import { Buffer } from '@craftzdog/react-native-buffer';
export const scrypt = (
passwd: Uint8Array,
salt: Uint8Array,
N: number,
r: number,
p: number,
size: number,
): Promise<Uint8Array> =>
new Promise((resolve, reject) =>
QuickCrypto.scrypt(
Buffer.from(passwd),
Buffer.from(salt),
size,
{ N, r, p, maxmem: 256 * 1024 * 1024 },
(err, derived) =>
err ? reject(err) : resolve(new Uint8Array(derived as Buffer)),
),
);
⚠️ Watch maxmem: Node's default is 32 MiB, which is too low for profile-sync params (N=2^17, r=8 → ~134 MiB needed). Set explicitly.
-
Add a known-vector test asserting our adapter produces the same bytes as fast-crypto did. Use RFC 7914 §12 vectors plus our actual profile-sync params (N=2^17, r=8, p=1, dkLen=32).
-
Remove the react-native-fast-crypto dependency from package.json and its yarn patch (.yarn/patches/react-native-fast-crypto-npm-2.2.0-57b8a8a01a.patch).
-
Remove the type declaration (app/declarations/index.d.ts line 15: declare module 'react-native-fast-crypto';).
Verification
- Both libraries implement RFC 7914 scrypt → byte-for-byte identical output for the same
(password, salt, N, r, p, dkLen).
- After the swap,
unzip -l app-prod-release.aab | grep libsecp256k1 should return empty across all ABIs.
unzip -l app-prod-release.aab | grep libcrypto_bridge (fast-crypto's JNI shim) should also disappear.
Risk
- Low. scrypt is a pure function with no persisted state; if the adapter is correct, there's no migration concern.
- The riskiest piece is the
react-native-quick-crypto 0.x → 1.x bump itself, not the scrypt swap. Margelo claims API compatibility in their README.
Acceptance Criteria
References
Description
react-native-fast-crypto@2.2.0ships a prebuiltlibsecp256k1.so(4 KB-aligned) inside its package'sandroid/jni/libs/<ABI>/directory. Google Play flagged this.soas non-compliant with Android's 16 KB page-size requirement on the x86_64 ABI. As a workaround, #30590 drops the x86_64 ABI from production AABs entirely, but the same.sostill ships forarm64-v8awith 4 KB alignment — which will eventually trigger the same Play warning on arm64, and is already non-compliant for Android 15+ devices running in 16 KB mode.We only consume
scryptfromreact-native-fast-crypto:react-native-quick-crypto@1.x(Nitro Modules rewrite) ships a Node-compatiblecrypto.scrypt(password, salt, keylen[, options], callback)API that uses OpenSSL underneath, is 16 KB-aligned, and is actively maintained by Margelo. We already depend onreact-native-quick-crypto@0.7.15so upgrading consolidates two crypto deps into one.Technical Details
Scope of change
Bump
react-native-quick-crypto0.7.15 → 1.xreact-native ≥ 0.75and New Architecture enabled (✅ we already havenewArchEnabled=true).react-native-nitro-modulesas a peer dep..yarn/patches/react-native-quick-crypto-npm-0.7.15-85c4f4892e.patch) against 1.x — rewrite or drop.Add a fast-crypto-shaped adapter at the single call site (
app/core/Engine/controllers/identity/user-storage-controller-init.ts):maxmem: Node's default is 32 MiB, which is too low for profile-sync params (N=2^17, r=8→ ~134 MiB needed). Set explicitly.Add a known-vector test asserting our adapter produces the same bytes as fast-crypto did. Use RFC 7914 §12 vectors plus our actual profile-sync params (
N=2^17, r=8, p=1, dkLen=32).Remove the
react-native-fast-cryptodependency frompackage.jsonand its yarn patch (.yarn/patches/react-native-fast-crypto-npm-2.2.0-57b8a8a01a.patch).Remove the type declaration (
app/declarations/index.d.tsline 15:declare module 'react-native-fast-crypto';).Verification
(password, salt, N, r, p, dkLen).unzip -l app-prod-release.aab | grep libsecp256k1should return empty across all ABIs.unzip -l app-prod-release.aab | grep libcrypto_bridge(fast-crypto's JNI shim) should also disappear.Risk
react-native-quick-crypto0.x → 1.x bump itself, not the scrypt swap. Margelo claims API compatibility in their README.Acceptance Criteria
react-native-fast-cryptoremoved frompackage.jsonand.yarn/patches/.react-native-quick-cryptobumped to a 1.x version with scrypt support.libsecp256k1.soand nolibcrypto_bridge.sounder any ABI.react-native-fast-cryptoin favor ofreact-native-quick-cryptoscrypt; reduces Android AAB size and improves 16 KB compliance.").References
.yarn/patches/react-native-fast-crypto-npm-2.2.0-57b8a8a01a.patch— already attempts 16 KB alignment for the compiled bridge code but cannot rebuild the importedlibsecp256k1.so.app/core/Engine/controllers/identity/user-storage-controller-init.ts