From 0e7989111f871ed0387fab7453f153125e0145e6 Mon Sep 17 00:00:00 2001 From: n1474335 Date: Mon, 25 Dec 2017 23:11:52 +0000 Subject: [PATCH] Removed CryptoJS from Utils.js. UTF8 conversion is now achieved with the much smaller and actively maintained utf8 library. --- package-lock.json | 5 ++ package.json | 1 + src/core/Dish.js | 24 ++++++ src/core/Utils.js | 118 +++++++++-------------------- src/core/config/modules/Default.js | 1 - src/core/operations/BitwiseOp.js | 18 ++--- src/core/operations/Cipher.js | 95 ++++++++++++++++------- src/web/HighlighterWaiter.js | 2 +- src/web/OutputWaiter.js | 10 +++ 9 files changed, 151 insertions(+), 123 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f0fddf5..5bfed4eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8663,6 +8663,11 @@ } } }, + "utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, "util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", diff --git a/package.json b/package.json index f62b95e2..ac864d02 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "sladex-blowfish": "^0.8.1", "sortablejs": "^1.7.0", "split.js": "^1.3.5", + "utf8": "^3.0.0", "vkbeautify": "^0.99.3", "xmldom": "^0.1.27", "xpath": "0.0.24", diff --git a/src/core/Dish.js b/src/core/Dish.js index 3cd1c6f3..f1093966 100755 --- a/src/core/Dish.js +++ b/src/core/Dish.js @@ -217,4 +217,28 @@ Dish.prototype.valid = function() { } }; + +/** + * Determines how much space the Dish takes up. + * Numbers in JavaScript are 64-bit floating point, however for the purposes of the Dish, + * we measure how many bytes are taken up when the number is written as a string. + * + * @returns {number} +*/ +Dish.prototype.size = function() { + switch (this.type) { + case Dish.BYTE_ARRAY: + case Dish.STRING: + case Dish.HTML: + return this.value.length; + case Dish.NUMBER: + return this.value.toString().length; + case Dish.ARRAY_BUFFER: + return this.value.byteLength; + default: + return -1; + } +}; + + export default Dish; diff --git a/src/core/Utils.js b/src/core/Utils.js index f2a992e5..a4a455a2 100755 --- a/src/core/Utils.js +++ b/src/core/Utils.js @@ -1,4 +1,4 @@ -import CryptoJS from "crypto-js"; +import utf8 from "utf8"; /** @@ -340,6 +340,32 @@ const Utils = { }, + /** + * Coverts data of varying types to a byteArray. + * Accepts hex, Base64, UTF8 and Latin1 strings. + * + * @param {string} str + * @param {string} type - One of "Hex", "Base64", "UTF8" or "Latin1" + * @returns {byteArray} + * + * @example + * // returns [] + */ + convertToByteArray: function(str, type) { + switch (type.toLowerCase()) { + case "hex": + return Utils.fromHex(str); + case "base64": + return Utils.fromBase64(str, null, "byteArray"); + case "utf8": + return Utils.strToUtf8ByteArray(str); + case "latin1": + default: + return Utils.strToByteArray(str); + } + }, + + /** * Converts a string to a byte array. * Treats the string as UTF-8 if any values are over 255. @@ -381,17 +407,17 @@ const Utils = { * Utils.strToUtf8ByteArray("你好"); */ strToUtf8ByteArray: function(str) { - let wordArray = CryptoJS.enc.Utf8.parse(str), - byteArray = Utils.wordArrayToByteArray(wordArray); + const utf8Str = utf8.encode(str); - if (str.length !== wordArray.sigBytes) { + if (str.length !== utf8Str.length) { if (ENVIRONMENT_IS_WORKER()) { self.setOption("attemptHighlight", false); } else if (ENVIRONMENT_IS_WEB()) { window.app.options.attemptHighlight = false; } } - return byteArray; + + return Utils.strToByteArray(utf8Str); }, @@ -443,26 +469,21 @@ const Utils = { * Utils.byteArrayToUtf8([228,189,160,229,165,189]); */ byteArrayToUtf8: function(byteArray) { + const str = Utils.byteArrayToChars(byteArray); try { - // Try to output data as UTF-8 string - const words = []; - for (let i = 0; i < byteArray.length; i++) { - words[i >>> 2] |= byteArray[i] << (24 - (i % 4) * 8); - } - let wordArray = new CryptoJS.lib.WordArray.init(words, byteArray.length), - str = CryptoJS.enc.Utf8.stringify(wordArray); + const utf8Str = utf8.decode(str); - if (str.length !== wordArray.sigBytes) { + if (str.length !== utf8Str.length) { if (ENVIRONMENT_IS_WORKER()) { self.setOption("attemptHighlight", false); } else if (ENVIRONMENT_IS_WEB()) { window.app.options.attemptHighlight = false; } } - return str; + return utf8Str; } catch (err) { // If it fails, treat it as ANSI - return Utils.byteArrayToChars(byteArray); + return str; } }, @@ -490,30 +511,6 @@ const Utils = { }, - /** - * Converts a CryptoJS.lib.WordArray to a byteArray. - * - * @param {CryptoJS.lib.WordArray} wordArray - * @returns {byteArray} - * - * @example - * // returns [84, 101, 115, 116] - * Utils.wordArrayToByteArray(CryptoJS.enc.Hex.parse("54657374")); - */ - wordArrayToByteArray: function(wordArray) { - if (wordArray.sigBytes <= 0) return []; - - let words = wordArray.words, - byteArray = []; - - for (let i = 0; i < wordArray.sigBytes; i++) { - byteArray.push((words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff); - } - - return byteArray; - }, - - /** * Base64's the input byte array using the given alphabet, returning a string. * @@ -1248,21 +1245,6 @@ const Utils = { "None": /\s+/g // Included here to remove whitespace when there shouldn't be any }, - - /** - * A mapping of string formats to their classes in the CryptoJS library. - * @constant - */ - format: { - "Hex": CryptoJS.enc.Hex, - "Base64": CryptoJS.enc.Base64, - "UTF8": CryptoJS.enc.Utf8, - "UTF16": CryptoJS.enc.Utf16, - "UTF16LE": CryptoJS.enc.Utf16LE, - "UTF16BE": CryptoJS.enc.Utf16BE, - "Latin1": CryptoJS.enc.Latin1, - }, - }; export default Utils; @@ -1374,31 +1356,3 @@ Array.prototype.equals = function(other) { String.prototype.count = function(chr) { return this.split(chr).length - 1; }; - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Library overrides /////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Override for the CryptoJS Hex encoding parser to remove whitespace before attempting to parse - * the hex string. - * - * @param {string} hexStr - * @returns {CryptoJS.lib.WordArray} - */ -CryptoJS.enc.Hex.parse = function (hexStr) { - // Remove whitespace - hexStr = hexStr.replace(/\s/g, ""); - - // Shortcut - const hexStrLength = hexStr.length; - - // Convert - const words = []; - for (let i = 0; i < hexStrLength; i += 2) { - words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4); - } - - return new CryptoJS.lib.WordArray.init(words, hexStrLength / 2); -}; diff --git a/src/core/config/modules/Default.js b/src/core/config/modules/Default.js index dec015a5..eea41164 100644 --- a/src/core/config/modules/Default.js +++ b/src/core/config/modules/Default.js @@ -37,7 +37,6 @@ import UUID from "../../operations/UUID.js"; * * Libraries: * - Utils.js - * - CryptoJS * - otp * * @author n1474335 [n1474335@gmail.com] diff --git a/src/core/operations/BitwiseOp.js b/src/core/operations/BitwiseOp.js index 7512aa32..f2551cba 100755 --- a/src/core/operations/BitwiseOp.js +++ b/src/core/operations/BitwiseOp.js @@ -67,7 +67,7 @@ const BitwiseOp = { * @constant * @default */ - KEY_FORMAT: ["Hex", "Base64", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1"], + KEY_FORMAT: ["Hex", "Base64", "UTF8", "Latin1"], /** * XOR operation. @@ -77,12 +77,10 @@ const BitwiseOp = { * @returns {byteArray} */ runXor: function (input, args) { - let key = Utils.format[args[0].option].parse(args[0].string || ""), + const key = Utils.convertToByteArray(args[0].string || "", args[0].option), scheme = args[1], nullPreserving = args[2]; - key = Utils.wordArrayToByteArray(key); - return BitwiseOp._bitOp(input, key, BitwiseOp._xor, nullPreserving, scheme); }, @@ -200,8 +198,7 @@ const BitwiseOp = { * @returns {byteArray} */ runAnd: function (input, args) { - let key = Utils.format[args[0].option].parse(args[0].string || ""); - key = Utils.wordArrayToByteArray(key); + const key = Utils.convertToByteArray(args[0].string || "", args[0].option); return BitwiseOp._bitOp(input, key, BitwiseOp._and); }, @@ -215,8 +212,7 @@ const BitwiseOp = { * @returns {byteArray} */ runOr: function (input, args) { - let key = Utils.format[args[0].option].parse(args[0].string || ""); - key = Utils.wordArrayToByteArray(key); + const key = Utils.convertToByteArray(args[0].string || "", args[0].option); return BitwiseOp._bitOp(input, key, BitwiseOp._or); }, @@ -230,8 +226,7 @@ const BitwiseOp = { * @returns {byteArray} */ runAdd: function (input, args) { - let key = Utils.format[args[0].option].parse(args[0].string || ""); - key = Utils.wordArrayToByteArray(key); + const key = Utils.convertToByteArray(args[0].string || "", args[0].option); return BitwiseOp._bitOp(input, key, BitwiseOp._add); }, @@ -245,8 +240,7 @@ const BitwiseOp = { * @returns {byteArray} */ runSub: function (input, args) { - let key = Utils.format[args[0].option].parse(args[0].string || ""); - key = Utils.wordArrayToByteArray(key); + const key = Utils.convertToByteArray(args[0].string || "", args[0].option); return BitwiseOp._bitOp(input, key, BitwiseOp._sub); }, diff --git a/src/core/operations/Cipher.js b/src/core/operations/Cipher.js index 465c3e9e..227c2735 100755 --- a/src/core/operations/Cipher.js +++ b/src/core/operations/Cipher.js @@ -61,9 +61,9 @@ const Cipher = { * @returns {string} */ _enc: function (algo, input, args) { - let key = Utils.format[args[0].option].parse(args[0].string || ""), - iv = Utils.format[args[1].option].parse(args[1].string || ""), - salt = Utils.format[args[2].option].parse(args[2].string || ""), + let key = Cipher._format[args[0].option].parse(args[0].string || ""), + iv = Cipher._format[args[1].option].parse(args[1].string || ""), + salt = Cipher._format[args[2].option].parse(args[2].string || ""), mode = CryptoJS.mode[args[3]], padding = CryptoJS.pad[args[4]], resultOption = args[5].toLowerCase(), @@ -83,12 +83,12 @@ const Cipher = { let result = ""; if (resultOption === "show all") { - result += "Key: " + encrypted.key.toString(Utils.format[outputFormat]); - result += "\nIV: " + encrypted.iv.toString(Utils.format[outputFormat]); - if (encrypted.salt) result += "\nSalt: " + encrypted.salt.toString(Utils.format[outputFormat]); - result += "\n\nCiphertext: " + encrypted.ciphertext.toString(Utils.format[outputFormat]); + result += "Key: " + encrypted.key.toString(Cipher._format[outputFormat]); + result += "\nIV: " + encrypted.iv.toString(Cipher._format[outputFormat]); + if (encrypted.salt) result += "\nSalt: " + encrypted.salt.toString(Cipher._format[outputFormat]); + result += "\n\nCiphertext: " + encrypted.ciphertext.toString(Cipher._format[outputFormat]); } else { - result = encrypted[resultOption].toString(Utils.format[outputFormat]); + result = encrypted[resultOption].toString(Cipher._format[outputFormat]); } return result; @@ -105,9 +105,9 @@ const Cipher = { * @returns {string} */ _dec: function (algo, input, args) { - let key = Utils.format[args[0].option].parse(args[0].string || ""), - iv = Utils.format[args[1].option].parse(args[1].string || ""), - salt = Utils.format[args[2].option].parse(args[2].string || ""), + let key = Cipher._format[args[0].option].parse(args[0].string || ""), + iv = Cipher._format[args[1].option].parse(args[1].string || ""), + salt = Cipher._format[args[2].option].parse(args[2].string || ""), mode = CryptoJS.mode[args[3]], padding = CryptoJS.pad[args[4]], inputFormat = args[5], @@ -118,7 +118,7 @@ const Cipher = { return "No input"; } - const ciphertext = Utils.format[inputFormat].parse(input); + const ciphertext = Cipher._format[inputFormat].parse(input); if (iv.sigBytes === 0) { // Use passphrase rather than key. Need to convert it to a string. @@ -136,7 +136,7 @@ const Cipher = { let result; try { - result = decrypted.toString(Utils.format[outputFormat]); + result = decrypted.toString(Cipher._format[outputFormat]); } catch (err) { result = "Decrypt error: " + err.message; } @@ -260,7 +260,7 @@ const Cipher = { * @returns {string} */ runBlowfishEnc: function (input, args) { - let key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1), + let key = Cipher._format[args[0].option].parse(args[0].string).toString(Cipher._format.Latin1), mode = args[1], outputFormat = args[2]; @@ -272,7 +272,7 @@ const Cipher = { }), enc = CryptoJS.enc.Hex.parse(encHex); - return enc.toString(Utils.format[outputFormat]); + return enc.toString(Cipher._format[outputFormat]); }, @@ -284,13 +284,13 @@ const Cipher = { * @returns {string} */ runBlowfishDec: function (input, args) { - let key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1), + let key = Cipher._format[args[0].option].parse(args[0].string).toString(Cipher._format.Latin1), mode = args[1], inputFormat = args[2]; if (key.length === 0) return "Enter a key"; - input = Utils.format[inputFormat].parse(input); + input = Cipher._format[inputFormat].parse(input); return Blowfish.decrypt(input.toString(CryptoJS.enc.Base64), key, { outputType: 0, // This actually means inputType. The library is weird. @@ -329,14 +329,14 @@ const Cipher = { salt = CryptoJS.enc.Hex.parse(args[3] || ""), inputFormat = args[4], outputFormat = args[5], - passphrase = Utils.format[inputFormat].parse(input), + passphrase = Cipher._format[inputFormat].parse(input), key = CryptoJS.PBKDF2(passphrase, salt, { keySize: keySize, hasher: CryptoJS.algo[hasher], iterations: iterations, }); - return key.toString(Utils.format[outputFormat]); + return key.toString(Cipher._format[outputFormat]); }, @@ -354,14 +354,14 @@ const Cipher = { salt = CryptoJS.enc.Hex.parse(args[3] || ""), inputFormat = args[4], outputFormat = args[5], - passphrase = Utils.format[inputFormat].parse(input), + passphrase = Cipher._format[inputFormat].parse(input), key = CryptoJS.EvpKDF(passphrase, salt, { keySize: keySize, hasher: CryptoJS.algo[hasher], iterations: iterations, }); - return key.toString(Utils.format[outputFormat]); + return key.toString(Cipher._format[outputFormat]); }, @@ -373,11 +373,11 @@ const Cipher = { * @returns {string} */ runRc4: function (input, args) { - let message = Utils.format[args[1]].parse(input), - passphrase = Utils.format[args[0].option].parse(args[0].string), + let message = Cipher._format[args[1]].parse(input), + passphrase = Cipher._format[args[0].option].parse(args[0].string), encrypted = CryptoJS.RC4.encrypt(message, passphrase); - return encrypted.ciphertext.toString(Utils.format[args[2]]); + return encrypted.ciphertext.toString(Cipher._format[args[2]]); }, @@ -395,12 +395,12 @@ const Cipher = { * @returns {string} */ runRc4drop: function (input, args) { - let message = Utils.format[args[1]].parse(input), - passphrase = Utils.format[args[0].option].parse(args[0].string), + let message = Cipher._format[args[1]].parse(input), + passphrase = Cipher._format[args[0].option].parse(args[0].string), drop = args[3], encrypted = CryptoJS.RC4Drop.encrypt(message, passphrase, { drop: drop }); - return encrypted.ciphertext.toString(Utils.format[args[2]]); + return encrypted.ciphertext.toString(Cipher._format[args[2]]); }, @@ -783,6 +783,23 @@ const Cipher = { return output; }, + + /** + * A mapping of string formats to their classes in the CryptoJS library. + * + * @private + * @constant + */ + _format: { + "Hex": CryptoJS.enc.Hex, + "Base64": CryptoJS.enc.Base64, + "UTF8": CryptoJS.enc.Utf8, + "UTF16": CryptoJS.enc.Utf16, + "UTF16LE": CryptoJS.enc.Utf16LE, + "UTF16BE": CryptoJS.enc.Utf16BE, + "Latin1": CryptoJS.enc.Latin1, + }, + }; export default Cipher; @@ -827,3 +844,27 @@ CryptoJS.kdf.OpenSSL.execute = function (password, keySize, ivSize, salt) { // Return params return CryptoJS.lib.CipherParams.create({ key: key, iv: iv, salt: salt }); }; + + +/** + * Override for the CryptoJS Hex encoding parser to remove whitespace before attempting to parse + * the hex string. + * + * @param {string} hexStr + * @returns {CryptoJS.lib.WordArray} + */ +CryptoJS.enc.Hex.parse = function (hexStr) { + // Remove whitespace + hexStr = hexStr.replace(/\s/g, ""); + + // Shortcut + const hexStrLength = hexStr.length; + + // Convert + const words = []; + for (let i = 0; i < hexStrLength; i += 2) { + words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4); + } + + return new CryptoJS.lib.WordArray.init(words, hexStrLength / 2); +}; diff --git a/src/web/HighlighterWaiter.js b/src/web/HighlighterWaiter.js index c650e6ba..d9980fec 100755 --- a/src/web/HighlighterWaiter.js +++ b/src/web/HighlighterWaiter.js @@ -402,7 +402,7 @@ HighlighterWaiter.prototype.highlight = function(textarea, highlighter, pos) { // Check if there is a carriage return in the output dish as this will not // be displayed by the HTML textarea and will mess up highlighting offsets. - if (!this.app.dishStr || this.app.dishStr.indexOf("\r") >= 0) return false; + if (this.manager.output.containsCR()) return false; const startPlaceholder = "[startHighlight]"; const startPlaceholderRegex = /\[startHighlight\]/g; diff --git a/src/web/OutputWaiter.js b/src/web/OutputWaiter.js index a5576f31..076d62b2 100755 --- a/src/web/OutputWaiter.js +++ b/src/web/OutputWaiter.js @@ -282,4 +282,14 @@ OutputWaiter.prototype.setStatusMsg = function(msg) { el.textContent = msg; }; + +/** + * Returns true if the output contains carriage returns + * + * @returns {boolean} + */ +OutputWaiter.prototype.containsCR = function() { + return this.app.dishStr.indexOf("\r") >= 0; +}; + export default OutputWaiter;