mirror of
https://github.com/gchq/CyberChef
synced 2025-12-15 15:53:30 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfd9afc2c4 | ||
|
|
eb5663a1ed | ||
|
|
418a7962a5 | ||
|
|
2ffce23c67 | ||
|
|
b828b50ccc | ||
|
|
a6aa40db97 | ||
|
|
45ede4beaf | ||
|
|
98a95c8bbf | ||
|
|
e8f91316ff | ||
|
|
e91e993fb5 | ||
|
|
e71794d362 | ||
|
|
6fd929160d | ||
|
|
5cdd062ed9 | ||
|
|
0259ed8314 |
10
CHANGELOG.md
10
CHANGELOG.md
@@ -13,6 +13,12 @@ All major and minor version changes will be documented in this file. Details of
|
||||
|
||||
## Details
|
||||
|
||||
### [9.42.0] - 2022-07-08
|
||||
- Added 'LS47 Encrypt' and 'LS47 Decrypt' operations [@n1073645] | [#951]
|
||||
|
||||
### [9.41.0] - 2022-07-08
|
||||
- Added 'Caesar Box Cipher' operation [@n1073645] | [#1066]
|
||||
|
||||
### [9.40.0] - 2022-07-08
|
||||
- Added 'P-list Viewer' operation [@n1073645] | [#906]
|
||||
|
||||
@@ -297,6 +303,8 @@ All major and minor version changes will be documented in this file. Details of
|
||||
|
||||
|
||||
|
||||
[9.42.0]: https://github.com/gchq/CyberChef/releases/tag/v9.42.0
|
||||
[9.41.0]: https://github.com/gchq/CyberChef/releases/tag/v9.41.0
|
||||
[9.40.0]: https://github.com/gchq/CyberChef/releases/tag/v9.40.0
|
||||
[9.39.0]: https://github.com/gchq/CyberChef/releases/tag/v9.39.0
|
||||
[9.38.0]: https://github.com/gchq/CyberChef/releases/tag/v9.38.0
|
||||
@@ -500,6 +508,7 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[#917]: https://github.com/gchq/CyberChef/pull/917
|
||||
[#934]: https://github.com/gchq/CyberChef/pull/934
|
||||
[#948]: https://github.com/gchq/CyberChef/pull/948
|
||||
[#951]: https://github.com/gchq/CyberChef/pull/951
|
||||
[#952]: https://github.com/gchq/CyberChef/pull/952
|
||||
[#965]: https://github.com/gchq/CyberChef/pull/965
|
||||
[#966]: https://github.com/gchq/CyberChef/pull/966
|
||||
@@ -511,6 +520,7 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[#1045]: https://github.com/gchq/CyberChef/pull/1045
|
||||
[#1049]: https://github.com/gchq/CyberChef/pull/1049
|
||||
[#1065]: https://github.com/gchq/CyberChef/pull/1065
|
||||
[#1066]: https://github.com/gchq/CyberChef/pull/1066
|
||||
[#1083]: https://github.com/gchq/CyberChef/pull/1083
|
||||
[#1189]: https://github.com/gchq/CyberChef/pull/1189
|
||||
[#1242]: https://github.com/gchq/CyberChef/pull/1242
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "9.41.0",
|
||||
"version": "9.43.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cyberchef",
|
||||
"version": "9.41.0",
|
||||
"version": "9.43.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "9.41.0",
|
||||
"version": "9.43.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",
|
||||
|
||||
@@ -79,6 +79,8 @@
|
||||
"DES Decrypt",
|
||||
"Triple DES Encrypt",
|
||||
"Triple DES Decrypt",
|
||||
"LS47 Encrypt",
|
||||
"LS47 Decrypt",
|
||||
"RC2 Encrypt",
|
||||
"RC2 Decrypt",
|
||||
"RC4",
|
||||
@@ -86,7 +88,9 @@
|
||||
"SM4 Encrypt",
|
||||
"SM4 Decrypt",
|
||||
"ROT13",
|
||||
"ROT13 Brute Force",
|
||||
"ROT47",
|
||||
"ROT47 Brute Force",
|
||||
"XOR",
|
||||
"XOR Brute Force",
|
||||
"Vigenère Encode",
|
||||
|
||||
244
src/core/lib/LS47.mjs
Normal file
244
src/core/lib/LS47.mjs
Normal file
@@ -0,0 +1,244 @@
|
||||
/**
|
||||
* @author n1073645 [n1073645@gmail.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
const letters = "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()";
|
||||
const tiles = [];
|
||||
|
||||
/**
|
||||
* Initialises the tiles with values and positions.
|
||||
*/
|
||||
export function initTiles() {
|
||||
for (let i = 0; i < 49; i++)
|
||||
tiles.push([letters.charAt(i), [Math.floor(i/7), i % 7]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the key "down".
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {number} col
|
||||
* @param {number} n
|
||||
* @returns {string}
|
||||
*/
|
||||
function rotateDown(key, col, n) {
|
||||
const lines = [];
|
||||
for (let i = 0; i < 7; i++)
|
||||
lines.push(key.slice(i*7, (i + 1) * 7));
|
||||
const lefts = [];
|
||||
let mids = [];
|
||||
const rights = [];
|
||||
lines.forEach((element) => {
|
||||
lefts.push(element.slice(0, col));
|
||||
mids.push(element.charAt(col));
|
||||
rights.push(element.slice(col+1));
|
||||
});
|
||||
n = (7 - n % 7) % 7;
|
||||
mids = mids.slice(n).concat(mids.slice(0, n));
|
||||
let result = "";
|
||||
for (let i = 0; i < 7; i++)
|
||||
result += lefts[i] + mids[i] + rights[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the key "right".
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {number} row
|
||||
* @param {number} n
|
||||
* @returns {string}
|
||||
*/
|
||||
function rotateRight(key, row, n) {
|
||||
const mid = key.slice(row * 7, (row + 1) * 7);
|
||||
n = (7 - n % 7) % 7;
|
||||
return key.slice(0, 7 * row) + mid.slice(n) + mid.slice(0, n) + key.slice(7 * (row + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the position of a letter in the tiles.
|
||||
*
|
||||
* @param {string} letter
|
||||
* @returns {string}
|
||||
*/
|
||||
function findIx(letter) {
|
||||
for (let i = 0; i < tiles.length; i++)
|
||||
if (tiles[i][0] === letter)
|
||||
return tiles[i][1];
|
||||
throw new OperationError("Letter " + letter + " is not included in LS47");
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives key from the input password.
|
||||
*
|
||||
* @param {string} password
|
||||
* @returns {string}
|
||||
*/
|
||||
export function deriveKey(password) {
|
||||
let i = 0;
|
||||
let k = letters;
|
||||
for (const c of password) {
|
||||
const [row, col] = findIx(c);
|
||||
k = rotateDown(rotateRight(k, i, col), i, row);
|
||||
i = (i + 1) % 7;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the key is a valid key.
|
||||
*
|
||||
* @param {string} key
|
||||
*/
|
||||
function checkKey(key) {
|
||||
if (key.length !== letters.length)
|
||||
throw new OperationError("Wrong key size");
|
||||
const counts = new Array();
|
||||
for (let i = 0; i < letters.length; i++)
|
||||
counts[letters.charAt(i)] = 0;
|
||||
for (const elem of letters) {
|
||||
if (letters.indexOf(elem) === -1)
|
||||
throw new OperationError("Letter " + elem + " not in LS47");
|
||||
counts[elem]++;
|
||||
if (counts[elem] > 1)
|
||||
throw new OperationError("Letter duplicated in the key");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the position of a letter in they key.
|
||||
*
|
||||
* @param {letter} key
|
||||
* @param {string} letter
|
||||
* @returns {object}
|
||||
*/
|
||||
function findPos (key, letter) {
|
||||
const index = key.indexOf(letter);
|
||||
if (index >= 0 && index < 49)
|
||||
return [Math.floor(index/7), index%7];
|
||||
throw new OperationError("Letter " + letter + " is not in the key");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the character at the position on the tiles.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {object} coord
|
||||
* @returns {string}
|
||||
*/
|
||||
function findAtPos(key, coord) {
|
||||
return key.charAt(coord[1] + (coord[0] * 7));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new position by adding two positions.
|
||||
*
|
||||
* @param {object} a
|
||||
* @param {object} b
|
||||
* @returns {object}
|
||||
*/
|
||||
function addPos(a, b) {
|
||||
return [(a[0] + b[0]) % 7, (a[1] + b[1]) % 7];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new position by subtracting two positions.
|
||||
* Note: We have to manually do the remainder division, since JS does not
|
||||
* operate correctly on negative numbers (e.g. -3 % 4 = -3 when it should be 1).
|
||||
*
|
||||
* @param {object} a
|
||||
* @param {object} b
|
||||
* @returns {object}
|
||||
*/
|
||||
function subPos(a, b) {
|
||||
const asub = a[0] - b[0];
|
||||
const bsub = a[1] - b[1];
|
||||
return [asub - (Math.floor(asub/7) * 7), bsub - (Math.floor(bsub/7) * 7)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the plaintext string.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {string} plaintext
|
||||
* @returns {string}
|
||||
*/
|
||||
function encrypt(key, plaintext) {
|
||||
checkKey(key);
|
||||
let mp = [0, 0];
|
||||
let ciphertext = "";
|
||||
for (const p of plaintext) {
|
||||
const pp = findPos(key, p);
|
||||
const mix = findIx(findAtPos(key, mp));
|
||||
let cp = addPos(pp, mix);
|
||||
const c = findAtPos(key, cp);
|
||||
ciphertext += c;
|
||||
key = rotateRight(key, pp[0], 1);
|
||||
cp = findPos(key, c);
|
||||
key = rotateDown(key, cp[1], 1);
|
||||
mp = addPos(mp, findIx(c));
|
||||
}
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the ciphertext string.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {string} ciphertext
|
||||
* @returns {string}
|
||||
*/
|
||||
function decrypt(key, ciphertext) {
|
||||
checkKey(key);
|
||||
let mp = [0, 0];
|
||||
let plaintext = "";
|
||||
for (const c of ciphertext) {
|
||||
let cp = findPos(key, c);
|
||||
const mix = findIx(findAtPos(key, mp));
|
||||
const pp = subPos(cp, mix);
|
||||
const p = findAtPos(key, pp);
|
||||
plaintext += p;
|
||||
key = rotateRight(key, pp[0], 1);
|
||||
cp = findPos(key, c);
|
||||
key = rotateDown(key, cp[1], 1);
|
||||
mp = addPos(mp, findIx(c));
|
||||
}
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds padding to the input.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {string} plaintext
|
||||
* @param {string} signature
|
||||
* @param {number} paddingSize
|
||||
* @returns {string}
|
||||
*/
|
||||
export function encryptPad(key, plaintext, signature, paddingSize) {
|
||||
initTiles();
|
||||
checkKey(key);
|
||||
let padding = "";
|
||||
for (let i = 0; i < paddingSize; i++) {
|
||||
padding += letters.charAt(Math.floor(Math.random() * letters.length));
|
||||
}
|
||||
return encrypt(key, padding+plaintext+"---"+signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes padding from the ouput.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {string} ciphertext
|
||||
* @param {number} paddingSize
|
||||
* @returns {string}
|
||||
*/
|
||||
export function decryptPad(key, ciphertext, paddingSize) {
|
||||
initTiles();
|
||||
checkKey(key);
|
||||
return decrypt(key, ciphertext).slice(paddingSize);
|
||||
}
|
||||
57
src/core/operations/LS47Decrypt.mjs
Normal file
57
src/core/operations/LS47Decrypt.mjs
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @author n1073645 [n1073645@gmail.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import * as LS47 from "../lib/LS47.mjs";
|
||||
|
||||
/**
|
||||
* LS47 Decrypt operation
|
||||
*/
|
||||
class LS47Decrypt extends Operation {
|
||||
|
||||
/**
|
||||
* LS47Decrypt constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "LS47 Decrypt";
|
||||
this.module = "Crypto";
|
||||
this.description = "This is a slight improvement of the ElsieFour cipher as described by Alan Kaminsky. We use 7x7 characters instead of original (barely fitting) 6x6, to be able to encrypt some structured information. We also describe a simple key-expansion algorithm, because remembering passwords is popular. Similar security considerations as with ElsieFour hold.<br>The LS47 alphabet consists of following characters: <code>_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()</code><br>An LS47 key is a permutation of the alphabet that is then represented in a 7x7 grid used for the encryption or decryption.";
|
||||
this.infoURL = "https://github.com/exaexa/ls47";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "Password",
|
||||
type: "string",
|
||||
value: ""
|
||||
},
|
||||
{
|
||||
name: "Padding",
|
||||
type: "number",
|
||||
value: 10
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
this.paddingSize = parseInt(args[1], 10);
|
||||
|
||||
LS47.initTiles();
|
||||
|
||||
const key = LS47.deriveKey(args[0]);
|
||||
return LS47.decryptPad(key, input, this.paddingSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default LS47Decrypt;
|
||||
62
src/core/operations/LS47Encrypt.mjs
Normal file
62
src/core/operations/LS47Encrypt.mjs
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @author n1073645 [n1073645@gmail.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import * as LS47 from "../lib/LS47.mjs";
|
||||
|
||||
/**
|
||||
* LS47 Encrypt operation
|
||||
*/
|
||||
class LS47Encrypt extends Operation {
|
||||
|
||||
/**
|
||||
* LS47Encrypt constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "LS47 Encrypt";
|
||||
this.module = "Crypto";
|
||||
this.description = "This is a slight improvement of the ElsieFour cipher as described by Alan Kaminsky. We use 7x7 characters instead of original (barely fitting) 6x6, to be able to encrypt some structured information. We also describe a simple key-expansion algorithm, because remembering passwords is popular. Similar security considerations as with ElsieFour hold.<br>The LS47 alphabet consists of following characters: <code>_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()</code><br>A LS47 key is a permutation of the alphabet that is then represented in a 7x7 grid used for the encryption or decryption.";
|
||||
this.infoURL = "https://github.com/exaexa/ls47";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "Password",
|
||||
type: "string",
|
||||
value: ""
|
||||
},
|
||||
{
|
||||
name: "Padding",
|
||||
type: "number",
|
||||
value: 10
|
||||
},
|
||||
{
|
||||
name: "Signature",
|
||||
type: "string",
|
||||
value: ""
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
this.paddingSize = parseInt(args[1], 10);
|
||||
|
||||
LS47.initTiles();
|
||||
|
||||
const key = LS47.deriveKey(args[0]);
|
||||
return LS47.encryptPad(key, input, args[2], this.paddingSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default LS47Encrypt;
|
||||
102
src/core/operations/ROT13BruteForce.mjs
Normal file
102
src/core/operations/ROT13BruteForce.mjs
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @author MikeCAT
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* ROT13 Brute Force operation.
|
||||
*/
|
||||
class ROT13BruteForce extends Operation {
|
||||
|
||||
/**
|
||||
* ROT13BruteForce constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "ROT13 Brute Force";
|
||||
this.module = "Default";
|
||||
this.description = "Try all meaningful amounts for ROT13.<br><br>Optionally you can enter your known plaintext (crib) to filter the result.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/ROT13";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "Rotate lower case chars",
|
||||
type: "boolean",
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "Rotate upper case chars",
|
||||
type: "boolean",
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "Rotate numbers",
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Sample length",
|
||||
type: "number",
|
||||
value: 100
|
||||
},
|
||||
{
|
||||
name: "Sample offset",
|
||||
type: "number",
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: "Print amount",
|
||||
type: "boolean",
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "Crib (known plaintext string)",
|
||||
type: "string",
|
||||
value: ""
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [rotateLower, rotateUpper, rotateNum, sampleLength, sampleOffset, printAmount, crib] = args;
|
||||
const sample = input.slice(sampleOffset, sampleOffset + sampleLength);
|
||||
const cribLower = crib.toLowerCase();
|
||||
const lowerStart = "a".charCodeAt(0), upperStart = "A".charCodeAt(0), numStart = "0".charCodeAt(0);
|
||||
const result = [];
|
||||
for (let amount = 1; amount < 26; amount++) {
|
||||
const rotated = sample.slice();
|
||||
for (let i = 0; i < rotated.length; i++) {
|
||||
if (rotateLower && lowerStart <= rotated[i] && rotated[i] < lowerStart + 26) {
|
||||
rotated[i] = (rotated[i] - lowerStart + amount) % 26 + lowerStart;
|
||||
} else if (rotateUpper && upperStart <= rotated[i] && rotated[i] < upperStart + 26) {
|
||||
rotated[i] = (rotated[i] - upperStart + amount) % 26 + upperStart;
|
||||
} else if (rotateNum && numStart <= rotated[i] && rotated[i] < numStart + 10) {
|
||||
rotated[i] = (rotated[i] - numStart + amount) % 10 + numStart;
|
||||
}
|
||||
}
|
||||
const rotatedString = Utils.byteArrayToUtf8(rotated);
|
||||
if (rotatedString.toLowerCase().indexOf(cribLower) >= 0) {
|
||||
const rotatedStringPrintable = Utils.printable(rotatedString, false);
|
||||
if (printAmount) {
|
||||
const amountStr = "Amount = " + (" " + amount).slice(-2) + ": ";
|
||||
result.push(amountStr + rotatedStringPrintable);
|
||||
} else {
|
||||
result.push(rotatedStringPrintable);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.join("\n");
|
||||
}
|
||||
}
|
||||
|
||||
export default ROT13BruteForce;
|
||||
82
src/core/operations/ROT47BruteForce.mjs
Normal file
82
src/core/operations/ROT47BruteForce.mjs
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @author MikeCAT
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* ROT47 Brute Force operation.
|
||||
*/
|
||||
class ROT47BruteForce extends Operation {
|
||||
|
||||
/**
|
||||
* ROT47BruteForce constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "ROT47 Brute Force";
|
||||
this.module = "Default";
|
||||
this.description = "Try all meaningful amounts for ROT47.<br><br>Optionally you can enter your known plaintext (crib) to filter the result.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/ROT13#Variants";
|
||||
this.inputType = "byteArray";
|
||||
this.outputType = "string";
|
||||
this.args = [
|
||||
{
|
||||
name: "Sample length",
|
||||
type: "number",
|
||||
value: 100
|
||||
},
|
||||
{
|
||||
name: "Sample offset",
|
||||
type: "number",
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: "Print amount",
|
||||
type: "boolean",
|
||||
value: true
|
||||
},
|
||||
{
|
||||
name: "Crib (known plaintext string)",
|
||||
type: "string",
|
||||
value: ""
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [sampleLength, sampleOffset, printAmount, crib] = args;
|
||||
const sample = input.slice(sampleOffset, sampleOffset + sampleLength);
|
||||
const cribLower = crib.toLowerCase();
|
||||
const result = [];
|
||||
for (let amount = 1; amount < 94; amount++) {
|
||||
const rotated = sample.slice();
|
||||
for (let i = 0; i < rotated.length; i++) {
|
||||
if (33 <= rotated[i] && rotated[i] <= 126) {
|
||||
rotated[i] = (rotated[i] - 33 + amount) % 94 + 33;
|
||||
}
|
||||
}
|
||||
const rotatedString = Utils.byteArrayToUtf8(rotated);
|
||||
if (rotatedString.toLowerCase().indexOf(cribLower) >= 0) {
|
||||
const rotatedStringPrintable = Utils.printable(rotatedString, false);
|
||||
if (printAmount) {
|
||||
const amountStr = "Amount = " + (" " + amount).slice(-2) + ": ";
|
||||
result.push(amountStr + rotatedStringPrintable);
|
||||
} else {
|
||||
result.push(rotatedStringPrintable);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.join("\n");
|
||||
}
|
||||
}
|
||||
|
||||
export default ROT47BruteForce;
|
||||
@@ -117,6 +117,7 @@ import "./tests/SIGABA.mjs";
|
||||
import "./tests/ELFInfo.mjs";
|
||||
import "./tests/Subsection.mjs";
|
||||
import "./tests/CaesarBoxCipher.mjs";
|
||||
import "./tests/LS47.mjs";
|
||||
|
||||
|
||||
// Cannot test operations that use the File type yet
|
||||
|
||||
45
tests/operations/tests/LS47.mjs
Normal file
45
tests/operations/tests/LS47.mjs
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* LS47 tests.
|
||||
*
|
||||
* @author n1073645 [n1073645@gmail.com]
|
||||
*
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
import TestRegister from "../../lib/TestRegister.mjs";
|
||||
|
||||
TestRegister.addTests([
|
||||
{
|
||||
name: "LS47 Encrypt",
|
||||
input: "thequickbrownfoxjumped",
|
||||
expectedOutput: "(,t74ci78cp/8trx*yesu:alp1wqy",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "LS47 Encrypt",
|
||||
args: ["helloworld", 0, "test"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "LS47 Decrypt",
|
||||
input: "(,t74ci78cp/8trx*yesu:alp1wqy",
|
||||
expectedOutput: "thequickbrownfoxjumped---test",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "LS47 Decrypt",
|
||||
args: ["helloworld", 0],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "LS47 Encrypt",
|
||||
input: "thequickbrownfoxjumped",
|
||||
expectedOutput: "Letter H is not included in LS47",
|
||||
recipeConfig: [
|
||||
{
|
||||
op: "LS47 Encrypt",
|
||||
args: ["Helloworld", 0, "test"],
|
||||
},
|
||||
],
|
||||
}
|
||||
]);
|
||||
Reference in New Issue
Block a user