From 59cdd259ac8915368d5b81354a8d7d046359b608 Mon Sep 17 00:00:00 2001 From: j433866 Date: Thu, 23 May 2019 11:11:37 +0100 Subject: [PATCH 1/8] Add new parse ssh host key operation --- src/core/operations/ParseSSHHostKey.mjs | 143 ++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 src/core/operations/ParseSSHHostKey.mjs diff --git a/src/core/operations/ParseSSHHostKey.mjs b/src/core/operations/ParseSSHHostKey.mjs new file mode 100644 index 00000000..811f33c8 --- /dev/null +++ b/src/core/operations/ParseSSHHostKey.mjs @@ -0,0 +1,143 @@ +/** + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import Utils from "../Utils"; +import { fromBase64 } from "../lib/Base64"; +import { fromHex, toHexFast } from "../lib/Hex"; + +/** + * Parse SSH Host Key operation + */ +class ParseSSHHostKey extends Operation { + + /** + * ParseSSHHostKey constructor + */ + constructor() { + super(); + + this.name = "Parse SSH Host Key"; + this.module = "Default"; + this.description = "Parses a SSH host key. The key can be in either Hex or Base64."; + this.infoURL = "https://wikipedia.org/wiki/Secure_Shell"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Input Format", + type: "option", + value: [ + "Auto", + "Base64", + "Hex" + ] + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [inputFormat] = args, + inputKey = this.convertKeyToBinary(input.trim(), inputFormat), + fields = this.parseKey(inputKey), + keyType = Utils.byteArrayToChars(fromHex(fields[0]), ""); + + let output = `Key type: ${keyType}`; + + if (keyType === "ssh-rsa") { + output += `\nExponent: 0x${fields[1]}`; + output += `\nModulus: 0x${fields[2]}`; + } else if (keyType === "ssh-dss") { + output += `\np: 0x${fields[1]}`; + output += `\nq: 0x${fields[2]}`; + output += `\ng: 0x${fields[3]}`; + output += `\ny: 0x${fields[4]}`; + } else if (keyType.startsWith("ecdsa-sha2")) { + output += `\nCurve: ${Utils.byteArrayToChars(fromHex(fields[0]))}`; + output += `\nPoint: 0x${fields.slice(2)}`; + } else { + output += "\nUnsupported key type."; + output += `\nParameters: ${fields.slice(1)}`; + } + + return output; + } + + /** + * Converts the key to binary format from either hex or base64 + * + * @param {string} inputKey + * @param {string} inputFormat + * @returns {byteArray} + */ + convertKeyToBinary(inputKey, inputFormat) { + if (inputFormat === "Auto") { + inputFormat = this.detectKeyFormat(inputKey); + } + if (inputFormat === "Hex") { + return fromHex(inputKey); + } else if (inputFormat === "Base64") { + return fromBase64(inputKey, null, "byteArray"); + } else { + throw new OperationError("Invalid input format."); + } + } + + + /** + * Detects if the key is base64 or hex encoded + * + * @param {string} inputKey + * @returns {string} + */ + detectKeyFormat(inputKey) { + const hexPattern = new RegExp(/^(?:[\dA-Fa-f]{2}[ ,;:]?)+$/); + const b64Pattern = new RegExp(/^\s*(?:[A-Za-z\d+/]{4})+(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?\s*$/); + + if (hexPattern.test(inputKey)) { + return "Hex"; + } else if (b64Pattern.test(inputKey)) { + return "Base64"; + } else { + throw new OperationError("Unable to detect input key format."); + } + } + + + /** + * Parses fields from the key + * + * @param {byteArray} key + */ + parseKey(key) { + const fields = []; + while (key.length > 0) { + const lengthField = key.slice(0, 4); + let decodedLength = 0; + for (let i = 0; i < lengthField.length; i++) { + decodedLength += lengthField[i]; + decodedLength = decodedLength << 8; + } + decodedLength = decodedLength >> 8; + // Break if length wasn't decoded correctly + if (decodedLength <= 0) break; + + fields.push(toHexFast(key.slice(4, 4 + decodedLength))); + key = key.slice(4 + decodedLength); + } + + return fields; + } + +} + +export default ParseSSHHostKey; From d56ff0825a0d61c63ab897522c861522c9fd287d Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 8 Jul 2019 15:58:56 +0100 Subject: [PATCH 2/8] Add extraction of actual key from public key file --- src/core/operations/ParseSSHHostKey.mjs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/operations/ParseSSHHostKey.mjs b/src/core/operations/ParseSSHHostKey.mjs index 811f33c8..fe055496 100644 --- a/src/core/operations/ParseSSHHostKey.mjs +++ b/src/core/operations/ParseSSHHostKey.mjs @@ -80,6 +80,13 @@ class ParseSSHHostKey extends Operation { * @returns {byteArray} */ convertKeyToBinary(inputKey, inputFormat) { + const keyPattern = new RegExp(/^(?:[ssh]|[ecdsa-sha2])\S+\s+(\S*)/), + keyMatch = inputKey.match(keyPattern); + + if (keyMatch) { + inputKey = keyMatch[1]; + } + if (inputFormat === "Auto") { inputFormat = this.detectKeyFormat(inputKey); } From 944842d4eb2695db5b463b48c531558fb4732085 Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 8 Jul 2019 16:44:36 +0100 Subject: [PATCH 3/8] Improve description and add to Categories --- src/core/config/Categories.json | 3 ++- src/core/operations/ParseSSHHostKey.mjs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 2d194c37..ae5c68ed 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -122,7 +122,8 @@ "PGP Encrypt", "PGP Decrypt", "PGP Encrypt and Sign", - "PGP Decrypt and Verify" + "PGP Decrypt and Verify", + "Parse SSH Host Key" ] }, { diff --git a/src/core/operations/ParseSSHHostKey.mjs b/src/core/operations/ParseSSHHostKey.mjs index fe055496..f83f6536 100644 --- a/src/core/operations/ParseSSHHostKey.mjs +++ b/src/core/operations/ParseSSHHostKey.mjs @@ -23,7 +23,7 @@ class ParseSSHHostKey extends Operation { this.name = "Parse SSH Host Key"; this.module = "Default"; - this.description = "Parses a SSH host key. The key can be in either Hex or Base64."; + this.description = "Parses a SSH host key and extracts fields from it.
The key type can be:
  • ssh-rsa
  • ssh-dss
  • ecdsa-sha2
The key format can be either Hex or Base64."; this.infoURL = "https://wikipedia.org/wiki/Secure_Shell"; this.inputType = "string"; this.outputType = "string"; From ac1c93d29bdf89ee41d9cbdec361167b4f8329bf Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 8 Jul 2019 16:58:03 +0100 Subject: [PATCH 4/8] Fix incorrect curve detection for ecdsa-sha2 --- src/core/operations/ParseSSHHostKey.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/ParseSSHHostKey.mjs b/src/core/operations/ParseSSHHostKey.mjs index f83f6536..17b1a8d1 100644 --- a/src/core/operations/ParseSSHHostKey.mjs +++ b/src/core/operations/ParseSSHHostKey.mjs @@ -62,7 +62,7 @@ class ParseSSHHostKey extends Operation { output += `\ng: 0x${fields[3]}`; output += `\ny: 0x${fields[4]}`; } else if (keyType.startsWith("ecdsa-sha2")) { - output += `\nCurve: ${Utils.byteArrayToChars(fromHex(fields[0]))}`; + output += `\nCurve: ${Utils.byteArrayToChars(fromHex(fields[1]))}`; output += `\nPoint: 0x${fields.slice(2)}`; } else { output += "\nUnsupported key type."; From 9c6ceaa58a0497f2f20fd756d24c193d0c0b16fc Mon Sep 17 00:00:00 2001 From: j433866 Date: Mon, 15 Jul 2019 14:12:40 +0100 Subject: [PATCH 5/8] Add tests --- tests/operations/index.mjs | 1 + tests/operations/tests/ParseSSHHostKey.mjs | 65 ++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 tests/operations/tests/ParseSSHHostKey.mjs diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 41d78c35..e107ac97 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -90,6 +90,7 @@ import "./tests/Typex"; import "./tests/BLAKE2b"; import "./tests/BLAKE2s"; import "./tests/Protobuf"; +import "./tests/ParseSSHHostKey"; // Cannot test operations that use the File type yet //import "./tests/SplitColourChannels"; diff --git a/tests/operations/tests/ParseSSHHostKey.mjs b/tests/operations/tests/ParseSSHHostKey.mjs new file mode 100644 index 00000000..d6668e40 --- /dev/null +++ b/tests/operations/tests/ParseSSHHostKey.mjs @@ -0,0 +1,65 @@ +/** + * Parse SSH Host Key tests + * + * @author j433866 [j433866@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ +import TestRegister from "../TestRegister"; + +TestRegister.addTests([ + { + name: "SSH Host Key: RSA", + input: "AAAAB3NzaC1yc2EAAAADAQABAAABAQDiJZ/9W9Ix/Dk9b+K4E+RGCug1AtkGXaJ9vNIY0YHFHLpWsB8DAuh/cGEI9TLbL1gzR2wG+RJNQ2EAQVWe6ypkK63Jm4zw4re+vhEiszpnP889J0h5N9yzyTndesrl4d3cQtv861FcKDPxUJbRALdtl6gwOB7BCL8gsXJLLVLO4EesrbPXD454qpVt7CgJXEXByOFjcIm3XwkdOnXMPHHnMSD7EIN1SvQMD6PfIDrbDd6KQt5QXW/Rc/BsfX5cbUIV1QW5A/GbepXHHKmWRtLC2J/mH3hW2Zq/hITPEaJdG1CtIilQmJaZGXpfGIwFeb0Av9pSL926arZZ6vDi9ctF", + expectedOutput: `Key type: ssh-rsa +Exponent: 0x010001 +Modulus: 0x00e2259ffd5bd231fc393d6fe2b813e4460ae83502d9065da27dbcd218d181c51cba56b01f0302e87f706108f532db2f5833476c06f9124d43610041559eeb2a642badc99b8cf0e2b7bebe1122b33a673fcf3d27487937dcb3c939dd7acae5e1dddc42dbfceb515c2833f15096d100b76d97a830381ec108bf20b1724b2d52cee047acadb3d70f8e78aa956dec28095c45c1c8e1637089b75f091d3a75cc3c71e73120fb1083754af40c0fa3df203adb0dde8a42de505d6fd173f06c7d7e5c6d4215d505b903f19b7a95c71ca99646d2c2d89fe61f7856d99abf8484cf11a25d1b50ad222950989699197a5f188c0579bd00bfda522fddba6ab659eaf0e2f5cb45`, + recipeConfig: [ + { + op: "Parse SSH Host Key", + args: ["Base64"] + } + ] + }, + { + name: "SSH Host Key: DSA", + input: "AAAAB3NzaC1kc3MAAACBAMnoZCOzvaQqs//9mxK2USZvJBc7b1dFJiBcV80abN6maE+203pTRPIPCpPt0deQxv4YN3dSHoodEcArWxs1QRAIuRsQIvsUP7chovzGnxP84XWK5sbfrseD0vxZ7UR0NaAFPcSgeXcWC1SG9uvrAJQlyp4DBy+fKuqiYmwaz0bHAAAAFQCXNJ4yiE1V7LpCU2V1JKbqDvICMwAAAIB/5aR1iBOeyCVpj0dP3YZmoxd9R7FCC/0UuOf0lx4E6WHT6Z2QuPBhc2mpNDq2M0VF9oJfVWgcfG8r1rlXaCYODSacGcbnW5VKQ+LKkkALmg4h8jFCHReUC+Hmia/v8LyDwPO1wK6ETn7a3m80yM7gAU5ZNurVIVVP2lB65mjEsQAAAIA3ct9YRB6iUCvOD45sZM1C9oTC24Ttmaou0GcpWx3h0/iZ8mbil1cjaO9frRNZ/vSSVWEhEDNG8gwkjZWlvnJL3y1XUxbMll4WbmI/Q1kzKwopceaFwMbYTPKDg6L1RtCMUxSUyKsFk1c4SpEPlDS7DApZs5PgmWgMd/u6vwMXyg==", + expectedOutput: `Key type: ssh-dss +p: 0x00c9e86423b3bda42ab3fffd9b12b651266f24173b6f574526205c57cd1a6cdea6684fb6d37a5344f20f0a93edd1d790c6fe183777521e8a1d11c02b5b1b35411008b91b1022fb143fb721a2fcc69f13fce1758ae6c6dfaec783d2fc59ed447435a0053dc4a07977160b5486f6ebeb009425ca9e03072f9f2aeaa2626c1acf46c7 +q: 0x0097349e32884d55ecba4253657524a6ea0ef20233 +g: 0x7fe5a47588139ec825698f474fdd8666a3177d47b1420bfd14b8e7f4971e04e961d3e99d90b8f0617369a9343ab6334545f6825f55681c7c6f2bd6b95768260e0d269c19c6e75b954a43e2ca92400b9a0e21f231421d17940be1e689afeff0bc83c0f3b5c0ae844e7edade6f34c8cee0014e5936ead521554fda507ae668c4b1 +y: 0x3772df58441ea2502bce0f8e6c64cd42f684c2db84ed99aa2ed067295b1de1d3f899f266e297572368ef5fad1359fef492556121103346f20c248d95a5be724bdf2d575316cc965e166e623f4359332b0a2971e685c0c6d84cf28383a2f546d08c531494c8ab059357384a910f9434bb0c0a59b393e099680c77fbbabf0317ca`, + recipeConfig: [ + { + op: "Parse SSH Host Key", + args: ["Base64"] + } + ] + }, + { + name: "SSH Host Key: ECDSA", + input: "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGxZWSAGJyJQoVBwFCpr420eRUZDE/kw2YWm5vDro8050DZ1ZzqIuYaNl0BGzMcRTeasGtJuI8G84ZQQSgca3C4=", + expectedOutput: `Key type: ecdsa-sha2-nistp256 +Curve: nistp256 +Point: 0x046c59592006272250a15070142a6be36d1e45464313f930d985a6e6f0eba3cd39d03675673a88b9868d974046ccc7114de6ac1ad26e23c1bce194104a071adc2e`, + recipeConfig: [ + { + op: "Parse SSH Host Key", + args: ["Base64"] + } + ] + }, + { + name: "SSH Host Key: Extract key", + input: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDiJZ/9W9Ix/Dk9b+K4E+RGCug1AtkGXaJ9vNIY0YHFHLpWsB8DAuh/cGEI9TLbL1gzR2wG+RJNQ2EAQVWe6ypkK63Jm4zw4re+vhEiszpnP889J0h5N9yzyTndesrl4d3cQtv861FcKDPxUJbRALdtl6gwOB7BCL8gsXJLLVLO4EesrbPXD454qpVt7CgJXEXByOFjcIm3XwkdOnXMPHHnMSD7EIN1SvQMD6PfIDrbDd6KQt5QXW/Rc/BsfX5cbUIV1QW5A/GbepXHHKmWRtLC2J/mH3hW2Zq/hITPEaJdG1CtIilQmJaZGXpfGIwFeb0Av9pSL926arZZ6vDi9ctF test@test", + expectedOutput: `Key type: ssh-rsa +Exponent: 0x010001 +Modulus: 0x00e2259ffd5bd231fc393d6fe2b813e4460ae83502d9065da27dbcd218d181c51cba56b01f0302e87f706108f532db2f5833476c06f9124d43610041559eeb2a642badc99b8cf0e2b7bebe1122b33a673fcf3d27487937dcb3c939dd7acae5e1dddc42dbfceb515c2833f15096d100b76d97a830381ec108bf20b1724b2d52cee047acadb3d70f8e78aa956dec28095c45c1c8e1637089b75f091d3a75cc3c71e73120fb1083754af40c0fa3df203adb0dde8a42de505d6fd173f06c7d7e5c6d4215d505b903f19b7a95c71ca99646d2c2d89fe61f7856d99abf8484cf11a25d1b50ad222950989699197a5f188c0579bd00bfda522fddba6ab659eaf0e2f5cb45`, + recipeConfig: [ + { + op: "Parse SSH Host Key", + args: ["Base64"] + } + ] + } +]); From 863675e63643fb5338c254d0e93a09cf9d479d3b Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 13 Aug 2019 13:37:21 +0100 Subject: [PATCH 6/8] Update nodeApi test. 'base 64' now returns 11 results as the SSH host key module mentions it --- tests/node/tests/nodeApi.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/node/tests/nodeApi.mjs b/tests/node/tests/nodeApi.mjs index e775f3a0..954a19ce 100644 --- a/tests/node/tests/nodeApi.mjs +++ b/tests/node/tests/nodeApi.mjs @@ -136,7 +136,7 @@ TestRegister.addApiTests([ it("chef.help: returns multiple results", () => { const result = chef.help("base 64"); - assert.strictEqual(result.length, 10); + assert.strictEqual(result.length, 11); }), it("chef.help: looks in description for matches too", () => { From 4bc4db82329958f3d5c2bd94266a4a630fb6355b Mon Sep 17 00:00:00 2001 From: j433866 Date: Tue, 13 Aug 2019 13:39:21 +0100 Subject: [PATCH 7/8] Fix incorrect import of TestRegister --- tests/operations/tests/ParseSSHHostKey.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/operations/tests/ParseSSHHostKey.mjs b/tests/operations/tests/ParseSSHHostKey.mjs index d6668e40..00e13c6c 100644 --- a/tests/operations/tests/ParseSSHHostKey.mjs +++ b/tests/operations/tests/ParseSSHHostKey.mjs @@ -5,7 +5,7 @@ * @copyright Crown Copyright 2019 * @license Apache-2.0 */ -import TestRegister from "../TestRegister"; +import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { From d90a23bfd5ed2c0cb6cc43930fb089899eb5b8ea Mon Sep 17 00:00:00 2001 From: n1474335 Date: Tue, 13 Aug 2019 14:11:52 +0100 Subject: [PATCH 8/8] Added 'Parse SSH Host Key' operation to the Networking category --- src/core/config/Categories.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index f590b02c..d9305639 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -167,6 +167,7 @@ "Parse IP range", "Parse IPv6 address", "Parse IPv4 header", + "Parse SSH Host Key", "Parse URI", "URL Encode", "URL Decode",