From 2b0c3270018f00e71860742ab46029d89b8939f6 Mon Sep 17 00:00:00 2001 From: Matt C Date: Tue, 15 May 2018 10:15:31 +0100 Subject: [PATCH] Ported x86 Disassembler & PGP ops --- src/core/lib/PGP.mjs | 116 ++++++++++++++++++ src/core/operations/DisassembleX86.mjs | 127 ++++++++++++++++++++ src/core/operations/GeneratePGPKeyPair.mjs | 114 ++++++++++++++++++ src/core/operations/PGPDecrypt.mjs | 74 ++++++++++++ src/core/operations/PGPDecryptAndVerify.mjs | 111 +++++++++++++++++ src/core/operations/PGPEncrypt.mjs | 72 +++++++++++ src/core/operations/PGPEncryptAndSign.mjs | 80 ++++++++++++ 7 files changed, 694 insertions(+) create mode 100644 src/core/lib/PGP.mjs create mode 100644 src/core/operations/DisassembleX86.mjs create mode 100644 src/core/operations/GeneratePGPKeyPair.mjs create mode 100644 src/core/operations/PGPDecrypt.mjs create mode 100644 src/core/operations/PGPDecryptAndVerify.mjs create mode 100644 src/core/operations/PGPEncrypt.mjs create mode 100644 src/core/operations/PGPEncryptAndSign.mjs diff --git a/src/core/lib/PGP.mjs b/src/core/lib/PGP.mjs new file mode 100644 index 00000000..3b034ec5 --- /dev/null +++ b/src/core/lib/PGP.mjs @@ -0,0 +1,116 @@ +/** + * PGP functions. + * + * @author tlwr [toby@toby.codes] + * @author Matt C [matt@artemisbot.uk] + * @author n1474335 [n1474335@gmail.com] + * + * @copyright Crown Copyright 2018 + * @license Apache-2.0 + * + */ + +import * as kbpgp from "kbpgp"; +import { promisify } from "es6-promisify"; + +/** + * Progress callback + * + */ +export const ASP = kbpgp.ASP({ + "progress_hook": info => { + let msg = ""; + + switch (info.what) { + case "guess": + msg = "Guessing a prime"; + break; + case "fermat": + msg = "Factoring prime using Fermat's factorization method"; + break; + case "mr": + msg = "Performing Miller-Rabin primality test"; + break; + case "passed_mr": + msg = "Passed Miller-Rabin primality test"; + break; + case "failed_mr": + msg = "Failed Miller-Rabin primality test"; + break; + case "found": + msg = "Prime found"; + break; + default: + msg = `Stage: ${info.what}`; + } + + if (ENVIRONMENT_IS_WORKER()) + self.sendStatusMessage(msg); + } +}); + +/** + * Get size of subkey + * + * @param {number} keySize + * @returns {number} + */ +export function getSubkeySize(keySize) { + return { + 1024: 1024, + 2048: 1024, + 4096: 2048, + 256: 256, + 384: 256, + }[keySize]; +} + +/** +* Import private key and unlock if necessary +* +* @param {string} privateKey +* @param {string} [passphrase] +* @returns {Object} +*/ +export async function importPrivateKey(privateKey, passphrase) { + try { + const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({ + armored: privateKey, + opts: { + "no_check_keys": true + } + }); + if (key.is_pgp_locked()) { + if (passphrase) { + await promisify(key.unlock_pgp.bind(key))({ + passphrase + }); + } else { + throw "Did not provide passphrase with locked private key."; + } + } + return key; + } catch (err) { + throw `Could not import private key: ${err}`; + } +} + +/** + * Import public key + * + * @param {string} publicKey + * @returns {Object} + */ +export async function importPublicKey (publicKey) { + try { + const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({ + armored: publicKey, + opts: { + "no_check_keys": true + } + }); + return key; + } catch (err) { + throw `Could not import public key: ${err}`; + } +} diff --git a/src/core/operations/DisassembleX86.mjs b/src/core/operations/DisassembleX86.mjs new file mode 100644 index 00000000..5b207205 --- /dev/null +++ b/src/core/operations/DisassembleX86.mjs @@ -0,0 +1,127 @@ +/** + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import disassemble from "../vendor/DisassembleX86-64.js"; +/** + * Disassemble x86 operation + */ +class DisassembleX86 extends Operation { + + /** + * DisassembleX86 constructor + */ + constructor() { + super(); + + this.name = "Disassemble x86"; + this.module = "Shellcode"; + this.description = "Disassembly is the process of translating machine language into assembly language.

This operation supports 64-bit, 32-bit and 16-bit code written for Intel or AMD x86 processors. It is particularly useful for reverse engineering shellcode.

Input should be in hexadecimal."; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Bit mode", + "type": "option", + "value": ["64", "32", "16"] + }, + { + "name": "Compatibility", + "type": "option", + "value": [ + "Full x86 architecture", + "Knights Corner", + "Larrabee", + "Cyrix", + "Geode", + "Centaur", + "X86/486" + ] + }, + { + "name": "Code Segment (CS)", + "type": "number", + "value": 16 + }, + { + "name": "Offset (IP)", + "type": "number", + "value": 0 + }, + { + "name": "Show instruction hex", + "type": "boolean", + "value": true + }, + { + "name": "Show instruction position", + "type": "boolean", + "value": true + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const mode = args[0], + compatibility = args[1], + codeSegment = args[2], + offset = args[3], + showInstructionHex = args[4], + showInstructionPos = args[5]; + + switch (mode) { + case "64": + disassemble.setBitMode(2); + break; + case "32": + disassemble.setBitMode(1); + break; + case "16": + disassemble.setBitMode(0); + break; + default: + throw "Invalid mode value"; + } + + switch (compatibility) { + case "Full x86 architecture": + disassemble.CompatibilityMode(0); + break; + case "Knights Corner": + disassemble.CompatibilityMode(1); + break; + case "Larrabee": + disassemble.CompatibilityMode(2); + break; + case "Cyrix": + disassemble.CompatibilityMode(3); + break; + case "Geode": + disassemble.CompatibilityMode(4); + break; + case "Centaur": + disassemble.CompatibilityMode(5); + break; + case "X86/486": + disassemble.CompatibilityMode(6); + break; + } + + disassemble.SetBasePosition(codeSegment + ":" + offset); + disassemble.setShowInstructionHex(showInstructionHex); + disassemble.setShowInstructionPos(showInstructionPos); + disassemble.LoadBinCode(input.replace(/\s/g, "")); + return disassemble.LDisassemble(); + } + +} + +export default DisassembleX86; diff --git a/src/core/operations/GeneratePGPKeyPair.mjs b/src/core/operations/GeneratePGPKeyPair.mjs new file mode 100644 index 00000000..d7cf6ba3 --- /dev/null +++ b/src/core/operations/GeneratePGPKeyPair.mjs @@ -0,0 +1,114 @@ +/** + * @author tlwr [toby@toby.codes] + * @author Matt C [matt@artemisbot.uk] + * @author n1474335 [n1474335@gmail.com] + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import * as kbpgp from "kbpgp"; +import { promisify } from "es6-promisify"; +import { getSubkeySize, ASP } from "../lib/PGP"; +/** + * Generate PGP Key Pair operation + */ +class GeneratePGPKeyPair extends Operation { + + /** + * GeneratePGPKeyPair constructor + */ + constructor() { + super(); + + this.name = "Generate PGP Key Pair"; + this.module = "PGP"; + this.description = "Generates a new public/private PGP key pair. Supports RSA and Eliptic Curve (EC) keys."; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Key type", + "type": "option", + "value": ["RSA-1024", "RSA-2048", "RSA-4096", "ECC-256", "ECC-384"] + }, + { + "name": "Password (optional)", + "type": "string", + "value": "" + }, + { + "name": "Name (optional)", + "type": "string", + "value": "" + }, + { + "name": "Email (optional)", + "type": "string", + "value": "" + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [keyType, keySize] = args[0].split("-"), + password = args[1], + name = args[2], + email = args[3]; + let userIdentifier = ""; + + if (name) userIdentifier += name; + if (email) userIdentifier += ` <${email}>`; + + let flags = kbpgp.const.openpgp.certify_keys; + flags |= kbpgp.const.openpgp.sign_data; + flags |= kbpgp.const.openpgp.auth; + flags |= kbpgp.const.openpgp.encrypt_comm; + flags |= kbpgp.const.openpgp.encrypt_storage; + + const keyGenerationOptions = { + userid: userIdentifier, + ecc: keyType === "ecc", + primary: { + "nbits": keySize, + "flags": flags, + "expire_in": 0 + }, + subkeys: [{ + "nbits": getSubkeySize(keySize), + "flags": kbpgp.const.openpgp.sign_data, + "expire_in": 86400 * 365 * 8 + }, { + "nbits": getSubkeySize(keySize), + "flags": kbpgp.const.openpgp.encrypt_comm | kbpgp.const.openpgp.encrypt_storage, + "expire_in": 86400 * 365 * 2 + }], + asp: ASP + }; + + return new Promise(async (resolve, reject) => { + try { + const unsignedKey = await promisify(kbpgp.KeyManager.generate)(keyGenerationOptions); + await promisify(unsignedKey.sign.bind(unsignedKey))({}); + + const signedKey = unsignedKey, + privateKeyExportOptions = {}; + + if (password) privateKeyExportOptions.passphrase = password; + const privateKey = await promisify(signedKey.export_pgp_private.bind(signedKey))(privateKeyExportOptions); + const publicKey = await promisify(signedKey.export_pgp_public.bind(signedKey))({}); + resolve(privateKey + "\n" + publicKey.trim()); + } catch (err) { + reject(`Error whilst generating key pair: ${err}`); + } + }); + } + +} + +export default GeneratePGPKeyPair; diff --git a/src/core/operations/PGPDecrypt.mjs b/src/core/operations/PGPDecrypt.mjs new file mode 100644 index 00000000..47ce787a --- /dev/null +++ b/src/core/operations/PGPDecrypt.mjs @@ -0,0 +1,74 @@ +/** + * @author tlwr [toby@toby.codes] + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import * as kbpgp from "kbpgp"; +import { promisify } from "es6-promisify"; +import { ASP, importPrivateKey } from "../lib/PGP"; + +/** + * PGP Decrypt operation + */ +class PGPDecrypt extends Operation { + + /** + * PGPDecrypt constructor + */ + constructor() { + super(); + + this.name = "PGP Decrypt"; + this.module = "PGP"; + this.description = "Input: the ASCII-armoured PGP message you want to decrypt.\n

\nArguments: the ASCII-armoured PGP private key of the recipient, \n(and the private key password if necessary).\n

\nPretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.\n

\nThis function uses the Keybase implementation of PGP."; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Private key of recipient", + "type": "text", + "value": "" + }, + { + "name": "Private key passphrase", + "type": "string", + "value": "" + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + async run(input, args) { + const encryptedMessage = input, + privateKey = args[0], + passphrase = args[1], + keyring = new kbpgp.keyring.KeyRing(); + let plaintextMessage; + + if (!privateKey) return "Enter the private key of the recipient."; + + const key = await importPrivateKey(privateKey, passphrase); + keyring.add_key_manager(key); + + try { + plaintextMessage = await promisify(kbpgp.unbox)({ + armored: encryptedMessage, + keyfetch: keyring, + asp: ASP + }); + } catch (err) { + throw `Couldn't decrypt message with provided private key: ${err}`; + } + + return plaintextMessage.toString(); + } + +} + +export default PGPDecrypt; diff --git a/src/core/operations/PGPDecryptAndVerify.mjs b/src/core/operations/PGPDecryptAndVerify.mjs new file mode 100644 index 00000000..d02d6d2e --- /dev/null +++ b/src/core/operations/PGPDecryptAndVerify.mjs @@ -0,0 +1,111 @@ +/** + * @author tlwr [toby@toby.codes] + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import * as kbpgp from "kbpgp"; +import { promisify } from "es6-promisify"; +import { ASP, importPrivateKey, importPublicKey } from "../lib/PGP"; + +/** + * PGP Decrypt and Verify operation + */ +class PGPDecryptAndVerify extends Operation { + + /** + * PGPDecryptAndVerify constructor + */ + constructor() { + super(); + + this.name = "PGP Decrypt and Verify"; + this.module = "PGP"; + this.description = "Input: the ASCII-armoured encrypted PGP message you want to verify.\n

\nArguments: the ASCII-armoured PGP public key of the signer, \nthe ASCII-armoured private key of the recipient (and the private key password if necessary).\n

\nThis operation uses PGP to decrypt and verify an encrypted digital signature.\n

\nPretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.\n

\nThis function uses the Keybase implementation of PGP."; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Public key of signer", + "type": "text", + "value": "" + }, + { + "name": "Private key of recipient", + "type": "text", + "value": "" + }, + { + "name": "Private key password", + "type": "string", + "value": "" + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + async run(input, args) { + const signedMessage = input, + publicKey = args[0], + privateKey = args[1], + passphrase = args[2], + keyring = new kbpgp.keyring.KeyRing(); + let unboxedLiterals; + + if (!publicKey) return "Enter the public key of the signer."; + if (!privateKey) return "Enter the private key of the recipient."; + const privKey = await importPrivateKey(privateKey, passphrase); + const pubKey = await importPublicKey(publicKey); + keyring.add_key_manager(privKey); + keyring.add_key_manager(pubKey); + + try { + unboxedLiterals = await promisify(kbpgp.unbox)({ + armored: signedMessage, + keyfetch: keyring, + asp: ASP + }); + const ds = unboxedLiterals[0].get_data_signer(); + if (ds) { + const km = ds.get_key_manager(); + if (km) { + const signer = km.get_userids_mark_primary()[0].components; + let text = "Signed by "; + if (signer.email || signer.username || signer.comment) { + if (signer.username) { + text += `${signer.username} `; + } + if (signer.comment) { + text += `${signer.comment} `; + } + if (signer.email) { + text += `<${signer.email}>`; + } + text += "\n"; + } + text += [ + `PGP fingerprint: ${km.get_pgp_fingerprint().toString("hex")}`, + `Signed on ${new Date(ds.sig.hashed_subpackets[0].time * 1000).toUTCString()}`, + "----------------------------------\n" + ].join("\n"); + text += unboxedLiterals.toString(); + return text.trim(); + } else { + return "Could not identify a key manager."; + } + } else { + return "The data does not appear to be signed."; + } + } catch (err) { + return `Couldn't verify message: ${err}`; + } + } + +} + +export default PGPDecryptAndVerify; diff --git a/src/core/operations/PGPEncrypt.mjs b/src/core/operations/PGPEncrypt.mjs new file mode 100644 index 00000000..b4193a80 --- /dev/null +++ b/src/core/operations/PGPEncrypt.mjs @@ -0,0 +1,72 @@ +/** + * @author tlwr [toby@toby.codes] + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import * as kbpgp from "kbpgp"; +import { promisify } from "es6-promisify"; +import { ASP } from "../lib/PGP"; +/** + * PGP Encrypt operation + */ +class PGPEncrypt extends Operation { + + /** + * PGPEncrypt constructor + */ + constructor() { + super(); + + this.name = "PGP Encrypt"; + this.module = "PGP"; + this.description = "Input: the message you want to encrypt.\n

\nArguments: the ASCII-armoured PGP public key of the recipient.\n

\nPretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.\n

\nThis function uses the Keybase implementation of PGP."; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Public key of recipient", + "type": "text", + "value": "" + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + async run(input, args) { + const plaintextMessage = input, + plainPubKey = args[0]; + let key, + encryptedMessage; + + if (!plainPubKey) return "Enter the public key of the recipient."; + + try { + key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({ + armored: plainPubKey, + }); + } catch (err) { + throw `Could not import public key: ${err}`; + } + + try { + encryptedMessage = await promisify(kbpgp.box)({ + "msg": plaintextMessage, + "encrypt_for": key, + "asp": ASP + }); + } catch (err) { + throw `Couldn't encrypt message with provided public key: ${err}`; + } + + return encryptedMessage.toString(); + } + +} + +export default PGPEncrypt; diff --git a/src/core/operations/PGPEncryptAndSign.mjs b/src/core/operations/PGPEncryptAndSign.mjs new file mode 100644 index 00000000..165bc9be --- /dev/null +++ b/src/core/operations/PGPEncryptAndSign.mjs @@ -0,0 +1,80 @@ +/** + * @author tlwr [toby@toby.codes] + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import * as kbpgp from "kbpgp"; +import { promisify } from "es6-promisify"; +import { ASP, importPrivateKey, importPublicKey } from "../lib/PGP"; + +/** + * PGP Encrypt and Sign operation + */ +class PGPEncryptAndSign extends Operation { + + /** + * PGPEncryptAndSign constructor + */ + constructor() { + super(); + + this.name = "PGP Encrypt and Sign"; + this.module = "PGP"; + this.description = "Input: the cleartext you want to sign.\n

\nArguments: the ASCII-armoured private key of the signer (plus the private key password if necessary)\nand the ASCII-armoured PGP public key of the recipient.\n

\nThis operation uses PGP to produce an encrypted digital signature.\n

\nPretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.\n

\nThis function uses the Keybase implementation of PGP."; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Private key of signer", + "type": "text", + "value": "" + }, + { + "name": "Private key passphrase", + "type": "string", + "value": "" + }, + { + "name": "Public key of recipient", + "type": "text", + "value": "" + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + async run(input, args) { + const message = input, + privateKey = args[0], + passphrase = args[1], + publicKey = args[2]; + let signedMessage; + + if (!privateKey) return "Enter the private key of the signer."; + if (!publicKey) return "Enter the public key of the recipient."; + const privKey = await importPrivateKey(privateKey, passphrase); + const pubKey = await importPublicKey(publicKey); + + try { + signedMessage = await promisify(kbpgp.box)({ + "msg": message, + "encrypt_for": pubKey, + "sign_with": privKey, + "asp": ASP + }); + } catch (err) { + throw `Couldn't sign message: ${err}`; + } + + return signedMessage; + } + +} + +export default PGPEncryptAndSign;