From 10f7c2cc09b35e7f35149d6be1f9cb36beeb7b2d Mon Sep 17 00:00:00 2001 From: Piotr Kaminski Date: Tue, 11 Oct 2016 23:43:03 -0700 Subject: [PATCH 1/5] Make AES reset a no-op after first call, since data doesn't change. The AES _doReset function sets up a bunch of structures that aren't actually modified when the cipher runs. We can save a bunch of time by re-using previously set up data on subsequent calls to _doReset. --- src/aes.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/aes.js b/src/aes.js index 80eff25..df7e1b2 100644 --- a/src/aes.js +++ b/src/aes.js @@ -76,6 +76,8 @@ */ var AES = C_algo.AES = BlockCipher.extend({ _doReset: function () { + if (this._nRounds) return; + // Shortcuts var key = this._key; var keyWords = key.words; From 2f7fa2b0809f155f05e68f2381e90e441b8125d6 Mon Sep 17 00:00:00 2001 From: Piotr Kaminski Date: Tue, 11 Oct 2016 23:45:20 -0700 Subject: [PATCH 2/5] Improve enc.Base64 parse performance by x10. I replaced the linear map.indexOf() lookup with a reverse lookup table (an array of charCodes turned out to be significantly faster than an object with actual char keys, even though both are O(1)), for a speedup of about x2 on large-ish strings. The change is compatible with reuses of enc.Base64 that provide a different map. I also extracted the inner loop into its own function, since for some reason Chrome was consistently de-optimizing the parse function when the loop was embedded in it. This way, both functions get optimized for an additional speedup of about x5 in my tests. --- src/enc-base64.js | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/enc-base64.js b/src/enc-base64.js index 1d09ec9..67fce1d 100644 --- a/src/enc-base64.js +++ b/src/enc-base64.js @@ -73,32 +73,43 @@ // Shortcuts var base64StrLength = base64Str.length; var map = this._map; + var reverseMap = this._reverseMap; + + if (!reverseMap) { + reverseMap = this._reverseMap = []; + for (var j = 0; j < map.length; j++) { + reverseMap[map.charCodeAt(j)] = j; + } + } // Ignore padding var paddingChar = map.charAt(64); if (paddingChar) { var paddingIndex = base64Str.indexOf(paddingChar); - if (paddingIndex != -1) { + if (paddingIndex !== -1) { base64StrLength = paddingIndex; } } // Convert - var words = []; - var nBytes = 0; - for (var i = 0; i < base64StrLength; i++) { - if (i % 4) { - var bits1 = map.indexOf(base64Str.charAt(i - 1)) << ((i % 4) * 2); - var bits2 = map.indexOf(base64Str.charAt(i)) >>> (6 - (i % 4) * 2); - var bitsCombined = bits1 | bits2; - words[nBytes >>> 2] |= (bitsCombined) << (24 - (nBytes % 4) * 8); - nBytes++; - } - } + return parseLoop(base64Str, base64StrLength, reverseMap); - return WordArray.create(words, nBytes); }, _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' }; + + function parseLoop(base64Str, base64StrLength, reverseMap) { + var words = []; + var nBytes = 0; + for (var i = 0; i < base64StrLength; i++) { + if (i % 4) { + var bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << ((i % 4) * 2); + var bits2 = reverseMap[base64Str.charCodeAt(i)] >>> (6 - (i % 4) * 2); + words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8); + nBytes++; + } + } + return WordArray.create(words, nBytes); + } }()); From 8edc21ea8610f2ab8e95c244b690f05bb776062f Mon Sep 17 00:00:00 2001 From: evanvosberg Date: Thu, 27 Oct 2016 02:53:27 +0200 Subject: [PATCH 3/5] Use Object.create() and make manipulating F.prototype a fallback solution. --- src/core.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/core.js b/src/core.js index c318ca7..8903bf8 100644 --- a/src/core.js +++ b/src/core.js @@ -2,6 +2,25 @@ * CryptoJS core components. */ var CryptoJS = CryptoJS || (function (Math, undefined) { + /* + * Local polyfil of Object.create + */ + var create = Object.create || (function () { + function F() {}; + + return function (obj) { + var subtype; + + F.prototype = obj; + + subtype = new F(); + + F.prototype = null; + + return subtype; + }; + }()) + /** * CryptoJS namespace. */ @@ -16,7 +35,7 @@ var CryptoJS = CryptoJS || (function (Math, undefined) { * Base object for prototypal inheritance. */ var Base = C_lib.Base = (function () { - function F() {} + return { /** @@ -39,8 +58,7 @@ var CryptoJS = CryptoJS || (function (Math, undefined) { */ extend: function (overrides) { // Spawn - F.prototype = this; - var subtype = new F(); + var subtype = create(this); // Augment if (overrides) { From 7ff3a3c828767485c141d0e8dc0dfa7eae96fc31 Mon Sep 17 00:00:00 2001 From: evanvosberg Date: Thu, 27 Oct 2016 03:12:06 +0200 Subject: [PATCH 4/5] Only skip _doReset if _key has not been changed since last reset. --- src/aes.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/aes.js b/src/aes.js index df7e1b2..aaf6e92 100644 --- a/src/aes.js +++ b/src/aes.js @@ -76,15 +76,18 @@ */ var AES = C_algo.AES = BlockCipher.extend({ _doReset: function () { - if (this._nRounds) return; + // Skip reset of nRounds has been set before and key did not change + if (this._nRounds && this._keyPriorReset === this._key) { + return; + } // Shortcuts - var key = this._key; + var key = this._keyPriorReset = this._key; var keyWords = key.words; var keySize = key.sigBytes / 4; // Compute number of rounds - var nRounds = this._nRounds = keySize + 6 + var nRounds = this._nRounds = keySize + 6; // Compute number of key schedule rows var ksRows = (nRounds + 1) * 4; From 91819e831d6878b2f5322b92e5fc515e2a04a072 Mon Sep 17 00:00:00 2001 From: evanvosberg Date: Thu, 27 Oct 2016 14:12:26 +0200 Subject: [PATCH 5/5] Bump version. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9dec147..02dbc21 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "crypto-js", "title": "crypto-js", "description": "JavaScript library of crypto standards.", - "version": "3.1.7", + "version": "3.1.8", "homepage": "http://github.com/brix/crypto-js", "author": { "name": "Evan Vosberg",