Merged master into esm branch

feature-extract-files
n1474335 2018-05-14 11:58:01 +00:00
commit a98d37e61c
21 changed files with 2206 additions and 2342 deletions

View File

@ -280,7 +280,7 @@ module.exports = function (grunt) {
chunks: false, chunks: false,
modules: false, modules: false,
entrypoints: false, entrypoints: false,
warningsFilter: /source-map/, warningsFilter: [/source-map/, /dependency is an expression/],
} }
}, },
start: { start: {

3521
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "cyberchef", "name": "cyberchef",
"version": "7.9.0", "version": "7.11.1",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>", "author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef", "homepage": "https://gchq.github.io/CyberChef",
@ -30,14 +30,14 @@
"main": "build/node/CyberChef.js", "main": "build/node/CyberChef.js",
"bugs": "https://github.com/gchq/CyberChef/issues", "bugs": "https://github.com/gchq/CyberChef/issues",
"devDependencies": { "devDependencies": {
"babel-core": "^6.26.0", "babel-core": "^6.26.3",
"babel-loader": "^7.1.3", "babel-loader": "^7.1.4",
"babel-preset-env": "^1.6.1", "babel-preset-env": "^1.6.1",
"css-loader": "^0.28.10", "css-loader": "^0.28.11",
"eslint": "^4.18.1", "eslint": "^4.19.1",
"exports-loader": "^0.7.0", "exports-loader": "^0.7.0",
"extract-text-webpack-plugin": "^4.0.0-alpha0", "extract-text-webpack-plugin": "^4.0.0-alpha0",
"file-loader": "^1.1.10", "file-loader": "^1.1.11",
"grunt": ">=1.0.2", "grunt": ">=1.0.2",
"grunt-accessibility": "~6.0.0", "grunt-accessibility": "~6.0.0",
"grunt-chmod": "~1.1.1", "grunt-chmod": "~1.1.1",
@ -48,23 +48,23 @@
"grunt-eslint": "^20.1.0", "grunt-eslint": "^20.1.0",
"grunt-exec": "~3.0.0", "grunt-exec": "~3.0.0",
"grunt-jsdoc": "^2.2.1", "grunt-jsdoc": "^2.2.1",
"grunt-webpack": "^3.0.2", "grunt-webpack": "^3.1.1",
"html-webpack-plugin": "^3.0.4", "html-webpack-plugin": "^3.2.0",
"imports-loader": "^0.8.0", "imports-loader": "^0.8.0",
"ink-docstrap": "^1.3.2", "ink-docstrap": "^1.3.2",
"jsdoc-babel": "^0.3.0", "jsdoc-babel": "^0.4.0",
"less": "^3.0.1", "less": "^3.0.2",
"less-loader": "^4.0.6", "less-loader": "^4.1.0",
"postcss-css-variables": "^0.8.0", "postcss-css-variables": "^0.8.1",
"postcss-import": "^11.1.0", "postcss-import": "^11.1.0",
"postcss-loader": "^2.1.1", "postcss-loader": "^2.1.4",
"sitemap": "^1.13.0", "sitemap": "^1.13.0",
"style-loader": "^0.20.2", "style-loader": "^0.21.0",
"url-loader": "^0.6.2", "url-loader": "^1.0.1",
"web-resource-inliner": "^4.2.1", "web-resource-inliner": "^4.2.1",
"webpack": "^4.0.1", "webpack": "^4.6.0",
"webpack-dev-server": "^3.1.0", "webpack-dev-server": "^3.1.3",
"webpack-node-externals": "^1.6.0", "webpack-node-externals": "^1.7.2",
"webpack-synchronizable-shell-plugin": "0.0.7", "webpack-synchronizable-shell-plugin": "0.0.7",
"worker-loader": "^1.1.1" "worker-loader": "^1.1.1"
}, },
@ -72,20 +72,21 @@
"babel-polyfill": "^6.26.0", "babel-polyfill": "^6.26.0",
"babel-plugin-transform-builtin-extend": "1.1.2", "babel-plugin-transform-builtin-extend": "1.1.2",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"bignumber.js": "^6.0.0", "bignumber.js": "^7.0.1",
"bootstrap": "^3.3.7", "bootstrap": "^3.3.7",
"bootstrap-colorpicker": "^2.5.2", "bootstrap-colorpicker": "^2.5.2",
"bootstrap-switch": "^3.3.4", "bootstrap-switch": "^3.3.4",
"bson": "^2.0.4", "bson": "^2.0.6",
"crypto-api": "^0.8.0", "crypto-api": "^0.8.0",
"crypto-js": "^3.1.9-1", "crypto-js": "^3.1.9-1",
"ctph.js": "0.0.5", "ctph.js": "0.0.5",
"diff": "^3.4.0", "diff": "^3.5.0",
"escodegen": "^1.9.1", "escodegen": "^1.9.1",
"es6-promisify": "^6.0.0",
"esmangle": "^1.0.1", "esmangle": "^1.0.1",
"esprima": "^4.0.0", "esprima": "^4.0.0",
"exif-parser": "^0.1.12", "exif-parser": "^0.1.12",
"file-saver": "^1.3.3", "file-saver": "^1.3.8",
"highlight.js": "^9.12.0", "highlight.js": "^9.12.0",
"jquery": "^3.3.1", "jquery": "^3.3.1",
"js-crc": "^0.2.0", "js-crc": "^0.2.0",
@ -93,15 +94,16 @@
"jsbn": "^1.1.0", "jsbn": "^1.1.0",
"jsesc": "^2.5.1", "jsesc": "^2.5.1",
"jsonpath": "^1.0.0", "jsonpath": "^1.0.0",
"jsrsasign": "8.0.6", "jsrsasign": "8.0.12",
"lodash": "^4.17.5", "lodash": "^4.17.10",
"loglevel": "^1.6.1", "loglevel": "^1.6.1",
"kbpgp": "^2.0.77",
"loglevel-message-prefix": "^3.0.0", "loglevel-message-prefix": "^3.0.0",
"moment": "^2.20.1", "moment": "^2.22.1",
"moment-timezone": "^0.5.14", "moment-timezone": "^0.5.16",
"node-forge": "^0.7.2", "node-forge": "^0.7.5",
"node-md6": "^0.1.0", "node-md6": "^0.1.0",
"nwmatcher": "^1.4.3", "nwmatcher": "^1.4.4",
"otp": "^0.1.3", "otp": "^0.1.3",
"scryptsy": "^2.0.0", "scryptsy": "^2.0.0",
"sladex-blowfish": "^0.8.1", "sladex-blowfish": "^0.8.1",

View File

@ -524,36 +524,43 @@ class Utils {
* Parses CSV data and returns it as a two dimensional array or strings. * Parses CSV data and returns it as a two dimensional array or strings.
* *
* @param {string} data * @param {string} data
* @param {string[]} [cellDelims=[","]]
* @param {string[]} [lineDelims=["\n", "\r"]]
* @returns {string[][]} * @returns {string[][]}
* *
* @example * @example
* // returns [["head1", "head2"], ["data1", "data2"]] * // returns [["head1", "head2"], ["data1", "data2"]]
* Utils.parseCSV("head1,head2\ndata1,data2"); * Utils.parseCSV("head1,head2\ndata1,data2");
*/ */
static parseCSV(data) { static parseCSV(data, cellDelims=[","], lineDelims=["\n", "\r"]) {
let b, let b,
ignoreNext = false, next,
renderNext = false,
inString = false, inString = false,
cell = "", cell = "",
line = []; line = [];
const lines = []; const lines = [];
// Remove BOM, often present in Excel CSV files
if (data.length && data[0] === "\uFEFF") data = data.substr(1);
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
b = data[i]; b = data[i];
if (ignoreNext) { next = data[i+1] || "";
if (renderNext) {
cell += b; cell += b;
ignoreNext = false; renderNext = false;
} else if (b === "\\") { } else if (b === "\\") {
cell += b; renderNext = true;
ignoreNext = true;
} else if (b === "\"" && !inString) { } else if (b === "\"" && !inString) {
inString = true; inString = true;
} else if (b === "\"" && inString) { } else if (b === "\"" && inString) {
inString = false; if (next === "\"") renderNext = true;
} else if (b === "," && !inString) { else inString = false;
} else if (!inString && cellDelims.indexOf(b) >= 0) {
line.push(cell); line.push(cell);
cell = ""; cell = "";
} else if ((b === "\n" || b === "\r") && !inString) { } else if (!inString && lineDelims.indexOf(b) >= 0) {
line.push(cell); line.push(cell);
cell = ""; cell = "";
lines.push(line); lines.push(line);

View File

@ -94,7 +94,12 @@
"PEM to Hex", "PEM to Hex",
"Hex to PEM", "Hex to PEM",
"Hex to Object Identifier", "Hex to Object Identifier",
"Object Identifier to Hex" "Object Identifier to Hex",
"Generate PGP Key Pair",
"PGP Encrypt",
"PGP Decrypt",
"PGP Encrypt and Sign",
"PGP Decrypt and Verify"
] ]
}, },
{ {
@ -164,6 +169,7 @@
"To Lower case", "To Lower case",
"Add line numbers", "Add line numbers",
"Remove line numbers", "Remove line numbers",
"To Table",
"Reverse", "Reverse",
"Sort", "Sort",
"Unique", "Unique",

View File

@ -672,6 +672,34 @@ const OP_CONFIG = {
} }
] ]
}, },
"To Table": {
module: "Default",
description: "Data can be split on different characters and rendered as an HTML or ASCII table with an optional header row.<br><br>Supports the CSV (Comma Separated Values) file format by default. Change the cell delimiter argument to <code>\\t</code> to support TSV (Tab Separated Values) or <code>|</code> for PSV (Pipe Separated Values).<br><br>You can enter as many delimiters as you like. Each character will be treat as a separate possible delimiter.",
inputType: "string",
outputType: "html",
args: [
{
name: "Cell delimiters",
type: "binaryShortString",
value: ","
},
{
name: "Row delimiters",
type: "binaryShortString",
value: "\\n\\r"
},
{
name: "Make first row header",
type: "boolean",
value: false
},
{
name: "Format",
type: "option",
value: "ToTable.FORMATS"
}
]
},
"From Hex": { "From Hex": {
module: "Default", module: "Default",
description: "Converts a hexadecimal byte string back into its raw value.<br><br>e.g. <code>ce 93 ce b5 ce b9 ce ac 20 cf 83 ce bf cf 85 0a</code> becomes the UTF-8 encoded string <code>Γειά σου</code>", description: "Converts a hexadecimal byte string back into its raw value.<br><br>e.g. <code>ce 93 ce b5 ce b9 ce ac 20 cf 83 ce bf cf 85 0a</code> becomes the UTF-8 encoded string <code>Γειά σου</code>",
@ -3363,14 +3391,14 @@ const OP_CONFIG = {
"CRC-32 Checksum": { "CRC-32 Checksum": {
module: "Hashing", module: "Hashing",
description: "A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data.<br><br>The CRC was invented by W. Wesley Peterson in 1961; the 32-bit CRC function of Ethernet and many other standards is the work of several researchers and was published in 1975.", description: "A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data.<br><br>The CRC was invented by W. Wesley Peterson in 1961; the 32-bit CRC function of Ethernet and many other standards is the work of several researchers and was published in 1975.",
inputType: "string", inputType: "ArrayBuffer",
outputType: "string", outputType: "string",
args: [] args: []
}, },
"CRC-16 Checksum": { "CRC-16 Checksum": {
module: "Hashing", module: "Hashing",
description: "A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data.<br><br>The CRC was invented by W. Wesley Peterson in 1961.", description: "A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data.<br><br>The CRC was invented by W. Wesley Peterson in 1961.",
inputType: "string", inputType: "ArrayBuffer",
outputType: "string", outputType: "string",
args: [] args: []
}, },
@ -3423,7 +3451,7 @@ const OP_CONFIG = {
}, },
"Parse X.509 certificate": { "Parse X.509 certificate": {
module: "PublicKey", module: "PublicKey",
description: "X.509 is an ITU-T standard for a public key infrastructure (PKI) and Privilege Management Infrastructure (PMI). It is commonly involved with SSL/TLS security.<br><br>This operation displays the contents of a certificate in a human readable format, similar to the openssl command line tool.", description: "X.509 is an ITU-T standard for a public key infrastructure (PKI) and Privilege Management Infrastructure (PMI). It is commonly involved with SSL/TLS security.<br><br>This operation displays the contents of a certificate in a human readable format, similar to the openssl command line tool.<br><br>Tags: X509, server hello, handshake",
inputType: "string", inputType: "string",
outputType: "string", outputType: "string",
args: [ args: [
@ -4219,6 +4247,150 @@ const OP_CONFIG = {
outputType: "string", outputType: "string",
args: [] args: []
}, },
"Generate PGP Key Pair": {
module: "PGP",
description: "Generates a new public/private PGP key pair. Supports RSA and Eliptic Curve (EC) keys.",
inputType: "string",
outputType: "string",
args: [
{
name: "Key type",
type: "option",
value: "PGP.KEY_TYPES"
},
{
name: "Password (optional)",
type: "string",
value: ""
},
{
name: "Name (optional)",
type: "string",
value: ""
},
{
name: "Email (optional)",
type: "string",
value: ""
},
]
},
"PGP Encrypt": {
module: "PGP",
description: [
"Input: the message you want to encrypt.",
"<br><br>",
"Arguments: the ASCII-armoured PGP public key of the recipient.",
"<br><br>",
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
"<br><br>",
"This function uses the Keybase implementation of PGP.",
].join("\n"),
inputType: "string",
outputType: "string",
args: [
{
name: "Public key of recipient",
type: "text",
value: ""
},
]
},
"PGP Decrypt": {
module: "PGP",
description: [
"Input: the ASCII-armoured PGP message you want to decrypt.",
"<br><br>",
"Arguments: the ASCII-armoured PGP private key of the recipient, ",
"(and the private key password if necessary).",
"<br><br>",
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
"<br><br>",
"This function uses the Keybase implementation of PGP.",
].join("\n"),
inputType: "string",
outputType: "string",
args: [
{
name: "Private key of recipient",
type: "text",
value: ""
},
{
name: "Private key passphrase",
type: "string",
value: ""
},
]
},
"PGP Encrypt and Sign": {
module: "PGP",
description: [
"Input: the cleartext you want to sign.",
"<br><br>",
"Arguments: the ASCII-armoured private key of the signer (plus the private key password if necessary)",
"and the ASCII-armoured PGP public key of the recipient.",
"<br><br>",
"This operation uses PGP to produce an encrypted digital signature.",
"<br><br>",
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
"<br><br>",
"This function uses the Keybase implementation of PGP.",
].join("\n"),
inputType: "string",
outputType: "string",
args: [
{
name: "Private key of signer",
type: "text",
value: ""
},
{
name: "Private key passphrase",
type: "string",
value: ""
},
{
name: "Public key of recipient",
type: "text",
value: ""
},
]
},
"PGP Decrypt and Verify": {
module: "PGP",
description: [
"Input: the ASCII-armoured encrypted PGP message you want to verify.",
"<br><br>",
"Arguments: the ASCII-armoured PGP public key of the signer, ",
"the ASCII-armoured private key of the recipient (and the private key password if necessary).",
"<br><br>",
"This operation uses PGP to decrypt and verify an encrypted digital signature.",
"<br><br>",
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
"<br><br>",
"This function uses the Keybase implementation of PGP.",
].join("\n"),
inputType: "string",
outputType: "string",
args: [
{
name: "Public key of signer",
type: "text",
value: "",
},
{
name: "Private key of recipient",
type: "text",
value: "",
},
{
name: "Private key password",
type: "string",
value: "",
},
]
},
}; };
main(); main();

View File

@ -84,9 +84,9 @@ export function toHexFast(data) {
* fromHex("0a:14:1e", "Colon"); * fromHex("0a:14:1e", "Colon");
*/ */
export function fromHex(data, delim, byteLen=2) { export function fromHex(data, delim, byteLen=2) {
delim = delim || (data.indexOf(" ") >= 0 ? "Space" : "None"); delim = delim || "Auto";
if (delim !== "None") { if (delim !== "None") {
const delimRegex = Utils.regexRep(delim); const delimRegex = delim === "Auto" ? /[^a-f\d]/gi : Utils.regexRep(delim);
data = data.replace(delimRegex, ""); data = data.replace(delimRegex, "");
} }
@ -99,6 +99,12 @@ export function fromHex(data, delim, byteLen=2) {
/** /**
* Hexadecimal delimiters. * To Hexadecimal delimiters.
*/ */
export const HEX_DELIM_OPTIONS = ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"]; export const TO_HEX_DELIM_OPTIONS = ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"];
/**
* From Hexadecimal delimiters.
*/
export const FROM_HEX_DELIM_OPTIONS = ["Auto"].concat(TO_HEX_DELIM_OPTIONS);

View File

@ -5,7 +5,7 @@
*/ */
import Operation from "../Operation"; import Operation from "../Operation";
import {fromHex, HEX_DELIM_OPTIONS} from "../lib/Hex"; import {fromHex, FROM_HEX_DELIM_OPTIONS} from "../lib/Hex";
import Utils from "../Utils"; import Utils from "../Utils";
/** /**
@ -28,7 +28,7 @@ class FromHex extends Operation {
{ {
name: "Delimiter", name: "Delimiter",
type: "option", type: "option",
value: HEX_DELIM_OPTIONS value: FROM_HEX_DELIM_OPTIONS
} }
]; ];
} }

View File

@ -5,7 +5,7 @@
*/ */
import Operation from "../Operation"; import Operation from "../Operation";
import {toHex, HEX_DELIM_OPTIONS} from "../lib/Hex"; import {toHex, TO_HEX_DELIM_OPTIONS} from "../lib/Hex";
import Utils from "../Utils"; import Utils from "../Utils";
/** /**
@ -28,7 +28,7 @@ class ToHex extends Operation {
{ {
name: "Delimiter", name: "Delimiter",
type: "option", type: "option",
value: HEX_DELIM_OPTIONS value: TO_HEX_DELIM_OPTIONS
} }
]; ];
} }

View File

@ -29,7 +29,7 @@ const BSON = {
const data = JSON.parse(input); const data = JSON.parse(input);
return bson.serialize(data).buffer; return bson.serialize(data).buffer;
} catch (err) { } catch (err) {
return err.toString(); throw err.toString();
} }
}, },

View File

@ -18,6 +18,16 @@ const ByteRepr = {
* @default * @default
*/ */
DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF"], DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF"],
/**
* @constant
* @default
*/
TO_HEX_DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"],
/**
* @constant
* @default
*/
FROM_HEX_DELIM_OPTIONS: ["Auto", "Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"],
/** /**
* @constant * @constant
* @default * @default

View File

@ -120,7 +120,7 @@ const Checksum = {
/** /**
* CRC-32 Checksum operation. * CRC-32 Checksum operation.
* *
* @param {string} input * @param {ArrayBuffer} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
*/ */
@ -132,7 +132,7 @@ const Checksum = {
/** /**
* CRC-16 Checksum operation. * CRC-16 Checksum operation.
* *
* @param {string} input * @param {ArrayBuffer} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
*/ */

364
src/core/operations/legacy/PGP.js Executable file
View File

@ -0,0 +1,364 @@
import * as kbpgp from "kbpgp";
import {promisify} from "es6-promisify";
/**
* PGP operations.
*
* @author tlwr [toby@toby.codes]
* @author Matt C [matt@artemisbot.uk]
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const PGP = {
/**
* @constant
* @default
*/
KEY_TYPES: ["RSA-1024", "RSA-2048", "RSA-4096", "ECC-256", "ECC-384"],
/**
* Get size of subkey
*
* @private
* @param {number} keySize
* @returns {number}
*/
_getSubkeySize(keySize) {
return {
1024: 1024,
2048: 1024,
4096: 2048,
256: 256,
384: 256,
}[keySize];
},
/**
* Progress callback
*
* @private
*/
_ASP: new 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);
}
}),
/**
* Import private key and unlock if necessary
*
* @private
* @param {string} privateKey
* @param {string} [passphrase]
* @returns {Object}
*/
async _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
*
* @private
* @param {string} publicKey
* @returns {Object}
*/
async _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}`;
}
},
/**
* Generate PGP Key Pair operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runGenerateKeyPair(input, args) {
let [keyType, keySize] = args[0].split("-"),
password = args[1],
name = args[2],
email = args[3],
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;
let keyGenerationOptions = {
userid: userIdentifier,
ecc: keyType === "ecc",
primary: {
"nbits": keySize,
"flags": flags,
"expire_in": 0
},
subkeys: [{
"nbits": PGP._getSubkeySize(keySize),
"flags": kbpgp.const.openpgp.sign_data,
"expire_in": 86400 * 365 * 8
}, {
"nbits": PGP._getSubkeySize(keySize),
"flags": kbpgp.const.openpgp.encrypt_comm | kbpgp.const.openpgp.encrypt_storage,
"expire_in": 86400 * 365 * 2
}],
asp: PGP._ASP
};
return new Promise(async (resolve, reject) => {
try {
const unsignedKey = await promisify(kbpgp.KeyManager.generate)(keyGenerationOptions);
await promisify(unsignedKey.sign.bind(unsignedKey))({});
let signedKey = unsignedKey;
let 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}`);
}
});
},
/**
* PGP Encrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
async runEncrypt(input, args) {
let plaintextMessage = input,
plainPubKey = args[0],
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": PGP._ASP
});
} catch (err) {
throw `Couldn't encrypt message with provided public key: ${err}`;
}
return encryptedMessage.toString();
},
/**
* PGP Decrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
async runDecrypt(input, args) {
let encryptedMessage = input,
privateKey = args[0],
passphrase = args[1],
keyring = new kbpgp.keyring.KeyRing(),
plaintextMessage;
if (!privateKey) return "Enter the private key of the recipient.";
const key = await PGP._importPrivateKey(privateKey, passphrase);
keyring.add_key_manager(key);
try {
plaintextMessage = await promisify(kbpgp.unbox)({
armored: encryptedMessage,
keyfetch: keyring,
asp: PGP._ASP
});
} catch (err) {
throw `Couldn't decrypt message with provided private key: ${err}`;
}
return plaintextMessage.toString();
},
/**
* PGP Sign Message operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
async runSign(input, args) {
let message = input,
privateKey = args[0],
passphrase = args[1],
publicKey = args[2],
signedMessage;
if (!privateKey) return "Enter the private key of the signer.";
if (!publicKey) return "Enter the public key of the recipient.";
const privKey = await PGP._importPrivateKey(privateKey, passphrase);
const pubKey = await PGP._importPublicKey(publicKey);
try {
signedMessage = await promisify(kbpgp.box)({
"msg": message,
"encrypt_for": pubKey,
"sign_with": privKey,
"asp": PGP._ASP
});
} catch (err) {
throw `Couldn't sign message: ${err}`;
}
return signedMessage;
},
/**
* PGP Verify Message operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
async runVerify(input, args) {
let signedMessage = input,
publicKey = args[0],
privateKey = args[1],
passphrase = args[2],
keyring = new kbpgp.keyring.KeyRing(),
unboxedLiterals;
if (!publicKey) return "Enter the public key of the signer.";
if (!privateKey) return "Enter the private key of the recipient.";
const privKey = await PGP._importPrivateKey(privateKey, passphrase);
const pubKey = await PGP._importPublicKey(publicKey);
keyring.add_key_manager(privKey);
keyring.add_key_manager(pubKey);
try {
unboxedLiterals = await promisify(kbpgp.unbox)({
armored: signedMessage,
keyfetch: keyring,
asp: PGP._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 PGP;

View File

@ -0,0 +1,164 @@
/**
* ToTable operations.
*
* @author Mark Jones [github.com/justanothermark]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*
* @namespace
*/
import Utils from "../Utils.js";
const ToTable = {
/**
* @constant
* @default
*/
FORMATS: [
"ASCII",
"HTML"
],
/**
* To Table operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runToTable: function (input, args) {
const [cellDelims, rowDelims, firstRowHeader, format] = args;
// Process the input into a nested array of elements.
const tableData = Utils.parseCSV(input, cellDelims.split(""), rowDelims.split(""));
if (!tableData.length) return "";
// Render the data in the requested format.
switch (format) {
case "ASCII":
return asciiOutput(tableData);
case "HTML":
default:
return htmlOutput(tableData);
}
/**
* Outputs an array of data as an ASCII table.
*
* @param {string[][]} tableData
* @returns {string}
*/
function asciiOutput(tableData) {
const horizontalBorder = "-";
const verticalBorder = "|";
const crossBorder = "+";
let output = "";
let longestCells = [];
// Find longestCells value per column to pad cells equally.
tableData.forEach(function(row, index) {
row.forEach(function(cell, cellIndex) {
if (longestCells[cellIndex] === undefined || cell.length > longestCells[cellIndex]) {
longestCells[cellIndex] = cell.length;
}
});
});
// Add the top border of the table to the output.
output += outputHorizontalBorder(longestCells);
// If the first row is a header, remove the row from the data and
// add it to the output with another horizontal border.
if (firstRowHeader) {
let row = tableData.shift();
output += outputRow(row, longestCells);
output += outputHorizontalBorder(longestCells);
}
// Add the rest of the table rows.
tableData.forEach(function(row, index) {
output += outputRow(row, longestCells);
});
// Close the table with a final horizontal border.
output += outputHorizontalBorder(longestCells);
return output;
/**
* Outputs a row of correctly padded cells.
*/
function outputRow(row, longestCells) {
let rowOutput = verticalBorder;
row.forEach(function(cell, index) {
rowOutput += " " + cell + " ".repeat(longestCells[index] - cell.length) + " " + verticalBorder;
});
rowOutput += "\n";
return rowOutput;
}
/**
* Outputs a horizontal border with a different character where
* the horizontal border meets a vertical border.
*/
function outputHorizontalBorder(longestCells) {
let rowOutput = crossBorder;
longestCells.forEach(function(cellLength) {
rowOutput += horizontalBorder.repeat(cellLength + 2) + crossBorder;
});
rowOutput += "\n";
return rowOutput;
}
}
/**
* Outputs a table of data as a HTML table.
*
* @param {string[][]} tableData
* @returns {string}
*/
function htmlOutput(tableData) {
// Start the HTML output with suitable classes for styling.
let output = "<table class='table table-hover table-condensed table-bordered table-nonfluid'>";
// If the first row is a header then put it in <thead> with <th> cells.
if (firstRowHeader) {
let row = tableData.shift();
output += "<thead>";
output += outputRow(row, "th");
output += "</thead>";
}
// Output the rest of the rows in the <tbody>.
output += "<tbody>";
tableData.forEach(function(row, index) {
output += outputRow(row, "td");
});
// Close the body and table elements.
output += "</tbody></table>";
return output;
/**
* Outputs a table row.
*
* @param {string[]} row
* @param {string} cellType
*/
function outputRow(row, cellType) {
let output = "<tr>";
row.forEach(function(cell) {
output += "<" + cellType + ">" + cell + "</" + cellType + ">";
});
output += "</tr>";
return output;
}
}
}
};
export default ToTable;

View File

@ -83,6 +83,9 @@ App.prototype.loaded = function() {
// Clear the loading message interval // Clear the loading message interval
clearInterval(window.loadingMsgsInt); clearInterval(window.loadingMsgsInt);
// Remove the loading error handler
window.removeEventListener("error", window.loadingErrorHandler);
document.dispatchEvent(this.manager.apploaded); document.dispatchEvent(this.manager.apploaded);
}; };

View File

@ -42,7 +42,7 @@
} }
// Define loading messages // Define loading messages
const loadingMsgs = [ var loadingMsgs = [
"Proving P = NP...", "Proving P = NP...",
"Computing 6 x 9...", "Computing 6 x 9...",
"Mining bitcoin...", "Mining bitcoin...",
@ -66,18 +66,18 @@
// Shuffle array using Durstenfeld algorithm // Shuffle array using Durstenfeld algorithm
for (let i = loadingMsgs.length - 1; i > 0; --i) { for (let i = loadingMsgs.length - 1; i > 0; --i) {
const j = Math.floor(Math.random() * (i + 1)); var j = Math.floor(Math.random() * (i + 1));
const temp = loadingMsgs[i]; var temp = loadingMsgs[i];
loadingMsgs[i] = loadingMsgs[j]; loadingMsgs[i] = loadingMsgs[j];
loadingMsgs[j] = temp; loadingMsgs[j] = temp;
} }
// Show next loading message and move it to the end of the array // Show next loading message and move it to the end of the array
function changeLoadingMsg() { function changeLoadingMsg() {
const msg = loadingMsgs.shift(); var msg = loadingMsgs.shift();
loadingMsgs.push(msg); loadingMsgs.push(msg);
try { try {
const el = document.getElementById("preloader-msg"); var el = document.getElementById("preloader-msg");
if (!el.classList.contains("loading")) if (!el.classList.contains("loading"))
el.classList.add("loading"); // Causes CSS transition on first message el.classList.add("loading"); // Causes CSS transition on first message
el.innerHTML = msg; el.innerHTML = msg;
@ -86,6 +86,46 @@
changeLoadingMsg(); changeLoadingMsg();
window.loadingMsgsInt = setInterval(changeLoadingMsg, (Math.random() * 2000) + 1500); window.loadingMsgsInt = setInterval(changeLoadingMsg, (Math.random() * 2000) + 1500);
// If any errors are thrown during loading, handle them here
function loadingErrorHandler(e) {
function escapeHtml(str) {
var HTML_CHARS = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#x27;", // &apos; not recommended because it's not in the HTML spec
"/": "&#x2F;", // forward slash is included as it helps end an HTML entity
"`": "&#x60;"
};
return str.replace(/[&<>"'/`]/g, function (match) {
return HTML_CHARS[match];
});
}
var msg = e.message +
(e.filename ? "\nFilename: " + e.filename : "") +
(e.lineno ? "\nLine: " + e.lineno : "") +
(e.colno ? "\nColumn: " + e.colno : "") +
(e.error ? "\nError: " + e.error : "") +
"\nUser-Agent: " + navigator.userAgent +
"\nCyberChef version: <%= htmlWebpackPlugin.options.version %>";
clearInterval(window.loadingMsgsInt);
document.getElementById("preloader").remove();
document.getElementById("preloader-msg").remove();
document.getElementById("preloader-error").innerHTML =
"CyberChef encountered an error while loading.<br><br>" +
"The following browser versions are supported:" +
"<ul><li>Google Chrome 40+</li><li>Mozilla Firefox 35+</li><li>Microsoft Edge 14+</li></ul>" +
"Your user agent is:<br>" + escapeHtml(navigator.userAgent) + "<br><br>" +
"If your browser is supported, please <a href='https://github.com/gchq/CyberChef/issues/new'>" +
"raise an issue</a> including the following details:<br><br>" +
"<pre>" + escapeHtml(msg) + "</pre>";
};
window.addEventListener("error", loadingErrorHandler);
</script> </script>
<% if (htmlWebpackPlugin.options.inline) { %> <% if (htmlWebpackPlugin.options.inline) { %>
<meta name="robots" content="noindex" /> <meta name="robots" content="noindex" />
@ -100,6 +140,7 @@
<div id="loader-wrapper"> <div id="loader-wrapper">
<div id="preloader" class="loader"></div> <div id="preloader" class="loader"></div>
<div id="preloader-msg" class="loading-msg"></div> <div id="preloader-msg" class="loading-msg"></div>
<div id="preloader-error" class="loading-error"></div>
</div> </div>
<!-- End preloader overlay --> <!-- End preloader overlay -->
<span id="edit-favourites" class="btn btn-default btn-sm"><img aria-hidden="true" src="<%- require('../static/images/favourite-16x16.png') %>" alt="Star Icon"/> Edit</span> <span id="edit-favourites" class="btn btn-default btn-sm"><img aria-hidden="true" src="<%- require('../static/images/favourite-16x16.png') %>" alt="Star Icon"/> Edit</span>

View File

@ -64,3 +64,4 @@ window.compileMessage = COMPILE_MSG;
window.CanvasComponents = CanvasComponents; window.CanvasComponents = CanvasComponents;
document.addEventListener("DOMContentLoaded", main, false); document.addEventListener("DOMContentLoaded", main, false);

View File

@ -74,6 +74,14 @@
transition: all 0.1s ease-in; transition: all 0.1s ease-in;
} }
.loading-error {
display: block;
position: relative;
width: 600px;
left: calc(50% - 300px);
top: 10%;
}
/* Loaded */ /* Loaded */
.loaded .loading-msg { .loaded .loading-msg {

View File

@ -31,6 +31,7 @@ import "./tests/operations/Base64";
// import "./tests/operations/BSON.js"; // import "./tests/operations/BSON.js";
// import "./tests/operations/ByteRepr.js"; // import "./tests/operations/ByteRepr.js";
// import "./tests/operations/CharEnc.js"; // import "./tests/operations/CharEnc.js";
//import "./tests/operations/Checksum.js";
// import "./tests/operations/Cipher.js"; // import "./tests/operations/Cipher.js";
// import "./tests/operations/Code.js"; // import "./tests/operations/Code.js";
// import "./tests/operations/Compress.js"; // import "./tests/operations/Compress.js";
@ -45,7 +46,7 @@ import "./tests/operations/Base64";
// import "./tests/operations/NetBIOS.js"; // import "./tests/operations/NetBIOS.js";
// import "./tests/operations/OTP.js"; // import "./tests/operations/OTP.js";
// import "./tests/operations/Regex.js"; // import "./tests/operations/Regex.js";
import "./tests/operations/Rotate.mjs"; import "./tests/operations/Rotate";
// import "./tests/operations/StrUtils.js"; // import "./tests/operations/StrUtils.js";
// import "./tests/operations/SeqUtils.js"; // import "./tests/operations/SeqUtils.js";
import "./tests/operations/SetUnion"; import "./tests/operations/SetUnion";

View File

@ -0,0 +1,120 @@
/**
* Checksum tests.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import TestRegister from "../../TestRegister.js";
const BASIC_STRING = "The ships hung in the sky in much the same way that bricks don't.";
const UTF8_STR = "ნუ პანიკას";
const ALL_BYTES = [
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f",
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f",
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f",
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf",
"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
"\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
].join("");
TestRegister.addTests([
{
name: "CRC-16: nothing",
input: "",
expectedOutput: "0000",
recipeConfig: [
{
"op": "CRC-16 Checksum",
"args": []
}
]
},
{
name: "CRC-16: basic string",
input: BASIC_STRING,
expectedOutput: "0c70",
recipeConfig: [
{
"op": "CRC-16 Checksum",
"args": []
}
]
},
{
name: "CRC-16: UTF-8",
input: UTF8_STR,
expectedOutput: "dcf6",
recipeConfig: [
{
"op": "CRC-16 Checksum",
"args": []
}
]
},
{
name: "CRC-16: all bytes",
input: ALL_BYTES,
expectedOutput: "bad3",
recipeConfig: [
{
"op": "CRC-16 Checksum",
"args": []
}
]
},
{
name: "CRC-32: nothing",
input: "",
expectedOutput: "00000000",
recipeConfig: [
{
"op": "CRC-32 Checksum",
"args": []
}
]
},
{
name: "CRC-32: basic string",
input: BASIC_STRING,
expectedOutput: "bf4b739c",
recipeConfig: [
{
"op": "CRC-32 Checksum",
"args": []
}
]
},
{
name: "CRC-32: UTF-8",
input: UTF8_STR,
expectedOutput: "87553290",
recipeConfig: [
{
"op": "CRC-32 Checksum",
"args": []
}
]
},
{
name: "CRC-32: all bytes",
input: ALL_BYTES,
expectedOutput: "29058c73",
recipeConfig: [
{
"op": "CRC-32 Checksum",
"args": []
}
]
},
]);

View File

@ -66,7 +66,7 @@ module.exports = {
rules: [ rules: [
{ {
test: /\.m?js$/, test: /\.m?js$/,
exclude: /node_modules/, exclude: /node_modules\/(?!jsesc)/,
loader: "babel-loader?compact=false" loader: "babel-loader?compact=false"
}, },
{ {
@ -118,7 +118,7 @@ module.exports = {
chunks: false, chunks: false,
modules: false, modules: false,
entrypoints: false, entrypoints: false,
warningsFilter: /source-map/, warningsFilter: [/source-map/, /dependency is an expression/],
}, },
node: { node: {
fs: "empty" fs: "empty"