瀏覽代碼

Merge branch 'qrcodes' of https://github.com/j433866/CyberChef into j433866-qrcodes

n1474335 6 年之前
父節點
當前提交
4ee0800990

+ 3 - 0
package.json

@@ -92,6 +92,7 @@
     "exif-parser": "^0.1.12",
     "file-saver": "^2.0.0-rc.4",
     "highlight.js": "^9.13.1",
+    "jimp": "^0.6.0",
     "jquery": "^3.3.1",
     "js-crc": "^0.2.0",
     "js-sha3": "^0.8.0",
@@ -99,6 +100,7 @@
     "jsesc": "^2.5.1",
     "jsonpath": "^1.0.0",
     "jsonwebtoken": "^8.3.0",
+    "jsqr": "^1.1.1",
     "jsrsasign": "8.0.12",
     "kbpgp": "^2.0.82",
     "lodash": "^4.17.11",
@@ -113,6 +115,7 @@
     "nwmatcher": "^1.4.4",
     "otp": "^0.1.3",
     "popper.js": "^1.14.4",
+    "qr-image": "^3.2.0",
     "scryptsy": "^2.0.0",
     "snackbarjs": "^1.1.0",
     "sortablejs": "^1.7.0",

+ 2 - 0
src/core/config/Categories.json

@@ -363,6 +363,8 @@
             "Generate UUID",
             "Generate TOTP",
             "Generate HOTP",
+            "Generate QR Code",
+            "Parse QR Code",
             "Haversine distance",
             "Numberwang",
             "XKCD Random Number"

+ 104 - 0
src/core/operations/GenerateQRCode.mjs

@@ -0,0 +1,104 @@
+/**
+ * @author j433866 [j433866@gmail.com]
+ * @copyright Crown Copyright 2018
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation";
+import OperationError from "../errors/OperationError";
+import qr from "qr-image";
+import { toBase64 } from "../lib/Base64";
+import Magic from "../lib/Magic";
+
+/**
+ * Generate QR Code operation
+ */
+class GenerateQRCode extends Operation {
+
+    /**
+     * GenerateQRCode constructor
+     */
+    constructor() {
+        super();
+
+        this.name = "Generate QR Code";
+        this.module = "Image";
+        this.description = "Generates a QR code from text.";
+        this.infoURL = "https://wikipedia.org/wiki/QR_code";
+        this.inputType = "string";
+        this.outputType = "byteArray";
+        this.presentType = "html";
+        this.args = [
+            {
+                "name": "Image Format",
+                "type": "option",
+                "value": ["PNG", "SVG"]
+            },
+            {
+                "name": "Size of QR module",
+                "type": "number",
+                "value": 5
+            },
+            {
+                "name": "Margin",
+                "type": "number",
+                "value": 2
+            }
+        ];
+    }
+
+    /**
+     * @param {string} input
+     * @param {Object[]} args
+     * @returns {File}
+     */
+    run(input, args) {
+        // Create new QR image from the input data, and convert it to a buffer
+        const [format, size, margin] = args;
+        const qrImage = qr.imageSync(input, { type: format, size: size, margin: margin });
+        if (qrImage == null) {
+            throw new OperationError("Error generating QR code.");
+        }
+        if (format === "SVG") {
+            return [...Buffer.from(qrImage)];
+        } else if (format === "PNG") {
+            // Return the QR image buffer as a byte array
+            return [...qrImage];
+        } else {
+            throw new OperationError("Error generating QR code.");
+        }
+    }
+
+    /**
+     * Displays the QR image using HTML for web apps
+     * 
+     * @param {byteArray} data
+     * @returns {html}
+     */
+    present(data, args) {
+        if (!data.length) return "";
+
+        const [format] = args;
+        if (format === "SVG") {
+            let outputData = "";
+            for (let i = 0; i < data.length; i++){
+                outputData += String.fromCharCode(parseInt(data[i]));
+            }
+            return outputData;
+        } else {
+            let dataURI = "data:";
+            const type = Magic.magicFileType(data);
+            if (type && type.mime.indexOf("image") === 0){
+                dataURI += type.mime + ";";
+            } else {
+                throw new OperationError("Invalid file type");
+            }
+            dataURI += "base64," + toBase64(data);
+
+            return "<img src='" + dataURI + "'>";
+        }
+    }
+
+}
+
+export default GenerateQRCode;

+ 105 - 0
src/core/operations/ParseQRCode.mjs

@@ -0,0 +1,105 @@
+/**
+ * @author j433866 [j433866@gmail.com]
+ * @copyright Crown Copyright 2018
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation";
+import OperationError from "../errors/OperationError";
+import Magic from "../lib/Magic";
+import jsqr from "jsqr";
+import jimp from "jimp";
+
+/**
+ * Parse QR Code operation
+ */
+class ParseQRCode extends Operation {
+
+    /**
+     * ParseQRCode constructor
+     */
+    constructor() {
+        super();
+
+        this.name = "Parse QR Code";
+        this.module = "Image";
+        this.description = "Reads an image file and attempts to detect and read a QR code from the image.<br><br><u>Normalise Image</u><br>Attempt to normalise the image before parsing it, to try and improve detection of a QR code.";
+        this.infoURL = "https://wikipedia.org/wiki/QR_code";
+        this.inputType = "byteArray";
+        this.outputType = "string";
+        this.args = [
+            {
+                "name": "Normalise image",
+                "type": "boolean",
+                "value": true
+            }
+        ];
+    }
+
+    /**
+     * @param {byteArray} input
+     * @param {Object[]} args
+     * @returns {string}
+     */
+    async run(input, args) {
+        const type = Magic.magicFileType(input);
+        const [normalise] = args;
+        // Make sure that the input is an image
+        if (type && type.mime.indexOf("image") === 0){
+            let normalisedImage = null;
+            if (normalise){
+                // Process the image to be easier to read by jsqr
+                // Disables the alpha channel
+                // Sets the image default background to white
+                // Normalises the image colours
+                // Makes the image greyscale
+                // Converts image to a JPEG 
+                normalisedImage = await new Promise((resolve, reject) => {
+                    jimp.read(Buffer.from(input))
+                        .then(image => {
+                            image
+                                .rgba(false)
+                                .background(0xFFFFFFFF)
+                                .normalize()
+                                .greyscale()
+                                .getBuffer(jimp.MIME_JPEG, (error, result) => {
+                                    resolve([...result]);
+                                });
+                        })
+                        .catch(err => {
+                            reject(new OperationError("Error reading the image file."));
+                        });
+                });
+            } else {
+                normalisedImage = input;
+            }
+            if (normalisedImage instanceof OperationError){
+                return normalisedImage;
+            }
+            return new Promise((resolve, reject) => {
+                jimp.read(Buffer.from(normalisedImage))
+                    .then(image => {
+                        if (image.bitmap != null){
+                            const qrData = jsqr(image.bitmap.data, image.getWidth(), image.getHeight());
+                            if (qrData != null){
+                                resolve(qrData.data);
+                            } else {
+                                reject(new OperationError("Couldn't read a QR code from the image."));
+                            }
+                        } else {
+                            reject(new OperationError("Error reading the normalised image file."));
+                        }
+                    })
+                    .catch(err => {
+                        reject(new OperationError("Error reading the normalised image file."));
+                    });
+            });
+        }  else {
+            throw new OperationError("Invalid file type.");
+        }
+
+    }
+
+}
+
+export default ParseQRCode;

+ 1 - 0
test/index.mjs

@@ -64,6 +64,7 @@ import "./tests/operations/OTP";
 import "./tests/operations/PGP";
 import "./tests/operations/PHP";
 import "./tests/operations/ParseIPRange";
+import "./tests/operations/ParseQRCode";
 import "./tests/operations/PowerSet";
 import "./tests/operations/Regex";
 import "./tests/operations/Register";

File diff suppressed because it is too large
+ 12 - 0
test/tests/operations/ParseQRCode.mjs


+ 3 - 0
webpack.config.js

@@ -46,6 +46,9 @@ module.exports = {
             raw: true,
             entryOnly: true
         }),
+        new webpack.DefinePlugin({
+            "process.browser": "true"
+        }),
         vendorCSS,
         projectCSS
     ],

Some files were not shown because too many files changed in this diff