diff --git a/.changeset/pretty-teachers-press.md b/.changeset/pretty-teachers-press.md new file mode 100644 index 00000000..6d86f358 --- /dev/null +++ b/.changeset/pretty-teachers-press.md @@ -0,0 +1,5 @@ +--- +'@powersync/react-native': patch +--- + +Updated crypto.js vendor file to include fast-base64-decode dependency instead of bundling it. diff --git a/packages/react-native/vendor/crypto.js b/packages/react-native/vendor/crypto.js index 454e9ad0..e6cd6b16 100644 --- a/packages/react-native/vendor/crypto.js +++ b/packages/react-native/vendor/crypto.js @@ -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 };