Merge branch 'master' into module-charts

This commit is contained in:
Matt 2019-02-22 23:36:14 +00:00
commit 5bb8eb22ec
568 changed files with 68420 additions and 48547 deletions

View file

@ -0,0 +1,63 @@
/**
* @author Jarmo van Lenthe [github.com/jarmovanlenthe]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {DELIM_OPTIONS} from "../lib/Delim";
import OperationError from "../errors/OperationError";
/**
* A1Z26 Cipher Decode operation
*/
class A1Z26CipherDecode extends Operation {
/**
* A1Z26CipherDecode constructor
*/
constructor() {
super();
this.name = "A1Z26 Cipher Decode";
this.module = "Ciphers";
this.description = "Converts alphabet order numbers into their corresponding alphabet character.<br><br>e.g. <code>1</code> becomes <code>a</code> and <code>2</code> becomes <code>b</code>.";
this.infoURL = "";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "Delimiter",
type: "option",
value: DELIM_OPTIONS
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const delim = Utils.charRep(args[0] || "Space");
if (input.length === 0) {
return [];
}
const bites = input.split(delim);
let latin1 = "";
for (let i = 0; i < bites.length; i++) {
if (bites[i] < 1 || bites[i] > 26) {
throw new OperationError("Error: all numbers must be between 1 and 26.");
}
latin1 += Utils.chr(parseInt(bites[i], 10) + 96);
}
return latin1;
}
}
export default A1Z26CipherDecode;

View file

@ -0,0 +1,61 @@
/**
* @author Jarmo van Lenthe [github.com/jarmovanlenthe]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {DELIM_OPTIONS} from "../lib/Delim";
/**
* A1Z26 Cipher Encode operation
*/
class A1Z26CipherEncode extends Operation {
/**
* A1Z26CipherEncode constructor
*/
constructor() {
super();
this.name = "A1Z26 Cipher Encode";
this.module = "Ciphers";
this.description = "Converts alphabet characters into their corresponding alphabet order number.<br><br>e.g. <code>a</code> becomes <code>1</code> and <code>b</code> becomes <code>2</code>.<br><br>Non-alphabet characters are dropped.";
this.infoURL = "";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "Delimiter",
type: "option",
value: DELIM_OPTIONS
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const delim = Utils.charRep(args[0] || "Space");
let output = "";
const sanitizedinput = input.toLowerCase(),
charcode = Utils.strToCharcode(sanitizedinput);
for (let i = 0; i < charcode.length; i++) {
const ordinal = charcode[i] - 96;
if (ordinal > 0 && ordinal <= 26) {
output += ordinal.toString(10) + delim;
}
}
return output.slice(0, -delim.length);
}
}
export default A1Z26CipherEncode;

View file

@ -0,0 +1,77 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import { bitOp, add, BITWISE_OP_DELIMS } from "../lib/BitwiseOp";
/**
* ADD operation
*/
class ADD extends Operation {
/**
* ADD constructor
*/
constructor() {
super();
this.name = "ADD";
this.module = "Default";
this.description = "ADD the input with the given key (e.g. <code>fe023da5</code>), MOD 255";
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bitwise_operators";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
"name": "Key",
"type": "toggleString",
"value": "",
"toggleValues": BITWISE_OP_DELIMS
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
return bitOp(input, key, add);
}
/**
* Highlight ADD
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight ADD 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 ADD;

View file

@ -0,0 +1,109 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Advanced_Encryption_Standard";
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,107 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Advanced_Encryption_Standard";
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,77 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import { bitOp, and, BITWISE_OP_DELIMS } from "../lib/BitwiseOp";
/**
* AND operation
*/
class AND extends Operation {
/**
* AND constructor
*/
constructor() {
super();
this.name = "AND";
this.module = "Default";
this.description = "AND the input with the given key.<br>e.g. <code>fe023da5</code>";
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#AND";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
"name": "Key",
"type": "toggleString",
"value": "",
"toggleValues": BITWISE_OP_DELIMS
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
return bitOp(input, key, and);
}
/**
* Highlight AND
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight AND 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 AND;

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,53 @@
/**
* @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 = "Crypto";
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.infoURL = "https://wikipedia.org/wiki/Adler-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,106 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Affine_cipher";
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,78 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Affine_cipher";
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,184 @@
/**
* @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 = "Crypto";
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.infoURL = "https://wikipedia.org/wiki/Comparison_of_cryptographic_hash_functions";
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,67 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Atbash";
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,49 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import bson 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.infoURL = "https://wikipedia.org/wiki/BSON";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!input.byteLength) return "";
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,49 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import bson 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.infoURL = "https://wikipedia.org/wiki/BSON";
this.inputType = "string";
this.outputType = "ArrayBuffer";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
run(input, args) {
if (!input) return new ArrayBuffer();
try {
const data = JSON.parse(input);
return bson.serialize(data).buffer;
} catch (err) {
throw new OperationError(err.toString());
}
}
}
export default BSONSerialise;

View file

@ -1,66 +0,0 @@
/**
* Numerical base operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Base = {
/**
* @constant
* @default
*/
DEFAULT_RADIX: 36,
/**
* To Base operation.
*
* @param {number} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
if (!input) {
throw ("Error: Input must be a number");
}
const radix = args[0] || Base.DEFAULT_RADIX;
if (radix < 2 || radix > 36) {
throw "Error: Radix argument must be between 2 and 36";
}
return input.toString(radix);
},
/**
* From Base operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
*/
runFrom: function(input, args) {
const radix = args[0] || Base.DEFAULT_RADIX;
if (radix < 2 || radix > 36) {
throw "Error: Radix argument must be between 2 and 36";
}
let number = input.replace(/\s/g, "").split("."),
result = parseInt(number[0], radix) || 0;
if (number.length === 1) return result;
// Fractional part
for (let i = 0; i < number[1].length; i++) {
const digit = parseInt(number[1][i], radix);
result += digit / Math.pow(radix, i+1);
}
return result;
},
};
export default Base;

View file

@ -1,137 +0,0 @@
import Utils from "../Utils.js";
/**
* Base58 operations.
*
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const Base58 = {
/**
* @constant
* @default
*/
ALPHABET_OPTIONS: [
{
name: "Bitcoin",
value: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
},
{
name: "Ripple",
value: "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz",
},
],
/**
* @constant
* @default
*/
REMOVE_NON_ALPH_CHARS: true,
/**
* To Base58 operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
let alphabet = args[0] || Base58.ALPHABET_OPTIONS[0].value,
result = [0];
alphabet = Utils.expandAlphRange(alphabet).join("");
if (alphabet.length !== 58 ||
[].unique.call(alphabet).length !== 58) {
throw ("Error: alphabet must be of length 58");
}
if (input.length === 0) return "";
input.forEach(function(b) {
let carry = (result[0] << 8) + b;
result[0] = carry % 58;
carry = (carry / 58) | 0;
for (let i = 1; i < result.length; i++) {
carry += result[i] << 8;
result[i] = carry % 58;
carry = (carry / 58) | 0;
}
while (carry > 0) {
result.push(carry % 58);
carry = (carry / 58) | 0;
}
});
result = result.map(function(b) {
return alphabet[b];
}).reverse().join("");
while (result.length < input.length) {
result = alphabet[0] + result;
}
return result;
},
/**
* From Base58 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFrom: function(input, args) {
let alphabet = args[0] || Base58.ALPHABET_OPTIONS[0].value,
removeNonAlphaChars = args[1] === undefined ? true : args[1],
result = [0];
alphabet = Utils.expandAlphRange(alphabet).join("");
if (alphabet.length !== 58 ||
[].unique.call(alphabet).length !== 58) {
throw ("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 ("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 Base58;

View file

@ -1,347 +0,0 @@
import Utils from "../Utils.js";
/**
* Base64 operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Base64 = {
/**
* @constant
* @default
*/
ALPHABET: "A-Za-z0-9+/=",
/**
* @constant
* @default
*/
ALPHABET_OPTIONS: [
{name: "Standard: A-Za-z0-9+/=", value: "A-Za-z0-9+/="},
{name: "URL safe: A-Za-z0-9-_", value: "A-Za-z0-9-_"},
{name: "Filename safe: A-Za-z0-9+-=", value: "A-Za-z0-9+\\-="},
{name: "itoa64: ./0-9A-Za-z=", value: "./0-9A-Za-z="},
{name: "XML: A-Za-z0-9_.", value: "A-Za-z0-9_."},
{name: "y64: A-Za-z0-9._-", value: "A-Za-z0-9._-"},
{name: "z64: 0-9a-zA-Z+/=", value: "0-9a-zA-Z+/="},
{name: "Radix-64: 0-9A-Za-z+/=", value: "0-9A-Za-z+/="},
{name: "Uuencoding: [space]-_", value: " -_"},
{name: "Xxencoding: +-0-9A-Za-z", value: "+\\-0-9A-Za-z"},
{name: "BinHex: !-,-0-689@A-NP-VX-Z[`a-fh-mp-r", value: "!-,-0-689@A-NP-VX-Z[`a-fh-mp-r"},
{name: "ROT13: N-ZA-Mn-za-m0-9+/=", value: "N-ZA-Mn-za-m0-9+/="},
{name: "UNIX crypt: ./0-9A-Za-z", value: "./0-9A-Za-z"},
],
/**
* To Base64 operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
const alphabet = args[0] || Base64.ALPHABET;
return Utils.toBase64(input, alphabet);
},
/**
* @constant
* @default
*/
REMOVE_NON_ALPH_CHARS: true,
/**
* From Base64 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFrom: function(input, args) {
let alphabet = args[0] || Base64.ALPHABET,
removeNonAlphChars = args[1];
return Utils.fromBase64(input, alphabet, "byteArray", removeNonAlphChars);
},
/**
* @constant
* @default
*/
BASE32_ALPHABET: "A-Z2-7=",
/**
* To Base32 operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runTo32: function(input, args) {
if (!input) return "";
let alphabet = args[0] ?
Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
output = "",
chr1, chr2, chr3, chr4, chr5,
enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
i = 0;
while (i < input.length) {
chr1 = input[i++];
chr2 = input[i++];
chr3 = input[i++];
chr4 = input[i++];
chr5 = input[i++];
enc1 = chr1 >> 3;
enc2 = ((chr1 & 7) << 2) | (chr2 >> 6);
enc3 = (chr2 >> 1) & 31;
enc4 = ((chr2 & 1) << 4) | (chr3 >> 4);
enc5 = ((chr3 & 15) << 1) | (chr4 >> 7);
enc6 = (chr4 >> 2) & 31;
enc7 = ((chr4 & 3) << 3) | (chr5 >> 5);
enc8 = chr5 & 31;
if (isNaN(chr2)) {
enc3 = enc4 = enc5 = enc6 = enc7 = enc8 = 32;
} else if (isNaN(chr3)) {
enc5 = enc6 = enc7 = enc8 = 32;
} else if (isNaN(chr4)) {
enc6 = enc7 = enc8 = 32;
} else if (isNaN(chr5)) {
enc8 = 32;
}
output += alphabet.charAt(enc1) + alphabet.charAt(enc2) + alphabet.charAt(enc3) +
alphabet.charAt(enc4) + alphabet.charAt(enc5) + alphabet.charAt(enc6) +
alphabet.charAt(enc7) + alphabet.charAt(enc8);
}
return output;
},
/**
* From Base32 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFrom32: function(input, args) {
if (!input) return [];
let alphabet = args[0] ?
Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
removeNonAlphChars = args[0];
let output = [],
chr1, chr2, chr3, chr4, chr5,
enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
i = 0;
if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[\]\\\-^]/g, "\\$&") + "]", "g");
input = input.replace(re, "");
}
while (i < input.length) {
enc1 = alphabet.indexOf(input.charAt(i++));
enc2 = alphabet.indexOf(input.charAt(i++) || "=");
enc3 = alphabet.indexOf(input.charAt(i++) || "=");
enc4 = alphabet.indexOf(input.charAt(i++) || "=");
enc5 = alphabet.indexOf(input.charAt(i++) || "=");
enc6 = alphabet.indexOf(input.charAt(i++) || "=");
enc7 = alphabet.indexOf(input.charAt(i++) || "=");
enc8 = alphabet.indexOf(input.charAt(i++) || "=");
chr1 = (enc1 << 3) | (enc2 >> 2);
chr2 = ((enc2 & 3) << 6) | (enc3 << 1) | (enc4 >> 4);
chr3 = ((enc4 & 15) << 4) | (enc5 >> 1);
chr4 = ((enc5 & 1) << 7) | (enc6 << 2) | (enc7 >> 3);
chr5 = ((enc7 & 7) << 5) | enc8;
output.push(chr1);
if (enc2 & 3 !== 0 || enc3 !== 32) output.push(chr2);
if (enc4 & 15 !== 0 || enc5 !== 32) output.push(chr3);
if (enc5 & 1 !== 0 || enc6 !== 32) output.push(chr4);
if (enc7 & 7 !== 0 || enc8 !== 32) output.push(chr5);
}
return output;
},
/**
* @constant
* @default
*/
SHOW_IN_BINARY: false,
/**
* @constant
* @default
*/
OFFSETS_SHOW_VARIABLE: true,
/**
* Show Base64 offsets operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {html}
*/
runOffsets: function(input, args) {
let alphabet = args[0] || Base64.ALPHABET,
showVariable = args[1],
offset0 = Utils.toBase64(input, alphabet),
offset1 = Utils.toBase64([0].concat(input), alphabet),
offset2 = Utils.toBase64([0, 0].concat(input), alphabet),
len0 = offset0.indexOf("="),
len1 = offset1.indexOf("="),
len2 = offset2.indexOf("="),
script = "<script type='application/javascript'>$('[data-toggle=\"tooltip\"]').tooltip()</script>",
staticSection = "",
padding = "";
if (input.length < 1) {
return "Please enter a string.";
}
// Highlight offset 0
if (len0 % 4 === 2) {
staticSection = offset0.slice(0, -3);
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hlgreen'>" + offset0.substr(offset0.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset0.substr(offset0.length - 2) + "</span>";
} else if (len0 % 4 === 3) {
staticSection = offset0.slice(0, -2);
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -1)) + "'>" +
staticSection + "</span>" +
"<span class='hlgreen'>" + offset0.substr(offset0.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset0.substr(offset0.length - 1) + "</span>";
} else {
staticSection = offset0;
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet)) + "'>" +
staticSection + "</span>";
}
if (!showVariable) {
offset0 = staticSection;
}
// Highlight offset 1
padding = "<span class='hlred'>" + offset1.substr(0, 1) + "</span>" +
"<span class='hlgreen'>" + offset1.substr(1, 1) + "</span>";
offset1 = offset1.substr(2);
if (len1 % 4 === 2) {
staticSection = offset1.slice(0, -3);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hlgreen'>" + offset1.substr(offset1.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset1.substr(offset1.length - 2) + "</span>";
} else if (len1 % 4 === 3) {
staticSection = offset1.slice(0, -2);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -1)) + "'>" +
staticSection + "</span>" +
"<span class='hlgreen'>" + offset1.substr(offset1.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset1.substr(offset1.length - 1) + "</span>";
} else {
staticSection = offset1;
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1)) + "'>" +
staticSection + "</span>";
}
if (!showVariable) {
offset1 = staticSection;
}
// Highlight offset 2
padding = "<span class='hlred'>" + offset2.substr(0, 2) + "</span>" +
"<span class='hlgreen'>" + offset2.substr(2, 1) + "</span>";
offset2 = offset2.substr(3);
if (len2 % 4 === 2) {
staticSection = offset2.slice(0, -3);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hlgreen'>" + offset2.substr(offset2.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset2.substr(offset2.length - 2) + "</span>";
} else if (len2 % 4 === 3) {
staticSection = offset2.slice(0, -2);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hlgreen'>" + offset2.substr(offset2.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset2.substr(offset2.length - 1) + "</span>";
} else {
staticSection = offset2;
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2)) + "'>" +
staticSection + "</span>";
}
if (!showVariable) {
offset2 = staticSection;
}
return (showVariable ? "Characters highlighted in <span class='hlgreen'>green</span> could change if the input is surrounded by more data." +
"\nCharacters highlighted in <span class='hlred'>red</span> are for padding purposes only." +
"\nUnhighlighted characters are <span data-toggle='tooltip' data-placement='top' title='Tooltip on left'>static</span>." +
"\nHover over the static sections to see what they decode to on their own.\n" +
"\nOffset 0: " + offset0 +
"\nOffset 1: " + offset1 +
"\nOffset 2: " + offset2 +
script :
offset0 + "\n" + offset1 + "\n" + offset2);
},
/**
* Highlight to Base64
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightTo: function(pos, args) {
pos[0].start = Math.floor(pos[0].start / 3 * 4);
pos[0].end = Math.ceil(pos[0].end / 3 * 4);
return pos;
},
/**
* Highlight from Base64
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightFrom: function(pos, args) {
pos[0].start = Math.ceil(pos[0].start / 4 * 3);
pos[0].end = Math.floor(pos[0].end / 4 * 3);
return pos;
},
};
export default Base64;

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 operation
*/
class Bcrypt extends Operation {
/**
* Bcrypt constructor
*/
constructor() {
super();
this.name = "Bcrypt";
this.module = "Crypto";
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.infoURL = "https://wikipedia.org/wiki/Bcrypt";
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,56 @@
/**
* @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 = "Crypto";
this.description = "Tests whether the input matches the given bcrypt hash. To test multiple possible passwords, use the 'Fork' operation.";
this.infoURL = "https://wikipedia.org/wiki/Bcrypt";
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,49 @@
/**
* @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 = "Crypto";
this.description = "Parses a bcrypt hash to determine the number of rounds used, the salt, and the password hash.";
this.infoURL = "https://wikipedia.org/wiki/Bcrypt";
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,125 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Bifid_cipher";
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,130 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Bifid_cipher";
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,76 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bit_shifts";
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,83 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bit_shifts";
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

@ -1,310 +0,0 @@
import Utils from "../Utils.js";
import CryptoJS from "crypto-js";
/**
* Bitwise operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const BitwiseOp = {
/**
* Runs bitwise operations across the input data.
*
* @private
* @param {byteArray} input
* @param {byteArray} key
* @param {function} func - The bitwise calculation to carry out
* @param {boolean} nullPreserving
* @param {string} scheme
* @returns {byteArray}
*/
_bitOp: function (input, key, func, nullPreserving, scheme) {
if (!key || !key.length) key = [0];
let result = [],
x = null,
k = null,
o = null;
for (let i = 0; i < input.length; i++) {
k = key[i % key.length];
o = input[i];
x = nullPreserving && (o === 0 || o === k) ? o : func(o, k);
result.push(x);
if (scheme !== "Standard" && !(nullPreserving && (o === 0 || o === k))) {
switch (scheme) {
case "Input differential":
key[i % key.length] = x;
break;
case "Output differential":
key[i % key.length] = o;
break;
}
}
}
return result;
},
/**
* @constant
* @default
*/
XOR_PRESERVE_NULLS: false,
/**
* @constant
* @default
*/
XOR_SCHEME: ["Standard", "Input differential", "Output differential"],
/**
* @constant
* @default
*/
KEY_FORMAT: ["Hex", "Base64", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1"],
/**
* XOR operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runXor: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || ""),
scheme = args[1],
nullPreserving = args[2];
key = Utils.wordArrayToByteArray(key);
return BitwiseOp._bitOp(input, key, BitwiseOp._xor, nullPreserving, scheme);
},
/**
* @constant
* @default
*/
XOR_BRUTE_KEY_LENGTH: ["1", "2"],
/**
* @constant
* @default
*/
XOR_BRUTE_SAMPLE_LENGTH: 100,
/**
* @constant
* @default
*/
XOR_BRUTE_SAMPLE_OFFSET: 0,
/**
* @constant
* @default
*/
XOR_BRUTE_PRINT_KEY: true,
/**
* @constant
* @default
*/
XOR_BRUTE_OUTPUT_HEX: false,
/**
* XOR Brute Force operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runXorBrute: function (input, args) {
let keyLength = parseInt(args[0], 10),
sampleLength = args[1],
sampleOffset = args[2],
nullPreserving = args[3],
differential = args[4],
crib = args[5],
printKey = args[6],
outputHex = args[7],
regex;
let output = "",
result,
resultUtf8;
input = input.slice(sampleOffset, sampleOffset + sampleLength);
if (crib !== "") {
regex = new RegExp(crib, "im");
}
for (let key = 1, l = Math.pow(256, keyLength); key < l; key++) {
result = BitwiseOp._bitOp(input, Utils.hexToByteArray(key.toString(16)), BitwiseOp._xor, nullPreserving, differential);
resultUtf8 = Utils.byteArrayToUtf8(result);
if (crib !== "" && resultUtf8.search(regex) === -1) continue;
if (printKey) output += "Key = " + Utils.hex(key, (2*keyLength)) + ": ";
if (outputHex)
output += Utils.byteArrayToHex(result) + "\n";
else
output += Utils.printable(resultUtf8, false) + "\n";
if (printKey) output += "\n";
}
return output;
},
/**
* NOT operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runNot: function (input, args) {
return BitwiseOp._bitOp(input, null, BitwiseOp._not);
},
/**
* AND operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runAnd: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || "");
key = Utils.wordArrayToByteArray(key);
return BitwiseOp._bitOp(input, key, BitwiseOp._and);
},
/**
* OR operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runOr: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || "");
key = Utils.wordArrayToByteArray(key);
return BitwiseOp._bitOp(input, key, BitwiseOp._or);
},
/**
* ADD operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runAdd: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || "");
key = Utils.wordArrayToByteArray(key);
return BitwiseOp._bitOp(input, key, BitwiseOp._add);
},
/**
* SUB operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runSub: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || "");
key = Utils.wordArrayToByteArray(key);
return BitwiseOp._bitOp(input, key, BitwiseOp._sub);
},
/**
* XOR bitwise calculation.
*
* @private
* @param {number} operand
* @param {number} key
* @returns {number}
*/
_xor: function (operand, key) {
return operand ^ key;
},
/**
* NOT bitwise calculation.
*
* @private
* @param {number} operand
* @returns {number}
*/
_not: function (operand, _) {
return ~operand & 0xff;
},
/**
* AND bitwise calculation.
*
* @private
* @param {number} operand
* @param {number} key
* @returns {number}
*/
_and: function (operand, key) {
return operand & key;
},
/**
* OR bitwise calculation.
*
* @private
* @param {number} operand
* @param {number} key
* @returns {number}
*/
_or: function (operand, key) {
return operand | key;
},
/**
* ADD bitwise calculation.
*
* @private
* @param {number} operand
* @param {number} key
* @returns {number}
*/
_add: function (operand, key) {
return (operand + key) % 256;
},
/**
* SUB bitwise calculation.
*
* @private
* @param {number} operand
* @param {number} key
* @returns {number}
*/
_sub: function (operand, key) {
const result = operand - key;
return (result < 0) ? 256 + result : result;
},
};
export default BitwiseOp;

View file

@ -0,0 +1,101 @@
/**
* @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";
import { Blowfish } from "../vendor/Blowfish";
import { toBase64 } from "../lib/Base64";
import { toHexFast } from "../lib/Hex";
/**
* Lookup table for Blowfish output types.
*/
const BLOWFISH_OUTPUT_TYPE_LOOKUP = {
Base64: 0, Hex: 1, String: 2, Raw: 3
};
/**
* Lookup table for Blowfish modes.
*/
const BLOWFISH_MODE_LOOKUP = {
ECB: 0, CBC: 1, PCBC: 2, CFB: 3, OFB: 4, CTR: 5
};
/**
* Blowfish Decrypt operation
*/
class BlowfishDecrypt extends Operation {
/**
* BlowfishDecrypt constructor
*/
constructor() {
super();
this.name = "Blowfish Decrypt";
this.module = "Ciphers";
this.description = "Blowfish is a symmetric-key block cipher designed in 1993 by Bruce Schneier and included in a large number of cipher suites and encryption products. AES now receives more attention.<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.";
this.infoURL = "https://wikipedia.org/wiki/Blowfish_(cipher)";
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", "PCBC", "CFB", "OFB", "CTR", "ECB"]
},
{
"name": "Input",
"type": "option",
"value": ["Hex", "Base64", "Raw"]
},
{
"name": "Output",
"type": "option",
"value": ["Raw", "Hex"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
[,, mode, inputType, outputType] = args;
if (key.length === 0) throw new OperationError("Enter a key");
input = inputType === "Raw" ? Utils.strToByteArray(input) : input;
Blowfish.setIV(toBase64(iv), 0);
const result = Blowfish.decrypt(input, key, {
outputType: BLOWFISH_OUTPUT_TYPE_LOOKUP[inputType], // This actually means inputType. The library is weird.
cipherMode: BLOWFISH_MODE_LOOKUP[mode]
});
return outputType === "Hex" ? toHexFast(Utils.strToByteArray(result)) : result;
}
}
export default BlowfishDecrypt;

View file

@ -0,0 +1,102 @@
/**
* @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";
import { Blowfish } from "../vendor/Blowfish";
import { toBase64 } from "../lib/Base64";
/**
* Lookup table for Blowfish output types.
*/
const BLOWFISH_OUTPUT_TYPE_LOOKUP = {
Base64: 0, Hex: 1, String: 2, Raw: 3
};
/**
* Lookup table for Blowfish modes.
*/
const BLOWFISH_MODE_LOOKUP = {
ECB: 0, CBC: 1, PCBC: 2, CFB: 3, OFB: 4, CTR: 5
};
/**
* Blowfish Encrypt operation
*/
class BlowfishEncrypt extends Operation {
/**
* BlowfishEncrypt constructor
*/
constructor() {
super();
this.name = "Blowfish Encrypt";
this.module = "Ciphers";
this.description = "Blowfish is a symmetric-key block cipher designed in 1993 by Bruce Schneier and included in a large number of cipher suites and encryption products. AES now receives more attention.<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.";
this.infoURL = "https://wikipedia.org/wiki/Blowfish_(cipher)";
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", "PCBC", "CFB", "OFB", "CTR", "ECB"]
},
{
"name": "Input",
"type": "option",
"value": ["Raw", "Hex"]
},
{
"name": "Output",
"type": "option",
"value": ["Hex", "Base64", "Raw"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
[,, mode, inputType, outputType] = args;
if (key.length === 0) throw new OperationError("Enter a key");
input = Utils.convertToByteString(input, inputType);
Blowfish.setIV(toBase64(iv), 0);
const enc = Blowfish.encrypt(input, key, {
outputType: BLOWFISH_OUTPUT_TYPE_LOOKUP[outputType],
cipherMode: BLOWFISH_MODE_LOOKUP[mode]
});
return outputType === "Raw" ? Utils.byteArrayToChars(enc) : enc;
}
}
export default BlowfishEncrypt;

View file

@ -1,427 +0,0 @@
/* globals app */
import Utils from "../Utils.js";
/**
* Byte representation operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const ByteRepr = {
/**
* @constant
* @default
*/
DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF"],
/**
* @constant
* @default
*/
HEX_DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"],
/**
* @constant
* @default
*/
BIN_DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "None"],
/**
* To Hex operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runToHex: function(input, args) {
const delim = Utils.charRep[args[0] || "Space"];
return Utils.toHex(input, delim, 2);
},
/**
* From Hex operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromHex: function(input, args) {
const delim = args[0] || "Space";
return Utils.fromHex(input, delim, 2);
},
/**
* To Octal operation.
*
* @author Matt C [matt@artemisbot.pw]
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runToOct: function(input, args) {
const delim = Utils.charRep[args[0] || "Space"];
return input.map(val => val.toString(8)).join(delim);
},
/**
* From Octal operation.
*
* @author Matt C [matt@artemisbot.pw]
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromOct: function(input, args) {
const delim = Utils.charRep[args[0] || "Space"];
if (input.length === 0) return [];
return input.split(delim).map(val => parseInt(val, 8));
},
/**
* @constant
* @default
*/
CHARCODE_BASE: 16,
/**
* To Charcode operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runToCharcode: function(input, args) {
let delim = Utils.charRep[args[0] || "Space"],
base = args[1],
output = "",
padding = 2,
ordinal;
if (base < 2 || base > 36) {
throw "Error: Base argument must be between 2 and 36";
}
for (let i = 0; i < input.length; i++) {
ordinal = Utils.ord(input[i]);
if (base === 16) {
if (ordinal < 256) padding = 2;
else if (ordinal < 65536) padding = 4;
else if (ordinal < 16777216) padding = 6;
else if (ordinal < 4294967296) padding = 8;
else padding = 2;
if (padding > 2 && app) app.options.attemptHighlight = false;
output += Utils.hex(ordinal, padding) + delim;
} else {
if (app) app.options.attemptHighlight = false;
output += ordinal.toString(base) + delim;
}
}
return output.slice(0, -delim.length);
},
/**
* From Charcode operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromCharcode: function(input, args) {
let delim = Utils.charRep[args[0] || "Space"],
base = args[1],
bites = input.split(delim),
i = 0;
if (base < 2 || base > 36) {
throw "Error: Base argument must be between 2 and 36";
}
if (base !== 16 && app) {
app.options.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);
},
/**
* Highlight to hex
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightTo: function(pos, args) {
let delim = Utils.charRep[args[0] || "Space"],
len = delim === "\r\n" ? 1 : delim.length;
pos[0].start = pos[0].start * (2 + len);
pos[0].end = pos[0].end * (2 + len) - len;
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
if (delim === "0x" || delim === "\\x") {
pos[0].start += 2;
pos[0].end += 2;
}
return pos;
},
/**
* Highlight to hex
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightFrom: function(pos, args) {
let delim = Utils.charRep[args[0] || "Space"],
len = delim === "\r\n" ? 1 : delim.length,
width = len + 2;
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
if (delim === "0x" || delim === "\\x") {
if (pos[0].start > 1) pos[0].start -= 2;
else pos[0].start = 0;
if (pos[0].end > 1) pos[0].end -= 2;
else pos[0].end = 0;
}
pos[0].start = pos[0].start === 0 ? 0 : Math.round(pos[0].start / width);
pos[0].end = pos[0].end === 0 ? 0 : Math.ceil(pos[0].end / width);
return pos;
},
/**
* To Decimal operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runToDecimal: function(input, args) {
const delim = Utils.charRep[args[0]];
return input.join(delim);
},
/**
* From Decimal operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromDecimal: function(input, args) {
const delim = Utils.charRep[args[0]];
let byteStr = input.split(delim), output = [];
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;
},
/**
* To Binary operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runToBinary: function(input, args) {
let delim = Utils.charRep[args[0] || "Space"],
output = "",
padding = 8;
for (let i = 0; i < input.length; i++) {
output += Utils.pad(input[i].toString(2), padding) + delim;
}
if (delim.length) {
return output.slice(0, -delim.length);
} else {
return output;
}
},
/**
* From Binary operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromBinary: function(input, args) {
if (args[0] !== "None") {
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 to binary
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightToBinary: function(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;
},
/**
* Highlight from binary
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightFromBinary: function(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;
},
/**
* @constant
* @default
*/
HEX_CONTENT_CONVERT_WHICH: ["Only special chars", "Only special chars including spaces", "All chars"],
/**
* @constant
* @default
*/
HEX_CONTENT_SPACES_BETWEEN_BYTES: false,
/**
* To Hex Content operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runToHexContent: function(input, args) {
const convert = args[0];
const spaces = args[1];
if (convert === "All chars") {
let result = "|" + Utils.toHex(input) + "|";
if (!spaces) result = result.replace(/ /g, "");
return result;
}
let output = "",
inHex = false,
convertSpaces = convert === "Only special chars including spaces",
b;
for (let i = 0; i < input.length; i++) {
b = input[i];
if ((b === 32 && convertSpaces) || (b < 48 && b !== 32) || (b > 57 && b < 65) || (b > 90 && b < 97) || b > 122) {
if (!inHex) {
output += "|";
inHex = true;
} else if (spaces) output += " ";
output += Utils.toHex([b]);
} else {
if (inHex) {
output += "|";
inHex = false;
}
output += Utils.chr(input[i]);
}
}
if (inHex) output += "|";
return output;
},
/**
* From Hex Content operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromHexContent: function(input, args) {
const regex = /\|([a-f\d ]{2,})\|/gi;
let output = [], 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 = Utils.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 ByteRepr;

View file

@ -0,0 +1,56 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import bzip2 from "../vendor/bzip2";
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.infoURL = "https://wikipedia.org/wiki/Bzip2";
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,41 @@
/**
* @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 = "Crypto";
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.infoURL = "https://wikipedia.org/wiki/Cyclic_redundancy_check";
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,41 @@
/**
* @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 = "Crypto";
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.infoURL = "https://wikipedia.org/wiki/Cyclic_redundancy_check";
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,47 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import vkbeautify from "vkbeautify";
import Operation from "../Operation";
/**
* CSS Beautify operation
*/
class CSSBeautify extends Operation {
/**
* CSSBeautify constructor
*/
constructor() {
super();
this.name = "CSS Beautify";
this.module = "Code";
this.description = "Indents and prettifies Cascading Style Sheets (CSS) code.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Indent string",
"type": "binaryShortString",
"value": "\\t"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const indentStr = args[0];
return vkbeautify.css(input, indentStr);
}
}
export default CSSBeautify;

View file

@ -0,0 +1,47 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import vkbeautify from "vkbeautify";
import Operation from "../Operation";
/**
* CSS Minify operation
*/
class CSSMinify extends Operation {
/**
* CSSMinify constructor
*/
constructor() {
super();
this.name = "CSS Minify";
this.module = "Code";
this.description = "Compresses Cascading Style Sheets (CSS) code.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Preserve comments",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const preserveComments = args[0];
return vkbeautify.cssmin(input, preserveComments);
}
}
export default CSSMinify;

View file

@ -0,0 +1,91 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import xmldom from "xmldom";
import nwmatcher from "nwmatcher";
/**
* CSS selector operation
*/
class CSSSelector extends Operation {
/**
* CSSSelector constructor
*/
constructor() {
super();
this.name = "CSS selector";
this.module = "Code";
this.description = "Extract information from an HTML document with a CSS selector";
this.infoURL = "https://wikipedia.org/wiki/Cascading_Style_Sheets#Selector";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "CSS selector",
"type": "string",
"value": ""
},
{
"name": "Delimiter",
"type": "binaryShortString",
"value": "\\n"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [query, delimiter] = args,
parser = new xmldom.DOMParser();
let dom,
result;
if (!query.length || !input.length) {
return "";
}
try {
dom = parser.parseFromString(input);
} catch (err) {
throw new OperationError("Invalid input HTML.");
}
try {
const matcher = nwmatcher({document: dom});
result = matcher.select(query, dom);
} catch (err) {
throw new OperationError("Invalid CSS Selector. Details:\n" + err.message);
}
const nodeToString = function(node) {
return node.toString();
/* xmldom does not return the outerHTML value.
switch (node.nodeType) {
case node.ELEMENT_NODE: return node.outerHTML;
case node.ATTRIBUTE_NODE: return node.value;
case node.TEXT_NODE: return node.wholeText;
case node.COMMENT_NODE: return node.data;
case node.DOCUMENT_NODE: return node.outerHTML;
default: throw new Error("Unknown Node Type: " + node.nodeType);
}*/
};
return result
.map(nodeToString)
.join(delimiter);
}
}
export default CSSSelector;

View file

@ -0,0 +1,80 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Utils from "../Utils";
/**
* CSV to JSON operation
*/
class CSVToJSON extends Operation {
/**
* CSVToJSON constructor
*/
constructor() {
super();
this.name = "CSV to JSON";
this.module = "Default";
this.description = "Converts a CSV file to JSON format.";
this.infoURL = "https://wikipedia.org/wiki/Comma-separated_values";
this.inputType = "string";
this.outputType = "JSON";
this.args = [
{
name: "Cell delimiters",
type: "binaryShortString",
value: ","
},
{
name: "Row delimiters",
type: "binaryShortString",
value: "\\r\\n"
},
{
name: "Format",
type: "option",
value: ["Array of dictionaries", "Array of arrays"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {JSON}
*/
run(input, args) {
const [cellDelims, rowDelims, format] = args;
let json, header;
try {
json = Utils.parseCSV(input, cellDelims.split(""), rowDelims.split(""));
} catch (err) {
throw new OperationError("Unable to parse CSV: " + err);
}
switch (format) {
case "Array of dictionaries":
header = json[0];
return json.slice(1).map(row => {
const obj = {};
header.forEach((h, i) => {
obj[h] = row[i];
});
return obj;
});
case "Array of arrays":
default:
return json;
}
}
}
export default CSVToJSON;

View file

@ -0,0 +1,41 @@
/**
* @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 = "Crypto";
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.infoURL = "https://forensicswiki.org/wiki/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

@ -0,0 +1,97 @@
/**
* @author d98762625 [d98762625@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
/**
* Set cartesian product operation
*/
class CartesianProduct extends Operation {
/**
* Cartesian Product constructor
*/
constructor() {
super();
this.name = "Cartesian Product";
this.module = "Default";
this.description = "Calculates the cartesian product of multiple sets of data, returning all possible combinations.";
this.infoURL = "https://wikipedia.org/wiki/Cartesian_product";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "Sample delimiter",
type: "binaryString",
value: "\\n\\n"
},
{
name: "Item delimiter",
type: "binaryString",
value: ","
},
];
}
/**
* Validate input length
*
* @param {Object[]} sets
* @throws {OperationError} if fewer than 2 sets
*/
validateSampleNumbers(sets) {
if (!sets || sets.length < 2) {
throw new OperationError("Incorrect number of sets, perhaps you" +
" need to modify the sample delimiter or add more samples?");
}
}
/**
* Run the product operation
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
* @throws {OperationError}
*/
run(input, args) {
[this.sampleDelim, this.itemDelimiter] = args;
const sets = input.split(this.sampleDelim);
this.validateSampleNumbers(sets);
return this.runCartesianProduct(...sets.map(s => s.split(this.itemDelimiter)));
}
/**
* Return the cartesian product of the two inputted sets.
*
* @param {Object[]} a
* @param {Object[]} b
* @param {Object[]} c
* @returns {string}
*/
runCartesianProduct(a, b, ...c) {
/**
* https://stackoverflow.com/a/43053803/7200497
* @returns {Object[]}
*/
const f = (a, b) => [].concat(...a.map(d => b.map(e => [].concat(d, e))));
/**
* https://stackoverflow.com/a/43053803/7200497
* @returns {Object[][]}
*/
const cartesian = (a, b, ...c) => (b ? cartesian(f(a, b), ...c) : a);
return cartesian(a, b, ...c)
.map(set => `(${set.join(",")})`)
.join(this.itemDelimiter);
}
}
export default CartesianProduct;

View file

@ -0,0 +1,120 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Utils from "../Utils";
import {fromHex} from "../lib/Hex";
/**
* Change IP format operation
*/
class ChangeIPFormat extends Operation {
/**
* ChangeIPFormat constructor
*/
constructor() {
super();
this.name = "Change IP format";
this.module = "Default";
this.description = "Convert an IP address from one format to another, e.g. <code>172.20.23.54</code> to <code>ac141736</code>";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Input format",
"type": "option",
"value": ["Dotted Decimal", "Decimal", "Hex"]
},
{
"name": "Output format",
"type": "option",
"value": ["Dotted Decimal", "Decimal", "Hex"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [inFormat, outFormat] = args,
lines = input.split("\n");
let output = "",
j = 0;
for (let i = 0; i < lines.length; i++) {
if (lines[i] === "") continue;
let baIp = [];
let octets;
let decimal;
if (inFormat === outFormat) {
output += lines[i] + "\n";
continue;
}
// Convert to byte array IP from input format
switch (inFormat) {
case "Dotted Decimal":
octets = lines[i].split(".");
for (j = 0; j < octets.length; j++) {
baIp.push(parseInt(octets[j], 10));
}
break;
case "Decimal":
decimal = lines[i].toString();
baIp.push(decimal >> 24 & 255);
baIp.push(decimal >> 16 & 255);
baIp.push(decimal >> 8 & 255);
baIp.push(decimal & 255);
break;
case "Hex":
baIp = fromHex(lines[i]);
break;
default:
throw new OperationError("Unsupported input IP format");
}
let ddIp;
let decIp;
let hexIp;
// Convert byte array IP to output format
switch (outFormat) {
case "Dotted Decimal":
ddIp = "";
for (j = 0; j < baIp.length; j++) {
ddIp += baIp[j] + ".";
}
output += ddIp.slice(0, ddIp.length-1) + "\n";
break;
case "Decimal":
decIp = ((baIp[0] << 24) | (baIp[1] << 16) | (baIp[2] << 8) | baIp[3]) >>> 0;
output += decIp.toString() + "\n";
break;
case "Hex":
hexIp = "";
for (j = 0; j < baIp.length; j++) {
hexIp += Utils.hex(baIp[j]);
}
output += hexIp + "\n";
break;
default:
throw new OperationError("Unsupported output IP format");
}
}
return output.slice(0, output.length-1);
}
}
export default ChangeIPFormat;

View file

@ -1,99 +0,0 @@
import cptable from "../lib/js-codepage/cptable.js";
import Utils from "../Utils.js";
import CryptoJS from "crypto-js";
/**
* Character encoding operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const CharEnc = {
/**
* @constant
* @default
*/
IO_FORMAT: {
"UTF-8 (65001)": 65001,
"UTF-7 (65000)": 65000,
"UTF16LE (1200)": 1200,
"UTF16BE (1201)": 1201,
"UTF16 (1201)": 1201,
"IBM EBCDIC International (500)": 500,
"IBM EBCDIC US-Canada (37)": 37,
"Windows-874 Thai (874)": 874,
"Japanese Shift-JIS (932)": 932,
"Simplified Chinese GBK (936)": 936,
"Korean (949)": 949,
"Traditional Chinese Big5 (950)": 950,
"Windows-1250 Central European (1250)": 1250,
"Windows-1251 Cyrillic (1251)": 1251,
"Windows-1252 Latin (1252)": 1252,
"Windows-1253 Greek (1253)": 1253,
"Windows-1254 Turkish (1254)": 1254,
"Windows-1255 Hebrew (1255)": 1255,
"Windows-1256 Arabic (1256)": 1256,
"Windows-1257 Baltic (1257)": 1257,
"Windows-1258 Vietnam (1258)": 1258,
"US-ASCII (20127)": 20127,
"Simplified Chinese GB2312 (20936)": 20936,
"KOI8-R Russian Cyrillic (20866)": 20866,
"KOI8-U Ukrainian Cyrillic (21866)": 21866,
"ISO-8859-1 Latin 1 Western European (28591)": 28591,
"ISO-8859-2 Latin 2 Central European (28592)": 28592,
"ISO-8859-3 Latin 3 South European (28593)": 28593,
"ISO-8859-4 Latin 4 North European (28594)": 28594,
"ISO-8859-5 Latin/Cyrillic (28595)": 28595,
"ISO-8859-6 Latin/Arabic (28596)": 28596,
"ISO-8859-7 Latin/Greek (28597)": 28597,
"ISO-8859-8 Latin/Hebrew (28598)": 28598,
"ISO-8859-9 Latin 5 Turkish (28599)": 28599,
"ISO-8859-10 Latin 6 Nordic (28600)": 28600,
"ISO-8859-11 Latin/Thai (28601)": 28601,
"ISO-8859-13 Latin 7 Baltic Rim (28603)": 28603,
"ISO-8859-14 Latin 8 Celtic (28604)": 28604,
"ISO-8859-15 Latin 9 (28605)": 28605,
"ISO-8859-16 Latin 10 (28606)": 28606,
"ISO-2022 JIS Japanese (50222)": 50222,
"EUC Japanese (51932)": 51932,
"EUC Korean (51949)": 51949,
"Simplified Chinese GB18030 (54936)": 54936,
},
/**
* Encode text operation.
* @author tlwr [toby@toby.codes]
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runEncode: function(input, args) {
const format = CharEnc.IO_FORMAT[args[0]];
let encoded = cptable.utils.encode(format, input);
encoded = Array.from(encoded);
return encoded;
},
/**
* Decode text operation.
* @author tlwr [toby@toby.codes]
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runDecode: function(input, args) {
const format = CharEnc.IO_FORMAT[args[0]];
let decoded = cptable.utils.decode(format, input);
return decoded;
},
};
export default CharEnc;

View file

@ -1,195 +0,0 @@
import Utils from "../Utils.js";
/**
* Checksum operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Checksum = {
/**
* Fletcher-8 Checksum operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runFletcher8: function(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);
},
/**
* Fletcher-16 Checksum operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runFletcher16: function(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);
},
/**
* Fletcher-32 Checksum operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runFletcher32: function(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);
},
/**
* Fletcher-64 Checksum operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runFletcher64: function(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);
},
/**
* Adler-32 Checksum operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runAdler32: function(input, args) {
let MOD_ADLER = 65521,
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);
},
/**
* CRC-32 Checksum operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runCRC32: function(input, args) {
let crcTable = global.crcTable || (global.crcTable = Checksum._genCRCTable()),
crc = 0 ^ (-1);
for (let i = 0; i < input.length; i++) {
crc = (crc >>> 8) ^ crcTable[(crc ^ input[i]) & 0xff];
}
return Utils.hex((crc ^ (-1)) >>> 0);
},
/**
* TCP/IP Checksum operation.
*
* @author GCHQ Contributor [1]
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*
* @example
* // returns '3f2c'
* Checksum.runTcpIp([0x45,0x00,0x00,0x87,0xa3,0x1b,0x40,0x00,0x40,0x06,
* 0x00,0x00,0xac,0x11,0x00,0x04,0xac,0x11,0x00,0x03])
*
* // returns 'a249'
* Checksum.runTcpIp([0x45,0x00,0x01,0x11,0x3f,0x74,0x40,0x00,0x40,0x06,
* 0x00,0x00,0xac,0x11,0x00,0x03,0xac,0x11,0x00,0x04])
*/
runTCPIP: function(input, args) {
let csum = 0;
for (let i = 0; i < input.length; i++) {
if (i % 2 === 0) {
csum += (input[i] << 8);
} else {
csum += input[i];
}
}
csum = (csum >> 16) + (csum & 0xffff);
return Utils.hex(0xffff - csum);
},
/**
* Generates a CRC table for use with CRC checksums.
*
* @private
* @returns {array}
*/
_genCRCTable: function() {
let c,
crcTable = [];
for (let n = 0; n < 256; n++) {
c = n;
for (let k = 0; k < 8; k++) {
c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
}
crcTable[n] = c;
}
return crcTable;
},
};
export default Checksum;

View file

@ -0,0 +1,54 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Chi-squared_distribution";
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

@ -1,676 +0,0 @@
import Utils from "../Utils.js";
import CryptoJS from "crypto-js";
import {blowfish as Blowfish} from "sladex-blowfish";
/**
* Cipher operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Cipher = {
/**
* @constant
* @default
*/
IO_FORMAT1: ["Hex", "Base64", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1"],
/**
* @constant
* @default
*/
IO_FORMAT2: ["UTF8", "UTF16", "UTF16LE", "UTF16BE", "Latin1", "Hex", "Base64"],
/**
* @constant
* @default
*/
IO_FORMAT3: ["Hex", "Base64", "UTF16", "UTF16LE", "UTF16BE", "Latin1"],
/**
* @constant
* @default
*/
IO_FORMAT4: ["Latin1", "UTF8", "UTF16", "UTF16LE", "UTF16BE", "Hex", "Base64"],
/**
* @constant
* @default
*/
MODES: ["CBC", "CFB", "CTR", "OFB", "ECB"],
/**
* @constant
* @default
*/
PADDING: ["Pkcs7", "Iso97971", "AnsiX923", "Iso10126", "ZeroPadding", "NoPadding"],
/**
* @constant
* @default
*/
RESULT_TYPE: ["Show all", "Ciphertext", "Key", "IV", "Salt"],
/**
* Runs encryption operations using the CryptoJS framework.
*
* @private
* @param {function} algo - The CryptoJS algorithm to use
* @param {byteArray} input
* @param {function} args
* @returns {string}
*/
_enc: function (algo, input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || ""),
iv = Utils.format[args[1].option].parse(args[1].string || ""),
salt = Utils.format[args[2].option].parse(args[2].string || ""),
mode = CryptoJS.mode[args[3]],
padding = CryptoJS.pad[args[4]],
resultOption = args[5].toLowerCase(),
outputFormat = args[6];
if (iv.sigBytes === 0) {
// Use passphrase rather than key. Need to convert it to a string.
key = key.toString(CryptoJS.enc.Latin1);
}
const encrypted = algo.encrypt(input, key, {
salt: salt.sigBytes > 0 ? salt : false,
iv: iv.sigBytes > 0 ? iv : null,
mode: mode,
padding: padding
});
let result = "";
if (resultOption === "show all") {
result += "Key: " + encrypted.key.toString(Utils.format[outputFormat]);
result += "\nIV: " + encrypted.iv.toString(Utils.format[outputFormat]);
if (encrypted.salt) result += "\nSalt: " + encrypted.salt.toString(Utils.format[outputFormat]);
result += "\n\nCiphertext: " + encrypted.ciphertext.toString(Utils.format[outputFormat]);
} else {
result = encrypted[resultOption].toString(Utils.format[outputFormat]);
}
return result;
},
/**
* Runs decryption operations using the CryptoJS framework.
*
* @private
* @param {function} algo - The CryptoJS algorithm to use
* @param {byteArray} input
* @param {function} args
* @returns {string}
*/
_dec: function (algo, input, args) {
let key = Utils.format[args[0].option].parse(args[0].string || ""),
iv = Utils.format[args[1].option].parse(args[1].string || ""),
salt = Utils.format[args[2].option].parse(args[2].string || ""),
mode = CryptoJS.mode[args[3]],
padding = CryptoJS.pad[args[4]],
inputFormat = args[5],
outputFormat = args[6];
// The ZeroPadding option causes a crash when the input length is 0
if (!input.length) {
return "No input";
}
const ciphertext = Utils.format[inputFormat].parse(input);
if (iv.sigBytes === 0) {
// Use passphrase rather than key. Need to convert it to a string.
key = key.toString(CryptoJS.enc.Latin1);
}
const decrypted = algo.decrypt({
ciphertext: ciphertext,
salt: salt.sigBytes > 0 ? salt : false
}, key, {
iv: iv.sigBytes > 0 ? iv : null,
mode: mode,
padding: padding
});
let result;
try {
result = decrypted.toString(Utils.format[outputFormat]);
} catch (err) {
result = "Decrypt error: " + err.message;
}
return result;
},
/**
* AES Encrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runAesEnc: function (input, args) {
return Cipher._enc(CryptoJS.AES, input, args);
},
/**
* AES Decrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runAesDec: function (input, args) {
return Cipher._dec(CryptoJS.AES, input, args);
},
/**
* DES Encrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runDesEnc: function (input, args) {
return Cipher._enc(CryptoJS.DES, input, args);
},
/**
* DES Decrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runDesDec: function (input, args) {
return Cipher._dec(CryptoJS.DES, input, args);
},
/**
* Triple DES Encrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runTripleDesEnc: function (input, args) {
return Cipher._enc(CryptoJS.TripleDES, input, args);
},
/**
* Triple DES Decrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runTripleDesDec: function (input, args) {
return Cipher._dec(CryptoJS.TripleDES, input, args);
},
/**
* Rabbit Encrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runRabbitEnc: function (input, args) {
return Cipher._enc(CryptoJS.Rabbit, input, args);
},
/**
* Rabbit Decrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runRabbitDec: function (input, args) {
return Cipher._dec(CryptoJS.Rabbit, input, args);
},
/**
* @constant
* @default
*/
BLOWFISH_MODES: ["ECB", "CBC", "PCBC", "CFB", "OFB", "CTR"],
/**
* @constant
* @default
*/
BLOWFISH_OUTPUT_TYPES: ["Base64", "Hex", "String", "Raw"],
/**
* Blowfish Encrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runBlowfishEnc: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1),
mode = args[1],
outputFormat = args[2];
if (key.length === 0) return "Enter a key";
let encHex = Blowfish.encrypt(input, key, {
outputType: 1,
cipherMode: Cipher.BLOWFISH_MODES.indexOf(mode)
}),
enc = CryptoJS.enc.Hex.parse(encHex);
return enc.toString(Utils.format[outputFormat]);
},
/**
* Blowfish Decrypt operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runBlowfishDec: function (input, args) {
let key = Utils.format[args[0].option].parse(args[0].string).toString(Utils.format.Latin1),
mode = args[1],
inputFormat = args[2];
if (key.length === 0) return "Enter a key";
input = Utils.format[inputFormat].parse(input);
return Blowfish.decrypt(input.toString(CryptoJS.enc.Base64), key, {
outputType: 0, // This actually means inputType. The library is weird.
cipherMode: Cipher.BLOWFISH_MODES.indexOf(mode)
});
},
/**
* @constant
* @default
*/
KDF_KEY_SIZE: 256,
/**
* @constant
* @default
*/
KDF_ITERATIONS: 1,
/**
* @constant
* @default
*/
HASHERS: ["MD5", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "RIPEMD160"],
/**
* Derive PBKDF2 key operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runPbkdf2: function (input, args) {
let keySize = args[0] / 32,
iterations = args[1],
hasher = args[2],
salt = CryptoJS.enc.Hex.parse(args[3] || ""),
inputFormat = args[4],
outputFormat = args[5],
passphrase = Utils.format[inputFormat].parse(input),
key = CryptoJS.PBKDF2(passphrase, salt, {
keySize: keySize,
hasher: CryptoJS.algo[hasher],
iterations: iterations,
});
return key.toString(Utils.format[outputFormat]);
},
/**
* Derive EVP key operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runEvpkdf: function (input, args) {
let keySize = args[0] / 32,
iterations = args[1],
hasher = args[2],
salt = CryptoJS.enc.Hex.parse(args[3] || ""),
inputFormat = args[4],
outputFormat = args[5],
passphrase = Utils.format[inputFormat].parse(input),
key = CryptoJS.EvpKDF(passphrase, salt, {
keySize: keySize,
hasher: CryptoJS.algo[hasher],
iterations: iterations,
});
return key.toString(Utils.format[outputFormat]);
},
/**
* RC4 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runRc4: function (input, args) {
let message = Utils.format[args[1]].parse(input),
passphrase = Utils.format[args[0].option].parse(args[0].string),
encrypted = CryptoJS.RC4.encrypt(message, passphrase);
return encrypted.ciphertext.toString(Utils.format[args[2]]);
},
/**
* @constant
* @default
*/
RC4DROP_BYTES: 768,
/**
* RC4 Drop operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runRc4drop: function (input, args) {
let message = Utils.format[args[1]].parse(input),
passphrase = Utils.format[args[0].option].parse(args[0].string),
drop = args[3],
encrypted = CryptoJS.RC4Drop.encrypt(message, passphrase, { drop: drop });
return encrypted.ciphertext.toString(Utils.format[args[2]]);
},
/**
* Vigenère Encode operation.
*
* @author Matt C [matt@artemisbot.pw]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runVigenereEnc: function (input, args) {
let alphabet = "abcdefghijklmnopqrstuvwxyz",
key = args[0].toLowerCase(),
output = "",
fail = 0,
keyIndex,
msgIndex,
chr;
if (!key) return "No key entered";
if (!/^[a-zA-Z]+$/.test(key)) return "The key must consist only of letters";
for (let i = 0; i < input.length; i++) {
if (alphabet.indexOf(input[i]) >= 0) {
// Get the corresponding character of key for the current letter, accounting
// for chars not in alphabet
chr = key[(i - fail) % key.length];
// Get the location in the vigenere square of the key char
keyIndex = alphabet.indexOf(chr);
// Get the location in the vigenere square of the message char
msgIndex = alphabet.indexOf(input[i]);
// Get the encoded letter by finding the sum of indexes modulo 26 and finding
// the letter corresponding to that
output += alphabet[(keyIndex + msgIndex) % 26];
} else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
chr = key[(i - fail) % key.length].toLowerCase();
keyIndex = alphabet.indexOf(chr);
msgIndex = alphabet.indexOf(input[i].toLowerCase());
output += alphabet[(keyIndex + msgIndex) % 26].toUpperCase();
} else {
output += input[i];
fail++;
}
}
return output;
},
/**
* Vigenère Decode operation.
*
* @author Matt C [matt@artemisbot.pw]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runVigenereDec: function (input, args) {
let alphabet = "abcdefghijklmnopqrstuvwxyz",
key = args[0].toLowerCase(),
output = "",
fail = 0,
keyIndex,
msgIndex,
chr;
if (!key) return "No key entered";
if (!/^[a-zA-Z]+$/.test(key)) return "The key must consist only of letters";
for (let i = 0; i < input.length; i++) {
if (alphabet.indexOf(input[i]) >= 0) {
chr = key[(i - fail) % key.length];
keyIndex = alphabet.indexOf(chr);
msgIndex = alphabet.indexOf(input[i]);
// Subtract indexes from each other, add 26 just in case the value is negative,
// modulo to remove if neccessary
output += alphabet[(msgIndex - keyIndex + alphabet.length) % 26];
} else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
chr = key[(i - fail) % key.length].toLowerCase();
keyIndex = alphabet.indexOf(chr);
msgIndex = alphabet.indexOf(input[i].toLowerCase());
output += alphabet[(msgIndex + alphabet.length - keyIndex) % 26].toUpperCase();
} else {
output += input[i];
fail++;
}
}
return output;
},
/**
* @constant
* @default
*/
AFFINE_A: 1,
/**
* @constant
* @default
*/
AFFINE_B: 0,
/**
* Affine Cipher Encode operation.
*
* @author Matt C [matt@artemisbot.pw]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runAffineEnc: function (input, args) {
let alphabet = "abcdefghijklmnopqrstuvwxyz",
a = args[0],
b = args[1],
output = "";
if (!/^\+?(0|[1-9]\d*)$/.test(a) || !/^\+?(0|[1-9]\d*)$/.test(b)) {
return "The values of a and b can only be integers.";
}
for (let i = 0; i < input.length; i++) {
if (alphabet.indexOf(input[i]) >= 0) {
// Uses the affine function ax+b % m = y (where m is length of the alphabet)
output += alphabet[((a * alphabet.indexOf(input[i])) + b) % 26];
} else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
// Same as above, accounting for uppercase
output += alphabet[((a * alphabet.indexOf(input[i].toLowerCase())) + b) % 26].toUpperCase();
} else {
// Non-alphabetic characters
output += input[i];
}
}
return output;
},
/**
* Affine Cipher Encode operation.
*
* @author Matt C [matt@artemisbot.pw]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runAffineDec: function (input, args) {
let alphabet = "abcdefghijklmnopqrstuvwxyz",
a = args[0],
b = args[1],
output = "",
aModInv;
if (!/^\+?(0|[1-9]\d*)$/.test(a) || !/^\+?(0|[1-9]\d*)$/.test(b)) {
return "The values of a and b can only be integers.";
}
if (Utils.gcd(a, 26) !== 1) {
return "The value of a must be coprime to 26.";
}
// Calculates modular inverse of a
aModInv = Utils.modInv(a, 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;
},
/**
* Atbash Cipher Encode operation.
*
* @author Matt C [matt@artemisbot.pw]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runAtbash: function (input, args) {
return Cipher.runAffineEnc(input, [25, 25]);
},
/**
* @constant
* @default
*/
SUBS_PLAINTEXT: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
/**
* @constant
* @default
*/
SUBS_CIPHERTEXT: "XYZABCDEFGHIJKLMNOPQRSTUVW",
/**
* Substitute operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runSubstitute: function (input, args) {
let plaintext = Utils.strToByteArray(Utils.expandAlphRange(args[0]).join()),
ciphertext = Utils.strToByteArray(Utils.expandAlphRange(args[1]).join()),
output = [],
index = -1;
if (plaintext.length !== ciphertext.length) {
output = Utils.strToByteArray("Warning: Plaintext and Ciphertext lengths differ\n\n");
}
for (let i = 0; i < input.length; i++) {
index = plaintext.indexOf(input[i]);
output.push(index > -1 && index < ciphertext.length ? ciphertext[index] : input[i]);
}
return output;
},
};
export default Cipher;
/**
* 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 });
};

View file

@ -0,0 +1,58 @@
/**
* @author bwhitn [brian.m.whitney@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import cptable from "../vendor/js-codepage/cptable.js";
/**
* Citrix CTX1 Decode operation
*/
class CitrixCTX1Decode extends Operation {
/**
* CitrixCTX1Decode constructor
*/
constructor() {
super();
this.name = "Citrix CTX1 Decode";
this.module = "Encodings";
this.description = "Decodes strings in a Citrix CTX1 password format to plaintext.";
this.infoURL = "https://www.reddit.com/r/AskNetsec/comments/1s3r6y/citrix_ctx1_hash_decoding/";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (input.length % 4 !== 0) {
throw new OperationError("Incorrect hash length");
}
const revinput = input.reverse();
const result = [];
let temp = 0;
for (let i = 0; i < revinput.length; i += 2) {
if (i + 2 >= revinput.length) {
temp = 0;
} else {
temp = ((revinput[i + 2] - 0x41) & 0xf) ^ (((revinput[i + 3]- 0x41) << 4) & 0xf0);
}
temp = (((revinput[i] - 0x41) & 0xf) ^ (((revinput[i + 1] - 0x41) << 4) & 0xf0)) ^ 0xa5 ^ temp;
result.push(temp);
}
// Decodes a utf-16le string
return cptable.utils.decode(1200, result.reverse());
}
}
export default CitrixCTX1Decode;

View file

@ -0,0 +1,50 @@
/**
* @author bwhitn [brian.m.whitney@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import cptable from "../vendor/js-codepage/cptable.js";
/**
* Citrix CTX1 Encode operation
*/
class CitrixCTX1Encode extends Operation {
/**
* CitrixCTX1Encode constructor
*/
constructor() {
super();
this.name = "Citrix CTX1 Encode";
this.module = "Encodings";
this.description = "Encodes strings to Citrix CTX1 password format.";
this.infoURL = "https://www.reddit.com/r/AskNetsec/comments/1s3r6y/citrix_ctx1_hash_decoding/";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const utf16pass = Array.from(cptable.utils.encode(1200, input));
const result = [];
let temp = 0;
for (let i = 0; i < utf16pass.length; i++) {
temp = utf16pass[i] ^ 0xa5 ^ temp;
result.push(((temp >>> 4) & 0xf) + 0x41);
result.push((temp & 0xf) + 0x41);
}
return result;
}
}
export default CitrixCTX1Encode;

View file

@ -1,501 +0,0 @@
import {camelCase, kebabCase, snakeCase} from "lodash";
import Utils from "../Utils.js";
import vkbeautify from "vkbeautify";
import {DOMParser as dom} from "xmldom";
import xpath from "xpath";
import prettyPrintOne from "imports-loader?window=>global!exports-loader?prettyPrintOne!google-code-prettify/bin/prettify.min.js";
/**
* Code operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Code = {
/**
* @constant
* @default
*/
LANGUAGES: ["default-code", "default-markup", "bash", "bsh", "c", "cc", "coffee", "cpp", "cs", "csh", "cv", "cxx", "cyc", "htm", "html", "in.tag", "java", "javascript", "js", "json", "m", "mxml", "perl", "pl", "pm", "py", "python", "rb", "rc", "rs", "ruby", "rust", "sh", "uq.val", "xhtml", "xml", "xsl"],
/**
* @constant
* @default
*/
LINE_NUMS: false,
/**
* Syntax highlighter operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runSyntaxHighlight: function(input, args) {
let language = args[0],
lineNums = args[1];
return "<code class='prettyprint'>" + prettyPrintOne(Utils.escapeHtml(input), language, lineNums) + "</code>";
},
/**
* @constant
* @default
*/
BEAUTIFY_INDENT: "\\t",
/**
* XML Beautify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runXmlBeautify: function(input, args) {
const indentStr = args[0];
return vkbeautify.xml(input, indentStr);
},
/**
* JSON Beautify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runJsonBeautify: function(input, args) {
const indentStr = args[0];
if (!input) return "";
return vkbeautify.json(input, indentStr);
},
/**
* CSS Beautify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runCssBeautify: function(input, args) {
const indentStr = args[0];
return vkbeautify.css(input, indentStr);
},
/**
* SQL Beautify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSqlBeautify: function(input, args) {
const indentStr = args[0];
return vkbeautify.sql(input, indentStr);
},
/**
* @constant
* @default
*/
PRESERVE_COMMENTS: false,
/**
* XML Minify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runXmlMinify: function(input, args) {
const preserveComments = args[0];
return vkbeautify.xmlmin(input, preserveComments);
},
/**
* JSON Minify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runJsonMinify: function(input, args) {
if (!input) return "";
return vkbeautify.jsonmin(input);
},
/**
* CSS Minify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runCssMinify: function(input, args) {
const preserveComments = args[0];
return vkbeautify.cssmin(input, preserveComments);
},
/**
* SQL Minify operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSqlMinify: function(input, args) {
return vkbeautify.sqlmin(input);
},
/**
* Generic Code Beautify operation.
*
* Yeeeaaah...
*
* I'm not proud of this code, but seriously, try writing a generic lexer and parser that
* correctly generates an AST for multiple different languages. I have tried, and I can tell
* you it's pretty much impossible.
*
* This basically works. That'll have to be good enough. It's not meant to produce working code,
* just slightly more readable code.
*
* Things that don't work:
* - For loop formatting
* - Do-While loop formatting
* - Switch/Case indentation
* - Bit shift operators
*
* @author n1474335 [n1474335@gmail.com]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runGenericBeautify: function(input, args) {
let code = input,
t = 0,
preservedTokens = [],
m;
// Remove strings
const sstrings = /'([^'\\]|\\.)*'/g;
while ((m = sstrings.exec(code))) {
code = preserveToken(code, m, t++);
sstrings.lastIndex = m.index;
}
const dstrings = /"([^"\\]|\\.)*"/g;
while ((m = dstrings.exec(code))) {
code = preserveToken(code, m, t++);
dstrings.lastIndex = m.index;
}
// Remove comments
const scomments = /\/\/[^\n\r]*/g;
while ((m = scomments.exec(code))) {
code = preserveToken(code, m, t++);
scomments.lastIndex = m.index;
}
const mcomments = /\/\*[\s\S]*?\*\//gm;
while ((m = mcomments.exec(code))) {
code = preserveToken(code, m, t++);
mcomments.lastIndex = m.index;
}
const hcomments = /(^|\n)#[^\n\r#]+/g;
while ((m = hcomments.exec(code))) {
code = preserveToken(code, m, t++);
hcomments.lastIndex = m.index;
}
// Remove regexes
const regexes = /\/.*?[^\\]\/[gim]{0,3}/gi;
while ((m = regexes.exec(code))) {
code = preserveToken(code, m, t++);
regexes.lastIndex = m.index;
}
code = code
// Create newlines after ;
.replace(/;/g, ";\n")
// Create newlines after { and around }
.replace(/{/g, "{\n")
.replace(/}/g, "\n}\n")
// Remove carriage returns
.replace(/\r/g, "")
// Remove all indentation
.replace(/^\s+/g, "")
.replace(/\n\s+/g, "\n")
// Remove trailing spaces
.replace(/\s*$/g, "")
.replace(/\n{/g, "{");
// Indent
let i = 0,
level = 0,
indent;
while (i < code.length) {
switch (code[i]) {
case "{":
level++;
break;
case "\n":
if (i+1 >= code.length) break;
if (code[i+1] === "}") level--;
indent = (level >= 0) ? Array(level*4+1).join(" ") : "";
code = code.substring(0, i+1) + indent + code.substring(i+1);
if (level > 0) i += level*4;
break;
}
i++;
}
code = code
// Add strategic spaces
.replace(/\s*([!<>=+-/*]?)=\s*/g, " $1= ")
.replace(/\s*<([=]?)\s*/g, " <$1 ")
.replace(/\s*>([=]?)\s*/g, " >$1 ")
.replace(/([^+])\+([^+=])/g, "$1 + $2")
.replace(/([^-])-([^-=])/g, "$1 - $2")
.replace(/([^*])\*([^*=])/g, "$1 * $2")
.replace(/([^/])\/([^/=])/g, "$1 / $2")
.replace(/\s*,\s*/g, ", ")
.replace(/\s*{/g, " {")
.replace(/}\n/g, "}\n\n")
// Hacky horribleness
.replace(/(if|for|while|with|elif|elseif)\s*\(([^\n]*)\)\s*\n([^{])/gim, "$1 ($2)\n $3")
.replace(/(if|for|while|with|elif|elseif)\s*\(([^\n]*)\)([^{])/gim, "$1 ($2) $3")
.replace(/else\s*\n([^{])/gim, "else\n $1")
.replace(/else\s+([^{])/gim, "else $1")
// Remove strategic spaces
.replace(/\s+;/g, ";")
.replace(/\{\s+\}/g, "{}")
.replace(/\[\s+\]/g, "[]")
.replace(/}\s*(else|catch|except|finally|elif|elseif|else if)/gi, "} $1");
// Replace preserved tokens
const ptokens = /###preservedToken(\d+)###/g;
while ((m = ptokens.exec(code))) {
const ti = parseInt(m[1], 10);
code = code.substring(0, m.index) + preservedTokens[ti] + code.substring(m.index + m[0].length);
ptokens.lastIndex = m.index;
}
return code;
/**
* Replaces a matched token with a placeholder value.
*/
function preserveToken(str, match, t) {
preservedTokens[t] = match[0];
return str.substring(0, match.index) +
"###preservedToken" + t + "###" +
str.substring(match.index + match[0].length);
}
},
/**
* @constant
* @default
*/
XPATH_INITIAL: "",
/**
* @constant
* @default
*/
XPATH_DELIMITER: "\\n",
/**
* XPath expression operation.
*
* @author Mikescher (https://github.com/Mikescher | https://mikescher.com)
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runXpath:function(input, args) {
let query = args[0],
delimiter = args[1];
let doc;
try {
doc = new dom().parseFromString(input);
} catch (err) {
return "Invalid input XML.";
}
let nodes;
try {
nodes = xpath.select(query, doc);
} catch (err) {
return "Invalid XPath. Details:\n" + err.message;
}
const nodeToString = function(node) {
return node.toString();
};
return nodes.map(nodeToString).join(delimiter);
},
/**
* @constant
* @default
*/
CSS_SELECTOR_INITIAL: "",
/**
* @constant
* @default
*/
CSS_QUERY_DELIMITER: "\\n",
/**
* CSS selector operation.
*
* @author Mikescher (https://github.com/Mikescher | https://mikescher.com)
* @author n1474335 [n1474335@gmail.com]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runCSSQuery: function(input, args) {
let query = args[0],
delimiter = args[1],
parser = new DOMParser(),
html,
result;
if (!query.length || !input.length) {
return "";
}
try {
html = parser.parseFromString(input, "text/html");
} catch (err) {
return "Invalid input HTML.";
}
try {
result = html.querySelectorAll(query);
} catch (err) {
return "Invalid CSS Selector. Details:\n" + err.message;
}
const nodeToString = function(node) {
switch (node.nodeType) {
case Node.ELEMENT_NODE: return node.outerHTML;
case Node.ATTRIBUTE_NODE: return node.value;
case Node.COMMENT_NODE: return node.data;
case Node.TEXT_NODE: return node.wholeText;
case Node.DOCUMENT_NODE: return node.outerHTML;
default: throw new Error("Unknown Node Type: " + node.nodeType);
}
};
return Array.apply(null, Array(result.length))
.map(function(_, i) {
return result[i];
})
.map(nodeToString)
.join(delimiter);
},
/**
* This tries to rename variable names in a code snippet according to a function.
*
* @param {string} input
* @param {function} replacer - this function will be fed the token which should be renamed.
* @returns {string}
*/
_replaceVariableNames(input, replacer) {
const tokenRegex = /\\"|"(?:\\"|[^"])*"|(\b[a-z0-9\-_]+\b)/ig;
return input.replace(tokenRegex, (...args) => {
let match = args[0],
quotes = args[1];
if (!quotes) {
return match;
} else {
return replacer(match);
}
});
},
/**
* Converts to snake_case.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
*/
runToSnakeCase(input, args) {
const smart = args[0];
if (smart) {
return Code._replaceVariableNames(input, snakeCase);
} else {
return snakeCase(input);
}
},
/**
* Converts to camelCase.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
*/
runToCamelCase(input, args) {
const smart = args[0];
if (smart) {
return Code._replaceVariableNames(input, camelCase);
} else {
return camelCase(input);
}
},
/**
* Converts to kebab-case.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
*/
runToKebabCase(input, args) {
const smart = args[0];
if (smart) {
return Code._replaceVariableNames(input, kebabCase);
} else {
return kebabCase(input);
}
},
};
export default Code;

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,52 @@
/**
* @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 = "Crypto";
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.infoURL = "https://forensicswiki.org/wiki/Context_Triggered_Piecewise_Hashing";
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,52 @@
/**
* @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 = "Crypto";
this.description = "Compares two SSDEEP fuzzy hashes to determine the similarity between them on a scale of 0 to 100.";
this.infoURL = "https://forensicswiki.org/wiki/Ssdeep";
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

@ -1,578 +0,0 @@
import Utils from "../Utils.js";
import rawdeflate from "zlibjs/bin/rawdeflate.min";
import rawinflate from "zlibjs/bin/rawinflate.min";
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min";
import zip from "zlibjs/bin/zip.min";
import unzip from "zlibjs/bin/unzip.min";
import bzip2 from "exports-loader?bzip2!../lib/bzip2.js";
const Zlib = {
RawDeflate: rawdeflate.Zlib.RawDeflate,
RawInflate: rawinflate.Zlib.RawInflate,
Deflate: zlibAndGzip.Zlib.Deflate,
Inflate: zlibAndGzip.Zlib.Inflate,
Gzip: zlibAndGzip.Zlib.Gzip,
Gunzip: zlibAndGzip.Zlib.Gunzip,
Zip: zip.Zlib.Zip,
Unzip: unzip.Zlib.Unzip,
};
/**
* Compression operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Compress = {
/**
* @constant
* @default
*/
COMPRESSION_TYPE: ["Dynamic Huffman Coding", "Fixed Huffman Coding", "None (Store)"],
/**
* @constant
* @default
*/
INFLATE_BUFFER_TYPE: ["Adaptive", "Block"],
/**
* @constant
* @default
*/
COMPRESSION_METHOD: ["Deflate", "None (Store)"],
/**
* @constant
* @default
*/
OS: ["MSDOS", "Unix", "Macintosh"],
/**
* @constant
* @default
*/
RAW_COMPRESSION_TYPE_LOOKUP: {
"Fixed Huffman Coding" : Zlib.RawDeflate.CompressionType.FIXED,
"Dynamic Huffman Coding" : Zlib.RawDeflate.CompressionType.DYNAMIC,
"None (Store)" : Zlib.RawDeflate.CompressionType.NONE,
},
/**
* Raw Deflate operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runRawDeflate: function(input, args) {
const deflate = new Zlib.RawDeflate(input, {
compressionType: Compress.RAW_COMPRESSION_TYPE_LOOKUP[args[0]]
});
return Array.prototype.slice.call(deflate.compress());
},
/**
* @constant
* @default
*/
INFLATE_INDEX: 0,
/**
* @constant
* @default
*/
INFLATE_BUFFER_SIZE: 0,
/**
* @constant
* @default
*/
INFLATE_RESIZE: false,
/**
* @constant
* @default
*/
INFLATE_VERIFY: false,
/**
* @constant
* @default
*/
RAW_BUFFER_TYPE_LOOKUP: {
"Adaptive" : Zlib.RawInflate.BufferType.ADAPTIVE,
"Block" : Zlib.RawInflate.BufferType.BLOCK,
},
/**
* Raw Inflate operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runRawInflate: function(input, args) {
// Deal with character encoding issues
input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
let inflate = new Zlib.RawInflate(input, {
index: args[0],
bufferSize: args[1],
bufferType: Compress.RAW_BUFFER_TYPE_LOOKUP[args[2]],
resize: args[3],
verify: args[4]
}),
result = Array.prototype.slice.call(inflate.decompress());
// Raw Inflate somethimes messes up and returns nonsense like this:
// ]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]...
// e.g. Input data of [8b, 1d, dc, 44]
// Look for the first two square brackets:
if (result.length > 158 && result[0] === 93 && result[5] === 93) {
// If the first two square brackets are there, check that the others
// are also there. If they are, throw an error. If not, continue.
let valid = false;
for (let i = 0; i < 155; i += 5) {
if (result[i] !== 93) {
valid = true;
}
}
if (!valid) {
throw "Error: Unable to inflate data";
}
}
// Trust me, this is the easiest way...
return result;
},
/**
* @constant
* @default
*/
ZLIB_COMPRESSION_TYPE_LOOKUP: {
"Fixed Huffman Coding" : Zlib.Deflate.CompressionType.FIXED,
"Dynamic Huffman Coding" : Zlib.Deflate.CompressionType.DYNAMIC,
"None (Store)" : Zlib.Deflate.CompressionType.NONE,
},
/**
* Zlib Deflate operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runZlibDeflate: function(input, args) {
const deflate = new Zlib.Deflate(input, {
compressionType: Compress.ZLIB_COMPRESSION_TYPE_LOOKUP[args[0]]
});
return Array.prototype.slice.call(deflate.compress());
},
/**
* @constant
* @default
*/
ZLIB_BUFFER_TYPE_LOOKUP: {
"Adaptive" : Zlib.Inflate.BufferType.ADAPTIVE,
"Block" : Zlib.Inflate.BufferType.BLOCK,
},
/**
* Zlib Inflate operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runZlibInflate: function(input, args) {
// Deal with character encoding issues
input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
const inflate = new Zlib.Inflate(input, {
index: args[0],
bufferSize: args[1],
bufferType: Compress.ZLIB_BUFFER_TYPE_LOOKUP[args[2]],
resize: args[3],
verify: args[4]
});
return Array.prototype.slice.call(inflate.decompress());
},
/**
* @constant
* @default
*/
GZIP_CHECKSUM: false,
/**
* Gzip operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runGzip: function(input, args) {
let filename = args[1],
comment = args[2],
options = {
deflateOptions: {
compressionType: Compress.ZLIB_COMPRESSION_TYPE_LOOKUP[args[0]]
},
flags: {
fhcrc: args[3]
}
};
if (filename.length) {
options.flags.fname = true;
options.filename = filename;
}
if (comment.length) {
options.flags.fcommenct = true;
options.comment = comment;
}
const gzip = new Zlib.Gzip(input, options);
return Array.prototype.slice.call(gzip.compress());
},
/**
* Gunzip operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runGunzip: function(input, args) {
// Deal with character encoding issues
input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
const gunzip = new Zlib.Gunzip(input);
return Array.prototype.slice.call(gunzip.decompress());
},
/**
* @constant
* @default
*/
PKZIP_FILENAME: "file.txt",
/**
* @constant
* @default
*/
ZIP_COMPRESSION_METHOD_LOOKUP: {
"Deflate" : Zlib.Zip.CompressionMethod.DEFLATE,
"None (Store)" : Zlib.Zip.CompressionMethod.STORE
},
/**
* @constant
* @default
*/
ZIP_OS_LOOKUP: {
"MSDOS" : Zlib.Zip.OperatingSystem.MSDOS,
"Unix" : Zlib.Zip.OperatingSystem.UNIX,
"Macintosh" : Zlib.Zip.OperatingSystem.MACINTOSH
},
/**
* Zip operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runPkzip: function(input, args) {
let password = Utils.strToByteArray(args[2]),
options = {
filename: Utils.strToByteArray(args[0]),
comment: Utils.strToByteArray(args[1]),
compressionMethod: Compress.ZIP_COMPRESSION_METHOD_LOOKUP[args[3]],
os: Compress.ZIP_OS_LOOKUP[args[4]],
deflateOption: {
compressionType: Compress.ZLIB_COMPRESSION_TYPE_LOOKUP[args[5]]
},
},
zip = new Zlib.Zip();
if (password.length)
zip.setPassword(password);
zip.addFile(input, options);
return Array.prototype.slice.call(zip.compress());
},
/**
* @constant
* @default
*/
PKUNZIP_VERIFY: false,
/**
* Unzip operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runPkunzip: function(input, args) {
let options = {
password: Utils.strToByteArray(args[0]),
verify: args[1]
},
unzip = new Zlib.Unzip(input, options),
filenames = unzip.getFilenames(),
files = [];
filenames.forEach(function(fileName) {
const bytes = unzip.decompress(fileName);
const contents = Utils.byteArrayToUtf8(bytes);
const file = {
fileName: fileName,
size: contents.length,
};
const isDir = contents.length === 0 && fileName.endsWith("/");
if (!isDir) {
file.bytes = bytes;
file.contents = contents;
}
files.push(file);
});
return Utils.displayFilesAsHTML(files);
},
/**
* Bzip2 Decompress operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runBzip2Decompress: function(input, args) {
let compressed = new Uint8Array(input),
bzip2Reader,
plain = "";
bzip2Reader = bzip2.array(compressed);
plain = bzip2.simple(bzip2Reader);
return plain;
},
/**
* @constant
* @default
*/
TAR_FILENAME: "file.txt",
/**
* Tar pack operation.
*
* @author tlwr [toby@toby.codes]
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runTar: function(input, args) {
const Tarball = function() {
this.bytes = new Array(512);
this.position = 0;
};
Tarball.prototype.addEmptyBlock = function() {
const filler = new Array(512);
filler.fill(0);
this.bytes = this.bytes.concat(filler);
};
Tarball.prototype.writeBytes = function(bytes) {
const self = this;
if (this.position + bytes.length > this.bytes.length) {
this.addEmptyBlock();
}
Array.prototype.forEach.call(bytes, function(b, i) {
if (typeof b.charCodeAt !== "undefined") {
b = b.charCodeAt();
}
self.bytes[self.position] = b;
self.position += 1;
});
};
Tarball.prototype.writeEndBlocks = function() {
const numEmptyBlocks = 2;
for (let i = 0; i < numEmptyBlocks; i++) {
this.addEmptyBlock();
}
};
const fileSize = Utils.padLeft(input.length.toString(8), 11, "0");
const currentUnixTimestamp = Math.floor(Date.now() / 1000);
const lastModTime = Utils.padLeft(currentUnixTimestamp.toString(8), 11, "0");
const file = {
fileName: Utils.padBytesRight(args[0], 100),
fileMode: Utils.padBytesRight("0000664", 8),
ownerUID: Utils.padBytesRight("0", 8),
ownerGID: Utils.padBytesRight("0", 8),
size: Utils.padBytesRight(fileSize, 12),
lastModTime: Utils.padBytesRight(lastModTime, 12),
checksum: " ",
type: "0",
linkedFileName: Utils.padBytesRight("", 100),
USTARFormat: Utils.padBytesRight("ustar", 6),
version: "00",
ownerUserName: Utils.padBytesRight("", 32),
ownerGroupName: Utils.padBytesRight("", 32),
deviceMajor: Utils.padBytesRight("", 8),
deviceMinor: Utils.padBytesRight("", 8),
fileNamePrefix: Utils.padBytesRight("", 155),
};
let checksum = 0;
for (const key in file) {
const bytes = file[key];
Array.prototype.forEach.call(bytes, function(b) {
if (typeof b.charCodeAt !== "undefined") {
checksum += b.charCodeAt();
} else {
checksum += b;
}
});
}
checksum = Utils.padBytesRight(Utils.padLeft(checksum.toString(8), 7, "0"), 8);
file.checksum = checksum;
const tarball = new Tarball();
tarball.writeBytes(file.fileName);
tarball.writeBytes(file.fileMode);
tarball.writeBytes(file.ownerUID);
tarball.writeBytes(file.ownerGID);
tarball.writeBytes(file.size);
tarball.writeBytes(file.lastModTime);
tarball.writeBytes(file.checksum);
tarball.writeBytes(file.type);
tarball.writeBytes(file.linkedFileName);
tarball.writeBytes(file.USTARFormat);
tarball.writeBytes(file.version);
tarball.writeBytes(file.ownerUserName);
tarball.writeBytes(file.ownerGroupName);
tarball.writeBytes(file.deviceMajor);
tarball.writeBytes(file.deviceMinor);
tarball.writeBytes(file.fileNamePrefix);
tarball.writeBytes(Utils.padBytesRight("", 12));
tarball.writeBytes(input);
tarball.writeEndBlocks();
return tarball.bytes;
},
/**
* Untar unpack operation.
*
* @author tlwr [toby@toby.codes]
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {html}
*/
runUntar: function(input, args) {
const Stream = function(input) {
this.bytes = input;
this.position = 0;
};
Stream.prototype.getBytes = function(bytesToGet) {
const newPosition = this.position + bytesToGet;
const bytes = this.bytes.slice(this.position, newPosition);
this.position = newPosition;
return bytes;
};
Stream.prototype.readString = function(numBytes) {
let result = "";
for (let i = this.position; i < this.position + numBytes; i++) {
const currentByte = this.bytes[i];
if (currentByte === 0) break;
result += String.fromCharCode(currentByte);
}
this.position += numBytes;
return result;
};
Stream.prototype.readInt = function(numBytes, base) {
const string = this.readString(numBytes);
return parseInt(string, base);
};
Stream.prototype.hasMore = function() {
return this.position < this.bytes.length;
};
let stream = new Stream(input),
files = [];
while (stream.hasMore()) {
const dataPosition = stream.position + 512;
const file = {
fileName: stream.readString(100),
fileMode: stream.readString(8),
ownerUID: stream.readString(8),
ownerGID: stream.readString(8),
size: parseInt(stream.readString(12), 8), // Octal
lastModTime: new Date(1000 * stream.readInt(12, 8)), // Octal
checksum: stream.readString(8),
type: stream.readString(1),
linkedFileName: stream.readString(100),
USTARFormat: stream.readString(6).indexOf("ustar") >= 0,
};
if (file.USTARFormat) {
file.version = stream.readString(2);
file.ownerUserName = stream.readString(32);
file.ownerGroupName = stream.readString(32);
file.deviceMajor = stream.readString(8);
file.deviceMinor = stream.readString(8);
file.filenamePrefix = stream.readString(155);
}
stream.position = dataPosition;
if (file.type === "0") {
// File
files.push(file);
let endPosition = stream.position + file.size;
if (file.size % 512 !== 0) {
endPosition += 512 - (file.size % 512);
}
file.bytes = stream.getBytes(file.size);
file.contents = Utils.byteArrayToUtf8(file.bytes);
stream.position = endPosition;
} else if (file.type === "5") {
// Directory
files.push(file);
} else {
// Symlink or empty bytes
}
}
return Utils.displayFilesAsHTML(files);
},
};
export default Compress;

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

@ -1,414 +0,0 @@
/**
* Unit conversion operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Convert = {
/**
* @constant
* @default
*/
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]",
],
/**
* @constant
* @default
*/
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
},
/**
* Convert distance operation.
*
* @param {number} input
* @param {Object[]} args
* @returns {number}
*/
runDistance: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input * Convert.DISTANCE_FACTOR[inputUnits];
return input / Convert.DISTANCE_FACTOR[outputUnits];
// TODO Remove rounding errors (e.g. 1.000000000001)
},
/**
* @constant
* @default
*/
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)]"
],
/**
* @constant
* @default
*/
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,
},
/**
* Convert data units operation.
*
* @param {number} input
* @param {Object[]} args
* @returns {number}
*/
runDataSize: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input * Convert.DATA_FACTOR[inputUnits];
return input / Convert.DATA_FACTOR[outputUnits];
},
/**
* @constant
* @default
*/
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]",
],
/**
* @constant
* @default
*/
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,
},
/**
* Convert area operation.
*
* @param {number} input
* @param {Object[]} args
* @returns {number}
*/
runArea: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input * Convert.AREA_FACTOR[inputUnits];
return input / Convert.AREA_FACTOR[outputUnits];
},
/**
* @constant
* @default
*/
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]",
],
/**
* @constant
* @default
*/
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,
},
/**
* Convert mass operation.
*
* @param {number} input
* @param {Object[]} args
* @returns {number}
*/
runMass: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input * Convert.MASS_FACTOR[inputUnits];
return input / Convert.MASS_FACTOR[outputUnits];
},
/**
* @constant
* @default
*/
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]",
],
/**
* @constant
* @default
*/
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,
},
/**
* Convert speed operation.
*
* @param {number} input
* @param {Object[]} args
* @returns {number}
*/
runSpeed: function (input, args) {
let inputUnits = args[0],
outputUnits = args[1];
input = input * Convert.SPEED_FACTOR[inputUnits];
return input / Convert.SPEED_FACTOR[outputUnits];
},
};
export default Convert;

View file

@ -0,0 +1,113 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Orders_of_magnitude_(area)";
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,95 @@
/**
* @author j433866 [j433866@gmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation";
import {FORMATS, convertCoordinates} from "../lib/ConvertCoordinates";
/**
* Convert co-ordinate format operation
*/
class ConvertCoordinateFormat extends Operation {
/**
* ConvertCoordinateFormat constructor
*/
constructor() {
super();
this.name = "Convert co-ordinate format";
this.module = "Hashing";
this.description = "Converts geographical coordinates between different formats.<br><br>Supported formats:<ul><li>Degrees Minutes Seconds (DMS)</li><li>Degrees Decimal Minutes (DDM)</li><li>Decimal Degrees (DD)</li><li>Geohash</li><li>Military Grid Reference System (MGRS)</li><li>Ordnance Survey National Grid (OSNG)</li><li>Universal Transverse Mercator (UTM)</li></ul><br>The operation can try to detect the input co-ordinate format and delimiter automatically, but this may not always work correctly.";
this.infoURL = "https://wikipedia.org/wiki/Geographic_coordinate_conversion";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Input Format",
"type": "option",
"value": ["Auto"].concat(FORMATS)
},
{
"name": "Input Delimiter",
"type": "option",
"value": [
"Auto",
"Direction Preceding",
"Direction Following",
"\\n",
"Comma",
"Semi-colon",
"Colon"
]
},
{
"name": "Output Format",
"type": "option",
"value": FORMATS
},
{
"name": "Output Delimiter",
"type": "option",
"value": [
"Space",
"\\n",
"Comma",
"Semi-colon",
"Colon"
]
},
{
"name": "Include Compass Directions",
"type": "option",
"value": [
"None",
"Before",
"After"
]
},
{
"name": "Precision",
"type": "number",
"value": 3
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (input.replace(/[\s+]/g, "") !== "") {
const [inFormat, inDelim, outFormat, outDelim, incDirection, precision] = args;
const result = convertCoordinates(input, inFormat, inDelim, outFormat, outDelim, incDirection, precision);
return result;
} else {
return input;
}
}
}
export default ConvertCoordinateFormat;

View file

@ -0,0 +1,112 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Orders_of_magnitude_(data)";
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,96 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Orders_of_magnitude_(length)";
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,144 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Orders_of_magnitude_(mass)";
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,97 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Orders_of_magnitude_(speed)";
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,93 @@
/**
* @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";
import forge from "node-forge/dist/forge.min.js";
/**
* DES Decrypt operation
*/
class DESDecrypt extends Operation {
/**
* DESDecrypt constructor
*/
constructor() {
super();
this.name = "DES Decrypt";
this.module = "Ciphers";
this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br>Triple DES uses a key length of 24 bytes (192 bits).<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used.";
this.infoURL = "https://wikipedia.org/wiki/Data_Encryption_Standard";
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", "ECB"]
},
{
"name": "Input",
"type": "option",
"value": ["Hex", "Raw"]
},
{
"name": "Output",
"type": "option",
"value": ["Raw", "Hex"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
[,, mode, inputType, outputType] = args;
if (key.length !== 8) {
throw new OperationError(`Invalid key length: ${key.length} bytes
DES uses a key length of 8 bytes (64 bits).
Triple DES uses a key length of 24 bytes (192 bits).`);
}
input = Utils.convertToByteString(input, inputType);
const decipher = forge.cipher.createDecipher("DES-" + mode, key);
decipher.start({iv: iv});
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 DESDecrypt;

View file

@ -0,0 +1,89 @@
/**
* @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";
import forge from "node-forge/dist/forge.min.js";
/**
* DES Encrypt operation
*/
class DESEncrypt extends Operation {
/**
* DESEncrypt constructor
*/
constructor() {
super();
this.name = "DES Encrypt";
this.module = "Ciphers";
this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br>Triple DES uses a key length of 24 bytes (192 bits).<br><br>You can generate a password-based key using one of the KDF operations.<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used.";
this.infoURL = "https://wikipedia.org/wiki/Data_Encryption_Standard";
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", "ECB"]
},
{
"name": "Input",
"type": "option",
"value": ["Raw", "Hex"]
},
{
"name": "Output",
"type": "option",
"value": ["Hex", "Raw"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
[,, mode, inputType, outputType] = args;
if (key.length !== 8) {
throw new OperationError(`Invalid key length: ${key.length} bytes
DES uses a key length of 8 bytes (64 bits).
Triple DES uses a key length of 24 bytes (192 bits).`);
}
input = Utils.convertToByteString(input, inputType);
const cipher = forge.cipher.createCipher("DES-" + mode, key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(input));
cipher.finish();
return outputType === "Hex" ? cipher.output.toHex() : cipher.output.getBytes();
}
}
export default DESEncrypt;

View file

@ -0,0 +1,125 @@
/**
* @author h345983745
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
/**
* DNS over HTTPS operation
*/
class DNSOverHTTPS extends Operation {
/**
* DNSOverHTTPS constructor
*/
constructor() {
super();
this.name = "DNS over HTTPS";
this.module = "Default";
this.description = [
"Takes a single domain name and performs a DNS lookup using DNS over HTTPS.",
"<br><br>",
"By default, <a href='https://developers.cloudflare.com/1.1.1.1/dns-over-https/'>Cloudflare</a> and <a href='https://developers.google.com/speed/public-dns/docs/dns-over-https'>Google</a> DNS over HTTPS services are supported.",
"<br><br>",
"Can be used with any service that supports the GET parameters <code>name</code> and <code>type</code>."
].join("\n");
this.infoURL = "https://wikipedia.org/wiki/DNS_over_HTTPS";
this.inputType = "string";
this.outputType = "JSON";
this.manualBake = true;
this.args = [
{
name: "Resolver",
type: "editableOption",
value: [
{
name: "Google",
value: "https://dns.google.com/resolve"
},
{
name: "Cloudflare",
value: "https://cloudflare-dns.com/dns-query"
}
]
},
{
name: "Request Type",
type: "option",
value: [
"A",
"AAAA",
"TXT",
"MX",
"DNSKEY",
"NS"
]
},
{
name: "Answer Data Only",
type: "boolean",
value: false
},
{
name: "Validate DNSSEC",
type: "boolean",
value: true
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {JSON}
*/
run(input, args) {
const [resolver, requestType, justAnswer, DNSSEC] = args;
let url = URL;
try {
url = new URL(resolver);
} catch (error) {
throw new OperationError(error.toString() +
"\n\nThis error could be caused by one of the following:\n" +
" - An invalid Resolver URL\n");
}
const params = {name: input, type: requestType, cd: DNSSEC};
url.search = new URLSearchParams(params);
return fetch(url, {headers: {"accept": "application/dns-json"}}).then(response => {
return response.json();
}).then(data => {
if (justAnswer) {
return extractData(data.Answer);
}
return data;
}).catch(e => {
throw new OperationError(`Error making request to ${url}\n${e.toString()}`);
});
}
}
/**
* Construct an array of just data from a DNS Answer section
*
* @private
* @param {JSON} data
* @returns {JSON}
*/
function extractData(data) {
if (typeof(data) == "undefined"){
return [];
} else {
const dataValues = [];
data.forEach(element => {
dataValues.push(element.data);
});
return dataValues;
}
}
export default DNSOverHTTPS;

View file

@ -1,460 +0,0 @@
/**
* Date and time operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const DateTime = {
/**
* @constant
* @default
*/
UNITS: ["Seconds (s)", "Milliseconds (ms)", "Microseconds (μs)", "Nanoseconds (ns)"],
/**
* From UNIX Timestamp operation.
*
* @param {number} input
* @param {Object[]} args
* @returns {string}
*/
runFromUnixTimestamp: function(input, args) {
let units = args[0],
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 "Unrecognised unit";
}
},
/**
* @constant
* @default
*/
TREAT_AS_UTC: true,
/**
* To UNIX Timestamp operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
*/
runToUnixTimestamp: function(input, args) {
let units = args[0],
treatAsUTC = args[1],
d = treatAsUTC ? moment.utc(input) : moment(input);
if (units === "Seconds (s)") {
return d.unix();
} else if (units === "Milliseconds (ms)") {
return d.valueOf();
} else if (units === "Microseconds (μs)") {
return d.valueOf() * 1000;
} else if (units === "Nanoseconds (ns)") {
return d.valueOf() * 1000000;
} else {
throw "Unrecognised unit";
}
},
/**
* @constant
* @default
*/
DATETIME_FORMATS: [
{
name: "Standard date and time",
value: "DD/MM/YYYY HH:mm:ss"
},
{
name: "American-style date and time",
value: "MM/DD/YYYY HH:mm:ss"
},
{
name: "International date and time",
value: "YYYY-MM-DD HH:mm:ss"
},
{
name: "Verbose date and time",
value: "dddd Do MMMM YYYY HH:mm:ss Z z"
},
{
name: "UNIX timestamp (seconds)",
value: "X"
},
{
name: "UNIX timestamp offset (milliseconds)",
value: "x"
},
{
name: "Automatic",
value: ""
},
],
/**
* @constant
* @default
*/
INPUT_FORMAT_STRING: "DD/MM/YYYY HH:mm:ss",
/**
* @constant
* @default
*/
OUTPUT_FORMAT_STRING: "dddd Do MMMM YYYY HH:mm:ss Z z",
/**
* @constant
* @default
*/
TIMEZONES: ["UTC"].concat(moment.tz.names()),
/**
* Translate DateTime Format operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runTranslateFormat: function(input, args) {
let inputFormat = args[1],
inputTimezone = args[2],
outputFormat = args[3],
outputTimezone = args[4],
date;
try {
date = moment.tz(input, inputFormat, inputTimezone);
if (!date || date.format() === "Invalid date") throw Error;
} catch (err) {
return "Invalid format.\n\n" + DateTime.FORMAT_EXAMPLES;
}
return date.tz(outputTimezone).format(outputFormat);
},
/**
* Parse DateTime operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
runParse: function(input, args) {
let inputFormat = args[1],
inputTimezone = args[2],
date,
output = "";
try {
date = moment.tz(input, inputFormat, inputTimezone);
if (!date || date.format() === "Invalid date") throw Error;
} catch (err) {
return "Invalid format.\n\n" + DateTime.FORMAT_EXAMPLES;
}
output += "Date: " + date.format("dddd Do MMMM YYYY") +
"\nTime: " + date.format("HH:mm:ss") +
"\nPeriod: " + date.format("A") +
"\nTimezone: " + date.format("z") +
"\nUTC offset: " + date.format("ZZ") +
"\n\nDaylight Saving Time: " + date.isDST() +
"\nLeap year: " + date.isLeapYear() +
"\nDays in this month: " + date.daysInMonth() +
"\n\nDay of year: " + date.dayOfYear() +
"\nWeek number: " + date.weekYear() +
"\nQuarter: " + date.quarter();
return output;
},
/**
* @constant
*/
FORMAT_EXAMPLES: "Format string tokens:\n\n\
<table class='table table-striped table-hover table-condensed table-bordered' style='font-family: sans-serif'>\
<thead>\
<tr>\
<th>Category</th>\
<th>Token</th>\
<th>Output</th>\
</tr>\
</thead>\
<tbody>\
<tr>\
<td><b>Month</b></td>\
<td>M</td>\
<td>1 2 ... 11 12</td>\
</tr>\
<tr>\
<td></td>\
<td>Mo</td>\
<td>1st 2nd ... 11th 12th</td>\
</tr>\
<tr>\
<td></td>\
<td>MM</td>\
<td>01 02 ... 11 12</td>\
</tr>\
<tr>\
<td></td>\
<td>MMM</td>\
<td>Jan Feb ... Nov Dec</td>\
</tr>\
<tr>\
<td></td>\
<td>MMMM</td>\
<td>January February ... November December</td>\
</tr>\
<tr>\
<td><b>Quarter</b></td>\
<td>Q</td>\
<td>1 2 3 4</td>\
</tr>\
<tr>\
<td><b>Day of Month</b></td>\
<td>D</td>\
<td>1 2 ... 30 31</td>\
</tr>\
<tr>\
<td></td>\
<td>Do</td>\
<td>1st 2nd ... 30th 31st</td>\
</tr>\
<tr>\
<td></td>\
<td>DD</td>\
<td>01 02 ... 30 31</td>\
</tr>\
<tr>\
<td><b>Day of Year</b></td>\
<td>DDD</td>\
<td>1 2 ... 364 365</td>\
</tr>\
<tr>\
<td></td>\
<td>DDDo</td>\
<td>1st 2nd ... 364th 365th</td>\
</tr>\
<tr>\
<td></td>\
<td>DDDD</td>\
<td>001 002 ... 364 365</td>\
</tr>\
<tr>\
<td><b>Day of Week</b></td>\
<td>d</td>\
<td>0 1 ... 5 6</td>\
</tr>\
<tr>\
<td></td>\
<td>do</td>\
<td>0th 1st ... 5th 6th</td>\
</tr>\
<tr>\
<td></td>\
<td>dd</td>\
<td>Su Mo ... Fr Sa</td>\
</tr>\
<tr>\
<td></td>\
<td>ddd</td>\
<td>Sun Mon ... Fri Sat</td>\
</tr>\
<tr>\
<td></td>\
<td>dddd</td>\
<td>Sunday Monday ... Friday Saturday</td>\
</tr>\
<tr>\
<td><b>Day of Week (Locale)</b></td>\
<td>e</td>\
<td>0 1 ... 5 6</td>\
</tr>\
<tr>\
<td><b>Day of Week (ISO)</b></td>\
<td>E</td>\
<td>1 2 ... 6 7</td>\
</tr>\
<tr>\
<td><b>Week of Year</b></td>\
<td>w</td>\
<td>1 2 ... 52 53</td>\
</tr>\
<tr>\
<td></td>\
<td>wo</td>\
<td>1st 2nd ... 52nd 53rd</td>\
</tr>\
<tr>\
<td></td>\
<td>ww</td>\
<td>01 02 ... 52 53</td>\
</tr>\
<tr>\
<td><b>Week of Year (ISO)</b></td>\
<td>W</td>\
<td>1 2 ... 52 53</td>\
</tr>\
<tr>\
<td></td>\
<td>Wo</td>\
<td>1st 2nd ... 52nd 53rd</td>\
</tr>\
<tr>\
<td></td>\
<td>WW</td>\
<td>01 02 ... 52 53</td>\
</tr>\
<tr>\
<td><b>Year</b></td>\
<td>YY</td>\
<td>70 71 ... 29 30</td>\
</tr>\
<tr>\
<td></td>\
<td>YYYY</td>\
<td>1970 1971 ... 2029 2030</td>\
</tr>\
<tr>\
<td><b>Week Year</b></td>\
<td>gg</td>\
<td>70 71 ... 29 30</td>\
</tr>\
<tr>\
<td></td>\
<td>gggg</td>\
<td>1970 1971 ... 2029 2030</td>\
</tr>\
<tr>\
<td><b>Week Year (ISO)</b></td>\
<td>GG</td>\
<td>70 71 ... 29 30</td>\
</tr>\
<tr>\
<td></td>\
<td>GGGG</td>\
<td>1970 1971 ... 2029 2030</td>\
</tr>\
<tr>\
<td><b>AM/PM</b></td>\
<td>A</td>\
<td>AM PM</td>\
</tr>\
<tr>\
<td></td>\
<td>a</td>\
<td>am pm</td>\
</tr>\
<tr>\
<td><b>Hour</b></td>\
<td>H</td>\
<td>0 1 ... 22 23</td>\
</tr>\
<tr>\
<td></td>\
<td>HH</td>\
<td>00 01 ... 22 23</td>\
</tr>\
<tr>\
<td></td>\
<td>h</td>\
<td>1 2 ... 11 12</td>\
</tr>\
<tr>\
<td></td>\
<td>hh</td>\
<td>01 02 ... 11 12</td>\
</tr>\
<tr>\
<td><b>Minute</b></td>\
<td>m</td>\
<td>0 1 ... 58 59</td>\
</tr>\
<tr>\
<td></td>\
<td>mm</td>\
<td>00 01 ... 58 59</td>\
</tr>\
<tr>\
<td><b>Second</b></td>\
<td>s</td>\
<td>0 1 ... 58 59</td>\
</tr>\
<tr>\
<td></td>\
<td>ss</td>\
<td>00 01 ... 58 59</td>\
</tr>\
<tr>\
<td><b>Fractional Second</b></td>\
<td>S</td>\
<td>0 1 ... 8 9</td>\
</tr>\
<tr>\
<td></td>\
<td>SS</td>\
<td>00 01 ... 98 99</td>\
</tr>\
<tr>\
<td></td>\
<td>SSS</td>\
<td>000 001 ... 998 999</td>\
</tr>\
<tr>\
<td></td>\
<td>SSSS ... SSSSSSSSS</td>\
<td>000[0..] 001[0..] ... 998[0..] 999[0..]</td>\
</tr>\
<tr>\
<td><b>Timezone</b></td>\
<td>z or zz</td>\
<td>EST CST ... MST PST</td>\
</tr>\
<tr>\
<td></td>\
<td>Z</td>\
<td>-07:00 -06:00 ... +06:00 +07:00</td>\
</tr>\
<tr>\
<td></td>\
<td>ZZ</td>\
<td>-0700 -0600 ... +0600 +0700</td>\
</tr>\
<tr>\
<td><b>Unix Timestamp</b></td>\
<td>X</td>\
<td>1360013296</td>\
</tr>\
<tr>\
<td><b>Unix Millisecond Timestamp</b></td>\
<td>x</td>\
<td>1360013296123</td>\
</tr>\
</tbody>\
</table>",
};
export default DateTime;

View file

@ -0,0 +1,51 @@
/**
* @author sevzero [sevzero@protonmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Dechunk HTTP response operation
*/
class DechunkHTTPResponse extends Operation {
/**
* DechunkHTTPResponse constructor
*/
constructor() {
super();
this.name = "Dechunk HTTP response";
this.module = "Default";
this.description = "Parses an HTTP response transferred using Transfer-Encoding: Chunked";
this.infoURL = "https://wikipedia.org/wiki/Chunked_transfer_encoding";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const chunks = [];
let chunkSizeEnd = input.indexOf("\n") + 1;
const lineEndings = input.charAt(chunkSizeEnd - 2) === "\r" ? "\r\n" : "\n";
const lineEndingsLength = lineEndings.length;
let chunkSize = parseInt(input.slice(0, chunkSizeEnd), 16);
while (!isNaN(chunkSize)) {
chunks.push(input.slice(chunkSizeEnd, chunkSize + chunkSizeEnd));
input = input.slice(chunkSizeEnd + chunkSize + lineEndingsLength);
chunkSizeEnd = input.indexOf(lineEndings) + lineEndingsLength;
chunkSize = parseInt(input.slice(0, chunkSizeEnd), 16);
}
return chunks.join("") + input;
}
}
export default DechunkHTTPResponse;

View file

@ -0,0 +1,60 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/NetBIOS";
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,56 @@
/**
* @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 = "Encodings";
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.infoURL = "https://wikipedia.org/wiki/Character_encoding";
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,102 @@
/**
* @author arnydo [arnydo@protonmail.com]
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import {URL_REGEX, DOMAIN_REGEX} from "../lib/Extract";
/**
* DefangURL operation
*/
class DefangURL extends Operation {
/**
* DefangURL constructor
*/
constructor() {
super();
this.name = "Defang URL";
this.module = "Default";
this.description = "Takes a Universal Resource Locator (URL) and 'Defangs' it; meaning the URL becomes invalid, neutralising the risk of accidentally clicking on a malicious link.<br><br>This is often used when dealing with malicious links or IOCs.<br><br>Works well when combined with the 'Extract URLs' operation.";
this.infoURL = "https://isc.sans.edu/forums/diary/Defang+all+the+things/22744/";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "Escape dots",
type: "boolean",
value: true
},
{
name: "Escape http",
type: "boolean",
value: true
},
{
name: "Escape ://",
type: "boolean",
value: true
},
{
name: "Process",
type: "option",
value: ["Valid domains and full URLs", "Only full URLs", "Everything"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [dots, http, slashes, process] = args;
switch (process) {
case "Valid domains and full URLs":
input = input.replace(URL_REGEX, x => {
return defangURL(x, dots, http, slashes);
});
input = input.replace(DOMAIN_REGEX, x => {
return defangURL(x, dots, http, slashes);
});
break;
case "Only full URLs":
input = input.replace(URL_REGEX, x => {
return defangURL(x, dots, http, slashes);
});
break;
case "Everything":
input = defangURL(input, dots, http, slashes);
break;
}
return input;
}
}
/**
* Defangs a given URL
*
* @param {string} url
* @param {boolean} dots
* @param {boolean} http
* @param {boolean} slashes
* @returns {string}
*/
function defangURL(url, dots, http, slashes) {
if (dots) url = url.replace(/\./g, "[.]");
if (http) url = url.replace(/http/gi, "hxxp");
if (slashes) url = url.replace(/:\/\//g, "[://]");
return url;
}
export default DefangURL;

View file

@ -0,0 +1,145 @@
/**
* @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 = "This operation performs 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.infoURL = "https://wikipedia.org/wiki/Key_derivation_function";
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,78 @@
/**
* @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";
/**
* Derive PBKDF2 key operation
*/
class DerivePBKDF2Key extends Operation {
/**
* DerivePBKDF2Key constructor
*/
constructor() {
super();
this.name = "Derive PBKDF2 key";
this.module = "Ciphers";
this.description = "PBKDF2 is a password-based key derivation function. It is part of RSA Laboratories' Public-Key Cryptography Standards (PKCS) series, specifically PKCS #5 v2.0, also published as Internet Engineering Task Force's RFC 2898.<br><br>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.infoURL = "https://wikipedia.org/wiki/PBKDF2";
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],
iterations = args[2],
hasher = args[3],
salt = Utils.convertToByteString(args[4].string, args[4].option) ||
forge.random.getBytesSync(keySize),
derivedKey = forge.pkcs5.pbkdf2(passphrase, salt, iterations, keySize / 8, hasher.toLowerCase());
return forge.util.bytesToHex(derivedKey);
}
}
export default DerivePBKDF2Key;

View file

@ -0,0 +1,55 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/List_of_file_signatures";
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,129 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/File_comparison";
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",
"type": "boolean",
"value": false,
"hint": "Relevant for word and line"
}
];
}
/**
* @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;
// Node and Webpack load modules slightly differently
const jsdiff = JsDiff.default ? JsDiff.default : JsDiff;
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,134 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/X86";
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 BigNumber.isBigNumber(val) ? val : new BigNumber(NaN);
}
}
export default Divide;

View file

@ -0,0 +1,123 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* 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. Negative values are allowed.";
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) {
let start = args[0],
length = args[1];
const applyToEachLine = args[2];
if (!applyToEachLine) {
if (start < 0) { // Take from the end
start = input.byteLength + start;
}
if (length < 0) { // Flip start point
start = start + length;
if (start < 0) {
start = input.byteLength + start;
length = start - length;
} else {
length = -length;
}
}
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 = [],
s = start,
l = length;
for (i = 0; i < lines.length; i++) {
if (s < 0) { // Take from the end
s = lines[i].length + s;
}
if (l < 0) { // Flip start point
s = s + l;
if (s < 0) {
s = lines[i].length + s;
l = s - l;
} else {
l = -l;
}
}
output = output.concat(lines[i].slice(0, s).concat(lines[i].slice(s+l, lines[i].length)));
output.push(0x0a);
s = start;
l = length;
}
return new Uint8Array(output.slice(0, output.length-1)).buffer;
}
}
export default DropBytes;

View file

@ -0,0 +1,60 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/NetBIOS";
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,59 @@
/**
* @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 = "Encodings";
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.infoURL = "https://wikipedia.org/wiki/Character_encoding";
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

@ -1,99 +0,0 @@
import Utils from "../Utils.js";
/**
* Endian operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Endian = {
/**
* @constant
* @default
*/
DATA_FORMAT: ["Hex", "Raw"],
/**
* @constant
* @default
*/
WORD_LENGTH: 4,
/**
* @constant
* @default
*/
PAD_INCOMPLETE_WORDS: true,
/**
* Swap endianness operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runSwapEndianness: function(input, args) {
let dataFormat = args[0],
wordLength = args[1],
padIncompleteWords = args[2],
data = [],
result = [],
words = [],
i = 0,
j = 0;
if (wordLength <= 0) {
return "Word length must be greater than 0";
}
// Convert input to raw data based on specified data format
switch (dataFormat) {
case "Hex":
data = Utils.fromHex(input);
break;
case "Raw":
data = Utils.strToByteArray(input);
break;
default:
data = input;
}
// Split up into words
for (i = 0; i < data.length; i += wordLength) {
const word = data.slice(i, i + wordLength);
// Pad word if too short
if (padIncompleteWords && word.length < wordLength){
for (j = word.length; j < wordLength; j++) {
word.push(0);
}
}
words.push(word);
}
// Swap endianness and flatten
for (i = 0; i < words.length; i++) {
j = words[i].length;
while (j--) {
result.push(words[i][j]);
}
}
// Convert data back to specified data format
switch (dataFormat) {
case "Hex":
return Utils.toHex(result);
case "Raw":
return Utils.byteArrayToUtf8(result);
default:
return result;
}
},
};
export default Endian;

View file

@ -1,168 +0,0 @@
import Utils from "../Utils.js";
/**
* Entropy operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Entropy = {
/**
* @constant
* @default
*/
CHUNK_SIZE: 1000,
/**
* Entropy operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {html}
*/
runEntropy: function(input, args) {
let chunkSize = args[0],
output = "",
entropy = Entropy._calcEntropy(input);
output += "Shannon entropy: " + entropy + "\n" +
"<br><canvas id='chart-area'></canvas><br>\n" +
"- 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.\n" +
"- Standard English text usually falls somewhere between 3.5 and 5.\n" +
"- Properly encrypted or compressed data of a reasonable length should have an entropy of over 7.5.\n\n" +
"The following results show the entropy of chunks of the input data. Chunks with particularly high entropy could suggest encrypted or compressed sections.\n\n" +
"<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>";
let chunkEntropy = 0;
if (chunkSize !== 0) {
for (let i = 0; i < input.length; i += chunkSize) {
chunkEntropy = Entropy._calcEntropy(input.slice(i, i+chunkSize));
output += "Bytes " + i + " to " + (i+chunkSize) + ": " + chunkEntropy + "\n";
}
} else {
output += "Chunk size cannot be 0.";
}
return output;
},
/**
* @constant
* @default
*/
FREQ_ZEROS: false,
/**
* Frequency distribution operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {html}
*/
runFreqDistrib: function (input, args) {
if (!input.length) return "No data";
let distrib = new Array(256).fill(0),
percentages = new Array(256),
len = input.length,
showZeroes = args[0],
i;
// Count bytes
for (i = 0; i < len; i++) {
distrib[input[i]]++;
}
// Calculate percentages
let repr = 0;
for (i = 0; i < 256; i++) {
if (distrib[i] > 0) repr++;
percentages[i] = distrib[i] / len * 100;
}
// Print
let output = "<canvas id='chart-area'></canvas><br>" +
"Total data length: " + len +
"\nNumber of bytes represented: " + repr +
"\nNumber of bytes not represented: " + (256-repr) +
"\n\nByte Percentage\n" +
"<script>\
var canvas = document.getElementById('chart-area'),\
parentRect = canvas.parentNode.getBoundingClientRect(),\
scores = " + JSON.stringify(percentages) + ";\
\
canvas.width = parentRect.width * 0.95;\
canvas.height = parentRect.height * 0.9;\
\
CanvasComponents.drawBarChart(canvas, scores, 'Byte', 'Frequency %', 16, 6);\
</script>";
for (i = 0; i < 256; i++) {
if (distrib[i] || showZeroes) {
output += " " + Utils.hex(i, 2) + " (" +
Utils.padRight(percentages[i].toFixed(2).replace(".00", "") + "%)", 8) +
Array(Math.ceil(percentages[i])+1).join("|") + "\n";
}
}
return output;
},
/**
* Calculates the Shannon entropy for a given chunk of data.
*
* @private
* @param {byteArray} data
* @returns {number}
*/
_calcEntropy: function(data) {
let prob = [],
uniques = data.unique(),
str = Utils.byteArrayToChars(data),
i;
for (i = 0; i < uniques.length; i++) {
prob.push(str.count(Utils.chr(uniques[i])) / data.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;
},
};
export default Entropy;

View file

@ -0,0 +1,97 @@
/**
* @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 = "Shannon Entropy, in the context of information theory, is a measure of the rate at which information is produced by a source of data. It can be used, in a broad sense, to detect whether data is likely to be structured or unstructured. 8 is the maximum, representing highly unstructured, 'random' data. English language text usually falls somewhere between 3.5 and 5. Properly encrypted or compressed data should have an entropy of over 7.5.";
this.infoURL = "https://wikipedia.org/wiki/Entropy_(information_theory)";
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,88 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Escape_sequence";
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

@ -1,299 +0,0 @@
/**
* Identifier extraction operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Extract = {
/**
* Runs search operations across the input data using regular expressions.
*
* @private
* @param {string} input
* @param {RegExp} searchRegex
* @param {RegExp} removeRegex - A regular expression defining results to remove from the
* final list
* @param {boolean} includeTotal - Whether or not to include the total number of results
* @returns {string}
*/
_search: function(input, searchRegex, removeRegex, includeTotal) {
let output = "",
total = 0,
match;
while ((match = searchRegex.exec(input))) {
if (removeRegex && removeRegex.test(match[0]))
continue;
total++;
output += match[0] + "\n";
}
if (includeTotal)
output = "Total found: " + total + "\n\n" + output;
return output;
},
/**
* @constant
* @default
*/
MIN_STRING_LEN: 3,
/**
* @constant
* @default
*/
DISPLAY_TOTAL: false,
/**
* Strings operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runStrings: function(input, args) {
let minLen = args[0] || Extract.MIN_STRING_LEN,
displayTotal = args[1],
strings = "[A-Z\\d/\\-:.,_$%'\"()<>= !\\[\\]{}@]",
regex = new RegExp(strings + "{" + minLen + ",}", "ig");
return Extract._search(input, regex, null, displayTotal);
},
/**
* @constant
* @default
*/
INCLUDE_IPV4: true,
/**
* @constant
* @default
*/
INCLUDE_IPV6: false,
/**
* @constant
* @default
*/
REMOVE_LOCAL: false,
/**
* Extract IP addresses operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runIp: function(input, args) {
let includeIpv4 = args[0],
includeIpv6 = args[1],
removeLocal = args[2],
displayTotal = args[3],
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})",
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) {
let ten = "10\\..+",
oneninetwo = "192\\.168\\..+",
oneseventwo = "172\\.(?:1[6-9]|2\\d|3[01])\\..+",
onetwoseven = "127\\..+",
removeRegex = new RegExp("^(?:" + ten + "|" + oneninetwo +
"|" + oneseventwo + "|" + onetwoseven + ")");
return Extract._search(input, regex, removeRegex, displayTotal);
} else {
return Extract._search(input, regex, null, displayTotal);
}
} else {
return "";
}
},
/**
* Extract email addresses operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runEmail: function(input, args) {
let displayTotal = args[0],
regex = /\w[-.\w]*@[-\w]+(?:\.[-\w]+)*\.[A-Z]{2,4}/ig;
return Extract._search(input, regex, null, displayTotal);
},
/**
* Extract MAC addresses operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runMac: function(input, args) {
let displayTotal = args[0],
regex = /[A-F\d]{2}(?:[:-][A-F\d]{2}){5}/ig;
return Extract._search(input, regex, null, displayTotal);
},
/**
* Extract URLs operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runUrls: function(input, args) {
let displayTotal = args[0],
protocol = "[A-Z]+://",
hostname = "[-\\w]+(?:\\.\\w[-\\w]*)+",
port = ":\\d+",
path = "/[^.!,?;\"'<>()\\[\\]{}\\s\\x7F-\\xFF]*";
path += "(?:[.!,?]+[^.!,?;\"'<>()\\[\\]{}\\s\\x7F-\\xFF]+)*";
const regex = new RegExp(protocol + hostname + "(?:" + port +
")?(?:" + path + ")?", "ig");
return Extract._search(input, regex, null, displayTotal);
},
/**
* Extract domains operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runDomains: function(input, args) {
let displayTotal = args[0],
protocol = "https?://",
hostname = "[-\\w\\.]+",
tld = "\\.(?:com|net|org|biz|info|co|uk|onion|int|mobi|name|edu|gov|mil|eu|ac|ae|af|de|ca|ch|cn|cy|es|gb|hk|il|in|io|tv|me|nl|no|nz|ro|ru|tr|us|az|ir|kz|uz|pk)+",
regex = new RegExp("(?:" + protocol + ")?" + hostname + tld, "ig");
return Extract._search(input, regex, null, displayTotal);
},
/**
* @constant
* @default
*/
INCLUDE_WIN_PATH: true,
/**
* @constant
* @default
*/
INCLUDE_UNIX_PATH: true,
/**
* Extract file paths operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runFilePaths: function(input, args) {
let includeWinPath = args[0],
includeUnixPath = args[1],
displayTotal = args[2],
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})+",
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 Extract._search(input, regex, null, displayTotal);
} else {
return "";
}
},
/**
* Extract dates operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runDates: function(input, args) {
let 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 Extract._search(input, regex, null, displayTotal);
},
/**
* Extract all identifiers operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runAllIdents: function(input, args) {
let output = "";
output += "IP addresses\n";
output += Extract.runIp(input, [true, true, false]);
output += "\nEmail addresses\n";
output += Extract.runEmail(input, []);
output += "\nMAC addresses\n";
output += Extract.runMac(input, []);
output += "\nURLs\n";
output += Extract.runUrls(input, []);
output += "\nDomain names\n";
output += Extract.runDomains(input, []);
output += "\nFile paths\n";
output += Extract.runFilePaths(input, [true, true]);
output += "\nDates\n";
output += Extract.runDates(input, []);
return output;
},
};
export default Extract;

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,47 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search, DOMAIN_REGEX } 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];
return search(input, DOMAIN_REGEX, null, displayTotal);
}
}
export default ExtractDomains;

View file

@ -0,0 +1,63 @@
/**
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import ExifParser from "exif-parser";
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
/**
* Extract EXIF operation
*/
class ExtractEXIF extends Operation {
/**
* ExtractEXIF constructor
*/
constructor() {
super();
this.name = "Extract EXIF";
this.module = "Image";
this.description = [
"Extracts EXIF data from an image.",
"<br><br>",
"EXIF data is metadata embedded in images (JPEG, JPG, TIFF) and audio files.",
"<br><br>",
"EXIF data from photos usually contains information about the image file itself as well as the device used to create it.",
].join("\n");
this.infoURL = "https://wikipedia.org/wiki/Exif";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
try {
const parser = ExifParser.create(input);
const result = parser.parse();
const lines = [];
for (const tagName in result.tags) {
const value = result.tags[tagName];
lines.push(`${tagName}: ${value}`);
}
const numTags = lines.length;
lines.unshift(`Found ${numTags} tags.\n`);
return lines.join("\n");
} catch (err) {
throw new OperationError(`Could not extract EXIF data from image: ${err}`);
}
}
}
export default ExtractEXIF;

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],
// email regex from: https://www.regextester.com/98066
regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/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,47 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search, URL_REGEX } 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];
return search(input, URL_REGEX, null, displayTotal);
}
}
export default ExtractURLs;

View file

@ -1,530 +0,0 @@
import Utils from "../Utils.js";
/**
* File type operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const FileType = {
/**
* Detect File Type operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runDetect: function(input, args) {
const type = FileType.magicType(input);
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;
}
},
/**
* @constant
* @default
*/
IGNORE_COMMON_BYTE_SEQUENCES: true,
/**
* Scan for Embedded Files operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runScanForEmbeddedFiles: function(input, args) {
let output = "Scanning data for 'magic bytes' which may indicate embedded files. The following results may be false positives and should not be treat as reliable. Any suffiently long file is likely to contain these magic bytes coincidentally.\n",
type,
ignoreCommon = args[0],
commonExts = ["ico", "ttf", ""],
numFound = 0,
numCommonFound = 0;
for (let i = 0; i < input.length; i++) {
type = FileType.magicType(input.slice(i));
if (type) {
if (ignoreCommon && commonExts.indexOf(type.ext) > -1) {
numCommonFound++;
continue;
}
numFound++;
output += "\nOffset " + i + " (0x" + Utils.hex(i) + "):\n" +
" File extension: " + type.ext + "\n" +
" MIME type: " + type.mime + "\n";
if (type.desc && type.desc.length) {
output += " Description: " + type.desc + "\n";
}
}
}
if (numFound === 0) {
output += "\nNo embedded files were found.";
}
if (numCommonFound > 0) {
output += "\n\n" + numCommonFound;
output += numCommonFound === 1 ?
" file type was detected that has a common byte sequence. This is likely to be a false positive." :
" file types were detected that have common byte sequences. These are likely to be false positives.";
output += " Run this operation with the 'Ignore common byte sequences' option unchecked to see details.";
}
return output;
},
/**
* Given a buffer, detects magic byte sequences at specific positions and returns the
* extension and mime type.
*
* @param {byteArray} buf
* @returns {Object} type
* @returns {string} type.ext - File extension
* @returns {string} type.mime - Mime type
* @returns {string} [type.desc] - Description
*/
magicType: function (buf) {
if (!(buf && buf.length > 1)) {
return null;
}
if (buf[0] === 0xFF && buf[1] === 0xD8 && buf[2] === 0xFF) {
return {
ext: "jpg",
mime: "image/jpeg"
};
}
if (buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4E && buf[3] === 0x47) {
return {
ext: "png",
mime: "image/png"
};
}
if (buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46) {
return {
ext: "gif",
mime: "image/gif"
};
}
if (buf[8] === 0x57 && buf[9] === 0x45 && buf[10] === 0x42 && buf[11] === 0x50) {
return {
ext: "webp",
mime: "image/webp"
};
}
// needs to be before `tif` check
if (((buf[0] === 0x49 && buf[1] === 0x49 && buf[2] === 0x2A && buf[3] === 0x0) || (buf[0] === 0x4D && buf[1] === 0x4D && buf[2] === 0x0 && buf[3] === 0x2A)) && buf[8] === 0x43 && buf[9] === 0x52) {
return {
ext: "cr2",
mime: "image/x-canon-cr2"
};
}
if ((buf[0] === 0x49 && buf[1] === 0x49 && buf[2] === 0x2A && buf[3] === 0x0) || (buf[0] === 0x4D && buf[1] === 0x4D && buf[2] === 0x0 && buf[3] === 0x2A)) {
return {
ext: "tif",
mime: "image/tiff"
};
}
if (buf[0] === 0x42 && buf[1] === 0x4D) {
return {
ext: "bmp",
mime: "image/bmp"
};
}
if (buf[0] === 0x49 && buf[1] === 0x49 && buf[2] === 0xBC) {
return {
ext: "jxr",
mime: "image/vnd.ms-photo"
};
}
if (buf[0] === 0x38 && buf[1] === 0x42 && buf[2] === 0x50 && buf[3] === 0x53) {
return {
ext: "psd",
mime: "image/vnd.adobe.photoshop"
};
}
// needs to be before `zip` check
if (buf[0] === 0x50 && buf[1] === 0x4B && buf[2] === 0x3 && buf[3] === 0x4 && buf[30] === 0x6D && buf[31] === 0x69 && buf[32] === 0x6D && buf[33] === 0x65 && buf[34] === 0x74 && buf[35] === 0x79 && buf[36] === 0x70 && buf[37] === 0x65 && buf[38] === 0x61 && buf[39] === 0x70 && buf[40] === 0x70 && buf[41] === 0x6C && buf[42] === 0x69 && buf[43] === 0x63 && buf[44] === 0x61 && buf[45] === 0x74 && buf[46] === 0x69 && buf[47] === 0x6F && buf[48] === 0x6E && buf[49] === 0x2F && buf[50] === 0x65 && buf[51] === 0x70 && buf[52] === 0x75 && buf[53] === 0x62 && buf[54] === 0x2B && buf[55] === 0x7A && buf[56] === 0x69 && buf[57] === 0x70) {
return {
ext: "epub",
mime: "application/epub+zip"
};
}
if (buf[0] === 0x50 && buf[1] === 0x4B && (buf[2] === 0x3 || buf[2] === 0x5 || buf[2] === 0x7) && (buf[3] === 0x4 || buf[3] === 0x6 || buf[3] === 0x8)) {
return {
ext: "zip",
mime: "application/zip"
};
}
if (buf[257] === 0x75 && buf[258] === 0x73 && buf[259] === 0x74 && buf[260] === 0x61 && buf[261] === 0x72) {
return {
ext: "tar",
mime: "application/x-tar"
};
}
if (buf[0] === 0x52 && buf[1] === 0x61 && buf[2] === 0x72 && buf[3] === 0x21 && buf[4] === 0x1A && buf[5] === 0x7 && (buf[6] === 0x0 || buf[6] === 0x1)) {
return {
ext: "rar",
mime: "application/x-rar-compressed"
};
}
if (buf[0] === 0x1F && buf[1] === 0x8B && buf[2] === 0x8) {
return {
ext: "gz",
mime: "application/gzip"
};
}
if (buf[0] === 0x42 && buf[1] === 0x5A && buf[2] === 0x68) {
return {
ext: "bz2",
mime: "application/x-bzip2"
};
}
if (buf[0] === 0x37 && buf[1] === 0x7A && buf[2] === 0xBC && buf[3] === 0xAF && buf[4] === 0x27 && buf[5] === 0x1C) {
return {
ext: "7z",
mime: "application/x-7z-compressed"
};
}
if (buf[0] === 0x78 && buf[1] === 0x01) {
return {
ext: "dmg",
mime: "application/x-apple-diskimage"
};
}
if ((buf[0] === 0x0 && buf[1] === 0x0 && buf[2] === 0x0 && (buf[3] === 0x18 || buf[3] === 0x20) && buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70) || (buf[0] === 0x33 && buf[1] === 0x67 && buf[2] === 0x70 && buf[3] === 0x35) || (buf[0] === 0x0 && buf[1] === 0x0 && buf[2] === 0x0 && buf[3] === 0x1C && buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70 && buf[8] === 0x6D && buf[9] === 0x70 && buf[10] === 0x34 && buf[11] === 0x32 && buf[16] === 0x6D && buf[17] === 0x70 && buf[18] === 0x34 && buf[19] === 0x31 && buf[20] === 0x6D && buf[21] === 0x70 && buf[22] === 0x34 && buf[23] === 0x32 && buf[24] === 0x69 && buf[25] === 0x73 && buf[26] === 0x6F && buf[27] === 0x6D)) {
return {
ext: "mp4",
mime: "video/mp4"
};
}
if ((buf[0] === 0x0 && buf[1] === 0x0 && buf[2] === 0x0 && buf[3] === 0x1C && buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70 && buf[8] === 0x4D && buf[9] === 0x34 && buf[10] === 0x56)) {
return {
ext: "m4v",
mime: "video/x-m4v"
};
}
if (buf[0] === 0x4D && buf[1] === 0x54 && buf[2] === 0x68 && buf[3] === 0x64) {
return {
ext: "mid",
mime: "audio/midi"
};
}
// needs to be before the `webm` check
if (buf[31] === 0x6D && buf[32] === 0x61 && buf[33] === 0x74 && buf[34] === 0x72 && buf[35] === 0x6f && buf[36] === 0x73 && buf[37] === 0x6B && buf[38] === 0x61) {
return {
ext: "mkv",
mime: "video/x-matroska"
};
}
if (buf[0] === 0x1A && buf[1] === 0x45 && buf[2] === 0xDF && buf[3] === 0xA3) {
return {
ext: "webm",
mime: "video/webm"
};
}
if (buf[0] === 0x0 && buf[1] === 0x0 && buf[2] === 0x0 && buf[3] === 0x14 && buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70) {
return {
ext: "mov",
mime: "video/quicktime"
};
}
if (buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 && buf[8] === 0x41 && buf[9] === 0x56 && buf[10] === 0x49) {
return {
ext: "avi",
mime: "video/x-msvideo"
};
}
if (buf[0] === 0x30 && buf[1] === 0x26 && buf[2] === 0xB2 && buf[3] === 0x75 && buf[4] === 0x8E && buf[5] === 0x66 && buf[6] === 0xCF && buf[7] === 0x11 && buf[8] === 0xA6 && buf[9] === 0xD9) {
return {
ext: "wmv",
mime: "video/x-ms-wmv"
};
}
if (buf[0] === 0x0 && buf[1] === 0x0 && buf[2] === 0x1 && buf[3].toString(16)[0] === "b") {
return {
ext: "mpg",
mime: "video/mpeg"
};
}
if ((buf[0] === 0x49 && buf[1] === 0x44 && buf[2] === 0x33) || (buf[0] === 0xFF && buf[1] === 0xfb)) {
return {
ext: "mp3",
mime: "audio/mpeg"
};
}
if ((buf[4] === 0x66 && buf[5] === 0x74 && buf[6] === 0x79 && buf[7] === 0x70 && buf[8] === 0x4D && buf[9] === 0x34 && buf[10] === 0x41) || (buf[0] === 0x4D && buf[1] === 0x34 && buf[2] === 0x41 && buf[3] === 0x20)) {
return {
ext: "m4a",
mime: "audio/m4a"
};
}
if (buf[0] === 0x4F && buf[1] === 0x67 && buf[2] === 0x67 && buf[3] === 0x53) {
return {
ext: "ogg",
mime: "audio/ogg"
};
}
if (buf[0] === 0x66 && buf[1] === 0x4C && buf[2] === 0x61 && buf[3] === 0x43) {
return {
ext: "flac",
mime: "audio/x-flac"
};
}
if (buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 && buf[8] === 0x57 && buf[9] === 0x41 && buf[10] === 0x56 && buf[11] === 0x45) {
return {
ext: "wav",
mime: "audio/x-wav"
};
}
if (buf[0] === 0x23 && buf[1] === 0x21 && buf[2] === 0x41 && buf[3] === 0x4D && buf[4] === 0x52 && buf[5] === 0x0A) {
return {
ext: "amr",
mime: "audio/amr"
};
}
if (buf[0] === 0x25 && buf[1] === 0x50 && buf[2] === 0x44 && buf[3] === 0x46) {
return {
ext: "pdf",
mime: "application/pdf"
};
}
if (buf[0] === 0x4D && buf[1] === 0x5A) {
return {
ext: "exe",
mime: "application/x-msdownload"
};
}
if ((buf[0] === 0x43 || buf[0] === 0x46) && buf[1] === 0x57 && buf[2] === 0x53) {
return {
ext: "swf",
mime: "application/x-shockwave-flash"
};
}
if (buf[0] === 0x7B && buf[1] === 0x5C && buf[2] === 0x72 && buf[3] === 0x74 && buf[4] === 0x66) {
return {
ext: "rtf",
mime: "application/rtf"
};
}
if (buf[0] === 0x77 && buf[1] === 0x4F && buf[2] === 0x46 && buf[3] === 0x46 && buf[4] === 0x00 && buf[5] === 0x01 && buf[6] === 0x00 && buf[7] === 0x00) {
return {
ext: "woff",
mime: "application/font-woff"
};
}
if (buf[0] === 0x77 && buf[1] === 0x4F && buf[2] === 0x46 && buf[3] === 0x32 && buf[4] === 0x00 && buf[5] === 0x01 && buf[6] === 0x00 && buf[7] === 0x00) {
return {
ext: "woff2",
mime: "application/font-woff"
};
}
if (buf[34] === 0x4C && buf[35] === 0x50 && ((buf[8] === 0x02 && buf[9] === 0x00 && buf[10] === 0x01) || (buf[8] === 0x01 && buf[9] === 0x00 && buf[10] === 0x00) || (buf[8] === 0x02 && buf[9] === 0x00 && buf[10] === 0x02))) {
return {
ext: "eot",
mime: "application/octet-stream"
};
}
if (buf[0] === 0x00 && buf[1] === 0x01 && buf[2] === 0x00 && buf[3] === 0x00 && buf[4] === 0x00) {
return {
ext: "ttf",
mime: "application/font-sfnt"
};
}
if (buf[0] === 0x4F && buf[1] === 0x54 && buf[2] === 0x54 && buf[3] === 0x4F && buf[4] === 0x00) {
return {
ext: "otf",
mime: "application/font-sfnt"
};
}
if (buf[0] === 0x00 && buf[1] === 0x00 && buf[2] === 0x01 && buf[3] === 0x00) {
return {
ext: "ico",
mime: "image/x-icon"
};
}
if (buf[0] === 0x46 && buf[1] === 0x4C && buf[2] === 0x56 && buf[3] === 0x01) {
return {
ext: "flv",
mime: "video/x-flv"
};
}
if (buf[0] === 0x25 && buf[1] === 0x21) {
return {
ext: "ps",
mime: "application/postscript"
};
}
if (buf[0] === 0xFD && buf[1] === 0x37 && buf[2] === 0x7A && buf[3] === 0x58 && buf[4] === 0x5A && buf[5] === 0x00) {
return {
ext: "xz",
mime: "application/x-xz"
};
}
if (buf[0] === 0x53 && buf[1] === 0x51 && buf[2] === 0x4C && buf[3] === 0x69) {
return {
ext: "sqlite",
mime: "application/x-sqlite3"
};
}
// Added by n1474335 [n1474335@gmail.com] from here on
// ################################################################## //
if ((buf[0] === 0x1F && buf[1] === 0x9D) || (buf[0] === 0x1F && buf[1] === 0xA0)) {
return {
ext: "z, tar.z",
mime: "application/x-gtar"
};
}
if (buf[0] === 0x7F && buf[1] === 0x45 && buf[2] === 0x4C && buf[3] === 0x46) {
return {
ext: "none, axf, bin, elf, o, prx, puff, so",
mime: "application/x-executable",
desc: "Executable and Linkable Format file. No standard file extension."
};
}
if (buf[0] === 0xCA && buf[1] === 0xFE && buf[2] === 0xBA && buf[3] === 0xBE) {
return {
ext: "class",
mime: "application/java-vm"
};
}
if (buf[0] === 0xEF && buf[1] === 0xBB && buf[2] === 0xBF) {
return {
ext: "txt",
mime: "text/plain",
desc: "UTF-8 encoded Unicode byte order mark detected, commonly but not exclusively seen in text files."
};
}
// Must be before Little-endian UTF-16 BOM
if (buf[0] === 0xFF && buf[1] === 0xFE && buf[2] === 0x00 && buf[3] === 0x00) {
return {
ext: "",
mime: "",
desc: "Little-endian UTF-32 encoded Unicode byte order mark detected."
};
}
if (buf[0] === 0xFF && buf[1] === 0xFE) {
return {
ext: "",
mime: "",
desc: "Little-endian UTF-16 encoded Unicode byte order mark detected."
};
}
if ((buf[0x8001] === 0x43 && buf[0x8002] === 0x44 && buf[0x8003] === 0x30 && buf[0x8004] === 0x30 && buf[0x8005] === 0x31) ||
(buf[0x8801] === 0x43 && buf[0x8802] === 0x44 && buf[0x8803] === 0x30 && buf[0x8804] === 0x30 && buf[0x8805] === 0x31) ||
(buf[0x9001] === 0x43 && buf[0x9002] === 0x44 && buf[0x9003] === 0x30 && buf[0x9004] === 0x30 && buf[0x9005] === 0x31)) {
return {
ext: "iso",
mime: "application/octet-stream",
desc: "ISO 9660 CD/DVD image file"
};
}
if (buf[0] === 0xD0 && buf[1] === 0xCF && buf[2] === 0x11 && buf[3] === 0xE0 && buf[4] === 0xA1 && buf[5] === 0xB1 && buf[6] === 0x1A && buf[7] === 0xE1) {
return {
ext: "doc, xls, ppt",
mime: "application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint",
desc: "Microsoft Office documents"
};
}
if (buf[0] === 0x64 && buf[1] === 0x65 && buf[2] === 0x78 && buf[3] === 0x0A && buf[4] === 0x30 && buf[5] === 0x33 && buf[6] === 0x35 && buf[7] === 0x00) {
return {
ext: "dex",
mime: "application/octet-stream",
desc: "Dalvik Executable (Android)"
};
}
if (buf[0] === 0x4B && buf[1] === 0x44 && buf[2] === 0x4D) {
return {
ext: "vmdk",
mime: "application/vmdk, application/x-virtualbox-vmdk"
};
}
if (buf[0] === 0x43 && buf[1] === 0x72 && buf[2] === 0x32 && buf[3] === 0x34) {
return {
ext: "crx",
mime: "application/crx",
desc: "Google Chrome extension or packaged app"
};
}
return null;
},
};
export default FileType;

View file

@ -0,0 +1,73 @@
/**
* @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";
import XRegExp from "xregexp";
/**
* Filter operation
*/
class Filter extends Operation {
/**
* Filter constructor
*/
constructor() {
super();
this.name = "Filter";
this.module = "Regex";
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 XRegExp(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,94 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import XRegExp from "xregexp";
/**
* Find / Replace operation
*/
class FindReplace extends Operation {
/**
* FindReplace constructor
*/
constructor() {
super();
this.name = "Find / Replace";
this.module = "Regex";
this.description = "Replaces all occurrences of the first string with the second.<br><br>Includes support for regular expressions (regex), simple strings and extended strings (which support \\n, \\r, \\t, \\b, \\f and escaped hex bytes using \\x notation, e.g. \\x00 for a null byte).";
this.infoURL = "https://wikipedia.org/wiki/Regular_expression";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Find",
"type": "toggleString",
"value": "",
"toggleValues": ["Regex", "Extended (\\n, \\t, \\x...)", "Simple string"]
},
{
"name": "Replace",
"type": "binaryString",
"value": ""
},
{
"name": "Global match",
"type": "boolean",
"value": true
},
{
"name": "Case insensitive",
"type": "boolean",
"value": false
},
{
"name": "Multiline matching",
"type": "boolean",
"value": true
},
{
"name": "Dot matches all",
"type": "boolean",
"value": false
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [{option: type}, replace, g, i, m, s] = args;
let find = args[0].string,
modifiers = "";
if (g) modifiers += "g";
if (i) modifiers += "i";
if (m) modifiers += "m";
if (s) modifiers += "s";
if (type === "Regex") {
find = new XRegExp(find, modifiers);
return input.replace(find, replace);
}
if (type.indexOf("Extended") === 0) {
find = Utils.parseEscapedChars(find);
}
find = new XRegExp(Utils.escapeRegex(find), modifiers);
return input.replace(find, replace);
}
}
export default FindReplace;

View file

@ -0,0 +1,49 @@
/**
* @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 = "Crypto";
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.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum#Fletcher-16";
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,49 @@
/**
* @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 = "Crypto";
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.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum#Fletcher-32";
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,49 @@
/**
* @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 = "Crypto";
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.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum#Fletcher-64";
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,49 @@
/**
* @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 = "Crypto";
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.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum";
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,122 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/MAC_address#Notational_conventions";
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,111 @@
/**
* @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.infoURL = "https://wikipedia.org/wiki/Frequency_distribution";
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;

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