Merge branch 'v9' of github.com:gchq/CyberChef into node-lib

This commit is contained in:
d98762625 2019-07-05 11:07:31 +01:00
commit 368f508b17
122 changed files with 24317 additions and 4174 deletions

View file

@ -170,7 +170,7 @@ export const FILE_SIGNATURES = {
mime: "image/vnd.adobe.photoshop",
description: "",
signature: {
0: 0x38,
0: 0x38, // 8BPS
1: 0x42,
2: 0x50,
3: 0x53,
@ -185,6 +185,28 @@ export const FILE_SIGNATURES = {
},
extractor: null
},
{
name: "Photoshop Large Document",
extension: "psb",
mime: "application/x-photoshop",
description: "",
signature: {
0: 0x38, // 8BPS
1: 0x42,
2: 0x50,
3: 0x53,
4: 0x0,
5: 0x2,
6: 0x0,
7: 0x0,
8: 0x0,
9: 0x0,
10: 0x0,
11: 0x0,
12: 0x0
},
extractor: null
},
{
name: "Paint Shop Pro image",
extension: "psp",
@ -233,10 +255,114 @@ export const FILE_SIGNATURES = {
5: 0x0,
6: [0x10, 0x20, 0x30, 0x40, 0x80],
7: [0x10, 0x20, 0x30, 0x40, 0x80],
9: 0x00,
9: 0x0,
10: [0x0, 0x1]
},
extractor: null
},
{
name: "Radiance High Dynamic Range image",
extension: "hdr",
mime: "image/vnd.radiance",
description: "",
signature: {
0: 0x23, // #?RADIANCE
1: 0x3f,
2: 0x52,
3: 0x41,
4: 0x44,
5: 0x49,
6: 0x41,
7: 0x4e,
8: 0x43,
9: 0x45,
10: 0x0a
},
extractor: null
},
{
name: "Sony ARW image",
extension: "arw",
mime: "image/x-raw",
description: "",
signature: {
0: 0x05,
1: 0x0,
2: 0x0,
3: 0x0,
4: 0x41,
5: 0x57,
6: 0x31,
7: 0x2e
},
extractor: null
},
{
name: "Fujifilm Raw Image",
extension: "raf",
mime: "image/x-raw",
description: "",
signature: {
0: 0x46, // FUJIFILMCCD-RAW
1: 0x55,
2: 0x4a,
3: 0x49,
4: 0x46,
5: 0x49,
6: 0x4c,
7: 0x4d,
8: 0x43,
9: 0x43,
10: 0x44,
11: 0x2d,
12: 0x52,
13: 0x41,
14: 0x57
},
extractor: null
},
{
name: "Minolta RAW image",
extension: "mrw",
mime: "image/x-raw",
description: "",
signature: {
0: 0x0,
1: 0x4d, // MRM
2: 0x52,
3: 0x4d
},
extractor: null
},
{
name: "Adobe Bridge Thumbnail Cache",
extension: "bct",
mime: "application/octet-stream",
description: "",
signature: {
0: 0x6c,
1: 0x6e,
2: 0x62,
3: 0x74,
4: 0x02,
5: 0x0,
6: 0x0,
7: 0x0
},
extractor: null
},
{
name: "Microsoft Document Imaging",
extension: "mdi",
mime: "image/vnd.ms-modi",
description: "",
signature: {
0: 0x45,
1: 0x50,
2: 0x2a,
3: 0x00
},
extractor: null
}
],
"Video": [
@ -534,6 +660,97 @@ export const FILE_SIGNATURES = {
},
extractor: null
},
{
name: "Audacity",
extension: "au",
mime: "audio/x-au",
description: "",
signature: {
0: 0x64, // dns.
1: 0x6e,
2: 0x73,
3: 0x2e,
24: 0x41, // AudacityBlockFile
25: 0x75,
26: 0x64,
27: 0x61,
28: 0x63,
29: 0x69,
30: 0x74,
31: 0x79,
32: 0x42,
33: 0x6c,
34: 0x6f,
35: 0x63,
36: 0x6b,
37: 0x46,
38: 0x69,
39: 0x6c,
40: 0x65
},
extractor: null
},
{
name: "Audacity Block",
extension: "auf",
mime: "application/octet-stream",
description: "",
signature: {
0: 0x41, // AudacityBlockFile
1: 0x75,
2: 0x64,
3: 0x61,
4: 0x63,
5: 0x69,
6: 0x74,
7: 0x79,
8: 0x42,
9: 0x6c,
10: 0x6f,
11: 0x63,
12: 0x6b,
13: 0x46,
14: 0x69,
15: 0x6c,
16: 0x65
},
extractor: null
},
{
name: "Audio Interchange File",
extension: "aif",
mime: "audio/x-aiff",
description: "",
signature: {
0: 0x46, // FORM
1: 0x4f,
2: 0x52,
3: 0x4d,
8: 0x41, // AIFF
9: 0x49,
10: 0x46,
11: 0x46
},
extractor: null
},
{
name: "Audio Interchange File (compressed)",
extension: "aifc",
mime: "audio/x-aifc",
description: "",
signature: {
0: 0x46, // FORM
1: 0x4f,
2: 0x52,
3: 0x4d,
8: 0x41, // AIFC
9: 0x49,
10: 0x46,
11: 0x43
},
extractor: null
}
],
"Documents": [
{
@ -898,6 +1115,110 @@ export const FILE_SIGNATURES = {
},
extractor: null
},
{
name: "ARJ Archive",
extension: "arj",
mime: "application/x-arj-compressed",
description: "",
signature: {
0: 0x60,
1: 0xea,
8: [0x0, 0x10, 0x14],
9: 0x0,
10: 0x2
},
extractor: null
},
{
name: "WinAce Archive",
extension: "ace",
mime: "application/x-ace-compressed",
description: "",
signature: {
7: 0x2a, // **ACE**
8: 0x2a,
9: 0x41,
10: 0x43,
11: 0x45,
12: 0x2a,
13: 0x2a
},
extractor: null
},
{
name: "Macintosh BinHex Encoded File",
extension: "hqx",
mime: "application/mac-binhex",
description: "",
signature: {
11: 0x6d, // must be converted with BinHex
12: 0x75,
13: 0x73,
14: 0x74,
15: 0x20,
16: 0x62,
17: 0x65,
18: 0x20,
19: 0x63,
20: 0x6f,
21: 0x6e,
22: 0x76,
23: 0x65,
24: 0x72,
25: 0x74,
26: 0x65,
27: 0x64,
28: 0x20,
29: 0x77,
30: 0x69,
31: 0x74,
32: 0x68,
33: 0x20,
34: 0x42,
35: 0x69,
36: 0x6e,
37: 0x48,
38: 0x65,
39: 0x78
},
extractor: null
},
{
name: "ALZip Archive",
extension: "alz",
mime: "application/octet-stream",
description: "",
signature: {
0: 0x41, // ALZ
1: 0x4c,
2: 0x5a,
3: 0x01,
4: 0x0a,
5: 0x0,
6: 0x0,
7: 0x0
},
extractor: null
},
{
name: "KGB Compressed Archive",
extension: "kgb",
mime: "application/x-kgb-compressed",
description: "",
signature: {
0: 0x4b, // KGB_arch -
1: 0x47,
2: 0x42,
3: 0x5f,
4: 0x61,
5: 0x72,
6: 0x63,
7: 0x68,
8: 0x20,
9: 0x2d
},
extractor: null
}
],
"Miscellaneous": [
{
@ -1041,6 +1362,43 @@ export const FILE_SIGNATURES = {
},
extractor: null
},
{
name: "BitTorrent link",
extension: "torrent",
mime: "application/x-bittorrent",
description: "",
signature: [
{
0: 0x64, // d8:announce##:
1: 0x38,
2: 0x3a,
3: 0x61,
4: 0x6e,
5: 0x6e,
6: 0x6f,
7: 0x75,
8: 0x6e,
9: 0x63,
10: 0x65,
11: 0x23,
12: 0x23,
13: 0x3a
},
{
0: 0x64, // d4:infod
1: 0x34,
2: 0x3a,
3: 0x69,
4: 0x6e,
5: 0x66,
6: 0x6f,
7: 0x64,
8: [0x34, 0x35, 0x36],
9: 0x3a
}
],
extractor: null
}
]
};

View file

@ -12,7 +12,7 @@ import Utils from "../Utils";
/**
* Convert a byte array into a hex string.
*
* @param {Uint8Array|byteArray} data
* @param {byteArray|Uint8Array|ArrayBuffer} data
* @param {string} [delim=" "]
* @param {number} [padding=2]
* @returns {string}
@ -26,6 +26,7 @@ import Utils from "../Utils";
*/
export function toHex(data, delim=" ", padding=2) {
if (!data) return "";
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
let output = "";
@ -47,7 +48,7 @@ export function toHex(data, delim=" ", padding=2) {
/**
* Convert a byte array into a hex string as efficiently as possible with no options.
*
* @param {byteArray} data
* @param {byteArray|Uint8Array|ArrayBuffer} data
* @returns {string}
*
* @example
@ -56,6 +57,7 @@ export function toHex(data, delim=" ", padding=2) {
*/
export function toHexFast(data) {
if (!data) return "";
if (data instanceof ArrayBuffer) data = new Uint8Array(data);
const output = [];

View file

@ -0,0 +1,251 @@
/**
* Image manipulation resources
*
* @author j433866 [j433866@gmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError";
/**
* Gaussian blurs an image.
*
* @param {jimp} input
* @param {number} radius
* @param {boolean} fast
* @returns {jimp}
*/
export function gaussianBlur (input, radius) {
try {
// From http://blog.ivank.net/fastest-gaussian-blur.html
const boxes = boxesForGauss(radius, 3);
for (let i = 0; i < 3; i++) {
input = boxBlur(input, (boxes[i] - 1) / 2);
}
} catch (err) {
throw new OperationError(`Error blurring image. (${err})`);
}
return input;
}
/**
*
* @param {number} radius
* @param {number} numBoxes
* @returns {Array}
*/
function boxesForGauss(radius, numBoxes) {
const idealWidth = Math.sqrt((12 * radius * radius / numBoxes) + 1);
let wl = Math.floor(idealWidth);
if (wl % 2 === 0) {
wl--;
}
const wu = wl + 2;
const mIdeal = (12 * radius * radius - numBoxes * wl * wl - 4 * numBoxes * wl - 3 * numBoxes) / (-4 * wl - 4);
const m = Math.round(mIdeal);
const sizes = [];
for (let i = 0; i < numBoxes; i++) {
sizes.push(i < m ? wl : wu);
}
return sizes;
}
/**
* Applies a box blur effect to the image
*
* @param {jimp} source
* @param {number} radius
* @returns {jimp}
*/
function boxBlur (source, radius) {
const width = source.bitmap.width;
const height = source.bitmap.height;
let output = source.clone();
output = boxBlurH(source, output, width, height, radius);
source = boxBlurV(output, source, width, height, radius);
return source;
}
/**
* Applies the horizontal blur
*
* @param {jimp} source
* @param {jimp} output
* @param {number} width
* @param {number} height
* @param {number} radius
* @returns {jimp}
*/
function boxBlurH (source, output, width, height, radius) {
const iarr = 1 / (radius + radius + 1);
for (let i = 0; i < height; i++) {
let ti = 0,
li = ti,
ri = ti + radius;
const idx = source.getPixelIndex(ti, i);
const firstValRed = source.bitmap.data[idx],
firstValGreen = source.bitmap.data[idx + 1],
firstValBlue = source.bitmap.data[idx + 2],
firstValAlpha = source.bitmap.data[idx + 3];
const lastIdx = source.getPixelIndex(width - 1, i),
lastValRed = source.bitmap.data[lastIdx],
lastValGreen = source.bitmap.data[lastIdx + 1],
lastValBlue = source.bitmap.data[lastIdx + 2],
lastValAlpha = source.bitmap.data[lastIdx + 3];
let red = (radius + 1) * firstValRed;
let green = (radius + 1) * firstValGreen;
let blue = (radius + 1) * firstValBlue;
let alpha = (radius + 1) * firstValAlpha;
for (let j = 0; j < radius; j++) {
const jIdx = source.getPixelIndex(ti + j, i);
red += source.bitmap.data[jIdx];
green += source.bitmap.data[jIdx + 1];
blue += source.bitmap.data[jIdx + 2];
alpha += source.bitmap.data[jIdx + 3];
}
for (let j = 0; j <= radius; j++) {
const jIdx = source.getPixelIndex(ri++, i);
red += source.bitmap.data[jIdx] - firstValRed;
green += source.bitmap.data[jIdx + 1] - firstValGreen;
blue += source.bitmap.data[jIdx + 2] - firstValBlue;
alpha += source.bitmap.data[jIdx + 3] - firstValAlpha;
const tiIdx = source.getPixelIndex(ti++, i);
output.bitmap.data[tiIdx] = Math.round(red * iarr);
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
}
for (let j = radius + 1; j < width - radius; j++) {
const riIdx = source.getPixelIndex(ri++, i);
const liIdx = source.getPixelIndex(li++, i);
red += source.bitmap.data[riIdx] - source.bitmap.data[liIdx];
green += source.bitmap.data[riIdx + 1] - source.bitmap.data[liIdx + 1];
blue += source.bitmap.data[riIdx + 2] - source.bitmap.data[liIdx + 2];
alpha += source.bitmap.data[riIdx + 3] - source.bitmap.data[liIdx + 3];
const tiIdx = source.getPixelIndex(ti++, i);
output.bitmap.data[tiIdx] = Math.round(red * iarr);
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
}
for (let j = width - radius; j < width; j++) {
const liIdx = source.getPixelIndex(li++, i);
red += lastValRed - source.bitmap.data[liIdx];
green += lastValGreen - source.bitmap.data[liIdx + 1];
blue += lastValBlue - source.bitmap.data[liIdx + 2];
alpha += lastValAlpha - source.bitmap.data[liIdx + 3];
const tiIdx = source.getPixelIndex(ti++, i);
output.bitmap.data[tiIdx] = Math.round(red * iarr);
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
}
}
return output;
}
/**
* Applies the vertical blur
*
* @param {jimp} source
* @param {jimp} output
* @param {number} width
* @param {number} height
* @param {number} radius
* @returns {jimp}
*/
function boxBlurV (source, output, width, height, radius) {
const iarr = 1 / (radius + radius + 1);
for (let i = 0; i < width; i++) {
let ti = 0,
li = ti,
ri = ti + radius;
const idx = source.getPixelIndex(i, ti);
const firstValRed = source.bitmap.data[idx],
firstValGreen = source.bitmap.data[idx + 1],
firstValBlue = source.bitmap.data[idx + 2],
firstValAlpha = source.bitmap.data[idx + 3];
const lastIdx = source.getPixelIndex(i, height - 1),
lastValRed = source.bitmap.data[lastIdx],
lastValGreen = source.bitmap.data[lastIdx + 1],
lastValBlue = source.bitmap.data[lastIdx + 2],
lastValAlpha = source.bitmap.data[lastIdx + 3];
let red = (radius + 1) * firstValRed;
let green = (radius + 1) * firstValGreen;
let blue = (radius + 1) * firstValBlue;
let alpha = (radius + 1) * firstValAlpha;
for (let j = 0; j < radius; j++) {
const jIdx = source.getPixelIndex(i, ti + j);
red += source.bitmap.data[jIdx];
green += source.bitmap.data[jIdx + 1];
blue += source.bitmap.data[jIdx + 2];
alpha += source.bitmap.data[jIdx + 3];
}
for (let j = 0; j <= radius; j++) {
const riIdx = source.getPixelIndex(i, ri++);
red += source.bitmap.data[riIdx] - firstValRed;
green += source.bitmap.data[riIdx + 1] - firstValGreen;
blue += source.bitmap.data[riIdx + 2] - firstValBlue;
alpha += source.bitmap.data[riIdx + 3] - firstValAlpha;
const tiIdx = source.getPixelIndex(i, ti++);
output.bitmap.data[tiIdx] = Math.round(red * iarr);
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
}
for (let j = radius + 1; j < height - radius; j++) {
const riIdx = source.getPixelIndex(i, ri++);
const liIdx = source.getPixelIndex(i, li++);
red += source.bitmap.data[riIdx] - source.bitmap.data[liIdx];
green += source.bitmap.data[riIdx + 1] - source.bitmap.data[liIdx + 1];
blue += source.bitmap.data[riIdx + 2] - source.bitmap.data[liIdx + 2];
alpha += source.bitmap.data[riIdx + 3] - source.bitmap.data[liIdx + 3];
const tiIdx = source.getPixelIndex(i, ti++);
output.bitmap.data[tiIdx] = Math.round(red * iarr);
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
}
for (let j = height - radius; j < height; j++) {
const liIdx = source.getPixelIndex(i, li++);
red += lastValRed - source.bitmap.data[liIdx];
green += lastValGreen - source.bitmap.data[liIdx + 1];
blue += lastValBlue - source.bitmap.data[liIdx + 2];
alpha += lastValAlpha - source.bitmap.data[liIdx + 3];
const tiIdx = source.getPixelIndex(i, ti++);
output.bitmap.data[tiIdx] = Math.round(red * iarr);
output.bitmap.data[tiIdx + 1] = Math.round(green * iarr);
output.bitmap.data[tiIdx + 2] = Math.round(blue * iarr);
output.bitmap.data[tiIdx + 3] = Math.round(alpha * iarr);
}
}
return output;
}

View file

@ -312,6 +312,11 @@ class Magic {
return;
}
// If the recipe returned an empty buffer, do not continue
if (_buffersEqual(output, new ArrayBuffer())) {
return;
}
const magic = new Magic(output, this.opPatterns),
speculativeResults = await magic.speculativeExecution(
depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful, crib);
@ -395,7 +400,12 @@ class Magic {
const recipe = new Recipe(recipeConfig);
try {
await recipe.execute(dish);
return dish.get(Dish.ARRAY_BUFFER);
// Return an empty buffer if the recipe did not run to completion
if (recipe.lastRunOp === recipe.opList[recipe.opList.length - 1]) {
return dish.get(Dish.ARRAY_BUFFER);
} else {
return new ArrayBuffer();
}
} catch (err) {
// If there are errors, return an empty buffer
return new ArrayBuffer();

93
src/core/lib/QRCode.mjs Normal file
View file

@ -0,0 +1,93 @@
/**
* QR code resources
*
* @author j433866 [j433866@gmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError";
import jsQR from "jsqr";
import qr from "qr-image";
import jimp from "jimp";
import Utils from "../Utils";
/**
* Parses a QR code image from an image
*
* @param {ArrayBuffer} input
* @param {boolean} normalise
* @returns {string}
*/
export async function parseQrCode(input, normalise) {
let image;
try {
image = await jimp.read(input);
} catch (err) {
throw new OperationError(`Error opening image. (${err})`);
}
try {
if (normalise) {
image.rgba(false);
image.background(0xFFFFFFFF);
image.normalize();
image.greyscale();
image = await image.getBufferAsync(jimp.MIME_JPEG);
image = await jimp.read(image);
}
} catch (err) {
throw new OperationError(`Error normalising iamge. (${err})`);
}
const qrData = jsQR(image.bitmap.data, image.getWidth(), image.getHeight());
if (qrData) {
return qrData.data;
} else {
throw new OperationError("Could not read a QR code from the image.");
}
}
/**
* Generates a QR code from the input string
*
* @param {string} input
* @param {string} format
* @param {number} moduleSize
* @param {number} margin
* @param {string} errorCorrection
* @returns {ArrayBuffer}
*/
export function generateQrCode(input, format, moduleSize, margin, errorCorrection) {
const formats = ["SVG", "EPS", "PDF", "PNG"];
if (!formats.includes(format.toUpperCase())) {
throw new OperationError("Unsupported QR code format.");
}
let qrImage;
try {
qrImage = qr.imageSync(input, {
type: format,
size: moduleSize,
margin: margin,
"ec_level": errorCorrection.charAt(0).toUpperCase()
});
} catch (err) {
throw new OperationError(`Error generating QR code. (${err})`);
}
if (!qrImage) {
throw new OperationError("Error generating QR code.");
}
switch (format) {
case "SVG":
case "EPS":
case "PDF":
return Utils.strToArrayBuffer(qrImage);
case "PNG":
return qrImage.buffer;
default:
throw new OperationError("Unsupported QR code format.");
}
}