@the-polyfills/get-random-values 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/GetRandomValues.podspec +20 -0
  2. package/LICENSE +20 -0
  3. package/README.md +5 -0
  4. package/android/build.gradle +55 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/com/thepolyfills/getrandomvalues/GetRandomValuesModule.kt +37 -0
  7. package/android/src/main/java/com/thepolyfills/getrandomvalues/GetRandomValuesPackage.kt +16 -0
  8. package/ios/GetRandomValues.h +5 -0
  9. package/ios/GetRandomValues.mm +35 -0
  10. package/lib/module/NativeGetRandomValues.js +17 -0
  11. package/lib/module/NativeGetRandomValues.js.map +1 -0
  12. package/lib/module/constants.js +54 -0
  13. package/lib/module/constants.js.map +1 -0
  14. package/lib/module/index.js +18 -0
  15. package/lib/module/index.js.map +1 -0
  16. package/lib/module/package.json +1 -0
  17. package/lib/module/utils/base64-decode.js +43 -0
  18. package/lib/module/utils/base64-decode.js.map +1 -0
  19. package/lib/module/utils/chrome.js +16 -0
  20. package/lib/module/utils/chrome.js.map +1 -0
  21. package/lib/module/utils/get-random-values.js +57 -0
  22. package/lib/module/utils/get-random-values.js.map +1 -0
  23. package/lib/module/utils/insecure-random-values.js +20 -0
  24. package/lib/module/utils/insecure-random-values.js.map +1 -0
  25. package/lib/typescript/package.json +1 -0
  26. package/lib/typescript/src/NativeGetRandomValues.d.ts +5 -0
  27. package/lib/typescript/src/NativeGetRandomValues.d.ts.map +1 -0
  28. package/lib/typescript/src/constants.d.ts +44 -0
  29. package/lib/typescript/src/constants.d.ts.map +1 -0
  30. package/lib/typescript/src/index.d.ts +6 -0
  31. package/lib/typescript/src/index.d.ts.map +1 -0
  32. package/lib/typescript/src/utils/base64-decode.d.ts +2 -0
  33. package/lib/typescript/src/utils/base64-decode.d.ts.map +1 -0
  34. package/lib/typescript/src/utils/chrome.d.ts +2 -0
  35. package/lib/typescript/src/utils/chrome.d.ts.map +1 -0
  36. package/lib/typescript/src/utils/get-random-values.d.ts +3 -0
  37. package/lib/typescript/src/utils/get-random-values.d.ts.map +1 -0
  38. package/lib/typescript/src/utils/insecure-random-values.d.ts +2 -0
  39. package/lib/typescript/src/utils/insecure-random-values.d.ts.map +1 -0
  40. package/package.json +128 -0
  41. package/src/NativeGetRandomValues.ts +21 -0
  42. package/src/constants.ts +55 -0
  43. package/src/index.tsx +20 -0
  44. package/src/utils/base64-decode.ts +59 -0
  45. package/src/utils/chrome.ts +18 -0
  46. package/src/utils/get-random-values.ts +81 -0
  47. package/src/utils/insecure-random-values.ts +25 -0
@@ -0,0 +1,20 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "GetRandomValues"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/doanhtu07/react-native-the-polyfills.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
17
+ s.private_header_files = "ios/**/*.h"
18
+
19
+ install_modules_dependencies(s)
20
+ end
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anh Tu Do
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # @the-polyfills/get-random-values
2
+
3
+ Polyfill for `crypto.getRandomValues` in React Native
4
+
5
+ Please refer to README.md at https://github.com/doanhtu07/react-native-the-polyfills for more details and documentation.
@@ -0,0 +1,55 @@
1
+ buildscript {
2
+ ext.GetRandomValues = [
3
+ kotlinVersion: "2.0.21",
4
+ minSdkVersion: 24,
5
+ compileSdkVersion: 36
6
+ ]
7
+
8
+ ext.getExtOrDefault = { prop ->
9
+ if (rootProject.ext.has(prop)) {
10
+ return rootProject.ext.get(prop)
11
+ }
12
+
13
+ return GetRandomValues[prop]
14
+ }
15
+
16
+ repositories {
17
+ google()
18
+ mavenCentral()
19
+ }
20
+
21
+ dependencies {
22
+ classpath "com.android.tools.build:gradle:8.7.2"
23
+ // noinspection DifferentKotlinGradleVersion
24
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
25
+ }
26
+ }
27
+
28
+
29
+ apply plugin: "com.android.library"
30
+
31
+ if (project.extensions.findByName("kotlin") == null) {
32
+ apply plugin: "kotlin-android"
33
+ }
34
+
35
+
36
+ apply plugin: "com.facebook.react"
37
+
38
+ android {
39
+ namespace "com.thepolyfills.getrandomvalues"
40
+
41
+ compileSdkVersion getExtOrDefault("compileSdkVersion")
42
+
43
+ defaultConfig {
44
+ minSdkVersion getExtOrDefault("minSdkVersion")
45
+ }
46
+
47
+ compileOptions {
48
+ sourceCompatibility JavaVersion.VERSION_17
49
+ targetCompatibility JavaVersion.VERSION_17
50
+ }
51
+ }
52
+
53
+ dependencies {
54
+ implementation "com.facebook.react:react-android"
55
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,37 @@
1
+ package com.thepolyfills.getrandomvalues
2
+
3
+ import android.util.Base64
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
6
+ import com.facebook.react.bridge.ReactMethod
7
+ import java.security.SecureRandom
8
+
9
+ class GetRandomValuesModule(reactContext: ReactApplicationContext) :
10
+ ReactContextBaseJavaModule(reactContext) {
11
+
12
+ override fun getName(): String = NAME
13
+
14
+ // Blocking synchronous method so `getRandomBase64` returns the value
15
+ // directly, matching the synchronous WebCrypto API.
16
+ @ReactMethod(isBlockingSynchronousMethod = true)
17
+ fun getRandomBase64(byteLength: Double): String {
18
+ val length = byteLength.toInt()
19
+
20
+ if (length < 0) {
21
+ throw IllegalArgumentException("byteLength must be >= 0")
22
+ }
23
+
24
+ if (length == 0) {
25
+ return ""
26
+ }
27
+
28
+ val bytes = ByteArray(length)
29
+ SecureRandom().nextBytes(bytes)
30
+
31
+ return Base64.encodeToString(bytes, Base64.NO_WRAP)
32
+ }
33
+
34
+ companion object {
35
+ const val NAME = "GetRandomValues"
36
+ }
37
+ }
@@ -0,0 +1,16 @@
1
+ package com.thepolyfills.getrandomvalues
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ class GetRandomValuesPackage : ReactPackage {
9
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
10
+ return listOf(GetRandomValuesModule(reactContext))
11
+ }
12
+
13
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
14
+ return emptyList()
15
+ }
16
+ }
@@ -0,0 +1,5 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface GetRandomValues : NSObject <RCTBridgeModule>
4
+
5
+ @end
@@ -0,0 +1,35 @@
1
+ #import "GetRandomValues.h"
2
+ #import <Security/Security.h>
3
+
4
+ @implementation GetRandomValues
5
+
6
+ RCT_EXPORT_MODULE(GetRandomValues)
7
+
8
+ + (BOOL)requiresMainQueueSetup {
9
+ return NO;
10
+ }
11
+
12
+ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getRandomBase64 : (double)byteLength) {
13
+ NSInteger length = (NSInteger)byteLength;
14
+
15
+ if (length < 0) {
16
+ return nil;
17
+ }
18
+
19
+ if (length == 0) {
20
+ return @"";
21
+ }
22
+
23
+ NSMutableData *data = [NSMutableData dataWithLength:(NSUInteger)length];
24
+
25
+ int status = SecRandomCopyBytes(kSecRandomDefault, (size_t)length,
26
+ data.mutableBytes);
27
+
28
+ if (status != errSecSuccess) {
29
+ return nil;
30
+ }
31
+
32
+ return [data base64EncodedStringWithOptions:0];
33
+ }
34
+
35
+ @end
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ import { NativeModules } from 'react-native';
4
+
5
+ // Old Architecture (Paper) only: the native module is registered via
6
+ // RCT_EXPORT_MODULE / ReactPackage and looked up through NativeModules.
7
+ // There is intentionally no TurboModuleRegistry usage and no codegen spec
8
+ // in this package.
9
+
10
+ export function getNativeGetRandomValues() {
11
+ const native = NativeModules.GetRandomValues;
12
+ if (native == null || typeof native.getRandomBase64 !== 'function') {
13
+ throw new Error("Native module 'GetRandomValues' not found. Rebuild the app after installing the package (e.g. `expo prebuild --clean`).");
14
+ }
15
+ return native;
16
+ }
17
+ //# sourceMappingURL=NativeGetRandomValues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NativeModules","getNativeGetRandomValues","native","GetRandomValues","getRandomBase64","Error"],"sourceRoot":"../../src","sources":["NativeGetRandomValues.ts"],"mappings":";;AAAA,SAASA,aAAa,QAAQ,cAAc;;AAE5C;AACA;AACA;AACA;;AAKA,OAAO,SAASC,wBAAwBA,CAAA,EAAS;EAC/C,MAAMC,MAAM,GAAGF,aAAa,CAACG,eAAmC;EAEhE,IAAID,MAAM,IAAI,IAAI,IAAI,OAAOA,MAAM,CAACE,eAAe,KAAK,UAAU,EAAE;IAClE,MAAM,IAAIC,KAAK,CACb,yHACF,CAAC;EACH;EAEA,OAAOH,MAAM;AACf","ignoreList":[]}
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Maximum number of bytes `crypto.getRandomValues()` may fill in a single call.
5
+ *
6
+ * The WebCrypto spec sets this quota at 65,536 bytes. Browsers throw a
7
+ * `QuotaExceededError` DOMException when `array.byteLength` exceeds it.
8
+ *
9
+ * Notes for this polyfill:
10
+ * - The check uses `byteLength`, not `length`. For example,
11
+ * `Uint16Array(40_000)` has `length === 40_000` but
12
+ * `byteLength === 80_000`, so it must throw.
13
+ * - Hermes (React Native) has no `DOMException`, so `getRandomValues`
14
+ * throws a plain `Error` with `error.name = 'QuotaExceededError'` instead.
15
+ * Browser-style `catch (e) { e.name === 'QuotaExceededError' }` code keeps
16
+ * working.
17
+ * - A zero-length array short-circuits before any Expo/native call.
18
+ */
19
+ export const MAX_BYTE_LENGTH = 65_536;
20
+
21
+ /**
22
+ * Reverse lookup table for standard base64 decoding.
23
+ *
24
+ * The native module can only return random bytes as a base64 `string` over
25
+ * the synchronous bridge, so JS decodes it back to bytes without an
26
+ * external dependency.
27
+ *
28
+ * How it works:
29
+ * - The base64 alphabet `'A-Za-z0-9+/'` maps index -> 6-bit value
30
+ * (`'A' === 0`, ..., `'/' === 63`).
31
+ * - This table inverts that: `table[charCode] === 6-bit value`, built once
32
+ * at module load via the IIFE below.
33
+ * - The table has 128 slots (one per ASCII code). Slots are pre-filled with
34
+ * `-1` as an "invalid character" marker; only the 64 alphabet codes are
35
+ * overwritten. `'='` padding is handled separately by the decoder (mapped
36
+ * to `0`), and any lookup that still yields `-1` (or `undefined ?? -1`
37
+ * for char codes > 127) makes `base64ToBytes` throw
38
+ * `'Invalid base64 character'`.
39
+ *
40
+ * Decode math (per 4-char block in `base64ToBytes`):
41
+ * - `triplet = (a << 18) | (b << 12) | (c << 6) | d` reassembles 4 x 6 bits
42
+ * into 24 bits, then splits into 3 bytes via `>> 16`, `>> 8`, `& 0xff`.
43
+ * - `outputLength = (clean.length / 4) * 3 - padding` accounts for trailing
44
+ * `'='` / `'=='`, and `outIndex < outputLength` guards drop padding bytes.
45
+ */
46
+ export const BASE64_LOOKUP = (() => {
47
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
48
+ const table = new Array(128).fill(-1);
49
+ for (let i = 0; i < chars.length; i++) {
50
+ table[chars.charCodeAt(i)] = i;
51
+ }
52
+ return table;
53
+ })();
54
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["MAX_BYTE_LENGTH","BASE64_LOOKUP","chars","table","Array","fill","i","length","charCodeAt"],"sourceRoot":"../../src","sources":["constants.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMA,eAAe,GAAG,MAAM;;AAErC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,aAAa,GAAG,CAAC,MAAM;EAClC,MAAMC,KAAK,GACT,kEAAkE;EAEpE,MAAMC,KAAK,GAAG,IAAIC,KAAK,CAAS,GAAG,CAAC,CAACC,IAAI,CAAC,CAAC,CAAC,CAAC;EAE7C,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGJ,KAAK,CAACK,MAAM,EAAED,CAAC,EAAE,EAAE;IACrCH,KAAK,CAACD,KAAK,CAACM,UAAU,CAACF,CAAC,CAAC,CAAC,GAAGA,CAAC;EAChC;EAEA,OAAOH,KAAK;AACd,CAAC,EAAE,CAAC","ignoreList":[]}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ import { getRandomValues } from "./utils/get-random-values.js";
4
+ export { getRandomValues } from "./utils/get-random-values.js";
5
+ export function installGetRandomValues(options) {
6
+ const g = globalThis;
7
+ if (typeof g.crypto !== 'object' || g.crypto === null) {
8
+ g.crypto = {};
9
+ }
10
+ if (options?.force || typeof g.crypto.getRandomValues !== 'function') {
11
+ g.crypto.getRandomValues = getRandomValues;
12
+ }
13
+ }
14
+
15
+ // Side-effect import installs the polyfill, matching
16
+ // `react-native-get-random-values` behavior.
17
+ installGetRandomValues();
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["getRandomValues","installGetRandomValues","options","g","globalThis","crypto","force"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,eAAe,QAAQ,8BAA2B;AAE3D,SAASA,eAAe,QAAQ,8BAA2B;AAG3D,OAAO,SAASC,sBAAsBA,CAACC,OAA6B,EAAQ;EAC1E,MAAMC,CAAC,GAAGC,UAAiB;EAE3B,IAAI,OAAOD,CAAC,CAACE,MAAM,KAAK,QAAQ,IAAIF,CAAC,CAACE,MAAM,KAAK,IAAI,EAAE;IACrDF,CAAC,CAACE,MAAM,GAAG,CAAC,CAAC;EACf;EAEA,IAAIH,OAAO,EAAEI,KAAK,IAAI,OAAOH,CAAC,CAACE,MAAM,CAACL,eAAe,KAAK,UAAU,EAAE;IACpEG,CAAC,CAACE,MAAM,CAACL,eAAe,GAAGA,eAAe;EAC5C;AACF;;AAEA;AACA;AACAC,sBAAsB,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+
3
+ import { BASE64_LOOKUP } from "../constants.js";
4
+ export function base64ToBytes(base64) {
5
+ // Standard base64 (NO_WRAP on Android, no line breaks on iOS), may be padded.
6
+ const clean = base64.replace(/\s/g, '');
7
+ if (clean.length === 0) {
8
+ return new Uint8Array(0);
9
+ }
10
+ if (clean.length % 4 !== 0) {
11
+ throw new Error('Invalid base64 string length');
12
+ }
13
+ let padding = 0;
14
+ if (clean.endsWith('==')) {
15
+ padding = 2;
16
+ } else if (clean.endsWith('=')) {
17
+ padding = 1;
18
+ }
19
+ const outputLength = clean.length / 4 * 3 - padding;
20
+ const output = new Uint8Array(outputLength);
21
+ let outIndex = 0;
22
+ for (let i = 0; i < clean.length; i += 4) {
23
+ const a = BASE64_LOOKUP[clean.charCodeAt(i)] ?? -1;
24
+ const b = BASE64_LOOKUP[clean.charCodeAt(i + 1)] ?? -1;
25
+ const c = clean[i + 2] === '=' ? 0 : BASE64_LOOKUP[clean.charCodeAt(i + 2)] ?? -1;
26
+ const d = clean[i + 3] === '=' ? 0 : BASE64_LOOKUP[clean.charCodeAt(i + 3)] ?? -1;
27
+ if (a < 0 || b < 0 || c < 0 || d < 0) {
28
+ throw new Error('Invalid base64 character');
29
+ }
30
+ const triplet = a << 18 | b << 12 | c << 6 | d;
31
+ if (outIndex < outputLength) {
32
+ output[outIndex++] = triplet >> 16 & 0xff;
33
+ }
34
+ if (outIndex < outputLength) {
35
+ output[outIndex++] = triplet >> 8 & 0xff;
36
+ }
37
+ if (outIndex < outputLength) {
38
+ output[outIndex++] = triplet & 0xff;
39
+ }
40
+ }
41
+ return output;
42
+ }
43
+ //# sourceMappingURL=base64-decode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["BASE64_LOOKUP","base64ToBytes","base64","clean","replace","length","Uint8Array","Error","padding","endsWith","outputLength","output","outIndex","i","a","charCodeAt","b","c","d","triplet"],"sourceRoot":"../../../src","sources":["utils/base64-decode.ts"],"mappings":";;AAAA,SAASA,aAAa,QAAQ,iBAAc;AAE5C,OAAO,SAASC,aAAaA,CAACC,MAAc,EAAc;EACxD;EACA,MAAMC,KAAK,GAAGD,MAAM,CAACE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;EAEvC,IAAID,KAAK,CAACE,MAAM,KAAK,CAAC,EAAE;IACtB,OAAO,IAAIC,UAAU,CAAC,CAAC,CAAC;EAC1B;EAEA,IAAIH,KAAK,CAACE,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE;IAC1B,MAAM,IAAIE,KAAK,CAAC,8BAA8B,CAAC;EACjD;EAEA,IAAIC,OAAO,GAAG,CAAC;EAEf,IAAIL,KAAK,CAACM,QAAQ,CAAC,IAAI,CAAC,EAAE;IACxBD,OAAO,GAAG,CAAC;EACb,CAAC,MAAM,IAAIL,KAAK,CAACM,QAAQ,CAAC,GAAG,CAAC,EAAE;IAC9BD,OAAO,GAAG,CAAC;EACb;EAEA,MAAME,YAAY,GAAIP,KAAK,CAACE,MAAM,GAAG,CAAC,GAAI,CAAC,GAAGG,OAAO;EACrD,MAAMG,MAAM,GAAG,IAAIL,UAAU,CAACI,YAAY,CAAC;EAE3C,IAAIE,QAAQ,GAAG,CAAC;EAEhB,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGV,KAAK,CAACE,MAAM,EAAEQ,CAAC,IAAI,CAAC,EAAE;IACxC,MAAMC,CAAC,GAAGd,aAAa,CAACG,KAAK,CAACY,UAAU,CAACF,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAElD,MAAMG,CAAC,GAAGhB,aAAa,CAACG,KAAK,CAACY,UAAU,CAACF,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAEtD,MAAMI,CAAC,GACLd,KAAK,CAACU,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,GAAIb,aAAa,CAACG,KAAK,CAACY,UAAU,CAACF,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAE;IAE3E,MAAMK,CAAC,GACLf,KAAK,CAACU,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,GAAIb,aAAa,CAACG,KAAK,CAACY,UAAU,CAACF,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAE;IAE3E,IAAIC,CAAC,GAAG,CAAC,IAAIE,CAAC,GAAG,CAAC,IAAIC,CAAC,GAAG,CAAC,IAAIC,CAAC,GAAG,CAAC,EAAE;MACpC,MAAM,IAAIX,KAAK,CAAC,0BAA0B,CAAC;IAC7C;IAEA,MAAMY,OAAO,GAAIL,CAAC,IAAI,EAAE,GAAKE,CAAC,IAAI,EAAG,GAAIC,CAAC,IAAI,CAAE,GAAGC,CAAC;IAEpD,IAAIN,QAAQ,GAAGF,YAAY,EAAE;MAC3BC,MAAM,CAACC,QAAQ,EAAE,CAAC,GAAIO,OAAO,IAAI,EAAE,GAAI,IAAI;IAC7C;IAEA,IAAIP,QAAQ,GAAGF,YAAY,EAAE;MAC3BC,MAAM,CAACC,QAAQ,EAAE,CAAC,GAAIO,OAAO,IAAI,CAAC,GAAI,IAAI;IAC5C;IAEA,IAAIP,QAAQ,GAAGF,YAAY,EAAE;MAC3BC,MAAM,CAACC,QAAQ,EAAE,CAAC,GAAGO,OAAO,GAAG,IAAI;IACrC;EACF;EAEA,OAAOR,MAAM;AACf","ignoreList":[]}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ export function isRemoteDebuggingInChrome() {
4
+ const g = globalThis;
5
+
6
+ // Remote debugging in Chrome is not supported in bridgeless mode.
7
+ if ('RN$Bridgeless' in g && g.RN$Bridgeless === true) {
8
+ return false;
9
+ }
10
+ try {
11
+ return __DEV__ !== undefined && __DEV__ && g.nativeCallSyncHook === undefined;
12
+ } catch {
13
+ return false;
14
+ }
15
+ }
16
+ //# sourceMappingURL=chrome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["isRemoteDebuggingInChrome","g","globalThis","RN$Bridgeless","__DEV__","undefined","nativeCallSyncHook"],"sourceRoot":"../../../src","sources":["utils/chrome.ts"],"mappings":";;AAEA,OAAO,SAASA,yBAAyBA,CAAA,EAAY;EACnD,MAAMC,CAAC,GAAGC,UAAiB;;EAE3B;EACA,IAAI,eAAe,IAAID,CAAC,IAAIA,CAAC,CAACE,aAAa,KAAK,IAAI,EAAE;IACpD,OAAO,KAAK;EACd;EAEA,IAAI;IACF,OACEC,OAAO,KAAKC,SAAS,IAAID,OAAO,IAAIH,CAAC,CAACK,kBAAkB,KAAKD,SAAS;EAE1E,CAAC,CAAC,MAAM;IACN,OAAO,KAAK;EACd;AACF","ignoreList":[]}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+
3
+ import { MAX_BYTE_LENGTH } from "../constants.js";
4
+ import { getNativeGetRandomValues } from "../NativeGetRandomValues.js";
5
+ import { base64ToBytes } from "./base64-decode.js";
6
+ import { isRemoteDebuggingInChrome } from "./chrome.js";
7
+ import { insecureRandomValues } from "./insecure-random-values.js";
8
+ function isAllowedArray(value) {
9
+ if (!ArrayBuffer.isView(value) || value instanceof DataView) {
10
+ return false;
11
+ }
12
+
13
+ // WebCrypto only allows integer TypedArrays (no float16/32/64).
14
+ return value instanceof Int8Array || value instanceof Uint8Array || value instanceof Uint8ClampedArray || value instanceof Int16Array || value instanceof Uint16Array || value instanceof Int32Array || value instanceof Uint32Array || value instanceof BigInt64Array || value instanceof BigUint64Array;
15
+ }
16
+ export function getRandomValues(array) {
17
+ if (!isAllowedArray(array)) {
18
+ throw new TypeError('crypto.getRandomValues: expected an integer TypedArray (e.g. Uint8Array)');
19
+ }
20
+ if (array.byteLength > MAX_BYTE_LENGTH) {
21
+ // Browsers throw QuotaExceededError (DOMException). RN Hermes may not
22
+ // have DOMException, so use a plain Error with the same name.
23
+ const error = new Error(`crypto.getRandomValues: byteLength ${array.byteLength} exceeds ${MAX_BYTE_LENGTH}`);
24
+ error.name = 'QuotaExceededError';
25
+ throw error;
26
+ }
27
+ if (array.byteLength === 0) {
28
+ return array;
29
+ }
30
+
31
+ // Expo SDK 48+: prefer ExpoCrypto when available so managed workflow
32
+ // apps don't need our native module linked.
33
+
34
+ const expoGetRandomValues = globalThis?.expo?.modules?.ExpoCrypto?.getRandomValues;
35
+ if (typeof expoGetRandomValues === 'function') {
36
+ // ExpoCrypto mutates the array in place and returns void.
37
+ expoGetRandomValues.call(globalThis.expo.modules.ExpoCrypto, array);
38
+ return array;
39
+ }
40
+
41
+ // Remote debugging in Chrome can't call sync native methods
42
+ // ("Calling synchronous methods on native modules is not supported in
43
+ // Chrome"), so fall back to Math.random() there.
44
+ if (isRemoteDebuggingInChrome()) {
45
+ return insecureRandomValues(array);
46
+ }
47
+ const base64 = getNativeGetRandomValues().getRandomBase64(array.byteLength);
48
+ const bytes = base64ToBytes(base64);
49
+ if (bytes.byteLength !== array.byteLength) {
50
+ throw new Error(`crypto.getRandomValues: native returned ${bytes.byteLength} bytes, expected ${array.byteLength}`);
51
+ }
52
+
53
+ // Write through to the underlying buffer so BigInt arrays work too.
54
+ new Uint8Array(array.buffer, array.byteOffset, array.byteLength).set(bytes);
55
+ return array;
56
+ }
57
+ //# sourceMappingURL=get-random-values.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["MAX_BYTE_LENGTH","getNativeGetRandomValues","base64ToBytes","isRemoteDebuggingInChrome","insecureRandomValues","isAllowedArray","value","ArrayBuffer","isView","DataView","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Uint16Array","Int32Array","Uint32Array","BigInt64Array","BigUint64Array","getRandomValues","array","TypeError","byteLength","error","Error","name","expoGetRandomValues","globalThis","expo","modules","ExpoCrypto","call","base64","getRandomBase64","bytes","buffer","byteOffset","set"],"sourceRoot":"../../../src","sources":["utils/get-random-values.ts"],"mappings":";;AAAA,SAASA,eAAe,QAAQ,iBAAc;AAC9C,SAASC,wBAAwB,QAAQ,6BAA0B;AACnE,SAASC,aAAa,QAAQ,oBAAiB;AAC/C,SAASC,yBAAyB,QAAQ,aAAU;AACpD,SAASC,oBAAoB,QAAQ,6BAA0B;AAE/D,SAASC,cAAcA,CAACC,KAAc,EAA4B;EAChE,IAAI,CAACC,WAAW,CAACC,MAAM,CAACF,KAAK,CAAC,IAAIA,KAAK,YAAYG,QAAQ,EAAE;IAC3D,OAAO,KAAK;EACd;;EAEA;EACA,OACEH,KAAK,YAAYI,SAAS,IAC1BJ,KAAK,YAAYK,UAAU,IAC3BL,KAAK,YAAYM,iBAAiB,IAClCN,KAAK,YAAYO,UAAU,IAC3BP,KAAK,YAAYQ,WAAW,IAC5BR,KAAK,YAAYS,UAAU,IAC3BT,KAAK,YAAYU,WAAW,IAC5BV,KAAK,YAAYW,aAAa,IAC9BX,KAAK,YAAYY,cAAc;AAEnC;AAIA,OAAO,SAASC,eAAeA,CAA4BC,KAAQ,EAAK;EACtE,IAAI,CAACf,cAAc,CAACe,KAAK,CAAC,EAAE;IAC1B,MAAM,IAAIC,SAAS,CACjB,0EACF,CAAC;EACH;EAEA,IAAID,KAAK,CAACE,UAAU,GAAGtB,eAAe,EAAE;IACtC;IACA;IACA,MAAMuB,KAAK,GAAG,IAAIC,KAAK,CACrB,sCAAsCJ,KAAK,CAACE,UAAU,YAAYtB,eAAe,EACnF,CAAC;IACDuB,KAAK,CAACE,IAAI,GAAG,oBAAoB;IACjC,MAAMF,KAAK;EACb;EAEA,IAAIH,KAAK,CAACE,UAAU,KAAK,CAAC,EAAE;IAC1B,OAAOF,KAAK;EACd;;EAEA;EACA;;EAEA,MAAMM,mBAAmB,GAAIC,UAAU,EAAUC,IAAI,EAAEC,OAAO,EAAEC,UAAU,EACtEX,eAAiE;EAErE,IAAI,OAAOO,mBAAmB,KAAK,UAAU,EAAE;IAC7C;IACAA,mBAAmB,CAACK,IAAI,CAAEJ,UAAU,CAASC,IAAI,CAACC,OAAO,CAACC,UAAU,EAAEV,KAAK,CAAC;IAC5E,OAAOA,KAAK;EACd;;EAEA;EACA;EACA;EACA,IAAIjB,yBAAyB,CAAC,CAAC,EAAE;IAC/B,OAAOC,oBAAoB,CAACgB,KAAK,CAAC;EACpC;EAEA,MAAMY,MAAM,GAAG/B,wBAAwB,CAAC,CAAC,CAACgC,eAAe,CAACb,KAAK,CAACE,UAAU,CAAC;EAC3E,MAAMY,KAAK,GAAGhC,aAAa,CAAC8B,MAAM,CAAC;EAEnC,IAAIE,KAAK,CAACZ,UAAU,KAAKF,KAAK,CAACE,UAAU,EAAE;IACzC,MAAM,IAAIE,KAAK,CACb,2CAA2CU,KAAK,CAACZ,UAAU,oBAAoBF,KAAK,CAACE,UAAU,EACjG,CAAC;EACH;;EAEA;EACA,IAAIX,UAAU,CAACS,KAAK,CAACe,MAAM,EAAEf,KAAK,CAACgB,UAAU,EAAEhB,KAAK,CAACE,UAAU,CAAC,CAACe,GAAG,CAACH,KAAK,CAAC;EAE3E,OAAOd,KAAK;AACd","ignoreList":[]}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+
3
+ let insecureWarned = false;
4
+ export function insecureRandomValues(array) {
5
+ if (!insecureWarned) {
6
+ console.warn('crypto.getRandomValues: using an insecure random number generator, ' + 'this should only happen when running in a debugger without support for crypto.getRandomValues');
7
+ insecureWarned = true;
8
+ }
9
+
10
+ // Fill through a Uint8 view so BigInt arrays work too.
11
+ const bytes = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
12
+ for (let i = 0, r = 0; i < bytes.length; i++) {
13
+ if ((i & 0x03) === 0) {
14
+ r = Math.random() * 0x100000000;
15
+ }
16
+ bytes[i] = r >>> ((i & 0x03) << 3) & 0xff;
17
+ }
18
+ return array;
19
+ }
20
+ //# sourceMappingURL=insecure-random-values.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["insecureWarned","insecureRandomValues","array","console","warn","bytes","Uint8Array","buffer","byteOffset","byteLength","i","r","length","Math","random"],"sourceRoot":"../../../src","sources":["utils/insecure-random-values.ts"],"mappings":";;AAAA,IAAIA,cAAc,GAAG,KAAK;AAE1B,OAAO,SAASC,oBAAoBA,CAA4BC,KAAQ,EAAK;EAC3E,IAAI,CAACF,cAAc,EAAE;IACnBG,OAAO,CAACC,IAAI,CACV,qEAAqE,GACnE,+FACJ,CAAC;IAEDJ,cAAc,GAAG,IAAI;EACvB;;EAEA;EACA,MAAMK,KAAK,GAAG,IAAIC,UAAU,CAACJ,KAAK,CAACK,MAAM,EAAEL,KAAK,CAACM,UAAU,EAAEN,KAAK,CAACO,UAAU,CAAC;EAE9E,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEC,CAAC,GAAG,CAAC,EAAED,CAAC,GAAGL,KAAK,CAACO,MAAM,EAAEF,CAAC,EAAE,EAAE;IAC5C,IAAI,CAACA,CAAC,GAAG,IAAI,MAAM,CAAC,EAAE;MACpBC,CAAC,GAAGE,IAAI,CAACC,MAAM,CAAC,CAAC,GAAG,WAAW;IACjC;IAEAT,KAAK,CAACK,CAAC,CAAC,GAAIC,CAAC,MAAM,CAACD,CAAC,GAAG,IAAI,KAAK,CAAC,CAAC,GAAI,IAAI;EAC7C;EAEA,OAAOR,KAAK;AACd","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,5 @@
1
+ export interface Spec {
2
+ getRandomBase64(byteLength: number): string;
3
+ }
4
+ export declare function getNativeGetRandomValues(): Spec;
5
+ //# sourceMappingURL=NativeGetRandomValues.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeGetRandomValues.d.ts","sourceRoot":"","sources":["../../../src/NativeGetRandomValues.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,IAAI;IACnB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAA;CAC5C;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAU/C"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Maximum number of bytes `crypto.getRandomValues()` may fill in a single call.
3
+ *
4
+ * The WebCrypto spec sets this quota at 65,536 bytes. Browsers throw a
5
+ * `QuotaExceededError` DOMException when `array.byteLength` exceeds it.
6
+ *
7
+ * Notes for this polyfill:
8
+ * - The check uses `byteLength`, not `length`. For example,
9
+ * `Uint16Array(40_000)` has `length === 40_000` but
10
+ * `byteLength === 80_000`, so it must throw.
11
+ * - Hermes (React Native) has no `DOMException`, so `getRandomValues`
12
+ * throws a plain `Error` with `error.name = 'QuotaExceededError'` instead.
13
+ * Browser-style `catch (e) { e.name === 'QuotaExceededError' }` code keeps
14
+ * working.
15
+ * - A zero-length array short-circuits before any Expo/native call.
16
+ */
17
+ export declare const MAX_BYTE_LENGTH = 65536;
18
+ /**
19
+ * Reverse lookup table for standard base64 decoding.
20
+ *
21
+ * The native module can only return random bytes as a base64 `string` over
22
+ * the synchronous bridge, so JS decodes it back to bytes without an
23
+ * external dependency.
24
+ *
25
+ * How it works:
26
+ * - The base64 alphabet `'A-Za-z0-9+/'` maps index -> 6-bit value
27
+ * (`'A' === 0`, ..., `'/' === 63`).
28
+ * - This table inverts that: `table[charCode] === 6-bit value`, built once
29
+ * at module load via the IIFE below.
30
+ * - The table has 128 slots (one per ASCII code). Slots are pre-filled with
31
+ * `-1` as an "invalid character" marker; only the 64 alphabet codes are
32
+ * overwritten. `'='` padding is handled separately by the decoder (mapped
33
+ * to `0`), and any lookup that still yields `-1` (or `undefined ?? -1`
34
+ * for char codes > 127) makes `base64ToBytes` throw
35
+ * `'Invalid base64 character'`.
36
+ *
37
+ * Decode math (per 4-char block in `base64ToBytes`):
38
+ * - `triplet = (a << 18) | (b << 12) | (c << 6) | d` reassembles 4 x 6 bits
39
+ * into 24 bits, then splits into 3 bytes via `>> 16`, `>> 8`, `& 0xff`.
40
+ * - `outputLength = (clean.length / 4) * 3 - padding` accounts for trailing
41
+ * `'='` / `'=='`, and `outIndex < outputLength` guards drop padding bytes.
42
+ */
43
+ export declare const BASE64_LOOKUP: number[];
44
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,eAAe,QAAS,CAAA;AAErC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,aAAa,UAWtB,CAAA"}
@@ -0,0 +1,6 @@
1
+ export { getRandomValues } from './utils/get-random-values.js';
2
+ export type { GetRandomValuesFn } from './utils/get-random-values.js';
3
+ export declare function installGetRandomValues(options?: {
4
+ force?: boolean;
5
+ }): void;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA2B,CAAA;AAC3D,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA2B,CAAA;AAElE,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAU1E"}
@@ -0,0 +1,2 @@
1
+ export declare function base64ToBytes(base64: string): Uint8Array;
2
+ //# sourceMappingURL=base64-decode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64-decode.d.ts","sourceRoot":"","sources":["../../../../src/utils/base64-decode.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAwDxD"}
@@ -0,0 +1,2 @@
1
+ export declare function isRemoteDebuggingInChrome(): boolean;
2
+ //# sourceMappingURL=chrome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chrome.d.ts","sourceRoot":"","sources":["../../../../src/utils/chrome.ts"],"names":[],"mappings":"AAEA,wBAAgB,yBAAyB,IAAI,OAAO,CAenD"}
@@ -0,0 +1,3 @@
1
+ export type GetRandomValuesFn = <T extends ArrayBufferView>(array: T) => T;
2
+ export declare function getRandomValues<T extends ArrayBufferView>(array: T): T;
3
+ //# sourceMappingURL=get-random-values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-random-values.d.ts","sourceRoot":"","sources":["../../../../src/utils/get-random-values.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,CAAA;AAE1E,wBAAgB,eAAe,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAqDtE"}
@@ -0,0 +1,2 @@
1
+ export declare function insecureRandomValues<T extends ArrayBufferView>(array: T): T;
2
+ //# sourceMappingURL=insecure-random-values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insecure-random-values.d.ts","sourceRoot":"","sources":["../../../../src/utils/insecure-random-values.ts"],"names":[],"mappings":"AAEA,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAsB3E"}
package/package.json ADDED
@@ -0,0 +1,128 @@
1
+ {
2
+ "name": "@the-polyfills/get-random-values",
3
+ "version": "1.0.1",
4
+ "description": "Polyfill for crypto.getRandomValues in React Native",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "the-polyfills-get-random-values-source": "./src/index.tsx",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "android",
19
+ "ios",
20
+ "cpp",
21
+ "*.podspec",
22
+ "react-native.config.js",
23
+ "!ios/build",
24
+ "!android/build",
25
+ "!android/gradle",
26
+ "!android/gradlew",
27
+ "!android/gradlew.bat",
28
+ "!android/local.properties",
29
+ "!**/__tests__",
30
+ "!**/__fixtures__",
31
+ "!**/__mocks__",
32
+ "!**/.*"
33
+ ],
34
+ "keywords": [
35
+ "react-native",
36
+ "ios",
37
+ "android"
38
+ ],
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/doanhtu07/react-native-the-polyfills.git"
42
+ },
43
+ "author": "Anh Tu Do <doanhtu07@gmail.com> (https://github.com/doanhtu07)",
44
+ "license": "MIT",
45
+ "bugs": {
46
+ "url": "https://github.com/doanhtu07/react-native-the-polyfills/issues"
47
+ },
48
+ "homepage": "https://github.com/doanhtu07/react-native-the-polyfills#readme",
49
+ "publishConfig": {
50
+ "registry": "https://registry.npmjs.org/"
51
+ },
52
+ "devDependencies": {
53
+ "@eslint/compat": "^2.1.1",
54
+ "@eslint/eslintrc": "^3.3.7",
55
+ "@eslint/js": "^10.0.1",
56
+ "@jest/globals": "^30.5.2",
57
+ "@react-native/babel-preset": "0.87.1",
58
+ "@react-native/eslint-config": "0.87.1",
59
+ "@types/react": "~19.0.14",
60
+ "del-cli": "^7.0.0",
61
+ "eslint": "^10.11.0",
62
+ "eslint-config-prettier": "^10.1.8",
63
+ "eslint-plugin-ft-flow": "^3.0.11",
64
+ "eslint-plugin-prettier": "^5.5.6",
65
+ "jest": "^30.5.2",
66
+ "nodemon": "^3.1.14",
67
+ "prettier": "^3.9.9",
68
+ "react": "19.0.0",
69
+ "react-native": "0.79.6",
70
+ "react-native-builder-bob": "^0.43.1",
71
+ "turbo": "^2.11.3",
72
+ "typescript": "~5.8.3"
73
+ },
74
+ "peerDependencies": {
75
+ "react": "*",
76
+ "react-native": "*"
77
+ },
78
+ "react-native-builder-bob": {
79
+ "source": "src",
80
+ "output": "lib",
81
+ "targets": [
82
+ [
83
+ "module",
84
+ {
85
+ "esm": true
86
+ }
87
+ ],
88
+ [
89
+ "typescript",
90
+ {
91
+ "project": "tsconfig.build.json"
92
+ }
93
+ ]
94
+ ]
95
+ },
96
+ "jest": {
97
+ "preset": "react-native",
98
+ "testEnvironmentOptions": {
99
+ "customExportConditions": [
100
+ "require",
101
+ "react-native",
102
+ "<%- project.sourceCondition -%>"
103
+ ]
104
+ },
105
+ "modulePathIgnorePatterns": [
106
+ "<rootDir>/example/node_modules",
107
+ "<rootDir>/lib/"
108
+ ]
109
+ },
110
+ "create-react-native-library": {
111
+ "type": "turbo-module",
112
+ "languages": "kotlin-objc",
113
+ "tools": [
114
+ "eslint",
115
+ "jest"
116
+ ],
117
+ "version": "0.63.1"
118
+ },
119
+ "scripts": {
120
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
121
+ "dev": "nodemon",
122
+ "typecheck": "tsc",
123
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
124
+ "format": "prettier",
125
+ "check": "pnpm typecheck && prettier --write . && eslint \"**/*.{js,ts,tsx}\" --fix",
126
+ "test": "jest"
127
+ }
128
+ }
@@ -0,0 +1,21 @@
1
+ import { NativeModules } from 'react-native'
2
+
3
+ // Old Architecture (Paper) only: the native module is registered via
4
+ // RCT_EXPORT_MODULE / ReactPackage and looked up through NativeModules.
5
+ // There is intentionally no TurboModuleRegistry usage and no codegen spec
6
+ // in this package.
7
+ export interface Spec {
8
+ getRandomBase64(byteLength: number): string
9
+ }
10
+
11
+ export function getNativeGetRandomValues(): Spec {
12
+ const native = NativeModules.GetRandomValues as Spec | undefined
13
+
14
+ if (native == null || typeof native.getRandomBase64 !== 'function') {
15
+ throw new Error(
16
+ "Native module 'GetRandomValues' not found. Rebuild the app after installing the package (e.g. `expo prebuild --clean`).",
17
+ )
18
+ }
19
+
20
+ return native
21
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Maximum number of bytes `crypto.getRandomValues()` may fill in a single call.
3
+ *
4
+ * The WebCrypto spec sets this quota at 65,536 bytes. Browsers throw a
5
+ * `QuotaExceededError` DOMException when `array.byteLength` exceeds it.
6
+ *
7
+ * Notes for this polyfill:
8
+ * - The check uses `byteLength`, not `length`. For example,
9
+ * `Uint16Array(40_000)` has `length === 40_000` but
10
+ * `byteLength === 80_000`, so it must throw.
11
+ * - Hermes (React Native) has no `DOMException`, so `getRandomValues`
12
+ * throws a plain `Error` with `error.name = 'QuotaExceededError'` instead.
13
+ * Browser-style `catch (e) { e.name === 'QuotaExceededError' }` code keeps
14
+ * working.
15
+ * - A zero-length array short-circuits before any Expo/native call.
16
+ */
17
+ export const MAX_BYTE_LENGTH = 65_536
18
+
19
+ /**
20
+ * Reverse lookup table for standard base64 decoding.
21
+ *
22
+ * The native module can only return random bytes as a base64 `string` over
23
+ * the synchronous bridge, so JS decodes it back to bytes without an
24
+ * external dependency.
25
+ *
26
+ * How it works:
27
+ * - The base64 alphabet `'A-Za-z0-9+/'` maps index -> 6-bit value
28
+ * (`'A' === 0`, ..., `'/' === 63`).
29
+ * - This table inverts that: `table[charCode] === 6-bit value`, built once
30
+ * at module load via the IIFE below.
31
+ * - The table has 128 slots (one per ASCII code). Slots are pre-filled with
32
+ * `-1` as an "invalid character" marker; only the 64 alphabet codes are
33
+ * overwritten. `'='` padding is handled separately by the decoder (mapped
34
+ * to `0`), and any lookup that still yields `-1` (or `undefined ?? -1`
35
+ * for char codes > 127) makes `base64ToBytes` throw
36
+ * `'Invalid base64 character'`.
37
+ *
38
+ * Decode math (per 4-char block in `base64ToBytes`):
39
+ * - `triplet = (a << 18) | (b << 12) | (c << 6) | d` reassembles 4 x 6 bits
40
+ * into 24 bits, then splits into 3 bytes via `>> 16`, `>> 8`, `& 0xff`.
41
+ * - `outputLength = (clean.length / 4) * 3 - padding` accounts for trailing
42
+ * `'='` / `'=='`, and `outIndex < outputLength` guards drop padding bytes.
43
+ */
44
+ export const BASE64_LOOKUP = (() => {
45
+ const chars =
46
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
47
+
48
+ const table = new Array<number>(128).fill(-1)
49
+
50
+ for (let i = 0; i < chars.length; i++) {
51
+ table[chars.charCodeAt(i)] = i
52
+ }
53
+
54
+ return table
55
+ })()
package/src/index.tsx ADDED
@@ -0,0 +1,20 @@
1
+ import { getRandomValues } from './utils/get-random-values'
2
+
3
+ export { getRandomValues } from './utils/get-random-values'
4
+ export type { GetRandomValuesFn } from './utils/get-random-values'
5
+
6
+ export function installGetRandomValues(options?: { force?: boolean }): void {
7
+ const g = globalThis as any
8
+
9
+ if (typeof g.crypto !== 'object' || g.crypto === null) {
10
+ g.crypto = {}
11
+ }
12
+
13
+ if (options?.force || typeof g.crypto.getRandomValues !== 'function') {
14
+ g.crypto.getRandomValues = getRandomValues
15
+ }
16
+ }
17
+
18
+ // Side-effect import installs the polyfill, matching
19
+ // `react-native-get-random-values` behavior.
20
+ installGetRandomValues()
@@ -0,0 +1,59 @@
1
+ import { BASE64_LOOKUP } from '../constants'
2
+
3
+ export function base64ToBytes(base64: string): Uint8Array {
4
+ // Standard base64 (NO_WRAP on Android, no line breaks on iOS), may be padded.
5
+ const clean = base64.replace(/\s/g, '')
6
+
7
+ if (clean.length === 0) {
8
+ return new Uint8Array(0)
9
+ }
10
+
11
+ if (clean.length % 4 !== 0) {
12
+ throw new Error('Invalid base64 string length')
13
+ }
14
+
15
+ let padding = 0
16
+
17
+ if (clean.endsWith('==')) {
18
+ padding = 2
19
+ } else if (clean.endsWith('=')) {
20
+ padding = 1
21
+ }
22
+
23
+ const outputLength = (clean.length / 4) * 3 - padding
24
+ const output = new Uint8Array(outputLength)
25
+
26
+ let outIndex = 0
27
+
28
+ for (let i = 0; i < clean.length; i += 4) {
29
+ const a = BASE64_LOOKUP[clean.charCodeAt(i)] ?? -1
30
+
31
+ const b = BASE64_LOOKUP[clean.charCodeAt(i + 1)] ?? -1
32
+
33
+ const c =
34
+ clean[i + 2] === '=' ? 0 : (BASE64_LOOKUP[clean.charCodeAt(i + 2)] ?? -1)
35
+
36
+ const d =
37
+ clean[i + 3] === '=' ? 0 : (BASE64_LOOKUP[clean.charCodeAt(i + 3)] ?? -1)
38
+
39
+ if (a < 0 || b < 0 || c < 0 || d < 0) {
40
+ throw new Error('Invalid base64 character')
41
+ }
42
+
43
+ const triplet = (a << 18) | (b << 12) | (c << 6) | d
44
+
45
+ if (outIndex < outputLength) {
46
+ output[outIndex++] = (triplet >> 16) & 0xff
47
+ }
48
+
49
+ if (outIndex < outputLength) {
50
+ output[outIndex++] = (triplet >> 8) & 0xff
51
+ }
52
+
53
+ if (outIndex < outputLength) {
54
+ output[outIndex++] = triplet & 0xff
55
+ }
56
+ }
57
+
58
+ return output
59
+ }
@@ -0,0 +1,18 @@
1
+ declare const __DEV__: boolean | undefined
2
+
3
+ export function isRemoteDebuggingInChrome(): boolean {
4
+ const g = globalThis as any
5
+
6
+ // Remote debugging in Chrome is not supported in bridgeless mode.
7
+ if ('RN$Bridgeless' in g && g.RN$Bridgeless === true) {
8
+ return false
9
+ }
10
+
11
+ try {
12
+ return (
13
+ __DEV__ !== undefined && __DEV__ && g.nativeCallSyncHook === undefined
14
+ )
15
+ } catch {
16
+ return false
17
+ }
18
+ }
@@ -0,0 +1,81 @@
1
+ import { MAX_BYTE_LENGTH } from '../constants'
2
+ import { getNativeGetRandomValues } from '../NativeGetRandomValues'
3
+ import { base64ToBytes } from './base64-decode'
4
+ import { isRemoteDebuggingInChrome } from './chrome'
5
+ import { insecureRandomValues } from './insecure-random-values'
6
+
7
+ function isAllowedArray(value: unknown): value is ArrayBufferView {
8
+ if (!ArrayBuffer.isView(value) || value instanceof DataView) {
9
+ return false
10
+ }
11
+
12
+ // WebCrypto only allows integer TypedArrays (no float16/32/64).
13
+ return (
14
+ value instanceof Int8Array ||
15
+ value instanceof Uint8Array ||
16
+ value instanceof Uint8ClampedArray ||
17
+ value instanceof Int16Array ||
18
+ value instanceof Uint16Array ||
19
+ value instanceof Int32Array ||
20
+ value instanceof Uint32Array ||
21
+ value instanceof BigInt64Array ||
22
+ value instanceof BigUint64Array
23
+ )
24
+ }
25
+
26
+ export type GetRandomValuesFn = <T extends ArrayBufferView>(array: T) => T
27
+
28
+ export function getRandomValues<T extends ArrayBufferView>(array: T): T {
29
+ if (!isAllowedArray(array)) {
30
+ throw new TypeError(
31
+ 'crypto.getRandomValues: expected an integer TypedArray (e.g. Uint8Array)',
32
+ )
33
+ }
34
+
35
+ if (array.byteLength > MAX_BYTE_LENGTH) {
36
+ // Browsers throw QuotaExceededError (DOMException). RN Hermes may not
37
+ // have DOMException, so use a plain Error with the same name.
38
+ const error = new Error(
39
+ `crypto.getRandomValues: byteLength ${array.byteLength} exceeds ${MAX_BYTE_LENGTH}`,
40
+ )
41
+ error.name = 'QuotaExceededError'
42
+ throw error
43
+ }
44
+
45
+ if (array.byteLength === 0) {
46
+ return array
47
+ }
48
+
49
+ // Expo SDK 48+: prefer ExpoCrypto when available so managed workflow
50
+ // apps don't need our native module linked.
51
+
52
+ const expoGetRandomValues = (globalThis as any)?.expo?.modules?.ExpoCrypto
53
+ ?.getRandomValues as ((array: ArrayBufferView) => void) | undefined
54
+
55
+ if (typeof expoGetRandomValues === 'function') {
56
+ // ExpoCrypto mutates the array in place and returns void.
57
+ expoGetRandomValues.call((globalThis as any).expo.modules.ExpoCrypto, array)
58
+ return array
59
+ }
60
+
61
+ // Remote debugging in Chrome can't call sync native methods
62
+ // ("Calling synchronous methods on native modules is not supported in
63
+ // Chrome"), so fall back to Math.random() there.
64
+ if (isRemoteDebuggingInChrome()) {
65
+ return insecureRandomValues(array)
66
+ }
67
+
68
+ const base64 = getNativeGetRandomValues().getRandomBase64(array.byteLength)
69
+ const bytes = base64ToBytes(base64)
70
+
71
+ if (bytes.byteLength !== array.byteLength) {
72
+ throw new Error(
73
+ `crypto.getRandomValues: native returned ${bytes.byteLength} bytes, expected ${array.byteLength}`,
74
+ )
75
+ }
76
+
77
+ // Write through to the underlying buffer so BigInt arrays work too.
78
+ new Uint8Array(array.buffer, array.byteOffset, array.byteLength).set(bytes)
79
+
80
+ return array
81
+ }
@@ -0,0 +1,25 @@
1
+ let insecureWarned = false
2
+
3
+ export function insecureRandomValues<T extends ArrayBufferView>(array: T): T {
4
+ if (!insecureWarned) {
5
+ console.warn(
6
+ 'crypto.getRandomValues: using an insecure random number generator, ' +
7
+ 'this should only happen when running in a debugger without support for crypto.getRandomValues',
8
+ )
9
+
10
+ insecureWarned = true
11
+ }
12
+
13
+ // Fill through a Uint8 view so BigInt arrays work too.
14
+ const bytes = new Uint8Array(array.buffer, array.byteOffset, array.byteLength)
15
+
16
+ for (let i = 0, r = 0; i < bytes.length; i++) {
17
+ if ((i & 0x03) === 0) {
18
+ r = Math.random() * 0x100000000
19
+ }
20
+
21
+ bytes[i] = (r >>> ((i & 0x03) << 3)) & 0xff
22
+ }
23
+
24
+ return array
25
+ }