-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bit Plane Browser and LSB Extraction
- Loading branch information
Showing
2 changed files
with
217 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/** | ||
* @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; | ||
|
||
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |