/** * @author j433866 [j433866@gmail.com] * @copyright Crown Copyright 2019 * @license Apache-2.0 */ import Operation from "../Operation.mjs"; import OperationError from "../errors/OperationError.mjs"; import Utils from "../Utils.mjs"; import { fromBase64 } from "../lib/Base64.mjs"; import { fromHex, toHexFast } from "../lib/Hex.mjs"; /** * 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 and extracts fields from it.
The key type can be:The key format can be 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" ] } ]; this.checks = [ { pattern: "^\\s*([A-F\\d]{2}[,;:]){15,}[A-F\\d]{2}\\s*$", flags: "i", args: ["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[1]))}`; output += `\nPoint: 0x${fields.slice(2)}`; } else if (keyType === "ssh-ed25519") { output += `\nx: 0x${fields[1]}`; } 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) { 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); } 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;