Skip to content

Commit

Permalink
Fixed fast-base64-decode dependency issue in react-native-package.
Browse files Browse the repository at this point in the history
  • Loading branch information
Chriztiaan committed Sep 2, 2024
1 parent a65cd8c commit d9ca7e1
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 27 deletions.
5 changes: 5 additions & 0 deletions .changeset/pretty-teachers-press.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@powersync/react-native': patch
---

Updated crypto.js vendor file to include fast-base64-decode dependency instead of bundling it.
121 changes: 94 additions & 27 deletions packages/react-native/vendor/crypto.js
Original file line number Diff line number Diff line change
@@ -1,85 +1,152 @@
// This file is based on code from the react-native-get-random-values repository
// This file is based on code from the fast-base64-decode repository.
// Source: https://github.com/LinusU/fast-base64-decode/blob/master/index.js
// Modifications:
// - None

const lookup = new Uint8Array([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 62, 0, 62, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33,
34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
]);

function base64Decode(source, target) {
const sourceLength = source.length;
const paddingLength = source[sourceLength - 2] === '=' ? 2 : source[sourceLength - 1] === '=' ? 1 : 0;
const baseLength = (sourceLength - paddingLength) & 0xfffffffc;

let tmp;
let i = 0;
let byteIndex = 0;

for (; i < baseLength; i += 4) {
tmp =
(lookup[source.charCodeAt(i)] << 18) |
(lookup[source.charCodeAt(i + 1)] << 12) |
(lookup[source.charCodeAt(i + 2)] << 6) |
lookup[source.charCodeAt(i + 3)];

target[byteIndex++] = (tmp >> 16) & 0xff;
target[byteIndex++] = (tmp >> 8) & 0xff;
target[byteIndex++] = tmp & 0xff;
}

if (paddingLength === 1) {
tmp =
(lookup[source.charCodeAt(i)] << 10) |
(lookup[source.charCodeAt(i + 1)] << 4) |
(lookup[source.charCodeAt(i + 2)] >> 2);

target[byteIndex++] = (tmp >> 8) & 0xff;
target[byteIndex++] = tmp & 0xff;
}

if (paddingLength === 2) {
tmp = (lookup[source.charCodeAt(i)] << 2) | (lookup[source.charCodeAt(i + 1)] >> 4);

target[byteIndex++] = tmp & 0xff;
}
}

// This file is based on code from the react-native-get-random-values repository.
// Source: https://github.com/LinusU/react-native-get-random-values/blob/modern/index.js
// Modifications:
// - Instead of applying to all global references of crypto, provide a ponyfill export.
// - Instead of having a package dependency on fast-base64-decode, copy the code here. There are issues with injecting a vendor file in the rollup config.

const base64Decode = require('fast-base64-decode')
const { NativeModules } = require('react-native')
const { NativeModules } = require('react-native');

class TypeMismatchError extends Error {}
class QuotaExceededError extends Error {}

let warned = false
function insecureRandomValues (array) {
let warned = false;
function insecureRandomValues(array) {
if (!warned) {
console.warn('Using an insecure random number generator, this should only happen when running in a debugger without support for crypto.getRandomValues')
warned = true
console.warn(
'Using an insecure random number generator, this should only happen when running in a debugger without support for crypto.getRandomValues'
);
warned = true;
}

for (let i = 0, r; i < array.length; i++) {
if ((i & 0x03) === 0) r = Math.random() * 0x100000000
array[i] = (r >>> ((i & 0x03) << 3)) & 0xff
if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
array[i] = (r >>> ((i & 0x03) << 3)) & 0xff;
}

return array
return array;
}

/**
* @param {number} byteLength
* @returns {string}
*/
function getRandomBase64 (byteLength) {
function getRandomBase64(byteLength) {
if (NativeModules.RNGetRandomValues) {
return NativeModules.RNGetRandomValues.getRandomBase64(byteLength)
return NativeModules.RNGetRandomValues.getRandomBase64(byteLength);
} else if (NativeModules.ExpoRandom) {
// Expo SDK 41-44
return NativeModules.ExpoRandom.getRandomBase64String(byteLength)
return NativeModules.ExpoRandom.getRandomBase64String(byteLength);
} else if (global.ExpoModules) {
// Expo SDK 45+
return global.ExpoModules.ExpoRandom.getRandomBase64String(byteLength);
} else {
throw new Error('Native module not found')
throw new Error('Native module not found');
}
}

/**
* @param {Int8Array|Uint8Array|Int16Array|Uint16Array|Int32Array|Uint32Array|Uint8ClampedArray} array
*/
function getRandomValues (array) {
if (!(array instanceof Int8Array || array instanceof Uint8Array || array instanceof Int16Array || array instanceof Uint16Array || array instanceof Int32Array || array instanceof Uint32Array || array instanceof Uint8ClampedArray)) {
throw new TypeMismatchError('Expected an integer array')
function getRandomValues(array) {
if (
!(
array instanceof Int8Array ||
array instanceof Uint8Array ||
array instanceof Int16Array ||
array instanceof Uint16Array ||
array instanceof Int32Array ||
array instanceof Uint32Array ||
array instanceof Uint8ClampedArray
)
) {
throw new TypeMismatchError('Expected an integer array');
}

if (array.byteLength > 65536) {
throw new QuotaExceededError('Can only request a maximum of 65536 bytes')
throw new QuotaExceededError('Can only request a maximum of 65536 bytes');
}

// Expo SDK 48+
if (global.expo && global.expo.modules && global.expo.modules.ExpoCrypto && global.expo.modules.ExpoCrypto.getRandomValues) {
if (
global.expo &&
global.expo.modules &&
global.expo.modules.ExpoCrypto &&
global.expo.modules.ExpoCrypto.getRandomValues
) {
// ExpoCrypto.getRandomValues doesn't return the array
global.expo.modules.ExpoCrypto.getRandomValues(array)
return array
global.expo.modules.ExpoCrypto.getRandomValues(array);
return array;
}

// Calling getRandomBase64 in remote debugging mode leads to the error
// "Calling synchronous methods on native modules is not supported in Chrome".
// So in that specific case we fall back to just using Math.random().
if (isRemoteDebuggingInChrome()) {
return insecureRandomValues(array)
return insecureRandomValues(array);
}

base64Decode(getRandomBase64(array.byteLength), new Uint8Array(array.buffer, array.byteOffset, array.byteLength))
base64Decode(getRandomBase64(array.byteLength), new Uint8Array(array.buffer, array.byteOffset, array.byteLength));

return array
return array;
}

function isRemoteDebuggingInChrome () {
function isRemoteDebuggingInChrome() {
// Remote debugging in Chrome is not supported in bridgeless
if ('RN$Bridgeless' in global && RN$Bridgeless === true) {
return false
return false;
}

return __DEV__ && typeof global.nativeCallSyncHook === 'undefined'
return __DEV__ && typeof global.nativeCallSyncHook === 'undefined';
}

export default { getRandomValues };

0 comments on commit d9ca7e1

Please sign in to comment.