Merged esm branch into feature-magic. Ported FileType ops.

This commit is contained in:
n1474335 2018-05-20 16:49:42 +01:00
commit ee519c7352
385 changed files with 34205 additions and 39954 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import JSCRC from "js-crc";
/**
* CRC-16 Checksum operation
*/
class CRC16Checksum extends Operation {
/**
* CRC16Checksum constructor
*/
constructor() {
super();
this.name = "CRC-16 Checksum";
this.module = "Hashing";
this.description = "A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data.<br><br>The CRC was invented by W. Wesley Peterson in 1961.";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return JSCRC.crc16(input);
}
}
export default CRC16Checksum;

View file

@ -0,0 +1,40 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import JSCRC from "js-crc";
/**
* CRC-32 Checksum operation
*/
class CRC32Checksum extends Operation {
/**
* CRC32Checksum constructor
*/
constructor() {
super();
this.name = "CRC-32 Checksum";
this.module = "Hashing";
this.description = "A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data.<br><br>The CRC was invented by W. Wesley Peterson in 1961; the 32-bit CRC function of Ethernet and many other standards is the work of several researchers and was published in 1975.";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return JSCRC.crc32(input);
}
}
export default CRC32Checksum;

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,133 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
import * as disassemble from "../vendor/DisassembleX86-64";
import OperationError from "../errors/OperationError";
/**
* Disassemble x86 operation
*/
class DisassembleX86 extends Operation {
/**
* DisassembleX86 constructor
*/
constructor() {
super();
this.name = "Disassemble x86";
this.module = "Shellcode";
this.description = "Disassembly is the process of translating machine language into assembly language.<br><br>This operation supports 64-bit, 32-bit and 16-bit code written for Intel or AMD x86 processors. It is particularly useful for reverse engineering shellcode.<br><br>Input should be in hexadecimal.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Bit mode",
"type": "option",
"value": ["64", "32", "16"]
},
{
"name": "Compatibility",
"type": "option",
"value": [
"Full x86 architecture",
"Knights Corner",
"Larrabee",
"Cyrix",
"Geode",
"Centaur",
"X86/486"
]
},
{
"name": "Code Segment (CS)",
"type": "number",
"value": 16
},
{
"name": "Offset (IP)",
"type": "number",
"value": 0
},
{
"name": "Show instruction hex",
"type": "boolean",
"value": true
},
{
"name": "Show instruction position",
"type": "boolean",
"value": true
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if invalid mode value
*/
run(input, args) {
const [
mode,
compatibility,
codeSegment,
offset,
showInstructionHex,
showInstructionPos
] = args;
switch (mode) {
case "64":
disassemble.setBitMode(2);
break;
case "32":
disassemble.setBitMode(1);
break;
case "16":
disassemble.setBitMode(0);
break;
default:
throw new OperationError("Invalid mode value");
}
switch (compatibility) {
case "Full x86 architecture":
disassemble.CompatibilityMode(0);
break;
case "Knights Corner":
disassemble.CompatibilityMode(1);
break;
case "Larrabee":
disassemble.CompatibilityMode(2);
break;
case "Cyrix":
disassemble.CompatibilityMode(3);
break;
case "Geode":
disassemble.CompatibilityMode(4);
break;
case "Centaur":
disassemble.CompatibilityMode(5);
break;
case "X86/486":
disassemble.CompatibilityMode(6);
break;
}
disassemble.SetBasePosition(codeSegment + ":" + offset);
disassemble.setShowInstructionHex(showInstructionHex);
disassemble.setShowInstructionPos(showInstructionPos);
disassemble.LoadBinCode(input.replace(/\s/g, ""));
return disassemble.LDisassemble();
}
}
export default DisassembleX86;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,333 +0,0 @@
import XRegExp from "xregexp";
/**
* 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))) {
// Moves pointer when an empty string is matched (prevents infinite loop)
if (match.index === searchRegex.lastIndex) {
searchRegex.lastIndex++;
}
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: 4,
/**
* @constant
* @default
*/
STRING_MATCH_TYPE: [
"[ASCII]", "Alphanumeric + punctuation (A)", "All printable chars (A)", "Null-terminated strings (A)",
"[Unicode]", "Alphanumeric + punctuation (U)", "All printable chars (U)", "Null-terminated strings (U)"
],
/**
* @constant
* @default
*/
ENCODING_LIST: ["Single byte", "16-bit littleendian", "16-bit bigendian", "All"],
/**
* @constant
* @default
*/
DISPLAY_TOTAL: false,
/**
* Strings operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runStrings: function(input, args) {
const encoding = args[0],
minLen = args[1],
matchType = args[2],
displayTotal = args[3],
alphanumeric = "A-Z\\d",
punctuation = "/\\-:.,_$%'\"()<>= !\\[\\]{}@",
printable = "\x20-\x7e",
uniAlphanumeric = "\\pL\\pN",
uniPunctuation = "\\pP\\pZ",
uniPrintable = "\\pL\\pM\\pZ\\pS\\pN\\pP";
let strings = "";
switch (matchType) {
case "Alphanumeric + punctuation (A)":
strings = `[${alphanumeric + punctuation}]`;
break;
case "All printable chars (A)":
case "Null-terminated strings (A)":
strings = `[${printable}]`;
break;
case "Alphanumeric + punctuation (U)":
strings = `[${uniAlphanumeric + uniPunctuation}]`;
break;
case "All printable chars (U)":
case "Null-terminated strings (U)":
strings = `[${uniPrintable}]`;
break;
}
// UTF-16 support is hacked in by allowing null bytes on either side of the matched chars
switch (encoding) {
case "All":
strings = `(\x00?${strings}\x00?)`;
break;
case "16-bit littleendian":
strings = `(${strings}\x00)`;
break;
case "16-bit bigendian":
strings = `(\x00${strings})`;
break;
case "Single byte":
default:
break;
}
strings = `${strings}{${minLen},}`;
if (matchType.includes("Null-terminated")) {
strings += "\x00";
}
const regex = new XRegExp(strings, "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) {
const displayTotal = args[0],
regex = /\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b/ig;
return 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);
},
};
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,49 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search } from "../lib/Extract";
/**
* Extract domains operation
*/
class ExtractDomains extends Operation {
/**
* ExtractDomains constructor
*/
constructor() {
super();
this.name = "Extract domains";
this.module = "Regex";
this.description = "Extracts domain names.<br>Note that this will not include paths. Use <strong>Extract URLs</strong> to find entire URLs.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Display total",
"type": "boolean",
"value": "Extract.DISPLAY_TOTAL"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const displayTotal = args[0],
regex = /\b((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}\b/ig;
return search(input, regex, null, displayTotal);
}
}
export default ExtractDomains;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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";
/**
* From Base32 operation
*/
class FromBase32 extends Operation {
/**
* FromBase32 constructor
*/
constructor() {
super();
this.name = "From Base32";
this.module = "Default";
this.description = "Base32 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers. It uses a smaller set of characters than Base64, usually the uppercase alphabet and the numbers 2 to 7.";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
name: "Alphabet",
type: "binaryString",
value: "A-Z2-7="
},
{
name: "Remove non-alphabet chars",
type: "boolean",
value: true
}
];
this.patterns = [
{
match: "^(?:[A-Z2-7]{8})+(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}={1})?$",
flags: "",
args: ["A-Z2-7=", false]
},
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
if (!input) return [];
const alphabet = args[0] ?
Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
removeNonAlphChars = args[1],
output = [];
let 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;
}
}
export default FromBase32;

View file

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

View file

@ -0,0 +1,149 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {fromBase64, ALPHABET_OPTIONS} from "../lib/Base64";
/**
* From Base64 operation
*/
class FromBase64 extends Operation {
/**
* FromBase64 constructor
*/
constructor() {
super();
this.name = "From Base64";
this.module = "Default";
this.description = "Base64 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers.<br><br>This operation decodes data from an ASCII Base64 string back into its raw format.<br><br>e.g. <code>aGVsbG8=</code> becomes <code>hello</code>";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
name: "Alphabet",
type: "editableOption",
value: ALPHABET_OPTIONS
},
{
name: "Remove non-alphabet chars",
type: "boolean",
value: true
}
];
this.patterns = [
{
match: "^(?:[A-Z\\d+/]{4})+(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?$",
flags: "i",
args: ["A-Za-z0-9+/=", false]
},
{
match: "^[A-Z\\d\\-_]{20,}$",
flags: "i",
args: ["A-Za-z0-9-_", false]
},
{
match: "^(?:[A-Z\\d+\\-]{4}){5,}(?:[A-Z\\d+\\-]{2}==|[A-Z\\d+\\-]{3}=)?$",
flags: "i",
args: ["A-Za-z0-9+\\-=", false]
},
{
match: "^(?:[A-Z\\d./]{4}){5,}(?:[A-Z\\d./]{2}==|[A-Z\\d./]{3}=)?$",
flags: "i",
args: ["./0-9A-Za-z=", false]
},
{
match: "^[A-Z\\d_.]{20,}$",
flags: "i",
args: ["A-Za-z0-9_.", false]
},
{
match: "^(?:[A-Z\\d._]{4}){5,}(?:[A-Z\\d._]{2}--|[A-Z\\d._]{3}-)?$",
flags: "i",
args: ["A-Za-z0-9._-", false]
},
{
match: "^(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?$",
flags: "i",
args: ["0-9a-zA-Z+/=", false]
},
{
match: "^(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?$",
flags: "i",
args: ["0-9A-Za-z+/=", false]
},
{
match: "^[ !\"#$%&'()*+,\\-./\\d:;<=>?@A-Z[\\\\\\]^_]{20,}$",
flags: "",
args: [" -_", false]
},
{
match: "^[A-Z\\d+\\-]{20,}$",
flags: "i",
args: ["+\\-0-9A-Za-z", false]
},
{
match: "^[!\"#$%&'()*+,\\-0-689@A-NP-VX-Z[`a-fh-mp-r]{20,}$",
flags: "",
args: ["!-,-0-689@A-NP-VX-Z[`a-fh-mp-r", false]
},
{
match: "^(?:[N-ZA-M\\d+/]{4}){5,}(?:[N-ZA-M\\d+/]{2}==|[N-ZA-M\\d+/]{3}=)?$",
flags: "i",
args: ["N-ZA-Mn-za-m0-9+/=", false]
},
{
match: "^[A-Z\\d./]{20,}$",
flags: "i",
args: ["./0-9A-Za-z", false]
},
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [alphabet, removeNonAlphChars] = args;
return fromBase64(input, alphabet, "byteArray", removeNonAlphChars);
}
/**
* Highlight to Base64
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
pos[0].start = Math.ceil(pos[0].start / 4 * 3);
pos[0].end = Math.floor(pos[0].end / 4 * 3);
return pos;
}
/**
* Highlight from Base64
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
pos[0].start = Math.floor(pos[0].start / 3 * 4);
pos[0].end = Math.ceil(pos[0].end / 3 * 4);
return pos;
}
}
export default FromBase64;

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,146 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {fromHex, FROM_HEX_DELIM_OPTIONS} from "../lib/Hex";
import Utils from "../Utils";
/**
* From Hex operation
*/
class FromHex extends Operation {
/**
* FromHex constructor
*/
constructor() {
super();
this.name = "From Hex";
this.module = "Default";
this.description = "Converts a hexadecimal byte string back into its raw value.<br><br>e.g. <code>ce 93 ce b5 ce b9 ce ac 20 cf 83 ce bf cf 85 0a</code> becomes the UTF-8 encoded string <code>Γειά σου</code>";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
name: "Delimiter",
type: "option",
value: FROM_HEX_DELIM_OPTIONS
}
];
this.patterns = [
{
match: "^(?:[\\dA-F]{2})+$",
flags: "i",
args: ["None"]
},
{
match: "^[\\dA-F]{2}(?: [\\dA-F]{2})*$",
flags: "i",
args: ["Space"]
},
{
match: "^[\\dA-F]{2}(?:,[\\dA-F]{2})*$",
flags: "i",
args: ["Comma"]
},
{
match: "^[\\dA-F]{2}(?:;[\\dA-F]{2})*$",
flags: "i",
args: ["Semi-colon"]
},
{
match: "^[\\dA-F]{2}(?::[\\dA-F]{2})*$",
flags: "i",
args: ["Colon"]
},
{
match: "^[\\dA-F]{2}(?:\\n[\\dA-F]{2})*$",
flags: "i",
args: ["Line feed"]
},
{
match: "^[\\dA-F]{2}(?:\\r\\n[\\dA-F]{2})*$",
flags: "i",
args: ["CRLF"]
},
{
match: "^[\\dA-F]{2}(?:0x[\\dA-F]{2})*$",
flags: "i",
args: ["0x"]
},
{
match: "^[\\dA-F]{2}(?:\\\\x[\\dA-F]{2})*$",
flags: "i",
args: ["\\x"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const delim = args[0] || "Space";
return fromHex(input, delim, 2);
}
/**
* Highlight to Hex
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
if (args[0] === "Auto") return false;
const delim = Utils.charRep(args[0] || "Space"),
len = delim === "\r\n" ? 1 : delim.length,
width = len + 2;
// 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;
}
/**
* Highlight from Hex
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
const delim = Utils.charRep(args[0] || "Space"),
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;
}
}
export default FromHex;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,116 @@
/**
* @author tlwr [toby@toby.codes]
* @author Matt C [matt@artemisbot.uk]
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
import kbpgp from "kbpgp";
import { getSubkeySize, ASP } from "../lib/PGP";
import promisifyDefault from "es6-promisify";
const promisify = promisifyDefault.promisify;
/**
* Generate PGP Key Pair operation
*/
class GeneratePGPKeyPair extends Operation {
/**
* GeneratePGPKeyPair constructor
*/
constructor() {
super();
this.name = "Generate PGP Key Pair";
this.module = "PGP";
this.description = "Generates a new public/private PGP key pair. Supports RSA and Eliptic Curve (EC) keys.";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Key type",
"type": "option",
"value": ["RSA-1024", "RSA-2048", "RSA-4096", "ECC-256", "ECC-384"]
},
{
"name": "Password (optional)",
"type": "string",
"value": ""
},
{
"name": "Name (optional)",
"type": "string",
"value": ""
},
{
"name": "Email (optional)",
"type": "string",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [keyType, keySize] = args[0].split("-"),
password = args[1],
name = args[2],
email = args[3];
let userIdentifier = "";
if (name) userIdentifier += name;
if (email) userIdentifier += ` <${email}>`;
let flags = kbpgp.const.openpgp.certify_keys;
flags |= kbpgp.const.openpgp.sign_data;
flags |= kbpgp.const.openpgp.auth;
flags |= kbpgp.const.openpgp.encrypt_comm;
flags |= kbpgp.const.openpgp.encrypt_storage;
const keyGenerationOptions = {
userid: userIdentifier,
ecc: keyType === "ecc",
primary: {
"nbits": keySize,
"flags": flags,
"expire_in": 0
},
subkeys: [{
"nbits": getSubkeySize(keySize),
"flags": kbpgp.const.openpgp.sign_data,
"expire_in": 86400 * 365 * 8
}, {
"nbits": getSubkeySize(keySize),
"flags": kbpgp.const.openpgp.encrypt_comm | kbpgp.const.openpgp.encrypt_storage,
"expire_in": 86400 * 365 * 2
}],
asp: ASP
};
return new Promise(async (resolve, reject) => {
try {
const unsignedKey = await promisify(kbpgp.KeyManager.generate)(keyGenerationOptions);
await promisify(unsignedKey.sign.bind(unsignedKey))({});
const signedKey = unsignedKey,
privateKeyExportOptions = {};
if (password) privateKeyExportOptions.passphrase = password;
const privateKey = await promisify(signedKey.export_pgp_private.bind(signedKey))(privateKeyExportOptions);
const publicKey = await promisify(signedKey.export_pgp_public.bind(signedKey))({});
resolve(privateKey + "\n" + publicKey.trim());
} catch (err) {
reject(`Error whilst generating key pair: ${err}`);
}
});
}
}
export default GeneratePGPKeyPair;

View file

@ -0,0 +1,50 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min";
const Zlib = zlibAndGzip.Zlib;
/**
* Gunzip operation
*/
class Gunzip extends Operation {
/**
* Gunzip constructor
*/
constructor() {
super();
this.name = "Gunzip";
this.module = "Compression";
this.description = "Decompresses data which has been compressed using the deflate algorithm with gzip headers.";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.args = [];
this.patterns = [
{
match: "^\\x1f\\x8b\\x08",
flags: "",
args: []
},
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {File}
*/
run(input, args) {
const gunzip = new Zlib.Gunzip(new Uint8Array(input));
return new Uint8Array(gunzip.decompress()).buffer;
}
}
export default Gunzip;

View file

@ -0,0 +1,85 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import {COMPRESSION_TYPE, ZLIB_COMPRESSION_TYPE_LOOKUP} from "../lib/Zlib";
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min";
const Zlib = zlibAndGzip.Zlib;
/**
* Gzip operation
*/
class Gzip extends Operation {
/**
* Gzip constructor
*/
constructor() {
super();
this.name = "Gzip";
this.module = "Compression";
this.description = "Compresses data using the deflate algorithm with gzip headers.";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.args = [
{
name: "Compression type",
type: "option",
value: COMPRESSION_TYPE
},
{
name: "Filename (optional)",
type: "string",
value: ""
},
{
name: "Comment (optional)",
type: "string",
value: ""
},
{
name: "Include file checksum",
type: "boolean",
value: false
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
run(input, args) {
const filename = args[1],
comment = args[2],
options = {
deflateOptions: {
compressionType: 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(new Uint8Array(input), options);
return new Uint8Array(gzip.compress()).buffer;
}
}
export default Gzip;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,217 @@
/**
* @author bmwhitn [brian.m.whitney@outlook.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Microsoft Script Decoder operation
*/
class MicrosoftScriptDecoder extends Operation {
/**
* MicrosoftScriptDecoder constructor
*/
constructor() {
super();
this.name = "Microsoft Script Decoder";
this.module = "Default";
this.description = "Decodes Microsoft Encoded Script files that have been encoded with Microsoft's custom encoding. These are often VBS (Visual Basic Script) files that are encoded and renamed with a '.vbe' extention or JS (JScript) files renamed with a '.jse' extention.<br><br><b>Sample</b><br><br>Encoded:<br><code>#@~^RQAAAA==-mD~sX|:/TP{~J:+dYbxL~@!F@*@!+@*@!&amp;@*eEI@#@&amp;@#@&amp;.jm.raY 214Wv:zms/obI0xEAAA==^#~@</code><br><br>Decoded:<br><code>var my_msg = &#34;Testing <1><2><3>!&#34;;\n\nVScript.Echo(my_msg);</code>";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const matcher = /#@~\^.{6}==(.+).{6}==\^#~@/;
const encodedData = matcher.exec(input);
if (encodedData){
return MicrosoftScriptDecoder._decode(encodedData[1]);
} else {
return "";
}
}
/**
* Decodes Microsoft Encoded Script files that can be read and executed by cscript.exe/wscript.exe.
* This is a conversion of a Python script that was originally created by Didier Stevens
* (https://DidierStevens.com).
*
* @private
* @param {string} data
* @returns {string}
*/
static _decode(data) {
const result = [];
let index = -1;
data = data.replace(/@&/g, String.fromCharCode(10))
.replace(/@#/g, String.fromCharCode(13))
.replace(/@\*/g, ">")
.replace(/@!/g, "<")
.replace(/@\$/g, "@");
for (let i = 0; i < data.length; i++) {
const byte = data.charCodeAt(i);
let char = data.charAt(i);
if (byte < 128) {
index++;
}
if ((byte === 9 || byte > 31 && byte < 128) &&
byte !== 60 &&
byte !== 62 &&
byte !== 64) {
char = D_DECODE[byte].charAt(D_COMBINATION[index % 64]);
}
result.push(char);
}
return result.join("");
}
}
const D_DECODE = [
"",
"",
"",
"",
"",
"",
"",
"",
"",
"\x57\x6E\x7B",
"\x4A\x4C\x41",
"\x0B\x0B\x0B",
"\x0C\x0C\x0C",
"\x4A\x4C\x41",
"\x0E\x0E\x0E",
"\x0F\x0F\x0F",
"\x10\x10\x10",
"\x11\x11\x11",
"\x12\x12\x12",
"\x13\x13\x13",
"\x14\x14\x14",
"\x15\x15\x15",
"\x16\x16\x16",
"\x17\x17\x17",
"\x18\x18\x18",
"\x19\x19\x19",
"\x1A\x1A\x1A",
"\x1B\x1B\x1B",
"\x1C\x1C\x1C",
"\x1D\x1D\x1D",
"\x1E\x1E\x1E",
"\x1F\x1F\x1F",
"\x2E\x2D\x32",
"\x47\x75\x30",
"\x7A\x52\x21",
"\x56\x60\x29",
"\x42\x71\x5B",
"\x6A\x5E\x38",
"\x2F\x49\x33",
"\x26\x5C\x3D",
"\x49\x62\x58",
"\x41\x7D\x3A",
"\x34\x29\x35",
"\x32\x36\x65",
"\x5B\x20\x39",
"\x76\x7C\x5C",
"\x72\x7A\x56",
"\x43\x7F\x73",
"\x38\x6B\x66",
"\x39\x63\x4E",
"\x70\x33\x45",
"\x45\x2B\x6B",
"\x68\x68\x62",
"\x71\x51\x59",
"\x4F\x66\x78",
"\x09\x76\x5E",
"\x62\x31\x7D",
"\x44\x64\x4A",
"\x23\x54\x6D",
"\x75\x43\x71",
"\x4A\x4C\x41",
"\x7E\x3A\x60",
"\x4A\x4C\x41",
"\x5E\x7E\x53",
"\x40\x4C\x40",
"\x77\x45\x42",
"\x4A\x2C\x27",
"\x61\x2A\x48",
"\x5D\x74\x72",
"\x22\x27\x75",
"\x4B\x37\x31",
"\x6F\x44\x37",
"\x4E\x79\x4D",
"\x3B\x59\x52",
"\x4C\x2F\x22",
"\x50\x6F\x54",
"\x67\x26\x6A",
"\x2A\x72\x47",
"\x7D\x6A\x64",
"\x74\x39\x2D",
"\x54\x7B\x20",
"\x2B\x3F\x7F",
"\x2D\x38\x2E",
"\x2C\x77\x4C",
"\x30\x67\x5D",
"\x6E\x53\x7E",
"\x6B\x47\x6C",
"\x66\x34\x6F",
"\x35\x78\x79",
"\x25\x5D\x74",
"\x21\x30\x43",
"\x64\x23\x26",
"\x4D\x5A\x76",
"\x52\x5B\x25",
"\x63\x6C\x24",
"\x3F\x48\x2B",
"\x7B\x55\x28",
"\x78\x70\x23",
"\x29\x69\x41",
"\x28\x2E\x34",
"\x73\x4C\x09",
"\x59\x21\x2A",
"\x33\x24\x44",
"\x7F\x4E\x3F",
"\x6D\x50\x77",
"\x55\x09\x3B",
"\x53\x56\x55",
"\x7C\x73\x69",
"\x3A\x35\x61",
"\x5F\x61\x63",
"\x65\x4B\x50",
"\x46\x58\x67",
"\x58\x3B\x51",
"\x31\x57\x49",
"\x69\x22\x4F",
"\x6C\x6D\x46",
"\x5A\x4D\x68",
"\x48\x25\x7C",
"\x27\x28\x36",
"\x5C\x46\x70",
"\x3D\x4A\x6E",
"\x24\x32\x7A",
"\x79\x41\x2F",
"\x37\x3D\x5F",
"\x60\x5F\x4B",
"\x51\x4F\x5A",
"\x20\x42\x2C",
"\x36\x65\x57"
];
const D_COMBINATION = [
0, 1, 2, 0, 1, 2, 1, 2, 2, 1, 2, 1, 0, 2, 1, 2, 0, 2, 1, 2, 0, 0, 1, 2, 2, 1, 0, 2, 1, 2, 2, 1,
0, 0, 2, 1, 2, 1, 2, 0, 2, 0, 0, 1, 2, 0, 2, 1, 0, 2, 1, 2, 0, 0, 1, 2, 2, 0, 0, 1, 2, 0, 2, 1
];
export default MicrosoftScriptDecoder;

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 { multi, createNumArray } from "../lib/Arithmetic";
import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim";
/**
* Multiply operation
*/
class Multiply extends Operation {
/**
* Multiply constructor
*/
constructor() {
super();
this.name = "Multiply";
this.module = "Default";
this.description = "Multiplies 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>40</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 = multi(createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
}
}
export default Multiply;

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 OperationError from "../errors/OperationError";
/**
* Offset checker operation
*/
class OffsetChecker extends Operation {
/**
* OffsetChecker constructor
*/
constructor() {
super();
this.name = "Offset checker";
this.module = "Default";
this.description = "Compares multiple inputs (separated by the specified delimiter) and highlights matching characters which appear at the same position in all samples.";
this.inputType = "string";
this.outputType = "html";
this.args = [
{
"name": "Sample delimiter",
"type": "binaryString",
"value": "\\n\\n"
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
run(input, args) {
const sampleDelim = args[0],
samples = input.split(sampleDelim),
outputs = new Array(samples.length);
let i = 0,
s = 0,
match = false,
inMatch = false,
chr;
if (!samples || samples.length < 2) {
throw new OperationError("Not enough samples, perhaps you need to modify the sample delimiter or add more data?");
}
// Initialise output strings
outputs.fill("", 0, samples.length);
// Loop through each character in the first sample
for (i = 0; i < samples[0].length; i++) {
chr = samples[0][i];
match = false;
// Loop through each sample to see if the chars are the same
for (s = 1; s < samples.length; s++) {
if (samples[s][i] !== chr) {
match = false;
break;
}
match = true;
}
// Write output for each sample
for (s = 0; s < samples.length; s++) {
if (samples[s].length <= i) {
if (inMatch) outputs[s] += "</span>";
if (s === samples.length - 1) inMatch = false;
continue;
}
if (match && !inMatch) {
outputs[s] += "<span class='hl5'>" + Utils.escapeHtml(samples[s][i]);
if (samples[s].length === i + 1) outputs[s] += "</span>";
if (s === samples.length - 1) inMatch = true;
} else if (!match && inMatch) {
outputs[s] += "</span>" + Utils.escapeHtml(samples[s][i]);
if (s === samples.length - 1) inMatch = false;
} else {
outputs[s] += Utils.escapeHtml(samples[s][i]);
if (inMatch && samples[s].length === i + 1) {
outputs[s] += "</span>";
if (samples[s].length - 1 !== i) inMatch = false;
}
}
if (samples[0].length - 1 === i) {
if (inMatch) outputs[s] += "</span>";
outputs[s] += Utils.escapeHtml(samples[s].substring(i + 1));
}
}
}
return outputs.join(sampleDelim);
}
}
export default OffsetChecker;

View file

@ -0,0 +1,86 @@
/**
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
import kbpgp from "kbpgp";
import { ASP, importPrivateKey } from "../lib/PGP";
import OperationError from "../errors/OperationError";
import promisifyDefault from "es6-promisify";
const promisify = promisifyDefault.promisify;
/**
* PGP Decrypt operation
*/
class PGPDecrypt extends Operation {
/**
* PGPDecrypt constructor
*/
constructor() {
super();
this.name = "PGP Decrypt";
this.module = "PGP";
this.description = [
"Input: the ASCII-armoured PGP message you want to decrypt.",
"<br><br>",
"Arguments: the ASCII-armoured PGP private key of the recipient, ",
"(and the private key password if necessary).",
"<br><br>",
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
"<br><br>",
"This function uses the Keybase implementation of PGP.",
].join("\n");
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Private key of recipient",
"type": "text",
"value": ""
},
{
"name": "Private key passphrase",
"type": "string",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if invalid private key
*/
async run(input, args) {
const encryptedMessage = input,
[privateKey, passphrase] = args,
keyring = new kbpgp.keyring.KeyRing();
let plaintextMessage;
if (!privateKey) throw new OperationError("Enter the private key of the recipient.");
const key = await importPrivateKey(privateKey, passphrase);
keyring.add_key_manager(key);
try {
plaintextMessage = await promisify(kbpgp.unbox)({
armored: encryptedMessage,
keyfetch: keyring,
asp: ASP
});
} catch (err) {
throw new OperationError(`Couldn't decrypt message with provided private key: ${err}`);
}
return plaintextMessage.toString();
}
}
export default PGPDecrypt;

View file

@ -0,0 +1,122 @@
/**
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
import kbpgp from "kbpgp";
import { ASP, importPrivateKey, importPublicKey } from "../lib/PGP";
import OperationError from "../errors/OperationError";
import promisifyDefault from "es6-promisify";
const promisify = promisifyDefault.promisify;
/**
* PGP Decrypt and Verify operation
*/
class PGPDecryptAndVerify extends Operation {
/**
* PGPDecryptAndVerify constructor
*/
constructor() {
super();
this.name = "PGP Decrypt and Verify";
this.module = "PGP";
this.description = [
"Input: the ASCII-armoured encrypted PGP message you want to verify.",
"<br><br>",
"Arguments: the ASCII-armoured PGP public key of the signer, ",
"the ASCII-armoured private key of the recipient (and the private key password if necessary).",
"<br><br>",
"This operation uses PGP to decrypt and verify an encrypted digital signature.",
"<br><br>",
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
"<br><br>",
"This function uses the Keybase implementation of PGP.",
].join("\n");
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Public key of signer",
"type": "text",
"value": ""
},
{
"name": "Private key of recipient",
"type": "text",
"value": ""
},
{
"name": "Private key password",
"type": "string",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
async run(input, args) {
const signedMessage = input,
[publicKey, privateKey, passphrase] = args,
keyring = new kbpgp.keyring.KeyRing();
let unboxedLiterals;
if (!publicKey) throw new OperationError("Enter the public key of the signer.");
if (!privateKey) throw new OperationError("Enter the private key of the recipient.");
const privKey = await importPrivateKey(privateKey, passphrase);
const pubKey = await importPublicKey(publicKey);
keyring.add_key_manager(privKey);
keyring.add_key_manager(pubKey);
try {
unboxedLiterals = await promisify(kbpgp.unbox)({
armored: signedMessage,
keyfetch: keyring,
asp: ASP
});
const ds = unboxedLiterals[0].get_data_signer();
if (ds) {
const km = ds.get_key_manager();
if (km) {
const signer = km.get_userids_mark_primary()[0].components;
let text = "Signed by ";
if (signer.email || signer.username || signer.comment) {
if (signer.username) {
text += `${signer.username} `;
}
if (signer.comment) {
text += `${signer.comment} `;
}
if (signer.email) {
text += `<${signer.email}>`;
}
text += "\n";
}
text += [
`PGP fingerprint: ${km.get_pgp_fingerprint().toString("hex")}`,
`Signed on ${new Date(ds.sig.hashed_subpackets[0].time * 1000).toUTCString()}`,
"----------------------------------\n"
].join("\n");
text += unboxedLiterals.toString();
return text.trim();
} else {
throw new OperationError("Could not identify a key manager.");
}
} else {
throw new OperationError("The data does not appear to be signed.");
}
} catch (err) {
throw new OperationError(`Couldn't verify message: ${err}`);
}
}
}
export default PGPDecryptAndVerify;

View file

@ -0,0 +1,85 @@
/**
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
import kbpgp from "kbpgp";
import { ASP } from "../lib/PGP";
import OperationError from "../errors/OperationError";
import promisifyDefault from "es6-promisify";
const promisify = promisifyDefault.promisify;
/**
* PGP Encrypt operation
*/
class PGPEncrypt extends Operation {
/**
* PGPEncrypt constructor
*/
constructor() {
super();
this.name = "PGP Encrypt";
this.module = "PGP";
this.description = [
"Input: the message you want to encrypt.",
"<br><br>",
"Arguments: the ASCII-armoured PGP public key of the recipient.",
"<br><br>",
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
"<br><br>",
"This function uses the Keybase implementation of PGP.",
].join("\n");
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Public key of recipient",
"type": "text",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if failed private key import or failed encryption
*/
async run(input, args) {
const plaintextMessage = input,
plainPubKey = args[0];
let key,
encryptedMessage;
if (!plainPubKey) throw new OperationError("Enter the public key of the recipient.");
try {
key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
armored: plainPubKey,
});
} catch (err) {
throw new OperationError(`Could not import public key: ${err}`);
}
try {
encryptedMessage = await promisify(kbpgp.box)({
"msg": plaintextMessage,
"encrypt_for": key,
"asp": ASP
});
} catch (err) {
throw new OperationError(`Couldn't encrypt message with provided public key: ${err}`);
}
return encryptedMessage.toString();
}
}
export default PGPEncrypt;

View file

@ -0,0 +1,93 @@
/**
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import Operation from "../Operation";
import kbpgp from "kbpgp";
import { ASP, importPrivateKey, importPublicKey } from "../lib/PGP";
import OperationError from "../errors/OperationError";
import promisifyDefault from "es6-promisify";
const promisify = promisifyDefault.promisify;
/**
* PGP Encrypt and Sign operation
*/
class PGPEncryptAndSign extends Operation {
/**
* PGPEncryptAndSign constructor
*/
constructor() {
super();
this.name = "PGP Encrypt and Sign";
this.module = "PGP";
this.description = [
"Input: the cleartext you want to sign.",
"<br><br>",
"Arguments: the ASCII-armoured private key of the signer (plus the private key password if necessary)",
"and the ASCII-armoured PGP public key of the recipient.",
"<br><br>",
"This operation uses PGP to produce an encrypted digital signature.",
"<br><br>",
"Pretty Good Privacy is an encryption standard (OpenPGP) used for encrypting, decrypting, and signing messages.",
"<br><br>",
"This function uses the Keybase implementation of PGP.",
].join("\n");
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Private key of signer",
"type": "text",
"value": ""
},
{
"name": "Private key passphrase",
"type": "string",
"value": ""
},
{
"name": "Public key of recipient",
"type": "text",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if failure to sign message
*/
async run(input, args) {
const message = input,
[privateKey, passphrase, publicKey] = args;
let signedMessage;
if (!privateKey) throw new OperationError("Enter the private key of the signer.");
if (!publicKey) throw new OperationError("Enter the public key of the recipient.");
const privKey = await importPrivateKey(privateKey, passphrase);
const pubKey = await importPublicKey(publicKey);
try {
signedMessage = await promisify(kbpgp.box)({
"msg": message,
"encrypt_for": pubKey,
"sign_with": privKey,
"asp": ASP
});
} catch (err) {
throw new OperationError(`Couldn't sign message: ${err}`);
}
return signedMessage;
}
}
export default PGPEncryptAndSign;

View file

@ -0,0 +1,70 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Pad lines operation
*/
class PadLines extends Operation {
/**
* PadLines constructor
*/
constructor() {
super();
this.name = "Pad lines";
this.module = "Default";
this.description = "Add the specified number of the specified character to the beginning or end of each line";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Position",
"type": "option",
"value": ["Start", "End"]
},
{
"name": "Length",
"type": "number",
"value": 5
},
{
"name": "Character",
"type": "binaryShortString",
"value": " "
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const [position, len, chr] = args,
lines = input.split("\n");
let output = "",
i = 0;
if (position === "Start") {
for (i = 0; i < lines.length; i++) {
output += lines[i].padStart(lines[i].length+len, chr) + "\n";
}
} else if (position === "End") {
for (i = 0; i < lines.length; i++) {
output += lines[i].padEnd(lines[i].length+len, chr) + "\n";
}
}
return output.slice(0, output.length-1);
}
}
export default PadLines;

View file

@ -0,0 +1,195 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Parse colour code operation
*/
class ParseColourCode extends Operation {
/**
* ParseColourCode constructor
*/
constructor() {
super();
this.name = "Parse colour code";
this.module = "Default";
this.description = "Converts a colour code in a standard format to other standard formats and displays the colour itself.<br><br><strong>Example inputs</strong><ul><li><code>#d9edf7</code></li><li><code>rgba(217,237,247,1)</code></li><li><code>hsla(200,65%,91%,1)</code></li><li><code>cmyk(0.12, 0.04, 0.00, 0.03)</code></li></ul>";
this.inputType = "string";
this.outputType = "html";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {html}
*/
run(input, args) {
let m = null,
r = 0, g = 0, b = 0, a = 1;
// Read in the input
if ((m = input.match(/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/i))) {
// Hex - #d9edf7
r = parseInt(m[1], 16);
g = parseInt(m[2], 16);
b = parseInt(m[3], 16);
} else if ((m = input.match(/rgba?\((\d{1,3}(?:\.\d+)?),\s?(\d{1,3}(?:\.\d+)?),\s?(\d{1,3}(?:\.\d+)?)(?:,\s?(\d(?:\.\d+)?))?\)/i))) {
// RGB or RGBA - rgb(217,237,247) or rgba(217,237,247,1)
r = parseFloat(m[1]);
g = parseFloat(m[2]);
b = parseFloat(m[3]);
a = m[4] ? parseFloat(m[4]) : 1;
} else if ((m = input.match(/hsla?\((\d{1,3}(?:\.\d+)?),\s?(\d{1,3}(?:\.\d+)?)%,\s?(\d{1,3}(?:\.\d+)?)%(?:,\s?(\d(?:\.\d+)?))?\)/i))) {
// HSL or HSLA - hsl(200, 65%, 91%) or hsla(200, 65%, 91%, 1)
const h_ = parseFloat(m[1]) / 360,
s_ = parseFloat(m[2]) / 100,
l_ = parseFloat(m[3]) / 100,
rgb_ = ParseColourCode._hslToRgb(h_, s_, l_);
r = rgb_[0];
g = rgb_[1];
b = rgb_[2];
a = m[4] ? parseFloat(m[4]) : 1;
} else if ((m = input.match(/cmyk\((\d(?:\.\d+)?),\s?(\d(?:\.\d+)?),\s?(\d(?:\.\d+)?),\s?(\d(?:\.\d+)?)\)/i))) {
// CMYK - cmyk(0.12, 0.04, 0.00, 0.03)
const c_ = parseFloat(m[1]),
m_ = parseFloat(m[2]),
y_ = parseFloat(m[3]),
k_ = parseFloat(m[4]);
r = Math.round(255 * (1 - c_) * (1 - k_));
g = Math.round(255 * (1 - m_) * (1 - k_));
b = Math.round(255 * (1 - y_) * (1 - k_));
}
const hsl_ = ParseColourCode._rgbToHsl(r, g, b),
h = Math.round(hsl_[0] * 360),
s = Math.round(hsl_[1] * 100),
l = Math.round(hsl_[2] * 100);
let k = 1 - Math.max(r/255, g/255, b/255),
c = (1 - r/255 - k) / (1 - k),
y = (1 - b/255 - k) / (1 - k);
m = (1 - g/255 - k) / (1 - k);
c = isNaN(c) ? "0" : c.toFixed(2);
m = isNaN(m) ? "0" : m.toFixed(2);
y = isNaN(y) ? "0" : y.toFixed(2);
k = k.toFixed(2);
const hex = "#" +
Math.round(r).toString(16).padStart(2, "0") +
Math.round(g).toString(16).padStart(2, "0") +
Math.round(b).toString(16).padStart(2, "0"),
rgb = "rgb(" + r + ", " + g + ", " + b + ")",
rgba = "rgba(" + r + ", " + g + ", " + b + ", " + a + ")",
hsl = "hsl(" + h + ", " + s + "%, " + l + "%)",
hsla = "hsla(" + h + ", " + s + "%, " + l + "%, " + a + ")",
cmyk = "cmyk(" + c + ", " + m + ", " + y + ", " + k + ")";
// Generate output
return `<div id="colorpicker" style="display: inline-block"></div>
Hex: ${hex}
RGB: ${rgb}
RGBA: ${rgba}
HSL: ${hsl}
HSLA: ${hsla}
CMYK: ${cmyk}
<script>
$('#colorpicker').colorpicker({
format: 'rgba',
color: '${rgba}',
container: true,
inline: true,
}).on('changeColor', function(e) {
var color = e.color.toRGB();
document.getElementById('input-text').value = 'rgba(' +
color.r + ', ' + color.g + ', ' + color.b + ', ' + color.a + ')';
window.app.autoBake();
});
</script>`;
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_colorSpace.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @author Mohsen (http://stackoverflow.com/a/9493060)
*
* @param {number} h - The hue
* @param {number} s - The saturation
* @param {number} l - The lightness
* @return {Array} The RGB representation
*/
static _hslToRgb(h, s, l) {
let r, g, b;
if (s === 0){
r = g = b = l; // achromatic
} else {
const hue2rgb = function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_colorSpace.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* @author Mohsen (http://stackoverflow.com/a/9493060)
*
* @param {number} r - The red color value
* @param {number} g - The green color value
* @param {number} b - The blue color value
* @return {Array} The HSL representation
*/
static _rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b),
min = Math.min(r, g, b),
l = (max + min) / 2;
let h, s;
if (max === min) {
h = s = 0; // achromatic
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h, s, l];
}
}
export default ParseColourCode;

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