Ported x86 Disassembler & PGP ops

This commit is contained in:
Matt C 2018-05-15 10:15:31 +01:00
parent b8d39f49b2
commit 2b0c327001
7 changed files with 694 additions and 0 deletions

116
src/core/lib/PGP.mjs Normal file
View File

@ -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}`;
}
}

View File

@ -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.<br><br>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.<br><br>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;

View File

@ -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;

View File

@ -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<br><br>\nArguments: the ASCII-armoured PGP private key of the recipient, \n(and the private key password if necessary).\n<br><br>\nPretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.\n<br><br>\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;

View File

@ -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<br><br>\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<br><br>\nThis operation uses PGP to decrypt and verify an encrypted digital signature.\n<br><br>\nPretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.\n<br><br>\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;

View File

@ -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<br><br>\nArguments: the ASCII-armoured PGP public key of the recipient.\n<br><br>\nPretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.\n<br><br>\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;

View File

@ -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<br><br>\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<br><br>\nThis operation uses PGP to produce an encrypted digital signature.\n<br><br>\nPretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.\n<br><br>\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;