2019-01-03 16:36:56 +00:00
/**
* Emulation of the Enigma machine.
*
* @author s2224834
* @copyright Crown Copyright 2019
* @license Apache-2.0
*/
import Operation from "../Operation" ;
import OperationError from "../errors/OperationError" ;
2019-01-12 01:35:24 +00:00
import { ROTORS , LETTERS , ROTORS_FOURTH , REFLECTORS , Rotor , Reflector , Plugboard , EnigmaMachine } from "../lib/Enigma" ;
2019-01-03 16:36:56 +00:00
/**
* Enigma operation
*/
2019-01-03 18:51:39 +00:00
class Enigma extends Operation {
2019-01-03 16:36:56 +00:00
/**
* Enigma constructor
*/
constructor () {
super ();
this . name = "Enigma" ;
this . module = "Default" ;
2019-03-14 12:08:35 +00:00
this . description = "Encipher/decipher with the WW2 Enigma machine.<br><br>Enigma was used by the German military, among others, around the WW2 era as a portable cipher machine to protect sensitive military, diplomatic and commercial communications.<br><br>The standard set of German military rotors and reflectors are provided. To configure the plugboard, enter a string of connected pairs of letters, e.g. <code>AB CD EF</code> connects A to B, C to D, and E to F. This is also used to create your own reflectors. To create your own rotor, enter the letters that the rotor maps A to Z to, in order, optionally followed by <code><</code> then a list of stepping points.<br>This is deliberately fairly permissive with rotor placements etc compared to a real Enigma (on which, for example, a four-rotor Enigma uses only the thin reflectors and the beta or gamma rotor in the 4th slot).<br><br>More detailed descriptions of the Enigma, Typex and Bombe operations <a href='https://github.com/gchq/CyberChef/wiki/Enigma,-the-Bombe,-and-Typex'>can be found here</a>." ;
2019-01-03 16:36:56 +00:00
this . infoURL = "https://wikipedia.org/wiki/Enigma_machine" ;
this . inputType = "string" ;
this . outputType = "string" ;
this . args = [
{
2019-02-28 15:27:35 +00:00
name : "Model" ,
type : "argSelector" ,
value : [
{
name : "3-rotor" ,
off : [ 1 , 2 , 3 ]
},
{
name : "4-rotor" ,
on : [ 1 , 2 , 3 ]
}
]
},
{
2019-02-28 16:56:28 +00:00
name : "Left-most (4th) rotor" ,
2019-01-03 16:36:56 +00:00
type : "editableOption" ,
2019-02-28 15:27:35 +00:00
value : ROTORS_FOURTH ,
defaultIndex : 0
2019-01-03 16:36:56 +00:00
},
{
2019-02-28 15:27:35 +00:00
name : "Left-most rotor ring setting" ,
2019-01-03 16:36:56 +00:00
type : "option" ,
2019-01-03 18:51:39 +00:00
value : LETTERS
2019-01-03 16:36:56 +00:00
},
{
2019-02-28 15:27:35 +00:00
name : "Left-most rotor initial value" ,
2019-01-03 16:36:56 +00:00
type : "option" ,
2019-01-03 18:51:39 +00:00
value : LETTERS
2019-01-03 16:36:56 +00:00
},
{
2019-02-28 15:27:35 +00:00
name : "Left-hand rotor" ,
2019-01-03 16:36:56 +00:00
type : "editableOption" ,
2019-01-03 18:51:39 +00:00
value : ROTORS ,
2019-02-28 15:27:35 +00:00
defaultIndex : 0
2019-01-03 16:36:56 +00:00
},
{
2019-02-28 15:27:35 +00:00
name : "Left-hand rotor ring setting" ,
2019-01-03 16:36:56 +00:00
type : "option" ,
2019-01-03 18:51:39 +00:00
value : LETTERS
2019-01-03 16:36:56 +00:00
},
{
2019-02-28 15:27:35 +00:00
name : "Left-hand rotor initial value" ,
2019-01-03 16:36:56 +00:00
type : "option" ,
2019-01-03 18:51:39 +00:00
value : LETTERS
2019-01-03 16:36:56 +00:00
},
{
2019-02-28 15:27:35 +00:00
name : "Middle rotor" ,
2019-01-03 16:36:56 +00:00
type : "editableOption" ,
2019-01-03 18:51:39 +00:00
value : ROTORS ,
2019-02-28 15:27:35 +00:00
defaultIndex : 1
2019-01-03 16:36:56 +00:00
},
{
2019-02-28 15:27:35 +00:00
name : "Middle rotor ring setting" ,
2019-01-03 16:36:56 +00:00
type : "option" ,
2019-01-03 18:51:39 +00:00
value : LETTERS
2019-01-03 16:36:56 +00:00
},
{
2019-02-28 15:27:35 +00:00
name : "Middle rotor initial value" ,
2019-01-03 16:36:56 +00:00
type : "option" ,
2019-01-03 18:51:39 +00:00
value : LETTERS
2019-01-03 16:36:56 +00:00
},
{
2019-02-28 15:27:35 +00:00
name : "Right-hand rotor" ,
2019-01-03 16:36:56 +00:00
type : "editableOption" ,
2019-02-28 15:27:35 +00:00
value : ROTORS ,
// Default config is the rotors I-III *left to right*
defaultIndex : 2
2019-01-03 16:36:56 +00:00
},
2019-01-03 17:51:20 +00:00
{
2019-02-28 15:27:35 +00:00
name : "Right-hand rotor ring setting" ,
2019-01-03 17:51:20 +00:00
type : "option" ,
2019-01-03 18:51:39 +00:00
value : LETTERS
2019-01-03 17:51:20 +00:00
},
2019-01-03 16:36:56 +00:00
{
2019-02-28 15:27:35 +00:00
name : "Right-hand rotor initial value" ,
2019-01-03 16:36:56 +00:00
type : "option" ,
2019-01-03 18:51:39 +00:00
value : LETTERS
2019-01-03 16:36:56 +00:00
},
{
name : "Reflector" ,
type : "editableOption" ,
2019-01-03 18:51:39 +00:00
value : REFLECTORS
2019-01-03 16:36:56 +00:00
},
{
name : "Plugboard" ,
type : "string" ,
value : ""
},
{
name : "Strict output" ,
hint : "Remove non-alphabet letters and group output" ,
type : "boolean" ,
value : true
},
];
}
/**
* Helper - for ease of use rotors are specified as a single string; this
* method breaks the spec string into wiring and steps parts.
*
* @param {string} rotor - Rotor specification string.
* @param {number} i - For error messages, the number of this rotor.
* @returns {string[]}
*/
parseRotorStr ( rotor , i ) {
if ( rotor === "" ) {
throw new OperationError ( `Rotor ${ i } must be provided.` );
}
if ( ! rotor . includes ( "<" )) {
return [ rotor , "" ];
}
return rotor . split ( "<" , 2 );
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run ( input , args ) {
2019-02-28 15:27:35 +00:00
const model = args [ 0 ];
const reflectorstr = args [ 13 ];
const plugboardstr = args [ 14 ];
const removeOther = args [ 15 ];
2019-01-03 16:36:56 +00:00
const rotors = [];
2019-01-03 17:51:20 +00:00
for ( let i = 0 ; i < 4 ; i ++ ) {
2019-02-28 15:27:35 +00:00
if ( i === 0 && model === "3-rotor" ) {
// Skip the 4th rotor settings
continue ;
2019-01-03 17:51:20 +00:00
}
2019-02-28 15:27:35 +00:00
const [ rotorwiring , rotorsteps ] = this . parseRotorStr ( args [ i * 3 + 1 ], 1 );
rotors . push ( new Rotor ( rotorwiring , rotorsteps , args [ i * 3 + 2 ], args [ i * 3 + 3 ]));
2019-01-03 16:36:56 +00:00
}
2019-02-28 15:27:35 +00:00
// Rotors are handled in reverse
rotors . reverse ();
2019-01-03 18:51:39 +00:00
const reflector = new Reflector ( reflectorstr );
const plugboard = new Plugboard ( plugboardstr );
2019-01-03 16:36:56 +00:00
if ( removeOther ) {
input = input . replace ( /[^A-Za-z]/g , "" );
}
2019-01-03 18:51:39 +00:00
const enigma = new EnigmaMachine ( rotors , reflector , plugboard );
2019-01-03 16:36:56 +00:00
let result = enigma . crypt ( input );
if ( removeOther ) {
// Five character cipher groups is traditional
result = result . replace ( /([A-Z]{5})(?!$)/g , "$1 " );
}
return result ;
}
/**
* Highlight Enigma
* This is only possible if we're passing through non-alphabet characters.
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight ( pos , args ) {
if ( args [ 13 ] === false ) {
return pos ;
}
}
/**
* Highlight Enigma in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse ( pos , args ) {
if ( args [ 13 ] === false ) {
return pos ;
}
}
}
2019-01-03 18:51:39 +00:00
export default Enigma ;