Merge branch 'j433866-qrcodes'

This commit is contained in:
n1474335 2018-12-25 21:55:35 +00:00
commit dfe31980b7
9 changed files with 924 additions and 92 deletions

View file

@ -2,6 +2,9 @@
All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master). All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master).
### [8.17.0] - 2018-12-25
- 'Generate QR Code' and 'Parse QR Code' operations added [@j433866] | [#448]
### [8.16.0] - 2018-12-19 ### [8.16.0] - 2018-12-19
- 'Play Media' operation added [@anthony-arnold] | [#446] - 'Play Media' operation added [@anthony-arnold] | [#446]
@ -79,6 +82,7 @@ All major and minor version changes will be documented in this file. Details of
[8.17.0]: https://github.com/gchq/CyberChef/releases/tag/v8.17.0
[8.16.0]: https://github.com/gchq/CyberChef/releases/tag/v8.16.0 [8.16.0]: https://github.com/gchq/CyberChef/releases/tag/v8.16.0
[8.15.0]: https://github.com/gchq/CyberChef/releases/tag/v8.15.0 [8.15.0]: https://github.com/gchq/CyberChef/releases/tag/v8.15.0
[8.14.0]: https://github.com/gchq/CyberChef/releases/tag/v8.14.0 [8.14.0]: https://github.com/gchq/CyberChef/releases/tag/v8.14.0
@ -103,6 +107,7 @@ All major and minor version changes will be documented in this file. Details of
[@n1474335]: https://github.com/n1474335 [@n1474335]: https://github.com/n1474335
[@d98762625]: https://github.com/d98762625 [@d98762625]: https://github.com/d98762625
[@j433866]: https://github.com/j433866
[@GCHQ77703]: https://github.com/GCHQ77703 [@GCHQ77703]: https://github.com/GCHQ77703
[@artemisbot]: https://github.com/artemisbot [@artemisbot]: https://github.com/artemisbot
[@picapi]: https://github.com/picapi [@picapi]: https://github.com/picapi
@ -144,3 +149,4 @@ All major and minor version changes will be documented in this file. Details of
[#441]: https://github.com/gchq/CyberChef/pull/441 [#441]: https://github.com/gchq/CyberChef/pull/441
[#443]: https://github.com/gchq/CyberChef/pull/443 [#443]: https://github.com/gchq/CyberChef/pull/443
[#446]: https://github.com/gchq/CyberChef/pull/446 [#446]: https://github.com/gchq/CyberChef/pull/446
[#448]: https://github.com/gchq/CyberChef/pull/448

704
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

@ -0,0 +1,119 @@
/**
* @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";
import Utils from "../Utils";
/**
* Generate QR Code operation
*/
class GenerateQRCode extends Operation {
/**
* GenerateQRCode constructor
*/
constructor() {
super();
this.name = "Generate QR Code";
this.module = "Image";
this.description = "Generates a Quick Response (QR) code from the input text.<br><br>A QR code is a type of matrix barcode (or two-dimensional barcode) first designed in 1994 for the automotive industry in Japan. A barcode is a machine-readable optical label that contains information about the item to which it is attached.";
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", "EPS", "PDF"]
},
{
"name": "Module size (px)",
"type": "number",
"value": 5
},
{
"name": "Margin (num modules)",
"type": "number",
"value": 2
},
{
"name": "Error correction",
"type": "option",
"value": ["Low", "Medium", "Quartile", "High"],
"defaultIndex": 1
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const [format, size, margin, errorCorrection] = args;
// Create new QR image from the input data, and convert it to a buffer
const qrImage = qr.imageSync(input, {
type: format,
size: size,
margin: margin,
"ec_level": errorCorrection.charAt(0).toUpperCase()
});
if (qrImage == null) {
throw new OperationError("Error generating QR code.");
}
switch (format) {
case "SVG":
case "EPS":
case "PDF":
return [...Buffer.from(qrImage)];
case "PNG":
// Return the QR image buffer as a byte array
return [...qrImage];
default:
throw new OperationError("Unsupported QR code format.");
}
}
/**
* 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 === "PNG") {
let dataURI = "data:";
const type = Magic.magicFileType(data);
if (type && type.mime.indexOf("image") === 0){
dataURI += type.mime + ";";
} else {
throw new OperationError("Invalid PNG file generated by QR image");
}
dataURI += "base64," + toBase64(data);
return `<img src="${dataURI}">`;
}
return Utils.byteArrayToChars(data);
}
}
export default GenerateQRCode;

View file

@ -0,0 +1,107 @@
/**
* @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 Quick Response (QR) code from the image.<br><br><u>Normalise Image</u><br>Attempts to normalise the image before parsing it to 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": false
}
];
}
/**
* @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 image = input;
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
image = 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."));
});
});
}
if (image instanceof OperationError) {
throw image;
}
return new Promise((resolve, reject) => {
jimp.read(Buffer.from(image))
.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 image file."));
}
})
.catch(err => {
reject(new OperationError("Error reading the image file."));
});
});
} else {
throw new OperationError("Invalid file type.");
}
}
}
export default ParseQRCode;

View file

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

File diff suppressed because one or more lines are too long

View file

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