mirror of
https://github.com/gchq/CyberChef.git
synced 2024-11-16 08:58:30 +01:00
Typex: move machine implementation to lib/
This commit is contained in:
parent
eb1f8b4bc6
commit
53cc413eae
2 changed files with 193 additions and 183 deletions
183
src/core/lib/Typex.mjs
Normal file
183
src/core/lib/Typex.mjs
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
/**
|
||||||
|
* Emulation of the Typex machine.
|
||||||
|
*
|
||||||
|
* @author s2224834
|
||||||
|
* @copyright Crown Copyright 2019
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
import OperationError from "../errors/OperationError";
|
||||||
|
import * as Enigma from "../lib/Enigma";
|
||||||
|
import Utils from "../Utils";
|
||||||
|
|
||||||
|
export const ROTORS = [
|
||||||
|
{name: "1", value: "QWECYJIBFKMLTVZPOHUDGNRSXA<ACEINQTVY"},
|
||||||
|
{name: "2", value: "AJDKSIRUXBLHWTMCQGZNPYFVOE<ACEINQTVY"},
|
||||||
|
{name: "3", value: "BDFHJLCPRTXVZNYEIWGAKMUSQO<ACEINQTVY"},
|
||||||
|
{name: "4", value: "ESOVPZJAYQUIRHXLNFTGKDCMWB<ACEINQTVY"},
|
||||||
|
{name: "5", value: "VZBRGITYUPSDNHLXAWMJQOFECK<ACEINQTVY"},
|
||||||
|
{name: "6", value: "FVPJIAOYEDRZXWGCTKUQSBNMHL<ACEINQTVY"},
|
||||||
|
{name: "7", value: "KZGLIUCJEHADXRYWVTNSFQPMOB<ACEINQTVY"},
|
||||||
|
{name: "8", value: "ZLVGOIFTYWUEPMABNCXRQSDKHJ<ACEINQTVY"},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const REFLECTORS = [
|
||||||
|
{name: "Standard", value: "MO VW GL JX TZ AY EQ IP KN DH CU FS BR"},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Special character handling on Typex keyboard
|
||||||
|
const KEYBOARD = {
|
||||||
|
"Q": "1", "W": "2", "E": "3", "R": "4", "T": "5", "Y": "6", "U": "7", "I": "8", "O": "9", "P": "0",
|
||||||
|
"A": "-", "S": "/", "D": "Z", "F": "%", "G": "X", "H": "£", "K": "(", "L": ")",
|
||||||
|
"C": "V", "B": "'", "N": ",", "M": "."
|
||||||
|
};
|
||||||
|
const KEYBOARD_REV = {};
|
||||||
|
for (const i of Object.keys(KEYBOARD)) {
|
||||||
|
KEYBOARD_REV[KEYBOARD[i]] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Typex machine. A lot like the Enigma, but five rotors, of which the first two are static.
|
||||||
|
*/
|
||||||
|
export class TypexMachine extends Enigma.EnigmaBase {
|
||||||
|
/**
|
||||||
|
* TypexMachine constructor.
|
||||||
|
*
|
||||||
|
* @param {Object[]} rotors - List of Rotors.
|
||||||
|
* @param {Object} reflector - A Reflector.
|
||||||
|
* @param {Plugboard} plugboard - A Plugboard.
|
||||||
|
*/
|
||||||
|
constructor(rotors, reflector, plugboard, keyboard) {
|
||||||
|
super(rotors, reflector, plugboard);
|
||||||
|
if (rotors.length !== 5) {
|
||||||
|
throw new OperationError("Typex must have 5 rotors");
|
||||||
|
}
|
||||||
|
this.keyboard = keyboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the same as the Enigma step function, it's just that the right-
|
||||||
|
* most two rotors are static.
|
||||||
|
*/
|
||||||
|
step() {
|
||||||
|
const r0 = this.rotors[2];
|
||||||
|
const r1 = this.rotors[3];
|
||||||
|
r0.step();
|
||||||
|
// The second test here is the double-stepping anomaly
|
||||||
|
if (r0.steps.has(r0.pos) || r1.steps.has(Utils.mod(r1.pos + 1, 26))) {
|
||||||
|
r1.step();
|
||||||
|
if (r1.steps.has(r1.pos)) {
|
||||||
|
const r2 = this.rotors[4];
|
||||||
|
r2.step();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt/decrypt data. This is identical to the Enigma version cryptographically, but we have
|
||||||
|
* additional handling for the Typex's keyboard (which handles various special characters by
|
||||||
|
* mapping them to particular letter combinations).
|
||||||
|
*
|
||||||
|
* @param {string} input - The data to encrypt/decrypt.
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
crypt(input) {
|
||||||
|
let inputMod = input;
|
||||||
|
if (this.keyboard === "Encrypt") {
|
||||||
|
inputMod = "";
|
||||||
|
// true = in symbol mode
|
||||||
|
let mode = false;
|
||||||
|
for (const x of input) {
|
||||||
|
if (x === " ") {
|
||||||
|
inputMod += "X";
|
||||||
|
} else if (mode) {
|
||||||
|
if (KEYBOARD_REV.hasOwnProperty(x)) {
|
||||||
|
inputMod += KEYBOARD_REV[x];
|
||||||
|
} else {
|
||||||
|
mode = false;
|
||||||
|
inputMod += "V" + x;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (KEYBOARD_REV.hasOwnProperty(x)) {
|
||||||
|
mode = true;
|
||||||
|
inputMod += "Z" + KEYBOARD_REV[x];
|
||||||
|
} else {
|
||||||
|
inputMod += x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = super.crypt(inputMod);
|
||||||
|
|
||||||
|
let outputMod = output;
|
||||||
|
if (this.keyboard === "Decrypt") {
|
||||||
|
outputMod = "";
|
||||||
|
let mode = false;
|
||||||
|
for (const x of output) {
|
||||||
|
if (x === "X") {
|
||||||
|
outputMod += " ";
|
||||||
|
} else if (x === "V") {
|
||||||
|
mode = false;
|
||||||
|
} else if (x === "Z") {
|
||||||
|
mode = true;
|
||||||
|
} else if (mode) {
|
||||||
|
outputMod += KEYBOARD[x];
|
||||||
|
} else {
|
||||||
|
outputMod += x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputMod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Typex rotor. Like an Enigma rotor, but no ring setting, and can be reversed.
|
||||||
|
*/
|
||||||
|
export class Rotor extends Enigma.Rotor {
|
||||||
|
/**
|
||||||
|
* Rotor constructor.
|
||||||
|
*
|
||||||
|
* @param {string} wiring - A 26 character string of the wiring order.
|
||||||
|
* @param {string} steps - A 0..26 character string of stepping points.
|
||||||
|
* @param {bool} reversed - Whether to reverse the rotor.
|
||||||
|
* @param {char} initialPosition - The initial position of the rotor.
|
||||||
|
*/
|
||||||
|
constructor(wiring, steps, reversed, initialPos) {
|
||||||
|
let initialPosMod = initialPos;
|
||||||
|
let wiringMod = wiring;
|
||||||
|
if (reversed) {
|
||||||
|
initialPosMod = Enigma.i2a(Utils.mod(26 - Enigma.a2i(initialPos), 26));
|
||||||
|
const outMap = new Array(26).fill();
|
||||||
|
for (let i=0; i<26; i++) {
|
||||||
|
// wiring[i] is the original output
|
||||||
|
// Enigma.LETTERS[i] is the original input
|
||||||
|
const input = Utils.mod(26 - Enigma.a2i(wiring[i]), 26);
|
||||||
|
const output = Enigma.i2a(Utils.mod(26 - Enigma.a2i(Enigma.LETTERS[i]), 26));
|
||||||
|
outMap[input] = output;
|
||||||
|
}
|
||||||
|
wiringMod = outMap.join("");
|
||||||
|
}
|
||||||
|
super(wiringMod, steps, "A", initialPosMod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Typex input plugboard. Based on a Rotor, because it allows arbitrary maps, not just switches
|
||||||
|
* like the Enigma plugboard.
|
||||||
|
* Not to be confused with the reflector plugboard.
|
||||||
|
*/
|
||||||
|
export class Plugboard extends Enigma.Rotor {
|
||||||
|
/**
|
||||||
|
* Typex plugboard constructor.
|
||||||
|
*
|
||||||
|
* @param {string} wiring - 26 character string of mappings from A-Z, as per rotors, or "".
|
||||||
|
*/
|
||||||
|
constructor(wiring) {
|
||||||
|
try {
|
||||||
|
super(wiring, "", "A", "A");
|
||||||
|
} catch (err) {
|
||||||
|
throw new OperationError(err.message.replace("Rotor", "Plugboard"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,186 +8,13 @@
|
||||||
|
|
||||||
import Operation from "../Operation";
|
import Operation from "../Operation";
|
||||||
import OperationError from "../errors/OperationError";
|
import OperationError from "../errors/OperationError";
|
||||||
import Utils from "../Utils";
|
import {LETTERS, Reflector} from "../lib/Enigma";
|
||||||
import * as Enigma from "../lib/Enigma";
|
import {ROTORS, REFLECTORS, TypexMachine, Plugboard, Rotor} from "../lib/Typex";
|
||||||
|
|
||||||
const ROTORS = [
|
|
||||||
{name: "1", value: "QWECYJIBFKMLTVZPOHUDGNRSXA<ACEINQTVY"},
|
|
||||||
{name: "2", value: "AJDKSIRUXBLHWTMCQGZNPYFVOE<ACEINQTVY"},
|
|
||||||
{name: "3", value: "BDFHJLCPRTXVZNYEIWGAKMUSQO<ACEINQTVY"},
|
|
||||||
{name: "4", value: "ESOVPZJAYQUIRHXLNFTGKDCMWB<ACEINQTVY"},
|
|
||||||
{name: "5", value: "VZBRGITYUPSDNHLXAWMJQOFECK<ACEINQTVY"},
|
|
||||||
{name: "6", value: "FVPJIAOYEDRZXWGCTKUQSBNMHL<ACEINQTVY"},
|
|
||||||
{name: "7", value: "KZGLIUCJEHADXRYWVTNSFQPMOB<ACEINQTVY"},
|
|
||||||
{name: "8", value: "ZLVGOIFTYWUEPMABNCXRQSDKHJ<ACEINQTVY"},
|
|
||||||
];
|
|
||||||
|
|
||||||
const REFLECTORS = [
|
|
||||||
{name: "Standard", value: "MO VW GL JX TZ AY EQ IP KN DH CU FS BR"},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Special character handling on Typex keyboard
|
|
||||||
const KEYBOARD = {
|
|
||||||
"Q": "1", "W": "2", "E": "3", "R": "4", "T": "5", "Y": "6", "U": "7", "I": "8", "O": "9", "P": "0",
|
|
||||||
"A": "-", "S": "/", "D": "Z", "F": "%", "G": "X", "H": "£", "K": "(", "L": ")",
|
|
||||||
"C": "V", "B": "'", "N": ",", "M": "."
|
|
||||||
};
|
|
||||||
const KEYBOARD_REV = {};
|
|
||||||
for (const i of Object.keys(KEYBOARD)) {
|
|
||||||
KEYBOARD_REV[KEYBOARD[i]] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Typex machine. A lot like the Enigma, but five rotors, of which the first two are static.
|
|
||||||
*/
|
|
||||||
class TypexMachine extends Enigma.EnigmaBase {
|
|
||||||
/**
|
|
||||||
* TypexMachine constructor.
|
|
||||||
*
|
|
||||||
* @param {Object[]} rotors - List of Rotors.
|
|
||||||
* @param {Object} reflector - A Reflector.
|
|
||||||
* @param {Plugboard} plugboard - A Plugboard.
|
|
||||||
*/
|
|
||||||
constructor(rotors, reflector, plugboard, keyboard) {
|
|
||||||
super(rotors, reflector, plugboard);
|
|
||||||
if (rotors.length !== 5) {
|
|
||||||
throw new OperationError("Typex must have 5 rotors");
|
|
||||||
}
|
|
||||||
this.keyboard = keyboard;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the same as the Enigma step function, it's just that the right-
|
|
||||||
* most two rotors are static.
|
|
||||||
*/
|
|
||||||
step() {
|
|
||||||
const r0 = this.rotors[2];
|
|
||||||
const r1 = this.rotors[3];
|
|
||||||
r0.step();
|
|
||||||
// The second test here is the double-stepping anomaly
|
|
||||||
if (r0.steps.has(r0.pos) || r1.steps.has(Utils.mod(r1.pos + 1, 26))) {
|
|
||||||
r1.step();
|
|
||||||
if (r1.steps.has(r1.pos)) {
|
|
||||||
const r2 = this.rotors[4];
|
|
||||||
r2.step();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt/decrypt data. This is identical to the Enigma version cryptographically, but we have
|
|
||||||
* additional handling for the Typex's keyboard (which handles various special characters by
|
|
||||||
* mapping them to particular letter combinations).
|
|
||||||
*
|
|
||||||
* @param {string} input - The data to encrypt/decrypt.
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
crypt(input) {
|
|
||||||
let inputMod = input;
|
|
||||||
if (this.keyboard === "Encrypt") {
|
|
||||||
inputMod = "";
|
|
||||||
// true = in symbol mode
|
|
||||||
let mode = false;
|
|
||||||
for (const x of input) {
|
|
||||||
if (x === " ") {
|
|
||||||
inputMod += "X";
|
|
||||||
} else if (mode) {
|
|
||||||
if (KEYBOARD_REV.hasOwnProperty(x)) {
|
|
||||||
inputMod += KEYBOARD_REV[x];
|
|
||||||
} else {
|
|
||||||
mode = false;
|
|
||||||
inputMod += "V" + x;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (KEYBOARD_REV.hasOwnProperty(x)) {
|
|
||||||
mode = true;
|
|
||||||
inputMod += "Z" + KEYBOARD_REV[x];
|
|
||||||
} else {
|
|
||||||
inputMod += x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const output = super.crypt(inputMod);
|
|
||||||
|
|
||||||
let outputMod = output;
|
|
||||||
if (this.keyboard === "Decrypt") {
|
|
||||||
outputMod = "";
|
|
||||||
let mode = false;
|
|
||||||
for (const x of output) {
|
|
||||||
if (x === "X") {
|
|
||||||
outputMod += " ";
|
|
||||||
} else if (x === "V") {
|
|
||||||
mode = false;
|
|
||||||
} else if (x === "Z") {
|
|
||||||
mode = true;
|
|
||||||
} else if (mode) {
|
|
||||||
outputMod += KEYBOARD[x];
|
|
||||||
} else {
|
|
||||||
outputMod += x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return outputMod;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Typex rotor. Like an Enigma rotor, but no ring setting, and can be reversed.
|
|
||||||
*/
|
|
||||||
class Rotor extends Enigma.Rotor {
|
|
||||||
/**
|
|
||||||
* Rotor constructor.
|
|
||||||
*
|
|
||||||
* @param {string} wiring - A 26 character string of the wiring order.
|
|
||||||
* @param {string} steps - A 0..26 character string of stepping points.
|
|
||||||
* @param {bool} reversed - Whether to reverse the rotor.
|
|
||||||
* @param {char} initialPosition - The initial position of the rotor.
|
|
||||||
*/
|
|
||||||
constructor(wiring, steps, reversed, initialPos) {
|
|
||||||
let initialPosMod = initialPos;
|
|
||||||
let wiringMod = wiring;
|
|
||||||
if (reversed) {
|
|
||||||
initialPosMod = Enigma.i2a(Utils.mod(26 - Enigma.a2i(initialPos), 26));
|
|
||||||
const outMap = new Array(26).fill();
|
|
||||||
for (let i=0; i<26; i++) {
|
|
||||||
// wiring[i] is the original output
|
|
||||||
// Enigma.LETTERS[i] is the original input
|
|
||||||
const input = Utils.mod(26 - Enigma.a2i(wiring[i]), 26);
|
|
||||||
const output = Enigma.i2a(Utils.mod(26 - Enigma.a2i(Enigma.LETTERS[i]), 26));
|
|
||||||
outMap[input] = output;
|
|
||||||
}
|
|
||||||
wiringMod = outMap.join("");
|
|
||||||
}
|
|
||||||
super(wiringMod, steps, "A", initialPosMod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Typex input plugboard. Based on a Rotor, because it allows arbitrary maps, not just switches
|
|
||||||
* like the Enigma plugboard.
|
|
||||||
* Not to be confused with the reflector plugboard.
|
|
||||||
*/
|
|
||||||
class Plugboard extends Enigma.Rotor {
|
|
||||||
/**
|
|
||||||
* Typex plugboard constructor.
|
|
||||||
*
|
|
||||||
* @param {string} wiring - 26 character string of mappings from A-Z, as per rotors, or "".
|
|
||||||
*/
|
|
||||||
constructor(wiring) {
|
|
||||||
try {
|
|
||||||
super(wiring, "", "A", "A");
|
|
||||||
} catch (err) {
|
|
||||||
throw new OperationError(err.message.replace("Rotor", "Plugboard"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Typex operation
|
* Typex operation
|
||||||
*/
|
*/
|
||||||
class TypexOp extends Operation {
|
class Typex extends Operation {
|
||||||
/**
|
/**
|
||||||
* Typex constructor
|
* Typex constructor
|
||||||
*/
|
*/
|
||||||
|
@ -215,7 +42,7 @@ class TypexOp extends Operation {
|
||||||
{
|
{
|
||||||
name: "1st rotor initial value",
|
name: "1st rotor initial value",
|
||||||
type: "option",
|
type: "option",
|
||||||
value: Enigma.LETTERS
|
value: LETTERS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "2nd (static) rotor",
|
name: "2nd (static) rotor",
|
||||||
|
@ -231,7 +58,7 @@ class TypexOp extends Operation {
|
||||||
{
|
{
|
||||||
name: "2nd rotor initial value",
|
name: "2nd rotor initial value",
|
||||||
type: "option",
|
type: "option",
|
||||||
value: Enigma.LETTERS
|
value: LETTERS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "3rd rotor",
|
name: "3rd rotor",
|
||||||
|
@ -247,7 +74,7 @@ class TypexOp extends Operation {
|
||||||
{
|
{
|
||||||
name: "3rd rotor initial value",
|
name: "3rd rotor initial value",
|
||||||
type: "option",
|
type: "option",
|
||||||
value: Enigma.LETTERS
|
value: LETTERS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "4th rotor",
|
name: "4th rotor",
|
||||||
|
@ -263,7 +90,7 @@ class TypexOp extends Operation {
|
||||||
{
|
{
|
||||||
name: "4th rotor initial value",
|
name: "4th rotor initial value",
|
||||||
type: "option",
|
type: "option",
|
||||||
value: Enigma.LETTERS
|
value: LETTERS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "5th rotor",
|
name: "5th rotor",
|
||||||
|
@ -279,7 +106,7 @@ class TypexOp extends Operation {
|
||||||
{
|
{
|
||||||
name: "5th rotor initial value",
|
name: "5th rotor initial value",
|
||||||
type: "option",
|
type: "option",
|
||||||
value: Enigma.LETTERS
|
value: LETTERS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Reflector",
|
name: "Reflector",
|
||||||
|
@ -338,7 +165,7 @@ class TypexOp extends Operation {
|
||||||
const [rotorwiring, rotorsteps] = this.parseRotorStr(args[i*3]);
|
const [rotorwiring, rotorsteps] = this.parseRotorStr(args[i*3]);
|
||||||
rotors.push(new Rotor(rotorwiring, rotorsteps, args[i*3 + 1], args[i*3+2]));
|
rotors.push(new Rotor(rotorwiring, rotorsteps, args[i*3 + 1], args[i*3+2]));
|
||||||
}
|
}
|
||||||
const reflector = new Enigma.Reflector(reflectorstr);
|
const reflector = new Reflector(reflectorstr);
|
||||||
let plugboardstrMod = plugboardstr;
|
let plugboardstrMod = plugboardstr;
|
||||||
if (plugboardstrMod === "") {
|
if (plugboardstrMod === "") {
|
||||||
plugboardstrMod = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
plugboardstrMod = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
@ -393,4 +220,4 @@ class TypexOp extends Operation {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TypexOp;
|
export default Typex;
|
||||||
|
|
Loading…
Reference in a new issue