Merge remote-tracking branch 'upstream/master' into ssh-host-key

Bring up to date with master
This commit is contained in:
j433866 2019-08-13 13:26:40 +01:00
commit 1cdcaebb4d
540 changed files with 30965 additions and 5829 deletions

View file

@ -4,10 +4,10 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {DELIM_OPTIONS} from "../lib/Delim";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import {DELIM_OPTIONS} from "../lib/Delim.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* A1Z26 Cipher Decode operation

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {DELIM_OPTIONS} from "../lib/Delim";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import {DELIM_OPTIONS} from "../lib/Delim.mjs";
/**
* A1Z26 Cipher Encode operation

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import { bitOp, add, BITWISE_OP_DELIMS } from "../lib/BitwiseOp";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import { bitOp, add, BITWISE_OP_DELIMS } from "../lib/BitwiseOp.mjs";
/**
* ADD operation

View file

@ -4,10 +4,10 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import forge from "node-forge/dist/forge.min.js";
import OperationError from "../errors/OperationError";
import OperationError from "../errors/OperationError.mjs";
/**
* AES Decrypt operation

View file

@ -4,10 +4,10 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import forge from "node-forge/dist/forge.min.js";
import OperationError from "../errors/OperationError";
import OperationError from "../errors/OperationError.mjs";
/**
* AES Encrypt operation
@ -65,8 +65,8 @@ class AESEncrypt extends Operation {
* @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),
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteString(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4];

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import { bitOp, and, BITWISE_OP_DELIMS } from "../lib/BitwiseOp";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import { bitOp, and, BITWISE_OP_DELIMS } from "../lib/BitwiseOp.mjs";
/**
* AND operation

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Add line numbers operation

View file

@ -0,0 +1,267 @@
/**
* @author j433866 [j433866@gmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import jimp from "jimp";
/**
* Add Text To Image operation
*/
class AddTextToImage extends Operation {
/**
* AddTextToImage constructor
*/
constructor() {
super();
this.name = "Add Text To Image";
this.module = "Image";
this.description = "Adds text onto an image.<br><br>Text can be horizontally or vertically aligned, or the position can be manually specified.<br>Variants of the Roboto font face are available in any size or colour.";
this.infoURL = "";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.presentType = "html";
this.args = [
{
name: "Text",
type: "string",
value: ""
},
{
name: "Horizontal align",
type: "option",
value: ["None", "Left", "Center", "Right"]
},
{
name: "Vertical align",
type: "option",
value: ["None", "Top", "Middle", "Bottom"]
},
{
name: "X position",
type: "number",
value: 0
},
{
name: "Y position",
type: "number",
value: 0
},
{
name: "Size",
type: "number",
value: 32,
min: 8
},
{
name: "Font face",
type: "option",
value: [
"Roboto",
"Roboto Black",
"Roboto Mono",
"Roboto Slab"
]
},
{
name: "Red",
type: "number",
value: 255,
min: 0,
max: 255
},
{
name: "Green",
type: "number",
value: 255,
min: 0,
max: 255
},
{
name: "Blue",
type: "number",
value: 255,
min: 0,
max: 255
},
{
name: "Alpha",
type: "number",
value: 255,
min: 0,
max: 255
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {byteArray}
*/
async run(input, args) {
const text = args[0],
hAlign = args[1],
vAlign = args[2],
size = args[5],
fontFace = args[6],
red = args[7],
green = args[8],
blue = args[9],
alpha = args[10];
let xPos = args[3],
yPos = args[4];
if (!isImage(new Uint8Array(input))) {
throw new OperationError("Invalid file type.");
}
let image;
try {
image = await jimp.read(input);
} catch (err) {
throw new OperationError(`Error loading image. (${err})`);
}
try {
if (isWorkerEnvironment())
self.sendStatusMessage("Adding text to image...");
const fontsMap = {};
const fonts = [
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.fnt"),
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoBlack72White.fnt"),
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoMono72White.fnt"),
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoSlab72White.fnt")
];
await Promise.all(fonts)
.then(fonts => {
fontsMap.Roboto = fonts[0];
fontsMap["Roboto Black"] = fonts[1];
fontsMap["Roboto Mono"] = fonts[2];
fontsMap["Roboto Slab"] = fonts[3];
});
// Make Webpack load the png font images
await Promise.all([
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/Roboto72White.png"),
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoSlab72White.png"),
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoMono72White.png"),
import(/* webpackMode: "eager" */ "../../web/static/fonts/bmfonts/RobotoBlack72White.png")
]);
const font = fontsMap[fontFace];
// LoadFont needs an absolute url, so append the font name to self.docURL
const jimpFont = await jimp.loadFont(self.docURL + "/" + font.default);
jimpFont.pages.forEach(function(page) {
if (page.bitmap) {
// Adjust the RGB values of the image pages to change the font colour.
const pageWidth = page.bitmap.width;
const pageHeight = page.bitmap.height;
for (let ix = 0; ix < pageWidth; ix++) {
for (let iy = 0; iy < pageHeight; iy++) {
const idx = (iy * pageWidth + ix) << 2;
const newRed = page.bitmap.data[idx] - (255 - red);
const newGreen = page.bitmap.data[idx + 1] - (255 - green);
const newBlue = page.bitmap.data[idx + 2] - (255 - blue);
const newAlpha = page.bitmap.data[idx + 3] - (255 - alpha);
// Make sure the bitmap values don't go below 0 as that makes jimp very unhappy
page.bitmap.data[idx] = (newRed > 0) ? newRed : 0;
page.bitmap.data[idx + 1] = (newGreen > 0) ? newGreen : 0;
page.bitmap.data[idx + 2] = (newBlue > 0) ? newBlue : 0;
page.bitmap.data[idx + 3] = (newAlpha > 0) ? newAlpha : 0;
}
}
}
});
// Create a temporary image to hold the rendered text
const textImage = new jimp(jimp.measureText(jimpFont, text), jimp.measureTextHeight(jimpFont, text));
textImage.print(jimpFont, 0, 0, text);
// Scale the rendered text image to the correct size
const scaleFactor = size / 72;
if (size !== 1) {
// Use bicubic for decreasing size
if (size > 1) {
textImage.scale(scaleFactor, jimp.RESIZE_BICUBIC);
} else {
textImage.scale(scaleFactor, jimp.RESIZE_BILINEAR);
}
}
// If using the alignment options, calculate the pixel values AFTER the image has been scaled
switch (hAlign) {
case "Left":
xPos = 0;
break;
case "Center":
xPos = (image.getWidth() / 2) - (textImage.getWidth() / 2);
break;
case "Right":
xPos = image.getWidth() - textImage.getWidth();
break;
}
switch (vAlign) {
case "Top":
yPos = 0;
break;
case "Middle":
yPos = (image.getHeight() / 2) - (textImage.getHeight() / 2);
break;
case "Bottom":
yPos = image.getHeight() - textImage.getHeight();
break;
}
// Blit the rendered text image onto the original source image
image.blit(textImage, xPos, yPos);
let imageBuffer;
if (image.getMIME() === "image/gif") {
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
} else {
imageBuffer = await image.getBufferAsync(jimp.AUTO);
}
return imageBuffer.buffer;
} catch (err) {
throw new OperationError(`Error adding text to image. (${err})`);
}
}
/**
* Displays the blurred image using HTML for web apps
*
* @param {ArrayBuffer} data
* @returns {html}
*/
present(data) {
if (!data.byteLength) return "";
const dataArray = new Uint8Array(data);
const type = isImage(dataArray);
if (!type) {
throw new OperationError("Invalid file type.");
}
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
}
}
export default AddTextToImage;

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
/**
* Adler-32 Checksum operation
@ -22,13 +22,13 @@ class Adler32Checksum extends Operation {
this.module = "Crypto";
this.description = "Adler-32 is a checksum algorithm which was invented by Mark Adler in 1995, and is a modification of the Fletcher checksum. Compared to a cyclic redundancy check of the same length, it trades reliability for speed (preferring the latter).<br><br>Adler-32 is more reliable than Fletcher-16, and slightly less reliable than Fletcher-32.";
this.infoURL = "https://wikipedia.org/wiki/Adler-32";
this.inputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
@ -36,6 +36,7 @@ class Adler32Checksum extends Operation {
const MOD_ADLER = 65521;
let a = 1,
b = 0;
input = new Uint8Array(input);
for (let i = 0; i < input.length; i++) {
a += input[i];

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Affine Cipher Decode operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import { affineEncode } from "../lib/Ciphers";
import Operation from "../Operation.mjs";
import { affineEncode } from "../lib/Ciphers.mjs";
/**
* Affine Cipher Encode operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Analyse hash operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import { affineEncode } from "../lib/Ciphers";
import Operation from "../Operation.mjs";
import { affineEncode } from "../lib/Ciphers.mjs";
/**
* Atbash Cipher operation

View file

@ -4,11 +4,11 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
import blakejs from "blakejs";
import OperationError from "../errors/OperationError";
import Utils from "../Utils";
import { toBase64 } from "../lib/Base64";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
import { toBase64 } from "../lib/Base64.mjs";
/**
* BLAKE2b operation

View file

@ -4,11 +4,11 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
import blakejs from "blakejs";
import OperationError from "../errors/OperationError";
import Utils from "../Utils";
import { toBase64 } from "../lib/Base64";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
import { toBase64 } from "../lib/Base64.mjs";
/**
* BLAKE2s Operation

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
import bson from "bson";
import OperationError from "../errors/OperationError";
import OperationError from "../errors/OperationError.mjs";
/**
* BSON deserialise operation

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
import bson from "bson";
import OperationError from "../errors/OperationError";
import OperationError from "../errors/OperationError.mjs";
/**
* BSON serialise operation

View file

@ -4,8 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
import bcrypt from "bcryptjs";
import { isWorkerEnvironment } from "../Utils.mjs";
/**
* Bcrypt operation
@ -44,7 +45,7 @@ class Bcrypt extends Operation {
return await bcrypt.hash(input, salt, null, p => {
// Progress callback
if (ENVIRONMENT_IS_WORKER())
if (isWorkerEnvironment())
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
});

View file

@ -4,8 +4,10 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
import bcrypt from "bcryptjs";
import { isWorkerEnvironment } from "../Utils.mjs";
/**
* Bcrypt compare operation
@ -43,7 +45,7 @@ class BcryptCompare extends Operation {
const match = await bcrypt.compare(input, hash, null, p => {
// Progress callback
if (ENVIRONMENT_IS_WORKER())
if (isWorkerEnvironment())
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
});

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import bcrypt from "bcryptjs";
/**

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import { genPolybiusSquare } from "../lib/Ciphers";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import { genPolybiusSquare } from "../lib/Ciphers.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Bifid Cipher Decode operation

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import { genPolybiusSquare } from "../lib/Ciphers";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { genPolybiusSquare } from "../lib/Ciphers.mjs";
/**
* Bifid Cipher Encode operation

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Bit shift left operation
@ -21,8 +21,8 @@ class BitShiftLeft extends Operation {
this.module = "Default";
this.description = "Shifts the bits in each byte towards the left by the specified amount.";
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bit_shifts";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.args = [
{
"name": "Amount",
@ -33,16 +33,17 @@ class BitShiftLeft extends Operation {
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {byteArray}
* @returns {ArrayBuffer}
*/
run(input, args) {
const amount = args[0];
input = new Uint8Array(input);
return input.map(b => {
return (b << amount) & 0xff;
});
}).buffer;
}
/**

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Bit shift right operation
@ -21,8 +21,8 @@ class BitShiftRight extends Operation {
this.module = "Default";
this.description = "Shifts the bits in each byte towards the right by the specified amount.<br><br><i>Logical shifts</i> replace the leftmost bits with zeros.<br><i>Arithmetic shifts</i> preserve the most significant bit (MSB) of the original byte keeping the sign the same (positive or negative).";
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bit_shifts";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.args = [
{
"name": "Amount",
@ -38,18 +38,19 @@ class BitShiftRight extends Operation {
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {byteArray}
* @returns {ArrayBuffer}
*/
run(input, args) {
const amount = args[0],
type = args[1],
mask = type === "Logical shift" ? 0 : 0x80;
input = new Uint8Array(input);
return input.map(b => {
return (b >>> amount) ^ (b & mask);
});
}).buffer;
}
/**

View file

@ -4,12 +4,12 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
import { Blowfish } from "../vendor/Blowfish";
import { toBase64 } from "../lib/Base64";
import { toHexFast } from "../lib/Hex";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
import { Blowfish } from "../vendor/Blowfish.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { toHexFast } from "../lib/Hex.mjs";
/**
* Lookup table for Blowfish output types.

View file

@ -4,11 +4,11 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
import { Blowfish } from "../vendor/Blowfish";
import { toBase64 } from "../lib/Base64";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
import { Blowfish } from "../vendor/Blowfish.mjs";
import { toBase64 } from "../lib/Base64.mjs";
/**
* Lookup table for Blowfish output types.

View file

@ -4,11 +4,13 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import { isImage } from "../lib/FileType";
import { toBase64 } from "../lib/Base64";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import jimp from "jimp";
import { gaussianBlur } from "../lib/ImageManipulation.mjs";
/**
* Blur Image operation
@ -25,8 +27,8 @@ class BlurImage extends Operation {
this.module = "Image";
this.description = "Applies a blur effect to the image.<br><br>Gaussian blur is much slower than fast blur, but produces better results.";
this.infoURL = "https://wikipedia.org/wiki/Gaussian_blur";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.presentType = "html";
this.args = [
{
@ -44,37 +46,44 @@ class BlurImage extends Operation {
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {byteArray}
*/
async run(input, args) {
const [blurAmount, blurType] = args;
if (!isImage(input)) {
if (!isImage(new Uint8Array(input))) {
throw new OperationError("Invalid file type.");
}
let image;
try {
image = await jimp.read(Buffer.from(input));
image = await jimp.read(input);
} catch (err) {
throw new OperationError(`Error loading image. (${err})`);
}
try {
switch (blurType){
case "Fast":
if (isWorkerEnvironment())
self.sendStatusMessage("Fast blurring image...");
image.blur(blurAmount);
break;
case "Gaussian":
if (ENVIRONMENT_IS_WORKER())
self.sendStatusMessage("Gaussian blurring image. This may take a while...");
image.gaussian(blurAmount);
if (isWorkerEnvironment())
self.sendStatusMessage("Gaussian blurring image...");
image = gaussianBlur(image, blurAmount);
break;
}
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
return [...imageBuffer];
let imageBuffer;
if (image.getMIME() === "image/gif") {
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
} else {
imageBuffer = await image.getBufferAsync(jimp.AUTO);
}
return imageBuffer.buffer;
} catch (err) {
throw new OperationError(`Error blurring image. (${err})`);
}
@ -83,18 +92,19 @@ class BlurImage extends Operation {
/**
* Displays the blurred image using HTML for web apps
*
* @param {byteArray} data
* @param {ArrayBuffer} data
* @returns {html}
*/
present(data) {
if (!data.length) return "";
if (!data.byteLength) return "";
const dataArray = new Uint8Array(data);
const type = isImage(data);
const type = isImage(dataArray);
if (!type) {
throw new OperationError("Invalid file type.");
}
return `<img src="data:${type};base64,${toBase64(data)}">`;
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
}
}

View file

@ -6,10 +6,11 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import {BombeMachine} from "../lib/Bombe";
import {ROTORS, ROTORS_FOURTH, REFLECTORS, Reflector} from "../lib/Enigma";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import { BombeMachine } from "../lib/Bombe.mjs";
import { ROTORS, ROTORS_FOURTH, REFLECTORS, Reflector } from "../lib/Enigma.mjs";
/**
* Bombe operation
@ -139,7 +140,7 @@ class Bombe extends Operation {
const ciphertext = input.slice(offset);
const reflector = new Reflector(reflectorstr);
let update;
if (ENVIRONMENT_IS_WORKER()) {
if (isWorkerEnvironment()) {
update = this.updateStatus;
} else {
update = undefined;

View file

@ -0,0 +1,73 @@
/**
* @author Matt C [me@mitt.dev]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Bzip2 from "libbzip2-wasm";
import { isWorkerEnvironment } from "../Utils.mjs";
/**
* Bzip2 Compress operation
*/
class Bzip2Compress extends Operation {
/**
* Bzip2Compress constructor
*/
constructor() {
super();
this.name = "Bzip2 Compress";
this.module = "Compression";
this.description = "Bzip2 is a compression library developed by Julian Seward (of GHC fame) that uses the Burrows-Wheeler algorithm. It only supports compressing single files and its compression is slow, however is more effective than Deflate (.gz & .zip).";
this.infoURL = "https://wikipedia.org/wiki/Bzip2";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.args = [
{
name: "Block size (100s of kb)",
type: "number",
value: 9,
min: 1,
max: 9
},
{
name: "Work factor",
type: "number",
value: 30
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {File}
*/
run(input, args) {
const [blockSize, workFactor] = args;
if (input.byteLength <= 0) {
throw new OperationError("Please provide an input.");
}
if (isWorkerEnvironment()) self.sendStatusMessage("Loading Bzip2...");
return new Promise((resolve, reject) => {
Bzip2().then(bzip2 => {
if (isWorkerEnvironment()) self.sendStatusMessage("Compressing data...");
const inpArray = new Uint8Array(input);
const bzip2cc = bzip2.compressBZ2(inpArray, blockSize, workFactor);
if (bzip2cc.error !== 0) {
reject(new OperationError(bzip2cc.error_msg));
} else {
const output = bzip2cc.output;
resolve(output.buffer.slice(output.byteOffset, output.byteLength + output.byteOffset));
}
});
});
}
}
export default Bzip2Compress;

View file

@ -1,12 +1,13 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @author Matt C [me@mitt.dev]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation";
import bzip2 from "../vendor/bzip2";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Bzip2 from "libbzip2-wasm";
import { isWorkerEnvironment } from "../Utils.mjs";
/**
* Bzip2 Decompress operation
@ -23,9 +24,15 @@ class Bzip2Decompress extends Operation {
this.module = "Compression";
this.description = "Decompresses data using the Bzip2 algorithm.";
this.infoURL = "https://wikipedia.org/wiki/Bzip2";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [];
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.args = [
{
name: "Use low-memory, slower decompression algorithm",
type: "boolean",
value: false
}
];
this.patterns = [
{
"match": "^\\x42\\x5a\\x68",
@ -41,14 +48,24 @@ class Bzip2Decompress extends Operation {
* @returns {string}
*/
run(input, args) {
const compressed = new Uint8Array(input);
try {
const bzip2Reader = bzip2.array(compressed);
return bzip2.simple(bzip2Reader);
} catch (err) {
throw new OperationError(err);
const [small] = args;
if (input.byteLength <= 0) {
throw new OperationError("Please provide an input.");
}
if (isWorkerEnvironment()) self.sendStatusMessage("Loading Bzip2...");
return new Promise((resolve, reject) => {
Bzip2().then(bzip2 => {
if (isWorkerEnvironment()) self.sendStatusMessage("Decompressing data...");
const inpArray = new Uint8Array(input);
const bzip2cc = bzip2.decompressBZ2(inpArray, small ? 1 : 0);
if (bzip2cc.error !== 0) {
reject(new OperationError(bzip2cc.error_msg));
} else {
const output = bzip2cc.output;
resolve(output.buffer.slice(output.byteOffset, output.byteLength + output.byteOffset));
}
});
});
}
}

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
import JSCRC from "js-crc";
/**

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
import JSCRC from "js-crc";
/**

View file

@ -0,0 +1,157 @@
/**
* @author mshwed [m@ttshwed.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { toHexFast } from "../lib/Hex.mjs";
/**
* CRC-8 Checksum operation
*/
class CRC8Checksum extends Operation {
/**
* CRC8Checksum constructor
*/
constructor() {
super();
this.name = "CRC-8 Checksum";
this.module = "Crypto";
this.description = "A cyclic redundancy check (CRC) is an error-detecting code commonly used in digital networks and storage devices to detect accidental changes to raw data.<br><br>The CRC was invented by W. Wesley Peterson in 1961.";
this.infoURL = "https://wikipedia.org/wiki/Cyclic_redundancy_check";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
"name": "Algorithm",
"type": "option",
"value": [
"CRC-8",
"CRC-8/CDMA2000",
"CRC-8/DARC",
"CRC-8/DVB-S2",
"CRC-8/EBU",
"CRC-8/I-CODE",
"CRC-8/ITU",
"CRC-8/MAXIM",
"CRC-8/ROHC",
"CRC-8/WCDMA"
]
}
];
}
/**
* Generates the pre-computed lookup table for byte division
*
* @param polynomial
*/
calculateCRC8LookupTable(polynomial) {
const crc8Table = new Uint8Array(256);
let currentByte;
for (let i = 0; i < 256; i++) {
currentByte = i;
for (let bit = 0; bit < 8; bit++) {
if ((currentByte & 0x80) !== 0) {
currentByte <<= 1;
currentByte ^= polynomial;
} else {
currentByte <<= 1;
}
}
crc8Table[i] = currentByte;
}
return crc8Table;
}
/**
* Calculates the CRC-8 Checksum from an input
*
* @param {ArrayBuffer} input
* @param {number} polynomial
* @param {number} initializationValue
* @param {boolean} inputReflection
* @param {boolean} outputReflection
* @param {number} xorOut
*/
calculateCRC8(input, polynomial, initializationValue, inputReflection, outputReflection, xorOut) {
const crcSize = 8;
const crcTable = this.calculateCRC8LookupTable(polynomial);
let crc = initializationValue !== 0 ? initializationValue : 0;
let currentByte, position;
input = new Uint8Array(input);
for (const inputByte of input) {
currentByte = inputReflection ? this.reverseBits(inputByte, crcSize) : inputByte;
position = (currentByte ^ crc) & 255;
crc = crcTable[position];
}
crc = outputReflection ? this.reverseBits(crc, crcSize) : crc;
if (xorOut !== 0) crc = crc ^ xorOut;
return toHexFast(new Uint8Array([crc]));
}
/**
* Reverse the bits for a given input byte.
*
* @param {number} input
*/
reverseBits(input, hashSize) {
let reversedByte = 0;
for (let i = hashSize - 1; i >= 0; i--) {
reversedByte |= ((input & 1) << i);
input >>= 1;
}
return reversedByte;
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const algorithm = args[0];
switch (algorithm) {
case "CRC-8":
return this.calculateCRC8(input, 0x7, 0x0, false, false, 0x0);
case "CRC-8/CDMA2000":
return this.calculateCRC8(input, 0x9B, 0xFF, false, false, 0x0);
case "CRC-8/DARC":
return this.calculateCRC8(input, 0x39, 0x0, true, true, 0x0);
case "CRC-8/DVB-S2":
return this.calculateCRC8(input, 0xD5, 0x0, false, false, 0x0);
case "CRC-8/EBU":
return this.calculateCRC8(input, 0x1D, 0xFF, true, true, 0x0);
case "CRC-8/I-CODE":
return this.calculateCRC8(input, 0x1D, 0xFD, false, false, 0x0);
case "CRC-8/ITU":
return this.calculateCRC8(input, 0x7, 0x0, false, false, 0x55);
case "CRC-8/MAXIM":
return this.calculateCRC8(input, 0x31, 0x0, true, true, 0x0);
case "CRC-8/ROHC":
return this.calculateCRC8(input, 0x7, 0xFF, true, true, 0x0);
case "CRC-8/WCDMA":
return this.calculateCRC8(input, 0x9B, 0x0, true, true, 0x0);
default:
throw new OperationError("Unknown checksum algorithm");
}
}
}
export default CRC8Checksum;

View file

@ -5,7 +5,7 @@
*/
import vkbeautify from "vkbeautify";
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* CSS Beautify operation

View file

@ -5,7 +5,7 @@
*/
import vkbeautify from "vkbeautify";
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* CSS Minify operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import xmldom from "xmldom";
import nwmatcher from "nwmatcher";

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
/**
* CSV to JSON operation

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
import ctphjs from "ctph.js";
/**

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Set cartesian product operation

View file

@ -4,10 +4,10 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Utils from "../Utils";
import {fromHex} from "../lib/Hex";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
import {fromHex} from "../lib/Hex.mjs";
/**
* Change IP format operation

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Chi Square operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import cptable from "../vendor/js-codepage/cptable.js";
/**
@ -23,17 +23,18 @@ class CitrixCTX1Decode extends Operation {
this.module = "Encodings";
this.description = "Decodes strings in a Citrix CTX1 password format to plaintext.";
this.infoURL = "https://www.reddit.com/r/AskNetsec/comments/1s3r6y/citrix_ctx1_hash_decoding/";
this.inputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
input = new Uint8Array(input);
if (input.length % 4 !== 0) {
throw new OperationError("Incorrect hash length");
}

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
import cptable from "../vendor/js-codepage/cptable.js";
/**

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Comment operation

View file

@ -4,11 +4,11 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {HASH_DELIM_OPTIONS} from "../lib/Delim";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import {HASH_DELIM_OPTIONS} from "../lib/Delim.mjs";
import ctphjs from "ctph.js";
import OperationError from "../errors/OperationError";
import OperationError from "../errors/OperationError.mjs";
/**
* Compare CTPH hashes operation

View file

@ -4,11 +4,11 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {HASH_DELIM_OPTIONS} from "../lib/Delim";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import {HASH_DELIM_OPTIONS} from "../lib/Delim.mjs";
import ssdeepjs from "ssdeep.js";
import OperationError from "../errors/OperationError";
import OperationError from "../errors/OperationError.mjs";
/**
* Compare SSDEEP hashes operation

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Dish from "../Dish";
import { getLabelIndex } from "../lib/FlowControl";
import Operation from "../Operation.mjs";
import Dish from "../Dish.mjs";
import { getLabelIndex } from "../lib/FlowControl.mjs";
/**
* Conditional Jump operation

View file

@ -4,10 +4,11 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import { isImage } from "../lib/FileType";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import jimp from "jimp";
/**
@ -25,8 +26,8 @@ class ContainImage extends Operation {
this.module = "Image";
this.description = "Scales an image to the specified width and height, maintaining the aspect ratio. The image may be letterboxed.";
this.infoURL = "";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.presentType = "html";
this.args = [
{
@ -72,17 +73,22 @@ class ContainImage extends Operation {
"Bezier"
],
defaultIndex: 1
},
{
name: "Opaque background",
type: "boolean",
value: true
}
];
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {byteArray}
*/
async run(input, args) {
const [width, height, hAlign, vAlign, alg] = args;
const [width, height, hAlign, vAlign, alg, opaqueBg] = args;
const resizeMap = {
"Nearest Neighbour": jimp.RESIZE_NEAREST_NEIGHBOR,
@ -101,22 +107,34 @@ class ContainImage extends Operation {
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
};
if (!isImage(input)) {
if (!isImage(new Uint8Array(input))) {
throw new OperationError("Invalid file type.");
}
let image;
try {
image = await jimp.read(Buffer.from(input));
image = await jimp.read(input);
} catch (err) {
throw new OperationError(`Error loading image. (${err})`);
}
try {
if (ENVIRONMENT_IS_WORKER())
if (isWorkerEnvironment())
self.sendStatusMessage("Containing image...");
image.contain(width, height, alignMap[hAlign] | alignMap[vAlign], resizeMap[alg]);
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
return [...imageBuffer];
if (opaqueBg) {
const newImage = await jimp.read(width, height, 0x000000FF);
newImage.blit(image, 0, 0);
image = newImage;
}
let imageBuffer;
if (image.getMIME() === "image/gif") {
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
} else {
imageBuffer = await image.getBufferAsync(jimp.AUTO);
}
return imageBuffer.buffer;
} catch (err) {
throw new OperationError(`Error containing image. (${err})`);
}
@ -124,18 +142,19 @@ class ContainImage extends Operation {
/**
* Displays the contained image using HTML for web apps
* @param {byteArray} data
* @param {ArrayBuffer} data
* @returns {html}
*/
present(data) {
if (!data.length) return "";
if (!data.byteLength) return "";
const dataArray = new Uint8Array(data);
const type = isImage(data);
const type = isImage(dataArray);
if (!type) {
throw new OperationError("Invalid file type.");
}
return `<img src="data:${type};base64,${toBase64(data)}">`;
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
}
}

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Convert area operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import {FORMATS, convertCoordinates} from "../lib/ConvertCoordinates";
import Operation from "../Operation.mjs";
import {FORMATS, convertCoordinates} from "../lib/ConvertCoordinates.mjs";
/**
* Convert co-ordinate format operation

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Convert data units operation

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Convert distance operation

View file

@ -0,0 +1,143 @@
/**
* @author j433866 [j433866@gmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import jimp from "jimp";
/**
* Convert Image Format operation
*/
class ConvertImageFormat extends Operation {
/**
* ConvertImageFormat constructor
*/
constructor() {
super();
this.name = "Convert Image Format";
this.module = "Image";
this.description = "Converts an image between different formats. Supported formats:<br><ul><li>Joint Photographic Experts Group (JPEG)</li><li>Portable Network Graphics (PNG)</li><li>Bitmap (BMP)</li><li>Tagged Image File Format (TIFF)</li></ul><br>Note: GIF files are supported for input, but cannot be outputted.";
this.infoURL = "https://wikipedia.org/wiki/Image_file_formats";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.presentType = "html";
this.args = [
{
name: "Output Format",
type: "option",
value: [
"JPEG",
"PNG",
"BMP",
"TIFF"
]
},
{
name: "JPEG Quality",
type: "number",
value: 80,
min: 1,
max: 100
},
{
name: "PNG Filter Type",
type: "option",
value: [
"Auto",
"None",
"Sub",
"Up",
"Average",
"Paeth"
]
},
{
name: "PNG Deflate Level",
type: "number",
value: 9,
min: 0,
max: 9
}
];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {byteArray}
*/
async run(input, args) {
const [format, jpegQuality, pngFilterType, pngDeflateLevel] = args;
const formatMap = {
"JPEG": jimp.MIME_JPEG,
"PNG": jimp.MIME_PNG,
"BMP": jimp.MIME_BMP,
"TIFF": jimp.MIME_TIFF
};
const pngFilterMap = {
"Auto": jimp.PNG_FILTER_AUTO,
"None": jimp.PNG_FILTER_NONE,
"Sub": jimp.PNG_FILTER_SUB,
"Up": jimp.PNG_FILTER_UP,
"Average": jimp.PNG_FILTER_AVERAGE,
"Paeth": jimp.PNG_FILTER_PATH // Incorrect spelling in Jimp library
};
const mime = formatMap[format];
if (!isImage(new Uint8Array(input))) {
throw new OperationError("Invalid file format.");
}
let image;
try {
image = await jimp.read(input);
} catch (err) {
throw new OperationError(`Error opening image file. (${err})`);
}
try {
switch (format) {
case "JPEG":
image.quality(jpegQuality);
break;
case "PNG":
image.filterType(pngFilterMap[pngFilterType]);
image.deflateLevel(pngDeflateLevel);
break;
}
const imageBuffer = await image.getBufferAsync(mime);
return imageBuffer.buffer;
} catch (err) {
throw new OperationError(`Error converting image format. (${err})`);
}
}
/**
* Displays the converted image using HTML for web apps
*
* @param {ArrayBuffer} data
* @returns {html}
*/
present(data) {
if (!data.byteLength) return "";
const dataArray = new Uint8Array(data);
const type = isImage(dataArray);
if (!type) {
throw new OperationError("Invalid file type.");
}
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
}
}
export default ConvertImageFormat;

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Convert mass operation

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Convert speed operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
/**
* Count occurrences operation

View file

@ -4,10 +4,11 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import { isImage } from "../lib/FileType";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import jimp from "jimp";
/**
@ -25,8 +26,8 @@ class CoverImage extends Operation {
this.module = "Image";
this.description = "Scales the image to the given width and height, keeping the aspect ratio. The image may be clipped.";
this.infoURL = "";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.presentType = "html";
this.args = [
{
@ -77,7 +78,7 @@ class CoverImage extends Operation {
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {byteArray}
*/
@ -101,22 +102,27 @@ class CoverImage extends Operation {
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
};
if (!isImage(input)) {
if (!isImage(new Uint8Array(input))) {
throw new OperationError("Invalid file type.");
}
let image;
try {
image = await jimp.read(Buffer.from(input));
image = await jimp.read(input);
} catch (err) {
throw new OperationError(`Error loading image. (${err})`);
}
try {
if (ENVIRONMENT_IS_WORKER())
if (isWorkerEnvironment())
self.sendStatusMessage("Covering image...");
image.cover(width, height, alignMap[hAlign] | alignMap[vAlign], resizeMap[alg]);
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
return [...imageBuffer];
let imageBuffer;
if (image.getMIME() === "image/gif") {
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
} else {
imageBuffer = await image.getBufferAsync(jimp.AUTO);
}
return imageBuffer.buffer;
} catch (err) {
throw new OperationError(`Error covering image. (${err})`);
}
@ -124,18 +130,19 @@ class CoverImage extends Operation {
/**
* Displays the covered image using HTML for web apps
* @param {byteArray} data
* @param {ArrayBuffer} data
* @returns {html}
*/
present(data) {
if (!data.length) return "";
if (!data.byteLength) return "";
const dataArray = new Uint8Array(data);
const type = isImage(data);
const type = isImage(dataArray);
if (!type) {
throw new OperationError("Invalid file type.");
}
return `<img src="data:${type};base64,${toBase64(data)}">`;
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
}
}

View file

@ -4,10 +4,11 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import { isImage } from "../lib/FileType";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import jimp from "jimp";
/**
@ -25,8 +26,8 @@ class CropImage extends Operation {
this.module = "Image";
this.description = "Crops an image to the specified region, or automatically crops edges.<br><br><b><u>Autocrop</u></b><br>Automatically crops same-colour borders from the image.<br><br><u>Autocrop tolerance</u><br>A percentage value for the tolerance of colour difference between pixels.<br><br><u>Only autocrop frames</u><br>Only crop real frames (all sides must have the same border)<br><br><u>Symmetric autocrop</u><br>Force autocrop to be symmetric (top/bottom and left/right are cropped by the same amount)<br><br><u>Autocrop keep border</u><br>The number of pixels of border to leave around the image.";
this.infoURL = "https://wikipedia.org/wiki/Cropping_(image)";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.presentType = "html";
this.args = [
{
@ -86,24 +87,24 @@ class CropImage extends Operation {
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {byteArray}
*/
async run(input, args) {
const [xPos, yPos, width, height, autocrop, autoTolerance, autoFrames, autoSymmetric, autoBorder] = args;
if (!isImage(input)) {
if (!isImage(new Uint8Array(input))) {
throw new OperationError("Invalid file type.");
}
let image;
try {
image = await jimp.read(Buffer.from(input));
image = await jimp.read(input);
} catch (err) {
throw new OperationError(`Error loading image. (${err})`);
}
try {
if (ENVIRONMENT_IS_WORKER())
if (isWorkerEnvironment())
self.sendStatusMessage("Cropping image...");
if (autocrop) {
image.autocrop({
@ -116,8 +117,13 @@ class CropImage extends Operation {
image.crop(xPos, yPos, width, height);
}
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
return [...imageBuffer];
let imageBuffer;
if (image.getMIME() === "image/gif") {
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
} else {
imageBuffer = await image.getBufferAsync(jimp.AUTO);
}
return imageBuffer.buffer;
} catch (err) {
throw new OperationError(`Error cropping image. (${err})`);
}
@ -125,18 +131,19 @@ class CropImage extends Operation {
/**
* Displays the cropped image using HTML for web apps
* @param {byteArray} data
* @param {ArrayBuffer} data
* @returns {html}
*/
present(data) {
if (!data.length) return "";
if (!data.byteLength) return "";
const dataArray = new Uint8Array(data);
const type = isImage(data);
const type = isImage(dataArray);
if (!type) {
throw new OperationError("Invalid file type.");
}
return `<img src="data:${type};base64,${toBase64(data)}">`;
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
}
}

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
import forge from "node-forge/dist/forge.min.js";
/**

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
import forge from "node-forge/dist/forge.min.js";
/**

View file

@ -3,8 +3,8 @@
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* DNS over HTTPS operation

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Dechunk HTTP response operation

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Decode NetBIOS Name operation

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
import cptable from "../vendor/js-codepage/cptable.js";
import {IO_FORMAT} from "../lib/ChrEnc";
import {IO_FORMAT} from "../lib/ChrEnc.mjs";
/**
* Decode text operation
@ -30,7 +30,7 @@ class DecodeText extends Operation {
"</ul>",
].join("\n");
this.infoURL = "https://wikipedia.org/wiki/Character_encoding";
this.inputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
@ -42,13 +42,13 @@ class DecodeText extends Operation {
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const format = IO_FORMAT[args[0]];
return cptable.utils.decode(format, input);
return cptable.utils.decode(format, new Uint8Array(input));
}
}

View file

@ -5,8 +5,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import {URL_REGEX, DOMAIN_REGEX} from "../lib/Extract";
import Operation from "../Operation.mjs";
import {URL_REGEX, DOMAIN_REGEX} from "../lib/Extract.mjs";
/**
* DefangURL operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import CryptoJS from "crypto-js";
/**

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import forge from "node-forge/dist/forge.min.js";
/**

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import {detectFileType} from "../lib/FileType";
import {FILE_SIGNATURES} from "../lib/FileSignatures";
import Operation from "../Operation.mjs";
import {detectFileType} from "../lib/FileType.mjs";
import {FILE_SIGNATURES} from "../lib/FileSignatures.mjs";
/**
* Detect File Type operation
@ -21,7 +21,12 @@ class DetectFileType extends Operation {
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.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: " +
Object.keys(FILE_SIGNATURES).map(cat =>
FILE_SIGNATURES[cat].map(sig =>
sig.extension.split(",")[0]
).join(", ")
).join(", ") + ".";
this.infoURL = "https://wikipedia.org/wiki/List_of_file_signatures";
this.inputType = "ArrayBuffer";
this.outputType = "string";
@ -52,18 +57,19 @@ class DetectFileType extends Operation {
if (!types.length) {
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 = "";
types.forEach(type => {
output += "File extension: " + type.extension + "\n" +
"MIME type: " + type.mime + "\n";
const results = types.map(type => {
let output = `File type: ${type.name}
Extension: ${type.extension}
MIME type: ${type.mime}\n`;
if (type.description && type.description.length) {
output += "\nDescription: " + type.description + "\n";
output += `Description: ${type.description}\n`;
}
return output;
});
return output;
return results.join("\n");
}
}

View file

@ -4,10 +4,10 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import * as JsDiff from "diff";
import OperationError from "../errors/OperationError";
import OperationError from "../errors/OperationError.mjs";
/**
* Diff operation

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import * as disassemble from "../vendor/DisassembleX86-64";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import * as disassemble from "../vendor/DisassembleX86-64.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Disassemble x86 operation

View file

@ -4,10 +4,11 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import { isImage } from "../lib/FileType";
import { toBase64 } from "../lib/Base64";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import jimp from "jimp";
/**
@ -25,34 +26,40 @@ class DitherImage extends Operation {
this.module = "Image";
this.description = "Apply a dither effect to an image.";
this.infoURL = "https://wikipedia.org/wiki/Dither";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.presentType = "html";
this.args = [];
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {byteArray}
*/
async run(input, args) {
if (!isImage(input)) {
if (!isImage(new Uint8Array(input))) {
throw new OperationError("Invalid file type.");
}
let image;
try {
image = await jimp.read(Buffer.from(input));
image = await jimp.read(input);
} catch (err) {
throw new OperationError(`Error loading image. (${err})`);
}
try {
if (ENVIRONMENT_IS_WORKER())
if (isWorkerEnvironment())
self.sendStatusMessage("Applying dither to image...");
image.dither565();
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
return [...imageBuffer];
let imageBuffer;
if (image.getMIME() === "image/gif") {
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
} else {
imageBuffer = await image.getBufferAsync(jimp.AUTO);
}
return imageBuffer.buffer;
} catch (err) {
throw new OperationError(`Error applying dither to image. (${err})`);
}
@ -60,18 +67,19 @@ class DitherImage extends Operation {
/**
* Displays the dithered image using HTML for web apps
* @param {byteArray} data
* @param {ArrayBuffer} data
* @returns {html}
*/
present(data) {
if (!data.length) return "";
if (!data.byteLength) return "";
const dataArray = new Uint8Array(data);
const type = isImage(data);
const type = isImage(dataArray);
if (!type) {
throw new OperationError("Invalid file type.");
}
return `<img src="data:${type};base64,${toBase64(data)}">`;
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
}
}

View file

@ -6,9 +6,9 @@
*/
import BigNumber from "bignumber.js";
import Operation from "../Operation";
import { div, createNumArray } from "../lib/Arithmetic";
import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim";
import Operation from "../Operation.mjs";
import { div, createNumArray } from "../lib/Arithmetic.mjs";
import { ARITHMETIC_DELIM_OPTIONS } from "../lib/Delim.mjs";
/**

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Drop bytes operation

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Encode NetBIOS Name operation

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
import cptable from "../vendor/js-codepage/cptable.js";
import {IO_FORMAT} from "../lib/ChrEnc";
import {IO_FORMAT} from "../lib/ChrEnc.mjs";
/**
* Encode text operation
@ -31,7 +31,7 @@ class EncodeText extends Operation {
].join("\n");
this.infoURL = "https://wikipedia.org/wiki/Character_encoding";
this.inputType = "string";
this.outputType = "byteArray";
this.outputType = "ArrayBuffer";
this.args = [
{
"name": "Encoding",
@ -44,13 +44,12 @@ class EncodeText extends Operation {
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
* @returns {ArrayBuffer}
*/
run(input, args) {
const format = IO_FORMAT[args[0]];
let encoded = cptable.utils.encode(format, input);
encoded = Array.from(encoded);
return encoded;
const encoded = cptable.utils.encode(format, input);
return new Uint8Array(encoded).buffer;
}
}

View file

@ -6,9 +6,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import {ROTORS, LETTERS, ROTORS_FOURTH, REFLECTORS, Rotor, Reflector, Plugboard, EnigmaMachine} from "../lib/Enigma";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import {ROTORS, LETTERS, ROTORS_FOURTH, REFLECTORS, Rotor, Reflector, Plugboard, EnigmaMachine} from "../lib/Enigma.mjs";
/**
* Enigma operation

View file

@ -4,8 +4,13 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import * as d3temp from "d3";
import * as nodomtemp from "nodom";
import Operation from "../Operation.mjs";
const d3 = d3temp.default ? d3temp.default : d3temp;
const nodom = nodomtemp.default ? nodomtemp.default: nodomtemp;
/**
* Entropy operation
@ -19,30 +24,45 @@ class Entropy extends Operation {
super();
this.name = "Entropy";
this.module = "Default";
this.module = "Charts";
this.description = "Shannon Entropy, in the context of information theory, is a measure of the rate at which information is produced by a source of data. It can be used, in a broad sense, to detect whether data is likely to be structured or unstructured. 8 is the maximum, representing highly unstructured, 'random' data. English language text usually falls somewhere between 3.5 and 5. Properly encrypted or compressed data should have an entropy of over 7.5.";
this.infoURL = "https://wikipedia.org/wiki/Entropy_(information_theory)";
this.inputType = "byteArray";
this.outputType = "number";
this.inputType = "ArrayBuffer";
this.outputType = "json";
this.presentType = "html";
this.args = [];
this.args = [
{
"name": "Visualisation",
"type": "option",
"value": ["Shannon scale", "Histogram (Bar)", "Histogram (Line)", "Curve", "Image"]
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* Calculates the frequency of bytes in the input.
*
* @param {Uint8Array} input
* @returns {number}
*/
run(input, args) {
calculateShannonEntropy(input) {
const prob = [],
uniques = input.unique(),
str = Utils.byteArrayToChars(input);
let i;
occurrences = new Array(256).fill(0);
for (i = 0; i < uniques.length; i++) {
prob.push(str.count(Utils.chr(uniques[i])) / input.length);
// Count occurrences of each byte in the input
let i;
for (i = 0; i < input.length; i++) {
occurrences[input[i]]++;
}
// Store probability list
for (i = 0; i < occurrences.length; i++) {
if (occurrences[i] > 0) {
prob.push(occurrences[i] / input.length);
}
}
// Calculate Shannon entropy
let entropy = 0,
p;
@ -54,44 +74,357 @@ class Entropy extends Operation {
return -entropy;
}
/**
* Calculates the scanning entropy of the input
*
* @param {Uint8Array} inputBytes
* @returns {Object}
*/
calculateScanningEntropy(inputBytes) {
const entropyData = [];
const binWidth = inputBytes.length < 256 ? 8 : 256;
for (let bytePos = 0; bytePos < inputBytes.length; bytePos += binWidth) {
const block = inputBytes.slice(bytePos, bytePos+binWidth);
entropyData.push(this.calculateShannonEntropy(block));
}
return { entropyData, binWidth };
}
/**
* Calculates the frequency of bytes in the input.
*
* @param {object} svg
* @param {function} xScale
* @param {function} yScale
* @param {integer} svgHeight
* @param {integer} svgWidth
* @param {object} margins
* @param {string} xTitle
* @param {string} yTitle
*/
createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, title, xTitle, yTitle) {
// Axes
const yAxis = d3.axisLeft()
.scale(yScale);
const xAxis = d3.axisBottom()
.scale(xScale);
svg.append("g")
.attr("transform", `translate(0, ${svgHeight - margins.bottom})`)
.call(xAxis);
svg.append("g")
.attr("transform", `translate(${margins.left},0)`)
.call(yAxis);
// Axes labels
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margins.left)
.attr("x", 0 - (svgHeight / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text(yTitle);
svg.append("text")
.attr("transform", `translate(${svgWidth / 2}, ${svgHeight - margins.bottom + 40})`)
.style("text-anchor", "middle")
.text(xTitle);
// Add title
svg.append("text")
.attr("transform", `translate(${svgWidth / 2}, ${margins.top - 10})`)
.style("text-anchor", "middle")
.text(title);
}
/**
* Calculates the frequency of bytes in the input.
*
* @param {Uint8Array} inputBytes
* @returns {number[]}
*/
calculateByteFrequency(inputBytes) {
const freq = new Array(256).fill(0);
if (inputBytes.length === 0) return freq;
// Count occurrences of each byte in the input
let i;
for (i = 0; i < inputBytes.length; i++) {
freq[inputBytes[i]]++;
}
for (i = 0; i < freq.length; i++) {
freq[i] = freq[i] / inputBytes.length;
}
return freq;
}
/**
* Calculates the frequency of bytes in the input.
*
* @param {number[]} byteFrequency
* @returns {HTML}
*/
createByteFrequencyLineHistogram(byteFrequency) {
const margins = { top: 30, right: 20, bottom: 50, left: 30 };
const svgWidth = 500,
svgHeight = 500;
const document = new nodom.Document();
let svg = document.createElement("svg");
svg = d3.select(svg)
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
const yScale = d3.scaleLinear()
.domain([0, d3.max(byteFrequency, d => d)])
.range([svgHeight - margins.bottom, margins.top]);
const xScale = d3.scaleLinear()
.domain([0, byteFrequency.length - 1])
.range([margins.left, svgWidth - margins.right]);
const line = d3.line()
.x((_, i) => xScale(i))
.y(d => yScale(d))
.curve(d3.curveMonotoneX);
svg.append("path")
.datum(byteFrequency)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("d", line);
this.createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, "", "Byte", "Byte Frequency");
return svg._groups[0][0].outerHTML;
}
/**
* Creates a byte frequency histogram
*
* @param {number[]} byteFrequency
* @returns {HTML}
*/
createByteFrequencyBarHistogram(byteFrequency) {
const margins = { top: 30, right: 20, bottom: 50, left: 30 };
const svgWidth = 500,
svgHeight = 500,
binWidth = 1;
const document = new nodom.Document();
let svg = document.createElement("svg");
svg = d3.select(svg)
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
const yExtent = d3.extent(byteFrequency, d => d);
const yScale = d3.scaleLinear()
.domain(yExtent)
.range([svgHeight - margins.bottom, margins.top]);
const xScale = d3.scaleLinear()
.domain([0, byteFrequency.length - 1])
.range([margins.left - binWidth, svgWidth - margins.right]);
svg.selectAll("rect")
.data(byteFrequency)
.enter().append("rect")
.attr("x", (_, i) => xScale(i) + binWidth)
.attr("y", dataPoint => yScale(dataPoint))
.attr("width", binWidth)
.attr("height", dataPoint => yScale(yExtent[0]) - yScale(dataPoint))
.attr("fill", "blue");
this.createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, "", "Byte", "Byte Frequency");
return svg._groups[0][0].outerHTML;
}
/**
* Creates a byte frequency histogram
*
* @param {number[]} entropyData
* @returns {HTML}
*/
createEntropyCurve(entropyData) {
const margins = { top: 30, right: 20, bottom: 50, left: 30 };
const svgWidth = 500,
svgHeight = 500;
const document = new nodom.Document();
let svg = document.createElement("svg");
svg = d3.select(svg)
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
const yScale = d3.scaleLinear()
.domain([0, d3.max(entropyData, d => d)])
.range([svgHeight - margins.bottom, margins.top]);
const xScale = d3.scaleLinear()
.domain([0, entropyData.length])
.range([margins.left, svgWidth - margins.right]);
const line = d3.line()
.x((_, i) => xScale(i))
.y(d => yScale(d))
.curve(d3.curveMonotoneX);
if (entropyData.length > 0) {
svg.append("path")
.datum(entropyData)
.attr("d", line);
svg.selectAll("path").attr("fill", "none").attr("stroke", "steelblue");
}
this.createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, "Scanning Entropy", "Block", "Entropy");
return svg._groups[0][0].outerHTML;
}
/**
* Creates an image representation of the entropy
*
* @param {number[]} entropyData
* @returns {HTML}
*/
createEntropyImage(entropyData) {
const svgHeight = 100,
svgWidth = 100,
cellSize = 1,
nodes = [];
for (let i = 0; i < entropyData.length; i++) {
nodes.push({
x: i % svgWidth,
y: Math.floor(i / svgWidth),
entropy: entropyData[i]
});
}
const document = new nodom.Document();
let svg = document.createElement("svg");
svg = d3.select(svg)
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
const greyScale = d3.scaleLinear()
.domain([0, d3.max(entropyData, d => d)])
.range(["#000000", "#FFFFFF"])
.interpolate(d3.interpolateRgb);
svg
.selectAll("rect")
.data(nodes)
.enter().append("rect")
.attr("x", d => d.x * cellSize)
.attr("y", d => d.y * cellSize)
.attr("width", cellSize)
.attr("height", cellSize)
.style("fill", d => greyScale(d.entropy));
return svg._groups[0][0].outerHTML;
}
/**
* Displays the entropy as a scale bar for web apps.
*
* @param {number} entropy
* @returns {html}
* @returns {HTML}
*/
present(entropy) {
createShannonEntropyVisualization(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.
<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.
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;
<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;
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>`;
CanvasComponents.drawScaleBar(canvas, entropy, 8, [
{
label: "English text",
min: 3.5,
max: 5
},{
label: "Encrypted/compressed",
min: 7.5,
max: 8
}
]);
</script>`;
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {json}
*/
run(input, args) {
const visualizationType = args[0];
input = new Uint8Array(input);
switch (visualizationType) {
case "Histogram (Bar)":
case "Histogram (Line)":
return this.calculateByteFrequency(input);
case "Curve":
case "Image":
return this.calculateScanningEntropy(input).entropyData;
case "Shannon scale":
default:
return this.calculateShannonEntropy(input);
}
}
/**
* Displays the entropy in a visualisation for web apps.
*
* @param {json} entropyData
* @param {Object[]} args
* @returns {html}
*/
present(entropyData, args) {
const visualizationType = args[0];
switch (visualizationType) {
case "Histogram (Bar)":
return this.createByteFrequencyBarHistogram(entropyData);
case "Histogram (Line)":
return this.createByteFrequencyLineHistogram(entropyData);
case "Curve":
return this.createEntropyCurve(entropyData);
case "Image":
return this.createEntropyImage(entropyData);
case "Shannon scale":
default:
return this.createShannonEntropyVisualization(entropyData);
}
}
}
export default Entropy;

View file

@ -5,7 +5,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
import jsesc from "jsesc";
/**

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Escape Unicode Characters operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
/**
* Expand alphabet range operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search } from "../lib/Extract";
import Operation from "../Operation.mjs";
import { search } from "../lib/Extract.mjs";
/**
* Extract dates operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search, DOMAIN_REGEX } from "../lib/Extract";
import Operation from "../Operation.mjs";
import { search, DOMAIN_REGEX } from "../lib/Extract.mjs";
/**
* Extract domains operation

View file

@ -5,8 +5,8 @@
*/
import ExifParser from "exif-parser";
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Extract EXIF operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search } from "../lib/Extract";
import Operation from "../Operation.mjs";
import { search } from "../lib/Extract.mjs";
/**
* Extract email addresses operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search } from "../lib/Extract";
import Operation from "../Operation.mjs";
import { search } from "../lib/Extract.mjs";
/**
* Extract file paths operation

View file

@ -4,11 +4,11 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import Utils from "../Utils";
import {scanForFileTypes, extractFile} from "../lib/FileType";
import {FILE_SIGNATURES} from "../lib/FileSignatures";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
import {scanForFileTypes, extractFile} from "../lib/FileType.mjs";
import {FILE_SIGNATURES} from "../lib/FileSignatures.mjs";
/**
* Extract Files operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search } from "../lib/Extract";
import Operation from "../Operation.mjs";
import { search } from "../lib/Extract.mjs";
/**
* Extract IP addresses operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search } from "../lib/Extract";
import Operation from "../Operation.mjs";
import { search } from "../lib/Extract.mjs";
/**
* Extract MAC addresses operation

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import { search, URL_REGEX } from "../lib/Extract";
import Operation from "../Operation.mjs";
import { search, URL_REGEX } from "../lib/Extract.mjs";
/**
* Extract URLs operation

View file

@ -4,10 +4,10 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import {INPUT_DELIM_OPTIONS} from "../lib/Delim";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import {INPUT_DELIM_OPTIONS} from "../lib/Delim.mjs";
import OperationError from "../errors/OperationError.mjs";
import XRegExp from "xregexp";
/**

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import XRegExp from "xregexp";
/**

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
/**
* Fletcher-16 Checksum operation
@ -22,19 +22,20 @@ class Fletcher16Checksum extends Operation {
this.module = "Crypto";
this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.<br><br>The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques.";
this.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum#Fletcher-16";
this.inputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let a = 0,
b = 0;
input = new Uint8Array(input);
for (let i = 0; i < input.length; i++) {
a = (a + input[i]) % 0xff;

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
/**
* Fletcher-32 Checksum operation
@ -22,19 +22,20 @@ class Fletcher32Checksum extends Operation {
this.module = "Crypto";
this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.<br><br>The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques.";
this.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum#Fletcher-32";
this.inputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let a = 0,
b = 0;
input = new Uint8Array(input);
for (let i = 0; i < input.length; i++) {
a = (a + input[i]) % 0xffff;

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
/**
* Fletcher-64 Checksum operation
@ -22,19 +22,20 @@ class Fletcher64Checksum extends Operation {
this.module = "Crypto";
this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.<br><br>The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques.";
this.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum#Fletcher-64";
this.inputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let a = 0,
b = 0;
input = new Uint8Array(input);
for (let i = 0; i < input.length; i++) {
a = (a + input[i]) % 0xffffffff;

View file

@ -4,8 +4,8 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
/**
* Fletcher-8 Checksum operation
@ -22,19 +22,20 @@ class Fletcher8Checksum extends Operation {
this.module = "Crypto";
this.description = "The Fletcher checksum is an algorithm for computing a position-dependent checksum devised by John Gould Fletcher at Lawrence Livermore Labs in the late 1970s.<br><br>The objective of the Fletcher checksum was to provide error-detection properties approaching those of a cyclic redundancy check but with the lower computational effort associated with summation techniques.";
this.infoURL = "https://wikipedia.org/wiki/Fletcher%27s_checksum";
this.inputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let a = 0,
b = 0;
input = new Uint8Array(input);
for (let i = 0; i < input.length; i++) {
a = (a + input[i]) % 0xf;

View file

@ -4,10 +4,11 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import { isImage } from "../lib/FileType";
import { toBase64 } from "../lib/Base64";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import jimp from "jimp";
/**
@ -25,8 +26,8 @@ class FlipImage extends Operation {
this.module = "Image";
this.description = "Flips an image along its X or Y axis.";
this.infoURL = "";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.inputType = "ArrayBuffer";
this.outputType = "ArrayBuffer";
this.presentType = "html";
this.args = [
{
@ -38,24 +39,24 @@ class FlipImage extends Operation {
}
/**
* @param {byteArray} input
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {byteArray}
*/
async run(input, args) {
const [flipAxis] = args;
if (!isImage(input)) {
if (!isImage(new Uint8Array(input))) {
throw new OperationError("Invalid input file type.");
}
let image;
try {
image = await jimp.read(Buffer.from(input));
image = await jimp.read(input);
} catch (err) {
throw new OperationError(`Error loading image. (${err})`);
}
try {
if (ENVIRONMENT_IS_WORKER())
if (isWorkerEnvironment())
self.sendStatusMessage("Flipping image...");
switch (flipAxis){
case "Horizontal":
@ -66,8 +67,13 @@ class FlipImage extends Operation {
break;
}
const imageBuffer = await image.getBufferAsync(jimp.AUTO);
return [...imageBuffer];
let imageBuffer;
if (image.getMIME() === "image/gif") {
imageBuffer = await image.getBufferAsync(jimp.MIME_PNG);
} else {
imageBuffer = await image.getBufferAsync(jimp.AUTO);
}
return imageBuffer.buffer;
} catch (err) {
throw new OperationError(`Error flipping image. (${err})`);
}
@ -75,18 +81,19 @@ class FlipImage extends Operation {
/**
* Displays the flipped image using HTML for web apps
* @param {byteArray} data
* @param {ArrayBuffer} data
* @returns {html}
*/
present(data) {
if (!data.length) return "";
if (!data.byteLength) return "";
const dataArray = new Uint8Array(data);
const type = isImage(data);
const type = isImage(dataArray);
if (!type) {
throw new OperationError("Invalid file type.");
}
return `<img src="data:${type};base64,${toBase64(data)}">`;
return `<img src="data:${type};base64,${toBase64(dataArray)}">`;
}
}

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Recipe from "../Recipe";
import Dish from "../Dish";
import Operation from "../Operation.mjs";
import Recipe from "../Recipe.mjs";
import Dish from "../Dish.mjs";
/**
* Fork operation

View file

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Operation from "../Operation.mjs";
/**
* Format MAC addresses operation

View file

@ -4,9 +4,9 @@
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Frequency distribution operation

View file

@ -4,10 +4,10 @@
* @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 Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
import {ENCODING_SCHEME, ENCODING_LOOKUP, FORMAT} from "../lib/BCD.mjs";
import BigNumber from "bignumber.js";
/**

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