2
0
mirror of https://github.com/gchq/CyberChef synced 2026-01-06 18:43:23 +00:00

Compare commits

..

33 Commits

Author SHA1 Message Date
n1474335
1400b5cca4 9.36.0 2022-03-29 12:48:47 +01:00
n1474335
98b5f9cc29 Updated CHANGELOG 2022-03-29 12:48:43 +01:00
n1474335
e766cb009e Merge branch 'hettysymes-SIGABA' 2022-03-29 12:45:56 +01:00
n1474335
993e276858 Tidied up Bletchley ops 2022-03-29 12:45:42 +01:00
n1474335
a762fb4df4 Merge branch 'SIGABA' of https://github.com/hettysymes/CyberChef into hettysymes-SIGABA 2022-03-29 12:26:39 +01:00
n1474335
00781fa459 Fixed lint 2022-03-29 11:55:41 +01:00
n1474335
c6e7367a4e 9.35.1 2022-03-28 17:15:51 +01:00
n1474335
3749bb1222 Merge branch 'AlexAndHisScripts-master' 2022-03-28 17:15:39 +01:00
n1474335
f3c83b2009 Merge branch 'master' of https://github.com/AlexAndHisScripts/CyberChef into AlexAndHisScripts-master 2022-03-28 17:14:57 +01:00
n1474335
d3583c31bc 9.35.0 2022-03-28 16:19:55 +01:00
n1474335
2f638d8c0c Updated CHANGELOG 2022-03-28 16:19:51 +01:00
n1474335
74e43ff65a Merge branch 't-8ch-base45' 2022-03-28 16:18:02 +01:00
n1474335
4f0b160ed3 Tidied up Base45 ops 2022-03-28 16:15:37 +01:00
n1474335
709b8696fc Merge branch 'base45' of https://github.com/t-8ch/CyberChef into t-8ch-base45 2022-03-28 16:04:34 +01:00
n1474335
e080c5d72e 9.34.2 2022-03-28 15:57:00 +01:00
n1474335
9cc177a9ad Code quality improvements 2022-03-28 15:56:15 +01:00
n1474335
60b5fe0e76 9.34.1 2022-03-28 15:42:16 +01:00
n1474335
9273f97d88 Updated dependencies 2022-03-28 15:42:11 +01:00
n1474335
98a6baf55a 9.34.0 2022-03-28 11:40:04 +01:00
n1474335
b5677387f5 Updated CHANGELOG 2022-03-28 11:39:57 +01:00
n1474335
cd3eaa4762 Merge branch 'n1073645-variationsOfString' 2022-03-28 11:37:42 +01:00
n1474335
9733bf65de Merge branch 'nodejs16' of https://github.com/john19696/CyberChef into john19696-nodejs16 2022-03-28 11:37:23 +01:00
Thomas Weißschuh
7db1f39473 base45: Implement highlighting 2021-11-09 21:12:49 +01:00
Thomas Weißschuh
6017578964 Add Base45 operations
Closes #1219

Co-developed-by: Cyril Delétré <cyril.deletre@gmail.com>
2021-11-09 21:12:44 +01:00
Alex Chambers_Jones
d2174725a9 Fixed reflected XSS described in issue 1265 2021-10-29 17:59:02 +01:00
n1073645
c01ce90e06 Tests Added 2020-07-06 11:20:54 +01:00
n1073645
d68c8cb845 Casing Variations 2020-07-06 10:44:05 +01:00
hettysymes
88947b9d42 Added operation description note and modified comment formatting 2020-06-08 12:27:40 +01:00
hettysymes
3c68ad1302 Modified control rotor stepping so the next control rotor steps once the previous rotor reaches "O" and added tests 2020-06-07 17:45:17 +01:00
hettysymes
e2b3389da6 Added SIGABA simple test 2020-06-06 19:47:15 +01:00
hettysymes
938385c18b Fixed grunt lint errors 2020-06-06 19:46:42 +01:00
hettysymes
5d01b06877 Added copyright and clarified description 2020-06-06 19:46:42 +01:00
hettysymes
f007c093eb Emulation of the WW2 SIGABA machine
I have created an emulation of the SIGABA machine and have tested it against some test data from a Master's thesis by Miao Ai: https://scholarworks.sjsu.edu/cgi/viewcontent.cgi?article=1237&context=etd_projects
2020-06-06 19:46:42 +01:00
34 changed files with 7244 additions and 11002 deletions

View File

@@ -28,7 +28,7 @@ jobs:
- name: Unit Tests
run: |
npm test
npx grunt testnodeconsumer
npm run testnodeconsumer
- name: Production Build
if: success()

View File

@@ -27,7 +27,7 @@ jobs:
- name: Unit Tests
run: |
npm test
npx grunt testnodeconsumer
npm run testnodeconsumer
- name: Production Build
if: success()

View File

@@ -28,7 +28,7 @@ jobs:
- name: Unit Tests
run: |
npm test
npx grunt testnodeconsumer
npm run testnodeconsumer
- name: Production Build
if: success()

View File

@@ -13,6 +13,15 @@ All major and minor version changes will be documented in this file. Details of
## Details
### [9.36.0] - 2022-03-29
- 'SIGABA' operation added [@hettysymes] | [#934]
### [9.35.0] - 2022-03-28
- 'To Base45' and 'From Base45' operations added [@t-8ch] | [#1242]
### [9.34.0] - 2022-03-28
- 'Get All Casings' operation added [@n1073645] | [#1065]
### [9.33.0] - 2022-03-25
- Updated to support Node 17 [@n1474335] [@john19696] [@t-8ch] | [[#1326] [#1313] [#1244]
- Improved CJS and ESM module support [@d98762625] | [#1037]
@@ -275,6 +284,9 @@ All major and minor version changes will be documented in this file. Details of
[9.36.0]: https://github.com/gchq/CyberChef/releases/tag/v9.36.0
[9.35.0]: https://github.com/gchq/CyberChef/releases/tag/v9.35.0
[9.34.0]: https://github.com/gchq/CyberChef/releases/tag/v9.34.0
[9.33.0]: https://github.com/gchq/CyberChef/releases/tag/v9.33.0
[9.32.0]: https://github.com/gchq/CyberChef/releases/tag/v9.32.0
[9.31.0]: https://github.com/gchq/CyberChef/releases/tag/v9.31.0
@@ -391,6 +403,7 @@ All major and minor version changes will be documented in this file. Details of
[@Danh4]: https://github.com/Danh4
[@john19696]: https://github.com/john19696
[@t-8ch]: https://github.com/t-8ch
[@hettysymes]: https://github.com/hettysymes
[8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7
[9a33498]: https://github.com/gchq/CyberChef/commit/9a33498fed26a8df9c9f35f39a78a174bf50a513
@@ -465,6 +478,7 @@ All major and minor version changes will be documented in this file. Details of
[#865]: https://github.com/gchq/CyberChef/pull/865
[#912]: https://github.com/gchq/CyberChef/pull/912
[#917]: https://github.com/gchq/CyberChef/pull/917
[#934]: https://github.com/gchq/CyberChef/pull/934
[#948]: https://github.com/gchq/CyberChef/pull/948
[#952]: https://github.com/gchq/CyberChef/pull/952
[#965]: https://github.com/gchq/CyberChef/pull/965
@@ -476,7 +490,9 @@ All major and minor version changes will be documented in this file. Details of
[#1037]: https://github.com/gchq/CyberChef/pull/1037
[#1045]: https://github.com/gchq/CyberChef/pull/1045
[#1049]: https://github.com/gchq/CyberChef/pull/1049
[#1065]: https://github.com/gchq/CyberChef/pull/1065
[#1083]: https://github.com/gchq/CyberChef/pull/1083
[#1242]: https://github.com/gchq/CyberChef/pull/1242
[#1244]: https://github.com/gchq/CyberChef/pull/1244
[#1313]: https://github.com/gchq/CyberChef/pull/1313
[#1326]: https://github.com/gchq/CyberChef/pull/1326

View File

@@ -200,50 +200,35 @@ module.exports = function (grunt) {
web: webpackProdConf(),
},
"webpack-dev-server": {
options: {
webpack: webpackConfig,
host: "0.0.0.0",
port: grunt.option("port") || 8080,
disableHostCheck: true,
overlay: true,
inline: false,
clientLogLevel: "error",
stats: {
children: false,
chunks: false,
modules: false,
entrypoints: false,
warningsFilter: [
/source-map/,
/dependency is an expression/,
/export 'default'/,
/Can't resolve 'sodium'/
],
}
},
options: webpackConfig,
start: {
webpack: {
mode: "development",
target: "web",
entry: Object.assign({
main: "./src/web/index.js"
}, moduleEntryPoints),
resolve: {
alias: {
"./config/modules/OpModules.mjs": "./config/modules/Default.mjs"
}
},
plugins: [
new webpack.DefinePlugin(BUILD_CONSTANTS),
new HtmlWebpackPlugin({
filename: "index.html",
template: "./src/web/html/index.html",
chunks: ["main"],
compileTime: compileTime,
version: pkg.version,
})
]
}
mode: "development",
target: "web",
entry: Object.assign({
main: "./src/web/index.js"
}, moduleEntryPoints),
resolve: {
alias: {
"./config/modules/OpModules.mjs": "./config/modules/Default.mjs"
}
},
devServer: {
port: grunt.option("port") || 8080,
client: {
logging: "error",
overlay: true
}
},
plugins: [
new webpack.DefinePlugin(BUILD_CONSTANTS),
new HtmlWebpackPlugin({
filename: "index.html",
template: "./src/web/html/index.html",
chunks: ["main"],
compileTime: compileTime,
version: pkg.version,
})
]
}
},
zip: {

View File

@@ -2,7 +2,6 @@
[![](https://github.com/gchq/CyberChef/workflows/Master%20Build,%20Test%20&%20Deploy/badge.svg)](https://github.com/gchq/CyberChef/actions?query=workflow%3A%22Master+Build%2C+Test+%26+Deploy%22)
[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/gchq/CyberChef.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/gchq/CyberChef/context:javascript)
[![dependencies Status](https://david-dm.org/gchq/CyberChef/status.svg)](https://david-dm.org/gchq/CyberChef)
[![npm](https://img.shields.io/npm/v/cyberchef.svg)](https://www.npmjs.com/package/cyberchef)
[![](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/gchq/CyberChef/blob/master/LICENSE)
[![Gitter](https://badges.gitter.im/gchq/CyberChef.svg)](https://gitter.im/gchq/CyberChef?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)

16648
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "9.33.1",
"version": "9.36.0",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef",
@@ -45,18 +45,16 @@
"@babel/plugin-transform-runtime": "^7.17.0",
"@babel/preset-env": "^7.16.11",
"@babel/runtime": "^7.17.8",
"autoprefixer": "^10.3.1",
"autoprefixer": "^10.4.4",
"babel-loader": "^8.2.4",
"babel-plugin-dynamic-import-node": "^2.3.3",
"chromedriver": "^99.0.0",
"cli-progress": "^3.9.0",
"cli-progress": "^3.10.0",
"colors": "^1.4.0",
"copy-webpack-plugin": "^9.0.1",
"copy-webpack-plugin": "^10.2.4",
"core-js": "^3.21.1",
"css-loader": "5.2.7",
"eslint": "^8.11.0",
"exports-loader": "^3.0.0",
"file-loader": "^6.2.0",
"css-loader": "6.7.1",
"eslint": "^8.12.0",
"grunt": "^1.4.1",
"grunt-chmod": "~1.1.1",
"grunt-concurrent": "^3.0.0",
@@ -66,25 +64,23 @@
"grunt-contrib-watch": "^1.1.0",
"grunt-eslint": "^24.0.0",
"grunt-exec": "~3.0.0",
"grunt-webpack": "^4.0.3",
"grunt-webpack": "^5.0.0",
"grunt-zip": "^0.18.2",
"html-webpack-plugin": "^5.3.2",
"imports-loader": "^3.0.0",
"mini-css-extract-plugin": "1.3.7",
"nightwatch": "^1.7.8",
"postcss": "^8.3.6",
"html-webpack-plugin": "^5.5.0",
"imports-loader": "^3.1.1",
"mini-css-extract-plugin": "2.6.0",
"nightwatch": "^2.0.10",
"postcss": "^8.4.12",
"postcss-css-variables": "^0.18.0",
"postcss-import": "^14.0.2",
"postcss-loader": "^6.1.1",
"prompt": "^1.1.0",
"sass-loader": "^12.1.0",
"sitemap": "^7.0.0",
"style-loader": "^3.2.1",
"svg-url-loader": "^7.1.1",
"url-loader": "^4.1.1",
"postcss-import": "^14.1.0",
"postcss-loader": "^6.2.1",
"prompt": "^1.2.2",
"sass-loader": "^12.6.0",
"sitemap": "^7.1.1",
"terser": "^5.12.1",
"webpack": "^5.70.0",
"webpack-bundle-analyzer": "^4.4.2",
"webpack-dev-server": "3.11.2",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-dev-server": "4.7.4",
"webpack-node-externals": "^3.0.0",
"worker-loader": "^3.0.8"
},
@@ -94,22 +90,22 @@
"avsc": "^5.7.3",
"babel-plugin-transform-builtin-extend": "1.1.2",
"bcryptjs": "^2.4.3",
"bignumber.js": "^9.0.1",
"blakejs": "^1.1.1",
"bootstrap": "4.6.0",
"bignumber.js": "^9.0.2",
"blakejs": "^1.2.1",
"bootstrap": "4.6.1",
"bootstrap-colorpicker": "^3.4.0",
"bootstrap-material-design": "^4.1.3",
"browserify-zlib": "^0.2.0",
"bson": "^4.4.1",
"bson": "^4.6.2",
"buffer": "^6.0.3",
"cbor": "5.0.1",
"cbor": "8.1.0",
"chi-squared": "^1.1.0",
"codepage": "^1.15.0",
"crypto-api": "^0.8.5",
"crypto-browserify": "^3.12.0",
"crypto-js": "^4.1.1",
"ctph.js": "0.0.5",
"d3": "6.5.0",
"d3": "7.3.0",
"d3-hexbin": "^0.2.2",
"diff": "^5.0.0",
"es6-promisify": "^7.0.0",
@@ -119,7 +115,7 @@
"file-saver": "^2.0.5",
"flat": "^5.0.2",
"geodesy": "1.1.3",
"highlight.js": "^11.2.0",
"highlight.js": "^11.5.0",
"jimp": "^0.16.1",
"jquery": "3.6.0",
"js-crc": "^0.2.0",
@@ -128,18 +124,18 @@
"jsonpath": "^1.1.1",
"jsonwebtoken": "^8.5.1",
"jsqr": "^1.4.0",
"jsrsasign": "^10.4.0",
"jsrsasign": "^10.5.14",
"kbpgp": "2.1.15",
"libbzip2-wasm": "0.0.4",
"libyara-wasm": "^1.1.0",
"lodash": "^4.17.21",
"loglevel": "^1.7.1",
"loglevel": "^1.8.0",
"loglevel-message-prefix": "^3.0.0",
"markdown-it": "^12.3.2",
"moment": "^2.29.1",
"moment-timezone": "^0.5.33",
"moment-timezone": "^0.5.34",
"ngeohash": "^0.6.3",
"node-forge": "^0.10.0",
"node-forge": "^1.3.0",
"node-md6": "^0.1.0",
"node-sass": "^7.0.1",
"nodom": "^2.4.0",
@@ -153,13 +149,12 @@
"qr-image": "^3.2.0",
"scryptsy": "^2.1.0",
"snackbarjs": "^1.1.0",
"sortablejs": "^1.14.0",
"split.js": "^1.6.4",
"sortablejs": "^1.15.0",
"split.js": "^1.6.5",
"ssdeep.js": "0.0.3",
"stream-browserify": "^3.0.0",
"terser": "^5.7.1",
"tesseract.js": "2.1.5",
"ua-parser-js": "^0.7.28",
"ua-parser-js": "^1.0.2",
"unorm": "^1.6.0",
"utf8": "^3.0.0",
"vkbeautify": "^0.99.3",
@@ -173,7 +168,7 @@
"build": "npx grunt prod",
"repl": "node --experimental-modules --experimental-json-modules --experimental-specifier-resolution=node --no-warnings src/node/repl.mjs",
"test": "npx grunt configTests && node --experimental-modules --experimental-json-modules --no-warnings --no-deprecation tests/node/index.mjs && node --experimental-modules --experimental-json-modules --no-warnings --no-deprecation tests/operations/index.mjs",
"test-node-consumer": "npx grunt testnodeconsumer",
"testnodeconsumer": "npx grunt testnodeconsumer",
"testui": "npx grunt testui",
"testuidev": "npx nightwatch --env=dev",
"lint": "npx grunt lint",

View File

@@ -723,7 +723,8 @@ class Utils {
}
if (removeScriptAndStyle) {
htmlStr = recursiveRemove(/<(script|style)[^>]*>.*?<\/(script|style)>/gi, htmlStr);
htmlStr = recursiveRemove(/<script[^>]*>.*?<\/script>/gi, htmlStr);
htmlStr = recursiveRemove(/<style[^>]*>.*?<\/style>/gi, htmlStr);
}
return htmlStr.replace(/<[^>]+>/g, "");
}
@@ -1206,6 +1207,30 @@ class Utils {
}[token];
}
/**
* Iterate object in chunks of given size.
*
* @param {Iterable} iterable
* @param {number} chunksize
*/
static* chunked(iterable, chunksize) {
const iterator = iterable[Symbol.iterator]();
while (true) {
const res = [];
for (let i = 0; i < chunksize; i++) {
const next = iterator.next();
if (next.done) {
break;
}
res.push(next.value);
}
if (res.length) {
yield res;
} else {
return;
}
}
}
}
/**

View File

@@ -20,6 +20,8 @@
"From Octal",
"To Base32",
"From Base32",
"To Base45",
"From Base45",
"To Base58",
"From Base58",
"To Base62",
@@ -118,7 +120,8 @@
"Multiple Bombe",
"Typex",
"Lorenz",
"Colossus"
"Colossus",
"SIGABA"
]
},
{
@@ -230,6 +233,7 @@
"From Case Insensitive Regex",
"Add line numbers",
"Remove line numbers",
"Get All Casings",
"To Table",
"Reverse",
"Sort",

27
src/core/lib/Base45.mjs Normal file
View File

@@ -0,0 +1,27 @@
/**
* Base45 resources.
*
* @author Thomas Weißschuh [thomas@t-8ch.de]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
/**
* Highlight to Base45
*/
export function highlightToBase45(pos, args) {
pos[0].start = Math.floor(pos[0].start / 2) * 3;
pos[0].end = Math.ceil(pos[0].end / 2) * 3;
return pos;
}
/**
* Highlight from Base45
*/
export function highlightFromBase45(pos, args) {
pos[0].start = Math.floor(pos[0].start / 3) * 2;
pos[0].end = Math.ceil(pos[0].end / 3) * 2;
return pos;
}
export const ALPHABET = "0-9A-Z $%*+\\-./:";

View File

@@ -6,6 +6,7 @@
*/
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
/**
* @constant
@@ -128,7 +129,7 @@ export function getScatterValuesWithColour(input, recordDelimiter, fieldDelimite
if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
if (Number.isNaN(y)) throw new OperationError("Values must be numbers in base 10.");
return [x, y, colour];
return [x, y, Utils.escapeHtml(colour)];
});
return { headings, values };

502
src/core/lib/SIGABA.mjs Normal file
View File

@@ -0,0 +1,502 @@
/**
* Emulation of the SIGABA machine
*
* @author hettysymes
* @copyright hettysymes 2020
* @license Apache-2.0
*/
/**
* A set of randomised example SIGABA cipher/control rotors (these rotors are interchangeable). Cipher and control rotors can be referred to as C and R rotors respectively.
*/
export const CR_ROTORS = [
{name: "Example 1", value: "SRGWANHPJZFXVIDQCEUKBYOLMT"},
{name: "Example 2", value: "THQEFSAZVKJYULBODCPXNIMWRG"},
{name: "Example 3", value: "XDTUYLEVFNQZBPOGIRCSMHWKAJ"},
{name: "Example 4", value: "LOHDMCWUPSTNGVXYFJREQIKBZA"},
{name: "Example 5", value: "ERXWNZQIJYLVOFUMSGHTCKPBDA"},
{name: "Example 6", value: "FQECYHJIOUMDZVPSLKRTGWXBAN"},
{name: "Example 7", value: "TBYIUMKZDJSOPEWXVANHLCFQGR"},
{name: "Example 8", value: "QZUPDTFNYIAOMLEBWJXCGHKRSV"},
{name: "Example 9", value: "CZWNHEMPOVXLKRSIDGJFYBTQAU"},
{name: "Example 10", value: "ENPXJVKYQBFZTICAGMOHWRLDUS"}
];
/**
* A set of randomised example SIGABA index rotors (may be referred to as I rotors).
*/
export const I_ROTORS = [
{name: "Example 1", value: "6201348957"},
{name: "Example 2", value: "6147253089"},
{name: "Example 3", value: "8239647510"},
{name: "Example 4", value: "7194835260"},
{name: "Example 5", value: "4873205916"}
];
export const NUMBERS = "0123456789".split("");
/**
* Converts a letter to uppercase (if it already isn't)
*
* @param {char} letter - letter to convert to uppercase
* @returns {char}
*/
export function convToUpperCase(letter) {
const charCode = letter.charCodeAt();
if (97<=charCode && charCode<=122) {
return String.fromCharCode(charCode-32);
}
return letter;
}
/**
* The SIGABA machine consisting of the 3 rotor banks: cipher, control and index banks.
*/
export class SigabaMachine {
/**
* SigabaMachine constructor
*
* @param {Object[]} cipherRotors - list of CRRotors
* @param {Object[]} controlRotors - list of CRRotors
* @param {object[]} indexRotors - list of IRotors
*/
constructor(cipherRotors, controlRotors, indexRotors) {
this.cipherBank = new CipherBank(cipherRotors);
this.controlBank = new ControlBank(controlRotors);
this.indexBank = new IndexBank(indexRotors);
}
/**
* Steps all the correct rotors in the machine.
*/
step() {
const controlOut = this.controlBank.goThroughControl();
const indexOut = this.indexBank.goThroughIndex(controlOut);
this.cipherBank.step(indexOut);
}
/**
* Encrypts a letter. A space is converted to a "Z" before encryption, and a "Z" is converted to an "X". This allows spaces to be encrypted.
*
* @param {char} letter - letter to encrypt
* @returns {char}
*/
encryptLetter(letter) {
letter = convToUpperCase(letter);
if (letter === " ") {
letter = "Z";
} else if (letter === "Z") {
letter = "X";
}
const encryptedLetter = this.cipherBank.encrypt(letter);
this.step();
return encryptedLetter;
}
/**
* Decrypts a letter. A letter decrypted as a "Z" is converted to a space before it is output, since spaces are converted to "Z"s before encryption.
*
* @param {char} letter - letter to decrypt
* @returns {char}
*/
decryptLetter(letter) {
letter = convToUpperCase(letter);
let decryptedLetter = this.cipherBank.decrypt(letter);
if (decryptedLetter === "Z") {
decryptedLetter = " ";
}
this.step();
return decryptedLetter;
}
/**
* Encrypts a message of one or more letters
*
* @param {string} msg - message to encrypt
* @returns {string}
*/
encrypt(msg) {
let ciphertext = "";
for (const letter of msg) {
ciphertext = ciphertext.concat(this.encryptLetter(letter));
}
return ciphertext;
}
/**
* Decrypts a message of one or more letters
*
* @param {string} msg - message to decrypt
* @returns {string}
*/
decrypt(msg) {
let plaintext = "";
for (const letter of msg) {
plaintext = plaintext.concat(this.decryptLetter(letter));
}
return plaintext;
}
}
/**
* The cipher rotor bank consists of 5 cipher rotors in either a forward or reversed orientation.
*/
export class CipherBank {
/**
* CipherBank constructor
*
* @param {Object[]} rotors - list of CRRotors
*/
constructor(rotors) {
this.rotors = rotors;
}
/**
* Encrypts a letter through the cipher rotors (signal goes from left-to-right)
*
* @param {char} inputPos - the input position of the signal (letter to be encrypted)
* @returns {char}
*/
encrypt(inputPos) {
for (const rotor of this.rotors) {
inputPos = rotor.crypt(inputPos, "leftToRight");
}
return inputPos;
}
/**
* Decrypts a letter through the cipher rotors (signal goes from right-to-left)
*
* @param {char} inputPos - the input position of the signal (letter to be decrypted)
* @returns {char}
*/
decrypt(inputPos) {
const revOrderedRotors = [...this.rotors].reverse();
for (const rotor of revOrderedRotors) {
inputPos = rotor.crypt(inputPos, "rightToLeft");
}
return inputPos;
}
/**
* Step the cipher rotors forward according to the inputs from the index rotors
*
* @param {number[]} indexInputs - the inputs from the index rotors
*/
step(indexInputs) {
const logicDict = {0: [0, 9], 1: [7, 8], 2: [5, 6], 3: [3, 4], 4: [1, 2]};
const rotorsToMove = [];
for (const key in logicDict) {
const item = logicDict[key];
for (const i of indexInputs) {
if (item.includes(i)) {
rotorsToMove.push(this.rotors[key]);
break;
}
}
}
for (const rotor of rotorsToMove) {
rotor.step();
}
}
}
/**
* The control rotor bank consists of 5 control rotors in either a forward or reversed orientation. Signals to the control rotor bank always go from right-to-left.
*/
export class ControlBank {
/**
* ControlBank constructor. The rotors have been reversed as signals go from right-to-left through the control rotors.
*
* @param {Object[]} rotors - list of CRRotors
*/
constructor(rotors) {
this.rotors = [...rotors].reverse();
}
/**
* Encrypts a letter.
*
* @param {char} inputPos - the input position of the signal
* @returns {char}
*/
crypt(inputPos) {
for (const rotor of this.rotors) {
inputPos = rotor.crypt(inputPos, "rightToLeft");
}
return inputPos;
}
/**
* Gets the outputs of the control rotors. The inputs to the control rotors are always "F", "G", "H" and "I".
*
* @returns {number[]}
*/
getOutputs() {
const outputs = [this.crypt("F"), this.crypt("G"), this.crypt("H"), this.crypt("I")];
const logicDict = {1: "B", 2: "C", 3: "DE", 4: "FGH", 5: "IJK", 6: "LMNO", 7: "PQRST", 8: "UVWXYZ", 9: "A"};
const numberOutputs = [];
for (const key in logicDict) {
const item = logicDict[key];
for (const output of outputs) {
if (item.includes(output)) {
numberOutputs.push(key);
break;
}
}
}
return numberOutputs;
}
/**
* Steps the control rotors. Only 3 of the control rotors step: one after every encryption, one after every 26, and one after every 26 squared.
*/
step() {
const MRotor = this.rotors[1], FRotor = this.rotors[2], SRotor = this.rotors[3];
// 14 is the offset of "O" from "A" - the next rotor steps once the previous rotor reaches "O"
if (FRotor.state === 14) {
if (MRotor.state === 14) {
SRotor.step();
}
MRotor.step();
}
FRotor.step();
}
/**
* The goThroughControl function combines getting the outputs from the control rotor bank and then stepping them.
*
* @returns {number[]}
*/
goThroughControl() {
const outputs = this.getOutputs();
this.step();
return outputs;
}
}
/**
* The index rotor bank consists of 5 index rotors all placed in the forwards orientation.
*/
export class IndexBank {
/**
* IndexBank constructor
*
* @param {Object[]} rotors - list of IRotors
*/
constructor(rotors) {
this.rotors = rotors;
}
/**
* Encrypts a number.
*
* @param {number} inputPos - the input position of the signal
* @returns {number}
*/
crypt(inputPos) {
for (const rotor of this.rotors) {
inputPos = rotor.crypt(inputPos);
}
return inputPos;
}
/**
* The goThroughIndex function takes the inputs from the control rotor bank and returns the list of outputs after encryption through the index rotors.
*
* @param {number[]} controlInputs - inputs from the control rotors
* @returns {number[]}
*/
goThroughIndex(controlInputs) {
const outputs = [];
for (const inp of controlInputs) {
outputs.push(this.crypt(inp));
}
return outputs;
}
}
/**
* Rotor class
*/
export class Rotor {
/**
* Rotor constructor
*
* @param {number[]} wireSetting - the wirings within the rotor: mapping from left-to-right, the index of the number in the list maps onto the number at that index
* @param {bool} rev - true if the rotor is reversed, false if it isn't
* @param {number} key - the starting position or state of the rotor
*/
constructor(wireSetting, key, rev) {
this.state = key;
this.numMapping = this.getNumMapping(wireSetting, rev);
this.posMapping = this.getPosMapping(rev);
}
/**
* Get the number mapping from the wireSetting (only different from wireSetting if rotor is reversed)
*
* @param {number[]} wireSetting - the wirings within the rotors
* @param {bool} rev - true if reversed, false if not
* @returns {number[]}
*/
getNumMapping(wireSetting, rev) {
if (rev===false) {
return wireSetting;
} else {
const length = wireSetting.length;
const tempMapping = new Array(length);
for (let i=0; i<length; i++) {
tempMapping[wireSetting[i]] = i;
}
return tempMapping;
}
}
/**
* Get the position mapping (how the position numbers map onto the numbers of the rotor)
*
* @param {bool} rev - true if reversed, false if not
* @returns {number[]}
*/
getPosMapping(rev) {
const length = this.numMapping.length;
const posMapping = [];
if (rev===false) {
for (let i = this.state; i < this.state+length; i++) {
let res = i%length;
if (res<0) {
res += length;
}
posMapping.push(res);
}
} else {
for (let i = this.state; i > this.state-length; i--) {
let res = i%length;
if (res<0) {
res += length;
}
posMapping.push(res);
}
}
return posMapping;
}
/**
* Encrypt/decrypt data. This process is identical to the rotors of cipher machines such as Enigma or Typex.
*
* @param {number} inputPos - the input position of the signal (the data to encrypt/decrypt)
* @param {string} direction - one of "leftToRight" and "rightToLeft", states the direction in which the signal passes through the rotor
* @returns {number}
*/
cryptNum(inputPos, direction) {
const inpNum = this.posMapping[inputPos];
let outNum;
if (direction === "leftToRight") {
outNum = this.numMapping[inpNum];
} else if (direction === "rightToLeft") {
outNum = this.numMapping.indexOf(inpNum);
}
const outPos = this.posMapping.indexOf(outNum);
return outPos;
}
/**
* Steps the rotor. The number at position 0 will be moved to position 1 etc.
*/
step() {
const lastNum = this.posMapping.pop();
this.posMapping.splice(0, 0, lastNum);
this.state = this.posMapping[0];
}
}
/**
* A CRRotor is a cipher (C) or control (R) rotor. These rotors are identical and interchangeable. A C or R rotor consists of 26 contacts, one for each letter, and may be put into either a forwards of reversed orientation.
*/
export class CRRotor extends Rotor {
/**
* CRRotor constructor
*
* @param {string} wireSetting - the rotor wirings (string of letters)
* @param {char} key - initial state of rotor
* @param {bool} rev - true if reversed, false if not
*/
constructor(wireSetting, key, rev=false) {
wireSetting = wireSetting.split("").map(CRRotor.letterToNum);
super(wireSetting, CRRotor.letterToNum(key), rev);
}
/**
* Static function which converts a letter into its number i.e. its offset from the letter "A"
*
* @param {char} letter - letter to convert to number
* @returns {number}
*/
static letterToNum(letter) {
return letter.charCodeAt()-65;
}
/**
* Static function which converts a number (a letter's offset from "A") into its letter
*
* @param {number} num - number to convert to letter
* @returns {char}
*/
static numToLetter(num) {
return String.fromCharCode(num+65);
}
/**
* Encrypts/decrypts a letter.
*
* @param {char} inputPos - the input position of the signal ("A" refers to position 0 etc.)
* @param {string} direction - one of "leftToRight" and "rightToLeft"
* @returns {char}
*/
crypt(inputPos, direction) {
inputPos = CRRotor.letterToNum(inputPos);
const outPos = this.cryptNum(inputPos, direction);
return CRRotor.numToLetter(outPos);
}
}
/**
* An IRotor is an index rotor, which consists of 10 contacts each numbered from 0 to 9. Unlike C and R rotors, they cannot be put in the reversed orientation. The index rotors do not step at any point during encryption or decryption.
*/
export class IRotor extends Rotor {
/**
* IRotor constructor
*
* @param {string} wireSetting - the rotor wirings (string of numbers)
* @param {char} key - initial state of rotor
*/
constructor(wireSetting, key) {
wireSetting = wireSetting.split("").map(Number);
super(wireSetting, Number(key), false);
}
/**
* Encrypts a number
*
* @param {number} inputPos - the input position of the signal
* @returns {number}
*/
crypt(inputPos) {
return this.cryptNum(inputPos, "leftToRight");
}
}

View File

@@ -1,6 +1,9 @@
/**
* Emulation of the Bombe machine.
*
* Tested against the Bombe Rebuild at Bletchley Park's TNMOC
* using a variety of inputs and settings to confirm correctness.
*
* @author s2224834
* @copyright Crown Copyright 2019
* @license Apache-2.0

View File

@@ -1,6 +1,9 @@
/**
* Emulation of Colossus.
*
* Tested against the Colossus Rebuild at Bletchley Park's TNMOC
* using a variety of inputs and settings to confirm correctness.
*
* @author VirtualColossus [martin@virtualcolossus.co.uk]
* @copyright Crown Copyright 2019
* @license Apache-2.0

View File

@@ -67,7 +67,7 @@ class DeriveEVPKey extends Operation {
iterations = args[2],
hasher = args[3],
salt = Utils.convertToByteString(args[4].string, args[4].option),
key = CryptoJS.EvpKDF(passphrase, salt, {
key = CryptoJS.EvpKDF(passphrase, salt, { // lgtm [js/insufficient-password-hash]
keySize: keySize,
hasher: CryptoJS.algo[hasher],
iterations: iterations,

View File

@@ -1,6 +1,9 @@
/**
* Emulation of the Enigma machine.
*
* Tested against various genuine Enigma machines using a variety of inputs
* and settings to confirm correctness.
*
* @author s2224834
* @copyright Crown Copyright 2019
* @license Apache-2.0

View File

@@ -0,0 +1,89 @@
/**
* @author Thomas Weißschuh [thomas@t-8ch.de]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import {ALPHABET, highlightToBase45, highlightFromBase45} from "../lib/Base45.mjs";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
/**
* From Base45 operation
*/
class FromBase45 extends Operation {
/**
* FromBase45 constructor
*/
constructor() {
super();
this.name = "From Base45";
this.module = "Default";
this.description = "Base45 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers. The high number base results in shorter strings than with the decimal or hexadecimal system. Base45 is optimized for usage with QR codes.";
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
name: "Alphabet",
type: "string",
value: ALPHABET
}
];
this.highlight = highlightFromBase45;
this.highlightReverse = highlightToBase45;
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
if (!input) return [];
const alphabet = Utils.expandAlphRange(args[0]);
const res = [];
for (const triple of Utils.chunked(input, 3)) {
triple.reverse();
let b = 0;
for (const c of triple) {
const idx = alphabet.indexOf(c);
if (idx === -1) {
throw new OperationError(`Character not in alphabet: '${c}'`);
}
b *= 45;
b += idx;
}
if (b > 65535) {
throw new OperationError(`Triplet too large: '${triple.join("")}'`);
}
if (triple.length > 2) {
/**
* The last triple may only have 2 bytes so we push the MSB when we got 3 bytes
* Pushing MSB
*/
res.push(b >> 8);
}
/**
* Pushing LSB
*/
res.push(b & 0xff);
}
return res;
}
}
export default FromBase45;

View File

@@ -0,0 +1,53 @@
/**
* @author n1073645 [n1073645@gmail.com]
* @copyright Crown Copyright 2020
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
/**
* Permutate String operation
*/
class GetAllCasings extends Operation {
/**
* GetAllCasings constructor
*/
constructor() {
super();
this.name = "Get All Casings";
this.module = "Default";
this.description = "Outputs all possible casing variations of a string.";
this.infoURL = "";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const length = input.length;
const max = 1 << length;
input = input.toLowerCase();
let result = "";
for (let i = 0; i < max; i++) {
const temp = input.split("");
for (let j = 0; j < length; j++) {
if (((i >> j) & 1) === 1) {
temp[j] = temp[j].toUpperCase();
}
}
result += temp.join("") + "\n";
}
return result;
}
}
export default GetAllCasings;

View File

@@ -1,6 +1,9 @@
/**
* Emulation of the Lorenz SZ40/42a/42b cipher attachment.
*
* Tested against the Colossus Rebuild at Bletchley Park's TNMOC
* using a variety of inputs and settings to confirm correctness.
*
* @author VirtualColossus [martin@virtualcolossus.co.uk]
* @copyright Crown Copyright 2019
* @license Apache-2.0

View File

@@ -0,0 +1,287 @@
/**
* Emulation of the SIGABA machine.
*
* @author hettysymes
* @copyright hettysymes 2020
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import {LETTERS} from "../lib/Enigma.mjs";
import {NUMBERS, CR_ROTORS, I_ROTORS, SigabaMachine, CRRotor, IRotor} from "../lib/SIGABA.mjs";
/**
* Sigaba operation
*/
class Sigaba extends Operation {
/**
* Sigaba constructor
*/
constructor() {
super();
this.name = "SIGABA";
this.module = "Bletchley";
this.description = "Encipher/decipher with the WW2 SIGABA machine. <br><br>SIGABA, otherwise known as ECM Mark II, was used by the United States for message encryption during WW2 up to the 1950s. It was developed in the 1930s by the US Army and Navy, and has up to this day never been broken. Consisting of 15 rotors: 5 cipher rotors and 10 rotors (5 control rotors and 5 index rotors) controlling the stepping of the cipher rotors, the rotor stepping for SIGABA is much more complex than other rotor machines of its time, such as Enigma. All example rotor wirings are random example sets.<br><br>To configure rotor wirings, for the cipher and control rotors enter a string of letters which map from A to Z, and for the index rotors enter a sequence of numbers which map from 0 to 9. Note that encryption is not the same as decryption, so first choose the desired mode. <br><br> Note: Whilst this has been tested against other software emulators, it has not been tested against hardware.";
this.infoURL = "https://wikipedia.org/wiki/SIGABA";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "1st (left-hand) cipher rotor",
type: "editableOption",
value: CR_ROTORS,
defaultIndex: 0
},
{
name: "1st cipher rotor reversed",
type: "boolean",
value: false
},
{
name: "1st cipher rotor intial value",
type: "option",
value: LETTERS
},
{
name: "2nd cipher rotor",
type: "editableOption",
value: CR_ROTORS,
defaultIndex: 0
},
{
name: "2nd cipher rotor reversed",
type: "boolean",
value: false
},
{
name: "2nd cipher rotor intial value",
type: "option",
value: LETTERS
},
{
name: "3rd (middle) cipher rotor",
type: "editableOption",
value: CR_ROTORS,
defaultIndex: 0
},
{
name: "3rd cipher rotor reversed",
type: "boolean",
value: false
},
{
name: "3rd cipher rotor intial value",
type: "option",
value: LETTERS
},
{
name: "4th cipher rotor",
type: "editableOption",
value: CR_ROTORS,
defaultIndex: 0
},
{
name: "4th cipher rotor reversed",
type: "boolean",
value: false
},
{
name: "4th cipher rotor intial value",
type: "option",
value: LETTERS
},
{
name: "5th (right-hand) cipher rotor",
type: "editableOption",
value: CR_ROTORS,
defaultIndex: 0
},
{
name: "5th cipher rotor reversed",
type: "boolean",
value: false
},
{
name: "5th cipher rotor intial value",
type: "option",
value: LETTERS
},
{
name: "1st (left-hand) control rotor",
type: "editableOption",
value: CR_ROTORS,
defaultIndex: 0
},
{
name: "1st control rotor reversed",
type: "boolean",
value: false
},
{
name: "1st control rotor intial value",
type: "option",
value: LETTERS
},
{
name: "2nd control rotor",
type: "editableOption",
value: CR_ROTORS,
defaultIndex: 0
},
{
name: "2nd control rotor reversed",
type: "boolean",
value: false
},
{
name: "2nd control rotor intial value",
type: "option",
value: LETTERS
},
{
name: "3rd (middle) control rotor",
type: "editableOption",
value: CR_ROTORS,
defaultIndex: 0
},
{
name: "3rd control rotor reversed",
type: "boolean",
value: false
},
{
name: "3rd control rotor intial value",
type: "option",
value: LETTERS
},
{
name: "4th control rotor",
type: "editableOption",
value: CR_ROTORS,
defaultIndex: 0
},
{
name: "4th control rotor reversed",
type: "boolean",
value: false
},
{
name: "4th control rotor intial value",
type: "option",
value: LETTERS
},
{
name: "5th (right-hand) control rotor",
type: "editableOption",
value: CR_ROTORS,
defaultIndex: 0
},
{
name: "5th control rotor reversed",
type: "boolean",
value: false
},
{
name: "5th control rotor intial value",
type: "option",
value: LETTERS
},
{
name: "1st (left-hand) index rotor",
type: "editableOption",
value: I_ROTORS,
defaultIndex: 0
},
{
name: "1st index rotor intial value",
type: "option",
value: NUMBERS
},
{
name: "2nd index rotor",
type: "editableOption",
value: I_ROTORS,
defaultIndex: 0
},
{
name: "2nd index rotor intial value",
type: "option",
value: NUMBERS
},
{
name: "3rd (middle) index rotor",
type: "editableOption",
value: I_ROTORS,
defaultIndex: 0
},
{
name: "3rd index rotor intial value",
type: "option",
value: NUMBERS
},
{
name: "4th index rotor",
type: "editableOption",
value: I_ROTORS,
defaultIndex: 0
},
{
name: "4th index rotor intial value",
type: "option",
value: NUMBERS
},
{
name: "5th (right-hand) index rotor",
type: "editableOption",
value: I_ROTORS,
defaultIndex: 0
},
{
name: "5th index rotor intial value",
type: "option",
value: NUMBERS
},
{
name: "SIGABA mode",
type: "option",
value: ["Encrypt", "Decrypt"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const sigabaSwitch = args[40];
const cipherRotors = [];
const controlRotors = [];
const indexRotors = [];
for (let i=0; i<5; i++) {
const rotorWiring = args[i*3];
cipherRotors.push(new CRRotor(rotorWiring, args[i*3+2], args[i*3+1]));
}
for (let i=5; i<10; i++) {
const rotorWiring = args[i*3];
controlRotors.push(new CRRotor(rotorWiring, args[i*3+2], args[i*3+1]));
}
for (let i=15; i<20; i++) {
const rotorWiring = args[i*2];
indexRotors.push(new IRotor(rotorWiring, args[i*2+1]));
}
const sigaba = new SigabaMachine(cipherRotors, controlRotors, indexRotors);
let result;
if (sigabaSwitch === "Encrypt") {
result = sigaba.encrypt(input);
} else if (sigabaSwitch === "Decrypt") {
result = sigaba.decrypt(input);
}
return result;
}
}
export default Sigaba;

View File

@@ -87,7 +87,7 @@ class ScatterChart extends Operation {
const recordDelimiter = Utils.charRep(args[0]),
fieldDelimiter = Utils.charRep(args[1]),
columnHeadingsAreIncluded = args[2],
fillColour = args[5],
fillColour = Utils.escapeHtml(args[5]),
radius = args[6],
colourInInput = args[7],
dimension = 500;

View File

@@ -72,7 +72,10 @@ class SeriesChart extends Operation {
fieldDelimiter = Utils.charRep(args[1]),
xLabel = args[2],
pipRadius = args[3],
seriesColours = args[4].split(","),
// Escape HTML from all colours to prevent reflected XSS. See https://github.com/gchq/CyberChef/issues/1265
seriesColours = args[4].split(",").map((colour) => {
return Utils.escapeHtml(colour);
}),
svgWidth = 500,
interSeriesPadding = 20,
xAxisHeight = 50,

View File

@@ -0,0 +1,78 @@
/**
* @author Thomas Weißschuh [thomas@t-8ch.de]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import {ALPHABET, highlightToBase45, highlightFromBase45} from "../lib/Base45.mjs";
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
/**
* To Base45 operation
*/
class ToBase45 extends Operation {
/**
* ToBase45 constructor
*/
constructor() {
super();
this.name = "To Base45";
this.module = "Default";
this.description = "Base45 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers. The high number base results in shorter strings than with the decimal or hexadecimal system. Base45 is optimized for usage with QR codes.";
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [
{
name: "Alphabet",
type: "string",
value: ALPHABET
}
];
this.highlight = highlightToBase45;
this.highlightReverse = highlightFromBase45;
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
input = new Uint8Array(input);
const alphabet = Utils.expandAlphRange(args[0]);
if (!input) return "";
const res = [];
for (const pair of Utils.chunked(input, 2)) {
let b = 0;
for (const e of pair) {
b *= 256;
b += e;
}
let chars = 0;
do {
res.push(alphabet[b % 45]);
chars++;
b = Math.floor(b / 45);
} while (b > 0);
if (chars < 2) {
res.push("0");
}
}
return res.join("");
}
}
export default ToBase45;

View File

@@ -1,6 +1,9 @@
/**
* Emulation of the Typex machine.
*
* Tested against a genuine Typex machine using a variety of inputs
* and settings to confirm correctness.
*
* @author s2224834
* @copyright Crown Copyright 2019
* @license Apache-2.0

View File

@@ -29,7 +29,7 @@
<meta name="description" content="The Cyber Swiss Army Knife - a web app for encryption, encoding, compression and data analysis" />
<meta name="keywords" content="base64, hex, decode, encode, encrypt, decrypt, compress, decompress, regex, regular expressions, hash, crypt, hexadecimal, user agent, url, certificate, x.509, parser, JSON, gzip, md5, sha1, aes, des, blowfish, xor" />
<link rel="icon" type="image/ico" href="<%- require('../static/images/favicon.ico').default %>" />
<link rel="icon" type="image/ico" href="<%- require('../static/images/favicon.ico') %>" />
<script type="application/javascript">
"use strict";
@@ -197,7 +197,7 @@
</button>
<button type="button" class="mx-2 btn btn-lg btn-success btn-raised btn-block" id="bake">
<img aria-hidden="true" src="<%- require('../static/images/cook_male-32x32.png').default %>" alt="Chef Icon"/>
<img aria-hidden="true" src="<%- require('../static/images/cook_male-32x32.png') %>" alt="Chef Icon"/>
<span>Bake!</span>
</button>
@@ -271,7 +271,7 @@
<div class="file-overlay" id="file-overlay"></div>
<div style="position: relative; height: 100%;">
<div class="io-card card">
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png').default %>" alt="File icon" id="input-file-thumbnail"/>
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" alt="File icon" id="input-file-thumbnail"/>
<div class="card-body">
<button type="button" class="close" id="input-file-close">&times;</button>
Name: <span id="input-file-name"></span><br>
@@ -346,12 +346,12 @@
<div id="output-highlighter" class="no-select"></div>
<div id="output-html"></div>
<textarea id="output-text" readonly="readonly" spellcheck="false"></textarea>
<img id="show-file-overlay" aria-hidden="true" src="<%- require('../static/images/file-32x32.png').default %>" alt="Show file overlay" title="Show file overlay"/>
<img id="show-file-overlay" aria-hidden="true" src="<%- require('../static/images/file-32x32.png') %>" alt="Show file overlay" title="Show file overlay"/>
<div id="output-file">
<div class="file-overlay"></div>
<div style="position: relative; height: 100%;">
<div class="io-card card">
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png').default %>" alt="File icon"/>
<img aria-hidden="true" src="<%- require('../static/images/file-128x128.png') %>" alt="File icon"/>
<div class="card-body">
Size: <span id="output-file-size"></span><br>
<button id="output-file-download" type="button" class="btn btn-primary btn-outline">Download</button>
@@ -626,7 +626,7 @@
<h5 class="modal-title">CyberChef - The Cyber Swiss Army Knife</h5>
</div>
<div class="modal-body">
<img aria-hidden="true" class="about-img-left" src="<%- require('../static/images/cyberchef-128x128.png').default %>" alt="CyberChef Logo"/>
<img aria-hidden="true" class="about-img-left" src="<%- require('../static/images/cyberchef-128x128.png') %>" alt="CyberChef Logo"/>
<p class="subtext">
Version <%= htmlWebpackPlugin.options.version %><br>
Compile time: <%= htmlWebpackPlugin.options.compileTime %>
@@ -749,7 +749,7 @@
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
<a href="https://github.com/gchq/CyberChef">
<img aria-hidden="true" style="position: absolute; top: 0; right: 0; border: 0;" src="<%- require('../static/images/fork_me.png').default %>" alt="Fork me on GitHub">
<img aria-hidden="true" style="position: absolute; top: 0; right: 0; border: 0;" src="<%- require('../static/images/fork_me.png') %>" alt="Fork me on GitHub">
</a>
</div>
</div>

View File

@@ -71,7 +71,7 @@ module.exports = {
.moveToElement(toHex, 10, 10)
.useCss()
.waitForElementVisible(".popover-body", 1000)
.doubleClick();
.doubleClick("xpath", toHex);
// Confirm that it has been added to the recipe
browser
@@ -90,7 +90,7 @@ module.exports = {
browser
.useCss()
.waitForElementNotVisible("#stale-indicator", 1000)
.expect.element("#output-text").to.have.value.that.equals("44 6f 6e 27 74 20 50 61 6e 69 63 2e");
.expect.element("#output-text").to.have.property("value").that.equals("44 6f 6e 27 74 20 50 61 6e 69 63 2e");
// Clear recipe
browser
@@ -202,11 +202,11 @@ module.exports = {
browser
.getLocationInView(genUUID)
.moveToElement(genUUID, 10, 10)
.doubleClick()
.doubleClick("xpath", genUUID)
.useCss()
.waitForElementVisible(".operation .op-title", 1000)
.waitForElementNotVisible("#stale-indicator", 1000)
.expect.element("#output-text").to.have.value.which.matches(/[\da-f-]{36}/);
.expect.element("#output-text").to.have.property("value").which.matches(/[\da-f-]{36}/);
browser.click("#clr-recipe");
},

View File

@@ -393,13 +393,13 @@ function testOp(browser, opName, input, output, args=[]) {
.click("#clr-recipe")
.click("#clr-io")
.waitForElementNotPresent("#rec-list li.operation")
.expect.element("#input-text").to.have.value.that.equals("");
.expect.element("#input-text").to.have.property("value").that.equals("");
browser
.urlHash("recipe=" + recipeConfig)
.setValue("#input-text", input)
.waitForElementPresent("#rec-list li.operation")
.expect.element("#input-text").to.have.value.that.equals(input);
.expect.element("#input-text").to.have.property("value").that.equals(input);
browser
.waitForElementVisible("#stale-indicator", 5000)
@@ -410,8 +410,8 @@ function testOp(browser, opName, input, output, args=[]) {
.waitForElementNotVisible("#output-loader", 5000);
if (typeof output === "string") {
browser.expect.element("#output-text").to.have.value.that.equals(output);
browser.expect.element("#output-text").to.have.property("value").that.equals(output);
} else if (output instanceof RegExp) {
browser.expect.element("#output-text").to.have.value.that.matches(output);
browser.expect.element("#output-text").to.have.property("value").that.matches(output);
}
}

View File

@@ -685,8 +685,8 @@ Arguments:
it("Parse user agent", () => {
const result = chef.parseUserAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0 ");
const expected = `Browser
Name: Mozilla
Version: 5.0
Name: Firefox
Version: 47.0
Device
Model: unknown
Type: unknown

View File

@@ -20,6 +20,7 @@ import TestRegister from "../lib/TestRegister.mjs";
import "./tests/BCD.mjs";
import "./tests/BSON.mjs";
import "./tests/BaconCipher.mjs";
import "./tests/Base45.mjs";
import "./tests/Base58.mjs";
import "./tests/Base64.mjs";
import "./tests/Base62.mjs";
@@ -107,6 +108,8 @@ import "./tests/CBORDecode.mjs";
import "./tests/JA3Fingerprint.mjs";
import "./tests/JA3SFingerprint.mjs";
import "./tests/HASSH.mjs";
import "./tests/GetAllCasings.mjs";
import "./tests/SIGABA.mjs";
// Cannot test operations that use the File type yet

View File

@@ -0,0 +1,103 @@
/**
* Base45 tests.
*
* @author Thomas Weißschuh [thomas@t-8ch.de]
*
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
const defaultB45Alph = "0-9A-Z $%*+\\-./:";
TestRegister.addTests([
{
name: "To Base45: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "To Base45",
args: [defaultB45Alph],
},
],
},
{
name: "To Base45: Spec encoding example 1",
input: "AB",
expectedOutput: "BB8",
recipeConfig: [
{
op: "To Base45",
args: [defaultB45Alph],
},
],
},
{
name: "To Base45: Spec encoding example 2",
input: "Hello!!",
expectedOutput: "%69 VD92EX0",
recipeConfig: [
{
op: "To Base45",
args: [defaultB45Alph],
},
],
},
{
name: "To Base45: Spec encoding example 3",
input: "base-45",
expectedOutput: "UJCLQE7W581",
recipeConfig: [
{
op: "To Base45",
args: [defaultB45Alph],
},
],
},
{
name: "From Base45: nothing",
input: "",
expectedOutput: "",
recipeConfig: [
{
op: "From Base45",
args: [defaultB45Alph],
},
],
},
{
name: "From Base45: Spec decoding example 1",
input: "QED8WEX0",
expectedOutput: "ietf!",
recipeConfig: [
{
op: "From Base45",
args: [defaultB45Alph],
},
],
},
{
name: "From Base45: Invalid character",
input: "!",
expectedOutput: "Character not in alphabet: '!'",
recipeConfig: [
{
op: "From Base45",
args: [defaultB45Alph],
},
],
},
{
name: "From Base45: Invalid triplet value",
input: "ZZZ",
expectedOutput: "Triplet too large: 'ZZZ'",
recipeConfig: [
{
op: "From Base45",
args: [defaultB45Alph],
},
],
},
]);

View File

@@ -0,0 +1,44 @@
/**
* GetAllCasings tests.
*
* @author n1073645 [n1073645@gmail.com]
* @copyright Crown Copyright 2020
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "All casings of test",
input: "test",
expectedOutput: "test\nTest\ntEst\nTEst\nteSt\nTeSt\ntESt\nTESt\ntesT\nTesT\ntEsT\nTEsT\nteST\nTeST\ntEST\nTEST\n",
recipeConfig: [
{
"op": "Get All Casings",
"args": []
}
]
},
{
name: "All casings of t",
input: "t",
expectedOutput: "t\nT\n",
recipeConfig: [
{
"op": "Get All Casings",
"args": []
}
]
},
{
name: "All casings of null",
input: "",
expectedOutput: "\n",
recipeConfig: [
{
"op": "Get All Casings",
"args": []
}
]
}
]);

View File

@@ -0,0 +1,91 @@
/**
* SIGABA machine tests
*
* @author hettysymes
* @copyright hettysymes 2020
* @license Apache-2.0
*/
import TestRegister from "../../lib/TestRegister.mjs";
TestRegister.addTests([
{
name: "SIGABA: encrypt test 1",
input: "HELLO WORLD TESTING THE SIGABA MACHINE",
expectedOutput: "ULBECJCZJBJFVUDWAVRGRBMPSQHOTTNVQEESKN",
recipeConfig: [
{
"op": "SIGABA",
"args": [
"BHKWECJDOVAYLFMITUGXRNSPZQ", true, "G",
"CDTAKGQOZXLVJYHSWMIBPRUNEF", false, "L",
"WAXHJZMBVDPOLTUYRCQFNSGKEI", false, "I",
"HUSCWIMJQXDALVGBFTOYZKRPNE", false, "T",
"RTLSMNKXFVWQUZGCHEJBYDAIPO", false, "B",
"GHAQBRJWDMNZTSKLOUXYPFIECV", false, "N",
"VFLGEMTCXZIQDYAKRPBONHWSUJ", true, "Q",
"ZQCAYHRJNXPFLKIOTBUSVWMGDE", false, "B",
"EZVSWPCTULGAOFDJNBIYMXKQHR", false, "J",
"ELKSGDXMVYJUZNCAROQBPWHITF", false, "R",
"3891625740", "3",
"6297135408", "1",
"2389715064", "8",
"9264351708", "6",
"9573086142", "6",
"Encrypt"
]
}
]
},
{
name: "SIGABA: encrypt test 2",
input: "PCRPJZWSPNOHMWANBFBEIVZOXDQESPYDEFBNTHXLSICIRPKUATJVDUQFLZOKGHHHDUDIBRKUHVCGAGLBWVGFFXNDHKPFSPSCIIPCXUFRRHNYWIJFEJWQSGMSNJHWSLPKVXHUQUWIURHDIHIUTWGQFIYLTKEZAUESWYEKIWXUSSXWXBEHCXCUDQWKCISVPKXJVPOIJZWTUGKAORBMKBAQUZOPTSUSYZRROWQUYKNCLHVIHEGWCCONGVHEKCEXVYIPNILIXTXDELNGLJGMEQKKQJWZLPNXPOGIOSVAEAJYKWYJXXGKKPLVYAZGDCMNHMPLCYWDQSRBEMVVVZVFYJMRYGHJOTDOEQVRQOVXOGOVYGTXETFHAYELRYVDGWOFVGAOWPMHQYRZMNXVTAHWSKZLJDFVQPZGMHZWFNOBHSZHEDAEXIFCEEJYZDOEFOQWCXTKPJRUEITKHVCITCLKBUFNAFBYXELAYPBRGGGOCCAGLXXJXTSWCJHMHQPVUIBAGBDKAGEEEPKRGGICJQXSYHBNNAKGYODRAUWAEYHWCKHEQIBAONWQJYQCIFKDTOCTJMBJULWKMSNNMPXINHZQWUMJQLQKIPVZVRGYPCJJZMENWTFTUSPCSPRXHMZPCHCNQTUDCOUJHRKYQIUWWVEVVRYFDIYRQISNGPMQLNMCNMVBEWHNCUODHAGEVEUMKVZLEIKYAMPGVVSBYNRJMFCATDXTQCYXIBCXXKYEYHHYERQGQWZTWCEJBFQLRFFCIVVSZUKGLOTLNGLQNTIKTBBWVFMONUFKRLCJASEKUEEDDQDIVQMFRSJRNHYZJODFHSCJSDAIRUXOSDNFUFUFMNZYQIEGRUXKUPCHENUEZHRKYHDRJYSHLZNYRBWVXORMJMJRIRNSAJQRUMPCXUDFYRGKEAXQXJHPEWNIYIDURDGWIFEMSOFYYCFRZGMZXJNTLTJBBSZIULQSOMEVGCTCVXUHTIEHSPOPQYCJLPAJAPQPAQXE",
expectedOutput: "GMEXPPCMFGKUVGXZHVTCKXRSTJUYWNOKFVELWAHHSJBXGOEXCMLOVSIMCDMGEYMWWTFDUMCDUJEZITNPVVBGQDJEVHJXSKJAAUZWBELMSPUTXCUYPDTJCQXEBGWPWRSQLSNFMASCTJZDSFNKDDTAXLRGUPKCBNXMZPADJSFGGNYKRPYBNTYPTGVPACBEINILNACWFVKMJPGCEZFROEYYKTGYSQYMFSGVDOJJONNYEYSCCIXWLKUSJZDRVAQSNUWHMDJVDNNMPGOYRGQRSBGSPQKGCTFZQWSOXBWSQZDCRQJQAWZDPQEILGMMABIMCDPNSKAFCLPQGIRJCMGQREBEUHBYREXFABFMVZTZBDUMASVNUMHIYRSZLGNZFMVAIABLCUZLJLKKZPWEXDHYZFVSNRLCLNDRKLKSWRHQVQJRTHCNFZXDEXSLAXXOGMFVSGCJGAWOLGDMTLWSFNTCUVCCEACINRZAZZOGLEHHXLPHVKILBBJDPOOCILQKKGODSXOBDPZZDXHJLLBOBVFCHJVMUBUZZIKGCWGCYGXVEHHIJGPEQERWEZLILQNHPHALFKFMGADNELGBKILKIUETGDCBQUEOECWVFNOXTJKUYPWBNEKYSIKMVSAMBZGLIKDAOELRSTKFASEKABTUCPSFEGXXQGDFPSPVOLBHGLZSLLWCABSRKZDQQRKVCKXDGTIHPDNMPDZEXYFYKXZTPJPLYOFNLWAGKJEOHOYLMZELXIDWWNXPKEPUCKNNNHJLFYHPQNHMMCGMUPHSUSYYIVWTIMFKKKTFPGFTLTWWSQBRBMGBTZXPVULKNZIIKVTYLJFISGPTLZFTCLGNZOMVKZOIMUDGXRDDSVFRHRYWBEWHYLCUISYMRWAZZAQPJYXZQQKZLILOSHXUTQJFPTXQSREKSUDZTLGUDLUGOJMQHJRJHXCHQTKJULTWWQOXIRFRQEYBPJPEKXFIRMNATWNFBADOSIJVZYRYDBHDAEDJUVDHLDAU",
recipeConfig: [
{ "op": "SIGABA",
"args": [
"YCHLQSUGBDIXNZKERPVJTAWFOM", true, "A",
"INPXBWETGUYSAOCHVLDMQKZJFR", false, "B",
"WNDRIOZPTAXHFJYQBMSVEKUCGL", false, "C",
"TZGHOBKRVUXLQDMPNFWCJYEIAS", false, "D",
"YWTAHRQJVLCEXUNGBIPZMSDFOK", true, "E",
"QSLRBTEKOGAICFWYVMHJNXZUDP", false, "F",
"CHJDQIGNBSAKVTUOXFWLEPRMZY", false, "G",
"CDFAJXTIMNBEQHSUGRYLWZKVPO", true, "H",
"XHFESZDNRBCGKQIJLTVMUOYAPW", false, "I",
"EZJQXMOGYTCSFRIUPVNADLHWBK", false, "J",
"7591482630", "0",
"3810592764", "1",
"4086153297", "2",
"3980526174", "3",
"6497135280", "4",
"Encrypt"]
}
]
},
{
name: "SIGABA: decrypt test",
input: "AKDHFWAYSLHJDKXEVMJJHGKFTQBZPJPJILOVHMBYOAGBZVLLTQUOIKXFPUFNILBDPCAELMAPSXTLMUEGSDTNUDWGZDADBFELWWHKVPRZNDATDPYEHIDMTGAGPDEZYXFSASVKSBMXVOJQXRMHDBWUNZDTIIIVKHJYPIEUHAJCNBXNLGVFADEWIKXDJZBUTGOQBCQZWYKRVEENWRWWRYDNOAPGMODTPTUJZCLUCRDILJABNTBTWUEIJSJRQBUVCOUJJDWFMNNUHXBDFYXLGUMXQEAWSVHBXQGEOOGPYRVOAJLAIYIOHHEXACDTAWWCBGQRNPERSIKHTXPXKBUNACZLFZTRBMBBDDGKNBIQMFHZROCZZBGNZSJKDRRWPEQHLCFADNPWPWSLPIFNKBWQPMARUERGWUUODXSCOJQECGHIZRFRNRSXWSFWKISHHTUFRVXLHCQWGBMRDHCYDSVNIDDRSTODCGJSSBLUYOBGEWFOVKOZBJTYCAKMZECUGLJGTSZJNBOLTMUZRRSIGGRQHLRPMGLINASSMZOBNACKUMSFNIZAUFCPFXXOOTJQWWLZOFLGZLHJCWZJCRJKVOUDLNMKQATGVTOFHACAEKFLRWRTTMVRXHYGOTYPNBMUSKDAKXFCICUOVSWXGPQOYUUWTWRPQMEQCSDJMMJKELIHGEDYKWOVHVPUAIBFGAODXODXVFIIZIGWRZSBTIGXVHFABMMOPGVMLGHQQXNOEJRDLOBGUOWSELBHERZFSBLUODMOGIBNVGVGQYDBTKLOPNKZZNGLTTGZYYXIBAHZJDCILZXKNSJDHXWTYQLFHTUINTYSBPIXOPLOQHSAHGQPYUWYNPKMRBBBYIICCBBJRKWVLBIDBBEKJCXHLPUBMIGBUFYDPOCSRUNZOKMKJHMYFJZWFNHQZOGGRTNNUVLMRLDSAJIECTYCJKBYVNAXGCMGNVFJEDSATZQDQTYRBPLZKHAXMOVJZEDKINXKBUVWXXHTYUFO",
expectedOutput: "KTSOYDGMLPMVXEAJIATXCNQFXHBNCBXIJOCQGCQBRQSBYFOOEVPVXACBMIUIRNVMJHREKRHBSXJFSMWCKTTCYXJOFSJCQECXXCHTEGPEYSMYDHCSMODUAVBNLILYUIBBIXJCXXNQPCERRSMJTPQLMOXSKTRPWOFUSWXOYRJLBIJGIOYTEAEJEGGYAGSXNHNQTETANPWEGATHSBFLHCVHVIJUAKDVGQCWUSIFFFVAJYPJAFUYDXSLGPGESOUAYXBQIIOXWTXNOXLNCGWSUKVIBMOUGNHORYLSNVNNJLKKFDUAEISOLBLCXYHMDGVBVVVIKDLTMTDVWWJBXWXROVTJBXXKXLEWTTISKIUMYSACVUGGNANMCGUMFNQUXDLTHJNYTFIQEPKQQQSSROYJOILJYQXICXACWGOHCSHENXJILOMIIFCIOUDXDCINIVKIRJCVHWXSFQXMNRBJJWTPXNJADEOPEJBLKHKXNTORIRVRLXUXXAMKMODBXNLQCVJXVOTBRHXBBVJHPFEQFCRXYRRXHXPTXXSUESUTHUGOWQYQPQFPXQPVGEIRPQNKXXMBHIPECRUWFEWJUTYIKSMJSRQIQAIAMXTGDXSJIABHIGKUPJBCHWMVYTMQNQYGDHCNMBSVTPXNFRELFXXQYIOLCDEXDXDVSINICOXRMNSPICPQMOBIDJCNBJKXFAVMUXOXHERJIBIXLMXXULDXKXXHAQDXEXIWXOEEUGKSUGCMRWJDPYCYKXTPCOXMURAJCPRXKFJAJALERWRHVMFHOGMFHXGSXQDPJCJNXRQFGHKRCYTEBJDHPCMYFEAPWSVVMMBVUJJMCAAYURHUPVQVJYDCSNMQEMNIFEXYXIIXBVRVILXAUCBDXRJHGPKPYXHPPPNVSBBCDRLVVIYPKAKYIXTJVYDGVPHXULWMADBEICNIFKWUOOHEFNANDKOXMCVBVORLQYNXLULOEGVGWNKNMOHYVRSYSOVYGAKCGAWKGAIAQNQR",
recipeConfig: [
{ "op": "SIGABA",
"args": [
"YCHLQSUGBDIXNZKERPVJTAWFOM", true, "A",
"INPXBWETGUYSAOCHVLDMQKZJFR", false, "B",
"WNDRIOZPTAXHFJYQBMSVEKUCGL", false, "C",
"TZGHOBKRVUXLQDMPNFWCJYEIAS", false, "D",
"YWTAHRQJVLCEXUNGBIPZMSDFOK", true, "E",
"QSLRBTEKOGAICFWYVMHJNXZUDP", false, "F",
"CHJDQIGNBSAKVTUOXFWLEPRMZY", false, "G",
"CDFAJXTIMNBEQHSUGRYLWZKVPO", true, "H",
"XHFESZDNRBCGKQIJLTVMUOYAPW", false, "I",
"EZJQXMOGYTCSFRIUPVNADLHWBK", false, "J",
"7591482630", "0",
"3810592764", "1",
"4086153297", "2",
"3980526174", "3",
"6497135280", "4",
"Decrypt"]
}
]
}
]);

View File

@@ -36,7 +36,8 @@ const banner = `/**
module.exports = {
output: {
publicPath: "",
globalObject: "this"
globalObject: "this",
assetModuleFilename: "assets/[hash][ext][query]"
},
plugins: [
new webpack.ProvidePlugin({
@@ -122,7 +123,7 @@ module.exports = {
},
{
test: /prime.worker.min.js$/,
use: "raw-loader"
type: "asset/source"
},
{
test: /bootstrap-material-design/,
@@ -165,53 +166,33 @@ module.exports = {
"sass-loader",
]
},
/**
* The limit for these files has been increased to 60,000 (60KB)
* to ensure the material icons font is inlined.
*
* See: https://github.com/gchq/CyberChef/issues/612
*/
{
test: /\.(ico|eot|ttf|woff|woff2)$/,
loader: "url-loader",
options: {
limit: 60000,
name: "[hash].[ext]",
outputPath: "assets"
}
type: "asset/resource",
},
{
test: /\.svg$/,
loader: "svg-url-loader",
options: {
encoding: "base64"
}
type: "asset/inline",
},
{ // Store font .fnt and .png files in a separate fonts folder
test: /(\.fnt$|bmfonts\/.+\.png$)/,
loader: "file-loader",
options: {
name: "[name].[ext]",
outputPath: "assets/fonts"
type: "asset/resource",
generator: {
filename: "assets/fonts/[name][ext]"
}
},
{ // First party images are saved as files to be cached
test: /\.(png|jpg|gif)$/,
exclude: /(node_modules|bmfonts)/,
loader: "file-loader",
options: {
name: "images/[name].[ext]"
type: "asset/resource",
generator: {
filename: "images/[name][ext]"
}
},
{ // Third party images are inlined
test: /\.(png|jpg|gif)$/,
exclude: /web\/static/,
loader: "url-loader",
options: {
limit: 10000,
name: "[hash].[ext]",
outputPath: "assets"
}
type: "asset/inline",
},
]
},
@@ -219,14 +200,15 @@ module.exports = {
children: false,
chunks: false,
modules: false,
entrypoints: false,
warningsFilter: [
/source-map/,
/dependency is an expression/,
/export 'default'/,
/Can't resolve 'sodium'/
],
entrypoints: false
},
ignoreWarnings: [
/source-map/,
/source map/,
/dependency is an expression/,
/export 'default'/,
/Can't resolve 'sodium'/
],
performance: {
hints: false
}