mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-22 15:56:16 -04:00
Bring up to date with master
This commit is contained in:
commit
dd5038c15b
25 changed files with 2265 additions and 2202 deletions
|
@ -201,11 +201,20 @@ class Utils {
|
|||
* Utils.parseEscapedChars("\\n");
|
||||
*/
|
||||
static parseEscapedChars(str) {
|
||||
return str.replace(/(\\)?\\([bfnrtv0'"]|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\})/g, function(m, a, b) {
|
||||
return str.replace(/(\\)?\\([bfnrtv'"]|[0-3][0-7]{2}|[0-7]{1,2}|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\}|\\)/g, function(m, a, b) {
|
||||
if (a === "\\") return "\\"+b;
|
||||
switch (b[0]) {
|
||||
case "\\":
|
||||
return "\\";
|
||||
case "0":
|
||||
return "\0";
|
||||
case "1":
|
||||
case "2":
|
||||
case "3":
|
||||
case "4":
|
||||
case "5":
|
||||
case "6":
|
||||
case "7":
|
||||
return String.fromCharCode(parseInt(b, 8));
|
||||
case "b":
|
||||
return "\b";
|
||||
case "t":
|
||||
|
|
|
@ -395,6 +395,7 @@
|
|||
"ops": [
|
||||
"Entropy",
|
||||
"Frequency distribution",
|
||||
"Index of Coincidence",
|
||||
"Chi Square",
|
||||
"Disassemble x86",
|
||||
"Pseudo-Random Number Generator",
|
||||
|
|
72
src/core/operations/Bzip2Compress.mjs
Normal file
72
src/core/operations/Bzip2Compress.mjs
Normal file
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* @author Matt C [me@mitt.dev]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Bzip2 from "libbzip2-wasm";
|
||||
|
||||
/**
|
||||
* Bzip2 Compress operation
|
||||
*/
|
||||
class Bzip2Compress extends Operation {
|
||||
|
||||
/**
|
||||
* Bzip2Compress constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Bzip2 Compress";
|
||||
this.module = "Compression";
|
||||
this.description = "Bzip2 is a compression library developed by Julian Seward (of GHC fame) that uses the Burrows-Wheeler algorithm. It only supports compressing single files and its compression is slow, however is more effective than Deflate (.gz & .zip).";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bzip2";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
name: "Block size (100s of kb)",
|
||||
type: "number",
|
||||
value: 9,
|
||||
min: 1,
|
||||
max: 9
|
||||
},
|
||||
{
|
||||
name: "Work factor",
|
||||
type: "number",
|
||||
value: 30
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {File}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [blockSize, workFactor] = args;
|
||||
if (input.byteLength <= 0) {
|
||||
throw new OperationError("Please provide an input.");
|
||||
}
|
||||
if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Loading Bzip2...");
|
||||
return new Promise((resolve, reject) => {
|
||||
Bzip2().then(bzip2 => {
|
||||
if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Compressing data...");
|
||||
const inpArray = new Uint8Array(input);
|
||||
const bzip2cc = bzip2.compressBZ2(inpArray, blockSize, workFactor);
|
||||
if (bzip2cc.error !== 0) {
|
||||
reject(new OperationError(bzip2cc.error_msg));
|
||||
} else {
|
||||
const output = bzip2cc.output;
|
||||
resolve(output.buffer.slice(output.byteOffset, output.byteLength + output.byteOffset));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Bzip2Compress;
|
|
@ -1,12 +1,12 @@
|
|||
/**
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2016
|
||||
* @author Matt C [me@mitt.dev]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import bzip2 from "../vendor/bzip2";
|
||||
import OperationError from "../errors/OperationError";
|
||||
import Bzip2 from "libbzip2-wasm";
|
||||
|
||||
/**
|
||||
* Bzip2 Decompress operation
|
||||
|
@ -23,9 +23,15 @@ class Bzip2Decompress extends Operation {
|
|||
this.module = "Compression";
|
||||
this.description = "Decompresses data using the Bzip2 algorithm.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Bzip2";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [
|
||||
{
|
||||
name: "Use low-memory, slower decompression algorithm",
|
||||
type: "boolean",
|
||||
value: false
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
{
|
||||
"match": "^\\x42\\x5a\\x68",
|
||||
|
@ -41,14 +47,24 @@ class Bzip2Decompress extends Operation {
|
|||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const compressed = new Uint8Array(input);
|
||||
|
||||
try {
|
||||
const bzip2Reader = bzip2.array(compressed);
|
||||
return bzip2.simple(bzip2Reader);
|
||||
} catch (err) {
|
||||
throw new OperationError(err);
|
||||
const [small] = args;
|
||||
if (input.byteLength <= 0) {
|
||||
throw new OperationError("Please provide an input.");
|
||||
}
|
||||
if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Loading Bzip2...");
|
||||
return new Promise((resolve, reject) => {
|
||||
Bzip2().then(bzip2 => {
|
||||
if (ENVIRONMENT_IS_WORKER()) self.sendStatusMessage("Decompressing data...");
|
||||
const inpArray = new Uint8Array(input);
|
||||
const bzip2cc = bzip2.decompressBZ2(inpArray, small ? 1 : 0);
|
||||
if (bzip2cc.error !== 0) {
|
||||
reject(new OperationError(bzip2cc.error_msg));
|
||||
} else {
|
||||
const output = bzip2cc.output;
|
||||
resolve(output.buffer.slice(output.byteOffset, output.byteLength + output.byteOffset));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,8 +4,13 @@
|
|||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import * as d3temp from "d3";
|
||||
import * as nodomtemp from "nodom";
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
|
||||
const d3 = d3temp.default ? d3temp.default : d3temp;
|
||||
const nodom = nodomtemp.default ? nodomtemp.default: nodomtemp;
|
||||
|
||||
/**
|
||||
* Entropy operation
|
||||
|
@ -19,30 +24,45 @@ class Entropy extends Operation {
|
|||
super();
|
||||
|
||||
this.name = "Entropy";
|
||||
this.module = "Default";
|
||||
this.module = "Charts";
|
||||
this.description = "Shannon Entropy, in the context of information theory, is a measure of the rate at which information is produced by a source of data. It can be used, in a broad sense, to detect whether data is likely to be structured or unstructured. 8 is the maximum, representing highly unstructured, 'random' data. English language text usually falls somewhere between 3.5 and 5. Properly encrypted or compressed data should have an entropy of over 7.5.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Entropy_(information_theory)";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "number";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "json";
|
||||
this.presentType = "html";
|
||||
this.args = [];
|
||||
this.args = [
|
||||
{
|
||||
"name": "Visualisation",
|
||||
"type": "option",
|
||||
"value": ["Shannon scale", "Histogram (Bar)", "Histogram (Line)", "Curve", "Image"]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* Calculates the frequency of bytes in the input.
|
||||
*
|
||||
* @param {Uint8Array} input
|
||||
* @returns {number}
|
||||
*/
|
||||
run(input, args) {
|
||||
calculateShannonEntropy(input) {
|
||||
const prob = [],
|
||||
uniques = input.unique(),
|
||||
str = Utils.byteArrayToChars(input);
|
||||
let i;
|
||||
occurrences = new Array(256).fill(0);
|
||||
|
||||
for (i = 0; i < uniques.length; i++) {
|
||||
prob.push(str.count(Utils.chr(uniques[i])) / input.length);
|
||||
// Count occurrences of each byte in the input
|
||||
let i;
|
||||
for (i = 0; i < input.length; i++) {
|
||||
occurrences[input[i]]++;
|
||||
}
|
||||
|
||||
// Store probability list
|
||||
for (i = 0; i < occurrences.length; i++) {
|
||||
if (occurrences[i] > 0) {
|
||||
prob.push(occurrences[i] / input.length);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate Shannon entropy
|
||||
let entropy = 0,
|
||||
p;
|
||||
|
||||
|
@ -54,44 +74,357 @@ class Entropy extends Operation {
|
|||
return -entropy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the scanning entropy of the input
|
||||
*
|
||||
* @param {Uint8Array} inputBytes
|
||||
* @returns {Object}
|
||||
*/
|
||||
calculateScanningEntropy(inputBytes) {
|
||||
const entropyData = [];
|
||||
const binWidth = inputBytes.length < 256 ? 8 : 256;
|
||||
|
||||
for (let bytePos = 0; bytePos < inputBytes.length; bytePos += binWidth) {
|
||||
const block = inputBytes.slice(bytePos, bytePos+binWidth);
|
||||
entropyData.push(this.calculateShannonEntropy(block));
|
||||
}
|
||||
|
||||
return { entropyData, binWidth };
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the frequency of bytes in the input.
|
||||
*
|
||||
* @param {object} svg
|
||||
* @param {function} xScale
|
||||
* @param {function} yScale
|
||||
* @param {integer} svgHeight
|
||||
* @param {integer} svgWidth
|
||||
* @param {object} margins
|
||||
* @param {string} xTitle
|
||||
* @param {string} yTitle
|
||||
*/
|
||||
createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, title, xTitle, yTitle) {
|
||||
// Axes
|
||||
const yAxis = d3.axisLeft()
|
||||
.scale(yScale);
|
||||
|
||||
const xAxis = d3.axisBottom()
|
||||
.scale(xScale);
|
||||
|
||||
svg.append("g")
|
||||
.attr("transform", `translate(0, ${svgHeight - margins.bottom})`)
|
||||
.call(xAxis);
|
||||
|
||||
svg.append("g")
|
||||
.attr("transform", `translate(${margins.left},0)`)
|
||||
.call(yAxis);
|
||||
|
||||
// Axes labels
|
||||
svg.append("text")
|
||||
.attr("transform", "rotate(-90)")
|
||||
.attr("y", 0 - margins.left)
|
||||
.attr("x", 0 - (svgHeight / 2))
|
||||
.attr("dy", "1em")
|
||||
.style("text-anchor", "middle")
|
||||
.text(yTitle);
|
||||
|
||||
svg.append("text")
|
||||
.attr("transform", `translate(${svgWidth / 2}, ${svgHeight - margins.bottom + 40})`)
|
||||
.style("text-anchor", "middle")
|
||||
.text(xTitle);
|
||||
|
||||
// Add title
|
||||
svg.append("text")
|
||||
.attr("transform", `translate(${svgWidth / 2}, ${margins.top - 10})`)
|
||||
.style("text-anchor", "middle")
|
||||
.text(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the frequency of bytes in the input.
|
||||
*
|
||||
* @param {Uint8Array} inputBytes
|
||||
* @returns {number[]}
|
||||
*/
|
||||
calculateByteFrequency(inputBytes) {
|
||||
const freq = new Array(256).fill(0);
|
||||
if (inputBytes.length === 0) return freq;
|
||||
|
||||
// Count occurrences of each byte in the input
|
||||
let i;
|
||||
for (i = 0; i < inputBytes.length; i++) {
|
||||
freq[inputBytes[i]]++;
|
||||
}
|
||||
|
||||
for (i = 0; i < freq.length; i++) {
|
||||
freq[i] = freq[i] / inputBytes.length;
|
||||
}
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the frequency of bytes in the input.
|
||||
*
|
||||
* @param {number[]} byteFrequency
|
||||
* @returns {HTML}
|
||||
*/
|
||||
createByteFrequencyLineHistogram(byteFrequency) {
|
||||
const margins = { top: 30, right: 20, bottom: 50, left: 30 };
|
||||
|
||||
const svgWidth = 500,
|
||||
svgHeight = 500;
|
||||
|
||||
const document = new nodom.Document();
|
||||
let svg = document.createElement("svg");
|
||||
|
||||
svg = d3.select(svg)
|
||||
.attr("width", "100%")
|
||||
.attr("height", "100%")
|
||||
.attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
|
||||
|
||||
const yScale = d3.scaleLinear()
|
||||
.domain([0, d3.max(byteFrequency, d => d)])
|
||||
.range([svgHeight - margins.bottom, margins.top]);
|
||||
|
||||
const xScale = d3.scaleLinear()
|
||||
.domain([0, byteFrequency.length - 1])
|
||||
.range([margins.left, svgWidth - margins.right]);
|
||||
|
||||
const line = d3.line()
|
||||
.x((_, i) => xScale(i))
|
||||
.y(d => yScale(d))
|
||||
.curve(d3.curveMonotoneX);
|
||||
|
||||
svg.append("path")
|
||||
.datum(byteFrequency)
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", "steelblue")
|
||||
.attr("d", line);
|
||||
|
||||
this.createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, "", "Byte", "Byte Frequency");
|
||||
|
||||
return svg._groups[0][0].outerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a byte frequency histogram
|
||||
*
|
||||
* @param {number[]} byteFrequency
|
||||
* @returns {HTML}
|
||||
*/
|
||||
createByteFrequencyBarHistogram(byteFrequency) {
|
||||
const margins = { top: 30, right: 20, bottom: 50, left: 30 };
|
||||
|
||||
const svgWidth = 500,
|
||||
svgHeight = 500,
|
||||
binWidth = 1;
|
||||
|
||||
const document = new nodom.Document();
|
||||
let svg = document.createElement("svg");
|
||||
svg = d3.select(svg)
|
||||
.attr("width", "100%")
|
||||
.attr("height", "100%")
|
||||
.attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
|
||||
|
||||
const yExtent = d3.extent(byteFrequency, d => d);
|
||||
const yScale = d3.scaleLinear()
|
||||
.domain(yExtent)
|
||||
.range([svgHeight - margins.bottom, margins.top]);
|
||||
|
||||
const xScale = d3.scaleLinear()
|
||||
.domain([0, byteFrequency.length - 1])
|
||||
.range([margins.left - binWidth, svgWidth - margins.right]);
|
||||
|
||||
svg.selectAll("rect")
|
||||
.data(byteFrequency)
|
||||
.enter().append("rect")
|
||||
.attr("x", (_, i) => xScale(i) + binWidth)
|
||||
.attr("y", dataPoint => yScale(dataPoint))
|
||||
.attr("width", binWidth)
|
||||
.attr("height", dataPoint => yScale(yExtent[0]) - yScale(dataPoint))
|
||||
.attr("fill", "blue");
|
||||
|
||||
this.createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, "", "Byte", "Byte Frequency");
|
||||
|
||||
return svg._groups[0][0].outerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a byte frequency histogram
|
||||
*
|
||||
* @param {number[]} entropyData
|
||||
* @returns {HTML}
|
||||
*/
|
||||
createEntropyCurve(entropyData) {
|
||||
const margins = { top: 30, right: 20, bottom: 50, left: 30 };
|
||||
|
||||
const svgWidth = 500,
|
||||
svgHeight = 500;
|
||||
|
||||
const document = new nodom.Document();
|
||||
let svg = document.createElement("svg");
|
||||
svg = d3.select(svg)
|
||||
.attr("width", "100%")
|
||||
.attr("height", "100%")
|
||||
.attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
|
||||
|
||||
const yScale = d3.scaleLinear()
|
||||
.domain([0, d3.max(entropyData, d => d)])
|
||||
.range([svgHeight - margins.bottom, margins.top]);
|
||||
|
||||
const xScale = d3.scaleLinear()
|
||||
.domain([0, entropyData.length])
|
||||
.range([margins.left, svgWidth - margins.right]);
|
||||
|
||||
const line = d3.line()
|
||||
.x((_, i) => xScale(i))
|
||||
.y(d => yScale(d))
|
||||
.curve(d3.curveMonotoneX);
|
||||
|
||||
if (entropyData.length > 0) {
|
||||
svg.append("path")
|
||||
.datum(entropyData)
|
||||
.attr("d", line);
|
||||
|
||||
svg.selectAll("path").attr("fill", "none").attr("stroke", "steelblue");
|
||||
}
|
||||
|
||||
this.createAxes(svg, xScale, yScale, svgHeight, svgWidth, margins, "Scanning Entropy", "Block", "Entropy");
|
||||
|
||||
return svg._groups[0][0].outerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an image representation of the entropy
|
||||
*
|
||||
* @param {number[]} entropyData
|
||||
* @returns {HTML}
|
||||
*/
|
||||
createEntropyImage(entropyData) {
|
||||
const svgHeight = 100,
|
||||
svgWidth = 100,
|
||||
cellSize = 1,
|
||||
nodes = [];
|
||||
|
||||
for (let i = 0; i < entropyData.length; i++) {
|
||||
nodes.push({
|
||||
x: i % svgWidth,
|
||||
y: Math.floor(i / svgWidth),
|
||||
entropy: entropyData[i]
|
||||
});
|
||||
}
|
||||
|
||||
const document = new nodom.Document();
|
||||
let svg = document.createElement("svg");
|
||||
svg = d3.select(svg)
|
||||
.attr("width", "100%")
|
||||
.attr("height", "100%")
|
||||
.attr("viewBox", `0 0 ${svgWidth} ${svgHeight}`);
|
||||
|
||||
const greyScale = d3.scaleLinear()
|
||||
.domain([0, d3.max(entropyData, d => d)])
|
||||
.range(["#000000", "#FFFFFF"])
|
||||
.interpolate(d3.interpolateRgb);
|
||||
|
||||
svg
|
||||
.selectAll("rect")
|
||||
.data(nodes)
|
||||
.enter().append("rect")
|
||||
.attr("x", d => d.x * cellSize)
|
||||
.attr("y", d => d.y * cellSize)
|
||||
.attr("width", cellSize)
|
||||
.attr("height", cellSize)
|
||||
.style("fill", d => greyScale(d.entropy));
|
||||
|
||||
return svg._groups[0][0].outerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the entropy as a scale bar for web apps.
|
||||
*
|
||||
* @param {number} entropy
|
||||
* @returns {html}
|
||||
* @returns {HTML}
|
||||
*/
|
||||
present(entropy) {
|
||||
createShannonEntropyVisualization(entropy) {
|
||||
return `Shannon entropy: ${entropy}
|
||||
<br><canvas id='chart-area'></canvas><br>
|
||||
- 0 represents no randomness (i.e. all the bytes in the data have the same value) whereas 8, the maximum, represents a completely random string.
|
||||
- Standard English text usually falls somewhere between 3.5 and 5.
|
||||
- Properly encrypted or compressed data of a reasonable length should have an entropy of over 7.5.
|
||||
<br><canvas id='chart-area'></canvas><br>
|
||||
- 0 represents no randomness (i.e. all the bytes in the data have the same value) whereas 8, the maximum, represents a completely random string.
|
||||
- Standard English text usually falls somewhere between 3.5 and 5.
|
||||
- Properly encrypted or compressed data of a reasonable length should have an entropy of over 7.5.
|
||||
|
||||
The following results show the entropy of chunks of the input data. Chunks with particularly high entropy could suggest encrypted or compressed sections.
|
||||
The following results show the entropy of chunks of the input data. Chunks with particularly high entropy could suggest encrypted or compressed sections.
|
||||
|
||||
<br><script>
|
||||
var canvas = document.getElementById("chart-area"),
|
||||
parentRect = canvas.parentNode.getBoundingClientRect(),
|
||||
entropy = ${entropy},
|
||||
height = parentRect.height * 0.25;
|
||||
<br><script>
|
||||
var canvas = document.getElementById("chart-area"),
|
||||
parentRect = canvas.parentNode.getBoundingClientRect(),
|
||||
entropy = ${entropy},
|
||||
height = parentRect.height * 0.25;
|
||||
|
||||
canvas.width = parentRect.width * 0.95;
|
||||
canvas.height = height > 150 ? 150 : height;
|
||||
canvas.width = parentRect.width * 0.95;
|
||||
canvas.height = height > 150 ? 150 : height;
|
||||
|
||||
CanvasComponents.drawScaleBar(canvas, entropy, 8, [
|
||||
{
|
||||
label: "English text",
|
||||
min: 3.5,
|
||||
max: 5
|
||||
},{
|
||||
label: "Encrypted/compressed",
|
||||
min: 7.5,
|
||||
max: 8
|
||||
}
|
||||
]);
|
||||
</script>`;
|
||||
CanvasComponents.drawScaleBar(canvas, entropy, 8, [
|
||||
{
|
||||
label: "English text",
|
||||
min: 3.5,
|
||||
max: 5
|
||||
},{
|
||||
label: "Encrypted/compressed",
|
||||
min: 7.5,
|
||||
max: 8
|
||||
}
|
||||
]);
|
||||
</script>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {json}
|
||||
*/
|
||||
run(input, args) {
|
||||
const visualizationType = args[0];
|
||||
input = new Uint8Array(input);
|
||||
|
||||
switch (visualizationType) {
|
||||
case "Histogram (Bar)":
|
||||
case "Histogram (Line)":
|
||||
return this.calculateByteFrequency(input);
|
||||
case "Curve":
|
||||
case "Image":
|
||||
return this.calculateScanningEntropy(input).entropyData;
|
||||
case "Shannon scale":
|
||||
default:
|
||||
return this.calculateShannonEntropy(input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the entropy in a visualisation for web apps.
|
||||
*
|
||||
* @param {json} entropyData
|
||||
* @param {Object[]} args
|
||||
* @returns {html}
|
||||
*/
|
||||
present(entropyData, args) {
|
||||
const visualizationType = args[0];
|
||||
|
||||
switch (visualizationType) {
|
||||
case "Histogram (Bar)":
|
||||
return this.createByteFrequencyBarHistogram(entropyData);
|
||||
case "Histogram (Line)":
|
||||
return this.createByteFrequencyLineHistogram(entropyData);
|
||||
case "Curve":
|
||||
return this.createEntropyCurve(entropyData);
|
||||
case "Image":
|
||||
return this.createEntropyImage(entropyData);
|
||||
case "Shannon scale":
|
||||
default:
|
||||
return this.createShannonEntropyVisualization(entropyData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Entropy;
|
||||
|
|
107
src/core/operations/IndexOfCoincidence.mjs
Normal file
107
src/core/operations/IndexOfCoincidence.mjs
Normal file
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* @author George O [georgeomnet+cyberchef@gmail.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Utils from "../Utils";
|
||||
|
||||
/**
|
||||
* Index of Coincidence operation
|
||||
*/
|
||||
class IndexOfCoincidence extends Operation {
|
||||
|
||||
/**
|
||||
* IndexOfCoincidence constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Index of Coincidence";
|
||||
this.module = "Default";
|
||||
this.description = "Index of Coincidence (IC) is the probability of two randomly selected characters being the same. This can be used to determine whether text is readable or random, with English text having an IC of around 0.066. IC can therefore be a sound method to automate frequency analysis.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Index_of_coincidence";
|
||||
this.inputType = "string";
|
||||
this.outputType = "number";
|
||||
this.presentType = "html";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {number}
|
||||
*/
|
||||
run(input, args) {
|
||||
const text = input.toLowerCase().replace(/[^a-z]/g, ""),
|
||||
frequencies = new Array(26).fill(0),
|
||||
alphabet = Utils.expandAlphRange("a-z");
|
||||
let coincidence = 0.00,
|
||||
density = 0.00,
|
||||
result = 0.00,
|
||||
i;
|
||||
|
||||
for (i=0; i < alphabet.length; i++) {
|
||||
frequencies[i] = text.count(alphabet[i]);
|
||||
}
|
||||
|
||||
for (i=0; i < frequencies.length; i++) {
|
||||
coincidence += frequencies[i] * (frequencies[i] - 1);
|
||||
}
|
||||
|
||||
density = frequencies.sum();
|
||||
|
||||
// Ensure that we don't divide by 0
|
||||
if (density < 2) density = 2;
|
||||
|
||||
result = coincidence / (density * (density - 1));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the IC as a scale bar for web apps.
|
||||
*
|
||||
* @param {number} ic
|
||||
* @returns {html}
|
||||
*/
|
||||
present(ic) {
|
||||
return `Index of Coincidence: ${ic}
|
||||
Normalized: ${ic * 26}
|
||||
<br><canvas id='chart-area'></canvas><br>
|
||||
- 0 represents complete randomness (all characters are unique), whereas 1 represents no randomness (all characters are identical).
|
||||
- English text generally has an IC of between 0.67 to 0.78.
|
||||
- 'Random' text is determined by the probability that each letter occurs the same number of times as another.
|
||||
|
||||
The graph shows the IC of the input data. A low IC generally means that the text is random, compressed or encrypted.
|
||||
|
||||
<script type='application/javascript'>
|
||||
var canvas = document.getElementById("chart-area"),
|
||||
parentRect = canvas.parentNode.getBoundingClientRect(),
|
||||
ic = ${ic};
|
||||
|
||||
canvas.width = parentRect.width * 0.95;
|
||||
canvas.height = parentRect.height * 0.25;
|
||||
|
||||
ic = ic > 0.25 ? 0.25 : ic;
|
||||
|
||||
CanvasComponents.drawScaleBar(canvas, ic, 0.25, [
|
||||
{
|
||||
label: "English text",
|
||||
min: 0.05,
|
||||
max: 0.08
|
||||
},
|
||||
{
|
||||
label: "> 0.25",
|
||||
min: 0.24,
|
||||
max: 0.25
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default IndexOfCoincidence;
|
|
@ -51,6 +51,10 @@ class JSONToCSV extends Operation {
|
|||
this.rowDelim = rowDelim;
|
||||
const self = this;
|
||||
|
||||
if (!(input instanceof Array)) {
|
||||
input = [input];
|
||||
}
|
||||
|
||||
try {
|
||||
// If the JSON is an array of arrays, this is easy
|
||||
if (input[0] instanceof Array) {
|
||||
|
@ -89,6 +93,8 @@ class JSONToCSV extends Operation {
|
|||
* @returns {string}
|
||||
*/
|
||||
escapeCellContents(data) {
|
||||
if (typeof data === "number") data = data.toString();
|
||||
|
||||
// Double quotes should be doubled up
|
||||
data = data.replace(/"/g, '""');
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class UnescapeString extends Operation {
|
|||
|
||||
this.name = "Unescape string";
|
||||
this.module = "Default";
|
||||
this.description = "Unescapes characters in a string that have been escaped. For example, <code>Don\\'t stop me now</code> becomes <code>Don't stop me now</code>.<br><br>Supports the following escape sequences:<ul><li><code>\\n</code> (Line feed/newline)</li><li><code>\\r</code> (Carriage return)</li><li><code>\\t</code> (Horizontal tab)</li><li><code>\\b</code> (Backspace)</li><li><code>\\f</code> (Form feed)</li><li><code>\\xnn</code> (Hex, where n is 0-f)</li><li><code>\\\\</code> (Backslash)</li><li><code>\\'</code> (Single quote)</li><li><code>\\"</code> (Double quote)</li><li><code>\\unnnn</code> (Unicode character)</li><li><code>\\u{nnnnnn}</code> (Unicode code point)</li></ul>";
|
||||
this.description = "Unescapes characters in a string that have been escaped. For example, <code>Don\\'t stop me now</code> becomes <code>Don't stop me now</code>.<br><br>Supports the following escape sequences:<ul><li><code>\\n</code> (Line feed/newline)</li><li><code>\\r</code> (Carriage return)</li><li><code>\\t</code> (Horizontal tab)</li><li><code>\\b</code> (Backspace)</li><li><code>\\f</code> (Form feed)</li><li><code>\\nnn</code> (Octal, where n is 0-7)</li><li><code>\\xnn</code> (Hex, where n is 0-f)</li><li><code>\\\\</code> (Backslash)</li><li><code>\\'</code> (Single quote)</li><li><code>\\"</code> (Double quote)</li><li><code>\\unnnn</code> (Unicode character)</li><li><code>\\u{nnnnnn}</code> (Unicode code point)</li></ul>";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Escape_sequence";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
|
|
265
src/core/vendor/bzip2.mjs
vendored
265
src/core/vendor/bzip2.mjs
vendored
|
@ -1,265 +0,0 @@
|
|||
/** @license
|
||||
========================================================================
|
||||
bzip2.js - a small bzip2 decompression implementation
|
||||
|
||||
Copyright 2011 by antimatter15 (antimatter15@gmail.com)
|
||||
|
||||
Based on micro-bunzip by Rob Landley (rob@landley.net).
|
||||
|
||||
Copyright (c) 2011 by antimatter15 (antimatter15@gmail.com).
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
|
||||
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
var bzip2 = {};
|
||||
|
||||
bzip2.array = function(bytes){
|
||||
var bit = 0, byte = 0;
|
||||
var BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF ];
|
||||
return function(n){
|
||||
var result = 0;
|
||||
while(n > 0){
|
||||
var left = 8 - bit;
|
||||
if(n >= left){
|
||||
result <<= left;
|
||||
result |= (BITMASK[left] & bytes[byte++]);
|
||||
bit = 0;
|
||||
n -= left;
|
||||
}else{
|
||||
result <<= n;
|
||||
result |= ((bytes[byte] & (BITMASK[n] << (8 - n - bit))) >> (8 - n - bit));
|
||||
bit += n;
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
bzip2.simple = function(bits){
|
||||
var size = bzip2.header(bits);
|
||||
var all = '', chunk = '';
|
||||
do{
|
||||
all += chunk;
|
||||
chunk = bzip2.decompress(bits, size);
|
||||
}while(chunk != -1);
|
||||
return all;
|
||||
}
|
||||
|
||||
bzip2.header = function(bits){
|
||||
if(bits(8*3) != 4348520) throw "No magic number found";
|
||||
var i = bits(8) - 48;
|
||||
if(i < 1 || i > 9) throw "Not a BZIP archive";
|
||||
return i;
|
||||
};
|
||||
|
||||
|
||||
//takes a function for reading the block data (starting with 0x314159265359)
|
||||
//a block size (0-9) (optional, defaults to 9)
|
||||
//a length at which to stop decompressing and return the output
|
||||
bzip2.decompress = function(bits, size, len){
|
||||
var MAX_HUFCODE_BITS = 20;
|
||||
var MAX_SYMBOLS = 258;
|
||||
var SYMBOL_RUNA = 0;
|
||||
var SYMBOL_RUNB = 1;
|
||||
var GROUP_SIZE = 50;
|
||||
|
||||
var bufsize = 100000 * size;
|
||||
for(var h = '', i = 0; i < 6; i++) h += bits(8).toString(16);
|
||||
if(h == "177245385090") return -1; //last block
|
||||
if(h != "314159265359") throw "Not valid bzip data";
|
||||
bits(32); //ignore CRC codes
|
||||
if(bits(1)) throw "Unsupported obsolete version";
|
||||
var origPtr = bits(24);
|
||||
if(origPtr > bufsize) throw "Initial position larger than buffer size";
|
||||
var t = bits(16);
|
||||
var symToByte = new Uint8Array(256),
|
||||
symTotal = 0;
|
||||
for (i = 0; i < 16; i++) {
|
||||
if(t & (1 << (15 - i))) {
|
||||
var k = bits(16);
|
||||
for(j = 0; j < 16; j++){
|
||||
if(k & (1 << (15 - j))){
|
||||
symToByte[symTotal++] = (16 * i) + j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var groupCount = bits(3);
|
||||
if(groupCount < 2 || groupCount > 6) throw "Error 1";
|
||||
var nSelectors = bits(15);
|
||||
if(nSelectors == 0) throw "Error";
|
||||
var mtfSymbol = []; //TODO: possibly replace JS array with typed arrays
|
||||
for(var i = 0; i < groupCount; i++) mtfSymbol[i] = i;
|
||||
var selectors = new Uint8Array(32768);
|
||||
|
||||
for(var i = 0; i < nSelectors; i++){
|
||||
for(var j = 0; bits(1); j++) if(j >= groupCount) throw "Error 2";
|
||||
var uc = mtfSymbol[j];
|
||||
mtfSymbol.splice(j, 1); //this is a probably inefficient MTF transform
|
||||
mtfSymbol.splice(0, 0, uc);
|
||||
selectors[i] = uc;
|
||||
}
|
||||
|
||||
var symCount = symTotal + 2;
|
||||
var groups = [];
|
||||
for(var j = 0; j < groupCount; j++){
|
||||
var length = new Uint8Array(MAX_SYMBOLS),
|
||||
temp = new Uint8Array(MAX_HUFCODE_BITS+1);
|
||||
t = bits(5); //lengths
|
||||
for(var i = 0; i < symCount; i++){
|
||||
while(true){
|
||||
if (t < 1 || t > MAX_HUFCODE_BITS) throw "Error 3";
|
||||
if(!bits(1)) break;
|
||||
if(!bits(1)) t++;
|
||||
else t--;
|
||||
}
|
||||
length[i] = t;
|
||||
}
|
||||
var minLen, maxLen;
|
||||
minLen = maxLen = length[0];
|
||||
for(var i = 1; i < symCount; i++){
|
||||
if(length[i] > maxLen) maxLen = length[i];
|
||||
else if(length[i] < minLen) minLen = length[i];
|
||||
}
|
||||
var hufGroup;
|
||||
hufGroup = groups[j] = {};
|
||||
hufGroup.permute = new Uint32Array(MAX_SYMBOLS);
|
||||
hufGroup.limit = new Uint32Array(MAX_HUFCODE_BITS + 1);
|
||||
hufGroup.base = new Uint32Array(MAX_HUFCODE_BITS + 1);
|
||||
hufGroup.minLen = minLen;
|
||||
hufGroup.maxLen = maxLen;
|
||||
var base = hufGroup.base.subarray(1);
|
||||
var limit = hufGroup.limit.subarray(1);
|
||||
var pp = 0;
|
||||
for(var i = minLen; i <= maxLen; i++)
|
||||
for(var t = 0; t < symCount; t++)
|
||||
if(length[t] == i) hufGroup.permute[pp++] = t;
|
||||
for(i = minLen; i <= maxLen; i++) temp[i] = limit[i] = 0;
|
||||
for(i = 0; i < symCount; i++) temp[length[i]]++;
|
||||
pp = t = 0;
|
||||
for(i = minLen; i < maxLen; i++) {
|
||||
pp += temp[i];
|
||||
limit[i] = pp - 1;
|
||||
pp <<= 1;
|
||||
base[i+1] = pp - (t += temp[i]);
|
||||
}
|
||||
limit[maxLen]=pp+temp[maxLen]-1;
|
||||
base[minLen]=0;
|
||||
}
|
||||
var byteCount = new Uint32Array(256);
|
||||
for(var i = 0; i < 256; i++) mtfSymbol[i] = i;
|
||||
var runPos, count, symCount, selector;
|
||||
runPos = count = symCount = selector = 0;
|
||||
var buf = new Uint32Array(bufsize);
|
||||
while(true){
|
||||
if(!(symCount--)){
|
||||
symCount = GROUP_SIZE - 1;
|
||||
if(selector >= nSelectors) throw "Error 4";
|
||||
hufGroup = groups[selectors[selector++]];
|
||||
base = hufGroup.base.subarray(1);
|
||||
limit = hufGroup.limit.subarray(1);
|
||||
}
|
||||
i = hufGroup.minLen;
|
||||
j = bits(i);
|
||||
while(true){
|
||||
if(i > hufGroup.maxLen) throw "Error 5";
|
||||
if(j <= limit[i]) break;
|
||||
i++;
|
||||
j = (j << 1) | bits(1);
|
||||
}
|
||||
j -= base[i];
|
||||
if(j < 0 || j >= MAX_SYMBOLS) throw "Error 6";
|
||||
var nextSym = hufGroup.permute[j];
|
||||
if (nextSym == SYMBOL_RUNA || nextSym == SYMBOL_RUNB) {
|
||||
if(!runPos){
|
||||
runPos = 1;
|
||||
t = 0;
|
||||
}
|
||||
if(nextSym == SYMBOL_RUNA) t += runPos;
|
||||
else t += 2 * runPos;
|
||||
runPos <<= 1;
|
||||
continue;
|
||||
}
|
||||
if(runPos){
|
||||
runPos = 0;
|
||||
if(count + t >= bufsize) throw "Error 7";
|
||||
uc = symToByte[mtfSymbol[0]];
|
||||
byteCount[uc] += t;
|
||||
while(t--) buf[count++] = uc;
|
||||
}
|
||||
if(nextSym > symTotal) break;
|
||||
if(count >= bufsize) throw "Error 8";
|
||||
i = nextSym -1;
|
||||
uc = mtfSymbol[i];
|
||||
mtfSymbol.splice(i, 1);
|
||||
mtfSymbol.splice(0, 0, uc);
|
||||
uc = symToByte[uc];
|
||||
byteCount[uc]++;
|
||||
buf[count++] = uc;
|
||||
}
|
||||
if(origPtr < 0 || origPtr >= count) throw "Error 9";
|
||||
var j = 0;
|
||||
for(var i = 0; i < 256; i++){
|
||||
k = j + byteCount[i];
|
||||
byteCount[i] = j;
|
||||
j = k;
|
||||
}
|
||||
for(var i = 0; i < count; i++){
|
||||
uc = buf[i] & 0xff;
|
||||
buf[byteCount[uc]] |= (i << 8);
|
||||
byteCount[uc]++;
|
||||
}
|
||||
var pos = 0, current = 0, run = 0;
|
||||
if(count) {
|
||||
pos = buf[origPtr];
|
||||
current = (pos & 0xff);
|
||||
pos >>= 8;
|
||||
run = -1;
|
||||
}
|
||||
count = count;
|
||||
var output = '';
|
||||
var copies, previous, outbyte;
|
||||
if(!len) len = Infinity;
|
||||
while(count){
|
||||
count--;
|
||||
previous = current;
|
||||
pos = buf[pos];
|
||||
current = pos & 0xff;
|
||||
pos >>= 8;
|
||||
if(run++ == 3){
|
||||
copies = current;
|
||||
outbyte = previous;
|
||||
current = -1;
|
||||
}else{
|
||||
copies = 1;
|
||||
outbyte = current;
|
||||
}
|
||||
while(copies--){
|
||||
output += (String.fromCharCode(outbyte));
|
||||
if(!--len) return output;
|
||||
}
|
||||
if(current != previous) run = 0;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
export default bzip2;
|
Loading…
Add table
Add a link
Reference in a new issue