diff --git a/IntegrationTests/CryptoTest.js b/IntegrationTests/CryptoTest.js
new file mode 100644
index 000000000000..6651e9494343
--- /dev/null
+++ b/IntegrationTests/CryptoTest.js
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2018-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ * @flow
+ */
+
+'use strict';
+
+declare var crypto: {getRandomValues: (data: Uint8Array) => Uint8Array};
+
+const React = require('react');
+const ReactNative = require('react-native');
+const {View} = ReactNative;
+const {TestModule} = ReactNative.NativeModules;
+
+class CryptoTest extends React.Component<{}> {
+ componentDidMount() {
+ const data = new Uint8Array(8);
+ const returnValue = crypto.getRandomValues(data);
+
+ const returnsArray = data === returnValue;
+ const populatesData = data.find(value => value !== 0) !== undefined;
+
+ TestModule.markTestPassed(returnsArray && populatesData);
+ }
+
+ render(): React.Node {
+ return ;
+ }
+}
+
+CryptoTest.displayName = 'CryptoTest';
+
+module.exports = CryptoTest;
diff --git a/IntegrationTests/IntegrationTestsApp.js b/IntegrationTests/IntegrationTestsApp.js
index 421b4f6abe2b..e47018c30262 100644
--- a/IntegrationTests/IntegrationTestsApp.js
+++ b/IntegrationTests/IntegrationTestsApp.js
@@ -31,6 +31,7 @@ const TESTS = [
require('./SimpleSnapshotTest'),
require('./ImageCachePolicyTest'),
require('./ImageSnapshotTest'),
+ require('./CryptoTest'),
require('./PromiseTest'),
require('./WebViewTest'),
require('./SyncMethodTest'),
diff --git a/RNTester/RNTesterIntegrationTests/RNTesterIntegrationTests.m b/RNTester/RNTesterIntegrationTests/RNTesterIntegrationTests.m
index 1549898e4377..2f00509afa60 100644
--- a/RNTester/RNTesterIntegrationTests/RNTesterIntegrationTests.m
+++ b/RNTester/RNTesterIntegrationTests/RNTesterIntegrationTests.m
@@ -70,6 +70,7 @@ - (void)testTheTester_ExpectError
//RCT_TEST(LayoutEventsTest) // Disabled due to flakiness: #8686784
RCT_TEST(SimpleSnapshotTest)
RCT_TEST(SyncMethodTest)
+RCT_TEST(CryptoTest)
RCT_TEST(PromiseTest)
RCT_TEST_ONLY_WITH_PACKAGER(WebSocketTest)
RCT_TEST(AccessibilityManagerTest)
diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj
index ac97a4d41e64..dbcf30c769be 100644
--- a/React/React.xcodeproj/project.pbxproj
+++ b/React/React.xcodeproj/project.pbxproj
@@ -199,6 +199,10 @@
14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */; };
14F7A0EC1BDA3B3C003C6C10 /* RCTPerfMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F7A0EB1BDA3B3C003C6C10 /* RCTPerfMonitor.m */; };
14F7A0F01BDA714B003C6C10 /* RCTFPSGraph.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F7A0EF1BDA714B003C6C10 /* RCTFPSGraph.m */; };
+ 15698CC82125A6B300EE2C0D /* Crypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 15698CC62125A6B200EE2C0D /* Crypto.h */; };
+ 15698CC92125A6B300EE2C0D /* Crypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 15698CC62125A6B200EE2C0D /* Crypto.h */; };
+ 15698CCA2125A6B300EE2C0D /* Crypto.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 15698CC72125A6B300EE2C0D /* Crypto.cpp */; };
+ 15698CCB2125A6B300EE2C0D /* Crypto.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 15698CC72125A6B300EE2C0D /* Crypto.cpp */; };
191E3EBE1C29D9AF00C180A6 /* RCTRefreshControlManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 191E3EBD1C29D9AF00C180A6 /* RCTRefreshControlManager.m */; };
191E3EC11C29DC3800C180A6 /* RCTRefreshControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 191E3EC01C29DC3800C180A6 /* RCTRefreshControl.m */; };
199B8A6F1F44DB16005DEF67 /* RCTVersion.h in Headers */ = {isa = PBXBuildFile; fileRef = 199B8A6E1F44DB16005DEF67 /* RCTVersion.h */; };
@@ -2068,6 +2072,8 @@
14F7A0EB1BDA3B3C003C6C10 /* RCTPerfMonitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPerfMonitor.m; sourceTree = ""; };
14F7A0EE1BDA714B003C6C10 /* RCTFPSGraph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTFPSGraph.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
14F7A0EF1BDA714B003C6C10 /* RCTFPSGraph.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFPSGraph.m; sourceTree = ""; };
+ 15698CC62125A6B200EE2C0D /* Crypto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Crypto.h; sourceTree = ""; };
+ 15698CC72125A6B300EE2C0D /* Crypto.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Crypto.cpp; sourceTree = ""; };
191E3EBC1C29D9AF00C180A6 /* RCTRefreshControlManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRefreshControlManager.h; sourceTree = ""; };
191E3EBD1C29D9AF00C180A6 /* RCTRefreshControlManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRefreshControlManager.m; sourceTree = ""; };
191E3EBF1C29DC3800C180A6 /* RCTRefreshControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRefreshControl.h; sourceTree = ""; };
@@ -2759,6 +2765,8 @@
3D4A621D1DDD3985001F41B4 /* jschelpers */ = {
isa = PBXGroup;
children = (
+ 15698CC72125A6B300EE2C0D /* Crypto.cpp */,
+ 15698CC62125A6B200EE2C0D /* Crypto.h */,
19DED2281E77E29200F089BB /* systemJSCWrapper.cpp */,
3D7A27DC1DE32541002E3F95 /* JavaScriptCore.h */,
3D92B1071E0369AD0018521A /* JSCHelpers.cpp */,
@@ -3376,6 +3384,7 @@
19F61C041E8495FF00571D81 /* JSCHelpers.h in Headers */,
19F61C051E8495FF00571D81 /* noncopyable.h in Headers */,
19F61C061E8495FF00571D81 /* Unicode.h in Headers */,
+ 15698CC92125A6B300EE2C0D /* Crypto.h in Headers */,
19F61C071E8495FF00571D81 /* Value.h in Headers */,
3D3030251DF8295E00D6DDAE /* JavaScriptCore.h in Headers */,
3D3030261DF8295E00D6DDAE /* JSCWrapper.h in Headers */,
@@ -3423,6 +3432,7 @@
13EBC6821E28733C00880AC5 /* Value.h in Headers */,
13EBC6811E28733C00880AC5 /* Unicode.h in Headers */,
13EBC6801E28733C00880AC5 /* noncopyable.h in Headers */,
+ 15698CC82125A6B300EE2C0D /* Crypto.h in Headers */,
13EBC67E1E28726000880AC5 /* JSCHelpers.h in Headers */,
3D3CD93D1DE5FC1400167DC4 /* JavaScriptCore.h in Headers */,
3D3CD93E1DE5FC1400167DC4 /* JSCWrapper.h in Headers */,
@@ -4387,6 +4397,7 @@
13EBC6731E2870DE00880AC5 /* Value.cpp in Sources */,
13EBC67D1E28725900880AC5 /* JSCHelpers.cpp in Sources */,
135A9C021E7B0F4800587AEB /* systemJSCWrapper.cpp in Sources */,
+ 15698CCA2125A6B300EE2C0D /* Crypto.cpp in Sources */,
13EBC67B1E28723000880AC5 /* Unicode.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -4399,6 +4410,7 @@
13EBC6781E2870E400880AC5 /* JSCWrapper.cpp in Sources */,
13EBC6771E2870E400880AC5 /* JSCHelpers.cpp in Sources */,
135A9C011E7B0F4700587AEB /* systemJSCWrapper.cpp in Sources */,
+ 15698CCB2125A6B300EE2C0D /* Crypto.cpp in Sources */,
13EBC67A1E2870E400880AC5 /* Value.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/JSCryptoTest.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/JSCryptoTest.java
new file mode 100644
index 000000000000..9f02cbed1dd1
--- /dev/null
+++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/JSCryptoTest.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2018-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.facebook.react.tests;
+
+import com.facebook.react.bridge.CatalystInstance;
+import com.facebook.react.bridge.JavaScriptModule;
+import com.facebook.react.bridge.UiThreadUtil;
+import com.facebook.react.modules.appstate.AppStateModule;
+import com.facebook.react.modules.core.ReactChoreographer;
+import com.facebook.react.modules.deviceinfo.DeviceInfoModule;
+import com.facebook.react.testing.FakeWebSocketModule;
+import com.facebook.react.testing.ReactIntegrationTestCase;
+import com.facebook.react.testing.ReactTestHelper;
+import com.facebook.react.testing.StringRecordingModule;
+import com.facebook.react.uimanager.UIManagerModule;
+import com.facebook.react.uimanager.ViewManager;
+import com.facebook.react.views.view.ReactViewManager;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Test crypto-based functionality of JS VM
+ */
+public class JSCryptoTest extends ReactIntegrationTestCase {
+
+ private interface TestJSCryptoModule extends JavaScriptModule {
+ void getRandomValues();
+ }
+
+ StringRecordingModule mStringRecordingModule;
+
+ private CatalystInstance mInstance;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ List viewManagers = Arrays.asList(
+ new ReactViewManager());
+ final UIManagerModule mUIManager = new UIManagerModule(
+ getContext(),
+ viewManagers,
+ 0);
+ UiThreadUtil.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ ReactChoreographer.initialize();
+ mUIManager.onHostResume();
+ }
+ });
+ waitForIdleSync();
+
+ mStringRecordingModule = new StringRecordingModule();
+ mInstance = ReactTestHelper.catalystInstanceBuilder(this)
+ .addNativeModule(mStringRecordingModule)
+ .addNativeModule(mUIManager)
+ .addNativeModule(new DeviceInfoModule(getContext()))
+ .addNativeModule(new AppStateModule(getContext()))
+ .addNativeModule(new FakeWebSocketModule())
+ .build();
+ }
+
+ public void testGetRandomValues() {
+ TestJSCryptoModule testModule = mInstance.getJSModule(TestJSCryptoModule.class);
+ waitForBridgeAndUIIdle();
+
+ testModule.getRandomValues();
+ waitForBridgeAndUIIdle();
+
+ String[] answers = mStringRecordingModule.getCalls().toArray(new String[0]);
+ assertEquals("true", answers[0]);
+ }
+}
diff --git a/ReactAndroid/src/androidTest/js/TestBundle.js b/ReactAndroid/src/androidTest/js/TestBundle.js
index 59da7e6695b8..e85fc4c7f023 100644
--- a/ReactAndroid/src/androidTest/js/TestBundle.js
+++ b/ReactAndroid/src/androidTest/js/TestBundle.js
@@ -16,6 +16,7 @@ console.disableYellowBox = true;
require('ProgressBarTestModule');
require('ViewRenderingTestModule');
require('TestJavaToJSArgumentsModule');
+require('TestJSCryptoModule');
require('TestJSLocaleModule');
require('TestJSToJavaParametersModule');
require('TestJavaToJSReturnValuesModule');
diff --git a/ReactAndroid/src/androidTest/js/TestJSCryptoModule.js b/ReactAndroid/src/androidTest/js/TestJSCryptoModule.js
new file mode 100644
index 000000000000..8b88a22dc9ba
--- /dev/null
+++ b/ReactAndroid/src/androidTest/js/TestJSCryptoModule.js
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2018-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ */
+
+'use strict';
+
+/* global crypto */
+
+var BatchedBridge = require('BatchedBridge');
+var Recording = require('NativeModules').Recording;
+
+var TestJSCryptoModule = {
+ getRandomValues: function() {
+ const data = new Uint8Array(8);
+ const returnValue = crypto.getRandomValues(data);
+
+ const returnsArray = data === returnValue;
+ const populatesData = data.find(value => value !== 0) !== undefined;
+
+ Recording.record(returnsArray && populatesData ? 'true' : 'false');
+ },
+};
+
+BatchedBridge.registerCallableModule('TestJSCryptoModule', TestJSCryptoModule);
+
+module.exports = TestJSCryptoModule;
diff --git a/ReactCommon/cxxreact/JSCExecutor.cpp b/ReactCommon/cxxreact/JSCExecutor.cpp
index 65ee3bdd8025..4b201735e3dd 100644
--- a/ReactCommon/cxxreact/JSCExecutor.cpp
+++ b/ReactCommon/cxxreact/JSCExecutor.cpp
@@ -143,6 +143,11 @@ JSCExecutor::JSCExecutor(
"nativeModuleProxy",
exceptionWrapMethod<&JSCExecutor::getNativeModule>());
}
+
+ {
+ SystraceSection s("nativeModuleCrypto object");
+ installGlobalCrypto(m_context);
+ }
}
JSCExecutor::~JSCExecutor() {
diff --git a/ReactCommon/jschelpers/Android.mk b/ReactCommon/jschelpers/Android.mk
index 765b0ac7833a..5936a4cfb883 100644
--- a/ReactCommon/jschelpers/Android.mk
+++ b/ReactCommon/jschelpers/Android.mk
@@ -5,6 +5,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE := jschelpers
LOCAL_SRC_FILES := \
+ Crypto.cpp \
JSCHelpers.cpp \
Unicode.cpp \
Value.cpp \
diff --git a/ReactCommon/jschelpers/BUCK b/ReactCommon/jschelpers/BUCK
index 7739b8f7dc54..0320eae30124 100644
--- a/ReactCommon/jschelpers/BUCK
+++ b/ReactCommon/jschelpers/BUCK
@@ -1,6 +1,7 @@
load("//ReactNative:DEFS.bzl", "ANDROID", "ANDROID_JSC_INTERNAL_DEPS", "APPLE", "APPLE_JSC_INTERNAL_DEPS", "react_native_xplat_target", "rn_xplat_cxx_library")
EXPORTED_HEADERS = [
+ "Crypto.h",
"JavaScriptCore.h",
"JSCHelpers.h",
"JSCWrapper.h",
diff --git a/ReactCommon/jschelpers/Crypto.cpp b/ReactCommon/jschelpers/Crypto.cpp
new file mode 100644
index 000000000000..d9cb633034f3
--- /dev/null
+++ b/ReactCommon/jschelpers/Crypto.cpp
@@ -0,0 +1,221 @@
+// Copyright (c) 2018-present, Facebook, Inc.
+
+// This source code is licensed under the MIT license found in the
+// LICENSE file in the root directory of this source tree.
+
+#include "Crypto.h"
+
+#include "JSCHelpers.h"
+
+// FIXME: When we upgrade JSC for Android this will always be supported
+#ifdef __APPLE__
+#define JSC_HAS_TYPED_ARRAY_SUPPORT
+#endif
+
+#ifdef __APPLE__
+#import
+#else
+#include
+#endif
+
+namespace facebook {
+namespace react {
+
+#ifdef JSC_HAS_TYPED_ARRAY_SUPPORT
+
+static void populateRandomData (JSContextRef ctx, uint8_t *ptr, size_t length, JSValueRef *exception) {
+#ifdef __APPLE__
+ auto result = SecRandomCopyBytes(kSecRandomDefault, length, ptr);
+
+ if (result != errSecSuccess) {
+ JSValueRef errorMsgValue = JSC_JSValueMakeString(ctx, JSC_JSStringCreateWithUTF8CString(ctx, "Failed to retreive random values"));
+ JSValueRef args[] = {errorMsgValue};
+
+ JSObjectRef errorObj = JSC_JSObjectMakeError(ctx, 1, args, exception);
+ if (*exception != nullptr) return ;
+
+ JSValueRef errorRef = JSC_JSValueToObject(ctx, errorObj, exception);
+ if (*exception != nullptr) return ;
+
+ *exception = errorRef;
+ return ;
+ }
+#else
+ auto fd = fopen("/dev/urandom", "r");
+
+ if (fd == nullptr) {
+ JSValueRef errorMsgValue = JSC_JSValueMakeString(ctx, JSC_JSStringCreateWithUTF8CString(ctx, "Failed to open /dev/urandom"));
+ JSValueRef args[] = {errorMsgValue};
+
+ JSObjectRef errorObj = JSC_JSObjectMakeError(ctx, 1, args, exception);
+ if (*exception != nullptr) return ;
+
+ JSValueRef errorRef = JSC_JSValueToObject(ctx, errorObj, exception);
+ if (*exception != nullptr) return ;
+
+ *exception = errorRef;
+ return ;
+ }
+
+ auto result = fread(ptr, 1, length, fd);
+
+ fclose(fd);
+
+ if (result != length) {
+ JSValueRef errorMsgValue = JSC_JSValueMakeString(ctx, JSC_JSStringCreateWithUTF8CString(ctx, "Failed to retreive enough random values"));
+ JSValueRef args[] = {errorMsgValue};
+
+ JSObjectRef errorObj = JSC_JSObjectMakeError(ctx, 1, args, exception);
+ if (*exception != nullptr) return ;
+
+ JSValueRef errorRef = JSC_JSValueToObject(ctx, errorObj, exception);
+ if (*exception != nullptr) return ;
+
+ *exception = errorRef;
+ return ;
+ }
+#endif
+}
+
+JSValueRef getRandomValues(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
+ auto type = JSC_JSValueGetTypedArrayType(ctx, arguments[0], exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ auto typedArray = JSC_JSValueToObject(ctx, arguments[0], exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ /* 1. If array is not of an integer type (i.e., Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array or UInt8ClampedArray), throw a TypeMismatchError and terminate the algorithm. */
+ if (type == kJSTypedArrayTypeNone) {
+ JSValueRef errorMsgValue = JSC_JSValueMakeString(ctx, JSC_JSStringCreateWithUTF8CString(ctx, "TypeMismatchError"));
+ JSValueRef args[] = {errorMsgValue};
+
+ JSObjectRef errorObj = JSC_JSObjectMakeError(ctx, 1, args, exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ JSValueRef errorRef = JSC_JSValueToObject(ctx, errorObj, exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ *exception = errorRef;
+ return JSC_JSValueMakeUndefined(ctx);
+ }
+
+ auto length = JSC_JSObjectGetTypedArrayLength(ctx, typedArray, exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ /* 2. If the byteLength of array is greater than 65536, throw a QuotaExceededError and terminate the algorithm. */
+ if (length > 65536) {
+ JSValueRef errorMsgValue = JSC_JSValueMakeString(ctx, JSC_JSStringCreateWithUTF8CString(ctx, "QuotaExceededError"));
+ JSValueRef args[] = {errorMsgValue};
+
+ JSObjectRef errorObj = JSC_JSObjectMakeError(ctx, 1, args, exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ JSValueRef errorRef = JSC_JSValueToObject(ctx, errorObj, exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ *exception = errorRef;
+ return JSC_JSValueMakeUndefined(ctx);
+ }
+
+ uint8_t *ptr = static_cast(JSC_JSObjectGetTypedArrayBytesPtr(ctx, typedArray, exception));
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ auto offset = JSC_JSObjectGetTypedArrayByteOffset(ctx, typedArray, exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ /* 3. Overwrite all elements of array with cryptographically random values of the appropriate type. */
+ populateRandomData(ctx, ptr + offset, length, exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ /* 4. Return array. */
+ return arguments[0];
+}
+
+#else
+
+static JSValueRef getPropertyNamed(JSContextRef ctx, JSObjectRef object, const char *name, JSValueRef *exception) {
+ auto jsPropertyName = JSC_JSStringCreateWithUTF8CString(ctx, name);
+ auto value = JSC_JSObjectGetProperty(ctx, object, jsPropertyName, exception);
+ JSC_JSStringRelease(ctx, jsPropertyName);
+ return value;
+}
+
+JSValueRef getRandomValues(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) {
+ auto typedArray = JSC_JSValueToObject(ctx, arguments[0], exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ JSValueRef args[3];
+
+ args[0] = getPropertyNamed(ctx, typedArray, "buffer", exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ args[1] = getPropertyNamed(ctx, typedArray, "byteOffset", exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ args[2] = getPropertyNamed(ctx, typedArray, "byteLength", exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ auto byteLength = JSC_JSValueToNumber(ctx, args[2], exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ /* 2. If the byteLength of array is greater than 65536, throw a QuotaExceededError and terminate the algorithm. */
+ if (byteLength > 65536) {
+ JSValueRef errorMsgValue = JSC_JSValueMakeString(ctx, JSC_JSStringCreateWithUTF8CString(ctx, "QuotaExceededError"));
+ JSValueRef args[] = {errorMsgValue};
+
+ JSObjectRef errorObj = JSC_JSObjectMakeError(ctx, 1, args, exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ JSValueRef errorRef = JSC_JSValueToObject(ctx, errorObj, exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ *exception = errorRef;
+ return JSC_JSValueMakeUndefined(ctx);
+ }
+
+ JSObjectRef global = JSC_JSContextGetGlobalObject(ctx);
+ JSValueRef constructorValue = getPropertyNamed(ctx, global, "Uint8Array", exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ JSObjectRef constructor = JSC_JSValueToObject(ctx, constructorValue, exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ JSObjectRef view = JSC_JSObjectCallAsConstructor(ctx, constructor, 3, args, exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ auto fd = fopen("/dev/urandom", "r");
+
+ if (fd == nullptr) {
+ JSValueRef errorMsgValue = JSC_JSValueMakeString(ctx, JSC_JSStringCreateWithUTF8CString(ctx, "Failed to open /dev/urandom"));
+ JSValueRef args[] = {errorMsgValue};
+
+ JSObjectRef errorObj = JSC_JSObjectMakeError(ctx, 1, args, exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ JSValueRef errorRef = JSC_JSValueToObject(ctx, errorObj, exception);
+ if (*exception != nullptr) return JSC_JSValueMakeUndefined(ctx);
+
+ *exception = errorRef;
+ return JSC_JSValueMakeUndefined(ctx);
+ }
+
+ /* 3. Overwrite all elements of array with cryptographically random values of the appropriate type. */
+ for (auto idx = 0; idx < byteLength; idx++) {
+ auto randomByte = JSC_JSValueMakeNumber(ctx, fgetc(fd));
+ JSC_JSObjectSetPropertyAtIndex(ctx, view, idx, randomByte, exception);
+
+ if (*exception != nullptr) {
+ fclose(fd);
+ return JSC_JSValueMakeUndefined(ctx);
+ }
+ }
+
+ fclose(fd);
+
+ /* 4. Return array. */
+ return arguments[0];
+}
+
+#endif
+
+} }
diff --git a/ReactCommon/jschelpers/Crypto.h b/ReactCommon/jschelpers/Crypto.h
new file mode 100644
index 000000000000..e906aa6b90ff
--- /dev/null
+++ b/ReactCommon/jschelpers/Crypto.h
@@ -0,0 +1,14 @@
+// Copyright (c) 2018-present, Facebook, Inc.
+
+// This source code is licensed under the MIT license found in the
+// LICENSE file in the root directory of this source tree.
+
+#pragma once
+
+#include "JSCHelpers.h"
+
+namespace facebook {
+namespace react {
+__attribute__((visibility("default"))) JSValueRef getRandomValues(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception);
+}
+}
diff --git a/ReactCommon/jschelpers/JSCHelpers.cpp b/ReactCommon/jschelpers/JSCHelpers.cpp
index 3c28eb81ac6b..2ee0bdec9ca0 100644
--- a/ReactCommon/jschelpers/JSCHelpers.cpp
+++ b/ReactCommon/jschelpers/JSCHelpers.cpp
@@ -15,6 +15,7 @@
#include
#endif
+#include "Crypto.h"
#include "JavaScriptCore.h"
#include "Value.h"
#include
@@ -204,6 +205,17 @@ void installGlobalProxy(
Object::getGlobalObject(ctx).setProperty(name, Value(ctx, proxyObj));
}
+void installGlobalCrypto(JSGlobalContextRef ctx) {
+ JSObjectRef cryptoObject = JSC_JSObjectMake(ctx, NULL, NULL);
+
+ JSStringRef getRandomValuesName = JSC_JSStringCreateWithUTF8CString(ctx, "getRandomValues");
+ JSObjectRef getRandomValuesFunction = JSC_JSObjectMakeFunctionWithCallback(ctx, getRandomValuesName, getRandomValues);
+ JSC_JSObjectSetProperty(ctx, cryptoObject, getRandomValuesName, getRandomValuesFunction, kJSPropertyAttributeReadOnly & kJSPropertyAttributeDontDelete, NULL);
+ JSC_JSStringRelease(ctx, getRandomValuesName);
+
+ Object::getGlobalObject(ctx).setProperty("crypto", Value(ctx, cryptoObject));
+}
+
void removeGlobal(JSGlobalContextRef ctx, const char* name) {
Object::getGlobalObject(ctx).setProperty(name, Value::makeUndefined(ctx));
}
diff --git a/ReactCommon/jschelpers/JSCHelpers.h b/ReactCommon/jschelpers/JSCHelpers.h
index 9bf2e5ee9021..f4017caf3795 100644
--- a/ReactCommon/jschelpers/JSCHelpers.h
+++ b/ReactCommon/jschelpers/JSCHelpers.h
@@ -85,6 +85,8 @@ void installGlobalProxy(
const char* name,
JSObjectGetPropertyCallback callback);
+void installGlobalCrypto(JSGlobalContextRef ctx);
+
void removeGlobal(JSGlobalContextRef ctx, const char* name);
JSValueRef evaluateScript(
diff --git a/ReactCommon/jschelpers/JSCWrapper.h b/ReactCommon/jschelpers/JSCWrapper.h
index 471c23948b4a..e776c7204884 100644
--- a/ReactCommon/jschelpers/JSCWrapper.h
+++ b/ReactCommon/jschelpers/JSCWrapper.h
@@ -128,6 +128,7 @@ struct JSCWrapper {
// JSValue
JSC_WRAPPER_METHOD(JSValueCreateJSONString);
JSC_WRAPPER_METHOD(JSValueGetType);
+ JSC_WRAPPER_METHOD(JSValueGetTypedArrayType);
JSC_WRAPPER_METHOD(JSValueMakeFromJSONString);
JSC_WRAPPER_METHOD(JSValueMakeBoolean);
JSC_WRAPPER_METHOD(JSValueMakeNull);
@@ -142,6 +143,11 @@ struct JSCWrapper {
JSC_WRAPPER_METHOD(JSValueUnprotect);
JSC_WRAPPER_METHOD(JSValueIsNull);
+ // JSTypedArray
+ JSC_WRAPPER_METHOD(JSObjectGetTypedArrayLength);
+ JSC_WRAPPER_METHOD(JSObjectGetTypedArrayBytesPtr);
+ JSC_WRAPPER_METHOD(JSObjectGetTypedArrayByteOffset);
+
// Sampling profiler
JSC_WRAPPER_METHOD(JSSamplingProfilerEnabled);
JSC_WRAPPER_METHOD(JSPokeSamplingProfiler);
diff --git a/ReactCommon/jschelpers/JavaScriptCore.h b/ReactCommon/jschelpers/JavaScriptCore.h
index 4c61e8aeb3a0..24fc3239f018 100644
--- a/ReactCommon/jschelpers/JavaScriptCore.h
+++ b/ReactCommon/jschelpers/JavaScriptCore.h
@@ -110,6 +110,7 @@ jsc_poison(JSStringCopyCFString JSStringCreateWithCharacters JSStringCreateWithC
// JSValueRef
#define JSC_JSValueCreateJSONString(...) __jsc_wrapper(JSValueCreateJSONString, __VA_ARGS__)
#define JSC_JSValueGetType(...) __jsc_wrapper(JSValueGetType, __VA_ARGS__)
+#define JSC_JSValueGetTypedArrayType(...) __jsc_wrapper(JSValueGetTypedArrayType, __VA_ARGS__)
#define JSC_JSValueMakeFromJSONString(...) __jsc_wrapper(JSValueMakeFromJSONString, __VA_ARGS__)
#define JSC_JSValueMakeBoolean(...) __jsc_wrapper(JSValueMakeBoolean, __VA_ARGS__)
#define JSC_JSValueMakeNull(...) __jsc_wrapper(JSValueMakeNull, __VA_ARGS__)
@@ -175,6 +176,10 @@ jsc_poison(JSObjectCopyPropertyNames JSPropertyNameAccumulatorAddName
JSPropertyNameArrayRelease JSPropertyNameArrayRetain)
// JSTypedArray
+#define JSC_JSObjectGetTypedArrayLength(...) __jsc_wrapper(JSObjectGetTypedArrayLength, __VA_ARGS__)
+#define JSC_JSObjectGetTypedArrayBytesPtr(...) __jsc_wrapper(JSObjectGetTypedArrayBytesPtr, __VA_ARGS__)
+#define JSC_JSObjectGetTypedArrayByteOffset(...) __jsc_wrapper(JSObjectGetTypedArrayByteOffset, __VA_ARGS__)
+
jsc_poison(JSObjectMakeArrayBufferWithBytesNoCopy JSObjectMakeTypedArray
JSObjectMakeTypedArrayWithArrayBuffer
JSObjectMakeTypedArrayWithArrayBufferAndOffset
diff --git a/ReactCommon/jschelpers/systemJSCWrapper.cpp b/ReactCommon/jschelpers/systemJSCWrapper.cpp
index eecb036f547c..c8e6e315a90e 100644
--- a/ReactCommon/jschelpers/systemJSCWrapper.cpp
+++ b/ReactCommon/jschelpers/systemJSCWrapper.cpp
@@ -108,6 +108,7 @@ const JSCWrapper* systemJSCWrapper() {
.JSValueCreateJSONString = JSValueCreateJSONString,
.JSValueGetType = JSValueGetType,
+ .JSValueGetTypedArrayType = JSValueGetTypedArrayType,
.JSValueMakeFromJSONString = JSValueMakeFromJSONString,
.JSValueMakeBoolean = JSValueMakeBoolean,
.JSValueMakeNull = JSValueMakeNull,
@@ -122,6 +123,10 @@ const JSCWrapper* systemJSCWrapper() {
.JSValueUnprotect = JSValueUnprotect,
.JSValueIsNull = JSValueIsNull,
+ .JSObjectGetTypedArrayLength = JSObjectGetTypedArrayLength,
+ .JSObjectGetTypedArrayBytesPtr = JSObjectGetTypedArrayBytesPtr,
+ .JSObjectGetTypedArrayByteOffset = JSObjectGetTypedArrayByteOffset,
+
.JSSamplingProfilerEnabled = JSSamplingProfilerEnabled,
.JSPokeSamplingProfiler =
(decltype(&JSPokeSamplingProfiler))