diff --git a/Gruntfile.js b/Gruntfile.js index ac233ce0b2..bf5ddfcc45 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -16,7 +16,6 @@ module.exports = function (grunt) { fragments: 'src/platform/web/fragments', static: 'build', dest: 'build', - crypto_js: 'node_modules/crypto-js/src', tools_compiler: __dirname + '/node_modules/google-closure-compiler/compiler.jar', }; diff --git a/package-lock.json b/package-lock.json index 14b3a02edb..8f5486ebbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ }, "devDependencies": { "@ably/vcdiff-decoder": "1.0.4", - "@types/crypto-js": "^4.0.1", "@types/node": "^18.0.0", "@types/request": "^2.48.7", "@types/ws": "^8.2.0", @@ -26,7 +25,6 @@ "chai": "^4.2.0", "copy-webpack-plugin": "^11.0.0", "cors": "~2.7", - "crypto-js": "ably-forks/crypto-js#crypto-lite", "eslint": "^7.13.0", "eslint-plugin-jsdoc": "^40.0.0", "eslint-plugin-security": "^1.4.0", @@ -327,12 +325,6 @@ "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", "dev": true }, - "node_modules/@types/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==", - "dev": true - }, "node_modules/@types/eslint": { "version": "8.37.0", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz", @@ -1819,13 +1811,6 @@ "node": ">= 8" } }, - "node_modules/crypto-js": { - "version": "4.0.0", - "resolved": "git+https://git@github.com/ably-forks/crypto-js.git#07e09b48fd8f850f71c10c9d9307ca4e6ac622a0", - "integrity": "sha512-3RE9Ztinx+QLpEDwcsPTVGMXIIqtS/r+YyD1E7BGXE+CRBcCwkyHWVyl1Aperg5QBOUkg2dtiC8p18a41IKltQ==", - "dev": true, - "license": "MIT" - }, "node_modules/dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -7679,12 +7664,6 @@ "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==", "dev": true }, - "@types/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==", - "dev": true - }, "@types/eslint": { "version": "8.37.0", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz", @@ -8831,12 +8810,6 @@ "which": "^2.0.1" } }, - "crypto-js": { - "version": "git+https://git@github.com/ably-forks/crypto-js.git#07e09b48fd8f850f71c10c9d9307ca4e6ac622a0", - "integrity": "sha512-3RE9Ztinx+QLpEDwcsPTVGMXIIqtS/r+YyD1E7BGXE+CRBcCwkyHWVyl1Aperg5QBOUkg2dtiC8p18a41IKltQ==", - "dev": true, - "from": "crypto-js@ably-forks/crypto-js#crypto-lite" - }, "dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", diff --git a/package.json b/package.json index e97913a2e7..2d1e54f439 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ }, "devDependencies": { "@ably/vcdiff-decoder": "1.0.4", - "@types/crypto-js": "^4.0.1", "@types/node": "^18.0.0", "@types/request": "^2.48.7", "@types/ws": "^8.2.0", @@ -42,7 +41,6 @@ "chai": "^4.2.0", "copy-webpack-plugin": "^11.0.0", "cors": "~2.7", - "crypto-js": "ably-forks/crypto-js#crypto-lite", "eslint": "^7.13.0", "eslint-plugin-jsdoc": "^40.0.0", "eslint-plugin-security": "^1.4.0", diff --git a/src/common/lib/util/utils.ts b/src/common/lib/util/utils.ts index ed4afa4245..8edfa58f5c 100644 --- a/src/common/lib/util/utils.ts +++ b/src/common/lib/util/utils.ts @@ -461,37 +461,9 @@ export function cheapRandStr(): string { * include, not the length of the string. String length produced is not * guaranteed. */ export const randomString = (numBytes: number): string => { - if (Platform.Config.getRandomValues && typeof Uint8Array !== 'undefined') { - const uIntArr = new Uint8Array(numBytes); - (Platform.Config.getRandomValues as Function)(uIntArr); - return Platform.BufferUtils.base64Encode(uIntArr); - } - /* Old browser; fall back to Math.random. Could just use a - * CryptoJS version of the above, but want this to still work in nocrypto - * versions of the library */ - const charset = Platform.BufferUtils.base64CharSet; - /* base64 has 33% overhead; round length up */ - const length = Math.round((numBytes * 4) / 3); - let result = ''; - for (let i = 0; i < length; i++) { - result += charset[randomPosn(charset)]; - } - return result; -}; - -export const randomHexString = (numBytes: number): string => { - if (Platform.Config.getRandomValues && typeof Uint8Array !== 'undefined') { - const uIntArr = new Uint8Array(numBytes); - (Platform.Config.getRandomValues as Function)(uIntArr); - return Platform.BufferUtils.hexEncode(uIntArr); - } - const charset = Platform.BufferUtils.hexCharSet; - const length = numBytes * 2; - let result = ''; - for (let i = 0; i < length; i++) { - result += charset[randomPosn(charset)]; - } - return result; + const uIntArr = new Uint8Array(numBytes); + Platform.Config.getRandomValues(uIntArr); + return Platform.BufferUtils.base64Encode(uIntArr); }; /* Pick n elements at random without replacement from an array */ diff --git a/src/common/platform.ts b/src/common/platform.ts index 03973a09c6..41992c3bc5 100644 --- a/src/common/platform.ts +++ b/src/common/platform.ts @@ -11,7 +11,6 @@ import * as NodeBufferUtils from '../platform/nodejs/lib/util/bufferutils'; type Bufferlike = WebBufferUtils.Bufferlike | NodeBufferUtils.Bufferlike; type BufferUtilsOutput = WebBufferUtils.Output | NodeBufferUtils.Output; type ToBufferOutput = WebBufferUtils.ToBufferOutput | NodeBufferUtils.ToBufferOutput; -type WordArrayLike = WebBufferUtils.WordArrayLike | NodeBufferUtils.WordArrayLike; export default class Platform { static Config: IPlatformConfig; @@ -22,7 +21,7 @@ export default class Platform { BufferUtils object that accepts a broader range of data types than it can in reality handle. */ - static BufferUtils: IBufferUtils; + static BufferUtils: IBufferUtils; /* This should be a class whose static methods implement the ICryptoStatic interface, but (for the same reasons as described in the BufferUtils diff --git a/src/common/types/IBufferUtils.ts b/src/common/types/IBufferUtils.ts index 8e88110714..efa1b51209 100644 --- a/src/common/types/IBufferUtils.ts +++ b/src/common/types/IBufferUtils.ts @@ -1,11 +1,10 @@ -export default interface IBufferUtils { +export default interface IBufferUtils { base64CharSet: string; hexCharSet: string; isBuffer: (buffer: unknown) => buffer is Bufferlike; - isWordArray: (val: unknown) => val is WordArrayLike; // On browser this returns a Uint8Array, on node a Buffer toBuffer: (buffer: Bufferlike) => ToBufferOutput; - toArrayBuffer: (buffer: Bufferlike | WordArrayLike) => ArrayBuffer; + toArrayBuffer: (buffer: Bufferlike) => ArrayBuffer; base64Encode: (buffer: Bufferlike) => string; base64Decode: (string: string) => Output; hexEncode: (buffer: Bufferlike) => string; @@ -15,6 +14,5 @@ export default interface IBufferUtils boolean; byteLength: (buffer: Bufferlike) => number; arrayBufferViewToBuffer: (arrayBufferView: ArrayBufferView) => Bufferlike; - toWordArray: (buffer: Bufferlike | number[]) => WordArrayLike; hmacSha256(message: Bufferlike, key: Bufferlike): Output; } diff --git a/src/common/types/IPlatformConfig.d.ts b/src/common/types/IPlatformConfig.d.ts index f4766ea2e5..ed214699ed 100644 --- a/src/common/types/IPlatformConfig.d.ts +++ b/src/common/types/IPlatformConfig.d.ts @@ -17,7 +17,7 @@ export interface IPlatformConfig { stringByteSize: Buffer.byteLength; addEventListener?: typeof window.addEventListener | typeof global.addEventListener | null; Promise: typeof Promise; - getRandomValues?: (arr: ArrayBufferView, callback?: (error: Error | null) => void) => void; + getRandomValues: (arr: ArrayBufferView, callback?: (error: Error | null) => void) => void; userAgent?: string | null; inherits?: typeof import('util').inherits; currentUrl?: string; diff --git a/src/common/types/crypto-js.d.ts b/src/common/types/crypto-js.d.ts deleted file mode 100644 index dde4ea6b17..0000000000 --- a/src/common/types/crypto-js.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -declare module 'crypto-js/build' { - import CryptoJS from 'crypto-js'; - import algo = CryptoJS.algo; - export { algo }; -} - -declare module 'crypto-js/build/lib-typedarrays' { - import CryptoJS from 'crypto-js'; - export default CryptoJS.lib.WordArray; -} - -declare module 'crypto-js/build/hmac-sha256' { - import CryptoJS from 'crypto-js'; - export default CryptoJS.HmacSHA256; -} diff --git a/src/platform/nodejs/lib/util/bufferutils.ts b/src/platform/nodejs/lib/util/bufferutils.ts index bded068114..e1c6fbcb67 100644 --- a/src/platform/nodejs/lib/util/bufferutils.ts +++ b/src/platform/nodejs/lib/util/bufferutils.ts @@ -4,9 +4,8 @@ import crypto from 'crypto'; export type Bufferlike = Buffer | ArrayBuffer | ArrayBufferView; export type Output = Buffer; export type ToBufferOutput = Buffer; -export type WordArrayLike = never; -class BufferUtils implements IBufferUtils { +class BufferUtils implements IBufferUtils { base64CharSet: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; hexCharSet: string = '0123456789abcdef'; @@ -41,7 +40,7 @@ class BufferUtils implements IBufferUtils void) { + RNRandomBytes.randomBytes(byteLength, function (err: Error | null, base64String: string | null) { + callback(err, base64String ? bufferUtils.toArrayBuffer(bufferUtils.base64Decode(base64String)) : null); + }); + }; + // Installing @types/react-native would fix this but conflicts with @types/node + // See https://github.com/DefinitelyTyped/DefinitelyTyped/issues/15960 + // eslint-disable-next-line @typescript-eslint/no-var-requires + })(require('react-native').NativeModules.RNRandomBytes); + return { agent: 'reactnative', logTimestamps: true, @@ -33,15 +44,23 @@ export default function (bufferUtils: typeof BufferUtils): IPlatformConfig { TextEncoder: global.TextEncoder, TextDecoder: global.TextDecoder, Promise: global.Promise, - getRandomArrayBuffer: (function (RNRandomBytes) { - return function (byteLength: number, callback: (err: Error | null, result: ArrayBuffer | null) => void) { - RNRandomBytes.randomBytes(byteLength, function (err: Error | null, base64String: string | null) { - callback(err, base64String ? bufferUtils.toArrayBuffer(bufferUtils.base64Decode(base64String)) : null); - }); - }; - // Installing @types/react-native would fix this but conflicts with @types/node - // See https://github.com/DefinitelyTyped/DefinitelyTyped/issues/15960 - // eslint-disable-next-line @typescript-eslint/no-var-requires - })(require('react-native').NativeModules.RNRandomBytes), + getRandomArrayBuffer, + getRandomValues: (arr: ArrayBufferView, callback?: (error: Error | null) => void) => { + getRandomArrayBuffer(arr.byteLength, (err, randomArrayBuffer) => { + if (err) { + if (callback) { + callback(err); + } + return; + } + + const randomArrayBufferDataView = new DataView(randomArrayBuffer!); + const outputDataView = new DataView(arr.buffer, arr.byteOffset, arr.byteLength); + + for (let i = 0; i < randomArrayBufferDataView.byteLength; i++) { + outputDataView.setUint8(i, randomArrayBufferDataView.getUint8(i)); + } + }); + }, }; } diff --git a/src/platform/web/config.ts b/src/platform/web/config.ts index 0877337f3e..6aac75d88b 100644 --- a/src/platform/web/config.ts +++ b/src/platform/web/config.ts @@ -5,8 +5,6 @@ import * as Utils from 'common/lib/util/utils'; // Workaround for salesforce lightning locker compat const globalObject = Utils.getGlobalObject(); -declare var msCrypto: typeof crypto; // for IE11 - if (typeof Window === 'undefined' && typeof WorkerGlobalScope === 'undefined') { console.log( "Warning: this distribution of Ably is intended for browsers. On nodejs, please use the 'ably' package on npm" @@ -60,17 +58,12 @@ const Config: IPlatformConfig = { TextEncoder: globalObject.TextEncoder, TextDecoder: globalObject.TextDecoder, Promise: globalObject.Promise, - getRandomValues: (function (crypto) { - if (crypto === undefined) { - return undefined; + getRandomValues: function (arr: ArrayBufferView, callback?: (error: Error | null) => void) { + crypto.getRandomValues(arr); + if (callback) { + callback(null); } - return function (arr: ArrayBufferView, callback?: (error: Error | null) => void) { - crypto.getRandomValues(arr); - if (callback) { - callback(null); - } - }; - })(globalObject.crypto || msCrypto), + }, }; export default Config; diff --git a/src/platform/web/lib/util/bufferutils.ts b/src/platform/web/lib/util/bufferutils.ts index 392395fadb..279112ed95 100644 --- a/src/platform/web/lib/util/bufferutils.ts +++ b/src/platform/web/lib/util/bufferutils.ts @@ -1,4 +1,3 @@ -import WordArray from 'crypto-js/build/lib-typedarrays'; import Platform from 'common/platform'; import IBufferUtils from 'common/types/IBufferUtils'; import { hmac as hmacSha256 } from './hmac-sha256'; @@ -10,16 +9,11 @@ import { hmac as hmacSha256 } from './hmac-sha256'; export type Bufferlike = BufferSource; export type Output = Bufferlike; export type ToBufferOutput = Uint8Array; -export type WordArrayLike = WordArray; -class BufferUtils implements IBufferUtils { +class BufferUtils implements IBufferUtils { base64CharSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; hexCharSet = '0123456789abcdef'; - isWordArray(ob: unknown): ob is WordArray { - return ob !== null && ob !== undefined && (ob as WordArray).sigBytes !== undefined; - } - // // https://gist.githubusercontent.com/jonleighton/958841/raw/f200e30dfe95212c0165ccf1ae000ca51e9de803/gistfile1.js uint8ViewToBase64(bytes: Uint8Array) { let base64 = ''; @@ -104,32 +98,13 @@ class BufferUtils implements IBufferUtils>> 2] >>> (24 - (i % 4) * 8)) & 0xff; - } - - return uint8View; - } return this.toBuffer(buffer).buffer; } - toWordArray(buffer: Bufferlike | number[]) { - if (ArrayBuffer.isView(buffer)) { - buffer = buffer.buffer; - } - return this.isWordArray(buffer) ? buffer : WordArray.create(buffer as number[]); - } - base64Encode(buffer: Bufferlike) { return this.uint8ViewToBase64(this.toBuffer(buffer)); } diff --git a/src/platform/web/lib/util/crypto.ts b/src/platform/web/lib/util/crypto.ts index 1598981b51..cbc701150d 100644 --- a/src/platform/web/lib/util/crypto.ts +++ b/src/platform/web/lib/util/crypto.ts @@ -22,7 +22,6 @@ var CryptoFactory = function (config: IPlatformConfig, bufferUtils: typeof Buffe var DEFAULT_MODE = 'cbc'; var DEFAULT_BLOCKLENGTH = 16; // bytes var DEFAULT_BLOCKLENGTH_WORDS = 4; // 32-bit words - var UINT32_SUP = 0x100000000; /** * Internal: generate an array of secure random data corresponding to the given length of bytes @@ -32,32 +31,17 @@ var CryptoFactory = function (config: IPlatformConfig, bufferUtils: typeof Buffe var generateRandom: (byteLength: number, callback: (error: Error | null, result: ArrayBuffer | null) => void) => void; if (config.getRandomArrayBuffer) { generateRandom = config.getRandomArrayBuffer; - } else if (typeof Uint32Array !== 'undefined' && config.getRandomValues) { + } else { var blockRandomArray = new Uint32Array(DEFAULT_BLOCKLENGTH_WORDS); generateRandom = function (bytes, callback) { var words = bytes / 4, nativeArray = words == DEFAULT_BLOCKLENGTH_WORDS ? blockRandomArray : new Uint32Array(words); - config.getRandomValues!(nativeArray, function (err) { + config.getRandomValues(nativeArray, function (err) { if (typeof callback !== 'undefined') { callback(err, bufferUtils.toArrayBuffer(nativeArray)); } }); }; - } else { - generateRandom = function (bytes, callback) { - Logger.logAction( - Logger.LOG_MAJOR, - 'Ably.Crypto.generateRandom()', - 'Warning: the browser you are using does not support secure cryptographically secure randomness generation; falling back to insecure Math.random()' - ); - var words = bytes / 4, - array = new Uint32Array(words); - for (var i = 0; i < words; i++) { - array[i] = Math.floor(Math.random() * UINT32_SUP); - } - - callback(null, bufferUtils.toArrayBuffer(array)); - }; } /** @@ -123,10 +107,7 @@ var CryptoFactory = function (config: IPlatformConfig, bufferUtils: typeof Buffe * classes and interfaces here. *- * Secure random data for creation of Initialization Vectors (IVs) and keys - * is obtained from window.crypto.getRandomValues if available, or from - * Math.random() if not. Clients who do not want to depend on Math.random() - * should polyfill window.crypto.getRandomValues with a library that seeds - * a PRNG with real entropy. + * is obtained from window.crypto.getRandomValues. * * Each message payload is encrypted with an IV in CBC mode, and the IV is * concatenated with the resulting raw ciphertext to construct the "ciphertext" diff --git a/test/common/globals/named_dependencies.js b/test/common/globals/named_dependencies.js index 3228c893c6..4fc3a7407e 100644 --- a/test/common/globals/named_dependencies.js +++ b/test/common/globals/named_dependencies.js @@ -4,8 +4,6 @@ define(function () { // Ably modules ably: { browser: 'build/ably', node: 'build/ably-node' }, 'ably.noencryption': { browser: 'build/ably.noencryption' }, - base64: { browser: 'node_modules/crypto-js/build/enc-base64', node: 'skip' }, - utf8: { browser: 'node_modules/crypto-js/build/enc-utf8', node: 'skip' }, 'vcdiff-decoder': { browser: 'node_modules/@ably/vcdiff-decoder/dist/vcdiff-decoder', node: 'node_modules/@ably/vcdiff-decoder', diff --git a/test/common/modules/testapp_manager.js b/test/common/modules/testapp_manager.js index a6cc42253d..41724490e3 100644 --- a/test/common/modules/testapp_manager.js +++ b/test/common/modules/testapp_manager.js @@ -2,14 +2,13 @@ /* global define, isNativescript, fetch */ /* testapp module is responsible for setting up and tearing down apps in the test environment */ -define(['globals', 'base64', 'utf8', 'ably'], function (ablyGlobals, Base64, UTF8, ably) { +define(['globals', 'ably'], function (ablyGlobals, ably) { var restHost = ablyGlobals.restHost || prefixDomainWithEnvironment('rest.ably.io', ablyGlobals.environment), tlsPort = ablyGlobals.tlsPort; var isBrowser = typeof window === 'object', isNativescript = typeof global === 'object' && global.isNativescript, httpReq = httpReqFunction(), - toBase64 = base64Function(), loadJsonData = loadJsonDataNode, testResourcesPath = 'test/common/ably-common/test-resources/'; @@ -42,20 +41,10 @@ define(['globals', 'base64', 'utf8', 'ably'], function (ablyGlobals, Base64, UTF return null; } - function NSbase64Function(d) { - return Base64.stringify(d); - } - - function base64Function() { - if (isBrowser) { - return function (str) { - return Base64.stringify(UTF8.parse(str)); - }; - } else { - return function (str) { - return Buffer.from(str, 'ascii').toString('base64'); - }; - } + function toBase64(str) { + var bufferUtils = ably.Realtime.Platform.BufferUtils; + var buffer = bufferUtils.utf8Encode(str); + return bufferUtils.base64Encode(buffer); } function httpReqFunction() { diff --git a/test/support/browser_file_list.js b/test/support/browser_file_list.js index 0dbf2cd18b..ad830a97cc 100644 --- a/test/support/browser_file_list.js +++ b/test/support/browser_file_list.js @@ -11,9 +11,6 @@ window.__testFiles__.files = { 'browser/lib/util/base64.js': true, 'node_modules/async/lib/async.js': true, 'node_modules/@ably/vcdiff-decoder/dist/vcdiff-decoder.js': true, - 'node_modules/crypto-js/build/enc-base64.js': true, - 'node_modules/crypto-js/build/enc-utf8.js': true, - 'node_modules/crypto-js/build/core.js': true, 'test/common/globals/environment.js': true, 'test/common/globals/named_dependencies.js': true, 'test/common/modules/client_module.js': true, diff --git a/test/web_server b/test/web_server index 1549b96692..f42134d906 100755 --- a/test/web_server +++ b/test/web_server @@ -35,15 +35,6 @@ server.get('/', function(req, res) { } }); - -/** - * This is a rather hacky workaround since crypto-js base64 depends on node_modules/crypto-js/build/core but will request the file from the root level of the server. - * I'm leaving this here for now in lieu of a more elegant solution as the 'real' solution is to improve the way we use dependencies in these tests. - */ -server.get('/core.js', function (req, res) { - res.redirect('/node_modules/crypto-js/build/core.js'); -}); - server.use('/node_modules', express.static(__dirname + '/../node_modules')); server.use('/test', express.static(__dirname)); server.use('/browser', express.static(__dirname + '/../src/web')); diff --git a/webpack.config.js b/webpack.config.js index 085d573cbf..2c0c981ca7 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -79,9 +79,6 @@ const browserConfig = { crypto: false, }, }, - externals: { - 'crypto-js': true, - }, optimization: { minimize: false, },