Merge branch 'master' into feature-threading

This commit is contained in:
n1474335 2017-07-28 16:55:07 +01:00
commit e977a1006c
45 changed files with 3426 additions and 2588 deletions

View File

@ -52,6 +52,9 @@
"no-trailing-spaces": "warn",
"eol-last": "error",
"func-call-spacing": "error",
"key-spacing": ["warn", {
"mode": "minimum"
}],
"indent": ["error", 4, {
"ArrayExpression": "first",
"SwitchCase": 1

1882
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "5.11.0",
"version": "5.12.2",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef",
@ -31,39 +31,39 @@
"bugs": "https://github.com/gchq/CyberChef/issues",
"devDependencies": {
"babel-core": "^6.24.0",
"babel-loader": "^6.4.0",
"babel-loader": "^7.1.1",
"babel-polyfill": "^6.23.0",
"babel-preset-env": "^1.6.0",
"babel-regenerator-runtime": "^6.5.0",
"css-loader": "^0.27.3",
"css-loader": "^0.28.4",
"exports-loader": "^0.6.4",
"extract-text-webpack-plugin": "^2.1.0",
"file-loader": "^0.10.1",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^0.11.2",
"grunt": ">=0.4.5",
"grunt-accessibility": "~5.0.0",
"grunt-chmod": "~1.1.1",
"grunt-contrib-clean": "~1.0.0",
"grunt-contrib-clean": "~1.1.0",
"grunt-contrib-copy": "~1.0.0",
"grunt-eslint": "^19.0.0",
"grunt-exec": "~1.0.1",
"grunt-eslint": "^20.0.0",
"grunt-exec": "~2.0.0",
"grunt-execute": "^0.2.2",
"grunt-jsdoc": "^2.1.0",
"grunt-webpack": "^2.0.1",
"grunt-webpack": "^3.0.2",
"html-webpack-plugin": "^2.29.0",
"imports-loader": "^0.7.1",
"ink-docstrap": "^1.1.4",
"jsdoc-babel": "^0.3.0",
"less": "^2.7.2",
"less-loader": "^4.0.3",
"less-loader": "^4.0.5",
"postcss-css-variables": "^0.7.0",
"postcss-import": "^10.0.0",
"postcss-loader": "^2.0.5",
"style-loader": "^0.15.0",
"style-loader": "^0.18.2",
"url-loader": "^0.5.8",
"value-loader": "^0.1.3",
"web-resource-inliner": "^4.1.0",
"webpack": "^2.2.1",
"webpack-dev-server": "^2.5.0",
"webpack": "^3.3.0",
"worker-loader": "^0.8.0"
},
"dependencies": {
@ -72,15 +72,15 @@
"bootstrap-switch": "^3.3.4",
"crypto-api": "^0.6.2",
"crypto-js": "^3.1.9-1",
"diff": "^3.2.0",
"diff": "^3.3.0",
"escodegen": "^1.8.1",
"esmangle": "^1.0.1",
"esprima": "^3.1.3",
"exif-parser": "^0.1.9",
"esprima": "^4.0.0",
"exif-parser": "^0.1.12",
"google-code-prettify": "^1.0.5",
"jquery": "^3.1.1",
"jsbn": "^1.1.0",
"jsrsasign": "7.1.3",
"jsrsasign": "8.0.3",
"lodash": "^4.17.4",
"moment": "^2.17.1",
"moment-timezone": "^0.5.11",
@ -90,7 +90,7 @@
"vkbeautify": "^0.99.1",
"xmldom": "^0.1.27",
"xpath": "0.0.24",
"zlibjs": "^0.2.0"
"zlibjs": "^0.3.1"
},
"scripts": {
"start": "grunt dev",

View File

@ -73,7 +73,7 @@ Ingredient.prepare = function(data, type) {
case "byteArray":
if (typeof data == "string") {
data = data.replace(/\s+/g, "");
return Utils.hexToByteArray(data);
return Utils.fromHex(data);
} else {
return data;
}

View File

@ -293,7 +293,7 @@ const Utils = {
* Utils.escapeRegex("[example]");
*/
escapeRegex: function(str) {
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
return str.replace(/([.*+?^=!:${}()|[\]/\\])/g, "\\$1");
},
@ -340,50 +340,6 @@ const Utils = {
},
/**
* Translates a hex string into an array of bytes.
*
* @param {string} hexStr
* @returns {byteArray}
*
* @example
* // returns [0xfe, 0x09, 0xa7]
* Utils.hexToByteArray("fe09a7");
*/
hexToByteArray: function(hexStr) {
// TODO: Handle errors i.e. input string is not hex
if (!hexStr) return [];
hexStr = hexStr.replace(/\s+/g, "");
const byteArray = [];
for (let i = 0; i < hexStr.length; i += 2) {
byteArray.push(parseInt(hexStr.substr(i, 2), 16));
}
return byteArray;
},
/**
* Translates an array of bytes to a hex string.
*
* @param {byteArray} byteArray
* @param {string} [delim=" "]
* @returns {string}
*
* @example
* // returns "fe09a7"
* Utils.byteArrayToHex([0xfe, 0x09, 0xa7], "");
*/
byteArrayToHex: function(byteArray, delim) {
if (!byteArray) return "";
delim = typeof delim === "undefined" ? " " : delim;
let hexStr = "";
for (let i = 0; i < byteArray.length; i++) {
hexStr += Utils.hex(byteArray[i]) + delim;
}
return hexStr.slice(0, hexStr.length - delim.length);
},
/**
* Converts a string to a byte array.
* Treats the string as UTF-8 if any values are over 255.
@ -636,7 +592,7 @@ const Utils = {
i = 0;
if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[\[\]\\\-^$]/g, "\\$&") + "]", "g");
const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
data = data.replace(re, "");
}
@ -828,7 +784,7 @@ const Utils = {
if (removeScriptAndStyle) {
htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*<\/(script|style)>/gmi, "");
}
return htmlStr.replace(/<[^>\n]+>/g, "");
return htmlStr.replace(/<[^>]+>/g, "");
},
@ -854,7 +810,7 @@ const Utils = {
"`": "&#x60;"
};
return str.replace(/[&<>"'\/`]/g, function (match) {
return str.replace(/[&<>"'/`]/g, function (match) {
return HTML_CHARS[match];
});
},
@ -946,16 +902,19 @@ const Utils = {
* @returns {html}
*/
displayFilesAsHTML: function(files) {
/* <NL> and <SP> used to denote newlines and spaces in HTML markup.
* If a non-html operation is used, all markup will be removed but these
* whitespace chars will remain for formatting purposes.
*/
const formatDirectory = function(file) {
const html = "<div class='panel panel-default'>" +
"<div class='panel-heading' role='tab'>" +
"<h4 class='panel-title'>" +
Utils.escapeHtml(file.fileName) +
// The following line is for formatting when HTML is stripped
"<span style='display: none'>\n0 bytes\n</span>" +
"</h4>" +
"</div>" +
"</div>";
const html = `<div class='panel panel-default' style='white-space: normal;'>
<div class='panel-heading' role='tab'>
<h4 class='panel-title'>
<NL>${Utils.escapeHtml(file.fileName)}
</h4>
</div>
</div>`;
return html;
};
@ -966,45 +925,52 @@ const Utils = {
);
const blobUrl = URL.createObjectURL(blob);
const downloadAnchorElem = "<a href='" + blobUrl + "' " +
"title='Download " + Utils.escapeHtml(file.fileName) + "' " +
"download='" + Utils.escapeHtml(file.fileName) + "'>\u21B4</a>";
const viewFileElem = `<a href='#collapse${i}'
class='collapsed'
data-toggle='collapse'
aria-expanded='true'
aria-controls='collapse${i}'
title="Show/hide contents of '${Utils.escapeHtml(file.fileName)}'">&#x1f441;&#xfe0f;</a>`;
const expandFileContentsElem = "<a href='#collapse" + i + "' " +
"class='collapsed' " +
"data-toggle='collapse' " +
"aria-expanded='true' " +
"aria-controls='collapse" + i + "' " +
"title=\"Show/hide contents of '" + Utils.escapeHtml(file.fileName) + "'\">&#x1F50D</a>";
const downloadFileElem = `<a href='${blobUrl}'
title='Download ${Utils.escapeHtml(file.fileName)}'
download='${Utils.escapeHtml(file.fileName)}'>&#x1f4be;</a>`;
const html = "<div class='panel panel-default'>" +
"<div class='panel-heading' role='tab' id='heading" + i + "'>" +
"<h4 class='panel-title'>" +
"<div>" +
Utils.escapeHtml(file.fileName) +
" " + expandFileContentsElem +
" " + downloadAnchorElem +
"<span class='pull-right'>" +
// These are for formatting when stripping HTML
"<span style='display: none'>\n</span>" +
file.size.toLocaleString() + " bytes" +
"<span style='display: none'>\n</span>" +
"</span>" +
"</div>" +
"</h4>" +
"</div>" +
"<div id='collapse" + i + "' class='panel-collapse collapse' " +
"role='tabpanel' aria-labelledby='heading" + i + "'>" +
"<div class='panel-body'>" +
"<pre><code>" + Utils.escapeHtml(file.contents) + "</pre></code></div>" +
"</div>" +
"</div>";
const hexFileData = Utils.toHexFast(new Uint8Array(file.bytes));
const switchToInputElem = `<a href='#switchFileToInput${i}'
class='file-switch'
title='Move file to input as hex'
fileValue='${hexFileData}'>&#x21e7;</a>`;
const html = `<div class='panel panel-default' style='white-space: normal;'>
<div class='panel-heading' role='tab' id='heading${i}'>
<h4 class='panel-title'>
<div>
${Utils.escapeHtml(file.fileName)}<NL>
${viewFileElem}<SP>
${downloadFileElem}<SP>
${switchToInputElem}<SP>
<span class='pull-right'>
<NL>${file.size.toLocaleString()} bytes
</span>
</div>
</h4>
</div>
<div id='collapse${i}' class='panel-collapse collapse'
role='tabpanel' aria-labelledby='heading${i}'>
<div class='panel-body'>
<NL><NL><pre><code>${Utils.escapeHtml(file.contents)}</code></pre>
</div>
</div>
</div>`;
return html;
};
let html = "<div style='padding: 5px;'>" +
files.length +
" file(s) found</div>\n";
let html = `<div style='padding: 5px; white-space: normal;'>
${files.length} file(s) found<NL>
</div>`;
files.forEach(function(file, i) {
if (typeof file.contents !== "undefined") {
html += formatFile(file, i);
@ -1012,7 +978,10 @@ const Utils = {
html += formatDirectory(file);
}
});
return html;
return html.replace(/(?:(<pre>(?:\n|.)*<\/pre>)|\s{2,})/g, "$1") // Remove whitespace from markup
.replace(/<NL>/g, "\n") // Replace <NP> with newlines
.replace(/<SP>/g, " "); // Replace <SP> with spaces
},
@ -1031,9 +1000,14 @@ const Utils = {
if (paramStr === "") return {};
// Cut off ? or # and split on &
const params = paramStr.substr(1).split("&");
if (paramStr[0] === "?" ||
paramStr[0] === "#") {
paramStr = paramStr.substr(1);
}
const params = paramStr.split("&");
const result = {};
for (let i = 0; i < params.length; i++) {
const param = params[i].split("=");
if (param.length !== 2) {

View File

@ -46,6 +46,8 @@ const Categories = [
"From Base58",
"To Base",
"From Base",
"To BCD",
"From BCD",
"To HTML Entity",
"From HTML Entity",
"URL Encode",

View File

@ -2,6 +2,7 @@ import FlowControl from "../FlowControl.js";
import Base from "../operations/Base.js";
import Base58 from "../operations/Base58.js";
import Base64 from "../operations/Base64.js";
import BCD from "../operations/BCD.js";
import BitwiseOp from "../operations/BitwiseOp.js";
import ByteRepr from "../operations/ByteRepr.js";
import CharEnc from "../operations/CharEnc.js";
@ -330,30 +331,25 @@ const OperationConfig = {
value: BitwiseOp.XOR_BRUTE_KEY_LENGTH
},
{
name: "Length of sample",
name: "Sample length",
type: "number",
value: BitwiseOp.XOR_BRUTE_SAMPLE_LENGTH
},
{
name: "Offset of sample",
name: "Sample offset",
type: "number",
value: BitwiseOp.XOR_BRUTE_SAMPLE_OFFSET
},
{
name: "Scheme",
type: "option",
value: BitwiseOp.XOR_SCHEME
},
{
name: "Null preserving",
type: "boolean",
value: BitwiseOp.XOR_PRESERVE_NULLS
},
{
name: "Differential",
type: "boolean",
value: BitwiseOp.XOR_DIFFERENTIAL
},
{
name: "Crib (known plaintext string)",
type: "binaryString",
value: ""
},
{
name: "Print key",
type: "boolean",
@ -363,6 +359,11 @@ const OperationConfig = {
name: "Output as hex",
type: "boolean",
value: BitwiseOp.XOR_BRUTE_OUTPUT_HEX
},
{
name: "Crib (known plaintext string)",
type: "binaryString",
value: ""
}
]
},
@ -2301,6 +2302,11 @@ const OperationConfig = {
name: "Output units",
type: "option",
value: DateTime.UNITS
},
{
name: "Input format",
type: "option",
value: DateTime.FILETIME_FORMATS
}
]
},
@ -2314,6 +2320,11 @@ const OperationConfig = {
name: "Input units",
type: "option",
value: DateTime.UNITS
},
{
name: "Output format",
type: "option",
value: DateTime.FILETIME_FORMATS
}
]
},
@ -3497,6 +3508,64 @@ const OperationConfig = {
}
]
},
"From BCD": {
description: "Binary-Coded Decimal (BCD) is a class of binary encodings of decimal numbers where each decimal digit is represented by a fixed number of bits, usually four or eight. Special bit patterns are sometimes used for a sign.",
run: BCD.runFromBCD,
inputType: "string",
outputType: "number",
args: [
{
name: "Scheme",
type: "option",
value: BCD.ENCODING_SCHEME
},
{
name: "Packed",
type: "boolean",
value: true
},
{
name: "Signed",
type: "boolean",
value: false
},
{
name: "Input format",
type: "option",
value: BCD.FORMAT
}
]
},
"To BCD": {
description: "Binary-Coded Decimal (BCD) is a class of binary encodings of decimal numbers where each decimal digit is represented by a fixed number of bits, usually four or eight. Special bit patterns are sometimes used for a sign",
run: BCD.runToBCD,
inputType: "number",
outputType: "string",
args: [
{
name: "Scheme",
type: "option",
value: BCD.ENCODING_SCHEME
},
{
name: "Packed",
type: "boolean",
value: true
},
{
name: "Signed",
type: "boolean",
value: false
},
{
name: "Output format",
type: "option",
value: BCD.FORMAT
}
]
},
};
export default OperationConfig;

214
src/core/operations/BCD.js Executable file
View File

@ -0,0 +1,214 @@
import Utils from "../Utils.js";
/**
* Binary-Coded Decimal operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const BCD = {
/**
* @constant
* @default
*/
ENCODING_SCHEME: [
"8 4 2 1",
"7 4 2 1",
"4 2 2 1",
"2 4 2 1",
"8 4 -2 -1",
"Excess-3",
"IBM 8 4 2 1",
],
/**
* Lookup table for the binary value of each digit representation.
*
* I wrote a very nice algorithm to generate 8 4 2 1 encoding programatically,
* but unfortunately it's much easier (if less elegant) to use lookup tables
* when supporting multiple encoding schemes.
*
* "Practicality beats purity" - PEP 20
*
* In some schemes it is possible to represent the same value in multiple ways.
* For instance, in 4 2 2 1 encoding, 0100 and 0010 both represent 2. Support
* has not yet been added for this.
*
* @constant
*/
ENCODING_LOOKUP: {
"8 4 2 1": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"7 4 2 1": [0, 1, 2, 3, 4, 5, 6, 8, 9, 10],
"4 2 2 1": [0, 1, 4, 5, 8, 9, 12, 13, 14, 15],
"2 4 2 1": [0, 1, 2, 3, 4, 11, 12, 13, 14, 15],
"8 4 -2 -1": [0, 7, 6, 5, 4, 11, 10, 9, 8, 15],
"Excess-3": [3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
"IBM 8 4 2 1": [10, 1, 2, 3, 4, 5, 6, 7, 8, 9],
},
/**
* @default
* @constant
*/
FORMAT: ["Nibbles", "Bytes", "Raw"],
/**
* To BCD operation.
*
* @param {number} input
* @param {Object[]} args
* @returns {string}
*/
runToBCD: function(input, args) {
if (isNaN(input))
return "Invalid input";
if (Math.floor(input) !== input)
return "Fractional values are not supported by BCD";
const encoding = BCD.ENCODING_LOOKUP[args[0]],
packed = args[1],
signed = args[2],
outputFormat = args[3];
// Split input number up into separate digits
const digits = input.toString().split("");
if (digits[0] === "-" || digits[0] === "+") {
digits.shift();
}
let nibbles = [];
digits.forEach(d => {
const n = parseInt(d, 10);
nibbles.push(encoding[n]);
});
if (signed) {
if (packed && digits.length % 2 === 0) {
// If there are an even number of digits, we add a leading 0 so
// that the sign nibble doesn't sit in its own byte, leading to
// ambiguity around whether the number ends with a 0 or not.
nibbles.unshift(encoding[0]);
}
nibbles.push(input > 0 ? 12 : 13);
// 12 ("C") for + (credit)
// 13 ("D") for - (debit)
}
let bytes = [];
if (packed) {
let encoded = 0,
little = false;
nibbles.forEach(n => {
encoded ^= little ? n : (n << 4);
if (little) {
bytes.push(encoded);
encoded = 0;
}
little = !little;
});
if (little) bytes.push(encoded);
} else {
bytes = nibbles;
// Add null high nibbles
nibbles = nibbles.map(n => {
return [0, n];
}).reduce((a, b) => {
return a.concat(b);
});
}
// Output
switch (outputFormat) {
case "Nibbles":
return nibbles.map(n => {
return Utils.padLeft(n.toString(2), 4);
}).join(" ");
case "Bytes":
return bytes.map(b => {
return Utils.padLeft(b.toString(2), 8);
}).join(" ");
case "Raw":
default:
return Utils.byteArrayToChars(bytes);
}
},
/**
* From BCD operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {number}
*/
runFromBCD: function(input, args) {
const encoding = BCD.ENCODING_LOOKUP[args[0]],
packed = args[1],
signed = args[2],
inputFormat = args[3];
let nibbles = [],
output = "",
byteArray;
// Normalise the input
switch (inputFormat) {
case "Nibbles":
case "Bytes":
input = input.replace(/\s/g, "");
for (let i = 0; i < input.length; i += 4) {
nibbles.push(parseInt(input.substr(i, 4), 2));
}
break;
case "Raw":
default:
byteArray = Utils.strToByteArray(input);
byteArray.forEach(b => {
nibbles.push(b >>> 4);
nibbles.push(b & 15);
});
break;
}
if (!packed) {
// Discard each high nibble
for (let i = 0; i < nibbles.length; i++) {
nibbles.splice(i, 1);
}
}
if (signed) {
const sign = nibbles.pop();
if (sign === 13 ||
sign === 11) {
// Negative
output += "-";
}
}
nibbles.forEach(n => {
if (isNaN(n)) throw "Invalid input";
let val = encoding.indexOf(n);
if (val < 0) throw `Value ${Utils.bin(n, 4)} not in encoding scheme`;
output += val.toString();
});
return parseInt(output, 10);
},
};
export default BCD;

View File

@ -221,15 +221,15 @@ const Base64 = {
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hlgreen'>" + offset0.substr(offset0.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset0.substr(offset0.length - 2) + "</span>";
"<span class='hl5'>" + offset0.substr(offset0.length - 3, 1) + "</span>" +
"<span class='hl3'>" + offset0.substr(offset0.length - 2) + "</span>";
} else if (len0 % 4 === 3) {
staticSection = offset0.slice(0, -2);
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -1)) + "'>" +
staticSection + "</span>" +
"<span class='hlgreen'>" + offset0.substr(offset0.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset0.substr(offset0.length - 1) + "</span>";
"<span class='hl5'>" + offset0.substr(offset0.length - 2, 1) + "</span>" +
"<span class='hl3'>" + offset0.substr(offset0.length - 1) + "</span>";
} else {
staticSection = offset0;
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
@ -243,23 +243,23 @@ const Base64 = {
// Highlight offset 1
padding = "<span class='hlred'>" + offset1.substr(0, 1) + "</span>" +
"<span class='hlgreen'>" + offset1.substr(1, 1) + "</span>";
padding = "<span class='hl3'>" + offset1.substr(0, 1) + "</span>" +
"<span class='hl5'>" + offset1.substr(1, 1) + "</span>";
offset1 = offset1.substr(2);
if (len1 % 4 === 2) {
staticSection = offset1.slice(0, -3);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hlgreen'>" + offset1.substr(offset1.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset1.substr(offset1.length - 2) + "</span>";
"<span class='hl5'>" + offset1.substr(offset1.length - 3, 1) + "</span>" +
"<span class='hl3'>" + offset1.substr(offset1.length - 2) + "</span>";
} else if (len1 % 4 === 3) {
staticSection = offset1.slice(0, -2);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -1)) + "'>" +
staticSection + "</span>" +
"<span class='hlgreen'>" + offset1.substr(offset1.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset1.substr(offset1.length - 1) + "</span>";
"<span class='hl5'>" + offset1.substr(offset1.length - 2, 1) + "</span>" +
"<span class='hl3'>" + offset1.substr(offset1.length - 1) + "</span>";
} else {
staticSection = offset1;
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
@ -272,23 +272,23 @@ const Base64 = {
}
// Highlight offset 2
padding = "<span class='hlred'>" + offset2.substr(0, 2) + "</span>" +
"<span class='hlgreen'>" + offset2.substr(2, 1) + "</span>";
padding = "<span class='hl3'>" + offset2.substr(0, 2) + "</span>" +
"<span class='hl5'>" + offset2.substr(2, 1) + "</span>";
offset2 = offset2.substr(3);
if (len2 % 4 === 2) {
staticSection = offset2.slice(0, -3);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hlgreen'>" + offset2.substr(offset2.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset2.substr(offset2.length - 2) + "</span>";
"<span class='hl5'>" + offset2.substr(offset2.length - 3, 1) + "</span>" +
"<span class='hl3'>" + offset2.substr(offset2.length - 2) + "</span>";
} else if (len2 % 4 === 3) {
staticSection = offset2.slice(0, -2);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hlgreen'>" + offset2.substr(offset2.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset2.substr(offset2.length - 1) + "</span>";
"<span class='hl5'>" + offset2.substr(offset2.length - 2, 1) + "</span>" +
"<span class='hl3'>" + offset2.substr(offset2.length - 1) + "</span>";
} else {
staticSection = offset2;
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
@ -300,8 +300,8 @@ const Base64 = {
offset2 = staticSection;
}
return (showVariable ? "Characters highlighted in <span class='hlgreen'>green</span> could change if the input is surrounded by more data." +
"\nCharacters highlighted in <span class='hlred'>red</span> are for padding purposes only." +
return (showVariable ? "Characters highlighted in <span class='hl5'>green</span> could change if the input is surrounded by more data." +
"\nCharacters highlighted in <span class='hl3'>red</span> are for padding purposes only." +
"\nUnhighlighted characters are <span data-toggle='tooltip' data-placement='top' title='Tooltip on left'>static</span>." +
"\nHover over the static sections to see what they decode to on their own.\n" +
"\nOffset 0: " + offset0 +

View File

@ -36,7 +36,9 @@ const BitwiseOp = {
o = input[i];
x = nullPreserving && (o === 0 || o === k) ? o : func(o, k);
result.push(x);
if (scheme !== "Standard" && !(nullPreserving && (o === 0 || o === k))) {
if (scheme &&
scheme !== "Standard" &&
!(nullPreserving && (o === 0 || o === k))) {
switch (scheme) {
case "Input differential":
key[i % key.length] = x;
@ -120,19 +122,19 @@ const BitwiseOp = {
* @returns {string}
*/
runXorBrute: function (input, args) {
let keyLength = parseInt(args[0], 10),
const keyLength = parseInt(args[0], 10),
sampleLength = args[1],
sampleOffset = args[2],
nullPreserving = args[3],
differential = args[4],
crib = args[5],
printKey = args[6],
outputHex = args[7],
regex;
scheme = args[3],
nullPreserving = args[4],
printKey = args[5],
outputHex = args[6],
crib = args[7];
let output = "",
result,
resultUtf8;
resultUtf8,
regex;
input = input.slice(sampleOffset, sampleOffset + sampleLength);
@ -142,14 +144,12 @@ const BitwiseOp = {
for (let key = 1, l = Math.pow(256, keyLength); key < l; key++) {
result = BitwiseOp._bitOp(input, Utils.hexToByteArray(key.toString(16)), BitwiseOp._xor, nullPreserving, differential);
result = BitwiseOp._bitOp(input, Utils.fromHex(key.toString(16)), BitwiseOp._xor, nullPreserving, scheme);
resultUtf8 = Utils.byteArrayToUtf8(result);
if (crib !== "" && resultUtf8.search(regex) === -1) continue;
if (printKey) output += "Key = " + Utils.hex(key, (2*keyLength)) + ": ";
if (outputHex)
output += Utils.byteArrayToHex(result) + "\n";
else
output += Utils.printable(resultUtf8, false) + "\n";
if (outputHex) output += Utils.toHex(result) + "\n";
else output += Utils.printable(resultUtf8, false) + "\n";
if (printKey) output += "\n";
}
return output;

View File

@ -89,8 +89,17 @@ const DateTime = {
* @returns {string}
*/
runFromFiletimeToUnix: function(input, args) {
let units = args[0];
input = new BigInteger(input).subtract(new BigInteger("116444736000000000"));
let units = args[0],
format = args[1];
if (format === "Hex") {
input = new BigInteger(input, 16);
} else {
input = new BigInteger(input);
}
input = input.subtract(new BigInteger("116444736000000000"));
if (units === "Seconds (s)"){
input = input.divide(new BigInteger("10000000"));
} else if (units === "Milliseconds (ms)") {
@ -102,6 +111,7 @@ const DateTime = {
} else {
throw "Unrecognised unit";
}
return input.toString();
},
@ -115,8 +125,11 @@ const DateTime = {
* @returns {string}
*/
runToFiletimeFromUnix: function(input, args) {
let units = args[0];
let units = args[0],
format = args[1];
input = new BigInteger(input);
if (units === "Seconds (s)"){
input = input.multiply(new BigInteger("10000000"));
} else if (units === "Milliseconds (ms)") {
@ -128,10 +141,24 @@ const DateTime = {
} else {
throw "Unrecognised unit";
}
return input.add(new BigInteger("116444736000000000")).toString();
input = input.add(new BigInteger("116444736000000000"));
if (format === "Hex"){
return input.toString(16);
} else {
return input.toString();
}
},
/**
* @constant
* @default
*/
FILETIME_FORMATS: ["Decimal", "Hex"],
/**
* @constant
* @default

View File

@ -144,7 +144,8 @@ const HTML = {
}
if (removeLineBreaks) {
input = input.replace(/^\s*\n/, "") // first line
input = input
.replace(/^\s*\n/, "") // first line
.replace(/(\n\s*){2,}/g, "\n"); // all others
}

View File

@ -283,7 +283,7 @@ const IP = {
baIp.push(decimal & 255);
break;
case "Hex":
baIp = Utils.hexToByteArray(lines[i]);
baIp = Utils.fromHex(lines[i]);
break;
default:
throw "Unsupported input IP format";
@ -516,7 +516,7 @@ const IP = {
"<tr><td>Destination IP address</td><td>" + IP._ipv4ToStr(dstIP) + "</td></tr>";
if (ihl > 5) {
output += "<tr><td>Options</td><td>" + Utils.byteArrayToHex(options) + "</td></tr>";
output += "<tr><td>Options</td><td>" + Utils.toHex(options) + "</td></tr>";
}
return output + "</table>";

View File

@ -1,4 +1,4 @@
import esprima from "esprima";
import * as esprima from "esprima";
import escodegen from "escodegen";
import esmangle from "esmangle";
@ -62,7 +62,7 @@ const JS = {
tolerant: parseTolerant
};
result = esprima.parse(input, options);
result = esprima.parseScript(input, options);
return JSON.stringify(result, null, 2);
},
@ -104,7 +104,7 @@ const JS = {
AST;
try {
AST = esprima.parse(input, {
AST = esprima.parseScript(input, {
range: true,
tokens: true,
comment: true
@ -142,7 +142,7 @@ const JS = {
*/
runMinify: function(input, args) {
let result = "",
AST = esprima.parse(input),
AST = esprima.parseScript(input),
optimisedAST = esmangle.optimize(AST, null),
mangledAST = esmangle.mangle(optimisedAST);

View File

@ -27,52 +27,49 @@ const PublicKey = {
* @returns {string}
*/
runParseX509: function (input, args) {
let cert = new r.X509(),
inputFormat = args[0];
if (!input.length) {
return "No input";
}
let cert = new r.X509(),
inputFormat = args[0];
switch (inputFormat) {
case "DER Hex":
input = input.replace(/\s/g, "");
cert.hex = input;
cert.pem = r.KJUR.asn1.ASN1Util.getPEMStringFromHex(input, "CERTIFICATE");
cert.readCertHex(input);
break;
case "PEM":
cert.hex = r.X509.pemToHex(input);
cert.pem = input;
cert.readCertPEM(input);
break;
case "Base64":
cert.hex = Utils.toHex(Utils.fromBase64(input, null, "byteArray"), "");
cert.pem = r.KJUR.asn1.ASN1Util.getPEMStringFromHex(cert.hex, "CERTIFICATE");
cert.readCertHex(Utils.toHex(Utils.fromBase64(input, null, "byteArray"), ""));
break;
case "Raw":
cert.hex = Utils.toHex(Utils.strToByteArray(input), "");
cert.pem = r.KJUR.asn1.ASN1Util.getPEMStringFromHex(cert.hex, "CERTIFICATE");
cert.readCertHex(Utils.toHex(Utils.strToByteArray(input), ""));
break;
default:
throw "Undefined input format";
}
let version = r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [0, 0, 0]),
sn = cert.getSerialNumberHex(),
algorithm = r.KJUR.asn1.x509.OID.oid2name(r.KJUR.asn1.ASN1Util.oidHexToInt(r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [0, 2, 0]))),
let sn = cert.getSerialNumberHex(),
issuer = cert.getIssuerString(),
notBefore = cert.getNotBefore(),
notAfter = cert.getNotAfter(),
subject = cert.getSubjectString(),
pkAlgorithm = r.KJUR.asn1.x509.OID.oid2name(r.KJUR.asn1.ASN1Util.oidHexToInt(r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [0, 6, 0, 0]))),
pk = r.X509.getPublicKeyFromCertPEM(cert.pem),
pk = cert.getPublicKey(),
pkFields = [],
pkStr = "",
certSigAlg = r.KJUR.asn1.x509.OID.oid2name(r.KJUR.asn1.ASN1Util.oidHexToInt(r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [1, 0]))),
certSig = r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [2]).substr(2),
sig = cert.getSignatureValueHex(),
sigStr = "",
extensions = r.ASN1HEX.dump(r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [0, 7]));
extensions = cert.getInfo().split("X509v3 Extensions:\n")[1].split("signature")[0];
window.cert = cert;
window.r = r;
// Public Key fields
pkFields.push({
key: "Algorithm",
value: pk.type
});
if (pk.type === "EC") { // ECDSA
pkFields.push({
key: "Curve Name",
@ -123,21 +120,6 @@ const PublicKey = {
});
}
// Signature fields
let breakoutSig = false;
try {
breakoutSig = r.ASN1HEX.dump(certSig).indexOf("SEQUENCE") === 0;
} catch (err) {
// Error processing signature, output without further breakout
}
if (breakoutSig) { // DSA or ECDSA
sigStr = " r: " + PublicKey._formatByteStr(r.ASN1HEX.getDecendantHexVByNthList(certSig, 0, [0]), 16, 18) + "\n" +
" s: " + PublicKey._formatByteStr(r.ASN1HEX.getDecendantHexVByNthList(certSig, 0, [1]), 16, 18) + "\n";
} else { // RSA or unknown
sigStr = " Signature: " + PublicKey._formatByteStr(certSig, 16, 18) + "\n";
}
// Format Public Key fields
for (let i = 0; i < pkFields.length; i++) {
pkStr += " " + pkFields[i].key + ":" +
@ -148,31 +130,45 @@ const PublicKey = {
);
}
// Signature fields
let breakoutSig = false;
try {
breakoutSig = r.ASN1HEX.dump(sig).indexOf("SEQUENCE") === 0;
} catch (err) {
// Error processing signature, output without further breakout
}
if (breakoutSig) { // DSA or ECDSA
sigStr = " r: " + PublicKey._formatByteStr(r.ASN1HEX.getV(sig, 4), 16, 18) + "\n" +
" s: " + PublicKey._formatByteStr(r.ASN1HEX.getV(sig, 48), 16, 18);
} else { // RSA or unknown
sigStr = " Signature: " + PublicKey._formatByteStr(sig, 16, 18);
}
let issuerStr = PublicKey._formatDnStr(issuer, 2),
nbDate = PublicKey._formatDate(notBefore),
naDate = PublicKey._formatDate(notAfter),
nbDate = PublicKey._formatDate(cert.getNotBefore()),
naDate = PublicKey._formatDate(cert.getNotAfter()),
subjectStr = PublicKey._formatDnStr(subject, 2);
const output = "Version: " + (parseInt(version, 16) + 1) + " (0x" + version + ")\n" +
"Serial number: " + new r.BigInteger(sn, 16).toString() + " (0x" + sn + ")\n" +
"Algorithm ID: " + algorithm + "\n" +
"Validity\n" +
" Not Before: " + nbDate + " (dd-mm-yy hh:mm:ss) (" + notBefore + ")\n" +
" Not After: " + naDate + " (dd-mm-yy hh:mm:ss) (" + notAfter + ")\n" +
"Issuer\n" +
issuerStr +
"Subject\n" +
subjectStr +
"Public Key\n" +
" Algorithm: " + pkAlgorithm + "\n" +
pkStr +
"Certificate Signature\n" +
" Algorithm: " + certSigAlg + "\n" +
sigStr +
"\nExtensions (parsed ASN.1)\n" +
extensions;
return `Version: ${cert.version} (0x${Utils.hex(cert.version - 1)})
Serial number: ${new r.BigInteger(sn, 16).toString()} (0x${sn})
Algorithm ID: ${cert.getSignatureAlgorithmField()}
Validity
Not Before: ${nbDate} (dd-mm-yy hh:mm:ss) (${cert.getNotBefore()})
Not After: ${naDate} (dd-mm-yy hh:mm:ss) (${cert.getNotAfter()})
Issuer
${issuerStr}
Subject
${subjectStr}
Public Key
${pkStr.slice(0, -1)}
Certificate Signature
Algorithm: ${cert.getSignatureAlgorithmName()}
${sigStr}
return output;
Extensions
${extensions}`;
},
@ -192,7 +188,9 @@ const PublicKey = {
// Add footer so that the KEYUTIL function works
input = input + "-----END CERTIFICATE-----";
}
return r.KEYUTIL.getHexFromPEM(input);
let cert = new r.X509();
cert.readCertPEM(input);
return cert.hex;
},
@ -270,7 +268,7 @@ const PublicKey = {
*/
_formatDnStr: function(dnStr, indent) {
let output = "",
fields = dnStr.split(",/|"),
fields = dnStr.substr(1).replace(/([^\\])\//g, "$1$1/").split(/[^\\]\//),
maxKeyLen = 0,
key,
value,
@ -295,7 +293,7 @@ const PublicKey = {
output += Utils.padLeft(str, indent + str.length, " ");
}
return output;
return output.slice(0, -1);
},
@ -347,24 +345,6 @@ const PublicKey = {
export default PublicKey;
/**
* Overwrite X509.hex2dn function so as to join RDNs with a string which can be split on without
* causing problems later (I hope).
*
* @param {string} hDN - Hex DN string
* @returns {string}
*/
r.X509.hex2dn = function(hDN) {
let s = "";
const a = r.ASN1HEX.getPosArrayOfChildren_AtObj(hDN, 0);
for (let i = 0; i < a.length; i++) {
const hRDN = r.ASN1HEX.getHexOfTLV_AtObj(hDN, a[i]);
s = s + ",/|" + r.X509.hex2rdn(hRDN);
}
return s;
};
/**
* Overwrite DN attribute lookup in jsrasign library with a much more complete version from
* https://github.com/nfephp-org/nfephp/blob/master/libs/Common/Certificate/Oids.php

View File

@ -61,7 +61,7 @@ const QuotedPrintable = {
* @returns {byteArray}
*/
runFrom: function (input, args) {
const str = input.replace(/\=(?:\r?\n|$)/g, "");
const str = input.replace(/=(?:\r?\n|$)/g, "");
return QuotedPrintable.mimeDecode(str);
},
@ -73,7 +73,7 @@ const QuotedPrintable = {
* @returns {byteArray}
*/
mimeDecode: function(str) {
let encodedBytesCount = (str.match(/\=[\da-fA-F]{2}/g) || []).length,
let encodedBytesCount = (str.match(/=[\da-fA-F]{2}/g) || []).length,
bufferLength = str.length - encodedBytesCount * 2,
chr, hex,
buffer = new Array(bufferLength),
@ -219,21 +219,21 @@ const QuotedPrintable = {
result += line;
pos += line.length;
continue;
} else if (line.length > lineLengthMax - lineMargin && (match = line.substr(-lineMargin).match(/[ \t\.,!\?][^ \t\.,!\?]*$/))) {
} else if (line.length > lineLengthMax - lineMargin && (match = line.substr(-lineMargin).match(/[ \t.,!?][^ \t.,!?]*$/))) {
// truncate to nearest space
line = line.substr(0, line.length - (match[0].length - 1));
} else if (line.substr(-1) === "\r") {
line = line.substr(0, line.length - 1);
} else {
if (line.match(/\=[\da-f]{0,2}$/i)) {
if (line.match(/=[\da-f]{0,2}$/i)) {
// push incomplete encoding sequences to the next line
if ((match = line.match(/\=[\da-f]{0,1}$/i))) {
if ((match = line.match(/=[\da-f]{0,1}$/i))) {
line = line.substr(0, line.length - match[0].length);
}
// ensure that utf-8 sequences are not split
while (line.length > 3 && line.length < len - pos && !line.match(/^(?:=[\da-f]{2}){1,4}$/i) && (match = line.match(/\=[\da-f]{2}$/ig))) {
while (line.length > 3 && line.length < len - pos && !line.match(/^(?:=[\da-f]{2}){1,4}$/i) && (match = line.match(/=[\da-f]{2}$/ig))) {
code = parseInt(match[0].substr(1, 2), 16);
if (code < 128) {
break;
@ -250,7 +250,7 @@ const QuotedPrintable = {
}
if (pos + line.length < len && line.substr(-1) !== "\n") {
if (line.length === 76 && line.match(/\=[\da-f]{2}$/i)) {
if (line.length === 76 && line.match(/=[\da-f]{2}$/i)) {
line = line.substr(0, line.length - 3);
} else if (line.length === 76) {
line = line.substr(0, line.length - 1);

View File

@ -359,9 +359,9 @@ const StrUtils = {
for (let i = 0; i < diff.length; i++) {
if (diff[i].added) {
if (showAdded) output += "<span class='hlgreen'>" + Utils.escapeHtml(diff[i].value) + "</span>";
if (showAdded) output += "<span class='hl5'>" + Utils.escapeHtml(diff[i].value) + "</span>";
} else if (diff[i].removed) {
if (showRemoved) output += "<span class='hlred'>" + Utils.escapeHtml(diff[i].value) + "</span>";
if (showRemoved) output += "<span class='hl3'>" + Utils.escapeHtml(diff[i].value) + "</span>";
} else {
output += Utils.escapeHtml(diff[i].value);
}
@ -424,7 +424,7 @@ const StrUtils = {
}
if (match && !inMatch) {
outputs[s] += "<span class='hlgreen'>" + Utils.escapeHtml(samples[s][i]);
outputs[s] += "<span class='hl5'>" + Utils.escapeHtml(samples[s][i]);
if (samples[s].length === i + 1) outputs[s] += "</span>";
if (s === samples.length - 1) inMatch = true;
} else if (!match && inMatch) {

View File

@ -127,7 +127,7 @@ const URL_ = {
.replace(/\(/g, "%28")
.replace(/\)/g, "%29")
.replace(/\*/g, "%2A")
.replace(/\-/g, "%2D")
.replace(/-/g, "%2D")
.replace(/\./g, "%2E")
.replace(/_/g, "%5F")
.replace(/~/g, "%7E");

View File

@ -266,13 +266,7 @@ App.prototype.silentBake = function() {
* @returns {string}
*/
App.prototype.getInput = function() {
const input = this.manager.input.get();
// Save to session storage in case we need to restore it later
sessionStorage.setItem("inputLength", input.length);
sessionStorage.setItem("input", input);
return input;
return this.manager.input.get();
};
@ -282,8 +276,6 @@ App.prototype.getInput = function() {
* @param {string} input - The string to set the input to
*/
App.prototype.setInput = function(input) {
sessionStorage.setItem("inputLength", input.length);
sessionStorage.setItem("input", input);
this.manager.input.set(input);
};
@ -466,7 +458,12 @@ App.prototype.addFavourite = function(name) {
*/
App.prototype.loadURIParams = function() {
// Load query string or hash from URI (depending on which is populated)
const params = window.location.search || window.location.hash;
// We prefer getting the hash by splitting the href rather than referencing
// location.hash as some browsers (Firefox) automatically URL decode it,
// which cause issues.
const params = window.location.search ||
window.location.href.split("#")[1] ||
window.location.hash;
this.uriParams = Utils.parseURIParams(params);
// Pause auto-bake while loading but don't modify `this.autoBake_`
@ -529,9 +526,7 @@ App.prototype.nextIngId = function() {
* @returns {Object[]}
*/
App.prototype.getRecipeConfig = function() {
const recipeConfig = this.manager.recipe.getConfig();
sessionStorage.setItem("recipeConfig", JSON.stringify(recipeConfig));
return recipeConfig;
return this.manager.recipe.getConfig();
};
@ -541,7 +536,6 @@ App.prototype.getRecipeConfig = function() {
* @param {Object[]} recipeConfig - The recipe configuration
*/
App.prototype.setRecipeConfig = function(recipeConfig) {
sessionStorage.setItem("recipeConfig", JSON.stringify(recipeConfig));
document.getElementById("rec-list").innerHTML = null;
for (let i = 0; i < recipeConfig.length; i++) {
@ -596,15 +590,24 @@ App.prototype.resetLayout = function() {
App.prototype.setCompileMessage = function() {
// Display time since last build and compile message
let now = new Date(),
timeSinceCompile = Utils.fuzzyTime(now.getTime() - window.compileTime),
compileInfo = "<span style=\"font-weight: normal\">Last build: " +
timeSinceCompile.substr(0, 1).toUpperCase() + timeSinceCompile.substr(1) + " ago";
timeSinceCompile = Utils.fuzzyTime(now.getTime() - window.compileTime);
// Calculate previous version to compare to
let prev = PKG_VERSION.split(".").map(n => {
return parseInt(n, 10);
});
if (prev[2] > 0) prev[2]--;
else if (prev[1] > 0) prev[1]--;
else prev[0]--;
const compareURL = `https://github.com/gchq/CyberChef/compare/v${prev.join(".")}...v${PKG_VERSION}`;
let compileInfo = `<a href='${compareURL}'>Last build: ${timeSinceCompile.substr(0, 1).toUpperCase() + timeSinceCompile.substr(1)} ago</a>`;
if (window.compileMessage !== "") {
compileInfo += " - " + window.compileMessage;
}
compileInfo += "</span>";
document.getElementById("notice").innerHTML = compileInfo;
};
@ -729,10 +732,20 @@ App.prototype.alertCloseClick = function() {
App.prototype.stateChange = function(e) {
this.autoBake();
// Set title
const recipeConfig = this.getRecipeConfig();
let title = "CyberChef";
if (recipeConfig.length === 1) {
title = `${recipeConfig[0].op} - ${title}`;
} else if (recipeConfig.length > 1) {
title = `${recipeConfig.length} operations - ${title}`;
}
document.title = title;
// Update the current history state (not creating a new one)
if (this.options.updateUrl) {
this.lastStateUrl = this.manager.controls.generateStateUrl(true, true);
window.history.replaceState({}, "CyberChef", this.lastStateUrl);
this.lastStateUrl = this.manager.controls.generateStateUrl(true, true, recipeConfig);
window.history.replaceState({}, title, this.lastStateUrl);
}
};

View File

@ -493,7 +493,8 @@ HighlighterWaiter.prototype.highlight = function(textarea, highlighter, pos) {
//if (colour) cssClass += "-"+colour;
// Remove HTML tags
text = text.replace(/&/g, "&amp;")
text = text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/\n/g, "&#10;")

View File

@ -145,6 +145,7 @@ Manager.prototype.initialiseEventListeners = function() {
document.getElementById("output-html").addEventListener("mousemove", this.highlighter.outputHtmlMousemove.bind(this.highlighter));
this.addMultiEventListener("#output-text", "mousedown dblclick select", this.highlighter.outputMousedown, this.highlighter);
this.addMultiEventListener("#output-html", "mousedown dblclick select", this.highlighter.outputHtmlMousedown, this.highlighter);
this.addDynamicListener(".file-switch", "click", this.output.fileSwitch, this.output);
// Options
document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.options));

View File

@ -167,6 +167,17 @@ OutputWaiter.prototype.undoSwitchClick = function() {
document.getElementById("undo-switch").disabled = true;
};
/**
* Handler for file switch click events.
* Moves a files data for items created via Utils.displayFilesAsHTML to the input.
*/
OutputWaiter.prototype.fileSwitch = function(e) {
e.preventDefault();
this.switchOrigData = this.manager.input.get();
this.app.setInput(e.target.getAttribute("fileValue"));
document.getElementById("undo-switch").disabled = false;
};
/**
* Handler for maximise output click events.

View File

@ -26,12 +26,14 @@
<title>CyberChef</title>
<meta name="copyright" content="Crown Copyright 2016" />
<meta name="description" content="The Cyber Swiss Army Knife" />
<meta name="description" content="The Cyber Swiss Army Knife - a web app for encryption, encoding, compression and data analysis" />
<meta name="keywords" content="base64, hex, decode, encode, encrypt, decrypt, compress, decompress, regex, regular expressions, hash, crypt, hexadecimal, user agent, url, certificate, x.509, parser, JSON, gzip, md5, sha1, aes, des, blowfish, xor" />
<link rel="icon" type="image/ico" href="<%- require('../static/images/favicon.ico') %>" />
<script type="application/javascript">
"use strict";
// Load theme before the preloader is shown
document.querySelector(":root").className = (JSON.parse(localStorage.getItem("options")) || {}).theme;
@ -72,6 +74,11 @@
changeLoadingMsg();
window.loadingMsgsInt = setInterval(changeLoadingMsg, (Math.random() * 1000) + 1000);
</script>
<% if (!htmlWebpackPlugin.options.inline) { %>
<script type="application/ld+json">
<% print(JSON.stringify(require("../static/structuredData.json"))); %>
</script>
<% } %>
</head>
<body>
<!-- Preloader overlay -->
@ -87,11 +94,14 @@
</div>
<div id="content-wrapper">
<div id="banner">
<div class="col-md-4" style="text-align: left; padding-left: 10px;">
<% if (htmlWebpackPlugin.options.inline) { %>
<span style="float: left; margin-left: 10px;">Compile time: <%= htmlWebpackPlugin.options.compileTime %></span>
<span>Version <%= htmlWebpackPlugin.options.version %></span>
<% } else { %>
<a href="cyberchef.htm" style="float: left; margin-left: 10px; margin-right: 80px;" download>Download CyberChef<img aria-hidden="true" src="<%- require('../static/images/download-24x24.png') %>" alt="Download Icon"/></a>
<a href="cyberchef.htm" download>Download CyberChef<img aria-hidden="true" src="<%- require('../static/images/download-24x24.png') %>" alt="Download Icon"/></a>
<% } %>
</div>
<div class="col-md-4" style="text-align: center;">
<span id="notice">
<script type="text/javascript">
// Must be text/javascript rather than application/javascript otherwise IE won't recognise it...
@ -102,8 +112,11 @@
</script>
<noscript>JavaScript is not enabled. Good luck.</noscript>
</span>
<a href="#" id="support" class="banner-right" data-toggle="modal" data-target="#support-modal">About / Support<img aria-hidden="true" src="<%- require('../static/images/help-22x22.png') %>" alt="Question Mark Icon"/></a>
<a href="#" id="options" class="banner-right">Options<img aria-hidden="true" src="<%- require('../static/images/settings-22x22.png') %>" alt="Settings Icon"/></a>
</div>
<div class="col-md-4" style="text-align: right; padding-right: 0;">
<a href="#" id="options">Options<img aria-hidden="true" src="<%- require('../static/images/settings-22x22.png') %>" alt="Settings Icon"/></a>
<a href="#" id="support" data-toggle="modal" data-target="#support-modal">About / Support<img aria-hidden="true" src="<%- require('../static/images/help-22x22.png') %>" alt="Question Mark Icon"/></a>
</div>
</div>
<div id="workspace-wrapper">
<div id="operations" class="split split-horizontal no-select">
@ -266,32 +279,32 @@
<label for="theme"> Theme (only supported in modern browsers)</label>
</div>
<div class="option-item">
<input type="checkbox" option="update_url" id="update_url" checked />
<label for="update_url"> Update the URL when the input or recipe changes </label>
<input type="checkbox" option="updateUrl" id="updateUrl" checked />
<label for="updateUrl"> Update the URL when the input or recipe changes </label>
</div>
<div class="option-item">
<input type="checkbox" option="show_highlighter" id="show_highlighter" checked />
<label for="show_highlighter"> Highlight selected bytes in output and input (when possible) </label>
<input type="checkbox" option="showHighlighter" id="showHighlighter" checked />
<label for="showHighlighter"> Highlight selected bytes in output and input (when possible) </label>
</div>
<div class="option-item">
<input type="checkbox" option="treat_as_utf8" id="treat_as_utf8" checked />
<label for="treat_as_utf8"> Treat output as UTF-8 if possible </label>
<input type="checkbox" option="treatAsUtf8" id="treatAsUtf8" checked />
<label for="treatAsUtf8"> Treat output as UTF-8 if possible </label>
</div>
<div class="option-item">
<input type="checkbox" option="word_wrap" id="word_wrap" checked />
<label for="word_wrap"> Word wrap the input and output </label>
<input type="checkbox" option="wordWrap" id="wordWrap" checked />
<label for="wordWrap"> Word wrap the input and output </label>
</div>
<div class="option-item">
<input type="checkbox" option="show_errors" id="show_errors" checked />
<label for="show_errors"> Operation error reporting (recommended) </label>
<input type="checkbox" option="showErrors" id="showErrors" checked />
<label for="showErrors"> Operation error reporting (recommended) </label>
</div>
<div class="option-item">
<input type="number" option="error_timeout" id="error_timeout" />
<label for="error_timeout"> Operation error timeout in ms (0 for never) </label>
<input type="number" option="errorTimeout" id="errorTimeout" />
<label for="errorTimeout"> Operation error timeout in ms (0 for never) </label>
</div>
<div class="option-item">
<input type="number" option="auto_bake_threshold" id="auto_bake_threshold"/>
<label for="auto_bake_threshold"> Auto Bake threshold in ms </label>
<input type="number" option="autoBakeThreshold" id="autoBakeThreshold"/>
<label for="autoBakeThreshold"> Auto Bake threshold in ms </label>
</div>
</div>
<div class="modal-footer">

View File

@ -0,0 +1,23 @@
[
{
"@context": "http://schema.org",
"@type": "Organization",
"url": "https://gchq.github.io/CyberChef/",
"logo": "https://gchq.github.io/CyberChef/images/cyberchef-128x128.png",
"sameAs": [
"https://github.com/gchq/CyberChef",
"https://www.npmjs.com/package/cyberchef"
]
},
{
"@context": "http://schema.org",
"@type": "WebSite",
"url": "https://gchq.github.io/CyberChef/",
"name": "CyberChef",
"potentialAction": {
"@type": "SearchAction",
"target": "https://gchq.github.io/CyberChef/?op={operation_search_term}",
"query-input": "required name=operation_search_term"
}
}
]

View File

@ -59,6 +59,7 @@
background-color: var(--arg-background);
border: 1px solid var(--arg-border-colour);
font-family: var(--fixed-width-font-family);
text-overflow: ellipsis;
}
.short-string {

View File

@ -10,19 +10,15 @@
position: absolute;
height: 30px;
width: 100%;
text-align: center;
line-height: 30px;
border-bottom: 1px solid var(--primary-border-colour);
color: var(--banner-font-colour);
background-color: var(--banner-bg-colour);
}
.banner-right {
float: right;
margin-right: 10px;
}
#banner img {
margin-bottom: 2px;
margin-left: 8px;
padding-right: 10px;
}

View File

@ -46,8 +46,7 @@ import Chef from "../src/core/Chef.js";
{},
0,
false
)
.then(function(result) {
).then(function(result) {
const ret = {
test: test,
status: null,

View File

@ -12,6 +12,7 @@ import "babel-polyfill";
import TestRegister from "./TestRegister.js";
import "./tests/operations/Base58.js";
import "./tests/operations/BCD.js";
import "./tests/operations/ByteRepr.js";
import "./tests/operations/CharEnc.js";
import "./tests/operations/Cipher.js";

View File

@ -0,0 +1,103 @@
/**
* BCD tests
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import TestRegister from "../../TestRegister.js";
TestRegister.addTests([
{
name: "To BCD: default 0",
input: "0",
expectedOutput: "0000",
recipeConfig: [
{
"op": "To BCD",
"args": ["8 4 2 1", true, false, "Nibbles"]
}
]
},
{
name: "To BCD: unpacked nibbles",
input: "1234567890",
expectedOutput: "0000 0001 0000 0010 0000 0011 0000 0100 0000 0101 0000 0110 0000 0111 0000 1000 0000 1001 0000 0000",
recipeConfig: [
{
"op": "To BCD",
"args": ["8 4 2 1", false, false, "Nibbles"]
}
]
},
{
name: "To BCD: packed, signed bytes",
input: "1234567890",
expectedOutput: "00000001 00100011 01000101 01100111 10001001 00001100",
recipeConfig: [
{
"op": "To BCD",
"args": ["8 4 2 1", true, true, "Bytes"]
}
]
},
{
name: "To BCD: packed, signed nibbles, 8 4 -2 -1",
input: "-1234567890",
expectedOutput: "0000 0111 0110 0101 0100 1011 1010 1001 1000 1111 0000 1101",
recipeConfig: [
{
"op": "To BCD",
"args": ["8 4 -2 -1", true, true, "Nibbles"]
}
]
},
{
name: "From BCD: default 0",
input: "0000",
expectedOutput: "0",
recipeConfig: [
{
"op": "From BCD",
"args": ["8 4 2 1", true, false, "Nibbles"]
}
]
},
{
name: "From BCD: packed, signed bytes",
input: "00000001 00100011 01000101 01100111 10001001 00001101",
expectedOutput: "-1234567890",
recipeConfig: [
{
"op": "From BCD",
"args": ["8 4 2 1", true, true, "Bytes"]
}
]
},
{
name: "From BCD: Excess-3, unpacked, unsigned",
input: "00000100 00000101 00000110 00000111 00001000 00001001 00001010 00001011 00001100 00000011",
expectedOutput: "1234567890",
recipeConfig: [
{
"op": "From BCD",
"args": ["Excess-3", false, false, "Nibbles"]
}
]
},
{
name: "BCD: raw 4 2 2 1, packed, signed",
input: "1234567890",
expectedOutput: "1234567890",
recipeConfig: [
{
"op": "To BCD",
"args": ["4 2 2 1", true, true, "Raw"]
},
{
"op": "From BCD",
"args": ["4 2 2 1", true, true, "Raw"]
}
]
},
]);

View File

@ -16,7 +16,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "Windows Filetime to UNIX Timestamp",
args: ["Nanoseconds (ns)"],
args: ["Nanoseconds (ns)", "Decimal"],
},
],
},
@ -27,7 +27,7 @@ TestRegister.addTests([
recipeConfig: [
{
op: "UNIX Timestamp to Windows Filetime",
args: ["Nanoseconds (ns)"],
args: ["Nanoseconds (ns)", "Decimal"],
},
],
},

View File

@ -73,11 +73,11 @@ TestRegister.addTests([
"",
"Make: SONY",
"Model: DSC-H5",
"XResolution: 72",
"YResolution: 72",
"XResolution: 70",
"YResolution: 70",
"ResolutionUnit: 2",
"Software: Pictomio 1.2.31.0",
"ModifyDate: 2010:07:04 23:31:13",
"ModifyDate: 1278286273",
"ExposureTime: 0.008",
"FNumber: 3.7",
"ExposureProgram: 3",

View File

@ -26,7 +26,7 @@ TestRegister.addTests([
{
name: "Diff, basic usage",
input: "testing23\n\ntesting123",
expectedOutput: "testing<span class='hlgreen'>1</span>23",
expectedOutput: "testing<span class='hl5'>1</span>23",
recipeConfig: [
{
"op": "Diff",