Merge branch 'esm' into node-lib

This commit is contained in:
d98762625 2018-05-24 16:12:26 +01:00
commit 0977e82170
293 changed files with 32051 additions and 14655 deletions

View file

@ -0,0 +1,108 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import forge from "node-forge/dist/forge.min.js";
import OperationError from "../errors/OperationError";
/**
* AES Decrypt operation
*/
class AESDecrypt extends Operation {
/**
* AESDecrypt constructor
*/
constructor() {
super();
this.name = "AES Decrypt";
this.module = "Ciphers";
this.description = "Advanced Encryption Standard (AES) is a U.S. Federal Information Processing Standard (FIPS). It was selected after a 5-year process where 15 competing designs were evaluated.<br><br><b>Key:</b> The following algorithms will be used based on the size of the key:<ul><li>16 bytes = AES-128</li><li>24 bytes = AES-192</li><li>32 bytes = AES-256</li></ul><br><br><b>IV:</b> The Initialization Vector should be 16 bytes long. If not entered, it will default to 16 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used.<br><br><b>GCM Tag:</b> This field is ignored unless 'GCM' mode is used.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Key",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
{
"name": "IV",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
{
"name": "Mode",
"type": "option",
"value": ["CBC", "CFB", "OFB", "CTR", "GCM", "ECB"]
},
{
"name": "Input",
"type": "option",
"value": ["Hex", "Raw"]
},
{
"name": "Output",
"type": "option",
"value": ["Raw", "Hex"]
},
{
"name": "GCM Tag",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if cannot decrypt input or invalid key length
*/
run(input, args) {
const key = Utils.convertToByteArray(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4],
gcmTag = Utils.convertToByteString(args[5].string, args[5].option);
if ([16, 24, 32].indexOf(key.length) < 0) {
throw new OperationError(`Invalid key length: ${key.length} bytes
The following algorithms will be used based on the size of the key:
16 bytes = AES-128
24 bytes = AES-192
32 bytes = AES-256`);
}
input = Utils.convertToByteString(input, inputType);
const decipher = forge.cipher.createDecipher("AES-" + mode, key);
decipher.start({
iv: iv,
tag: gcmTag
});
decipher.update(forge.util.createBuffer(input));
const result = decipher.finish();
if (result) {
return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
} else {
throw new OperationError("Unable to decrypt input with these parameters.");
}
}
}
export default AESDecrypt;

View file

@ -0,0 +1,106 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import forge from "node-forge/dist/forge.min.js";
import OperationError from "../errors/OperationError";
/**
* AES Encrypt operation
*/
class AESEncrypt extends Operation {
/**
* AESEncrypt constructor
*/
constructor() {
super();
this.name = "AES Encrypt";
this.module = "Ciphers";
this.description = "Advanced Encryption Standard (AES) is a U.S. Federal Information Processing Standard (FIPS). It was selected after a 5-year process where 15 competing designs were evaluated.<br><br><b>Key:</b> The following algorithms will be used based on the size of the key:<ul><li>16 bytes = AES-128</li><li>24 bytes = AES-192</li><li>32 bytes = AES-256</li></ul>You can generate a password-based key using one of the KDF operations.<br><br><b>IV:</b> The Initialization Vector should be 16 bytes long. If not entered, it will default to 16 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Key",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
{
"name": "IV",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
{
"name": "Mode",
"type": "option",
"value": ["CBC", "CFB", "OFB", "CTR", "GCM", "ECB"]
},
{
"name": "Input",
"type": "option",
"value": ["Raw", "Hex"]
},
{
"name": "Output",
"type": "option",
"value": ["Hex", "Raw"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if invalid key length
*/
run(input, args) {
const key = Utils.convertToByteArray(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4];
if ([16, 24, 32].indexOf(key.length) < 0) {
throw new OperationError(`Invalid key length: ${key.length} bytes
The following algorithms will be used based on the size of the key:
16 bytes = AES-128
24 bytes = AES-192
32 bytes = AES-256`);
}
input = Utils.convertToByteString(input, inputType);
const cipher = forge.cipher.createCipher("AES-" + mode, key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(input));
cipher.finish();
if (outputType === "Hex") {
if (mode === "GCM") {
return cipher.output.toHex() + "\n\n" +
"Tag: " + cipher.mode.tag.toHex();
}
return cipher.output.toHex();
} else {
if (mode === "GCM") {
return cipher.output.getBytes() + "\n\n" +
"Tag: " + cipher.mode.tag.getBytes();
}
return cipher.output.getBytes();
}
}
}
export default AESEncrypt;

View file

@ -0,0 +1,46 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Add line numbers operation
*/
class AddLineNumbers extends Operation {
/**
* AddLineNumbers constructor
*/
constructor() {
super();
this.name = "Add line numbers";
this.module = "Default";
this.description = "Adds line numbers to the output.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const lines = input.split("\n"),
width = lines.length.toString().length;
let output = "";
for (let n = 0; n < lines.length; n++) {
output += (n+1).toString().padStart(width, " ") + " " + lines[n] + "\n";
}
return output.slice(0, output.length-1);
}
}
export default AddLineNumbers;

View file

@ -0,0 +1,52 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Adler-32 Checksum operation
*/
class Adler32Checksum extends Operation {
/**
* Adler32Checksum constructor
*/
constructor() {
super();
this.name = "Adler-32 Checksum";
this.module = "Hashing";
this.description = "Adler-32 is a checksum algorithm which was invented by Mark Adler in 1995, and is a modification of the Fletcher checksum. Compared to a cyclic redundancy check of the same length, it trades reliability for speed (preferring the latter).<br><br>Adler-32 is more reliable than Fletcher-16, and slightly less reliable than Fletcher-32.";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const MOD_ADLER = 65521;
let a = 1,
b = 0;
for (let i = 0; i < input.length; i++) {
a += input[i];
b += a;
}
a %= MOD_ADLER;
b %= MOD_ADLER;
return Utils.hex(((b << 16) | a) >>> 0, 8);
}
}
export default Adler32Checksum;

View file

@ -0,0 +1,105 @@
/**
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
/**
* Affine Cipher Decode operation
*/
class AffineCipherDecode extends Operation {
/**
* AffineCipherDecode constructor
*/
constructor() {
super();
this.name = "Affine Cipher Decode";
this.module = "Ciphers";
this.description = "The Affine cipher is a type of monoalphabetic substitution cipher. To decrypt, each letter in an alphabet is mapped to its numeric equivalent, decrypted by a mathematical function, and converted back to a letter.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "a",
"type": "number",
"value": 1
},
{
"name": "b",
"type": "number",
"value": 0
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if a or b values are invalid
*/
run(input, args) {
const alphabet = "abcdefghijklmnopqrstuvwxyz",
[a, b] = args,
aModInv = Utils.modInv(a, 26); // Calculates modular inverse of a
let output = "";
if (!/^\+?(0|[1-9]\d*)$/.test(a) || !/^\+?(0|[1-9]\d*)$/.test(b)) {
throw new OperationError("The values of a and b can only be integers.");
}
if (Utils.gcd(a, 26) !== 1) {
throw new OperationError("The value of `a` must be coprime to 26.");
}
for (let i = 0; i < input.length; i++) {
if (alphabet.indexOf(input[i]) >= 0) {
// Uses the affine decode function (y-b * A') % m = x (where m is length of the alphabet and A' is modular inverse)
output += alphabet[Utils.mod((alphabet.indexOf(input[i]) - b) * aModInv, 26)];
} else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
// Same as above, accounting for uppercase
output += alphabet[Utils.mod((alphabet.indexOf(input[i].toLowerCase()) - b) * aModInv, 26)].toUpperCase();
} else {
// Non-alphabetic characters
output += input[i];
}
}
return output;
}
/**
* Highlight Affine Cipher Decode
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Affine Cipher Decode in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default AffineCipherDecode;

View file

@ -0,0 +1,77 @@
/**
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import { affineEncode } from "../lib/Ciphers";
/**
* Affine Cipher Encode operation
*/
class AffineCipherEncode extends Operation {
/**
* AffineCipherEncode constructor
*/
constructor() {
super();
this.name = "Affine Cipher Encode";
this.module = "Ciphers";
this.description = "The Affine cipher is a type of monoalphabetic substitution cipher, wherein each letter in an alphabet is mapped to its numeric equivalent, encrypted using simple mathematical function, <code>(ax + b) % 26</code>, and converted back to a letter.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "a",
"type": "number",
"value": 1
},
{
"name": "b",
"type": "number",
"value": 0
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return affineEncode(input, args);
}
/**
* Highlight Affine Cipher Encode
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Affine Cipher Encode in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default AffineCipherEncode;

View file

@ -0,0 +1,183 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
/**
* Analyse hash operation
*/
class AnalyseHash extends Operation {
/**
* AnalyseHash constructor
*/
constructor() {
super();
this.name = "Analyse hash";
this.module = "Hashing";
this.description = "Tries to determine information about a given hash and suggests which algorithm may have been used to generate it based on its length.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
input = input.replace(/\s/g, "");
let output = "",
possibleHashFunctions = [];
const byteLength = input.length / 2,
bitLength = byteLength * 8;
if (!/^[a-f0-9]+$/i.test(input)) {
throw new OperationError("Invalid hash");
}
output += "Hash length: " + input.length + "\n" +
"Byte length: " + byteLength + "\n" +
"Bit length: " + bitLength + "\n\n" +
"Based on the length, this hash could have been generated by one of the following hashing functions:\n";
switch (bitLength) {
case 4:
possibleHashFunctions = [
"Fletcher-4",
"Luhn algorithm",
"Verhoeff algorithm",
];
break;
case 8:
possibleHashFunctions = [
"Fletcher-8",
];
break;
case 16:
possibleHashFunctions = [
"BSD checksum",
"CRC-16",
"SYSV checksum",
"Fletcher-16"
];
break;
case 32:
possibleHashFunctions = [
"CRC-32",
"Fletcher-32",
"Adler-32",
];
break;
case 64:
possibleHashFunctions = [
"CRC-64",
"RIPEMD-64",
"SipHash",
];
break;
case 128:
possibleHashFunctions = [
"MD5",
"MD4",
"MD2",
"HAVAL-128",
"RIPEMD-128",
"Snefru",
"Tiger-128",
];
break;
case 160:
possibleHashFunctions = [
"SHA-1",
"SHA-0",
"FSB-160",
"HAS-160",
"HAVAL-160",
"RIPEMD-160",
"Tiger-160",
];
break;
case 192:
possibleHashFunctions = [
"Tiger",
"HAVAL-192",
];
break;
case 224:
possibleHashFunctions = [
"SHA-224",
"SHA3-224",
"ECOH-224",
"FSB-224",
"HAVAL-224",
];
break;
case 256:
possibleHashFunctions = [
"SHA-256",
"SHA3-256",
"BLAKE-256",
"ECOH-256",
"FSB-256",
"GOST",
"Grøstl-256",
"HAVAL-256",
"PANAMA",
"RIPEMD-256",
"Snefru",
];
break;
case 320:
possibleHashFunctions = [
"RIPEMD-320",
];
break;
case 384:
possibleHashFunctions = [
"SHA-384",
"SHA3-384",
"ECOH-384",
"FSB-384",
];
break;
case 512:
possibleHashFunctions = [
"SHA-512",
"SHA3-512",
"BLAKE-512",
"ECOH-512",
"FSB-512",
"Grøstl-512",
"JH",
"MD6",
"Spectral Hash",
"SWIFFT",
"Whirlpool",
];
break;
case 1024:
possibleHashFunctions = [
"Fowler-Noll-Vo",
];
break;
default:
possibleHashFunctions = [
"Unknown"
];
break;
}
return output + possibleHashFunctions.join("\n");
}
}
export default AnalyseHash;

View file

@ -0,0 +1,66 @@
/**
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import { affineEncode } from "../lib/Ciphers";
/**
* Atbash Cipher operation
*/
class AtbashCipher extends Operation {
/**
* AtbashCipher constructor
*/
constructor() {
super();
this.name = "Atbash Cipher";
this.module = "Ciphers";
this.description = "Atbash is a mono-alphabetic substitution cipher originally used to encode the Hebrew alphabet. It has been modified here for use with the Latin alphabet.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return affineEncode(input, [25, 25]);
}
/**
* Highlight Atbash Cipher
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Atbash Cipher in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default AtbashCipher;

View file

@ -0,0 +1,50 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import bsonjs from "bson";
import OperationError from "../errors/OperationError";
/**
* BSON deserialise operation
*/
class BSONDeserialise extends Operation {
/**
* BSONDeserialise constructor
*/
constructor() {
super();
this.name = "BSON deserialise";
this.module = "BSON";
this.description = "BSON is a computer data interchange format used mainly as a data storage and network transfer format in the MongoDB database. It is a binary form for representing simple data structures, associative arrays (called objects or documents in MongoDB), and various data types of specific interest to MongoDB. The name 'BSON' is based on the term JSON and stands for 'Binary JSON'.<br><br>Input data should be in a raw bytes format.";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!input.byteLength) return "";
const bson = new bsonjs();
try {
const data = bson.deserialize(new Buffer(input));
return JSON.stringify(data, null, 2);
} catch (err) {
throw new OperationError(err.toString());
}
}
}
export default BSONDeserialise;

View file

@ -0,0 +1,50 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import bsonjs from "bson";
import OperationError from "../errors/OperationError";
/**
* BSON serialise operation
*/
class BSONSerialise extends Operation {
/**
* BSONSerialise constructor
*/
constructor() {
super();
this.name = "BSON serialise";
this.module = "BSON";
this.description = "BSON is a computer data interchange format used mainly as a data storage and network transfer format in the MongoDB database. It is a binary form for representing simple data structures, associative arrays (called objects or documents in MongoDB), and various data types of specific interest to MongoDB. The name 'BSON' is based on the term JSON and stands for 'Binary JSON'.<br><br>Input data should be valid JSON.";
this.inputType = "string";
this.outputType = "ArrayBuffer";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
run(input, args) {
if (!input) return new ArrayBuffer();
const bson = new bsonjs();
try {
const data = JSON.parse(input);
return bson.serialize(data).buffer;
} catch (err) {
throw new OperationError(err.toString());
}
}
}
export default BSONSerialise;

View file

@ -0,0 +1,54 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import bcrypt from "bcryptjs";
/**
* Bcrypt operation
*/
class Bcrypt extends Operation {
/**
* Bcrypt constructor
*/
constructor() {
super();
this.name = "Bcrypt";
this.module = "Hashing";
this.description = "bcrypt is a password hashing function designed by Niels Provos and David Mazi\xe8res, based on the Blowfish cipher, and presented at USENIX in 1999. Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over time, the iteration count (rounds) can be increased to make it slower, so it remains resistant to brute-force search attacks even with increasing computation power.<br><br>Enter the password in the input to generate its hash.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Rounds",
"type": "number",
"value": 10
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
async run(input, args) {
const rounds = args[0];
const salt = await bcrypt.genSalt(rounds);
return await bcrypt.hash(input, salt, null, p => {
// Progress callback
if (ENVIRONMENT_IS_WORKER())
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
});
}
}
export default Bcrypt;

View file

@ -0,0 +1,55 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import bcrypt from "bcryptjs";
/**
* Bcrypt compare operation
*/
class BcryptCompare extends Operation {
/**
* BcryptCompare constructor
*/
constructor() {
super();
this.name = "Bcrypt compare";
this.module = "Hashing";
this.description = "Tests whether the input matches the given bcrypt hash. To test multiple possible passwords, use the 'Fork' operation.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Hash",
"type": "string",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
async run(input, args) {
const hash = args[0];
const match = await bcrypt.compare(input, hash, null, p => {
// Progress callback
if (ENVIRONMENT_IS_WORKER())
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
});
return match ? "Match: " + input : "No match";
}
}
export default BcryptCompare;

View file

@ -0,0 +1,48 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import bcrypt from "bcryptjs";
/**
* Bcrypt parse operation
*/
class BcryptParse extends Operation {
/**
* BcryptParse constructor
*/
constructor() {
super();
this.name = "Bcrypt parse";
this.module = "Hashing";
this.description = "Parses a bcrypt hash to determine the number of rounds used, the salt, and the password hash.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
async run(input, args) {
try {
return `Rounds: ${bcrypt.getRounds(input)}
Salt: ${bcrypt.getSalt(input)}
Password hash: ${input.split(bcrypt.getSalt(input))[1]}
Full hash: ${input}`;
} catch (err) {
throw new OperationError("Error: " + err.toString());
}
}
}
export default BcryptParse;

View file

@ -0,0 +1,124 @@
/**
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import { genPolybiusSquare } from "../lib/Ciphers";
import OperationError from "../errors/OperationError";
/**
* Bifid Cipher Decode operation
*/
class BifidCipherDecode extends Operation {
/**
* BifidCipherDecode constructor
*/
constructor() {
super();
this.name = "Bifid Cipher Decode";
this.module = "Ciphers";
this.description = "The Bifid cipher is a cipher which uses a Polybius square in conjunction with transposition, which can be fairly difficult to decipher without knowing the alphabet keyword.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Keyword",
"type": "string",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if invalid key
*/
run(input, args) {
const keywordStr = args[0].toUpperCase().replace("J", "I"),
keyword = keywordStr.split("").unique(),
alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ",
structure = [];
let output = "",
count = 0,
trans = "";
if (!/^[A-Z]+$/.test(keywordStr) && keyword.length > 0)
throw new OperationError("The key must consist only of letters in the English alphabet");
const polybius = genPolybiusSquare(keywordStr);
input.replace("J", "I").split("").forEach((letter) => {
const alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0;
let polInd;
if (alpInd) {
for (let i = 0; i < 5; i++) {
polInd = polybius[i].indexOf(letter.toLocaleUpperCase());
if (polInd >= 0) {
trans += `${i}${polInd}`;
}
}
if (alpha.split("").indexOf(letter) >= 0) {
structure.push(true);
} else if (alpInd) {
structure.push(false);
}
} else {
structure.push(letter);
}
});
structure.forEach(pos => {
if (typeof pos === "boolean") {
const coords = [trans[count], trans[count+trans.length/2]];
output += pos ?
polybius[coords[0]][coords[1]] :
polybius[coords[0]][coords[1]].toLocaleLowerCase();
count++;
} else {
output += pos;
}
});
return output;
}
/**
* Highlight Bifid Cipher Decode
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Bifid Cipher Decode in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default BifidCipherDecode;

View file

@ -0,0 +1,129 @@
/**
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import { genPolybiusSquare } from "../lib/Ciphers";
/**
* Bifid Cipher Encode operation
*/
class BifidCipherEncode extends Operation {
/**
* BifidCipherEncode constructor
*/
constructor() {
super();
this.name = "Bifid Cipher Encode";
this.module = "Ciphers";
this.description = "The Bifid cipher is a cipher which uses a Polybius square in conjunction with transposition, which can be fairly difficult to decipher without knowing the alphabet keyword.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Keyword",
"type": "string",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if key is invalid
*/
run(input, args) {
const keywordStr = args[0].toUpperCase().replace("J", "I"),
keyword = keywordStr.split("").unique(),
alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ",
xCo = [],
yCo = [],
structure = [];
let output = "",
count = 0;
if (!/^[A-Z]+$/.test(keywordStr) && keyword.length > 0)
throw new OperationError("The key must consist only of letters in the English alphabet");
const polybius = genPolybiusSquare(keywordStr);
input.replace("J", "I").split("").forEach(letter => {
const alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0;
let polInd;
if (alpInd) {
for (let i = 0; i < 5; i++) {
polInd = polybius[i].indexOf(letter.toLocaleUpperCase());
if (polInd >= 0) {
xCo.push(polInd);
yCo.push(i);
}
}
if (alpha.split("").indexOf(letter) >= 0) {
structure.push(true);
} else if (alpInd) {
structure.push(false);
}
} else {
structure.push(letter);
}
});
const trans = `${yCo.join("")}${xCo.join("")}`;
structure.forEach(pos => {
if (typeof pos === "boolean") {
const coords = trans.substr(2*count, 2).split("");
output += pos ?
polybius[coords[0]][coords[1]] :
polybius[coords[0]][coords[1]].toLocaleLowerCase();
count++;
} else {
output += pos;
}
});
return output;
}
/**
* Highlight Bifid Cipher Encode
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Bifid Cipher Encode in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default BifidCipherEncode;

View file

@ -0,0 +1,75 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Bit shift left operation
*/
class BitShiftLeft extends Operation {
/**
* BitShiftLeft constructor
*/
constructor() {
super();
this.name = "Bit shift left";
this.module = "Default";
this.description = "Shifts the bits in each byte towards the left by the specified amount.";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
"name": "Amount",
"type": "number",
"value": 1
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const amount = args[0];
return input.map(b => {
return (b << amount) & 0xff;
});
}
/**
* Highlight Bit shift left
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Bit shift left in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default BitShiftLeft;

View file

@ -0,0 +1,82 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Bit shift right operation
*/
class BitShiftRight extends Operation {
/**
* BitShiftRight constructor
*/
constructor() {
super();
this.name = "Bit shift right";
this.module = "Default";
this.description = "Shifts the bits in each byte towards the right by the specified amount.<br><br><i>Logical shifts</i> replace the leftmost bits with zeros.<br><i>Arithmetic shifts</i> preserve the most significant bit (MSB) of the original byte keeping the sign the same (positive or negative).";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
"name": "Amount",
"type": "number",
"value": 1
},
{
"name": "Type",
"type": "option",
"value": ["Logical shift", "Arithmetic shift"]
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const amount = args[0],
type = args[1],
mask = type === "Logical shift" ? 0 : 0x80;
return input.map(b => {
return (b >>> amount) ^ (b & mask);
});
}
/**
* Highlight Bit shift right
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Bit shift right in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default BitShiftRight;

View file

@ -0,0 +1,55 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import bzip2 from "../vendor/bzip2.js";
import OperationError from "../errors/OperationError";
/**
* Bzip2 Decompress operation
*/
class Bzip2Decompress extends Operation {
/**
* Bzip2Decompress constructor
*/
constructor() {
super();
this.name = "Bzip2 Decompress";
this.module = "Compression";
this.description = "Decompresses data using the Bzip2 algorithm.";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [];
this.patterns = [
{
"match": "^\\x42\\x5a\\x68",
"flags": "",
"args": []
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const compressed = new Uint8Array(input);
try {
const bzip2Reader = bzip2.array(compressed);
return bzip2.simple(bzip2Reader);
} catch (err) {
throw new OperationError(err);
}
}
}
export default Bzip2Decompress;

View file

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import JSCRC from "js-crc";
/**
* CRC-16 Checksum operation
*/
class CRC16Checksum extends Operation {
/**
* CRC16Checksum constructor
*/
constructor() {
super();
this.name = "CRC-16 Checksum";
this.module = "Hashing";
this.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.";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return JSCRC.crc16(input);
}
}
export default CRC16Checksum;

View file

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import JSCRC from "js-crc";
/**
* CRC-32 Checksum operation
*/
class CRC32Checksum extends Operation {
/**
* CRC32Checksum constructor
*/
constructor() {
super();
this.name = "CRC-32 Checksum";
this.module = "Hashing";
this.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.";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return JSCRC.crc32(input);
}
}
export default CRC32Checksum;

View file

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import ctphjs from "ctph.js";
/**
* CTPH operation
*/
class CTPH extends Operation {
/**
* CTPH constructor
*/
constructor() {
super();
this.name = "CTPH";
this.module = "Hashing";
this.description = "Context Triggered Piecewise Hashing, also called Fuzzy Hashing, can match inputs that have homologies. Such inputs have sequences of identical bytes in the same order, although bytes in between these sequences may be different in both content and length.<br><br>CTPH was originally based on the work of Dr. Andrew Tridgell and a spam email detector called SpamSum. This method was adapted by Jesse Kornblum and published at the DFRWS conference in 2006 in a paper 'Identifying Almost Identical Files Using Context Triggered Piecewise Hashing'.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return ctphjs.digest(input);
}
}
export default CTPH;

View file

@ -41,7 +41,7 @@ class CartesianProduct extends Operation {
* Validate input length
*
* @param {Object[]} sets
* @throws {Error} if fewer than 2 sets
* @throws {OperationError} if fewer than 2 sets
*/
validateSampleNumbers(sets) {
if (!sets || sets.length < 2) {

View file

@ -0,0 +1,53 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Chi Square operation
*/
class ChiSquare extends Operation {
/**
* ChiSquare constructor
*/
constructor() {
super();
this.name = "Chi Square";
this.module = "Default";
this.description = "Calculates the Chi Square distribution of values.";
this.inputType = "ArrayBuffer";
this.outputType = "number";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {number}
*/
run(input, args) {
const data = new Uint8Array(input);
const distArray = new Array(256).fill(0);
let total = 0;
for (let i = 0; i < data.length; i++) {
distArray[data[i]]++;
}
for (let i = 0; i < distArray.length; i++) {
if (distArray[i] > 0) {
total += Math.pow(distArray[i] - data.length / 256, 2) / (data.length / 256);
}
}
return total;
}
}
export default ChiSquare;

View file

@ -0,0 +1,48 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Comment operation
*/
class Comment extends Operation {
/**
* Comment constructor
*/
constructor() {
super();
this.name = "Comment";
this.flowControl = true;
this.module = "Default";
this.description = "Provides a place to write comments within the flow of the recipe. This operation has no computational effect.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "",
"type": "text",
"value": ""
}
];
}
/**
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @returns {Object} The updated state of the recipe.
*/
run(state) {
return state;
}
}
export default Comment;

View file

@ -0,0 +1,51 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {HASH_DELIM_OPTIONS} from "../lib/Delim";
import ctphjs from "ctph.js";
import OperationError from "../errors/OperationError";
/**
* Compare CTPH hashes operation
*/
class CompareCTPHHashes extends Operation {
/**
* CompareCTPHHashes constructor
*/
constructor() {
super();
this.name = "Compare CTPH hashes";
this.module = "Hashing";
this.description = "Compares two Context Triggered Piecewise Hashing (CTPH) fuzzy hashes to determine the similarity between them on a scale of 0 to 100.";
this.inputType = "string";
this.outputType = "Number";
this.args = [
{
"name": "Delimiter",
"type": "option",
"value": HASH_DELIM_OPTIONS
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {Number}
*/
run(input, args) {
const samples = input.split(Utils.charRep(args[0]));
if (samples.length !== 2) throw new OperationError("Incorrect number of samples.");
return ctphjs.similarity(samples[0], samples[1]);
}
}
export default CompareCTPHHashes;

View file

@ -0,0 +1,51 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {HASH_DELIM_OPTIONS} from "../lib/Delim";
import ssdeepjs from "ssdeep.js";
import OperationError from "../errors/OperationError";
/**
* Compare SSDEEP hashes operation
*/
class CompareSSDEEPHashes extends Operation {
/**
* CompareSSDEEPHashes constructor
*/
constructor() {
super();
this.name = "Compare SSDEEP hashes";
this.module = "Hashing";
this.description = "Compares two SSDEEP fuzzy hashes to determine the similarity between them on a scale of 0 to 100.";
this.inputType = "string";
this.outputType = "Number";
this.args = [
{
"name": "Delimiter",
"type": "option",
"value": HASH_DELIM_OPTIONS
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {Number}
*/
run(input, args) {
const samples = input.split(Utils.charRep(args[0]));
if (samples.length !== 2) throw new OperationError("Incorrect number of samples.");
return ssdeepjs.similarity(samples[0], samples[1]);
}
}
export default CompareSSDEEPHashes;

View file

@ -0,0 +1,84 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import Dish from "../Dish";
import { getLabelIndex } from "../lib/FlowControl";
/**
* Conditional Jump operation
*/
class ConditionalJump extends Operation {
/**
* ConditionalJump constructor
*/
constructor() {
super();
this.name = "Conditional Jump";
this.flowControl = true;
this.module = "Default";
this.description = "Conditionally jump forwards or backwards to the specified Label based on whether the data matches the specified regular expression.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Match (regex)",
"type": "string",
"value": ""
},
{
"name": "Invert match",
"type": "boolean",
"value": false
},
{
"name": "Label name",
"type": "shortString",
"value": ""
},
{
"name": "Maximum jumps (if jumping backwards)",
"type": "number",
"value": 10
}
];
}
/**
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @param {number} state.numJumps - The number of jumps taken so far.
* @returns {Object} The updated state of the recipe.
*/
async run(state) {
const ings = state.opList[state.progress].ingValues,
dish = state.dish,
[regexStr, invert, label, maxJumps] = ings,
jmpIndex = getLabelIndex(label, state);
if (state.numJumps >= maxJumps || jmpIndex === -1) {
return state;
}
if (regexStr !== "") {
const str = await dish.get(Dish.STRING);
const strMatch = str.search(regexStr) > -1;
if (!invert && strMatch || invert && !strMatch) {
state.progress = jmpIndex;
state.numJumps++;
}
}
return state;
}
}
export default ConditionalJump;

View file

@ -0,0 +1,112 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Convert area operation
*/
class ConvertArea extends Operation {
/**
* ConvertArea constructor
*/
constructor() {
super();
this.name = "Convert area";
this.module = "Default";
this.description = "Converts a unit of area to another format.";
this.inputType = "BigNumber";
this.outputType = "BigNumber";
this.args = [
{
"name": "Input units",
"type": "option",
"value": AREA_UNITS
},
{
"name": "Output units",
"type": "option",
"value": AREA_UNITS
}
];
}
/**
* @param {BigNumber} input
* @param {Object[]} args
* @returns {BigNumber}
*/
run(input, args) {
const [inputUnits, outputUnits] = args;
input = input.times(AREA_FACTOR[inputUnits]);
return input.div(AREA_FACTOR[outputUnits]);
}
}
const AREA_UNITS = [
"[Metric]", "Square metre (sq m)", "Square kilometre (sq km)", "Centiare (ca)", "Deciare (da)", "Are (a)", "Decare (daa)", "Hectare (ha)", "[/Metric]",
"[Imperial]", "Square inch (sq in)", "Square foot (sq ft)", "Square yard (sq yd)", "Square mile (sq mi)", "Perch (sq per)", "Rood (ro)", "International acre (ac)", "[/Imperial]",
"[US customary units]", "US survey acre (ac)", "US survey square mile (sq mi)", "US survey township", "[/US customary units]",
"[Nuclear physics]", "Yoctobarn (yb)", "Zeptobarn (zb)", "Attobarn (ab)", "Femtobarn (fb)", "Picobarn (pb)", "Nanobarn (nb)", "Microbarn (μb)", "Millibarn (mb)", "Barn (b)", "Kilobarn (kb)", "Megabarn (Mb)", "Outhouse", "Shed", "Planck area", "[/Nuclear physics]",
"[Comparisons]", "Washington D.C.", "Isle of Wight", "Wales", "Texas", "[/Comparisons]",
];
const AREA_FACTOR = { // Multiples of a square metre
// Metric
"Square metre (sq m)": 1,
"Square kilometre (sq km)": 1e6,
"Centiare (ca)": 1,
"Deciare (da)": 10,
"Are (a)": 100,
"Decare (daa)": 1e3,
"Hectare (ha)": 1e4,
// Imperial
"Square inch (sq in)": 0.00064516,
"Square foot (sq ft)": 0.09290304,
"Square yard (sq yd)": 0.83612736,
"Square mile (sq mi)": 2589988.110336,
"Perch (sq per)": 42.21,
"Rood (ro)": 1011,
"International acre (ac)": 4046.8564224,
// US customary units
"US survey acre (ac)": 4046.87261,
"US survey square mile (sq mi)": 2589998.470305239,
"US survey township": 93239944.9309886,
// Nuclear physics
"Yoctobarn (yb)": 1e-52,
"Zeptobarn (zb)": 1e-49,
"Attobarn (ab)": 1e-46,
"Femtobarn (fb)": 1e-43,
"Picobarn (pb)": 1e-40,
"Nanobarn (nb)": 1e-37,
"Microbarn (μb)": 1e-34,
"Millibarn (mb)": 1e-31,
"Barn (b)": 1e-28,
"Kilobarn (kb)": 1e-25,
"Megabarn (Mb)": 1e-22,
"Planck area": 2.6e-70,
"Shed": 1e-52,
"Outhouse": 1e-34,
// Comparisons
"Washington D.C.": 176119191.502848,
"Isle of Wight": 380000000,
"Wales": 20779000000,
"Texas": 696241000000,
};
export default ConvertArea;

View file

@ -0,0 +1,111 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Convert data units operation
*/
class ConvertDataUnits extends Operation {
/**
* ConvertDataUnits constructor
*/
constructor() {
super();
this.name = "Convert data units";
this.module = "Default";
this.description = "Converts a unit of data to another format.";
this.inputType = "BigNumber";
this.outputType = "BigNumber";
this.args = [
{
"name": "Input units",
"type": "option",
"value": DATA_UNITS
},
{
"name": "Output units",
"type": "option",
"value": DATA_UNITS
}
];
}
/**
* @param {BigNumber} input
* @param {Object[]} args
* @returns {BigNumber}
*/
run(input, args) {
const [inputUnits, outputUnits] = args;
input = input.times(DATA_FACTOR[inputUnits]);
return input.div(DATA_FACTOR[outputUnits]);
}
}
const DATA_UNITS = [
"Bits (b)", "Nibbles", "Octets", "Bytes (B)",
"[Binary bits (2^n)]", "Kibibits (Kib)", "Mebibits (Mib)", "Gibibits (Gib)", "Tebibits (Tib)", "Pebibits (Pib)", "Exbibits (Eib)", "Zebibits (Zib)", "Yobibits (Yib)", "[/Binary bits (2^n)]",
"[Decimal bits (10^n)]", "Decabits", "Hectobits", "Kilobits (kb)", "Megabits (Mb)", "Gigabits (Gb)", "Terabits (Tb)", "Petabits (Pb)", "Exabits (Eb)", "Zettabits (Zb)", "Yottabits (Yb)", "[/Decimal bits (10^n)]",
"[Binary bytes (8 x 2^n)]", "Kibibytes (KiB)", "Mebibytes (MiB)", "Gibibytes (GiB)", "Tebibytes (TiB)", "Pebibytes (PiB)", "Exbibytes (EiB)", "Zebibytes (ZiB)", "Yobibytes (YiB)", "[/Binary bytes (8 x 2^n)]",
"[Decimal bytes (8 x 10^n)]", "Kilobytes (KB)", "Megabytes (MB)", "Gigabytes (GB)", "Terabytes (TB)", "Petabytes (PB)", "Exabytes (EB)", "Zettabytes (ZB)", "Yottabytes (YB)", "[/Decimal bytes (8 x 10^n)]"
];
const DATA_FACTOR = { // Multiples of a bit
"Bits (b)": 1,
"Nibbles": 4,
"Octets": 8,
"Bytes (B)": 8,
// Binary bits (2^n)
"Kibibits (Kib)": 1024,
"Mebibits (Mib)": 1048576,
"Gibibits (Gib)": 1073741824,
"Tebibits (Tib)": 1099511627776,
"Pebibits (Pib)": 1125899906842624,
"Exbibits (Eib)": 1152921504606846976,
"Zebibits (Zib)": 1180591620717411303424,
"Yobibits (Yib)": 1208925819614629174706176,
// Decimal bits (10^n)
"Decabits": 10,
"Hectobits": 100,
"Kilobits (Kb)": 1e3,
"Megabits (Mb)": 1e6,
"Gigabits (Gb)": 1e9,
"Terabits (Tb)": 1e12,
"Petabits (Pb)": 1e15,
"Exabits (Eb)": 1e18,
"Zettabits (Zb)": 1e21,
"Yottabits (Yb)": 1e24,
// Binary bytes (8 x 2^n)
"Kibibytes (KiB)": 8192,
"Mebibytes (MiB)": 8388608,
"Gibibytes (GiB)": 8589934592,
"Tebibytes (TiB)": 8796093022208,
"Pebibytes (PiB)": 9007199254740992,
"Exbibytes (EiB)": 9223372036854775808,
"Zebibytes (ZiB)": 9444732965739290427392,
"Yobibytes (YiB)": 9671406556917033397649408,
// Decimal bytes (8 x 10^n)
"Kilobytes (KB)": 8e3,
"Megabytes (MB)": 8e6,
"Gigabytes (GB)": 8e9,
"Terabytes (TB)": 8e12,
"Petabytes (PB)": 8e15,
"Exabytes (EB)": 8e18,
"Zettabytes (ZB)": 8e21,
"Yottabytes (YB)": 8e24,
};
export default ConvertDataUnits;

View file

@ -0,0 +1,95 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Convert distance operation
*/
class ConvertDistance extends Operation {
/**
* ConvertDistance constructor
*/
constructor() {
super();
this.name = "Convert distance";
this.module = "Default";
this.description = "Converts a unit of distance to another format.";
this.inputType = "BigNumber";
this.outputType = "BigNumber";
this.args = [
{
"name": "Input units",
"type": "option",
"value": DISTANCE_UNITS
},
{
"name": "Output units",
"type": "option",
"value": DISTANCE_UNITS
}
];
}
/**
* @param {BigNumber} input
* @param {Object[]} args
* @returns {BigNumber}
*/
run(input, args) {
const [inputUnits, outputUnits] = args;
input = input.times(DISTANCE_FACTOR[inputUnits]);
return input.div(DISTANCE_FACTOR[outputUnits]);
}
}
const DISTANCE_UNITS = [
"[Metric]", "Nanometres (nm)", "Micrometres (µm)", "Millimetres (mm)", "Centimetres (cm)", "Metres (m)", "Kilometers (km)", "[/Metric]",
"[Imperial]", "Thou (th)", "Inches (in)", "Feet (ft)", "Yards (yd)", "Chains (ch)", "Furlongs (fur)", "Miles (mi)", "Leagues (lea)", "[/Imperial]",
"[Maritime]", "Fathoms (ftm)", "Cables", "Nautical miles", "[/Maritime]",
"[Comparisons]", "Cars (4m)", "Buses (8.4m)", "American football fields (91m)", "Football pitches (105m)", "[/Comparisons]",
"[Astronomical]", "Earth-to-Moons", "Earth's equators", "Astronomical units (au)", "Light-years (ly)", "Parsecs (pc)", "[/Astronomical]",
];
const DISTANCE_FACTOR = { // Multiples of a metre
"Nanometres (nm)": 1e-9,
"Micrometres (µm)": 1e-6,
"Millimetres (mm)": 1e-3,
"Centimetres (cm)": 1e-2,
"Metres (m)": 1,
"Kilometers (km)": 1e3,
"Thou (th)": 0.0000254,
"Inches (in)": 0.0254,
"Feet (ft)": 0.3048,
"Yards (yd)": 0.9144,
"Chains (ch)": 20.1168,
"Furlongs (fur)": 201.168,
"Miles (mi)": 1609.344,
"Leagues (lea)": 4828.032,
"Fathoms (ftm)": 1.853184,
"Cables": 185.3184,
"Nautical miles": 1853.184,
"Cars (4m)": 4,
"Buses (8.4m)": 8.4,
"American football fields (91m)": 91,
"Football pitches (105m)": 105,
"Earth-to-Moons": 380000000,
"Earth's equators": 40075016.686,
"Astronomical units (au)": 149597870700,
"Light-years (ly)": 9460730472580800,
"Parsecs (pc)": 3.0856776e16
};
export default ConvertDistance;

View file

@ -0,0 +1,143 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Convert mass operation
*/
class ConvertMass extends Operation {
/**
* ConvertMass constructor
*/
constructor() {
super();
this.name = "Convert mass";
this.module = "Default";
this.description = "Converts a unit of mass to another format.";
this.inputType = "BigNumber";
this.outputType = "BigNumber";
this.args = [
{
"name": "Input units",
"type": "option",
"value": MASS_UNITS
},
{
"name": "Output units",
"type": "option",
"value": MASS_UNITS
}
];
}
/**
* @param {BigNumber} input
* @param {Object[]} args
* @returns {BigNumber}
*/
run(input, args) {
const [inputUnits, outputUnits] = args;
input = input.times(MASS_FACTOR[inputUnits]);
return input.div(MASS_FACTOR[outputUnits]);
}
}
const MASS_UNITS = [
"[Metric]", "Yoctogram (yg)", "Zeptogram (zg)", "Attogram (ag)", "Femtogram (fg)", "Picogram (pg)", "Nanogram (ng)", "Microgram (μg)", "Milligram (mg)", "Centigram (cg)", "Decigram (dg)", "Gram (g)", "Decagram (dag)", "Hectogram (hg)", "Kilogram (kg)", "Megagram (Mg)", "Tonne (t)", "Gigagram (Gg)", "Teragram (Tg)", "Petagram (Pg)", "Exagram (Eg)", "Zettagram (Zg)", "Yottagram (Yg)", "[/Metric]",
"[Imperial Avoirdupois]", "Grain (gr)", "Dram (dr)", "Ounce (oz)", "Pound (lb)", "Nail", "Stone (st)", "Quarter (gr)", "Tod", "US hundredweight (cwt)", "Imperial hundredweight (cwt)", "US ton (t)", "Imperial ton (t)", "[/Imperial Avoirdupois]",
"[Imperial Troy]", "Grain (gr)", "Pennyweight (dwt)", "Troy dram (dr t)", "Troy ounce (oz t)", "Troy pound (lb t)", "Mark", "[/Imperial Troy]",
"[Archaic]", "Wey", "Wool wey", "Suffolk wey", "Wool sack", "Coal sack", "Load", "Last", "Flax or feather last", "Gunpowder last", "Picul", "Rice last", "[/Archaic]",
"[Comparisons]", "Big Ben (14 tonnes)", "Blue whale (180 tonnes)", "International Space Station (417 tonnes)", "Space Shuttle (2,041 tonnes)", "RMS Titanic (52,000 tonnes)", "Great Pyramid of Giza (6,000,000 tonnes)", "Earth's oceans (1.4 yottagrams)", "[/Comparisons]",
"[Astronomical]", "A teaspoon of neutron star (5,500 million tonnes)", "Lunar mass (ML)", "Earth mass (M⊕)", "Jupiter mass (MJ)", "Solar mass (M☉)", "Sagittarius A* (7.5 x 10^36 kgs-ish)", "Milky Way galaxy (1.2 x 10^42 kgs)", "The observable universe (1.45 x 10^53 kgs)", "[/Astronomical]",
];
const MASS_FACTOR = { // Multiples of a gram
// Metric
"Yoctogram (yg)": 1e-24,
"Zeptogram (zg)": 1e-21,
"Attogram (ag)": 1e-18,
"Femtogram (fg)": 1e-15,
"Picogram (pg)": 1e-12,
"Nanogram (ng)": 1e-9,
"Microgram (μg)": 1e-6,
"Milligram (mg)": 1e-3,
"Centigram (cg)": 1e-2,
"Decigram (dg)": 1e-1,
"Gram (g)": 1,
"Decagram (dag)": 10,
"Hectogram (hg)": 100,
"Kilogram (kg)": 1000,
"Megagram (Mg)": 1e6,
"Tonne (t)": 1e6,
"Gigagram (Gg)": 1e9,
"Teragram (Tg)": 1e12,
"Petagram (Pg)": 1e15,
"Exagram (Eg)": 1e18,
"Zettagram (Zg)": 1e21,
"Yottagram (Yg)": 1e24,
// Imperial Avoirdupois
"Grain (gr)": 64.79891e-3,
"Dram (dr)": 1.7718451953125,
"Ounce (oz)": 28.349523125,
"Pound (lb)": 453.59237,
"Nail": 3175.14659,
"Stone (st)": 6.35029318e3,
"Quarter (gr)": 12700.58636,
"Tod": 12700.58636,
"US hundredweight (cwt)": 45.359237e3,
"Imperial hundredweight (cwt)": 50.80234544e3,
"US ton (t)": 907.18474e3,
"Imperial ton (t)": 1016.0469088e3,
// Imperial Troy
"Pennyweight (dwt)": 1.55517384,
"Troy dram (dr t)": 3.8879346,
"Troy ounce (oz t)": 31.1034768,
"Troy pound (lb t)": 373.2417216,
"Mark": 248.8278144,
// Archaic
"Wey": 76.5e3,
"Wool wey": 101.7e3,
"Suffolk wey": 161.5e3,
"Wool sack": 153000,
"Coal sack": 50.80234544e3,
"Load": 918000,
"Last": 1836000,
"Flax or feather last": 770e3,
"Gunpowder last": 1090e3,
"Picul": 60.478982e3,
"Rice last": 1200e3,
// Comparisons
"Big Ben (14 tonnes)": 14e6,
"Blue whale (180 tonnes)": 180e6,
"International Space Station (417 tonnes)": 417e6,
"Space Shuttle (2,041 tonnes)": 2041e6,
"RMS Titanic (52,000 tonnes)": 52000e6,
"Great Pyramid of Giza (6,000,000 tonnes)": 6e12,
"Earth's oceans (1.4 yottagrams)": 1.4e24,
// Astronomical
"A teaspoon of neutron star (5,500 million tonnes)": 5.5e15,
"Lunar mass (ML)": 7.342e25,
"Earth mass (M⊕)": 5.97219e27,
"Jupiter mass (MJ)": 1.8981411476999997e30,
"Solar mass (M☉)": 1.98855e33,
"Sagittarius A* (7.5 x 10^36 kgs-ish)": 7.5e39,
"Milky Way galaxy (1.2 x 10^42 kgs)": 1.2e45,
"The observable universe (1.45 x 10^53 kgs)": 1.45e56,
};
export default ConvertMass;

View file

@ -0,0 +1,96 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Convert speed operation
*/
class ConvertSpeed extends Operation {
/**
* ConvertSpeed constructor
*/
constructor() {
super();
this.name = "Convert speed";
this.module = "Default";
this.description = "Converts a unit of speed to another format.";
this.inputType = "BigNumber";
this.outputType = "BigNumber";
this.args = [
{
"name": "Input units",
"type": "option",
"value": SPEED_UNITS
},
{
"name": "Output units",
"type": "option",
"value": SPEED_UNITS
}
];
}
/**
* @param {BigNumber} input
* @param {Object[]} args
* @returns {BigNumber}
*/
run(input, args) {
const [inputUnits, outputUnits] = args;
input = input.times(SPEED_FACTOR[inputUnits]);
return input.div(SPEED_FACTOR[outputUnits]);
}
}
const SPEED_UNITS = [
"[Metric]", "Metres per second (m/s)", "Kilometres per hour (km/h)", "[/Metric]",
"[Imperial]", "Miles per hour (mph)", "Knots (kn)", "[/Imperial]",
"[Comparisons]", "Human hair growth rate", "Bamboo growth rate", "World's fastest snail", "Usain Bolt's top speed", "Jet airliner cruising speed", "Concorde", "SR-71 Blackbird", "Space Shuttle", "International Space Station", "[/Comparisons]",
"[Scientific]", "Sound in standard atmosphere", "Sound in water", "Lunar escape velocity", "Earth escape velocity", "Earth's solar orbit", "Solar system's Milky Way orbit", "Milky Way relative to the cosmic microwave background", "Solar escape velocity", "Neutron star escape velocity (0.3c)", "Light in a diamond (0.4136c)", "Signal in an optical fibre (0.667c)", "Light (c)", "[/Scientific]",
];
const SPEED_FACTOR = { // Multiples of m/s
// Metric
"Metres per second (m/s)": 1,
"Kilometres per hour (km/h)": 0.2778,
// Imperial
"Miles per hour (mph)": 0.44704,
"Knots (kn)": 0.5144,
// Comparisons
"Human hair growth rate": 4.8e-9,
"Bamboo growth rate": 1.4e-5,
"World's fastest snail": 0.00275,
"Usain Bolt's top speed": 12.42,
"Jet airliner cruising speed": 250,
"Concorde": 603,
"SR-71 Blackbird": 981,
"Space Shuttle": 1400,
"International Space Station": 7700,
// Scientific
"Sound in standard atmosphere": 340.3,
"Sound in water": 1500,
"Lunar escape velocity": 2375,
"Earth escape velocity": 11200,
"Earth's solar orbit": 29800,
"Solar system's Milky Way orbit": 200000,
"Milky Way relative to the cosmic microwave background": 552000,
"Solar escape velocity": 617700,
"Neutron star escape velocity (0.3c)": 100000000,
"Light in a diamond (0.4136c)": 124000000,
"Signal in an optical fibre (0.667c)": 200000000,
"Light (c)": 299792458,
};
export default ConvertSpeed;

View file

@ -0,0 +1,65 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Count occurrences operation
*/
class CountOccurrences extends Operation {
/**
* CountOccurrences constructor
*/
constructor() {
super();
this.name = "Count occurrences";
this.module = "Default";
this.description = "Counts the number of times the provided string occurs in the input.";
this.inputType = "string";
this.outputType = "number";
this.args = [
{
"name": "Search string",
"type": "toggleString",
"value": "",
"toggleValues": ["Regex", "Extended (\\n, \\t, \\x...)", "Simple string"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {number}
*/
run(input, args) {
let search = args[0].string;
const type = args[0].option;
if (type === "Regex" && search) {
try {
const regex = new RegExp(search, "gi"),
matches = input.match(regex);
return matches.length;
} catch (err) {
return 0;
}
} else if (search) {
if (type.indexOf("Extended") === 0) {
search = Utils.parseEscapedChars(search);
}
return input.count(search);
} else {
return 0;
}
}
}
export default CountOccurrences;

View file

@ -0,0 +1,59 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Decode NetBIOS Name operation
*/
class DecodeNetBIOSName extends Operation {
/**
* DecodeNetBIOSName constructor
*/
constructor() {
super();
this.name = "Decode NetBIOS Name";
this.module = "Default";
this.description = "NetBIOS names as seen across the client interface to NetBIOS are exactly 16 bytes long. Within the NetBIOS-over-TCP protocols, a longer representation is used.<br><br>There are two levels of encoding. The first level maps a NetBIOS name into a domain system name. The second level maps the domain system name into the 'compressed' representation required for interaction with the domain name system.<br><br>This operation decodes the first level of encoding. See RFC 1001 for full details.";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
"name": "Offset",
"type": "number",
"value": 65
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const output = [],
offset = args[0];
if (input.length <= 32 && (input.length % 2) === 0) {
for (let i = 0; i < input.length; i += 2) {
output.push((((input[i] & 0xff) - offset) << 4) |
(((input[i + 1] & 0xff) - offset) & 0xf));
}
for (let i = output.length - 1; i > 0; i--) {
if (output[i] === 32) output.splice(i, i);
else break;
}
}
return output;
}
}
export default DecodeNetBIOSName;

View file

@ -0,0 +1,55 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import cptable from "../vendor/js-codepage/cptable.js";
import {IO_FORMAT} from "../lib/ChrEnc";
/**
* Decode text operation
*/
class DecodeText extends Operation {
/**
* DecodeText constructor
*/
constructor() {
super();
this.name = "Decode text";
this.module = "CharEnc";
this.description = [
"Decodes text from the chosen character encoding.",
"<br><br>",
"Supported charsets are:",
"<ul>",
Object.keys(IO_FORMAT).map(e => `<li>${e}</li>`).join("\n"),
"</ul>",
].join("\n");
this.inputType = "byteArray";
this.outputType = "string";
this.args = [
{
"name": "Encoding",
"type": "option",
"value": Object.keys(IO_FORMAT)
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const format = IO_FORMAT[args[0]];
return cptable.utils.decode(format, input);
}
}
export default DecodeText;

View file

@ -0,0 +1,144 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import CryptoJS from "crypto-js";
/**
* Derive EVP key operation
*/
class DeriveEVPKey extends Operation {
/**
* DeriveEVPKey constructor
*/
constructor() {
super();
this.name = "Derive EVP key";
this.module = "Ciphers";
this.description = "EVP is a password-based key derivation function (PBKDF) used extensively in OpenSSL. In many applications of cryptography, user security is ultimately dependent on a password, and because a password usually can't be used directly as a cryptographic key, some processing is required.<br><br>A salt provides a large set of keys for any given password, and an iteration count increases the cost of producing keys from a password, thereby also increasing the difficulty of attack.<br><br>If you leave the salt argument empty, a random salt will be generated.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Passphrase",
"type": "toggleString",
"value": "",
"toggleValues": ["UTF8", "Latin1", "Hex", "Base64"]
},
{
"name": "Key size",
"type": "number",
"value": 128
},
{
"name": "Iterations",
"type": "number",
"value": 1
},
{
"name": "Hashing function",
"type": "option",
"value": ["SHA1", "SHA256", "SHA384", "SHA512", "MD5"]
},
{
"name": "Salt",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const passphrase = Utils.convertToByteString(args[0].string, args[0].option),
keySize = args[1] / 32,
iterations = args[2],
hasher = args[3],
salt = Utils.convertToByteString(args[4].string, args[4].option),
key = CryptoJS.EvpKDF(passphrase, salt, {
keySize: keySize,
hasher: CryptoJS.algo[hasher],
iterations: iterations,
});
return key.toString(CryptoJS.enc.Hex);
}
}
export default DeriveEVPKey;
/**
* Overwriting the CryptoJS OpenSSL key derivation function so that it is possible to not pass a
* salt in.
* @param {string} password - The password to derive from.
* @param {number} keySize - The size in words of the key to generate.
* @param {number} ivSize - The size in words of the IV to generate.
* @param {WordArray|string} salt (Optional) A 64-bit salt to use. If omitted, a salt will be
* generated randomly. If set to false, no salt will be added.
*
* @returns {CipherParams} A cipher params object with the key, IV, and salt.
*
* @static
*
* @example
* // Randomly generates a salt
* var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32);
* // Uses the salt 'saltsalt'
* var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt');
* // Does not use a salt
* var derivedParams = CryptoJS.kdf.OpenSSL.execute('Password', 256/32, 128/32, false);
*/
CryptoJS.kdf.OpenSSL.execute = function (password, keySize, ivSize, salt) {
// Generate random salt if no salt specified and not set to false
// This line changed from `if (!salt) {` to the following
if (salt === undefined || salt === null) {
salt = CryptoJS.lib.WordArray.random(64/8);
}
// Derive key and IV
const key = CryptoJS.algo.EvpKDF.create({ keySize: keySize + ivSize }).compute(password, salt);
// Separate key and IV
const iv = CryptoJS.lib.WordArray.create(key.words.slice(keySize), ivSize * 4);
key.sigBytes = keySize * 4;
// Return params
return CryptoJS.lib.CipherParams.create({ key: key, iv: iv, salt: salt });
};
/**
* Override for the CryptoJS Hex encoding parser to remove whitespace before attempting to parse
* the hex string.
*
* @param {string} hexStr
* @returns {CryptoJS.lib.WordArray}
*/
CryptoJS.enc.Hex.parse = function (hexStr) {
// Remove whitespace
hexStr = hexStr.replace(/\s/g, "");
// Shortcut
const hexStrLength = hexStr.length;
// Convert
const words = [];
for (let i = 0; i < hexStrLength; i += 2) {
words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4);
}
return new CryptoJS.lib.WordArray.init(words, hexStrLength / 2);
};

View file

@ -0,0 +1,54 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Magic from "../lib/Magic";
/**
* Detect File Type operation
*/
class DetectFileType extends Operation {
/**
* DetectFileType constructor
*/
constructor() {
super();
this.name = "Detect File Type";
this.module = "Default";
this.description = "Attempts to guess the MIME (Multipurpose Internet Mail Extensions) type of the data based on 'magic bytes'.<br><br>Currently supports the following file types: 7z, amr, avi, bmp, bz2, class, cr2, crx, dex, dmg, doc, elf, eot, epub, exe, flac, flv, gif, gz, ico, iso, jpg, jxr, m4a, m4v, mid, mkv, mov, mp3, mp4, mpg, ogg, otf, pdf, png, ppt, ps, psd, rar, rtf, sqlite, swf, tar, tar.z, tif, ttf, utf8, vmdk, wav, webm, webp, wmv, woff, woff2, xls, xz, zip.";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const data = new Uint8Array(input),
type = Magic.magicFileType(data);
if (!type) {
return "Unknown file type. Have you tried checking the entropy of this data to determine whether it might be encrypted or compressed?";
} else {
let output = "File extension: " + type.ext + "\n" +
"MIME type: " + type.mime;
if (type.desc && type.desc.length) {
output += "\nDescription: " + type.desc;
}
return output;
}
}
}
export default DetectFileType;

View file

@ -0,0 +1,124 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import * as JsDiff from "diff";
import OperationError from "../errors/OperationError";
/**
* Diff operation
*/
class Diff extends Operation {
/**
* Diff constructor
*/
constructor() {
super();
this.name = "Diff";
this.module = "Diff";
this.description = "Compares two inputs (separated by the specified delimiter) and highlights the differences between them.";
this.inputType = "string";
this.outputType = "html";
this.args = [
{
"name": "Sample delimiter",
"type": "binaryString",
"value": "\\n\\n"
},
{
"name": "Diff by",
"type": "option",
"value": ["Character", "Word", "Line", "Sentence", "CSS", "JSON"]
},
{
"name": "Show added",
"type": "boolean",
"value": true
},
{
"name": "Show removed",
"type": "boolean",
"value": true
},
{
"name": "Ignore whitespace (relevant for word and line)",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
run(input, args) {
const [
sampleDelim,
diffBy,
showAdded,
showRemoved,
ignoreWhitespace
] = args,
samples = input.split(sampleDelim);
let output = "",
diff;
if (!samples || samples.length !== 2) {
throw new OperationError("Incorrect number of samples, perhaps you need to modify the sample delimiter or add more samples?");
}
switch (diffBy) {
case "Character":
diff = JsDiff.diffChars(samples[0], samples[1]);
break;
case "Word":
if (ignoreWhitespace) {
diff = JsDiff.diffWords(samples[0], samples[1]);
} else {
diff = JsDiff.diffWordsWithSpace(samples[0], samples[1]);
}
break;
case "Line":
if (ignoreWhitespace) {
diff = JsDiff.diffTrimmedLines(samples[0], samples[1]);
} else {
diff = JsDiff.diffLines(samples[0], samples[1]);
}
break;
case "Sentence":
diff = JsDiff.diffSentences(samples[0], samples[1]);
break;
case "CSS":
diff = JsDiff.diffCss(samples[0], samples[1]);
break;
case "JSON":
diff = JsDiff.diffJson(samples[0], samples[1]);
break;
default:
throw new OperationError("Invalid 'Diff by' option.");
}
for (let i = 0; i < diff.length; i++) {
if (diff[i].added) {
if (showAdded) output += "<span class='hl5'>" + Utils.escapeHtml(diff[i].value) + "</span>";
} else if (diff[i].removed) {
if (showRemoved) output += "<span class='hl3'>" + Utils.escapeHtml(diff[i].value) + "</span>";
} else {
output += Utils.escapeHtml(diff[i].value);
}
}
return output;
}
}
export default Diff;

View file

@ -0,0 +1,133 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
import * as disassemble from "../vendor/DisassembleX86-64";
import OperationError from "../errors/OperationError";
/**
* 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}
*
* @throws {OperationError} if invalid mode value
*/
run(input, args) {
const [
mode,
compatibility,
codeSegment,
offset,
showInstructionHex,
showInstructionPos
] = args;
switch (mode) {
case "64":
disassemble.setBitMode(2);
break;
case "32":
disassemble.setBitMode(1);
break;
case "16":
disassemble.setBitMode(0);
break;
default:
throw new OperationError("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,51 @@
/**
* @author bwhitn [brian.m.whitney@outlook.com]
* @author d98762625 [d98762625@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import BigNumber from "bignumber.js";
import Operation from "../Operation";
import { div, createNumArray } from "../lib/Arithmetic";
import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim";
/**
* Divide operation
*/
class Divide extends Operation {
/**
* Divide constructor
*/
constructor() {
super();
this.name = "Divide";
this.module = "Default";
this.description = "Divides a list of numbers. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5</code> becomes <code>2.5</code>";
this.inputType = "string";
this.outputType = "BigNumber";
this.args = [
{
"name": "Delimiter",
"type": "option",
"value": ARITHMETIC_DELIM_OPTIONS,
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
run(input, args) {
const val = div(createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
}
}
export default Divide;

View file

@ -0,0 +1,95 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
/**
* Drop bytes operation
*/
class DropBytes extends Operation {
/**
* DropBytes constructor
*/
constructor() {
super();
this.name = "Drop bytes";
this.module = "Default";
this.description = "Cuts a slice of the specified number of bytes out of the data.";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.args = [
{
"name": "Start",
"type": "number",
"value": 0
},
{
"name": "Length",
"type": "number",
"value": 5
},
{
"name": "Apply to each line",
"type": "boolean",
"value": false
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*
* @throws {OperationError} if invalid input
*/
run(input, args) {
const start = args[0],
length = args[1],
applyToEachLine = args[2];
if (start < 0 || length < 0)
throw new OperationError("Error: Invalid value");
if (!applyToEachLine) {
const left = input.slice(0, start),
right = input.slice(start + length, input.byteLength);
const result = new Uint8Array(left.byteLength + right.byteLength);
result.set(new Uint8Array(left), 0);
result.set(new Uint8Array(right), left.byteLength);
return result.buffer;
}
// Split input into lines
const data = new Uint8Array(input);
const lines = [];
let line = [],
i;
for (i = 0; i < data.length; i++) {
if (data[i] === 0x0a) {
lines.push(line);
line = [];
} else {
line.push(data[i]);
}
}
lines.push(line);
let output = [];
for (i = 0; i < lines.length; i++) {
output = output.concat(lines[i].slice(0, start).concat(lines[i].slice(start+length, lines[i].length)));
output.push(0x0a);
}
return new Uint8Array(output.slice(0, output.length-1)).buffer;
}
}
export default DropBytes;

View file

@ -0,0 +1,59 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Encode NetBIOS Name operation
*/
class EncodeNetBIOSName extends Operation {
/**
* EncodeNetBIOSName constructor
*/
constructor() {
super();
this.name = "Encode NetBIOS Name";
this.module = "Default";
this.description = "NetBIOS names as seen across the client interface to NetBIOS are exactly 16 bytes long. Within the NetBIOS-over-TCP protocols, a longer representation is used.<br><br>There are two levels of encoding. The first level maps a NetBIOS name into a domain system name. The second level maps the domain system name into the 'compressed' representation required for interaction with the domain name system.<br><br>This operation carries out the first level of encoding. See RFC 1001 for full details.";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
"name": "Offset",
"type": "number",
"value": 65
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const output = [],
offset = args[0];
if (input.length <= 16) {
const len = input.length;
input.length = 16;
input.fill(32, len, 16);
for (let i = 0; i < input.length; i++) {
output.push((input[i] >> 4) + offset);
output.push((input[i] & 0xf) + offset);
}
}
return output;
}
}
export default EncodeNetBIOSName;

View file

@ -0,0 +1,58 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import cptable from "../vendor/js-codepage/cptable.js";
import {IO_FORMAT} from "../lib/ChrEnc";
/**
* Encode text operation
*/
class EncodeText extends Operation {
/**
* EncodeText constructor
*/
constructor() {
super();
this.name = "Encode text";
this.module = "CharEnc";
this.description = [
"Encodes text into the chosen character encoding.",
"<br><br>",
"Supported charsets are:",
"<ul>",
Object.keys(IO_FORMAT).map(e => `<li>${e}</li>`).join("\n"),
"</ul>",
].join("\n");
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
"name": "Encoding",
"type": "option",
"value": Object.keys(IO_FORMAT)
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const format = IO_FORMAT[args[0]];
let encoded = cptable.utils.encode(format, input);
encoded = Array.from(encoded);
return encoded;
}
}
export default EncodeText;

View file

@ -0,0 +1,96 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Entropy operation
*/
class Entropy extends Operation {
/**
* Entropy constructor
*/
constructor() {
super();
this.name = "Entropy";
this.module = "Default";
this.description = "Calculates the Shannon entropy of the input data which gives an idea of its randomness. 8 is the maximum.";
this.inputType = "byteArray";
this.outputType = "number";
this.presentType = "html";
this.args = [];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {number}
*/
run(input, args) {
const prob = [],
uniques = input.unique(),
str = Utils.byteArrayToChars(input);
let i;
for (i = 0; i < uniques.length; i++) {
prob.push(str.count(Utils.chr(uniques[i])) / input.length);
}
let entropy = 0,
p;
for (i = 0; i < prob.length; i++) {
p = prob[i];
entropy += p * Math.log(p) / Math.log(2);
}
return -entropy;
}
/**
* Displays the entropy as a scale bar for web apps.
*
* @param {number} entropy
* @returns {html}
*/
present(entropy) {
return `Shannon entropy: ${entropy}
<br><canvas id='chart-area'></canvas><br>
- 0 represents no randomness (i.e. all the bytes in the data have the same value) whereas 8, the maximum, represents a completely random string.
- Standard English text usually falls somewhere between 3.5 and 5.
- Properly encrypted or compressed data of a reasonable length should have an entropy of over 7.5.
The following results show the entropy of chunks of the input data. Chunks with particularly high entropy could suggest encrypted or compressed sections.
<br><script>
var canvas = document.getElementById("chart-area"),
parentRect = canvas.parentNode.getBoundingClientRect(),
entropy = ${entropy},
height = parentRect.height * 0.25;
canvas.width = parentRect.width * 0.95;
canvas.height = height > 150 ? 150 : height;
CanvasComponents.drawScaleBar(canvas, entropy, 8, [
{
label: "English text",
min: 3.5,
max: 5
},{
label: "Encrypted/compressed",
min: 7.5,
max: 8
}
]);
</script>`;
}
}
export default Entropy;

View file

@ -0,0 +1,87 @@
/**
* @author Vel0x [dalemy@microsoft.com]
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import jsesc from "jsesc";
/**
* Escape string operation
*/
class EscapeString extends Operation {
/**
* EscapeString constructor
*/
constructor() {
super();
this.name = "Escape string";
this.module = "Default";
this.description = "Escapes special characters in a string so that they do not cause conflicts. For example, <code>Don't stop me now</code> becomes <code>Don\\'t stop me now</code>.<br><br>Supports the following escape sequences:<ul><li><code>\\n</code> (Line feed/newline)</li><li><code>\\r</code> (Carriage return)</li><li><code>\\t</code> (Horizontal tab)</li><li><code>\\b</code> (Backspace)</li><li><code>\\f</code> (Form feed)</li><li><code>\\xnn</code> (Hex, where n is 0-f)</li><li><code>\\\\</code> (Backslash)</li><li><code>\\'</code> (Single quote)</li><li><code>\\&quot;</code> (Double quote)</li><li><code>\\unnnn</code> (Unicode character)</li><li><code>\\u{nnnnnn}</code> (Unicode code point)</li></ul>";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Escape level",
"type": "option",
"value": ["Special chars", "Everything", "Minimal"]
},
{
"name": "Escape quote",
"type": "option",
"value": ["Single", "Double", "Backtick"]
},
{
"name": "JSON compatible",
"type": "boolean",
"value": false
},
{
"name": "ES6 compatible",
"type": "boolean",
"value": true
},
{
"name": "Uppercase hex",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @example
* EscapeString.run("Don't do that", [])
* > "Don\'t do that"
* EscapeString.run(`Hello
* World`, [])
* > "Hello\nWorld"
*/
run(input, args) {
const level = args[0],
quotes = args[1],
jsonCompat = args[2],
es6Compat = args[3],
lowercaseHex = !args[4];
return jsesc(input, {
quotes: quotes.toLowerCase(),
es6: es6Compat,
escapeEverything: level === "Everything",
minimal: level === "Minimal",
json: jsonCompat,
lowercaseHex: lowercaseHex,
});
}
}
export default EscapeString;

View file

@ -0,0 +1,96 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Escape Unicode Characters operation
*/
class EscapeUnicodeCharacters extends Operation {
/**
* EscapeUnicodeCharacters constructor
*/
constructor() {
super();
this.name = "Escape Unicode Characters";
this.module = "Default";
this.description = "Converts characters to their unicode-escaped notations.<br><br>Supports the prefixes:<ul><li><code>\\u</code></li><li><code>%u</code></li><li><code>U+</code></li></ul>e.g. <code>σου</code> becomes <code>\\u03C3\\u03BF\\u03C5</code>";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Prefix",
"type": "option",
"value": ["\\u", "%u", "U+"]
},
{
"name": "Encode all chars",
"type": "boolean",
"value": false
},
{
"name": "Padding",
"type": "number",
"value": 4
},
{
"name": "Uppercase hex",
"type": "boolean",
"value": true
}
];
this.patterns = [
{
match: "\\\\u(?:[\\da-f]{4,6})",
flags: "i",
args: ["\\u"]
},
{
match: "%u(?:[\\da-f]{4,6})",
flags: "i",
args: ["%u"]
},
{
match: "U\\+(?:[\\da-f]{4,6})",
flags: "i",
args: ["U+"]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const regexWhitelist = /[ -~]/i,
[prefix, encodeAll, padding, uppercaseHex] = args;
let output = "",
character = "";
for (let i = 0; i < input.length; i++) {
character = input[i];
if (!encodeAll && regexWhitelist.test(character)) {
// Its a printable ASCII character so dont escape it.
output += character;
continue;
}
let cp = character.codePointAt(0).toString(16);
if (uppercaseHex) cp = cp.toUpperCase();
output += prefix + cp.padStart(padding, "0");
}
return output;
}
}
export default EscapeUnicodeCharacters;

View file

@ -0,0 +1,46 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Expand alphabet range operation
*/
class ExpandAlphabetRange extends Operation {
/**
* ExpandAlphabetRange constructor
*/
constructor() {
super();
this.name = "Expand alphabet range";
this.module = "Default";
this.description = "Expand an alphabet range string into a list of the characters in that range.<br><br>e.g. <code>a-z</code> becomes <code>abcdefghijklmnopqrstuvwxyz</code>.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Delimiter",
"type": "binaryString",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return Utils.expandAlphRange(input).join(args[0]);
}
}
export default ExpandAlphabetRange;

View file

@ -0,0 +1,52 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search } from "../lib/Extract";
/**
* Extract dates operation
*/
class ExtractDates extends Operation {
/**
* ExtractDates constructor
*/
constructor() {
super();
this.name = "Extract dates";
this.module = "Regex";
this.description = "Extracts dates in the following formats<ul><li><code>yyyy-mm-dd</code></li><li><code>dd/mm/yyyy</code></li><li><code>mm/dd/yyyy</code></li></ul>Dividers can be any of /, -, . or space";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Display total",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const displayTotal = args[0],
date1 = "(?:19|20)\\d\\d[- /.](?:0[1-9]|1[012])[- /.](?:0[1-9]|[12][0-9]|3[01])", // yyyy-mm-dd
date2 = "(?:0[1-9]|[12][0-9]|3[01])[- /.](?:0[1-9]|1[012])[- /.](?:19|20)\\d\\d", // dd/mm/yyyy
date3 = "(?:0[1-9]|1[012])[- /.](?:0[1-9]|[12][0-9]|3[01])[- /.](?:19|20)\\d\\d", // mm/dd/yyyy
regex = new RegExp(date1 + "|" + date2 + "|" + date3, "ig");
return search(input, regex, null, displayTotal);
}
}
export default ExtractDates;

View file

@ -0,0 +1,49 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search } from "../lib/Extract";
/**
* Extract domains operation
*/
class ExtractDomains extends Operation {
/**
* ExtractDomains constructor
*/
constructor() {
super();
this.name = "Extract domains";
this.module = "Regex";
this.description = "Extracts domain names.<br>Note that this will not include paths. Use <strong>Extract URLs</strong> to find entire URLs.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Display total",
"type": "boolean",
"value": "Extract.DISPLAY_TOTAL"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const displayTotal = args[0],
regex = /\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b/ig;
return search(input, regex, null, displayTotal);
}
}
export default ExtractDomains;

View file

@ -0,0 +1,49 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search } from "../lib/Extract";
/**
* Extract email addresses operation
*/
class ExtractEmailAddresses extends Operation {
/**
* ExtractEmailAddresses constructor
*/
constructor() {
super();
this.name = "Extract email addresses";
this.module = "Regex";
this.description = "Extracts all email addresses from the input.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Display total",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const displayTotal = args[0],
regex = /\b\w[-.\w]*@[-\w]+(?:\.[-\w]+)*\.[A-Z]{2,4}\b/ig;
return search(input, regex, null, displayTotal);
}
}
export default ExtractEmailAddresses;

View file

@ -0,0 +1,78 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search } from "../lib/Extract";
/**
* Extract file paths operation
*/
class ExtractFilePaths extends Operation {
/**
* ExtractFilePaths constructor
*/
constructor() {
super();
this.name = "Extract file paths";
this.module = "Regex";
this.description = "Extracts anything that looks like a Windows or UNIX file path.<br><br>Note that if UNIX is selected, there will likely be a lot of false positives.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Windows",
"type": "boolean",
"value": true
},
{
"name": "UNIX",
"type": "boolean",
"value": true
},
{
"name": "Display total",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [includeWinPath, includeUnixPath, displayTotal] = args,
winDrive = "[A-Z]:\\\\",
winName = "[A-Z\\d][A-Z\\d\\- '_\\(\\)~]{0,61}",
winExt = "[A-Z\\d]{1,6}",
winPath = winDrive + "(?:" + winName + "\\\\?)*" + winName +
"(?:\\." + winExt + ")?",
unixPath = "(?:/[A-Z\\d.][A-Z\\d\\-.]{0,61})+";
let filePaths = "";
if (includeWinPath && includeUnixPath) {
filePaths = winPath + "|" + unixPath;
} else if (includeWinPath) {
filePaths = winPath;
} else if (includeUnixPath) {
filePaths = unixPath;
}
if (filePaths) {
const regex = new RegExp(filePaths, "ig");
return search(input, regex, null, displayTotal);
} else {
return "";
}
}
}
export default ExtractFilePaths;

View file

@ -0,0 +1,91 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search } from "../lib/Extract";
/**
* Extract IP addresses operation
*/
class ExtractIPAddresses extends Operation {
/**
* ExtractIPAddresses constructor
*/
constructor() {
super();
this.name = "Extract IP addresses";
this.module = "Regex";
this.description = "Extracts all IPv4 and IPv6 addresses.<br><br>Warning: Given a string <code>710.65.0.456</code>, this will match <code>10.65.0.45</code> so always check the original input!";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "IPv4",
"type": "boolean",
"value": true
},
{
"name": "IPv6",
"type": "boolean",
"value": false
},
{
"name": "Remove local IPv4 addresses",
"type": "boolean",
"value": false
},
{
"name": "Display total",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [includeIpv4, includeIpv6, removeLocal, displayTotal] = args,
ipv4 = "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?",
ipv6 = "((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|\\b)|){5}|([\\dA-F]{1,4}:){6})((([\\dA-F]{1,4}((?!\\3)::|:\\b|(?![\\dA-F])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})";
let ips = "";
if (includeIpv4 && includeIpv6) {
ips = ipv4 + "|" + ipv6;
} else if (includeIpv4) {
ips = ipv4;
} else if (includeIpv6) {
ips = ipv6;
}
if (ips) {
const regex = new RegExp(ips, "ig");
if (removeLocal) {
const ten = "10\\..+",
oneninetwo = "192\\.168\\..+",
oneseventwo = "172\\.(?:1[6-9]|2\\d|3[01])\\..+",
onetwoseven = "127\\..+",
removeRegex = new RegExp("^(?:" + ten + "|" + oneninetwo +
"|" + oneseventwo + "|" + onetwoseven + ")");
return search(input, regex, removeRegex, displayTotal);
} else {
return search(input, regex, null, displayTotal);
}
} else {
return "";
}
}
}
export default ExtractIPAddresses;

View file

@ -0,0 +1,49 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search } from "../lib/Extract";
/**
* Extract MAC addresses operation
*/
class ExtractMACAddresses extends Operation {
/**
* ExtractMACAddresses constructor
*/
constructor() {
super();
this.name = "Extract MAC addresses";
this.module = "Regex";
this.description = "Extracts all Media Access Control (MAC) addresses from the input.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Display total",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const displayTotal = args[0],
regex = /[A-F\d]{2}(?:[:-][A-F\d]{2}){5}/ig;
return search(input, regex, null, displayTotal);
}
}
export default ExtractMACAddresses;

View file

@ -0,0 +1,55 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search } from "../lib/Extract";
/**
* Extract URLs operation
*/
class ExtractURLs extends Operation {
/**
* ExtractURLs constructor
*/
constructor() {
super();
this.name = "Extract URLs";
this.module = "Regex";
this.description = "Extracts Uniform Resource Locators (URLs) from the input. The protocol (http, ftp etc.) is required otherwise there will be far too many false positives.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Display total",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const displayTotal = args[0],
protocol = "[A-Z]+://",
hostname = "[-\\w]+(?:\\.\\w[-\\w]*)+",
port = ":\\d+";
let path = "/[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]*";
path += "(?:[.!,?]+[^.!,?\"<>\\[\\]{}\\s\\x7F-\\xFF]+)*";
const regex = new RegExp(protocol + hostname + "(?:" + port +
")?(?:" + path + ")?", "ig");
return search(input, regex, null, displayTotal);
}
}
export default ExtractURLs;

View file

@ -0,0 +1,72 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {INPUT_DELIM_OPTIONS} from "../lib/Delim";
import OperationError from "../errors/OperationError";
/**
* Filter operation
*/
class Filter extends Operation {
/**
* Filter constructor
*/
constructor() {
super();
this.name = "Filter";
this.module = "Default";
this.description = "Splits up the input using the specified delimiter and then filters each branch based on a regular expression.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Delimiter",
"type": "option",
"value": INPUT_DELIM_OPTIONS
},
{
"name": "Regex",
"type": "string",
"value": ""
},
{
"name": "Invert condition",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const delim = Utils.charRep(args[0]),
reverse = args[2];
let regex;
try {
regex = new RegExp(args[1]);
} catch (err) {
throw new OperationError(`Invalid regex. Details: ${err.message}`);
}
const regexFilter = function(value) {
return reverse ^ regex.test(value);
};
return input.split(delim).filter(regexFilter).join(delim);
}
}
export default Filter;

View file

@ -0,0 +1,48 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Fletcher-16 Checksum operation
*/
class Fletcher16Checksum extends Operation {
/**
* Fletcher16Checksum constructor
*/
constructor() {
super();
this.name = "Fletcher-16 Checksum";
this.module = "Hashing";
this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.<br><br>The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques.";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let a = 0,
b = 0;
for (let i = 0; i < input.length; i++) {
a = (a + input[i]) % 0xff;
b = (b + a) % 0xff;
}
return Utils.hex(((b << 8) | a) >>> 0, 4);
}
}
export default Fletcher16Checksum;

View file

@ -0,0 +1,48 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Fletcher-32 Checksum operation
*/
class Fletcher32Checksum extends Operation {
/**
* Fletcher32Checksum constructor
*/
constructor() {
super();
this.name = "Fletcher-32 Checksum";
this.module = "Hashing";
this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.<br><br>The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques.";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let a = 0,
b = 0;
for (let i = 0; i < input.length; i++) {
a = (a + input[i]) % 0xffff;
b = (b + a) % 0xffff;
}
return Utils.hex(((b << 16) | a) >>> 0, 8);
}
}
export default Fletcher32Checksum;

View file

@ -0,0 +1,48 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Fletcher-64 Checksum operation
*/
class Fletcher64Checksum extends Operation {
/**
* Fletcher64Checksum constructor
*/
constructor() {
super();
this.name = "Fletcher-64 Checksum";
this.module = "Hashing";
this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.<br><br>The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques.";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let a = 0,
b = 0;
for (let i = 0; i < input.length; i++) {
a = (a + input[i]) % 0xffffffff;
b = (b + a) % 0xffffffff;
}
return Utils.hex(b >>> 0, 8) + Utils.hex(a >>> 0, 8);
}
}
export default Fletcher64Checksum;

View file

@ -0,0 +1,48 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Fletcher-8 Checksum operation
*/
class Fletcher8Checksum extends Operation {
/**
* Fletcher8Checksum constructor
*/
constructor() {
super();
this.name = "Fletcher-8 Checksum";
this.module = "Hashing";
this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.<br><br>The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques.";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let a = 0,
b = 0;
for (let i = 0; i < input.length; i++) {
a = (a + input[i]) % 0xf;
b = (b + a) % 0xf;
}
return Utils.hex(((b << 4) | a) >>> 0, 2);
}
}
export default Fletcher8Checksum;

View file

@ -0,0 +1,117 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import Recipe from "../Recipe";
import Dish from "../Dish";
/**
* Fork operation
*/
class Fork extends Operation {
/**
* Fork constructor
*/
constructor() {
super();
this.name = "Fork";
this.flowControl = true;
this.module = "Default";
this.description = "Split the input data up based on the specified delimiter and run all subsequent operations on each branch separately.<br><br>For example, to decode multiple Base64 strings, enter them all on separate lines then add the 'Fork' and 'From Base64' operations to the recipe. Each string will be decoded separately.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Split delimiter",
"type": "binaryShortString",
"value": "\\n"
},
{
"name": "Merge delimiter",
"type": "binaryShortString",
"value": "\\n"
},
{
"name": "Ignore errors",
"type": "boolean",
"value": false
}
];
}
/**
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @returns {Object} The updated state of the recipe.
*/
async run(state) {
const opList = state.opList,
inputType = opList[state.progress].inputType,
outputType = opList[state.progress].outputType,
input = await state.dish.get(inputType),
ings = opList[state.progress].ingValues,
[splitDelim, mergeDelim, ignoreErrors] = ings,
subOpList = [];
let inputs = [],
i;
if (input)
inputs = input.split(splitDelim);
// Create subOpList for each tranche to operate on
// (all remaining operations unless we encounter a Merge)
for (i = state.progress + 1; i < opList.length; i++) {
if (opList[i].name === "Merge" && !opList[i].disabled) {
break;
} else {
subOpList.push(opList[i]);
}
}
const recipe = new Recipe();
let output = "",
progress = 0;
state.forkOffset += state.progress + 1;
recipe.addOperations(subOpList);
// Take a deep(ish) copy of the ingredient values
const ingValues = subOpList.map(op => JSON.parse(JSON.stringify(op.ingValues)));
// Run recipe over each tranche
for (i = 0; i < inputs.length; i++) {
// Baseline ing values for each tranche so that registers are reset
subOpList.forEach((op, i) => {
op.ingValues = JSON.parse(JSON.stringify(ingValues[i]));
});
const dish = new Dish();
dish.set(inputs[i], inputType);
try {
progress = await recipe.execute(dish, 0, state);
} catch (err) {
if (!ignoreErrors) {
throw err;
}
progress = err.progress + 1;
}
output += await dish.get(outputType) + mergeDelim;
}
state.dish.set(output, outputType);
state.progress += progress;
return state;
}
}
export default Fork;

View file

@ -0,0 +1,121 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Format MAC addresses operation
*/
class FormatMACAddresses extends Operation {
/**
* FormatMACAddresses constructor
*/
constructor() {
super();
this.name = "Format MAC addresses";
this.module = "Default";
this.description = "Displays given MAC addresses in multiple different formats.<br><br>Expects addresses in a list separated by newlines, spaces or commas.<br><br>WARNING: There are no validity checks.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Output case",
"type": "option",
"value": ["Both", "Upper only", "Lower only"]
},
{
"name": "No delimiter",
"type": "boolean",
"value": true
},
{
"name": "Dash delimiter",
"type": "boolean",
"value": true
},
{
"name": "Colon delimiter",
"type": "boolean",
"value": true
},
{
"name": "Cisco style",
"type": "boolean",
"value": false
},
{
"name": "IPv6 interface ID",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!input) return "";
const [
outputCase,
noDelim,
dashDelim,
colonDelim,
ciscoStyle,
ipv6IntID
] = args,
outputList = [],
macs = input.toLowerCase().split(/[,\s\r\n]+/);
macs.forEach(function(mac) {
const cleanMac = mac.replace(/[:.-]+/g, ""),
macHyphen = cleanMac.replace(/(.{2}(?=.))/g, "$1-"),
macColon = cleanMac.replace(/(.{2}(?=.))/g, "$1:"),
macCisco = cleanMac.replace(/(.{4}(?=.))/g, "$1.");
let macIPv6 = cleanMac.slice(0, 6) + "fffe" + cleanMac.slice(6);
macIPv6 = macIPv6.replace(/(.{4}(?=.))/g, "$1:");
let bite = parseInt(macIPv6.slice(0, 2), 16) ^ 2;
bite = bite.toString(16).padStart(2, "0");
macIPv6 = bite + macIPv6.slice(2);
if (outputCase === "Lower only") {
if (noDelim) outputList.push(cleanMac);
if (dashDelim) outputList.push(macHyphen);
if (colonDelim) outputList.push(macColon);
if (ciscoStyle) outputList.push(macCisco);
if (ipv6IntID) outputList.push(macIPv6);
} else if (outputCase === "Upper only") {
if (noDelim) outputList.push(cleanMac.toUpperCase());
if (dashDelim) outputList.push(macHyphen.toUpperCase());
if (colonDelim) outputList.push(macColon.toUpperCase());
if (ciscoStyle) outputList.push(macCisco.toUpperCase());
if (ipv6IntID) outputList.push(macIPv6.toUpperCase());
} else {
if (noDelim) outputList.push(cleanMac, cleanMac.toUpperCase());
if (dashDelim) outputList.push(macHyphen, macHyphen.toUpperCase());
if (colonDelim) outputList.push(macColon, macColon.toUpperCase());
if (ciscoStyle) outputList.push(macCisco, macCisco.toUpperCase());
if (ipv6IntID) outputList.push(macIPv6, macIPv6.toUpperCase());
}
outputList.push(
"" // Empty line to delimit groups
);
});
// Return the data as a string
return outputList.join("\n");
}
}
export default FormatMACAddresses;

View file

@ -0,0 +1,110 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
/**
* Frequency distribution operation
*/
class FrequencyDistribution extends Operation {
/**
* FrequencyDistribution constructor
*/
constructor() {
super();
this.name = "Frequency distribution";
this.module = "Default";
this.description = "Displays the distribution of bytes in the data as a graph.";
this.inputType = "ArrayBuffer";
this.outputType = "json";
this.presentType = "html";
this.args = [
{
"name": "Show 0%s",
"type": "boolean",
"value": "Entropy.FREQ_ZEROS"
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {json}
*/
run(input, args) {
const data = new Uint8Array(input);
if (!data.length) throw new OperationError("No data");
const distrib = new Array(256).fill(0),
percentages = new Array(256),
len = data.length;
let i;
// Count bytes
for (i = 0; i < len; i++) {
distrib[data[i]]++;
}
// Calculate percentages
let repr = 0;
for (i = 0; i < 256; i++) {
if (distrib[i] > 0) repr++;
percentages[i] = distrib[i] / len * 100;
}
return {
"dataLength": len,
"percentages": percentages,
"distribution": distrib,
"bytesRepresented": repr
};
}
/**
* Displays the frequency distribution as a bar chart for web apps.
*
* @param {json} freq
* @returns {html}
*/
present(freq, args) {
const showZeroes = args[0];
// Print
let output = `<canvas id='chart-area'></canvas><br>
Total data length: ${freq.dataLength}
Number of bytes represented: ${freq.bytesRepresented}
Number of bytes not represented: ${256 - freq.bytesRepresented}
Byte Percentage
<script>
var canvas = document.getElementById("chart-area"),
parentRect = canvas.parentNode.getBoundingClientRect(),
scores = ${JSON.stringify(freq.percentages)};
canvas.width = parentRect.width * 0.95;
canvas.height = parentRect.height * 0.9;
CanvasComponents.drawBarChart(canvas, scores, "Byte", "Frequency %", 16, 6);
</script>`;
for (let i = 0; i < 256; i++) {
if (freq.distribution[i] || showZeroes) {
output += " " + Utils.hex(i, 2) + " (" +
(freq.percentages[i].toFixed(2).replace(".00", "") + "%)").padEnd(8, " ") +
Array(Math.ceil(freq.percentages[i])+1).join("|") + "\n";
}
}
return output;
}
}
export default FrequencyDistribution;

View file

@ -0,0 +1,122 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
import {ENCODING_SCHEME, ENCODING_LOOKUP, FORMAT} from "../lib/BCD";
import BigNumber from "bignumber.js";
/**
* From BCD operation
*/
class FromBCD extends Operation {
/**
* FromBCD constructor
*/
constructor() {
super();
this.name = "From BCD";
this.module = "Default";
this.description = "Binary-Coded Decimal (BCD) is a class of binary encodings of decimal numbers where each decimal digit is represented by a fixed number of bits, usually four or eight. Special bit patterns are sometimes used for a sign.";
this.inputType = "string";
this.outputType = "BigNumber";
this.args = [
{
"name": "Scheme",
"type": "option",
"value": ENCODING_SCHEME
},
{
"name": "Packed",
"type": "boolean",
"value": true
},
{
"name": "Signed",
"type": "boolean",
"value": false
},
{
"name": "Input format",
"type": "option",
"value": FORMAT
}
];
this.patterns = [
{
match: "^(?:\\d{4} ){3,}\\d{4}$",
flags: "",
args: ["8 4 2 1", true, false, "Nibbles"]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
run(input, args) {
const encoding = ENCODING_LOOKUP[args[0]],
packed = args[1],
signed = args[2],
inputFormat = args[3],
nibbles = [];
let output = "",
byteArray;
// Normalise the input
switch (inputFormat) {
case "Nibbles":
case "Bytes":
input = input.replace(/\s/g, "");
for (let i = 0; i < input.length; i += 4) {
nibbles.push(parseInt(input.substr(i, 4), 2));
}
break;
case "Raw":
default:
byteArray = Utils.strToByteArray(input);
byteArray.forEach(b => {
nibbles.push(b >>> 4);
nibbles.push(b & 15);
});
break;
}
if (!packed) {
// Discard each high nibble
for (let i = 0; i < nibbles.length; i++) {
nibbles.splice(i, 1);
}
}
if (signed) {
const sign = nibbles.pop();
if (sign === 13 ||
sign === 11) {
// Negative
output += "-";
}
}
nibbles.forEach(n => {
if (isNaN(n)) throw new OperationError("Invalid input");
const val = encoding.indexOf(n);
if (val < 0) throw new OperationError(`Value ${Utils.bin(n, 4)} is not in the encoding scheme`);
output += val.toString();
});
return new BigNumber(output);
}
}
export default FromBCD;

View file

@ -0,0 +1,63 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import BigNumber from "bignumber.js";
import OperationError from "../errors/OperationError";
/**
* From Base operation
*/
class FromBase extends Operation {
/**
* FromBase constructor
*/
constructor() {
super();
this.name = "From Base";
this.module = "Default";
this.description = "Converts a number to decimal from a given numerical base.";
this.inputType = "string";
this.outputType = "BigNumber";
this.args = [
{
"name": "Radix",
"type": "number",
"value": 36
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
run(input, args) {
const radix = args[0];
if (radix < 2 || radix > 36) {
throw new OperationError("Error: Radix argument must be between 2 and 36");
}
const number = input.replace(/\s/g, "").split(".");
let result = new BigNumber(number[0], radix) || 0;
if (number.length === 1) return result;
// Fractional part
for (let i = 0; i < number[1].length; i++) {
const digit = new BigNumber(number[1][i], radix);
result += digit.div(Math.pow(radix, i+1));
}
return result;
}
}
export default FromBase;

View file

@ -35,6 +35,13 @@ class FromBase32 extends Operation {
value: true
}
];
this.patterns = [
{
match: "^(?:[A-Z2-7]{8})+(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}={1})?$",
flags: "",
args: ["A-Z2-7=", false]
},
];
}
/**

View file

@ -0,0 +1,105 @@
/**
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
import {ALPHABET_OPTIONS} from "../lib/Base58";
/**
* From Base58 operation
*/
class FromBase58 extends Operation {
/**
* FromBase58 constructor
*/
constructor() {
super();
this.name = "From Base58";
this.module = "Default";
this.description = "Base58 (similar to Base64) is a notation for encoding arbitrary byte data. It differs from Base64 by removing easily misread characters (i.e. l, I, 0 and O) to improve human readability.<br><br>This operation decodes data from an ASCII string (with an alphabet of your choosing, presets included) back into its raw form.<br><br>e.g. <code>StV1DL6CwTryKyV</code> becomes <code>hello world</code><br><br>Base58 is commonly used in cryptocurrencies (Bitcoin, Ripple, etc).";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
"name": "Alphabet",
"type": "editableOption",
"value": ALPHABET_OPTIONS
},
{
"name": "Remove non-alphabet chars",
"type": "boolean",
"value": true
}
];
this.patterns = [
{
match: "^[1-9A-HJ-NP-Za-km-z]{20,}$",
flags: "",
args: ["123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", false]
},
{
match: "^[1-9A-HJ-NP-Za-km-z]{20,}$",
flags: "",
args: ["rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz", false]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
let alphabet = args[0] || ALPHABET_OPTIONS[0].value;
const removeNonAlphaChars = args[1] === undefined ? true : args[1],
result = [0];
alphabet = Utils.expandAlphRange(alphabet).join("");
if (alphabet.length !== 58 ||
[].unique.call(alphabet).length !== 58) {
throw new OperationError("Alphabet must be of length 58");
}
if (input.length === 0) return [];
[].forEach.call(input, function(c, charIndex) {
const index = alphabet.indexOf(c);
if (index === -1) {
if (removeNonAlphaChars) {
return;
} else {
throw new OperationError(`Char '${c}' at position ${charIndex} not in alphabet`);
}
}
let carry = result[0] * 58 + index;
result[0] = carry & 0xFF;
carry = carry >> 8;
for (let i = 1; i < result.length; i++) {
carry += result[i] * 58;
result[i] = carry & 0xFF;
carry = carry >> 8;
}
while (carry > 0) {
result.push(carry & 0xFF);
carry = carry >> 8;
}
});
return result.reverse();
}
}
export default FromBase58;

View file

@ -35,6 +35,73 @@ class FromBase64 extends Operation {
value: true
}
];
this.patterns = [
{
match: "^(?:[A-Z\\d+/]{4})+(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?$",
flags: "i",
args: ["A-Za-z0-9+/=", false]
},
{
match: "^[A-Z\\d\\-_]{20,}$",
flags: "i",
args: ["A-Za-z0-9-_", false]
},
{
match: "^(?:[A-Z\\d+\\-]{4}){5,}(?:[A-Z\\d+\\-]{2}==|[A-Z\\d+\\-]{3}=)?$",
flags: "i",
args: ["A-Za-z0-9+\\-=", false]
},
{
match: "^(?:[A-Z\\d./]{4}){5,}(?:[A-Z\\d./]{2}==|[A-Z\\d./]{3}=)?$",
flags: "i",
args: ["./0-9A-Za-z=", false]
},
{
match: "^[A-Z\\d_.]{20,}$",
flags: "i",
args: ["A-Za-z0-9_.", false]
},
{
match: "^(?:[A-Z\\d._]{4}){5,}(?:[A-Z\\d._]{2}--|[A-Z\\d._]{3}-)?$",
flags: "i",
args: ["A-Za-z0-9._-", false]
},
{
match: "^(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?$",
flags: "i",
args: ["0-9a-zA-Z+/=", false]
},
{
match: "^(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?$",
flags: "i",
args: ["0-9A-Za-z+/=", false]
},
{
match: "^[ !\"#$%&'()*+,\\-./\\d:;<=>?@A-Z[\\\\\\]^_]{20,}$",
flags: "",
args: [" -_", false]
},
{
match: "^[A-Z\\d+\\-]{20,}$",
flags: "i",
args: ["+\\-0-9A-Za-z", false]
},
{
match: "^[!\"#$%&'()*+,\\-0-689@A-NP-VX-Z[`a-fh-mp-r]{20,}$",
flags: "",
args: ["!-,-0-689@A-NP-VX-Z[`a-fh-mp-r", false]
},
{
match: "^(?:[N-ZA-M\\d+/]{4}){5,}(?:[N-ZA-M\\d+/]{2}==|[N-ZA-M\\d+/]{3}=)?$",
flags: "i",
args: ["N-ZA-Mn-za-m0-9+/=", false]
},
{
match: "^[A-Z\\d./]{20,}$",
flags: "i",
args: ["./0-9A-Za-z", false]
},
];
}
/**

View file

@ -0,0 +1,124 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {BIN_DELIM_OPTIONS} from "../lib/Delim";
/**
* From Binary operation
*/
class FromBinary extends Operation {
/**
* FromBinary constructor
*/
constructor() {
super();
this.name = "From Binary";
this.module = "Default";
this.description = "Converts a binary string back into its raw form.<br><br>e.g. <code>01001000 01101001</code> becomes <code>Hi</code>";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
"name": "Delimiter",
"type": "option",
"value": BIN_DELIM_OPTIONS
}
];
this.patterns = [
{
match: "^(?:[01]{8})+$",
flags: "",
args: ["None"]
},
{
match: "^(?:[01]{8})(?: [01]{8})*$",
flags: "",
args: ["Space"]
},
{
match: "^(?:[01]{8})(?:,[01]{8})*$",
flags: "",
args: ["Comma"]
},
{
match: "^(?:[01]{8})(?:;[01]{8})*$",
flags: "",
args: ["Semi-colon"]
},
{
match: "^(?:[01]{8})(?::[01]{8})*$",
flags: "",
args: ["Colon"]
},
{
match: "^(?:[01]{8})(?:\\n[01]{8})*$",
flags: "",
args: ["Line feed"]
},
{
match: "^(?:[01]{8})(?:\\r\\n[01]{8})*$",
flags: "",
args: ["CRLF"]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const delimRegex = Utils.regexRep(args[0] || "Space");
input = input.replace(delimRegex, "");
const output = [];
const byteLen = 8;
for (let i = 0; i < input.length; i += byteLen) {
output.push(parseInt(input.substr(i, byteLen), 2));
}
return output;
}
/**
* Highlight From Binary
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
const delim = Utils.charRep(args[0] || "Space");
pos[0].start = pos[0].start === 0 ? 0 : Math.floor(pos[0].start / (8 + delim.length));
pos[0].end = pos[0].end === 0 ? 0 : Math.ceil(pos[0].end / (8 + delim.length));
return pos;
}
/**
* Highlight From Binary in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
const delim = Utils.charRep(args[0] || "Space");
pos[0].start = pos[0].start * (8 + delim.length);
pos[0].end = pos[0].end * (8 + delim.length) - delim.length;
return pos;
}
}
export default FromBinary;

View file

@ -0,0 +1,83 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {DELIM_OPTIONS} from "../lib/Delim";
import OperationError from "../errors/OperationError";
/**
* From Charcode operation
*/
class FromCharcode extends Operation {
/**
* FromCharcode constructor
*/
constructor() {
super();
this.name = "From Charcode";
this.module = "Default";
this.description = "Converts unicode character codes back into text.<br><br>e.g. <code>0393 03b5 03b9 03ac 20 03c3 03bf 03c5</code> becomes <code>Γειά σου</code>";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
"name": "Delimiter",
"type": "option",
"value": DELIM_OPTIONS
},
{
"name": "Base",
"type": "number",
"value": 16
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*
* @throws {OperationError} if base out of range
*/
run(input, args) {
const delim = Utils.charRep(args[0] || "Space"),
base = args[1];
let bites = input.split(delim),
i = 0;
if (base < 2 || base > 36) {
throw new OperationError("Error: Base argument must be between 2 and 36");
}
if (input.length === 0) {
return [];
}
if (base !== 16 && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
// Split into groups of 2 if the whole string is concatenated and
// too long to be a single character
if (bites.length === 1 && input.length > 17) {
bites = [];
for (i = 0; i < input.length; i += 2) {
bites.push(input.slice(i, i+2));
}
}
let latin1 = "";
for (i = 0; i < bites.length; i++) {
latin1 += Utils.chr(parseInt(bites[i], base));
}
return Utils.strToByteArray(latin1);
}
}
export default FromCharcode;

View file

@ -0,0 +1,88 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {DELIM_OPTIONS} from "../lib/Delim";
/**
* From Decimal operation
*/
class FromDecimal extends Operation {
/**
* FromDecimal constructor
*/
constructor() {
super();
this.name = "From Decimal";
this.module = "Default";
this.description = "Converts the data from an ordinal integer array back into its raw form.<br><br>e.g. <code>72 101 108 108 111</code> becomes <code>Hello</code>";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
"name": "Delimiter",
"type": "option",
"value": DELIM_OPTIONS
}
];
this.patterns = [
{
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?: (?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
flags: "",
args: ["Space"]
},
{
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:,(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
flags: "",
args: ["Comma"]
},
{
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:;(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
flags: "",
args: ["Semi-colon"]
},
{
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?::(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
flags: "",
args: ["Colon"]
},
{
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:\\n(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
flags: "",
args: ["Line feed"]
},
{
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:\\r\\n(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
flags: "",
args: ["CRLF"]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const delim = Utils.charRep(args[0]),
output = [];
let byteStr = input.split(delim);
if (byteStr[byteStr.length-1] === "")
byteStr = byteStr.slice(0, byteStr.length-1);
for (let i = 0; i < byteStr.length; i++) {
output[i] = parseInt(byteStr[i], 10);
}
return output;
}
}
export default FromDecimal;

View file

@ -0,0 +1,342 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* From HTML Entity operation
*/
class FromHTMLEntity extends Operation {
/**
* FromHTMLEntity constructor
*/
constructor() {
super();
this.name = "From HTML Entity";
this.module = "Default";
this.description = "Converts HTML entities back to characters<br><br>e.g. <code>&amp;<span>amp;</span></code> becomes <code>&amp;</code>";
this.inputType = "string";
this.outputType = "string";
this.args = [];
this.patterns = [
{
match: "&(?:#\\d{2,3}|#x[\\da-f]{2}|[a-z]{2,6});",
flags: "i",
args: []
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const regex = /&(#?x?[a-zA-Z0-9]{1,8});/g;
let output = "",
m,
i = 0;
while ((m = regex.exec(input))) {
// Add up to match
for (; i < m.index;)
output += input[i++];
// Add match
const bite = entityToByte[m[1]];
if (bite) {
output += Utils.chr(bite);
} else if (!bite && m[1][0] === "#" && m[1].length > 1 && /^#\d{1,6}$/.test(m[1])) {
// Numeric entity (e.g. &#10;)
const num = m[1].slice(1, m[1].length);
output += Utils.chr(parseInt(num, 10));
} else if (!bite && m[1][0] === "#" && m[1].length > 3 && /^#x[\dA-F]{2,8}$/i.test(m[1])) {
// Hex entity (e.g. &#x3A;)
const hex = m[1].slice(2, m[1].length);
output += Utils.chr(parseInt(hex, 16));
} else {
// Not a valid entity, print as normal
for (; i < regex.lastIndex;)
output += input[i++];
}
i = regex.lastIndex;
}
// Add all after final match
for (; i < input.length;)
output += input[i++];
return output;
}
}
/**
* Lookup table to translate HTML entity codes to their byte values.
*/
const entityToByte = {
"quot": 34,
"amp": 38,
"apos": 39,
"lt": 60,
"gt": 62,
"nbsp": 160,
"iexcl": 161,
"cent": 162,
"pound": 163,
"curren": 164,
"yen": 165,
"brvbar": 166,
"sect": 167,
"uml": 168,
"copy": 169,
"ordf": 170,
"laquo": 171,
"not": 172,
"shy": 173,
"reg": 174,
"macr": 175,
"deg": 176,
"plusmn": 177,
"sup2": 178,
"sup3": 179,
"acute": 180,
"micro": 181,
"para": 182,
"middot": 183,
"cedil": 184,
"sup1": 185,
"ordm": 186,
"raquo": 187,
"frac14": 188,
"frac12": 189,
"frac34": 190,
"iquest": 191,
"Agrave": 192,
"Aacute": 193,
"Acirc": 194,
"Atilde": 195,
"Auml": 196,
"Aring": 197,
"AElig": 198,
"Ccedil": 199,
"Egrave": 200,
"Eacute": 201,
"Ecirc": 202,
"Euml": 203,
"Igrave": 204,
"Iacute": 205,
"Icirc": 206,
"Iuml": 207,
"ETH": 208,
"Ntilde": 209,
"Ograve": 210,
"Oacute": 211,
"Ocirc": 212,
"Otilde": 213,
"Ouml": 214,
"times": 215,
"Oslash": 216,
"Ugrave": 217,
"Uacute": 218,
"Ucirc": 219,
"Uuml": 220,
"Yacute": 221,
"THORN": 222,
"szlig": 223,
"agrave": 224,
"aacute": 225,
"acirc": 226,
"atilde": 227,
"auml": 228,
"aring": 229,
"aelig": 230,
"ccedil": 231,
"egrave": 232,
"eacute": 233,
"ecirc": 234,
"euml": 235,
"igrave": 236,
"iacute": 237,
"icirc": 238,
"iuml": 239,
"eth": 240,
"ntilde": 241,
"ograve": 242,
"oacute": 243,
"ocirc": 244,
"otilde": 245,
"ouml": 246,
"divide": 247,
"oslash": 248,
"ugrave": 249,
"uacute": 250,
"ucirc": 251,
"uuml": 252,
"yacute": 253,
"thorn": 254,
"yuml": 255,
"OElig": 338,
"oelig": 339,
"Scaron": 352,
"scaron": 353,
"Yuml": 376,
"fnof": 402,
"circ": 710,
"tilde": 732,
"Alpha": 913,
"Beta": 914,
"Gamma": 915,
"Delta": 916,
"Epsilon": 917,
"Zeta": 918,
"Eta": 919,
"Theta": 920,
"Iota": 921,
"Kappa": 922,
"Lambda": 923,
"Mu": 924,
"Nu": 925,
"Xi": 926,
"Omicron": 927,
"Pi": 928,
"Rho": 929,
"Sigma": 931,
"Tau": 932,
"Upsilon": 933,
"Phi": 934,
"Chi": 935,
"Psi": 936,
"Omega": 937,
"alpha": 945,
"beta": 946,
"gamma": 947,
"delta": 948,
"epsilon": 949,
"zeta": 950,
"eta": 951,
"theta": 952,
"iota": 953,
"kappa": 954,
"lambda": 955,
"mu": 956,
"nu": 957,
"xi": 958,
"omicron": 959,
"pi": 960,
"rho": 961,
"sigmaf": 962,
"sigma": 963,
"tau": 964,
"upsilon": 965,
"phi": 966,
"chi": 967,
"psi": 968,
"omega": 969,
"thetasym": 977,
"upsih": 978,
"piv": 982,
"ensp": 8194,
"emsp": 8195,
"thinsp": 8201,
"zwnj": 8204,
"zwj": 8205,
"lrm": 8206,
"rlm": 8207,
"ndash": 8211,
"mdash": 8212,
"lsquo": 8216,
"rsquo": 8217,
"sbquo": 8218,
"ldquo": 8220,
"rdquo": 8221,
"bdquo": 8222,
"dagger": 8224,
"Dagger": 8225,
"bull": 8226,
"hellip": 8230,
"permil": 8240,
"prime": 8242,
"Prime": 8243,
"lsaquo": 8249,
"rsaquo": 8250,
"oline": 8254,
"frasl": 8260,
"euro": 8364,
"image": 8465,
"weierp": 8472,
"real": 8476,
"trade": 8482,
"alefsym": 8501,
"larr": 8592,
"uarr": 8593,
"rarr": 8594,
"darr": 8595,
"harr": 8596,
"crarr": 8629,
"lArr": 8656,
"uArr": 8657,
"rArr": 8658,
"dArr": 8659,
"hArr": 8660,
"forall": 8704,
"part": 8706,
"exist": 8707,
"empty": 8709,
"nabla": 8711,
"isin": 8712,
"notin": 8713,
"ni": 8715,
"prod": 8719,
"sum": 8721,
"minus": 8722,
"lowast": 8727,
"radic": 8730,
"prop": 8733,
"infin": 8734,
"ang": 8736,
"and": 8743,
"or": 8744,
"cap": 8745,
"cup": 8746,
"int": 8747,
"there4": 8756,
"sim": 8764,
"cong": 8773,
"asymp": 8776,
"ne": 8800,
"equiv": 8801,
"le": 8804,
"ge": 8805,
"sub": 8834,
"sup": 8835,
"nsub": 8836,
"sube": 8838,
"supe": 8839,
"oplus": 8853,
"otimes": 8855,
"perp": 8869,
"sdot": 8901,
"vellip": 8942,
"lceil": 8968,
"rceil": 8969,
"lfloor": 8970,
"rfloor": 8971,
"lang": 9001,
"rang": 9002,
"loz": 9674,
"spades": 9824,
"clubs": 9827,
"hearts": 9829,
"diams": 9830,
};
export default FromHTMLEntity;

View file

@ -5,7 +5,7 @@
*/
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";
/**
@ -28,7 +28,54 @@ class FromHex extends Operation {
{
name: "Delimiter",
type: "option",
value: HEX_DELIM_OPTIONS
value: FROM_HEX_DELIM_OPTIONS
}
];
this.patterns = [
{
match: "^(?:[\\dA-F]{2})+$",
flags: "i",
args: ["None"]
},
{
match: "^[\\dA-F]{2}(?: [\\dA-F]{2})*$",
flags: "i",
args: ["Space"]
},
{
match: "^[\\dA-F]{2}(?:,[\\dA-F]{2})*$",
flags: "i",
args: ["Comma"]
},
{
match: "^[\\dA-F]{2}(?:;[\\dA-F]{2})*$",
flags: "i",
args: ["Semi-colon"]
},
{
match: "^[\\dA-F]{2}(?::[\\dA-F]{2})*$",
flags: "i",
args: ["Colon"]
},
{
match: "^[\\dA-F]{2}(?:\\n[\\dA-F]{2})*$",
flags: "i",
args: ["Line feed"]
},
{
match: "^[\\dA-F]{2}(?:\\r\\n[\\dA-F]{2})*$",
flags: "i",
args: ["CRLF"]
},
{
match: "^[\\dA-F]{2}(?:0x[\\dA-F]{2})*$",
flags: "i",
args: ["0x"]
},
{
match: "^[\\dA-F]{2}(?:\\\\x[\\dA-F]{2})*$",
flags: "i",
args: ["\\x"]
}
];
}
@ -53,6 +100,7 @@ class FromHex extends Operation {
* @returns {Object[]} pos
*/
highlight(pos, args) {
if (args[0] === "Auto") return false;
const delim = Utils.charRep(args[0] || "Space"),
len = delim === "\r\n" ? 1 : delim.length,
width = len + 2;

View file

@ -0,0 +1,66 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {fromHex} from "../lib/Hex";
/**
* From Hex Content operation
*/
class FromHexContent extends Operation {
/**
* FromHexContent constructor
*/
constructor() {
super();
this.name = "From Hex Content";
this.module = "Default";
this.description = "Translates hexadecimal bytes in text back to raw bytes.<br><br>e.g. <code>foo|3d|bar</code> becomes <code>foo=bar</code>.";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const regex = /\|([a-f\d ]{2,})\|/gi,
output = [];
let m, i = 0;
while ((m = regex.exec(input))) {
// Add up to match
for (; i < m.index;)
output.push(Utils.ord(input[i++]));
// Add match
const bytes = fromHex(m[1]);
if (bytes) {
for (let a = 0; a < bytes.length;)
output.push(bytes[a++]);
} else {
// Not valid hex, print as normal
for (; i < regex.lastIndex;)
output.push(Utils.ord(input[i++]));
}
i = regex.lastIndex;
}
// Add all after final match
for (; i < input.length;)
output.push(Utils.ord(input[i++]));
return output;
}
}
export default FromHexContent;

View file

@ -0,0 +1,163 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {fromHex} from "../lib/Hex";
/**
* From Hexdump operation
*/
class FromHexdump extends Operation {
/**
* FromHexdump constructor
*/
constructor() {
super();
this.name = "From Hexdump";
this.module = "Default";
this.description = "Attempts to convert a hexdump back into raw data. This operation supports many different hexdump variations, but probably not all. Make sure you verify that the data it gives you is correct before continuing analysis.";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [];
this.patterns = [
{
match: "^(?:(?:[\\dA-F]{4,16}:?)?\\s*((?:[\\dA-F]{2}\\s){1,8}(?:\\s|[\\dA-F]{2}-)(?:[\\dA-F]{2}\\s){1,8}|(?:[\\dA-F]{2}\\s|[\\dA-F]{4}\\s)+)[^\\n]*\\n?)+$",
flags: "i",
args: []
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const output = [],
regex = /^\s*(?:[\dA-F]{4,16}h?:?)?\s*((?:[\dA-F]{2}\s){1,8}(?:\s|[\dA-F]{2}-)(?:[\dA-F]{2}\s){1,8}|(?:[\dA-F]{2}\s|[\dA-F]{4}\s)+)/igm;
let block, line;
while ((block = regex.exec(input))) {
line = fromHex(block[1].replace(/-/g, " "));
for (let i = 0; i < line.length; i++) {
output.push(line[i]);
}
}
// Is this a CyberChef hexdump or is it from a different tool?
const width = input.indexOf("\n");
const w = (width - 13) / 4;
// w should be the specified width of the hexdump and therefore a round number
if (Math.floor(w) !== w || input.indexOf("\r") !== -1 || output.indexOf(13) !== -1) {
if (ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
}
return output;
}
/**
* Highlight From Hexdump
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
const w = args[0] || 16;
const width = 14 + (w*4);
let line = Math.floor(pos[0].start / width);
let offset = pos[0].start % width;
if (offset < 10) { // In line number section
pos[0].start = line*w;
} else if (offset > 10+(w*3)) { // In ASCII section
pos[0].start = (line+1)*w;
} else { // In byte section
pos[0].start = line*w + Math.floor((offset-10)/3);
}
line = Math.floor(pos[0].end / width);
offset = pos[0].end % width;
if (offset < 10) { // In line number section
pos[0].end = line*w;
} else if (offset > 10+(w*3)) { // In ASCII section
pos[0].end = (line+1)*w;
} else { // In byte section
pos[0].end = line*w + Math.ceil((offset-10)/3);
}
return pos;
}
/**
* Highlight From Hexdump in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
// Calculate overall selection
const w = args[0] || 16,
width = 14 + (w*4);
let line = Math.floor(pos[0].start / w),
offset = pos[0].start % w,
start = 0,
end = 0;
pos[0].start = line*width + 10 + offset*3;
line = Math.floor(pos[0].end / w);
offset = pos[0].end % w;
if (offset === 0) {
line--;
offset = w;
}
pos[0].end = line*width + 10 + offset*3 - 1;
// Set up multiple selections for bytes
let startLineNum = Math.floor(pos[0].start / width);
const endLineNum = Math.floor(pos[0].end / width);
if (startLineNum === endLineNum) {
pos.push(pos[0]);
} else {
start = pos[0].start;
end = (startLineNum+1) * width - w - 5;
pos.push({ start: start, end: end });
while (end < pos[0].end) {
startLineNum++;
start = startLineNum * width + 10;
end = (startLineNum+1) * width - w - 5;
if (end > pos[0].end) end = pos[0].end;
pos.push({ start: start, end: end });
}
}
// Set up multiple selections for ASCII
const len = pos.length;
let lineNum = 0;
start = 0;
end = 0;
for (let i = 1; i < len; i++) {
lineNum = Math.floor(pos[i].start / width);
start = (((pos[i].start - (lineNum * width)) - 10) / 3) + (width - w -2) + (lineNum * width);
end = (((pos[i].end + 1 - (lineNum * width)) - 10) / 3) + (width - w -2) + (lineNum * width);
pos.push({ start: start, end: end });
}
return pos;
}
}
export default FromHexdump;

View file

@ -0,0 +1,152 @@
/**
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {LETTER_DELIM_OPTIONS, WORD_DELIM_OPTIONS} from "../lib/Delim";
/**
* From Morse Code operation
*/
class FromMorseCode extends Operation {
/**
* FromMorseCode constructor
*/
constructor() {
super();
this.name = "From Morse Code";
this.module = "Default";
this.description = "Translates Morse Code into (upper case) alphanumeric characters.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Letter delimiter",
"type": "option",
"value": LETTER_DELIM_OPTIONS
},
{
"name": "Word delimiter",
"type": "option",
"value": WORD_DELIM_OPTIONS
}
];
this.patterns = [
{
match: "(?:^[-. \\n]{5,}$|^[_. \\n]{5,}$|^(?:dash|dot| |\\n){5,}$)",
flags: "i",
args: ["Space", "Line feed"]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!this.reversedTable) {
this.reverseTable();
}
const letterDelim = Utils.charRep(args[0]);
const wordDelim = Utils.charRep(args[1]);
input = input.replace(/-|||_||—|dash/ig, "<dash>"); //hyphen-minus|hyphen|minus-sign|undersore|en-dash|em-dash
input = input.replace(/\.|·|dot/ig, "<dot>");
let words = input.split(wordDelim);
const self = this;
words = Array.prototype.map.call(words, function(word) {
const signals = word.split(letterDelim);
const letters = signals.map(function(signal) {
return self.reversedTable[signal];
});
return letters.join("");
});
words = words.join(" ");
return words;
}
/**
* Reverses the Morse Code lookup table
*/
reverseTable() {
this.reversedTable = {};
for (const letter in MORSE_TABLE) {
const signal = MORSE_TABLE[letter];
this.reversedTable[signal] = letter;
}
}
}
const MORSE_TABLE = {
"A": "<dot><dash>",
"B": "<dash><dot><dot><dot>",
"C": "<dash><dot><dash><dot>",
"D": "<dash><dot><dot>",
"E": "<dot>",
"F": "<dot><dot><dash><dot>",
"G": "<dash><dash><dot>",
"H": "<dot><dot><dot><dot>",
"I": "<dot><dot>",
"J": "<dot><dash><dash><dash>",
"K": "<dash><dot><dash>",
"L": "<dot><dash><dot><dot>",
"M": "<dash><dash>",
"N": "<dash><dot>",
"O": "<dash><dash><dash>",
"P": "<dot><dash><dash><dot>",
"Q": "<dash><dash><dot><dash>",
"R": "<dot><dash><dot>",
"S": "<dot><dot><dot>",
"T": "<dash>",
"U": "<dot><dot><dash>",
"V": "<dot><dot><dot><dash>",
"W": "<dot><dash><dash>",
"X": "<dash><dot><dot><dash>",
"Y": "<dash><dot><dash><dash>",
"Z": "<dash><dash><dot><dot>",
"1": "<dot><dash><dash><dash><dash>",
"2": "<dot><dot><dash><dash><dash>",
"3": "<dot><dot><dot><dash><dash>",
"4": "<dot><dot><dot><dot><dash>",
"5": "<dot><dot><dot><dot><dot>",
"6": "<dash><dot><dot><dot><dot>",
"7": "<dash><dash><dot><dot><dot>",
"8": "<dash><dash><dash><dot><dot>",
"9": "<dash><dash><dash><dash><dot>",
"0": "<dash><dash><dash><dash><dash>",
".": "<dot><dash><dot><dash><dot><dash>",
",": "<dash><dash><dot><dot><dash><dash>",
":": "<dash><dash><dash><dot><dot><dot>",
";": "<dash><dot><dash><dot><dash><dot>",
"!": "<dash><dot><dash><dot><dash><dash>",
"?": "<dot><dot><dash><dash><dot><dot>",
"'": "<dot><dash><dash><dash><dash><dot>",
"\"": "<dot><dash><dot><dot><dash><dot>",
"/": "<dash><dot><dot><dash><dot>",
"-": "<dash><dot><dot><dot><dot><dash>",
"+": "<dot><dash><dot><dash><dot>",
"(": "<dash><dot><dash><dash><dot>",
")": "<dash><dot><dash><dash><dot><dash>",
"@": "<dot><dash><dash><dot><dash><dot>",
"=": "<dash><dot><dot><dot><dash>",
"&": "<dot><dash><dot><dot><dot>",
"_": "<dot><dot><dash><dash><dot><dash>",
"$": "<dot><dot><dot><dash><dot><dot><dash>"
};
export default FromMorseCode;

View file

@ -0,0 +1,81 @@
/**
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {DELIM_OPTIONS} from "../lib/Delim";
/**
* From Octal operation
*/
class FromOctal extends Operation {
/**
* FromOctal constructor
*/
constructor() {
super();
this.name = "From Octal";
this.module = "Default";
this.description = "Converts an octal byte string back into its raw value.<br><br>e.g. <code>316 223 316 265 316 271 316 254 40 317 203 316 277 317 205</code> becomes the UTF-8 encoded string <code>Γειά σου</code>";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
"name": "Delimiter",
"type": "option",
"value": DELIM_OPTIONS
}
];
this.patterns = [
{
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?: (?:[0-7]{1,2}|[123][0-7]{2}))*$",
flags: "",
args: ["Space"]
},
{
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:,(?:[0-7]{1,2}|[123][0-7]{2}))*$",
flags: "",
args: ["Comma"]
},
{
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:;(?:[0-7]{1,2}|[123][0-7]{2}))*$",
flags: "",
args: ["Semi-colon"]
},
{
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?::(?:[0-7]{1,2}|[123][0-7]{2}))*$",
flags: "",
args: ["Colon"]
},
{
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:\\n(?:[0-7]{1,2}|[123][0-7]{2}))*$",
flags: "",
args: ["Line feed"]
},
{
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:\\r\\n(?:[0-7]{1,2}|[123][0-7]{2}))*$",
flags: "",
args: ["CRLF"]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const delim = Utils.charRep(args[0] || "Space");
if (input.length === 0) return [];
return input.split(delim).map(val => parseInt(val, 8));
}
}
export default FromOctal;

View file

@ -0,0 +1,52 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import punycode from "punycode";
/**
* From Punycode operation
*/
class FromPunycode extends Operation {
/**
* FromPunycode constructor
*/
constructor() {
super();
this.name = "From Punycode";
this.module = "Encodings";
this.description = "Punycode is a way to represent Unicode with the limited character subset of ASCII supported by the Domain Name System.<br><br>e.g. <code>mnchen-3ya</code> decodes to <code>m\xfcnchen</code>";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Internationalised domain name",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const idn = args[0];
if (idn) {
return punycode.toUnicode(input);
} else {
return punycode.decode(input);
}
}
}
export default FromPunycode;

View file

@ -0,0 +1,68 @@
/**
* Some parts taken from mimelib (http://github.com/andris9/mimelib)
* @author Andris Reinman
* @license MIT
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* From Quoted Printable operation
*/
class FromQuotedPrintable extends Operation {
/**
* FromQuotedPrintable constructor
*/
constructor() {
super();
this.name = "From Quoted Printable";
this.module = "Default";
this.description = "Converts QP-encoded text back to standard text.";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [];
this.patterns = [
{
match: "^[\\x21-\\x3d\\x3f-\\x7e \\t]*(?:=[\\da-f]{2}|=\\r?\\n)(?:[\\x21-\\x3d\\x3f-\\x7e \\t]|=[\\da-f]{2}|=\\r?\\n)*$",
flags: "i",
args: []
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const str = input.replace(/=(?:\r?\n|$)/g, "");
const encodedBytesCount = (str.match(/=[\da-fA-F]{2}/g) || []).length,
bufferLength = str.length - encodedBytesCount * 2,
buffer = new Array(bufferLength);
let chr, hex,
bufferPos = 0;
for (let i = 0, len = str.length; i < len; i++) {
chr = str.charAt(i);
if (chr === "=" && (hex = str.substr(i + 1, 2)) && /[\da-fA-F]{2}/.test(hex)) {
buffer[bufferPos++] = parseInt(hex, 16);
i += 2;
continue;
}
buffer[bufferPos++] = chr.charCodeAt(0);
}
return buffer;
}
}
export default FromQuotedPrintable;

View file

@ -0,0 +1,91 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import moment from "moment-timezone";
import {UNITS} from "../lib/DateTime";
import OperationError from "../errors/OperationError";
/**
* From UNIX Timestamp operation
*/
class FromUNIXTimestamp extends Operation {
/**
* FromUNIXTimestamp constructor
*/
constructor() {
super();
this.name = "From UNIX Timestamp";
this.module = "Default";
this.description = "Converts a UNIX timestamp to a datetime string.<br><br>e.g. <code>978346800</code> becomes <code>Mon 1 January 2001 11:00:00 UTC</code><br><br>A UNIX timestamp is a 32-bit value representing the number of seconds since January 1, 1970 UTC (the UNIX epoch).";
this.inputType = "number";
this.outputType = "string";
this.args = [
{
"name": "Units",
"type": "option",
"value": UNITS
}
];
this.patterns = [
{
match: "^1?\\d{9}$",
flags: "",
args: ["Seconds (s)"]
},
{
match: "^1?\\d{12}$",
flags: "",
args: ["Milliseconds (ms)"]
},
{
match: "^1?\\d{15}$",
flags: "",
args: ["Microseconds (μs)"]
},
{
match: "^1?\\d{18}$",
flags: "",
args: ["Nanoseconds (ns)"]
},
];
}
/**
* @param {number} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if invalid unit
*/
run(input, args) {
const units = args[0];
let d;
input = parseFloat(input);
if (units === "Seconds (s)") {
d = moment.unix(input);
return d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss") + " UTC";
} else if (units === "Milliseconds (ms)") {
d = moment(input);
return d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss.SSS") + " UTC";
} else if (units === "Microseconds (μs)") {
d = moment(input / 1000);
return d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss.SSS") + " UTC";
} else if (units === "Nanoseconds (ns)") {
d = moment(input / 1000000);
return d.tz("UTC").format("ddd D MMMM YYYY HH:mm:ss.SSS") + " UTC";
} else {
throw new OperationError("Unrecognised unit");
}
}
}
export default FromUNIXTimestamp;

View file

@ -0,0 +1,104 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import MD2 from "./MD2";
import MD4 from "./MD4";
import MD5 from "./MD5";
import MD6 from "./MD6";
import SHA0 from "./SHA0";
import SHA1 from "./SHA1";
import SHA2 from "./SHA2";
import SHA3 from "./SHA3";
import Keccak from "./Keccak";
import Shake from "./Shake";
import RIPEMD from "./RIPEMD";
import HAS160 from "./HAS160";
import Whirlpool from "./Whirlpool";
import SSDEEP from "./SSDEEP";
import CTPH from "./CTPH";
import Fletcher8Checksum from "./Fletcher8Checksum";
import Fletcher16Checksum from "./Fletcher16Checksum";
import Fletcher32Checksum from "./Fletcher32Checksum";
import Fletcher64Checksum from "./Fletcher64Checksum";
import Adler32Checksum from "./Adler32Checksum";
import CRC16Checksum from "./CRC16Checksum";
import CRC32Checksum from "./CRC32Checksum";
/**
* Generate all hashes operation
*/
class GenerateAllHashes extends Operation {
/**
* GenerateAllHashes constructor
*/
constructor() {
super();
this.name = "Generate all hashes";
this.module = "Hashing";
this.description = "Generates all available hashes and checksums for the input.";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const arrayBuffer = input,
str = Utils.arrayBufferToStr(arrayBuffer, false),
byteArray = new Uint8Array(arrayBuffer),
output = "MD2: " + (new MD2()).run(arrayBuffer, []) +
"\nMD4: " + (new MD4()).run(arrayBuffer, []) +
"\nMD5: " + (new MD5()).run(arrayBuffer, []) +
"\nMD6: " + (new MD6()).run(str, []) +
"\nSHA0: " + (new SHA0()).run(arrayBuffer, []) +
"\nSHA1: " + (new SHA1()).run(arrayBuffer, []) +
"\nSHA2 224: " + (new SHA2()).run(arrayBuffer, ["224"]) +
"\nSHA2 256: " + (new SHA2()).run(arrayBuffer, ["256"]) +
"\nSHA2 384: " + (new SHA2()).run(arrayBuffer, ["384"]) +
"\nSHA2 512: " + (new SHA2()).run(arrayBuffer, ["512"]) +
"\nSHA3 224: " + (new SHA3()).run(arrayBuffer, ["224"]) +
"\nSHA3 256: " + (new SHA3()).run(arrayBuffer, ["256"]) +
"\nSHA3 384: " + (new SHA3()).run(arrayBuffer, ["384"]) +
"\nSHA3 512: " + (new SHA3()).run(arrayBuffer, ["512"]) +
"\nKeccak 224: " + (new Keccak()).run(arrayBuffer, ["224"]) +
"\nKeccak 256: " + (new Keccak()).run(arrayBuffer, ["256"]) +
"\nKeccak 384: " + (new Keccak()).run(arrayBuffer, ["384"]) +
"\nKeccak 512: " + (new Keccak()).run(arrayBuffer, ["512"]) +
"\nShake 128: " + (new Shake()).run(arrayBuffer, ["128", 256]) +
"\nShake 256: " + (new Shake()).run(arrayBuffer, ["256", 512]) +
"\nRIPEMD-128: " + (new RIPEMD()).run(arrayBuffer, ["128"]) +
"\nRIPEMD-160: " + (new RIPEMD()).run(arrayBuffer, ["160"]) +
"\nRIPEMD-256: " + (new RIPEMD()).run(arrayBuffer, ["256"]) +
"\nRIPEMD-320: " + (new RIPEMD()).run(arrayBuffer, ["320"]) +
"\nHAS-160: " + (new HAS160()).run(arrayBuffer, []) +
"\nWhirlpool-0: " + (new Whirlpool()).run(arrayBuffer, ["Whirlpool-0"]) +
"\nWhirlpool-T: " + (new Whirlpool()).run(arrayBuffer, ["Whirlpool-T"]) +
"\nWhirlpool: " + (new Whirlpool()).run(arrayBuffer, ["Whirlpool"]) +
"\nSSDEEP: " + (new SSDEEP()).run(str) +
"\nCTPH: " + (new CTPH()).run(str) +
"\n\nChecksums:" +
"\nFletcher-8: " + (new Fletcher8Checksum).run(byteArray, []) +
"\nFletcher-16: " + (new Fletcher16Checksum).run(byteArray, []) +
"\nFletcher-32: " + (new Fletcher32Checksum).run(byteArray, []) +
"\nFletcher-64: " + (new Fletcher64Checksum).run(byteArray, []) +
"\nAdler-32: " + (new Adler32Checksum).run(byteArray, []) +
"\nCRC-16: " + (new CRC16Checksum).run(str, []) +
"\nCRC-32: " + (new CRC32Checksum).run(str, []);
return output;
}
}
export default GenerateAllHashes;

View file

@ -0,0 +1,69 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
import otp from "otp";
import ToBase32 from "./ToBase32";
/**
* Generate HOTP operation
*/
class GenerateHOTP extends Operation {
/**
* GenerateHOTP constructor
*/
constructor() {
super();
this.name = "Generate HOTP";
this.module = "Default";
this.description = "The HMAC-based One-Time Password algorithm (HOTP) is an algorithm that computes a one-time password from a shared secret key and an incrementing counter. It has been adopted as Internet Engineering Task Force standard RFC 4226, is the cornerstone of Initiative For Open Authentication (OATH), and is used in a number of two-factor authentication systems.<br><br>Enter the secret as the input or leave it blank for a random secret to be generated.";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [
{
"name": "Name",
"type": "string",
"value": ""
},
{
"name": "Key size",
"type": "number",
"value": 32
},
{
"name": "Code length",
"type": "number",
"value": 6
},
{
"name": "Counter",
"type": "number",
"value": 0
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const otpObj = otp({
name: args[0],
keySize: args[1],
codeLength: args[2],
secret: (new ToBase32).run(input, []),
});
const counter = args[3];
return `URI: ${otpObj.hotpURL}\n\nPassword: ${otpObj.hotp(counter)}`;
}
}
export default GenerateHOTP;

View file

@ -0,0 +1,116 @@
/**
* @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 kbpgp from "kbpgp";
import { getSubkeySize, ASP } from "../lib/PGP";
import promisifyDefault from "es6-promisify";
const promisify = promisifyDefault.promisify;
/**
* 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,75 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
import otp from "otp";
import ToBase32 from "./ToBase32";
/**
* Generate TOTP operation
*/
class GenerateTOTP extends Operation {
/**
* GenerateTOTP constructor
*/
constructor() {
super();
this.name = "Generate TOTP";
this.module = "Default";
this.description = "The Time-based One-Time Password algorithm (TOTP) is an algorithm that computes a one-time password from a shared secret key and the current time. It has been adopted as Internet Engineering Task Force standard RFC 6238, is the cornerstone of Initiative For Open Authentication (OATH), and is used in a number of two-factor authentication systems. A TOTP is an HOTP where the counter is the current time.<br><br>Enter the secret as the input or leave it blank for a random secret to be generated. T0 and T1 are in seconds.";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [
{
"name": "Name",
"type": "string",
"value": ""
},
{
"name": "Key size",
"type": "number",
"value": 32
},
{
"name": "Code length",
"type": "number",
"value": 6
},
{
"name": "Epoch offset (T0)",
"type": "number",
"value": 0
},
{
"name": "Interval (T1)",
"type": "number",
"value": 30
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const otpObj = otp({
name: args[0],
keySize: args[1],
codeLength: args[2],
secret: (new ToBase32).run(input, []),
epoch: args[3],
timeSlice: args[4]
});
return `URI: ${otpObj.totpURL}\n\nPassword: ${otpObj.totp()}`;
}
}
export default GenerateTOTP;

View file

@ -0,0 +1,49 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import crypto from "crypto";
/**
* Generate UUID operation
*/
class GenerateUUID extends Operation {
/**
* GenerateUUID constructor
*/
constructor() {
super();
this.name = "Generate UUID";
this.module = "Crypto";
this.description = "Generates an RFC 4122 version 4 compliant Universally Unique Identifier (UUID), also known as a Globally Unique Identifier (GUID).<br><br>A version 4 UUID relies on random numbers, in this case generated using <code>window.crypto</code> if available and falling back to <code>Math.random</code> if not.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const buf = new Uint32Array(4).map(() => {
return crypto.randomBytes(4).readUInt32BE(0, true);
});
let i = 0;
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
const r = (buf[i >> 3] >> ((i % 8) * 4)) & 0xf,
v = c === "x" ? r : (r & 0x3 | 0x8);
i++;
return v.toString(16);
});
}
}
export default GenerateUUID;

View file

@ -26,6 +26,13 @@ class Gunzip extends Operation {
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.args = [];
this.patterns = [
{
match: "^\\x1f\\x8b\\x08",
flags: "",
args: []
},
];
}
/**

View file

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {runHash} from "../lib/Hash";
/**
* HAS-160 operation
*/
class HAS160 extends Operation {
/**
* HAS-160 constructor
*/
constructor() {
super();
this.name = "HAS-160";
this.module = "Hashing";
this.description = "HAS-160 is a cryptographic hash function designed for use with the Korean KCDSA digital signature algorithm. It is derived from SHA-1, with assorted changes intended to increase its security. It produces a 160-bit output.<br><br>HAS-160 is used in the same way as SHA-1. First it divides input in blocks of 512 bits each and pads the final block. A digest function updates the intermediate hash value by processing the input blocks in turn.<br><br>The message digest algorithm consists of 80 rounds.";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return runHash("has160", input);
}
}
export default HAS160;

View file

@ -0,0 +1,87 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import CryptoApi from "crypto-api/src/crypto-api";
/**
* HMAC operation
*/
class HMAC extends Operation {
/**
* HMAC constructor
*/
constructor() {
super();
this.name = "HMAC";
this.module = "Hashing";
this.description = "Keyed-Hash Message Authentication Codes (HMAC) are a mechanism for message authentication using cryptographic hash functions.";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
"name": "Key",
"type": "binaryString",
"value": ""
},
{
"name": "Hashing function",
"type": "option",
"value": [
"MD2",
"MD4",
"MD5",
"SHA0",
"SHA1",
"SHA224",
"SHA256",
"SHA384",
"SHA512",
"SHA512/224",
"SHA512/256",
"RIPEMD128",
"RIPEMD160",
"RIPEMD256",
"RIPEMD320",
"HAS160",
"Whirlpool",
"Whirlpool-0",
"Whirlpool-T",
"Snefru"
]
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const key = args[0],
hashFunc = args[1].toLowerCase(),
msg = Utils.arrayBufferToStr(input, false),
hasher = CryptoApi.getHasher(hashFunc);
// Horrible shim to fix constructor bug. Reported in nf404/crypto-api#8
hasher.reset = () => {
hasher.state = {};
const tmp = new hasher.constructor();
hasher.state = tmp.state;
};
const mac = CryptoApi.getHmac(CryptoApi.encoder.fromUtf(key), hasher);
mac.update(msg);
return CryptoApi.encoder.toHex(mac.finalize());
}
}
export default HMAC;

View file

@ -0,0 +1,146 @@
/**
* @author tlwr [toby@toby.codes]
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
/**
* HTTP request operation
*/
class HTTPRequest extends Operation {
/**
* HTTPRequest constructor
*/
constructor() {
super();
this.name = "HTTP request";
this.module = "Default";
this.description = [
"Makes an HTTP request and returns the response.",
"<br><br>",
"This operation supports different HTTP verbs like GET, POST, PUT, etc.",
"<br><br>",
"You can add headers line by line in the format <code>Key: Value</code>",
"<br><br>",
"The status code of the response, along with a limited selection of exposed headers, can be viewed by checking the 'Show response metadata' option. Only a limited set of response headers are exposed by the browser for security reasons.",
].join("\n");
this.inputType = "string";
this.outputType = "string";
this.manualBake = true;
this.args = [
{
"name": "Method",
"type": "option",
"value": [
"GET", "POST", "HEAD",
"PUT", "PATCH", "DELETE",
"CONNECT", "TRACE", "OPTIONS"
]
},
{
"name": "URL",
"type": "string",
"value": ""
},
{
"name": "Headers",
"type": "text",
"value": ""
},
{
"name": "Mode",
"type": "option",
"value": [
"Cross-Origin Resource Sharing",
"No CORS (limited to HEAD, GET or POST)",
]
},
{
"name": "Show response metadata",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [method, url, headersText, mode, showResponseMetadata] = args;
if (url.length === 0) return "";
const headers = new Headers();
headersText.split(/\r?\n/).forEach(line => {
line = line.trim();
if (line.length === 0) return;
const split = line.split(":");
if (split.length !== 2) throw `Could not parse header in line: ${line}`;
headers.set(split[0].trim(), split[1].trim());
});
const config = {
method: method,
headers: headers,
mode: modeLookup[mode],
cache: "no-cache",
};
if (method !== "GET" && method !== "HEAD") {
config.body = input;
}
return fetch(url, config)
.then(r => {
if (r.status === 0 && r.type === "opaque") {
throw new OperationError("Error: Null response. Try setting the connection mode to CORS.");
}
if (showResponseMetadata) {
let headers = "";
for (const pair of r.headers.entries()) {
headers += " " + pair[0] + ": " + pair[1] + "\n";
}
return r.text().then(b => {
return "####\n Status: " + r.status + " " + r.statusText +
"\n Exposed headers:\n" + headers + "####\n\n" + b;
});
}
return r.text();
})
.catch(e => {
throw new OperationError(e.toString() +
"\n\nThis error could be caused by one of the following:\n" +
" - An invalid URL\n" +
" - Making a request to an insecure resource (HTTP) from a secure source (HTTPS)\n" +
" - Making a cross-origin request to a server which does not support CORS\n");
});
}
}
/**
* Lookup table for HTTP modes
*
* @private
*/
const modeLookup = {
"Cross-Origin Resource Sharing": "cors",
"No CORS (limited to HEAD, GET or POST)": "no-cors",
};
export default HTTPRequest;

View file

@ -0,0 +1,97 @@
/**
* @author GCHQ Contributor [2]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {fromHex} from "../lib/Hex";
import OperationError from "../errors/OperationError";
/**
* Hamming Distance operation
*/
class HammingDistance extends Operation {
/**
* HammingDistance constructor
*/
constructor() {
super();
this.name = "Hamming Distance";
this.module = "Default";
this.description = "In information theory, the Hamming distance between two strings of equal length is the number of positions at which the corresponding symbols are different. In other words, it measures the minimum number of substitutions required to change one string into the other, or the minimum number of errors that could have transformed one string into the other. In a more general context, the Hamming distance is one of several string metrics for measuring the edit distance between two sequences.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Delimiter",
"type": "binaryShortString",
"value": "\\n\\n"
},
{
"name": "Unit",
"type": "option",
"value": ["Byte", "Bit"]
},
{
"name": "Input type",
"type": "option",
"value": ["Raw string", "Hex"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const delim = args[0],
byByte = args[1] === "Byte",
inputType = args[2],
samples = input.split(delim);
if (samples.length !== 2) {
throw new OperationError("Error: You can only calculae the edit distance between 2 strings. Please ensure exactly two inputs are provided, separated by the specified delimiter.");
}
if (samples[0].length !== samples[1].length) {
throw new OperationError("Error: Both inputs must be of the same length.");
}
if (inputType === "Hex") {
samples[0] = fromHex(samples[0]);
samples[1] = fromHex(samples[1]);
} else {
samples[0] = Utils.strToByteArray(samples[0]);
samples[1] = Utils.strToByteArray(samples[1]);
}
let dist = 0;
for (let i = 0; i < samples[0].length; i++) {
const lhs = samples[0][i],
rhs = samples[1][i];
if (byByte && lhs !== rhs) {
dist++;
} else if (!byByte) {
let xord = lhs ^ rhs;
while (xord) {
dist++;
xord &= xord - 1;
}
}
}
return dist.toString();
}
}
export default HammingDistance;

View file

@ -0,0 +1,68 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {INPUT_DELIM_OPTIONS} from "../lib/Delim";
/**
* Head operation
*/
class Head extends Operation {
/**
* Head constructor
*/
constructor() {
super();
this.name = "Head";
this.module = "Default";
this.description = "Like the UNIX head utility.<br>Gets the first n lines.<br>You can select all but the last n lines by entering a negative value for n.<br>The delimiter can be changed so that instead of lines, fields (i.e. commas) are selected instead.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Delimiter",
"type": "option",
"value": INPUT_DELIM_OPTIONS
},
{
"name": "Number",
"type": "number",
"value": 10
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let delimiter = args[0];
const number = args[1];
delimiter = Utils.charRep(delimiter);
const splitInput = input.split(delimiter);
return splitInput
.filter((line, lineIndex) => {
lineIndex += 1;
if (number < 0) {
return lineIndex <= splitInput.length + number;
} else {
return lineIndex <= number;
}
})
.join(delimiter);
}
}
export default Head;

View file

@ -0,0 +1,65 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import { getLabelIndex } from "../lib/FlowControl";
/**
* Jump operation
*/
class Jump extends Operation {
/**
* Jump constructor
*/
constructor() {
super();
this.name = "Jump";
this.flowControl = true;
this.module = "Default";
this.description = "Jump forwards or backwards to the specified Label";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Label name",
"type": "string",
"value": ""
},
{
"name": "Maximum jumps (if jumping backwards)",
"type": "number",
"value": 10
}
];
}
/**
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @param {number} state.numJumps - The number of jumps taken so far.
* @returns {Object} The updated state of the recipe.
*/
run(state) {
const ings = state.opList[state.progress].ingValues;
const [label, maxJumps] = ings;
const jmpIndex = getLabelIndex(label, state);
if (state.numJumps >= maxJumps || jmpIndex === -1) {
return state;
}
state.progress = jmpIndex;
state.numJumps++;
return state;
}
}
export default Jump;

View file

@ -0,0 +1,67 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import JSSHA3 from "js-sha3";
import OperationError from "../errors/OperationError";
/**
* Keccak operation
*/
class Keccak extends Operation {
/**
* Keccak constructor
*/
constructor() {
super();
this.name = "Keccak";
this.module = "Hashing";
this.description = "The Keccak hash algorithm was designed by Guido Bertoni, Joan Daemen, Micha\xebl Peeters, and Gilles Van Assche, building upon RadioGat\xfan. It was selected as the winner of the SHA-3 design competition.<br><br>This version of the algorithm is Keccak[c=2d] and differs from the SHA-3 specification.";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
"name": "Size",
"type": "option",
"value": ["512", "384", "256", "224"]
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const size = parseInt(args[0], 10);
let algo;
switch (size) {
case 224:
algo = JSSHA3.keccak224;
break;
case 384:
algo = JSSHA3.keccak384;
break;
case 256:
algo = JSSHA3.keccak256;
break;
case 512:
algo = JSSHA3.keccak512;
break;
default:
throw new OperationError("Invalid size");
}
return algo(input);
}
}
export default Keccak;

View file

@ -0,0 +1,48 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Label operation. For use with Jump and Conditional Jump.
*/
class Label extends Operation {
/**
* Label constructor
*/
constructor() {
super();
this.name = "Label";
this.flowControl = true;
this.module = "Default";
this.description = "Provides a location for conditional and fixed jumps to redirect execution to.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Name",
"type": "shortString",
"value": ""
}
];
}
/**
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @returns {Object} The updated state of the recipe.
*/
run(state) {
return state;
}
}
export default Label;

View file

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {runHash} from "../lib/Hash";
/**
* MD2 operation
*/
class MD2 extends Operation {
/**
* MD2 constructor
*/
constructor() {
super();
this.name = "MD2";
this.module = "Hashing";
this.description = "The MD2 (Message-Digest 2) algorithm is a cryptographic hash function developed by Ronald Rivest in 1989. The algorithm is optimized for 8-bit computers.<br><br>Although MD2 is no longer considered secure, even as of 2014, it remains in use in public key infrastructures as part of certificates generated with MD2 and RSA.";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return runHash("md2", input);
}
}
export default MD2;

View file

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {runHash} from "../lib/Hash";
/**
* MD4 operation
*/
class MD4 extends Operation {
/**
* MD4 constructor
*/
constructor() {
super();
this.name = "MD4";
this.module = "Hashing";
this.description = "The MD4 (Message-Digest 4) algorithm is a cryptographic hash function developed by Ronald Rivest in 1990. The digest length is 128 bits. The algorithm has influenced later designs, such as the MD5, SHA-1 and RIPEMD algorithms.<br><br>The security of MD4 has been severely compromised.";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return runHash("md4", input);
}
}
export default MD4;

View file

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {runHash} from "../lib/Hash";
/**
* MD5 operation
*/
class MD5 extends Operation {
/**
* MD5 constructor
*/
constructor() {
super();
this.name = "MD5";
this.module = "Hashing";
this.description = "MD5 (Message-Digest 5) is a widely used hash function. It has been used in a variety of security applications and is also commonly used to check the integrity of files.<br><br>However, MD5 is not collision resistant and it isn't suitable for applications like SSL/TLS certificates or digital signatures that rely on this property.";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return runHash("md5", input);
}
}
export default MD5;

View file

@ -0,0 +1,64 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import NodeMD6 from "node-md6";
/**
* MD6 operation
*/
class MD6 extends Operation {
/**
* MD6 constructor
*/
constructor() {
super();
this.name = "MD6";
this.module = "Hashing";
this.description = "The MD6 (Message-Digest 6) algorithm is a cryptographic hash function. It uses a Merkle tree-like structure to allow for immense parallel computation of hashes for very long inputs.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Size",
"type": "number",
"value": 256
},
{
"name": "Levels",
"type": "number",
"value": 64
},
{
"name": "Key",
"type": "string",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [size, levels, key] = args;
if (size < 0 || size > 512)
throw new OperationError("Size must be between 0 and 512");
if (levels < 0)
throw new OperationError("Levels must be greater than 0");
return NodeMD6.getHashOfText(input, size, key, levels);
}
}
export default MD6;

View file

@ -0,0 +1,140 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Dish from "../Dish";
import MagicLib from "../lib/Magic";
/**
* Magic operation
*/
class Magic extends Operation {
/**
* Magic constructor
*/
constructor() {
super();
this.name = "Magic";
this.flowControl = true;
this.module = "Default";
this.description = "The Magic operation attempts to detect various properties of the input data and suggests which operations could help to make more sense of it.<br><br><b>Options</b><br><u>Depth:</u> If an operation appears to match the data, it will be run and the result will be analysed further. This argument controls the maximum number of levels of recursion.<br><br><u>Intensive mode:</u> When this is turned on, various operations like XOR, bit rotates, and character encodings are brute-forced to attempt to detect valid data underneath. To improve performance, only the first 100 bytes of the data is brute-forced.<br><br><u>Extensive language support:</u> At each stage, the relative byte frequencies of the data will be compared to average frequencies for a number of languages. The default set consists of ~40 of the most commonly used languages on the Internet. The extensive list consists of 284 languages and can result in many languages matching the data if their byte frequencies are similar.";
this.inputType = "ArrayBuffer";
this.outputType = "html";
this.args = [
{
"name": "Depth",
"type": "number",
"value": 3
},
{
"name": "Intensive mode",
"type": "boolean",
"value": false
},
{
"name": "Extensive language support",
"type": "boolean",
"value": false
}
];
}
/**
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @returns {Object} The updated state of the recipe.
*/
async run(state) {
const ings = state.opList[state.progress].ingValues,
[depth, intensive, extLang] = ings,
dish = state.dish,
currentRecipeConfig = state.opList.map(op => op.config),
magic = new MagicLib(await dish.get(Dish.ARRAY_BUFFER)),
options = await magic.speculativeExecution(depth, extLang, intensive);
let output = `<table
class='table table-hover table-condensed table-bordered'
style='table-layout: fixed;'>
<tr>
<th>Recipe (click to load)</th>
<th>Result snippet</th>
<th>Properties</th>
</tr>`;
/**
* Returns a CSS colour value based on an integer input.
*
* @param {number} val
* @returns {string}
*/
function chooseColour(val) {
if (val < 3) return "green";
if (val < 5) return "goldenrod";
return "red";
}
options.forEach(option => {
// Construct recipe URL
// Replace this Magic op with the generated recipe
const recipeConfig = currentRecipeConfig.slice(0, state.progress)
.concat(option.recipe)
.concat(currentRecipeConfig.slice(state.progress + 1)),
recipeURL = "recipe=" + Utils.encodeURIFragment(Utils.generatePrettyRecipe(recipeConfig));
let language = "",
fileType = "",
matchingOps = "",
useful = "";
const entropy = `<span data-toggle="tooltip" data-container="body" title="Shannon Entropy is measured from 0 to 8. High entropy suggests encrypted or compressed data. Normal text is usually around 3.5 to 5.">Entropy: <span style="color: ${chooseColour(option.entropy)}">${option.entropy.toFixed(2)}</span></span>`,
validUTF8 = option.isUTF8 ? "<span data-toggle='tooltip' data-container='body' title='The data could be a valid UTF8 string based on its encoding.'>Valid UTF8</span>\n" : "";
if (option.languageScores[0].probability > 0) {
let likelyLangs = option.languageScores.filter(l => l.probability > 0);
if (likelyLangs.length < 1) likelyLangs = [option.languageScores[0]];
language = "<span data-toggle='tooltip' data-container='body' title='Based on a statistical comparison of the frequency of bytes in various languages. Ordered by likelihood.'>" +
"Possible languages:\n " +
likelyLangs.map(lang => {
return MagicLib.codeToLanguage(lang.lang);
}).join("\n ") +
"</span>\n";
}
if (option.fileType) {
fileType = `<span data-toggle="tooltip" data-container="body" title="Based on the presence of magic bytes.">File type: ${option.fileType.mime} (${option.fileType.ext})</span>\n`;
}
if (option.matchingOps.length) {
matchingOps = `Matching ops: ${[...new Set(option.matchingOps.map(op => op.op))].join(", ")}\n`;
}
if (option.useful) {
useful = "<span data-toggle='tooltip' data-container='body' title='This could be an operation that displays data in a useful way, such as rendering an image.'>Useful op detected</span>\n";
}
output += `<tr>
<td><a href="#${recipeURL}">${Utils.generatePrettyRecipe(option.recipe, true)}</a></td>
<td>${Utils.escapeHtml(Utils.printable(Utils.truncate(option.data, 99)))}</td>
<td>${language}${fileType}${matchingOps}${useful}${validUTF8}${entropy}</td>
</tr>`;
});
output += "</table><script type='application/javascript'>$('[data-toggle=\"tooltip\"]').tooltip()</script>";
if (!options.length) {
output = "Nothing of interest could be detected about the input data.\nHave you tried modifying the operation arguments?";
}
dish.set(output, Dish.HTML);
return state;
}
}
export default Magic;

View file

@ -0,0 +1,50 @@
/**
* @author bwhitn [brian.m.whitney@outlook.com]
* @author d98762625 [d98762625@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import { mean, createNumArray } from "../lib/Arithmetic";
import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim";
import BigNumber from "bignumber.js";
/**
* Mean operation
*/
class Mean extends Operation {
/**
* Mean constructor
*/
constructor() {
super();
this.name = "Mean";
this.module = "Default";
this.description = "Computes the mean (average) of a number list. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 .5 .5</code> becomes <code>4.75</code>";
this.inputType = "string";
this.outputType = "BigNumber";
this.args = [
{
"name": "Delimiter",
"type": "option",
"value": ARITHMETIC_DELIM_OPTIONS,
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
run(input, args) {
const val = mean(createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
}
}
export default Mean;

View file

@ -0,0 +1,50 @@
/**
* @author bwhitn [brian.m.whitney@outlook.com]
* @author d98762625 [d98762625@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import BigNumber from "bignumber.js";
import Operation from "../Operation";
import { median, createNumArray } from "../lib/Arithmetic";
import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim";
/**
* Median operation
*/
class Median extends Operation {
/**
* Median constructor
*/
constructor() {
super();
this.name = "Median";
this.module = "Default";
this.description = "Computes the median of a number list. If an item in the string is not a number it is excluded from the list.<br><br>e.g. <code>0x0a 8 1 .5</code> becomes <code>4.5</code>";
this.inputType = "string";
this.outputType = "BigNumber";
this.args = [
{
"name": "Delimiter",
"type": "option",
"value": ARITHMETIC_DELIM_OPTIONS,
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
run(input, args) {
const val = median(createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
}
}
export default Median;

Some files were not shown because too many files have changed in this diff Show more