Skip to content

Commit

Permalink
Work around missing JSTypedArray APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
LinusU committed Aug 16, 2018
1 parent 75b0216 commit 02e7ff2
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 21 deletions.
142 changes: 121 additions & 21 deletions ReactCommon/jschelpers/Crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@

#include "JSCHelpers.h"

#ifdef __has_include
#if __has_include(<JavaScriptCore/JSTypedArray.h>)
#define JSC_HAS_TYPED_ARRAY_SUPPORT
#endif
#endif

#ifdef __APPLE__
#import <Security/SecRandom.h>
#else
Expand All @@ -16,6 +22,62 @@
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);
Expand Down Expand Up @@ -62,11 +124,44 @@ JSValueRef getRandomValues(JSContextRef ctx, JSObjectRef function, JSObjectRef t
auto offset = JSC_JSObjectGetTypedArrayByteOffset(ctx, typedArray, exception);
if (exception != nullptr) return JSC_JSValueMakeUndefined(ctx);

#ifdef __APPLE__
auto result = SecRandomCopyBytes(kSecRandomDefault, length, ptr + offset);
/* 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);

if (result != errSecSuccess) {
JSValueRef errorMsgValue = JSC_JSValueMakeString(ctx, JSC_JSStringCreateWithUTF8CString(ctx, "Failed to retreive random values"));
/* 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[3] = getPropertyNamed(ctx, typedArray, "byteLength", exception);
if (exception != nullptr) return JSC_JSValueMakeUndefined(ctx);

auto byteLength = JSC_JSValueToNumber(ctx, args[3], 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);
Expand All @@ -78,7 +173,17 @@ JSValueRef getRandomValues(JSContextRef ctx, JSObjectRef function, JSObjectRef t
exception = &errorRef;
return JSC_JSValueMakeUndefined(ctx);
}
#else

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) {
Expand All @@ -96,27 +201,22 @@ JSValueRef getRandomValues(JSContextRef ctx, JSObjectRef function, JSObjectRef t
}

/* 3. Overwrite all elements of array with cryptographically random values of the appropriate type. */
auto result = fread(ptr + offset, 1, length, fd);
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);

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 JSC_JSValueMakeUndefined(ctx);

JSValueRef errorRef = JSC_JSValueToObject(ctx, errorObj, exception);
if (exception != nullptr) return JSC_JSValueMakeUndefined(ctx);

exception = &errorRef;
return JSC_JSValueMakeUndefined(ctx);
}
#endif

/* 4. Return array. */
return arguments[0];
}

#endif

} }
10 changes: 10 additions & 0 deletions ReactCommon/jschelpers/JSCWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
#define RN_EXPORT __attribute__((visibility("default")))
#endif

#ifdef __has_include
#if __has_include(<JavaScriptCore/JSTypedArray.h>)
#define JSC_HAS_TYPED_ARRAY_SUPPORT
#endif
#endif

namespace facebook {
namespace react {
class IInspector;
Expand Down Expand Up @@ -128,7 +134,9 @@ struct JSCWrapper {
// JSValue
JSC_WRAPPER_METHOD(JSValueCreateJSONString);
JSC_WRAPPER_METHOD(JSValueGetType);
#ifdef JSC_HAS_TYPED_ARRAY_SUPPORT
JSC_WRAPPER_METHOD(JSValueGetTypedArrayType);
#endif
JSC_WRAPPER_METHOD(JSValueMakeFromJSONString);
JSC_WRAPPER_METHOD(JSValueMakeBoolean);
JSC_WRAPPER_METHOD(JSValueMakeNull);
Expand All @@ -144,9 +152,11 @@ struct JSCWrapper {
JSC_WRAPPER_METHOD(JSValueIsNull);

// JSTypedArray
#ifdef JSC_HAS_TYPED_ARRAY_SUPPORT
JSC_WRAPPER_METHOD(JSObjectGetTypedArrayLength);
JSC_WRAPPER_METHOD(JSObjectGetTypedArrayBytesPtr);
JSC_WRAPPER_METHOD(JSObjectGetTypedArrayByteOffset);
#endif

// Sampling profiler
JSC_WRAPPER_METHOD(JSSamplingProfilerEnabled);
Expand Down

0 comments on commit 02e7ff2

Please sign in to comment.