ExtractLSB.mjs 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /**
  2. * @author Ge0rg3 [georgeomnet+cyberchef@gmail.com]
  3. * @copyright Crown Copyright 2019
  4. * @license Apache-2.0
  5. */
  6. import Operation from "../Operation.mjs";
  7. import OperationError from "../errors/OperationError.mjs";
  8. import Utils from "../Utils";
  9. import { isImage } from "../lib/FileType";
  10. import jimp from "jimp";
  11. /**
  12. * Extract LSB operation
  13. */
  14. class ExtractLSB extends Operation {
  15. /**
  16. * ExtractLSB constructor
  17. */
  18. constructor() {
  19. super();
  20. this.name = "Extract LSB";
  21. this.module = "Image";
  22. this.description = "Extracts the Least Significant Bit data from each pixel in an image. This is a common way to hide data in Steganography.";
  23. this.infoURL = "https://en.wikipedia.org/wiki/Bit_numbering#Least_significant_bit_in_digital_steganography";
  24. this.inputType = "byteArray";
  25. this.outputType = "byteArray";
  26. this.args = [
  27. {
  28. name: "Colour Pattern #1",
  29. type: "option",
  30. value: COLOUR_OPTIONS,
  31. },
  32. {
  33. name: "Colour Pattern #2",
  34. type: "option",
  35. value: ["", ...COLOUR_OPTIONS],
  36. },
  37. {
  38. name: "Colour Pattern #3",
  39. type: "option",
  40. value: ["", ...COLOUR_OPTIONS],
  41. },
  42. {
  43. name: "Colour Pattern #4",
  44. type: "option",
  45. value: ["", ...COLOUR_OPTIONS],
  46. },
  47. {
  48. name: "Pixel Order",
  49. type: "option",
  50. value: ["Row", "Column"],
  51. },
  52. {
  53. name: "Bit",
  54. type: "number",
  55. value: 0
  56. }
  57. ];
  58. }
  59. /**
  60. * @param {File} input
  61. * @param {Object[]} args
  62. * @returns {File}
  63. */
  64. async run(input, args) {
  65. if (!isImage(input)) throw new OperationError("Please enter a valid image file.");
  66. const bit = 7 - args.pop(),
  67. pixelOrder = args.pop(),
  68. colours = args.filter(option => option !== "").map(option => COLOUR_OPTIONS.indexOf(option)),
  69. parsedImage = await jimp.read(Buffer.from(input)),
  70. width = parsedImage.bitmap.width,
  71. height = parsedImage.bitmap.height,
  72. rgba = parsedImage.bitmap.data;
  73. if (bit < 0 || bit > 7) {
  74. throw new OperationError("Error: Bit argument must be between 0 and 7");
  75. }
  76. let i, combinedBinary = "";
  77. if (pixelOrder === "Row") {
  78. for (i = 0; i < rgba.length; i += 4) {
  79. for (const colour of colours) {
  80. combinedBinary += Utils.bin(rgba[i + colour])[bit];
  81. }
  82. }
  83. } else {
  84. let rowWidth;
  85. const pixelWidth = width * 4;
  86. for (let col = 0; col < width; col++) {
  87. for (let row = 0; row < height; row++) {
  88. rowWidth = row * pixelWidth;
  89. for (const colour of colours) {
  90. i = rowWidth + (col + colour * 4);
  91. combinedBinary += Utils.bin(rgba[i])[bit];
  92. }
  93. }
  94. }
  95. }
  96. return Utils.convertToByteArray(combinedBinary, "binary");
  97. }
  98. }
  99. const COLOUR_OPTIONS = ["R", "G", "B", "A"];
  100. export default ExtractLSB;