From 4e8a79d8f1afad577882532c42b431299c9ece24 Mon Sep 17 00:00:00 2001 From: Ge0rg3 <16269580+Ge0rg3@users.noreply.github.com> Date: Wed, 28 Aug 2019 00:39:08 +0100 Subject: [PATCH 1/5] Bit Plane Browser and LSB Extraction Bit Plane Browser and LSB Extraction Bit Plane Browser and LSB Extraction --- src/core/config/Categories.json | 2 + src/core/operations/ExtractLSB.mjs | 114 +++++++++++++++++++++++++++ src/core/operations/ViewBitPlane.mjs | 107 +++++++++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 src/core/operations/ExtractLSB.mjs create mode 100644 src/core/operations/ViewBitPlane.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 89b93b87..188d6783 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -378,6 +378,8 @@ "Remove EXIF", "Extract EXIF", "Split Colour Channels", + "View Bit Plane", + "Extract LSB", "Rotate Image", "Resize Image", "Blur Image", diff --git a/src/core/operations/ExtractLSB.mjs b/src/core/operations/ExtractLSB.mjs new file mode 100644 index 00000000..cd6128d3 --- /dev/null +++ b/src/core/operations/ExtractLSB.mjs @@ -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; diff --git a/src/core/operations/ViewBitPlane.mjs b/src/core/operations/ViewBitPlane.mjs new file mode 100644 index 00000000..5c9c59c9 --- /dev/null +++ b/src/core/operations/ViewBitPlane.mjs @@ -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 ``; + } + +} + +const COLOUR_OPTIONS = [ + "Red", + "Green", + "Blue", + "Alpha" +]; + +export default ViewBitPlane; From 48831225acfb1a1fd68ebd9f573ae843bb5dc917 Mon Sep 17 00:00:00 2001 From: Ge0rg3 <16269580+Ge0rg3@users.noreply.github.com> Date: Wed, 28 Aug 2019 09:58:00 +0100 Subject: [PATCH 2/5] Extract RGBA Values Operation --- src/core/config/Categories.json | 1 + src/core/lib/Delim.mjs | 9 ++++ src/core/operations/ExtractRGBA.mjs | 65 +++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 src/core/operations/ExtractRGBA.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 188d6783..6f71cf72 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -378,6 +378,7 @@ "Remove EXIF", "Extract EXIF", "Split Colour Channels", + "Extract RGBA", "View Bit Plane", "Extract LSB", "Rotate Image", diff --git a/src/core/lib/Delim.mjs b/src/core/lib/Delim.mjs index 5ad3ddb3..eb390e64 100644 --- a/src/core/lib/Delim.mjs +++ b/src/core/lib/Delim.mjs @@ -72,3 +72,12 @@ export const JOIN_DELIM_OPTIONS = [ {name: "Nothing (join chars)", value: ""} ]; +/* + RGBA list delimiters. +*/ +export const RGBA_DELIM_OPTIONS = [ + {name: "Comma", value: ","}, + {name: "Space", value: " "}, + {name: "CRLF", value: "\\r\\n"}, + {name: "Line Feed", value: "\n"} +]; diff --git a/src/core/operations/ExtractRGBA.mjs b/src/core/operations/ExtractRGBA.mjs new file mode 100644 index 00000000..a215f58b --- /dev/null +++ b/src/core/operations/ExtractRGBA.mjs @@ -0,0 +1,65 @@ +/** + * @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 { isImage } from "../lib/FileType"; +import jimp from "jimp"; + +import {RGBA_DELIM_OPTIONS} from "../lib/Delim.mjs"; + +/** + * Extract RGBA operation + */ +class ExtractRGBA extends Operation { + + /** + * ExtractRGBA constructor + */ + constructor() { + super(); + + 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.outputType = "string"; + this.args = [ + { + name: "Delimiter", + type: "editableOption", + value: RGBA_DELIM_OPTIONS + }, + { + name: "Include Alpha", + type: "boolean", + value: true + } + ]; + } + + /** + * @param {byteArray} input + * @param {Object[]} args + * @returns {string} + */ + async run(input, args) { + if (!isImage(input)) throw new OperationError("Please enter a valid image file."); + + const delimiter = args[0], + includeAlpha = args[1], + parsedImage = await jimp.read(Buffer.from(input)); + + let bitmap = parsedImage.bitmap.data; + bitmap = includeAlpha ? bitmap : bitmap.filter((val, idx) => idx % 4 !== 3); + + return bitmap.join(delimiter); + } + +} + +export default ExtractRGBA; From 950a12360efdf29f07a71fbf9021d32965d44fd8 Mon Sep 17 00:00:00 2001 From: Ge0rg3 <16269580+Ge0rg3@users.noreply.github.com> Date: Wed, 28 Aug 2019 17:03:55 +0100 Subject: [PATCH 3/5] Tests + Bug Fixes * Test cases for LSB extraction, RGBA extraction and bit plane browsing * Bug fix for alpha planes in bit plane browser --- src/core/operations/ViewBitPlane.mjs | 5 ++- tests/operations/index.mjs | 1 - tests/operations/tests/Image.mjs | 57 +++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/core/operations/ViewBitPlane.mjs b/src/core/operations/ViewBitPlane.mjs index 5c9c59c9..864b196c 100644 --- a/src/core/operations/ViewBitPlane.mjs +++ b/src/core/operations/ViewBitPlane.mjs @@ -62,7 +62,6 @@ class ViewBitPlane extends Operation { throw new OperationError("Error: Bit argument must be between 0 and 7"); } - parsedImage.rgba(true); let pixel, bin, newPixelValue; @@ -73,9 +72,11 @@ class ViewBitPlane extends Operation { if (bin.charAt(bitIndex) === "1") newPixelValue = 0; - for (let i=0; i < 4; i++) { + for (let i=0; i < 3; i++) { this.bitmap.data[idx + i] = newPixelValue; } + this.bitmap.data[idx + 3] = 255; + }); const imageBuffer = await parsedImage.getBufferAsync(jimp.AUTO); diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 1ad69228..c9ba86f0 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -109,4 +109,3 @@ const logOpsTestReport = logTestReport.bind(null, testStatus); TestRegister.runTests() .then(logOpsTestReport); - diff --git a/tests/operations/tests/Image.mjs b/tests/operations/tests/Image.mjs index e617578e..511031f4 100644 --- a/tests/operations/tests/Image.mjs +++ b/tests/operations/tests/Image.mjs @@ -2,9 +2,10 @@ * Image operation tests. * * @author tlwr [toby@toby.codes] + * @author Ge0rg3 [georgeomnet+cyberchef@gmail.com] * @author n1474335 [n1474335@gmail.com] * - * @copyright Crown Copyright 2017 + * @copyright Crown Copyright 2019 * @license Apache-2.0 */ import TestRegister from "../../lib/TestRegister.mjs"; @@ -175,4 +176,58 @@ TestRegister.addTests([ }, ], }, + { + name: "Extract RGBA", + input: "424d460400000000000036040000280000000400000004000000010008000000000010000000120b0000120b0000000100000001000000c8000000cf000000d7000000df000000e7000000ef000000f7000000ff000083000000ac000000d5000000ff000000000083000000ac000000d5000000ff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f070d05030b01090c040e060008020a", + expectedOutput: "0 200 0 0 0 131 0 215 0 0 0 213 131 0 0 0 231 0 213 0 0 0 247 0 0 223 0 0 0 255 0 207 0 0 0 172 255 0 0 0 255 0 172 0 0 0 239 0", + recipeConfig: [ + { + op: "From Hex", + args: ["None"] + }, + { + op: "Extract RGBA", + args: [" ", false] + } + ] + }, + { + name: "Extract LSB", + input: "89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af400000449494441547801cc9703782b5914c783b56ddbb659db6e9f6ddbb66dd45e6fddc6aecd24cff67b99397b6ebe9d6216d99bcceebefb7dffbaf9ff0e7347e4e491eeddbbf7f5bcbcbc41b9b9b943376cd8f00ef9194afcfb67094ac0d3ed85870d1b764755555592c562616a6a6a18ad560b28b6a0a0203b3d3d7d4e494989fed75f7fdd45fe5e10e38c8c8c0f2a2b2b7737343468ebeaea8a516aabd50a441c00278d46c392cf2a95eaf475d75d7767525252505656565f67332296c9644330d2cbc48c2f3e005f656565ed08c410656666c653bb63e4ef9acd66ce9c0a8027164b430d20aea8a8d84e4c5c05c02c1ebce9a69b6e27af49d5e5f5f5f56a4700b5b5b5ac0300ae1cad3ffdf4d3321a0809365bc93f007068cea9b8b8584e35197abd7e8e5019c032983103aba8ca306fdebc07dadada2c02f4c0a15b6eb9e50ea746313535f5ade6e6e63a57008870170c14397b9e7cf2c97bc838d20294969636e30eb0a1181ce9de2217ce2d087089060037e139b1587ce7fefdfbfd30fa3ef4e9771140a1501cc3ffbb91deea2a05b8991640a9541ec5ffbb5e2800497272f20f7880afad5bb73273e7ce05bea64c99922ee49de07a9d4ed760341a812fbc9030696969c0574a4a8a11ffef5a979d5fbaffe61be604bf3dc698b9d966cade0add555ffa13ab91c96cbb76ed62e7cf9fdf43b367cf3ef7c5175f78ba9cfac5fe2f6cd62e88828a65f13c25c0595d09542b95cceeddbb212020007c7d7d7bc8c7c7e7b2bbbbfbf34ebbc7bcf3e0a33ba25eb992d8f70330ace8df692e9b1703478b7300dadaec00782784799be7815b9c1b676e070a0a0a2a78f7dd77ef7573737b468a871a60b6e733513ba35e818d612fc111b50cce5768a15f983fcc9c311d4a3233e1427d3dfc9c98c8e6e4e4c07d89f7c1dbd96fc3a8ef47c1f6eddb61c78e1dd0b76fdf8b090909c67efdfa31616161b154e6833e7ef4c96d112f1fe5008e6995f688372f5902512121d0a152c1711cb7cc8d1b01b71d4c2a9b64875893b70648461213138fa1794dfffefd01c54645458da102581df8c26a62ce07e0646b6981530a851d60f5ead5c4b48770f7d77b7b7bfb1180e8e8e82db43764c986d017cbfe0ee05c6d2d5cd268ec003366cce00390516c0c0f0f5f4c007af7ee5d4dfa800640bc2ef8c5744719384300366d62478d1a05a40fba03e0430b8bb5070240141717b792aa0433dd9f76db11f98a8d0fd05d6c6b2b54cae56c76763631e503407c7cbc2130303018cd777dfdf5d78f53df8a17fa3cdb7f7bc4cba7ba03301879bbc904c770024e3637c37eec7834204d4622be826fbd2c07803f5b2e72f584bdf1c0fd13be7e32fe54b9e92417f9653437e3fb40c39e3db067c10216f73eb36edd3a06cb6037479155dcfef9e79f3f2cd87361555e5ec4a5c64633bf0cdc22ea2ecc8265e7ce9dfe22a1cfe4a143ef32ab54532e363434f10038e30e9cffdfd650545424c8404bc0c4c4c47664cd1a83c7274ec41fdeb62d0f58192501a3c04c5e5e9e8d1cf30084683c77e1e9adc80000000049454e44ae426082", + expectedOutput: "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000240000000000000000000000208000000000000000000008000000000000000000000000248000000200240000000208908000000200240000000200821000000200240000000061249000000240000000000209b69000001a49b00000000a204a1200001a49b00000009800414000001a49b0000000035db6c00000094924000000086dffc20000df6dec8000001e10014a0000df6dec800002564924b00000df6dec80000009a6db20000007edb4124804177fffba0002fffff69249044e0924bc4002fffff6924905fb2db6d04002fffff692490416d2490040001bfffcc92030dbffffdc00037fffffdb6d302c6db6d700037fffffdb6d327eb6db6148037fffffdb6d30db4000014800dffffeb6d9aefffffff640", + recipeConfig: [ + { + op: "From Hex", + args: ["None"] + }, + { + op: "Extract LSB", + args: ["B", "G", "A", "", "Column", 2] + }, + { + op: "To Hex", + args: ["None"] + } + ] + }, + { + name: "View Bit Plane", + input: "89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af400000449494441547801cc9703782b5914c783b56ddbb659db6e9f6ddbb66dd45e6fddc6aecd24cff67b99397b6ebe9d6216d99bcceebefb7dffbaf9ff0e7347e4e491eeddbbf7f5bcbcbc41b9b9b943376cd8f00ef9194afcfb67094ac0d3ed85870d1b764755555592c562616a6a6a18ad560b28b6a0a0203b3d3d7d4e494989fed75f7fdd45fe5e10e38c8c8c0f2a2b2b7737343468ebeaea8a516aabd50a441c00278d46c392cf2a95eaf475d75d7767525252505656565f67332296c9644330d2cbc48c2f3e005f656565ed08c410656666c653bb63e4ef9acd66ce9c0a8027164b430d20aea8a8d84e4c5c05c02c1ebce9a69b6e27af49d5e5f5f5f56a4700b5b5b5ac0300ae1cad3ffdf4d3321a0809365bc93f007068cea9b8b8584e35197abd7e8e5019c032983103aba8ca306fdebc07dadada2c02f4c0a15b6eb9e50ea746313535f5ade6e6e63a57008870170c14397b9e7cf2c97bc838d20294969636e30eb0a1181ce9de2217ce2d087089060037e139b1587ce7fefdfbfd30fa3ef4e9771140a1501cc3ffbb91deea2a05b8991640a9541ec5ffbb5e2800497272f20f7880afad5bb73273e7ce05bea64c99922ee49de07a9d4ed760341a812fbc9030696969c0574a4a8a11ffef5a979d5fbaffe61be604bf3dc698b9d966cade0add555ffa13ab91c96cbb76ed62e7cf9fdf43b367cf3ef7c5175f78ba9cfac5fe2f6cd62e88828a65f13c25c0595d09542b95cceeddbb212020007c7d7d7bc8c7c7e7b2bbbbfbf34ebbc7bcf3e0a33ba25eb992d8f70330ace8df692e9b1703478b7300dadaec00782784799be7815b9c1b676e070a0a0a2a78f7dd77ef7573737b468a871a60b6e733513ba35e818d612fc111b50cce5768a15f983fcc9c311d4a3233e1427d3dfc9c98c8e6e4e4c07d89f7c1dbd96fc3a8ef47c1f6eddb61c78e1dd0b76fdf8b090909c67efdfa31616161b154e6833e7ef4c96d112f1fe5008e6995f688372f5902512121d0a152c1711cb7cc8d1b01b71d4c2a9b64875893b70648461213138fa1794dfffefd01c54645458da102581df8c26a62ce07e0646b6981530a851d60f5ead5c4b48770f7d77b7b7bfb1180e8e8e82db43764c986d017cbfe0ee05c6d2d5cd268ec003366cce00390516c0c0f0f5f4c007af7ee5d4dfa800640bc2ef8c5744719384300366d62478d1a05a40fba03e0430b8bb5070240141717b792aa0433dd9f76db11f98a8d0fd05d6c6b2b54cae56c76763631e503407c7cbc2130303018cd777dfdf5d78f53df8a17fa3cdb7f7bc4cba7ba03301879bbc904c770024e3637c37eec7834204d4622be826fbd2c07803f5b2e72f584bdf1c0fd13be7e32fe54b9e92417f9653437e3fb40c39e3db067c10216f73eb36edd3a06cb6037479155dcfef9e79f3f2cd87361555e5ec4a5c64633bf0cdc22ea2ecc8265e7ce9dfe22a1cfe4a143ef32ab54532e363434f10038e30e9cffdfd650545424c8404bc0c4c4c47664cd1a83c7274ec41fdeb62d0f58192501a3c04c5e5e9e8d1cf30084683c77e1e9adc80000000049454e44ae426082", + expectedOutput: "89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af400000140494441547801c5c1416ea3400000c1ee11ffff726fe6808410186ce26c95fde0432a154f0cdea4b2aa505151519954ee1a5c50995454aea8ac54ae2c5ca8982a3ea132551c199c507942e58ec1898adf50f1cae084ca15952b2a152a47067f40a5e2c8e00f54a81c199ca8b85271a542a5e2c8e005159527542ace0c5ea8a8f844c54ae5ccc217555c197c41c55d83ff6cf0052a772ddca052b1a752b1a772d7c2432a4f2c3c50f1d4c20b2a1593ca918a4965afe2cac29b2a562a93ca56c55d0b2754b62a269555c554b15251a9b8637040e5884ac54a654ba5a2624be5cce040c5918a55c55ec5a4a232a9549c197c48655239523155bc3278a862af624be5ccc2072aaea854a8549c5978834a85ca5ec5918a57ec076f50a958a9546ca94c1557ec071754a68a2d958a270637544c2a2abf69e1a68a95ca54b152a978d73f2e08bd57b6f839a00000000049454e44ae426082", + recipeConfig: [ + { + op: "From Hex", + args: ["None"] + }, + { + op: "View Bit Plane", + args: ["Green", 3] + }, + { + op: "To Hex", + args: ["None"] + } + ] + + } ]); From d23a584b9eaeb06b3ff3d14715e95a36b0f887a3 Mon Sep 17 00:00:00 2001 From: Ge0rg3 <16269580+Ge0rg3@users.noreply.github.com> Date: Thu, 29 Aug 2019 16:17:07 +0100 Subject: [PATCH 4/5] Randomize Colour Palette Operation --- src/core/config/Categories.json | 1 + .../operations/RandomizeColourPalette.mjs | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/core/operations/RandomizeColourPalette.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 6f71cf72..695546f0 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -380,6 +380,7 @@ "Split Colour Channels", "Extract RGBA", "View Bit Plane", + "Randomize Colour Palette", "Extract LSB", "Rotate Image", "Resize Image", diff --git a/src/core/operations/RandomizeColourPalette.mjs b/src/core/operations/RandomizeColourPalette.mjs new file mode 100644 index 00000000..ded8fd49 --- /dev/null +++ b/src/core/operations/RandomizeColourPalette.mjs @@ -0,0 +1,84 @@ +/** + * @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 PseudoRandomNumberGenerator from "./PseudoRandomNumberGenerator.mjs"; +import { isImage } from "../lib/FileType"; +import { runHash } from "../lib/Hash.mjs"; +import { toBase64 } from "../lib/Base64"; +import jimp from "jimp"; + +/** + * Randomize Colour Palette operation + */ +class RandomizeColourPalette extends Operation { + + /** + * RandomizeColourPalette constructor + */ + constructor() { + super(); + + 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.presentType = "html"; + this.args = [ + { + name: "Seed", + type: "string", + value: "" + } + ]; + } + + /** + * @param {byteArray} input + * @param {Object[]} args + * @returns {byteArray} + */ + 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)), + width = parsedImage.bitmap.width, + height = parsedImage.bitmap.height; + + let rgbString, rgbHash, rgbHex; + + parsedImage.scan(0, 0, width, height, function(x, y, idx) { + rgbString = this.bitmap.data.slice(idx, idx+3).join("."); + rgbHash = runHash("md5", Utils.strToArrayBuffer(seed + rgbString)); + rgbHex = rgbHash.substr(0, 6) + "ff"; + parsedImage.setPixelColor(parseInt(rgbHex, 16), x, y); + }); + + 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 ``; + } + +} + +export default RandomizeColourPalette; From aa5afadcceee57c432040ea723a221c1690a73ca Mon Sep 17 00:00:00 2001 From: Ge0rg3 <16269580+Ge0rg3@users.noreply.github.com> Date: Thu, 29 Aug 2019 16:24:21 +0100 Subject: [PATCH 5/5] Tests for Randomize Colour Palette Op --- tests/operations/tests/Image.mjs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/operations/tests/Image.mjs b/tests/operations/tests/Image.mjs index 511031f4..ea5c64a4 100644 --- a/tests/operations/tests/Image.mjs +++ b/tests/operations/tests/Image.mjs @@ -228,6 +228,24 @@ TestRegister.addTests([ args: ["None"] } ] - + }, + { + name: "Randomize Colour Palette", + "input": "89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af400000674494441547801c5970394244b1686bf44b9aaddd5fdfcc6330f63af6ddbdea3b56ddbb66ddbdeb16dcf5497d219b137f3d44363ba7b70cede73be74c6fd6f4454e45ffcbfc398ecdef7bef7bd35c562f1c59669de0fc32818467a590ec7bca635bab38fe27823b0278aa29f3cf0810ffc12a02e4680f9fdef7fff1903d5eaa766cd98412e9f2793c91204216118c83e052509b3d96c4aaeb3775d07152b1a8d3a478f1ee5452f7a5101f0384fd88b172f666c6cd8b0215f2e973fb576f56a40e37901be248ce34888514a9192542edcfebcb7a7977a7d844e70e4c891d2a402e401c6c6631ffbd82b92864dd3948ac3b4f1e4bc13c91008a6a0097c1fa7dd927d20421d9acd16beef1fdbba75eb8f5ef9ca577eee6286c010867ef5ab5f1dbfd7bdee858c2569d7cb5ec4488206b55a8d76bb8de7ba64b2198af9025ddddd5c71e5951cd8bf9f37bef18d4f9121fc0f705aa80bc1797b80f1a11392ea034968c8be561be1e4a913d425719ab054a6af7f003b63532a1629e4f3140a058a42d25b3367ce6c02870467aa1eb09938a2fffef7bf6f035e8661d85ac3cd37dfcc82053726e39c56ef380e511c89400bd34ab001b06c9beeee9e00f0264f3eb900f7e52f7ff9e7819f0995bffe6bddcf77ef3f8c8a0fa20123c1209d1771ace47a9cce0b3b63093656363f6a782f4680271c13ce093dcf7ae09d9ef8f47bf77c05a5c964f2686c1a4d9f43474e72e7bbaf65c5da9b3876ec0c9ff8c84fe8abf4b1eff4a118d04c234c260e25381d012756cfee5b15eb1c37ad5c4dbe7b8062ff20f778f0bd78c7c7dfc4fd1e716f8add7dcc98338365775a8c952bb260c6d58f03cc4b1030fa996ac57a96e318acbecb1a9efdba17f28ce73e8585cb1612470aa7ede17b3e9e1771e59503f8a14339977d1490bb1c028c27ace85f6d59e41dd764efcebdd07668b59c74127a92d8f7935531220803dc769b4ac10243171ebf727035605caa804c7fd1783268c2c0e2f73ffb2b5aa9643de82ccb091191ec93c9b86de35e72392bfd290e96cc2701994b1590b550cbb58a29e6234e9d70d9b3f3202a124191240f2479140a8a53c7ce51cae4b06d489eb78c782590bd6401b6a517a838c0204c1bffcd0fff045aa7e31f45b1a0e43466e3bf77808e8845905221b188b81c3d600611bb6215a1421fad7cf6eed8cba986471448a2385d9ef9f7e6bdd4ced688635f0404c46180a8b0a633076c260fe587d131db30e606848045fff09558a6c5f68367f0ddb6ac05a769b49a682d4f784e2a00342d37da04e84bed81e0137f6f3ef7f499c6df5b5ebc1b2b878a5c4c4333dc57c1360d8a390b4bce5bf5b3683343aee70ae7ecb9e696afad77df080497e2880032af7ff3dbeef8d0873dfcb5f3e6cfbb5bc182fffee8ad74570cd0a41f2ad334049346bdc1cd0f78335e0c070f1e3efbef7ffefddd4f7afc63de0f041723c0f8f297bf3c7be1c245df5db850be429d38f09faf523bb20ed3ca25d96fb366a929f1c915079877f797a6823ac6c6dbb973e7ddc45ffc13d0d31d02e3339ff9ccbc952b57ee4a92bb9e475daa6b341d8ce23544dac269d5709da6d0c2711ab49373d7c7ee992bcfd68526c97be2b6f2c2dfa598b58031dd1ec8feec673fdb70bffbdd6fc1b9da48faf93520add8346d4c3bc7b9037fe3e8a6efe33b67b0ed3cc3f3ef4b75eebdc1b2d17188d6f1adcea9afb7873ffce10fe1ddee76b72ec09b8e80ee75ebd68d2c59b2444cc8e9d10ef8162b264252e418345a451d5472065a20d96986aa83ac5fbf8eb7bef56dd701871813f6231ef1083ac177bffb5de3ddef7ef74ab1e2a9058b22459a7f5c244e2915031d630aa374269705952ed9625058b66cd9bc57bdea5547c69a145b928e3aaf56ab6b1301a9ed56112332a67d3d3da831494c6db26bcf1e868786e8aa54c4fd1e1e25a0d56c33343c4cb9524e2ddb8c19331e08fc4150934d42bb542a2defefef97065ae947a53e524f7a6394fd9663418112525714b362c50a962f5f2e7b418ed7ac59239577e3b5db744b01f97c7e39909d6a25cc4af5cb054e9d394bac54cae12347a94895e57209cbb268b7da341a0dec6c8eb3e76af84198625a26ade45ebd91bae96a7520494c75a88a14b614c84c25c0ce66b3c3406a322ccba4bfaf174f8e1bcdc6a83f1c32395314506fd4d37b1dbf28490b527d05dbce10851100b95c2e0fd853fd0a06befded6f7fbfb7b7f78e69825b67fe6d8f1a13bda9c71fca308dfacfa8b46ec9ff8c59c0a9c90454846b8501c1e6f285166ac27e6164b2217085431d9506973702a1cd98f81f11b2640d65786ac70000000049454e44ae426082", + expectedOutput: "89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af4000007ed494441547801c5c17b5c0d8602c0f15f9de3d16425a113999296348f3c0b75ea4e2b5be7a05bd46a32c99b5496477266d494ac97508a0e494848c2525e87bc95ccb31145915e3abacb38f7b6cfdde7d3a74faafff6fdaa59c81254fc8384b42179b40501455ee84476617b920eeee33e6742d7c994d685d15c9ac31ff85f5c4386b13ada4b0268a2597c8dc7fa72da23e423ccfca67221fa32f29dfe1c59b08ef4c4459c7a5acc1e130396d5aee2f8d8092c105ee7f9a91a6e6db6a6c4d3893e96b318aa1b41b7aa0822cd156c1aa5c39b2e0f698b7097d36b5af2caec499cc29dd409bd389b578ea66f10569394fc229b89f7fd2f890bbdcbbbeb83c8511b8acbc67b6cad39c7c691f7e81ceb829eb12deb6de740247f912e1c465b84d285c368c92150c68f338e31784e11c3924e522281d50a0156fdf84bf27a1fa2c4de7cb3d79dbdd3ab916f90a2b4ec45e3e6159cb54fc6c8741fa3b48a703e654e0f872ada2230f216cb348cebd030ae43c3b80e0de33aca2a2c30cc37a5d7c89714780a11de84dda24914ae28a5617b209f0f4e22bba03bfd973930a1df22ece73ea1dbe30d8c9ee48455e925a2d39dd861fa968e1088c41219ad1873b89e5137bf66a465029b46f461aaebf79cad7c4e68f0262aabba503ec09123e3b7a319e6cbdefe12385e485d5d0fe4d21e7c2bb047d1e94b3a429d8fb0d64de39cff1172f213581aea8f32eb2ebaf23bc85f6be2e1b5859af4a5680c9d4df046299551c9ec58ed49935d3f9bf04a2d8b8e1288c41219ad383ebe3f771475dc30f88490e1f7e82c8c62f34007be8e53e026b5c4c3a11b03ae6f657abf4944e8fe4efef3293c689c8d87961e11db9da91beb4847084462898c76e48499b243438fead0183e645660133886d3ffaa65acc0971b0113c9add4a3583b1c9f05b5a46c352447bb2f5a867de908753ac03f478bb26b9f52e4f88642e712b47cb7b122f4097ee5d5788cc8e2e8e20a4c94cf5962fa03e9257339635d4e47a9d30195dade841c9273d5408731bdcbf139b983d8de57393d2f908c992ed49b6c6474a101f1c11928dc8ef066ed753a4a483b740f34703fd38c82ada58c7c358f17c77ee2c98aa7acf9598df57ebfd0b93a8dfc3333c82aafc0e1e518968f49e7495e2e6a411fe8ba5e417bd469c79463eef8c54f63d76173a24f9f40b955c989dcb7f867bee7b33c29fd97b8e09c771de5ae5a9c4bd7d15747cae8ea30065ebc4d47086987b6cd34eef4aca56be65bfcc3c6312fab37bb2ec5303c64177d621c18eb25e24ef71ea41e0c216250000d9e525ef7d065d0d628ca3a4da73d02915822a30d1a29c398ebf12be1a5f3d1093a42df9709bc4a6e60d07721945c3d84a47b118a803c0483aff02e3a9eb74356b2283688ec05f6bcf94d8ff6a8d38ebb353ac4fe618ac53d57ee5cbd8874f26d2e9d8ec3eebc2b8ee163f1591bc3a0e25282b639d14bc305a7c652f20b37a06bb8878e50a71d8eff598d4cef2056dd8cd896da8537aebe6cd3b4e3c1706b2a53f5315d1b448d5a363a669fa2f2d442392198f8fe539816ad4e47084462898c36dc2cb4e2d6dc2f181b5241cf47f1147c178db67b19911e4fd1ba3c9e9f728e7241ef393766ca091a974145891ca5d88751920cf21555b44720124b64b44112f135a9053739693e0a3dd3f1684b5c392f4a24cefb027aef0663be3190c64867ba769acc90f43ed8c4a450239b43f5aa069e7656d11e753e62ffd52c94b646f49b7b1fcb3001d6dfbe6375d5778cebfd16cbc453b8669dc66e71144b8dc55cb7fb8a4b71fa1cb55d4279843e36cb2d18aa32e2135b7f027cbc688b402496c868c1cd620259ba4370b9aacec0a06c1cbfe8c2b08614a2b5df5353e14db0db441efa2e4623f92835c92bf18df56464919440d52c56b113b7ec4d686fd1e6b31d6b5813b6946335c51ce86b426b0422b144460be10633a9bff56ff236dee7878bc94c0e7d48a3d10d82baf664f45e3f7627a868541c47f9bb883d665ff15a56c5a36c2f54f16e6cb8a0497d4d29c2a365ecdf2ec2eb81132b0627a0fa7322ad11d28a5986a94c038cc2d7121da5e026ffa380a4862c0c0fc552231f87fcad90facf0229f23ac9dcc2482aee66919462c1a188857caf914813a3f0f1c03e9e8913c8ea398bd6a85d7bd85fc5fff9a4ace19c7e236689d964e858e26d9946c172335ab2d9ff827a83e1bc5fe8c78cf70dbc59684373f1ebeaf009fe940d3eb67cf54087dfb65c2379ea3cfc2ebfa725350b59828a66242bed51793b53f5a29ea84e3d89f2ddc4a4158598e9c969ce35ad8cf9f9fdb8bf3a86dcd35389720ce66fa22d2202a507099b338078fd18cc3ba961305f972901beb424108925329a99b5e021eb96e570f4543051b955ec769dca3c074b164b8d78f4e3532eb9f720b1fb5224a5d6ccd73d4892e609726a63095ee9c39e705b94cf26e3f3a18eb2ea7378561af0f3950f6cb1abe151cc455e5dba474b02915822a319ffd9c188cec8499a2a20c4269ebbb9ae7c1b684556c40786767bc6fdd05b4c1c6a49491739f2dc13d8efb62661531acbc6ebb1ece57ee6ab7499257067e7f2e92cb1d267709f517c73db8d068fde3cb1bc474beab4b0d376274df68d9b89dedd3b0ccc48648fc93374970e42651c878be1af8cd83986cd7602940557386babe0855a19b5ef42e86df788b3227b4e2c7362f3a248a47f1612e1349f26e71f3ca6356a16b20415cd445d7984225d4c695d1887334d386f664f9c7d3ff282dc68f2a7cf019a180d5cc4df7e2f8ee56fc278179a1cb00e67e26fa798e2f48034873f280ed1447cd98596d42c64092afe41eafcc3fe0b9c67148a38c1a5620000000049454e44ae426082", + recipeConfig: [ + { + op: "From Hex", + args: ["None"] + }, + { + op: "Randomize Colour Palette", + args: ["myseed"] + }, + { + op: "To Hex", + args: ["None"] + } + ] } ]);