diff --git a/package-lock.json b/package-lock.json index 823989f0..88f894ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1755,9 +1755,9 @@ } }, "crypto-api": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/crypto-api/-/crypto-api-0.7.3.tgz", - "integrity": "sha1-nHMgTE73lxYjIkOYuDS6fJ2B9uU=" + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/crypto-api/-/crypto-api-0.7.4.tgz", + "integrity": "sha1-wuiM1WOWxJ0A75uA40lGUKdIKoE=" }, "crypto-browserify": { "version": "3.11.1", diff --git a/package.json b/package.json index 2b73476f..6aef966f 100644 --- a/package.json +++ b/package.json @@ -61,8 +61,8 @@ "url-loader": "^0.5.9", "val-loader": "^1.0.2", "web-resource-inliner": "^4.1.1", - "webpack": "^3.5.6", - "webpack-dev-server": "^2.5.0", + "webpack": "^3.6.0", + "webpack-dev-server": "^2.8.2", "webpack-node-externals": "^1.6.0", "worker-loader": "^0.8.0" }, @@ -71,7 +71,7 @@ "bootstrap": "^3.3.7", "bootstrap-colorpicker": "^2.5.1", "bootstrap-switch": "^3.3.4", - "crypto-api": "^0.7.3", + "crypto-api": "^0.7.4", "crypto-js": "^3.1.9-1", "diff": "^3.3.1", "escodegen": "^1.9.0", diff --git a/src/core/config/Categories.js b/src/core/config/Categories.js index a882b442..4a551d37 100755 --- a/src/core/config/Categories.js +++ b/src/core/config/Categories.js @@ -255,6 +255,8 @@ const Categories = [ "Keccak", "Shake", "RIPEMD", + "HAS-160", + "Whirlpool", "HMAC", "Fletcher-8 Checksum", "Fletcher-16 Checksum", diff --git a/src/core/config/OperationConfig.js b/src/core/config/OperationConfig.js index d8b75ca5..5e1aae87 100755 --- a/src/core/config/OperationConfig.js +++ b/src/core/config/OperationConfig.js @@ -2991,6 +2991,26 @@ const OperationConfig = { } ] }, + "HAS-160": { + module: "Hashing", + description: "HAS-160 is a cryptographic hash function designed for use with the Korean KCDSA digital signature algorithm. It is derived from SHA-1, with assorted changes intended to increase its security. It produces a 160-bit output.

HAS-160 is used in the same way as SHA-1. First it divides input in blocks of 512 bits each and pads the final block. A digest function updates the intermediate hash value by processing the input blocks in turn.

The message digest algorithm consists of 80 rounds.", + inputType: "string", + outputType: "string", + args: [] + }, + "Whirlpool": { + module: "Hashing", + description: "Whirlpool is a cryptographic hash function designed by Vincent Rijmen (co-creator of AES) and Paulo S. L. M. Barreto, who first described it in 2000.

Several variants exist:", + inputType: "string", + outputType: "string", + args: [ + { + name: "Variant", + type: "option", + value: Hash.WHIRLPOOL_VARIANT + } + ] + }, "HMAC": { module: "Hashing", description: "Keyed-Hash Message Authentication Codes (HMAC) are a mechanism for message authentication using cryptographic hash functions.", diff --git a/src/core/config/modules/Hashing.js b/src/core/config/modules/Hashing.js index a04b87e3..235a9cfe 100644 --- a/src/core/config/modules/Hashing.js +++ b/src/core/config/modules/Hashing.js @@ -31,6 +31,8 @@ OpModules.Hashing = { "Keccak": Hash.runKeccak, "Shake": Hash.runShake, "RIPEMD": Hash.runRIPEMD, + "HAS-160": Hash.runHAS, + "Whirlpool": Hash.runWhirlpool, "HMAC": Hash.runHMAC, "Fletcher-8 Checksum": Checksum.runFletcher8, "Fletcher-16 Checksum": Checksum.runFletcher16, diff --git a/src/core/operations/Hash.js b/src/core/operations/Hash.js index 3f04e759..b165f51e 100755 --- a/src/core/operations/Hash.js +++ b/src/core/operations/Hash.js @@ -263,6 +263,37 @@ const Hash = { }, + /** + * HAS-160 operation. + * + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + runHAS: function (input, args) { + return CryptoApi.hash("has160", input, {}).stringify("hex"); + }, + + + /** + * @constant + * @default + */ + WHIRLPOOL_VARIANT: ["Whirlpool", "Whirlpool-T", "Whirlpool-0"], + + /** + * Whirlpool operation. + * + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + runWhirlpool: function (input, args) { + const variant = args[0].toLowerCase(); + return CryptoApi.hash(variant, input, {}).stringify("hex"); + }, + + /** * @constant * @default @@ -283,6 +314,10 @@ const Hash = { "RIPEMD160", "RIPEMD256", "RIPEMD320", + "HAS160", + "Whirlpool", + "Whirlpool-0", + "Whirlpool-T" ], /** @@ -335,6 +370,10 @@ const Hash = { "\nRIPEMD-160: " + Hash.runRIPEMD(input, ["160"]) + "\nRIPEMD-256: " + Hash.runRIPEMD(input, ["256"]) + "\nRIPEMD-320: " + Hash.runRIPEMD(input, ["320"]) + + "\nHAS-160: " + Hash.runHAS(input, []) + + "\nWhirlpool-0: " + Hash.runWhirlpool(input, ["Whirlpool-0"]) + + "\nWhirlpool-T: " + Hash.runWhirlpool(input, ["Whirlpool-T"]) + + "\nWhirlpool: " + Hash.runWhirlpool(input, ["Whirlpool"]) + "\n\nChecksums:" + "\nFletcher-8: " + Checksum.runFletcher8(byteArray, []) + "\nFletcher-16: " + Checksum.runFletcher16(byteArray, []) + diff --git a/src/web/App.js b/src/web/App.js index 53c0158f..ef3dc3f6 100755 --- a/src/web/App.js +++ b/src/web/App.js @@ -122,7 +122,12 @@ App.prototype.bake = function(step) { * Runs Auto Bake if it is set. */ App.prototype.autoBake = function() { - if (this.autoBake_ && !this.autoBakePause && !this.baking) { + // If autoBakePause is set, we are loading a full recipe (and potentially input), so there is no + // need to set the staleness indicator. Just exit and wait until auto bake is called after loading + // has completed. + if (this.autoBakePause) return false; + + if (this.autoBake_ && !this.baking) { this.bake(); } else { this.manager.controls.showStaleIndicator(); @@ -369,10 +374,6 @@ App.prototype.loadURIParams = function() { window.location.hash; this.uriParams = Utils.parseURIParams(params); - // Pause auto-bake while loading but don't modify `this.autoBake_` - // otherwise `manualBake` cannot trigger. - this.autoBakePause = true; - // Read in recipe from URI params if (this.uriParams.recipe) { try { @@ -407,8 +408,6 @@ App.prototype.loadURIParams = function() { } catch (err) {} } - // Unpause auto-bake - this.autoBakePause = false; this.autoBake(); }; @@ -441,6 +440,10 @@ App.prototype.getRecipeConfig = function() { App.prototype.setRecipeConfig = function(recipeConfig) { document.getElementById("rec-list").innerHTML = null; + // Pause auto-bake while loading but don't modify `this.autoBake_` + // otherwise `manualBake` cannot trigger. + this.autoBakePause = true; + for (let i = 0; i < recipeConfig.length; i++) { const item = this.manager.recipe.addOperation(recipeConfig[i].op); @@ -473,6 +476,9 @@ App.prototype.setRecipeConfig = function(recipeConfig) { this.progress = 0; } + + // Unpause auto bake + this.autoBakePause = false; }; diff --git a/src/web/ControlsWaiter.js b/src/web/ControlsWaiter.js index 85982e46..a49ad4fa 100755 --- a/src/web/ControlsWaiter.js +++ b/src/web/ControlsWaiter.js @@ -361,6 +361,7 @@ ControlsWaiter.prototype.loadButtonClick = function() { try { const recipeConfig = Utils.parseRecipeConfig(document.getElementById("load-text").value); this.app.setRecipeConfig(recipeConfig); + this.app.autoBake(); $("#rec-list [data-toggle=popover]").popover(); } catch (e) { diff --git a/src/web/InputWaiter.js b/src/web/InputWaiter.js index b771be2a..aba57334 100755 --- a/src/web/InputWaiter.js +++ b/src/web/InputWaiter.js @@ -158,13 +158,11 @@ InputWaiter.prototype.inputDrop = function(e) { const CHUNK_SIZE = 20480; // 20KB const setInput = function() { - this.app.autoBakePause = true; const recipeConfig = this.app.getRecipeConfig(); if (!recipeConfig[0] || recipeConfig[0].op !== "From Hex") { recipeConfig.unshift({op: "From Hex", args: ["Space"]}); this.app.setRecipeConfig(recipeConfig); } - this.app.autoBakePause = false; this.set(inputCharcode); diff --git a/src/web/Manager.js b/src/web/Manager.js index 39efc458..1d32758c 100755 --- a/src/web/Manager.js +++ b/src/web/Manager.js @@ -119,9 +119,8 @@ Manager.prototype.initialiseEventListeners = function() { this.addDynamicListener("li.operation", "operationadd", this.recipe.opAdd.bind(this.recipe)); // Recipe - this.addDynamicListener(".arg", "keyup", this.recipe.ingChange, this.recipe); - this.addDynamicListener(".arg", "change", this.recipe.ingChange, this.recipe); - this.addDynamicListener(".arg", "input", this.recipe.ingChange, this.recipe); + this.addDynamicListener(".arg:not(select)", "input", this.recipe.ingChange, this.recipe); + this.addDynamicListener(".arg[type=checkbox], .arg[type=radio], select.arg", "change", this.recipe.ingChange, this.recipe); this.addDynamicListener(".disable-icon", "click", this.recipe.disableClick, this.recipe); this.addDynamicListener(".breakpoint", "click", this.recipe.breakpointClick, this.recipe); this.addDynamicListener("#rec-list li.operation", "dblclick", this.recipe.operationDblclick, this.recipe); diff --git a/src/web/RecipeWaiter.js b/src/web/RecipeWaiter.js index 80c2fe96..121b64d2 100755 --- a/src/web/RecipeWaiter.js +++ b/src/web/RecipeWaiter.js @@ -191,7 +191,7 @@ RecipeWaiter.prototype.favDrop = function(e) { * * @fires Manager#statechange */ -RecipeWaiter.prototype.ingChange = function() { +RecipeWaiter.prototype.ingChange = function(e) { window.dispatchEvent(this.manager.statechange); }; diff --git a/test/tests/operations/Hash.js b/test/tests/operations/Hash.js index 6754f44b..a0cd5da2 100644 --- a/test/tests/operations/Hash.js +++ b/test/tests/operations/Hash.js @@ -294,6 +294,50 @@ TestRegister.addTests([ } ] }, + { + name: "HAS-160", + input: "Hello, World!", + expectedOutput: "8f6dd8d7c8a04b1cb3831adc358b1e4ac2ed5984", + recipeConfig: [ + { + "op": "HAS-160", + "args": [] + } + ] + }, + { + name: "Whirlpool-0", + input: "Hello, World!", + expectedOutput: "1c327026f565a0105a827efbfb3d3635cdb042c0aabb8416e96deb128e6c5c8684b13541cf31c26c1488949df050311c6999a12eb0e7002ad716350f5c7700ca", + recipeConfig: [ + { + "op": "Whirlpool", + "args": ["Whirlpool-0"] + } + ] + }, + { + name: "Whirlpool-T", + input: "Hello, World!", + expectedOutput: "16c581089b6a6f356ae56e16a63a4c613eecd82a2a894b293f5ee45c37a31d09d7a8b60bfa7e414bd4a7166662cea882b5cf8c96b7d583fc610ad202591bcdb1", + recipeConfig: [ + { + "op": "Whirlpool", + "args": ["Whirlpool-T"] + } + ] + }, + { + name: "Whirlpool", + input: "Hello, World!", + expectedOutput: "3d837c9ef7bb291bd1dcfc05d3004af2eeb8c631dd6a6c4ba35159b8889de4b1ec44076ce7a8f7bfa497e4d9dcb7c29337173f78d06791f3c3d9e00cc6017f0b", + recipeConfig: [ + { + "op": "Whirlpool", + "args": ["Whirlpool"] + } + ] + }, { name: "HMAC SHA256", input: "Hello, World!",