mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-21 15:26:16 -04:00
Tidied up Steganography operations. FileType and toBase64 functions now accept ArrayBuffers.
This commit is contained in:
parent
5bc5c0df90
commit
eb769c7fb4
28 changed files with 81 additions and 75 deletions
|
@ -177,7 +177,7 @@ class Dish {
|
|||
this.type = type;
|
||||
|
||||
if (!this.valid()) {
|
||||
const sample = Utils.truncate(JSON.stringify(this.value), 13);
|
||||
const sample = Utils.truncate(JSON.stringify(this.value), 25);
|
||||
throw new DishError(`Data is not a valid ${Dish.enumLookup(type)}: ${sample}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -369,7 +369,11 @@
|
|||
"Scan for Embedded Files",
|
||||
"Extract Files",
|
||||
"Remove EXIF",
|
||||
"Extract EXIF"
|
||||
"Extract EXIF",
|
||||
"Extract RGBA",
|
||||
"View Bit Plane",
|
||||
"Randomize Colour Palette",
|
||||
"Extract LSB"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -380,10 +384,6 @@
|
|||
"Remove EXIF",
|
||||
"Extract EXIF",
|
||||
"Split Colour Channels",
|
||||
"Extract RGBA",
|
||||
"View Bit Plane",
|
||||
"Randomize Colour Palette",
|
||||
"Extract LSB",
|
||||
"Rotate Image",
|
||||
"Resize Image",
|
||||
"Blur Image",
|
||||
|
|
|
@ -12,7 +12,7 @@ import Utils from "../Utils.mjs";
|
|||
/**
|
||||
* Base64's the input byte array using the given alphabet, returning a string.
|
||||
*
|
||||
* @param {byteArray|Uint8Array|string} data
|
||||
* @param {byteArray|Uint8Array|ArrayBuffer|string} data
|
||||
* @param {string} [alphabet="A-Za-z0-9+/="]
|
||||
* @returns {string}
|
||||
*
|
||||
|
@ -25,6 +25,9 @@ import Utils from "../Utils.mjs";
|
|||
*/
|
||||
export function toBase64(data, alphabet="A-Za-z0-9+/=") {
|
||||
if (!data) return "";
|
||||
if (data instanceof ArrayBuffer) {
|
||||
data = new Uint8Array(data);
|
||||
}
|
||||
if (typeof data == "string") {
|
||||
data = Utils.strToByteArray(data);
|
||||
}
|
||||
|
|
|
@ -72,9 +72,9 @@ export const JOIN_DELIM_OPTIONS = [
|
|||
{name: "Nothing (join chars)", value: ""}
|
||||
];
|
||||
|
||||
/*
|
||||
RGBA list delimiters.
|
||||
*/
|
||||
/**
|
||||
* RGBA list delimiters.
|
||||
*/
|
||||
export const RGBA_DELIM_OPTIONS = [
|
||||
{name: "Comma", value: ","},
|
||||
{name: "Space", value: " "},
|
||||
|
|
|
@ -75,7 +75,7 @@ function bytesMatch(sig, buf, offset=0) {
|
|||
* Given a buffer, detects magic byte sequences at specific positions and returns the
|
||||
* extension and mime type.
|
||||
*
|
||||
* @param {Uint8Array} buf
|
||||
* @param {Uint8Array|ArrayBuffer} buf
|
||||
* @param {string[]} [categories=All] - Which categories of file to look for
|
||||
* @returns {Object[]} types
|
||||
* @returns {string} type.name - Name of file type
|
||||
|
@ -84,6 +84,10 @@ function bytesMatch(sig, buf, offset=0) {
|
|||
* @returns {string} [type.desc] - Description
|
||||
*/
|
||||
export function detectFileType(buf, categories=Object.keys(FILE_SIGNATURES)) {
|
||||
if (buf instanceof ArrayBuffer) {
|
||||
buf = new Uint8Array(buf);
|
||||
}
|
||||
|
||||
if (!(buf && buf.length > 1)) {
|
||||
return [];
|
||||
}
|
||||
|
@ -203,7 +207,7 @@ function locatePotentialSig(buf, sig, offset) {
|
|||
* Detects whether the given buffer is a file of the type specified.
|
||||
*
|
||||
* @param {string|RegExp} type
|
||||
* @param {Uint8Array} buf
|
||||
* @param {Uint8Array|ArrayBuffer} buf
|
||||
* @returns {string|false} The mime type or false if the type does not match
|
||||
*/
|
||||
export function isType(type, buf) {
|
||||
|
@ -230,7 +234,7 @@ export function isType(type, buf) {
|
|||
/**
|
||||
* Detects whether the given buffer contains an image file.
|
||||
*
|
||||
* @param {Uint8Array} buf
|
||||
* @param {Uint8Array|ArrayBuffer} buf
|
||||
* @returns {string|false} The mime type or false if the type does not match
|
||||
*/
|
||||
export function isImage(buf) {
|
||||
|
|
|
@ -121,7 +121,7 @@ class AddTextToImage extends Operation {
|
|||
let xPos = args[3],
|
||||
yPos = args[4];
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ class BlurImage extends Operation {
|
|||
async run(input, args) {
|
||||
const [blurAmount, blurType] = args;
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ class ContainImage extends Operation {
|
|||
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
|
||||
};
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ class ConvertImageFormat extends Operation {
|
|||
|
||||
const mime = formatMap[format];
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file format.");
|
||||
}
|
||||
let image;
|
||||
|
|
|
@ -102,7 +102,7 @@ class CoverImage extends Operation {
|
|||
"Bottom": jimp.VERTICAL_ALIGN_BOTTOM
|
||||
};
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ class CropImage extends Operation {
|
|||
*/
|
||||
async run(input, args) {
|
||||
const [xPos, yPos, width, height, autocrop, autoTolerance, autoFrames, autoSymmetric, autoBorder] = args;
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ class DitherImage extends Operation {
|
|||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils";
|
||||
import { isImage } from "../lib/FileType";
|
||||
import Utils from "../Utils.mjs";
|
||||
import { fromBinary } from "../lib/Binary.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import jimp from "jimp";
|
||||
|
||||
/**
|
||||
|
@ -24,8 +25,8 @@ class ExtractLSB extends Operation {
|
|||
this.name = "Extract LSB";
|
||||
this.module = "Image";
|
||||
this.description = "Extracts the Least Significant Bit data from each pixel in an image. This is a common way to hide data in Steganography.";
|
||||
this.infoURL = "https://en.wikipedia.org/wiki/Bit_numbering#Least_significant_bit_in_digital_steganography";
|
||||
this.inputType = "byteArray";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bit_numbering#Least_significant_bit_in_digital_steganography";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [
|
||||
{
|
||||
|
@ -62,9 +63,9 @@ class ExtractLSB extends Operation {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {File} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {File}
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(input)) throw new OperationError("Please enter a valid image file.");
|
||||
|
@ -72,7 +73,7 @@ class ExtractLSB extends Operation {
|
|||
const bit = 7 - args.pop(),
|
||||
pixelOrder = args.pop(),
|
||||
colours = args.filter(option => option !== "").map(option => COLOUR_OPTIONS.indexOf(option)),
|
||||
parsedImage = await jimp.read(Buffer.from(input)),
|
||||
parsedImage = await jimp.read(input),
|
||||
width = parsedImage.bitmap.width,
|
||||
height = parsedImage.bitmap.height,
|
||||
rgba = parsedImage.bitmap.data;
|
||||
|
@ -103,8 +104,7 @@ class ExtractLSB extends Operation {
|
|||
}
|
||||
}
|
||||
|
||||
return Utils.convertToByteArray(combinedBinary, "binary");
|
||||
|
||||
return fromBinary(combinedBinary);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import jimp from "jimp";
|
||||
|
||||
import {RGBA_DELIM_OPTIONS} from "../lib/Delim.mjs";
|
||||
|
@ -25,8 +25,8 @@ class ExtractRGBA extends Operation {
|
|||
this.name = "Extract RGBA";
|
||||
this.module = "Image";
|
||||
this.description = "Extracts each pixel's RGBA value in an image. These are sometimes used in Steganography to hide text or data.";
|
||||
this.infoURL = "https://en.wikipedia.org/wiki/RGBA_color_space";
|
||||
this.inputType = "byteArray";
|
||||
this.infoURL = "https://wikipedia.org/wiki/RGBA_color_space";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
|
@ -43,7 +43,7 @@ class ExtractRGBA extends Operation {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
|
@ -52,7 +52,7 @@ class ExtractRGBA extends Operation {
|
|||
|
||||
const delimiter = args[0],
|
||||
includeAlpha = args[1],
|
||||
parsedImage = await jimp.read(Buffer.from(input));
|
||||
parsedImage = await jimp.read(input);
|
||||
|
||||
let bitmap = parsedImage.bitmap.data;
|
||||
bitmap = includeAlpha ? bitmap : bitmap.filter((val, idx) => idx % 4 !== 3);
|
||||
|
|
|
@ -45,7 +45,7 @@ class FlipImage extends Operation {
|
|||
*/
|
||||
async run(input, args) {
|
||||
const [flipAxis] = args;
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid input file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ class ImageBrightnessContrast extends Operation {
|
|||
*/
|
||||
async run(input, args) {
|
||||
const [brightness, contrast] = args;
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ class ImageFilter extends Operation {
|
|||
*/
|
||||
async run(input, args) {
|
||||
const [filterType] = args;
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ class ImageHueSaturationLightness extends Operation {
|
|||
async run(input, args) {
|
||||
const [hue, saturation, lightness] = args;
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ class ImageOpacity extends Operation {
|
|||
*/
|
||||
async run(input, args) {
|
||||
const [opacity] = args;
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ class InvertImage extends Operation {
|
|||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid input file format.");
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ class NormaliseImage extends Operation {
|
|||
* @returns {byteArray}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ class ParseQRCode extends Operation {
|
|||
async run(input, args) {
|
||||
const [normalise] = args;
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
return await parseQrCode(input, normalise);
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils";
|
||||
import PseudoRandomNumberGenerator from "./PseudoRandomNumberGenerator.mjs";
|
||||
import { isImage } from "../lib/FileType";
|
||||
import Utils from "../Utils.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { runHash } from "../lib/Hash.mjs";
|
||||
import { toBase64 } from "../lib/Base64";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import jimp from "jimp";
|
||||
import { toHex } from "../lib/Hex.mjs";
|
||||
|
||||
/**
|
||||
* Randomize Colour Palette operation
|
||||
|
@ -26,10 +26,10 @@ class RandomizeColourPalette extends Operation {
|
|||
|
||||
this.name = "Randomize Colour Palette";
|
||||
this.module = "Image";
|
||||
this.description = "Randomize's each colour in an image's colour palette. This can often reveal text or symbols that were previously a very similar colour to their surroundings.";
|
||||
this.infoURL = "https://en.wikipedia.org/wiki/Indexed_color";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.description = "Randomizes each colour in an image's colour palette. This can often reveal text or symbols that were previously a very similar colour to their surroundings, a technique sometimes used in Steganography.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Indexed_color";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
|
@ -41,15 +41,15 @@ class RandomizeColourPalette extends Operation {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(input)) throw new OperationError("Please enter a valid image file.");
|
||||
|
||||
const seed = args[0] || (new PseudoRandomNumberGenerator()).run("", [5, "Hex"]),
|
||||
parsedImage = await jimp.read(Buffer.from(input)),
|
||||
const seed = args[0] || (Math.random().toString().substr(2)),
|
||||
parsedImage = await jimp.read(input),
|
||||
width = parsedImage.bitmap.width,
|
||||
height = parsedImage.bitmap.height;
|
||||
|
||||
|
@ -64,16 +64,16 @@ class RandomizeColourPalette extends Operation {
|
|||
|
||||
const imageBuffer = await parsedImage.getBufferAsync(jimp.AUTO);
|
||||
|
||||
return Array.from(imageBuffer);
|
||||
return new Uint8Array(imageBuffer).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the extracted data as an image for web apps.
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data.length) return "";
|
||||
if (!data.byteLength) return "";
|
||||
const type = isImage(data);
|
||||
|
||||
return `<img src="data:${type};base64,${toBase64(data)}">`;
|
||||
|
|
|
@ -87,7 +87,7 @@ class ResizeImage extends Operation {
|
|||
"Bezier": jimp.RESIZE_BEZIER
|
||||
};
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ class RotateImage extends Operation {
|
|||
async run(input, args) {
|
||||
const [degrees] = args;
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ class SharpenImage extends Operation {
|
|||
async run(input, args) {
|
||||
const [radius, amount, threshold] = args;
|
||||
|
||||
if (!isImage(new Uint8Array(input))) {
|
||||
if (!isImage(input)) {
|
||||
throw new OperationError("Invalid file type.");
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class ToBase64 extends Operation {
|
|||
*/
|
||||
run(input, args) {
|
||||
const alphabet = args[0];
|
||||
return toBase64(new Uint8Array(input), alphabet);
|
||||
return toBase64(input, alphabet);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Utils from "../Utils";
|
||||
import { isImage } from "../lib/FileType";
|
||||
import { toBase64 } from "../lib/Base64";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import jimp from "jimp";
|
||||
|
||||
/**
|
||||
|
@ -24,10 +24,10 @@ class ViewBitPlane extends Operation {
|
|||
|
||||
this.name = "View Bit Plane";
|
||||
this.module = "Image";
|
||||
this.description = "Extracts and displays a bit plane of any given image. These show only a single bit from each pixel, and so are often used to hide messages in Steganography.";
|
||||
this.description = "Extracts and displays a bit plane of any given image. These show only a single bit from each pixel, and can be used to hide messages in Steganography.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bit_plane";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "byteArray";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.presentType = "html";
|
||||
this.args = [
|
||||
{
|
||||
|
@ -44,15 +44,15 @@ class ViewBitPlane extends Operation {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {File} input
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {File}
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
async run(input, args) {
|
||||
if (!isImage(input)) throw new OperationError("Please enter a valid image file.");
|
||||
|
||||
const [colour, bit] = args,
|
||||
parsedImage = await jimp.read(Buffer.from(input)),
|
||||
parsedImage = await jimp.read(input),
|
||||
width = parsedImage.bitmap.width,
|
||||
height = parsedImage.bitmap.height,
|
||||
colourIndex = COLOUR_OPTIONS.indexOf(colour),
|
||||
|
@ -62,7 +62,6 @@ class ViewBitPlane extends Operation {
|
|||
throw new OperationError("Error: Bit argument must be between 0 and 7");
|
||||
}
|
||||
|
||||
|
||||
let pixel, bin, newPixelValue;
|
||||
|
||||
parsedImage.scan(0, 0, width, height, function(x, y, idx) {
|
||||
|
@ -81,12 +80,12 @@ class ViewBitPlane extends Operation {
|
|||
|
||||
const imageBuffer = await parsedImage.getBufferAsync(jimp.AUTO);
|
||||
|
||||
return Array.from(imageBuffer);
|
||||
return new Uint8Array(imageBuffer).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the extracted data as an image for web apps.
|
||||
* @param {byteArray} data
|
||||
* @param {ArrayBuffer} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue