Skip to content

Commit

Permalink
Bit Plane Browser and LSB Extraction
Browse files Browse the repository at this point in the history
Bit Plane Browser and LSB Extraction

Bit Plane Browser and LSB Extraction
  • Loading branch information
Ge0rg3 committed Aug 28, 2019
1 parent d3e3e6e commit 4e8a79d
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,8 @@
"Remove EXIF",
"Extract EXIF",
"Split Colour Channels",
"View Bit Plane",
"Extract LSB",
"Rotate Image",
"Resize Image",
"Blur Image",
Expand Down
114 changes: 114 additions & 0 deletions src/core/operations/ExtractLSB.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* @author Ge0rg3 [georgeomnet+cyberchef@gmail.com]
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/

import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils";
import { isImage } from "../lib/FileType";
import jimp from "jimp";

/**
* Extract LSB operation
*/
class ExtractLSB extends Operation {

/**
* ExtractLSB constructor
*/
constructor() {
super();

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.outputType = "byteArray";
this.args = [
{
name: "Colour Pattern #1",
type: "option",
value: COLOUR_OPTIONS,
},
{
name: "Colour Pattern #2",
type: "option",
value: ["", ...COLOUR_OPTIONS],
},
{
name: "Colour Pattern #3",
type: "option",
value: ["", ...COLOUR_OPTIONS],
},
{
name: "Colour Pattern #4",
type: "option",
value: ["", ...COLOUR_OPTIONS],
},
{
name: "Pixel Order",
type: "option",
value: ["Row", "Column"],
},
{
name: "Bit",
type: "number",
value: 0
}
];
}

/**
* @param {File} input
* @param {Object[]} args
* @returns {File}
*/
async run(input, args) {
if (!isImage(input)) throw new OperationError("Please enter a valid image file.");

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)),
width = parsedImage.bitmap.width,
height = parsedImage.bitmap.height,
rgba = parsedImage.bitmap.data;

if (bit < 0 || bit > 7) {
throw new OperationError("Error: Bit argument must be between 0 and 7");
}

let i, combinedBinary = "";

if (pixelOrder === "Row") {
for (i = 0; i < rgba.length; i += 4) {
for (const colour of colours) {
combinedBinary += Utils.bin(rgba[i + colour])[bit];
}
}
} else {
let rowWidth;
const pixelWidth = width * 4;
for (let col = 0; col < width; col++) {
for (let row = 0; row < height; row++) {
rowWidth = row * pixelWidth;
for (const colour of colours) {
i = rowWidth + (col + colour * 4);
combinedBinary += Utils.bin(rgba[i])[bit];
}
}
}
}

return Utils.convertToByteArray(combinedBinary, "binary");

}

}

const COLOUR_OPTIONS = ["R", "G", "B", "A"];

export default ExtractLSB;
107 changes: 107 additions & 0 deletions src/core/operations/ViewBitPlane.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* @author Ge0rg3 [georgeomnet+cyberchef@gmail.com]
* @copyright Crown Copyright 2019
* @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 jimp from "jimp";

/**
* View Bit Plane operation
*/
class ViewBitPlane extends Operation {

/**
* ViewBitPlane constructor
*/
constructor() {
super();

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.infoURL = "https://wikipedia.org/wiki/Bit_plane";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.presentType = "html";
this.args = [
{
name: "Colour",
type: "option",
value: COLOUR_OPTIONS
},
{
name: "Bit",
type: "number",
value: 0
}
];
}

/**
* @param {File} input
* @param {Object[]} args
* @returns {File}
*/
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)),
width = parsedImage.bitmap.width,
height = parsedImage.bitmap.height,
colourIndex = COLOUR_OPTIONS.indexOf(colour),
bitIndex = 7-bit;

if (bit < 0 || bit > 7) {
throw new OperationError("Error: Bit argument must be between 0 and 7");
}

parsedImage.rgba(true);

let pixel, bin, newPixelValue;

parsedImage.scan(0, 0, width, height, function(x, y, idx) {
pixel = this.bitmap.data[idx + colourIndex];
bin = Utils.bin(pixel);
newPixelValue = 255;

if (bin.charAt(bitIndex) === "1") newPixelValue = 0;

for (let i=0; i < 4; i++) {
this.bitmap.data[idx + i] = newPixelValue;
}
});

const imageBuffer = await parsedImage.getBufferAsync(jimp.AUTO);

return Array.from(imageBuffer);
}

/**
* Displays the extracted data as an image for web apps.
* @param {byteArray} data
* @returns {html}
*/
present(data) {
if (!data.length) return "";
const type = isImage(data);

return `<img src="data:${type};base64,${toBase64(data)}">`;
}

}

const COLOUR_OPTIONS = [
"Red",
"Green",
"Blue",
"Alpha"
];

export default ViewBitPlane;

0 comments on commit 4e8a79d

Please sign in to comment.