From 2fab1028c51c6245480d03f4d9f2e8b7249996ec Mon Sep 17 00:00:00 2001 From: Tan Zhen Yong Date: Mon, 7 Oct 2019 02:24:09 +0800 Subject: [PATCH 1/2] Add Argon2 hash operation --- package-lock.json | 11 ++++ package.json | 1 + src/core/config/Categories.json | 1 + src/core/operations/Argon2.mjs | 101 ++++++++++++++++++++++++++++++++ tests/node/tests/operations.mjs | 5 ++ webpack.config.js | 12 ++-- 6 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 src/core/operations/Argon2.mjs diff --git a/package-lock.json b/package-lock.json index 8b5641c9..6e4f9230 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16587,6 +16587,11 @@ "integrity": "sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==", "dev": true }, + "argon2-browser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/argon2-browser/-/argon2-browser-1.11.1.tgz", + "integrity": "sha512-C+WsBLSkwQExkDYB7vriugrBTXq2z+fTRDlGWqr2zm89TaKo7zYtSGARMgoBxpDnmNNzduNlZJmpY2j0Dp7ZOQ==" + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -16976,6 +16981,12 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "base64-loader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64-loader/-/base64-loader-1.0.0.tgz", + "integrity": "sha1-5TC62I6QbdKh+tCvLZ5oP6i9kqg=", + "dev": true + }, "basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", diff --git a/package.json b/package.json index fe93fe52..46a0f232 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "@astronautlabs/amf": "^0.0.6", "@babel/polyfill": "^7.12.1", "@blu3r4y/lzma": "^2.3.3", + "argon2-browser": "^1.11.1", "arrive": "^2.4.1", "avsc": "^5.7.4", "bcryptjs": "^2.4.3", diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 075e8d66..da9e0d0d 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -379,6 +379,7 @@ "Bcrypt compare", "Bcrypt parse", "Scrypt", + "Argon2", "NT Hash", "LM Hash", "Fletcher-8 Checksum", diff --git a/src/core/operations/Argon2.mjs b/src/core/operations/Argon2.mjs new file mode 100644 index 00000000..f36e5d0a --- /dev/null +++ b/src/core/operations/Argon2.mjs @@ -0,0 +1,101 @@ +/** + * @author Tan Zhen Yong [tzy@beyondthesprawl.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import argon2 from "argon2-browser"; + +/** + * Argon2 operation + */ +class Argon2 extends Operation { + + /** + * Argon2 constructor + */ + constructor() { + super(); + + this.name = "Argon2"; + this.module = "Crypto"; + this.description = "Argon2 is a key derivation function that was selected as the winner of the Password Hashing Competition in July 2015. It was designed by Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich from the University of Luxembourg.

Enter the password in the input to generate its hash."; + this.infoURL = "https://wikipedia.org/wiki/Argon2"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Salt", + "type": "string", + "value": "somesalt" + }, + { + "name": "Iterations", + "type": "number", + "value": 3 + }, + { + "name": "Memory (KiB)", + "type": "number", + "value": 4096 + }, + { + "name": "Parallelism", + "type": "number", + "value": 1 + }, + { + "name": "Hash length (bytes)", + "type": "number", + "value": 32 + }, + { + "name": "Type", + "type": "option", + "value": ["Argon2i", "Argon2d", "Argon2id"], + "defaultIndex": 0 + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + async run(input, args) { + const argon2Types = { + "Argon2i": argon2.ArgonType.Argon2i, + "Argon2d": argon2.ArgonType.Argon2d, + "Argon2id": argon2.ArgonType.Argon2id + }; + + const salt = args[0], + time = args[1], + mem = args[2], + parallelism = args[3], + hashLen = args[4], + type = argon2Types[args[5]]; + + try { + const result = await argon2.hash({ + pass: input, + salt, + time, + mem, + parallelism, + hashLen, + type, + }); + + return result.encoded; + } catch (err) { + throw new OperationError(`Error: ${err.message}`); + } + } + +} + +export default Argon2; diff --git a/tests/node/tests/operations.mjs b/tests/node/tests/operations.mjs index 8611ecb4..45b38c7e 100644 --- a/tests/node/tests/operations.mjs +++ b/tests/node/tests/operations.mjs @@ -133,6 +133,11 @@ Tiger-128`; }), + it("argon2", async () => { + const result = await chef.argon2("argon2password"); + assert.strictEqual(result.toString(), "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ$s43my9eBljQADuF/LWCG8vGqwAJzOorKQ0Yog8jFvbw"); + }), + it("Bcrypt", async () => { const result = await chef.bcrypt("Put a Sock In It"); const strResult = result.toString(); diff --git a/webpack.config.js b/webpack.config.js index 50c4c731..6011b1dd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -114,6 +114,8 @@ module.exports = { } }, module: { + // argon2-browser loads argon2.wasm by itself, so Webpack should not load it + noParse: /node_modules\/argon2-browser\/dist\/argon2\.wasm$/, rules: [ { test: /\.m?js$/, @@ -127,11 +129,11 @@ module.exports = { loader: "babel-loader" }, { - test: /node-forge/, - loader: "imports-loader", - options: { - additionalCode: "var jQuery = false;" - } + test: /node_modules\/argon2-browser\/dist\/argon2\.wasm$/, + // Load argon2.wasm as base64-encoded binary file + // expected by argon2-browser + loaders: "base64-loader", + type: "javascript/auto" }, { test: /prime.worker.min.js$/, From bca4c34b3abe2e6cfea72d8589f2501e775e8aab Mon Sep 17 00:00:00 2001 From: Tan Zhen Yong Date: Mon, 7 Oct 2019 17:44:00 +0800 Subject: [PATCH 2/2] Add Argon2 hash compare operation --- src/core/config/Categories.json | 1 + src/core/operations/Argon2Compare.mjs | 58 +++++++++++++++++++++++++++ tests/node/tests/operations.mjs | 7 ++++ 3 files changed, 66 insertions(+) create mode 100644 src/core/operations/Argon2Compare.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index da9e0d0d..fec21d50 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -380,6 +380,7 @@ "Bcrypt parse", "Scrypt", "Argon2", + "Argon2 compare", "NT Hash", "LM Hash", "Fletcher-8 Checksum", diff --git a/src/core/operations/Argon2Compare.mjs b/src/core/operations/Argon2Compare.mjs new file mode 100644 index 00000000..01ad9234 --- /dev/null +++ b/src/core/operations/Argon2Compare.mjs @@ -0,0 +1,58 @@ +/** + * @author Tan Zhen Yong [tzy@beyondthesprawl.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import argon2 from "argon2-browser"; + +/** + * Argon2 compare operation + */ +class Argon2Compare extends Operation { + + /** + * Argon2Compare constructor + */ + constructor() { + super(); + + this.name = "Argon2 compare"; + this.module = "Crypto"; + this.description = "Tests whether the input matches the given Argon2 hash. To test multiple possible passwords, use the 'Fork' operation."; + this.infoURL = "https://wikipedia.org/wiki/Argon2"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Hash", + "type": "string", + "value": "" + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + async run(input, args) { + const encoded = args[0]; + + try { + await argon2.verify({ + pass: input, + encoded + }); + + return `Match: ${input}`; + } catch (err) { + return "No match"; + } + } + +} + +export default Argon2Compare; diff --git a/tests/node/tests/operations.mjs b/tests/node/tests/operations.mjs index 45b38c7e..eaf92804 100644 --- a/tests/node/tests/operations.mjs +++ b/tests/node/tests/operations.mjs @@ -138,6 +138,13 @@ Tiger-128`; assert.strictEqual(result.toString(), "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ$s43my9eBljQADuF/LWCG8vGqwAJzOorKQ0Yog8jFvbw"); }), + it("argon2 compare", async () => { + const result = await chef.argon2Compare("argon2password", { + hash: "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ$s43my9eBljQADuF/LWCG8vGqwAJzOorKQ0Yog8jFvbw" + }); + assert.strictEqual(result.toString(), "Match: argon2password"); + }), + it("Bcrypt", async () => { const result = await chef.bcrypt("Put a Sock In It"); const strResult = result.toString();