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

View file

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

View file

@ -293,7 +293,7 @@ const Utils = {
* Utils.escapeRegex("[example]"); * Utils.escapeRegex("[example]");
*/ */
escapeRegex: function(str) { 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. * Converts a string to a byte array.
* Treats the string as UTF-8 if any values are over 255. * Treats the string as UTF-8 if any values are over 255.
@ -636,7 +592,7 @@ const Utils = {
i = 0; i = 0;
if (removeNonAlphChars) { if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[\[\]\\\-^$]/g, "\\$&") + "]", "g"); const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
data = data.replace(re, ""); data = data.replace(re, "");
} }
@ -828,7 +784,7 @@ const Utils = {
if (removeScriptAndStyle) { if (removeScriptAndStyle) {
htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*<\/(script|style)>/gmi, ""); htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*<\/(script|style)>/gmi, "");
} }
return htmlStr.replace(/<[^>\n]+>/g, ""); return htmlStr.replace(/<[^>]+>/g, "");
}, },
@ -854,7 +810,7 @@ const Utils = {
"`": "&#x60;" "`": "&#x60;"
}; };
return str.replace(/[&<>"'\/`]/g, function (match) { return str.replace(/[&<>"'/`]/g, function (match) {
return HTML_CHARS[match]; return HTML_CHARS[match];
}); });
}, },
@ -946,16 +902,19 @@ const Utils = {
* @returns {html} * @returns {html}
*/ */
displayFilesAsHTML: function(files) { 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 formatDirectory = function(file) {
const html = "<div class='panel panel-default'>" + const html = `<div class='panel panel-default' style='white-space: normal;'>
"<div class='panel-heading' role='tab'>" + <div class='panel-heading' role='tab'>
"<h4 class='panel-title'>" + <h4 class='panel-title'>
Utils.escapeHtml(file.fileName) + <NL>${Utils.escapeHtml(file.fileName)}
// The following line is for formatting when HTML is stripped </h4>
"<span style='display: none'>\n0 bytes\n</span>" + </div>
"</h4>" + </div>`;
"</div>" +
"</div>";
return html; return html;
}; };
@ -966,45 +925,52 @@ const Utils = {
); );
const blobUrl = URL.createObjectURL(blob); const blobUrl = URL.createObjectURL(blob);
const downloadAnchorElem = "<a href='" + blobUrl + "' " + const viewFileElem = `<a href='#collapse${i}'
"title='Download " + Utils.escapeHtml(file.fileName) + "' " + class='collapsed'
"download='" + Utils.escapeHtml(file.fileName) + "'>\u21B4</a>"; 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 + "' " + const downloadFileElem = `<a href='${blobUrl}'
"class='collapsed' " + title='Download ${Utils.escapeHtml(file.fileName)}'
"data-toggle='collapse' " + download='${Utils.escapeHtml(file.fileName)}'>&#x1f4be;</a>`;
"aria-expanded='true' " +
"aria-controls='collapse" + i + "' " +
"title=\"Show/hide contents of '" + Utils.escapeHtml(file.fileName) + "'\">&#x1F50D</a>";
const html = "<div class='panel panel-default'>" + const hexFileData = Utils.toHexFast(new Uint8Array(file.bytes));
"<div class='panel-heading' role='tab' id='heading" + i + "'>" +
"<h4 class='panel-title'>" + const switchToInputElem = `<a href='#switchFileToInput${i}'
"<div>" + class='file-switch'
Utils.escapeHtml(file.fileName) + title='Move file to input as hex'
" " + expandFileContentsElem + fileValue='${hexFileData}'>&#x21e7;</a>`;
" " + downloadAnchorElem +
"<span class='pull-right'>" + const html = `<div class='panel panel-default' style='white-space: normal;'>
// These are for formatting when stripping HTML <div class='panel-heading' role='tab' id='heading${i}'>
"<span style='display: none'>\n</span>" + <h4 class='panel-title'>
file.size.toLocaleString() + " bytes" + <div>
"<span style='display: none'>\n</span>" + ${Utils.escapeHtml(file.fileName)}<NL>
"</span>" + ${viewFileElem}<SP>
"</div>" + ${downloadFileElem}<SP>
"</h4>" + ${switchToInputElem}<SP>
"</div>" + <span class='pull-right'>
"<div id='collapse" + i + "' class='panel-collapse collapse' " + <NL>${file.size.toLocaleString()} bytes
"role='tabpanel' aria-labelledby='heading" + i + "'>" + </span>
"<div class='panel-body'>" + </div>
"<pre><code>" + Utils.escapeHtml(file.contents) + "</pre></code></div>" + </h4>
"</div>" + </div>
"</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; return html;
}; };
let html = "<div style='padding: 5px;'>" + let html = `<div style='padding: 5px; white-space: normal;'>
files.length + ${files.length} file(s) found<NL>
" file(s) found</div>\n"; </div>`;
files.forEach(function(file, i) { files.forEach(function(file, i) {
if (typeof file.contents !== "undefined") { if (typeof file.contents !== "undefined") {
html += formatFile(file, i); html += formatFile(file, i);
@ -1012,7 +978,10 @@ const Utils = {
html += formatDirectory(file); 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 {}; if (paramStr === "") return {};
// Cut off ? or # and split on & // 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 = {}; const result = {};
for (let i = 0; i < params.length; i++) { for (let i = 0; i < params.length; i++) {
const param = params[i].split("="); const param = params[i].split("=");
if (param.length !== 2) { if (param.length !== 2) {

View file

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

View file

@ -2,6 +2,7 @@ import FlowControl from "../FlowControl.js";
import Base from "../operations/Base.js"; import Base from "../operations/Base.js";
import Base58 from "../operations/Base58.js"; import Base58 from "../operations/Base58.js";
import Base64 from "../operations/Base64.js"; import Base64 from "../operations/Base64.js";
import BCD from "../operations/BCD.js";
import BitwiseOp from "../operations/BitwiseOp.js"; import BitwiseOp from "../operations/BitwiseOp.js";
import ByteRepr from "../operations/ByteRepr.js"; import ByteRepr from "../operations/ByteRepr.js";
import CharEnc from "../operations/CharEnc.js"; import CharEnc from "../operations/CharEnc.js";
@ -330,30 +331,25 @@ const OperationConfig = {
value: BitwiseOp.XOR_BRUTE_KEY_LENGTH value: BitwiseOp.XOR_BRUTE_KEY_LENGTH
}, },
{ {
name: "Length of sample", name: "Sample length",
type: "number", type: "number",
value: BitwiseOp.XOR_BRUTE_SAMPLE_LENGTH value: BitwiseOp.XOR_BRUTE_SAMPLE_LENGTH
}, },
{ {
name: "Offset of sample", name: "Sample offset",
type: "number", type: "number",
value: BitwiseOp.XOR_BRUTE_SAMPLE_OFFSET value: BitwiseOp.XOR_BRUTE_SAMPLE_OFFSET
}, },
{
name: "Scheme",
type: "option",
value: BitwiseOp.XOR_SCHEME
},
{ {
name: "Null preserving", name: "Null preserving",
type: "boolean", type: "boolean",
value: BitwiseOp.XOR_PRESERVE_NULLS value: BitwiseOp.XOR_PRESERVE_NULLS
}, },
{
name: "Differential",
type: "boolean",
value: BitwiseOp.XOR_DIFFERENTIAL
},
{
name: "Crib (known plaintext string)",
type: "binaryString",
value: ""
},
{ {
name: "Print key", name: "Print key",
type: "boolean", type: "boolean",
@ -363,6 +359,11 @@ const OperationConfig = {
name: "Output as hex", name: "Output as hex",
type: "boolean", type: "boolean",
value: BitwiseOp.XOR_BRUTE_OUTPUT_HEX value: BitwiseOp.XOR_BRUTE_OUTPUT_HEX
},
{
name: "Crib (known plaintext string)",
type: "binaryString",
value: ""
} }
] ]
}, },
@ -2301,6 +2302,11 @@ const OperationConfig = {
name: "Output units", name: "Output units",
type: "option", type: "option",
value: DateTime.UNITS value: DateTime.UNITS
},
{
name: "Input format",
type: "option",
value: DateTime.FILETIME_FORMATS
} }
] ]
}, },
@ -2314,6 +2320,11 @@ const OperationConfig = {
name: "Input units", name: "Input units",
type: "option", type: "option",
value: DateTime.UNITS 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; 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='" + offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -2)) + "'>" + Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -2)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset0.substr(offset0.length - 3, 1) + "</span>" + "<span class='hl5'>" + offset0.substr(offset0.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset0.substr(offset0.length - 2) + "</span>"; "<span class='hl3'>" + offset0.substr(offset0.length - 2) + "</span>";
} else if (len0 % 4 === 3) { } else if (len0 % 4 === 3) {
staticSection = offset0.slice(0, -2); staticSection = offset0.slice(0, -2);
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" + offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -1)) + "'>" + Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -1)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset0.substr(offset0.length - 2, 1) + "</span>" + "<span class='hl5'>" + offset0.substr(offset0.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset0.substr(offset0.length - 1) + "</span>"; "<span class='hl3'>" + offset0.substr(offset0.length - 1) + "</span>";
} else { } else {
staticSection = offset0; staticSection = offset0;
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" + offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
@ -243,23 +243,23 @@ const Base64 = {
// Highlight offset 1 // Highlight offset 1
padding = "<span class='hlred'>" + offset1.substr(0, 1) + "</span>" + padding = "<span class='hl3'>" + offset1.substr(0, 1) + "</span>" +
"<span class='hlgreen'>" + offset1.substr(1, 1) + "</span>"; "<span class='hl5'>" + offset1.substr(1, 1) + "</span>";
offset1 = offset1.substr(2); offset1 = offset1.substr(2);
if (len1 % 4 === 2) { if (len1 % 4 === 2) {
staticSection = offset1.slice(0, -3); staticSection = offset1.slice(0, -3);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -2)) + "'>" + Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -2)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset1.substr(offset1.length - 3, 1) + "</span>" + "<span class='hl5'>" + offset1.substr(offset1.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset1.substr(offset1.length - 2) + "</span>"; "<span class='hl3'>" + offset1.substr(offset1.length - 2) + "</span>";
} else if (len1 % 4 === 3) { } else if (len1 % 4 === 3) {
staticSection = offset1.slice(0, -2); staticSection = offset1.slice(0, -2);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -1)) + "'>" + Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -1)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset1.substr(offset1.length - 2, 1) + "</span>" + "<span class='hl5'>" + offset1.substr(offset1.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset1.substr(offset1.length - 1) + "</span>"; "<span class='hl3'>" + offset1.substr(offset1.length - 1) + "</span>";
} else { } else {
staticSection = offset1; staticSection = offset1;
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
@ -272,23 +272,23 @@ const Base64 = {
} }
// Highlight offset 2 // Highlight offset 2
padding = "<span class='hlred'>" + offset2.substr(0, 2) + "</span>" + padding = "<span class='hl3'>" + offset2.substr(0, 2) + "</span>" +
"<span class='hlgreen'>" + offset2.substr(2, 1) + "</span>"; "<span class='hl5'>" + offset2.substr(2, 1) + "</span>";
offset2 = offset2.substr(3); offset2 = offset2.substr(3);
if (len2 % 4 === 2) { if (len2 % 4 === 2) {
staticSection = offset2.slice(0, -3); staticSection = offset2.slice(0, -3);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" + Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset2.substr(offset2.length - 3, 1) + "</span>" + "<span class='hl5'>" + offset2.substr(offset2.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset2.substr(offset2.length - 2) + "</span>"; "<span class='hl3'>" + offset2.substr(offset2.length - 2) + "</span>";
} else if (len2 % 4 === 3) { } else if (len2 % 4 === 3) {
staticSection = offset2.slice(0, -2); staticSection = offset2.slice(0, -2);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" + Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset2.substr(offset2.length - 2, 1) + "</span>" + "<span class='hl5'>" + offset2.substr(offset2.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset2.substr(offset2.length - 1) + "</span>"; "<span class='hl3'>" + offset2.substr(offset2.length - 1) + "</span>";
} else { } else {
staticSection = offset2; staticSection = offset2;
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
@ -300,8 +300,8 @@ const Base64 = {
offset2 = staticSection; offset2 = staticSection;
} }
return (showVariable ? "Characters highlighted in <span class='hlgreen'>green</span> could change if the input is surrounded by more data." + 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='hlred'>red</span> are for padding purposes only." + "\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>." + "\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" + "\nHover over the static sections to see what they decode to on their own.\n" +
"\nOffset 0: " + offset0 + "\nOffset 0: " + offset0 +

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -27,52 +27,49 @@ const PublicKey = {
* @returns {string} * @returns {string}
*/ */
runParseX509: function (input, args) { runParseX509: function (input, args) {
let cert = new r.X509(),
inputFormat = args[0];
if (!input.length) { if (!input.length) {
return "No input"; return "No input";
} }
let cert = new r.X509(),
inputFormat = args[0];
switch (inputFormat) { switch (inputFormat) {
case "DER Hex": case "DER Hex":
input = input.replace(/\s/g, ""); input = input.replace(/\s/g, "");
cert.hex = input; cert.readCertHex(input);
cert.pem = r.KJUR.asn1.ASN1Util.getPEMStringFromHex(input, "CERTIFICATE");
break; break;
case "PEM": case "PEM":
cert.hex = r.X509.pemToHex(input); cert.readCertPEM(input);
cert.pem = input;
break; break;
case "Base64": case "Base64":
cert.hex = Utils.toHex(Utils.fromBase64(input, null, "byteArray"), ""); cert.readCertHex(Utils.toHex(Utils.fromBase64(input, null, "byteArray"), ""));
cert.pem = r.KJUR.asn1.ASN1Util.getPEMStringFromHex(cert.hex, "CERTIFICATE");
break; break;
case "Raw": case "Raw":
cert.hex = Utils.toHex(Utils.strToByteArray(input), ""); cert.readCertHex(Utils.toHex(Utils.strToByteArray(input), ""));
cert.pem = r.KJUR.asn1.ASN1Util.getPEMStringFromHex(cert.hex, "CERTIFICATE");
break; break;
default: default:
throw "Undefined input format"; throw "Undefined input format";
} }
let version = r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [0, 0, 0]), let sn = cert.getSerialNumberHex(),
sn = cert.getSerialNumberHex(),
algorithm = r.KJUR.asn1.x509.OID.oid2name(r.KJUR.asn1.ASN1Util.oidHexToInt(r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [0, 2, 0]))),
issuer = cert.getIssuerString(), issuer = cert.getIssuerString(),
notBefore = cert.getNotBefore(),
notAfter = cert.getNotAfter(),
subject = cert.getSubjectString(), 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 = cert.getPublicKey(),
pk = r.X509.getPublicKeyFromCertPEM(cert.pem),
pkFields = [], pkFields = [],
pkStr = "", pkStr = "",
certSigAlg = r.KJUR.asn1.x509.OID.oid2name(r.KJUR.asn1.ASN1Util.oidHexToInt(r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [1, 0]))), sig = cert.getSignatureValueHex(),
certSig = r.ASN1HEX.getDecendantHexVByNthList(cert.hex, 0, [2]).substr(2),
sigStr = "", 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 // Public Key fields
pkFields.push({
key: "Algorithm",
value: pk.type
});
if (pk.type === "EC") { // ECDSA if (pk.type === "EC") { // ECDSA
pkFields.push({ pkFields.push({
key: "Curve Name", 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 // Format Public Key fields
for (let i = 0; i < pkFields.length; i++) { for (let i = 0; i < pkFields.length; i++) {
pkStr += " " + pkFields[i].key + ":" + 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), let issuerStr = PublicKey._formatDnStr(issuer, 2),
nbDate = PublicKey._formatDate(notBefore), nbDate = PublicKey._formatDate(cert.getNotBefore()),
naDate = PublicKey._formatDate(notAfter), naDate = PublicKey._formatDate(cert.getNotAfter()),
subjectStr = PublicKey._formatDnStr(subject, 2); subjectStr = PublicKey._formatDnStr(subject, 2);
const output = "Version: " + (parseInt(version, 16) + 1) + " (0x" + version + ")\n" + return `Version: ${cert.version} (0x${Utils.hex(cert.version - 1)})
"Serial number: " + new r.BigInteger(sn, 16).toString() + " (0x" + sn + ")\n" + Serial number: ${new r.BigInteger(sn, 16).toString()} (0x${sn})
"Algorithm ID: " + algorithm + "\n" + Algorithm ID: ${cert.getSignatureAlgorithmField()}
"Validity\n" + Validity
" Not Before: " + nbDate + " (dd-mm-yy hh:mm:ss) (" + notBefore + ")\n" + Not Before: ${nbDate} (dd-mm-yy hh:mm:ss) (${cert.getNotBefore()})
" Not After: " + naDate + " (dd-mm-yy hh:mm:ss) (" + notAfter + ")\n" + Not After: ${naDate} (dd-mm-yy hh:mm:ss) (${cert.getNotAfter()})
"Issuer\n" + Issuer
issuerStr + ${issuerStr}
"Subject\n" + Subject
subjectStr + ${subjectStr}
"Public Key\n" + Public Key
" Algorithm: " + pkAlgorithm + "\n" + ${pkStr.slice(0, -1)}
pkStr + Certificate Signature
"Certificate Signature\n" + Algorithm: ${cert.getSignatureAlgorithmName()}
" Algorithm: " + certSigAlg + "\n" + ${sigStr}
sigStr +
"\nExtensions (parsed ASN.1)\n" +
extensions;
return output; Extensions
${extensions}`;
}, },
@ -192,7 +188,9 @@ const PublicKey = {
// Add footer so that the KEYUTIL function works // Add footer so that the KEYUTIL function works
input = input + "-----END CERTIFICATE-----"; 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) { _formatDnStr: function(dnStr, indent) {
let output = "", let output = "",
fields = dnStr.split(",/|"), fields = dnStr.substr(1).replace(/([^\\])\//g, "$1$1/").split(/[^\\]\//),
maxKeyLen = 0, maxKeyLen = 0,
key, key,
value, value,
@ -295,7 +293,7 @@ const PublicKey = {
output += Utils.padLeft(str, indent + str.length, " "); output += Utils.padLeft(str, indent + str.length, " ");
} }
return output; return output.slice(0, -1);
}, },
@ -347,24 +345,6 @@ const PublicKey = {
export default 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 * 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 * https://github.com/nfephp-org/nfephp/blob/master/libs/Common/Certificate/Oids.php

View file

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

View file

@ -359,9 +359,9 @@ const StrUtils = {
for (let i = 0; i < diff.length; i++) { for (let i = 0; i < diff.length; i++) {
if (diff[i].added) { 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) { } 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 { } else {
output += Utils.escapeHtml(diff[i].value); output += Utils.escapeHtml(diff[i].value);
} }
@ -424,7 +424,7 @@ const StrUtils = {
} }
if (match && !inMatch) { 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 (samples[s].length === i + 1) outputs[s] += "</span>";
if (s === samples.length - 1) inMatch = true; if (s === samples.length - 1) inMatch = true;
} else if (!match && inMatch) { } else if (!match && inMatch) {

View file

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

View file

@ -266,13 +266,7 @@ App.prototype.silentBake = function() {
* @returns {string} * @returns {string}
*/ */
App.prototype.getInput = function() { App.prototype.getInput = function() {
const input = this.manager.input.get(); return 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;
}; };
@ -282,8 +276,6 @@ App.prototype.getInput = function() {
* @param {string} input - The string to set the input to * @param {string} input - The string to set the input to
*/ */
App.prototype.setInput = function(input) { App.prototype.setInput = function(input) {
sessionStorage.setItem("inputLength", input.length);
sessionStorage.setItem("input", input);
this.manager.input.set(input); this.manager.input.set(input);
}; };
@ -466,7 +458,12 @@ App.prototype.addFavourite = function(name) {
*/ */
App.prototype.loadURIParams = function() { App.prototype.loadURIParams = function() {
// Load query string or hash from URI (depending on which is populated) // 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); this.uriParams = Utils.parseURIParams(params);
// Pause auto-bake while loading but don't modify `this.autoBake_` // Pause auto-bake while loading but don't modify `this.autoBake_`
@ -529,9 +526,7 @@ App.prototype.nextIngId = function() {
* @returns {Object[]} * @returns {Object[]}
*/ */
App.prototype.getRecipeConfig = function() { App.prototype.getRecipeConfig = function() {
const recipeConfig = this.manager.recipe.getConfig(); return this.manager.recipe.getConfig();
sessionStorage.setItem("recipeConfig", JSON.stringify(recipeConfig));
return recipeConfig;
}; };
@ -541,7 +536,6 @@ App.prototype.getRecipeConfig = function() {
* @param {Object[]} recipeConfig - The recipe configuration * @param {Object[]} recipeConfig - The recipe configuration
*/ */
App.prototype.setRecipeConfig = function(recipeConfig) { App.prototype.setRecipeConfig = function(recipeConfig) {
sessionStorage.setItem("recipeConfig", JSON.stringify(recipeConfig));
document.getElementById("rec-list").innerHTML = null; document.getElementById("rec-list").innerHTML = null;
for (let i = 0; i < recipeConfig.length; i++) { for (let i = 0; i < recipeConfig.length; i++) {
@ -596,15 +590,24 @@ App.prototype.resetLayout = function() {
App.prototype.setCompileMessage = function() { App.prototype.setCompileMessage = function() {
// Display time since last build and compile message // Display time since last build and compile message
let now = new Date(), let now = new Date(),
timeSinceCompile = Utils.fuzzyTime(now.getTime() - window.compileTime), timeSinceCompile = Utils.fuzzyTime(now.getTime() - window.compileTime);
compileInfo = "<span style=\"font-weight: normal\">Last build: " +
timeSinceCompile.substr(0, 1).toUpperCase() + timeSinceCompile.substr(1) + " ago"; // 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 !== "") { if (window.compileMessage !== "") {
compileInfo += " - " + window.compileMessage; compileInfo += " - " + window.compileMessage;
} }
compileInfo += "</span>";
document.getElementById("notice").innerHTML = compileInfo; document.getElementById("notice").innerHTML = compileInfo;
}; };
@ -729,10 +732,20 @@ App.prototype.alertCloseClick = function() {
App.prototype.stateChange = function(e) { App.prototype.stateChange = function(e) {
this.autoBake(); 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) // Update the current history state (not creating a new one)
if (this.options.updateUrl) { if (this.options.updateUrl) {
this.lastStateUrl = this.manager.controls.generateStateUrl(true, true); this.lastStateUrl = this.manager.controls.generateStateUrl(true, true, recipeConfig);
window.history.replaceState({}, "CyberChef", this.lastStateUrl); window.history.replaceState({}, title, this.lastStateUrl);
} }
}; };

View file

@ -493,7 +493,8 @@ HighlighterWaiter.prototype.highlight = function(textarea, highlighter, pos) {
//if (colour) cssClass += "-"+colour; //if (colour) cssClass += "-"+colour;
// Remove HTML tags // Remove HTML tags
text = text.replace(/&/g, "&amp;") text = text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;") .replace(/</g, "&lt;")
.replace(/>/g, "&gt;") .replace(/>/g, "&gt;")
.replace(/\n/g, "&#10;") .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)); 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-text", "mousedown dblclick select", this.highlighter.outputMousedown, this.highlighter);
this.addMultiEventListener("#output-html", "mousedown dblclick select", this.highlighter.outputHtmlMousedown, 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 // Options
document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.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; 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. * Handler for maximise output click events.

View file

@ -26,12 +26,14 @@
<title>CyberChef</title> <title>CyberChef</title>
<meta name="copyright" content="Crown Copyright 2016" /> <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" /> <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') %>" /> <link rel="icon" type="image/ico" href="<%- require('../static/images/favicon.ico') %>" />
<script type="application/javascript"> <script type="application/javascript">
"use strict";
// Load theme before the preloader is shown // Load theme before the preloader is shown
document.querySelector(":root").className = (JSON.parse(localStorage.getItem("options")) || {}).theme; document.querySelector(":root").className = (JSON.parse(localStorage.getItem("options")) || {}).theme;
@ -72,6 +74,11 @@
changeLoadingMsg(); changeLoadingMsg();
window.loadingMsgsInt = setInterval(changeLoadingMsg, (Math.random() * 1000) + 1000); window.loadingMsgsInt = setInterval(changeLoadingMsg, (Math.random() * 1000) + 1000);
</script> </script>
<% if (!htmlWebpackPlugin.options.inline) { %>
<script type="application/ld+json">
<% print(JSON.stringify(require("../static/structuredData.json"))); %>
</script>
<% } %>
</head> </head>
<body> <body>
<!-- Preloader overlay --> <!-- Preloader overlay -->
@ -87,11 +94,14 @@
</div> </div>
<div id="content-wrapper"> <div id="content-wrapper">
<div id="banner"> <div id="banner">
<div class="col-md-4" style="text-align: left; padding-left: 10px;">
<% if (htmlWebpackPlugin.options.inline) { %> <% if (htmlWebpackPlugin.options.inline) { %>
<span style="float: left; margin-left: 10px;">Compile time: <%= htmlWebpackPlugin.options.compileTime %></span> <span>Version <%= htmlWebpackPlugin.options.version %></span>
<% } else { %> <% } 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"> <span id="notice">
<script type="text/javascript"> <script type="text/javascript">
// Must be text/javascript rather than application/javascript otherwise IE won't recognise it... // Must be text/javascript rather than application/javascript otherwise IE won't recognise it...
@ -102,8 +112,11 @@
</script> </script>
<noscript>JavaScript is not enabled. Good luck.</noscript> <noscript>JavaScript is not enabled. Good luck.</noscript>
</span> </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> </div>
<a href="#" id="options" class="banner-right">Options<img aria-hidden="true" src="<%- require('../static/images/settings-22x22.png') %>" alt="Settings Icon"/></a> <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>
<div id="workspace-wrapper"> <div id="workspace-wrapper">
<div id="operations" class="split split-horizontal no-select"> <div id="operations" class="split split-horizontal no-select">
@ -266,32 +279,32 @@
<label for="theme"> Theme (only supported in modern browsers)</label> <label for="theme"> Theme (only supported in modern browsers)</label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="update_url" id="update_url" checked /> <input type="checkbox" option="updateUrl" id="updateUrl" checked />
<label for="update_url"> Update the URL when the input or recipe changes </label> <label for="updateUrl"> Update the URL when the input or recipe changes </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="show_highlighter" id="show_highlighter" checked /> <input type="checkbox" option="showHighlighter" id="showHighlighter" checked />
<label for="show_highlighter"> Highlight selected bytes in output and input (when possible) </label> <label for="showHighlighter"> Highlight selected bytes in output and input (when possible) </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="treat_as_utf8" id="treat_as_utf8" checked /> <input type="checkbox" option="treatAsUtf8" id="treatAsUtf8" checked />
<label for="treat_as_utf8"> Treat output as UTF-8 if possible </label> <label for="treatAsUtf8"> Treat output as UTF-8 if possible </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="word_wrap" id="word_wrap" checked /> <input type="checkbox" option="wordWrap" id="wordWrap" checked />
<label for="word_wrap"> Word wrap the input and output </label> <label for="wordWrap"> Word wrap the input and output </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="show_errors" id="show_errors" checked /> <input type="checkbox" option="showErrors" id="showErrors" checked />
<label for="show_errors"> Operation error reporting (recommended) </label> <label for="showErrors"> Operation error reporting (recommended) </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="number" option="error_timeout" id="error_timeout" /> <input type="number" option="errorTimeout" id="errorTimeout" />
<label for="error_timeout"> Operation error timeout in ms (0 for never) </label> <label for="errorTimeout"> Operation error timeout in ms (0 for never) </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="number" option="auto_bake_threshold" id="auto_bake_threshold"/> <input type="number" option="autoBakeThreshold" id="autoBakeThreshold"/>
<label for="auto_bake_threshold"> Auto Bake threshold in ms </label> <label for="autoBakeThreshold"> Auto Bake threshold in ms </label>
</div> </div>
</div> </div>
<div class="modal-footer"> <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); background-color: var(--arg-background);
border: 1px solid var(--arg-border-colour); border: 1px solid var(--arg-border-colour);
font-family: var(--fixed-width-font-family); font-family: var(--fixed-width-font-family);
text-overflow: ellipsis;
} }
.short-string { .short-string {

View file

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

View file

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

View file

@ -12,6 +12,7 @@ import "babel-polyfill";
import TestRegister from "./TestRegister.js"; import TestRegister from "./TestRegister.js";
import "./tests/operations/Base58.js"; import "./tests/operations/Base58.js";
import "./tests/operations/BCD.js";
import "./tests/operations/ByteRepr.js"; import "./tests/operations/ByteRepr.js";
import "./tests/operations/CharEnc.js"; import "./tests/operations/CharEnc.js";
import "./tests/operations/Cipher.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: [ recipeConfig: [
{ {
op: "Windows Filetime to UNIX Timestamp", op: "Windows Filetime to UNIX Timestamp",
args: ["Nanoseconds (ns)"], args: ["Nanoseconds (ns)", "Decimal"],
}, },
], ],
}, },
@ -27,7 +27,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "UNIX Timestamp to Windows Filetime", op: "UNIX Timestamp to Windows Filetime",
args: ["Nanoseconds (ns)"], args: ["Nanoseconds (ns)", "Decimal"],
}, },
], ],
}, },

View file

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

View file

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