From f007c093eb3820161ab801a83e94a9da45d4f961 Mon Sep 17 00:00:00 2001 From: hettysymes <59455170+hettysymes@users.noreply.github.com> Date: Sun, 12 Jan 2020 15:06:41 +0000 Subject: [PATCH 1/6] 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 --- src/core/lib/SIGABA.mjs | 501 +++++++++++++++++++++++++++++++++ src/core/operations/SIGABA.mjs | 293 +++++++++++++++++++ 2 files changed, 794 insertions(+) create mode 100644 src/core/lib/SIGABA.mjs create mode 100644 src/core/operations/SIGABA.mjs diff --git a/src/core/lib/SIGABA.mjs b/src/core/lib/SIGABA.mjs new file mode 100644 index 00000000..c35eb3a5 --- /dev/null +++ b/src/core/lib/SIGABA.mjs @@ -0,0 +1,501 @@ +/** +Emulation of the SIGABA machine + +@author hettysymes +*/ + +/** +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 upper case +@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 (let 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 (let 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]}; + let 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 (let 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(); + this.numberOfMoves = 1; + } + + /** + Encrypts a letter. + + @param {char} inputPos - the input position of the signal + @returns {char} + */ + crypt(inputPos){ + for (let 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"}; + let numberOutputs = []; + for (let key in logicDict){ + const item = logicDict[key]; + for (let 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]; + this.numberOfMoves ++; + FRotor.step(); + if (this.numberOfMoves%26 == 0){ + MRotor.step(); + } + if (this.numberOfMoves%(26*26) == 0){ + SRotor.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 (let 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[]} - inputs from the control rotors + @returns {number[]} + */ + goThroughIndex(controlInputs){ + let 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; + let tempMapping = new Array(length); + for (let i=0; ithis.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]; + var 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"); + } + +} diff --git a/src/core/operations/SIGABA.mjs b/src/core/operations/SIGABA.mjs new file mode 100644 index 00000000..78f05530 --- /dev/null +++ b/src/core/operations/SIGABA.mjs @@ -0,0 +1,293 @@ +/** +Emulation of the SIGABA machine. + +@author hettysymes +*/ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.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 = "SIGABA"; + this.description = "Encipher/decipher with the WW2 SIGABA machine.

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. The idea behind its design was to truly randomise the motion of the rotors. In comparison, Enigma, which rotates its rotors once every key pressed, has much less randomised rotor movements. 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. All example rotor wirings are random example sets.

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."; + this.infoURL = "https://en.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} rotor - rotor wirings + @returns {string} + */ + + parseRotorStr(rotor){ + if (rotor === ""){ + throw new OperationError(`All rotor wirings must be provided.`); + } + return rotor; + } + + run(input, args){ + const sigabaSwitch = args[40]; + const cipherRotors = []; + const controlRotors = []; + const indexRotors = []; + for (let i=0; i<5; i++){ + const rotorWiring = this.parseRotorStr(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 = this.parseRotorStr(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 = this.parseRotorStr(args[i*2]); + indexRotors.push(new IRotor(rotorWiring, args[i*2+1])); + } + const sigaba = new SigabaMachine(cipherRotors, controlRotors, indexRotors); + var result; + if (sigabaSwitch === "Encrypt"){ + result = sigaba.encrypt(input); + } + else if (sigabaSwitch === "Decrypt") { + result = sigaba.decrypt(input); + } + return result; + } + +} +export default Sigaba; From 5d01b06877417120826826f823565c202a022b53 Mon Sep 17 00:00:00 2001 From: hettysymes <59455170+hettysymes@users.noreply.github.com> Date: Sun, 12 Jan 2020 15:37:07 +0000 Subject: [PATCH 2/6] Added copyright and clarified description --- src/core/lib/SIGABA.mjs | 2 ++ src/core/operations/SIGABA.mjs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/lib/SIGABA.mjs b/src/core/lib/SIGABA.mjs index c35eb3a5..b56b3e24 100644 --- a/src/core/lib/SIGABA.mjs +++ b/src/core/lib/SIGABA.mjs @@ -2,6 +2,8 @@ Emulation of the SIGABA machine @author hettysymes +@copyright hettysymes 2020 +@license Apache-2.0 */ /** diff --git a/src/core/operations/SIGABA.mjs b/src/core/operations/SIGABA.mjs index 78f05530..2f42c501 100644 --- a/src/core/operations/SIGABA.mjs +++ b/src/core/operations/SIGABA.mjs @@ -2,6 +2,8 @@ Emulation of the SIGABA machine. @author hettysymes +@copyright hettysymes 2020 +@license Apache-2.0 */ import Operation from "../Operation.mjs"; @@ -21,7 +23,7 @@ super(); this.name = "SIGABA"; this.module = "SIGABA"; - this.description = "Encipher/decipher with the WW2 SIGABA machine.

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. The idea behind its design was to truly randomise the motion of the rotors. In comparison, Enigma, which rotates its rotors once every key pressed, has much less randomised rotor movements. 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. All example rotor wirings are random example sets.

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."; + this.description = "Encipher/decipher with the WW2 SIGABA machine.

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.

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."; this.infoURL = "https://en.wikipedia.org/wiki/SIGABA"; this.inputType = "string"; this.outputType = "string"; From 938385c18b5b66c691fc08ed38949491107c75de Mon Sep 17 00:00:00 2001 From: hettysymes <59455170+hettysymes@users.noreply.github.com> Date: Sun, 12 Jan 2020 16:49:04 +0000 Subject: [PATCH 3/6] Fixed grunt lint errors --- src/core/lib/SIGABA.mjs | 156 +++++----- src/core/operations/SIGABA.mjs | 501 ++++++++++++++++----------------- 2 files changed, 322 insertions(+), 335 deletions(-) diff --git a/src/core/lib/SIGABA.mjs b/src/core/lib/SIGABA.mjs index b56b3e24..b69c7739 100644 --- a/src/core/lib/SIGABA.mjs +++ b/src/core/lib/SIGABA.mjs @@ -43,9 +43,9 @@ Converts a letter to uppercase (if it already isn't) @param {char} letter - letter to convert to upper case @returns {char} */ -export function convToUpperCase(letter){ +export function convToUpperCase(letter) { const charCode = letter.charCodeAt(); - if (97<=charCode && charCode<=122){ + if (97<=charCode && charCode<=122) { return String.fromCharCode(charCode-32); } return letter; @@ -54,7 +54,7 @@ export function convToUpperCase(letter){ /** The SIGABA machine consisting of the 3 rotor banks: cipher, control and index banks. */ -export class SigabaMachine{ +export class SigabaMachine { /** SigabaMachine constructor @@ -62,7 +62,7 @@ export class SigabaMachine{ @param {Object[]} controlRotors - list of CRRotors @param {object[]} indexRotors - list of IRotors */ - constructor(cipherRotors, controlRotors, indexRotors){ + constructor(cipherRotors, controlRotors, indexRotors) { this.cipherBank = new CipherBank(cipherRotors); this.controlBank = new ControlBank(controlRotors); this.indexBank = new IndexBank(indexRotors); @@ -71,7 +71,7 @@ export class SigabaMachine{ /** Steps all the correct rotors in the machine. */ - step(){ + step() { const controlOut = this.controlBank.goThroughControl(); const indexOut = this.indexBank.goThroughIndex(controlOut); this.cipherBank.step(indexOut); @@ -83,12 +83,11 @@ export class SigabaMachine{ @param {char} letter - letter to encrypt @returns {char} */ - encryptLetter(letter){ + encryptLetter(letter) { letter = convToUpperCase(letter); - if (letter == " "){ + if (letter === " ") { letter = "Z"; - } - else if (letter == "Z") { + } else if (letter === "Z") { letter = "X"; } const encryptedLetter = this.cipherBank.encrypt(letter); @@ -102,10 +101,10 @@ export class SigabaMachine{ @param {char} letter - letter to decrypt @returns {char} */ - decryptLetter(letter){ + decryptLetter(letter) { letter = convToUpperCase(letter); let decryptedLetter = this.cipherBank.decrypt(letter); - if (decryptedLetter == "Z"){ + if (decryptedLetter === "Z") { decryptedLetter = " "; } this.step(); @@ -118,9 +117,9 @@ export class SigabaMachine{ @param {string} msg - message to encrypt @returns {string} */ - encrypt(msg){ + encrypt(msg) { let ciphertext = ""; - for (const letter of msg){ + for (const letter of msg) { ciphertext = ciphertext.concat(this.encryptLetter(letter)); } return ciphertext; @@ -132,9 +131,9 @@ export class SigabaMachine{ @param {string} msg - message to decrypt @returns {string} */ - decrypt(msg){ + decrypt(msg) { let plaintext = ""; - for (const letter of msg){ + for (const letter of msg) { plaintext = plaintext.concat(this.decryptLetter(letter)); } return plaintext; @@ -145,13 +144,13 @@ export class SigabaMachine{ /** The cipher rotor bank consists of 5 cipher rotors in either a forward or reversed orientation. */ -export class CipherBank{ +export class CipherBank { /** CipherBank constructor @param {Object[]} rotors - list of CRRotors */ - constructor(rotors){ + constructor(rotors) { this.rotors = rotors; } @@ -161,8 +160,8 @@ export class CipherBank{ @param {char} inputPos - the input position of the signal (letter to be encrypted) @returns {char} */ - encrypt(inputPos){ - for (let rotor of this.rotors){ + encrypt(inputPos) { + for (const rotor of this.rotors) { inputPos = rotor.crypt(inputPos, "leftToRight"); } return inputPos; @@ -174,9 +173,9 @@ export class CipherBank{ @param {char} inputPos - the input position of the signal (letter to be decrypted) @returns {char} */ - decrypt(inputPos){ + decrypt(inputPos) { const revOrderedRotors = [...this.rotors].reverse(); - for (let rotor of revOrderedRotors){ + for (const rotor of revOrderedRotors) { inputPos = rotor.crypt(inputPos, "rightToLeft"); } return inputPos; @@ -187,19 +186,19 @@ export class CipherBank{ @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]}; - let rotorsToMove = []; - for (const key in logicDict){ + 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)){ + for (const i of indexInputs) { + if (item.includes(i)) { rotorsToMove.push(this.rotors[key]); break; } } } - for (let rotor of rotorsToMove){ + for (const rotor of rotorsToMove) { rotor.step(); } } @@ -209,13 +208,13 @@ export class CipherBank{ /** 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{ +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){ + constructor(rotors) { this.rotors = [...rotors].reverse(); this.numberOfMoves = 1; } @@ -226,8 +225,8 @@ export class ControlBank{ @param {char} inputPos - the input position of the signal @returns {char} */ - crypt(inputPos){ - for (let rotor of this.rotors){ + crypt(inputPos) { + for (const rotor of this.rotors) { inputPos = rotor.crypt(inputPos, "rightToLeft"); } return inputPos; @@ -238,14 +237,14 @@ export class ControlBank{ @returns {number[]} */ - getOutputs(){ + 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"}; - let numberOutputs = []; - for (let key in logicDict){ + 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 (let output of outputs){ - if (item.includes(output)){ + for (const output of outputs) { + if (item.includes(output)) { numberOutputs.push(key); break; } @@ -257,14 +256,14 @@ export class ControlBank{ /** 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(){ + step() { const MRotor = this.rotors[1], FRotor = this.rotors[2], SRotor = this.rotors[3]; this.numberOfMoves ++; FRotor.step(); - if (this.numberOfMoves%26 == 0){ + if (this.numberOfMoves%26 === 0) { MRotor.step(); } - if (this.numberOfMoves%(26*26) == 0){ + if (this.numberOfMoves%(26*26) === 0) { SRotor.step(); } } @@ -274,7 +273,7 @@ export class ControlBank{ @returns {number[]} */ - goThroughControl(){ + goThroughControl() { const outputs = this.getOutputs(); this.step(); return outputs; @@ -285,13 +284,13 @@ export class ControlBank{ /** The index rotor bank consists of 5 index rotors all placed in the forwards orientation. */ -export class IndexBank{ +export class IndexBank { /** IndexBank constructor @param {Object[]} rotors - list of IRotors */ - constructor(rotors){ + constructor(rotors) { this.rotors = rotors; } @@ -301,8 +300,8 @@ export class IndexBank{ @param {number} inputPos - the input position of the signal @returns {number} */ - crypt(inputPos){ - for (let rotor of this.rotors){ + crypt(inputPos) { + for (const rotor of this.rotors) { inputPos = rotor.crypt(inputPos); } return inputPos; @@ -314,9 +313,9 @@ export class IndexBank{ @param {number[]} - inputs from the control rotors @returns {number[]} */ - goThroughIndex(controlInputs){ - let outputs = []; - for (const inp of controlInputs){ + goThroughIndex(controlInputs) { + const outputs = []; + for (const inp of controlInputs) { outputs.push(this.crypt(inp)); } return outputs; @@ -327,7 +326,7 @@ export class IndexBank{ /** Rotor class */ -export class Rotor{ +export class Rotor { /** Rotor constructor @@ -335,7 +334,7 @@ export class Rotor{ @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){ + constructor(wireSetting, key, rev) { this.state = key; this.numMapping = this.getNumMapping(wireSetting, rev); this.posMapping = this.getPosMapping(rev); @@ -348,14 +347,13 @@ export class Rotor{ @param {bool} rev - true if reversed, false if not @returns {number[]} */ - getNumMapping(wireSetting, rev){ - if (rev==false){ + getNumMapping(wireSetting, rev) { + if (rev===false) { return wireSetting; - } - else { + } else { const length = wireSetting.length; - let tempMapping = new Array(length); - for (let i=0; ithis.state-length; i--){ + } else { + for (let i = this.state; i > this.state-length; i--) { let res = i%length; - if (res<0){ + if (res<0) { res += length; } posMapping.push(res); @@ -399,13 +396,12 @@ export class Rotor{ @param {string} direction - one of "leftToRight" and "rightToLeft", states the direction in which the signal passes through the rotor @returns {number} */ - cryptNum(inputPos, direction){ + cryptNum(inputPos, direction) { const inpNum = this.posMapping[inputPos]; - var outNum; - if (direction == "leftToRight"){ + let outNum; + if (direction === "leftToRight") { outNum = this.numMapping[inpNum]; - } - else if (direction == "rightToLeft") { + } else if (direction === "rightToLeft") { outNum = this.numMapping.indexOf(inpNum); } const outPos = this.posMapping.indexOf(outNum); @@ -415,7 +411,7 @@ export class Rotor{ /** Steps the rotor. The number at position 0 will be moved to position 1 etc. */ - step(){ + step() { const lastNum = this.posMapping.pop(); this.posMapping.splice(0, 0, lastNum); this.state = this.posMapping[0]; @@ -426,7 +422,7 @@ export class Rotor{ /** 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{ +export class CRRotor extends Rotor { /** CRRotor constructor @@ -435,7 +431,7 @@ export class CRRotor extends Rotor{ @param {char} key - initial state of rotor @param {bool} rev - true if reversed, false if not */ - constructor(wireSetting, key, rev=false){ + constructor(wireSetting, key, rev=false) { wireSetting = wireSetting.split("").map(CRRotor.letterToNum); super(wireSetting, CRRotor.letterToNum(key), rev); } @@ -446,7 +442,7 @@ export class CRRotor extends Rotor{ @param {char} letter - letter to convert to number @returns {number} */ - static letterToNum(letter){ + static letterToNum(letter) { return letter.charCodeAt()-65; } @@ -456,7 +452,7 @@ export class CRRotor extends Rotor{ @param {number} num - number to convert to letter @returns {char} */ - static numToLetter(num){ + static numToLetter(num) { return String.fromCharCode(num+65); } @@ -467,7 +463,7 @@ export class CRRotor extends Rotor{ @param {string} direction - one of "leftToRight" and "rightToLeft" @returns {char} */ - crypt(inputPos, direction){ + crypt(inputPos, direction) { inputPos = CRRotor.letterToNum(inputPos); const outPos = this.cryptNum(inputPos, direction); return CRRotor.numToLetter(outPos); @@ -478,14 +474,14 @@ export class CRRotor extends Rotor{ /** 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{ +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){ + constructor(wireSetting, key) { wireSetting = wireSetting.split("").map(Number); super(wireSetting, Number(key), false); } @@ -496,7 +492,7 @@ export class IRotor extends Rotor{ @param {number} inputPos - the input position of the signal @returns {number} */ - crypt(inputPos){ + crypt(inputPos) { return this.cryptNum(inputPos, "leftToRight"); } diff --git a/src/core/operations/SIGABA.mjs b/src/core/operations/SIGABA.mjs index 2f42c501..d82ee09a 100644 --- a/src/core/operations/SIGABA.mjs +++ b/src/core/operations/SIGABA.mjs @@ -7,285 +7,276 @@ Emulation of the SIGABA machine. */ import Operation from "../Operation.mjs"; -import OperationError from "../errors/OperationError.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(); +class Sigaba extends Operation { + /** + Sigaba constructor + */ + constructor() { + super(); -this.name = "SIGABA"; -this.module = "SIGABA"; - this.description = "Encipher/decipher with the WW2 SIGABA machine.

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.

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."; - this.infoURL = "https://en.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"] - } - ]; + this.name = "SIGABA"; + this.module = "SIGABA"; + this.description = "Encipher/decipher with the WW2 SIGABA machine.

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.

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."; + this.infoURL = "https://en.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} rotor - rotor wirings + @param {string} input + @param {Object[]} args @returns {string} */ - - parseRotorStr(rotor){ - if (rotor === ""){ - throw new OperationError(`All rotor wirings must be provided.`); - } - return rotor; - } - - run(input, args){ + run(input, args) { const sigabaSwitch = args[40]; const cipherRotors = []; const controlRotors = []; const indexRotors = []; - for (let i=0; i<5; i++){ - const rotorWiring = this.parseRotorStr(args[i*3]); + 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 = this.parseRotorStr(args[i*3]); + 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 = this.parseRotorStr(args[i*2]); + 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); - var result; - if (sigabaSwitch === "Encrypt"){ + let result; + if (sigabaSwitch === "Encrypt") { result = sigaba.encrypt(input); - } - else if (sigabaSwitch === "Decrypt") { + } else if (sigabaSwitch === "Decrypt") { result = sigaba.decrypt(input); } return result; From e2b3389da687e74896c0d0ee8e6e89e40141ec9f Mon Sep 17 00:00:00 2001 From: hettysymes <59455170+hettysymes@users.noreply.github.com> Date: Sun, 12 Jan 2020 17:57:20 +0000 Subject: [PATCH 4/6] Added SIGABA simple test --- src/core/config/Categories.json | 3 +- tests/operations/tests/SIGABA.mjs | 67 +++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) mode change 100755 => 100644 src/core/config/Categories.json create mode 100644 tests/operations/tests/SIGABA.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json old mode 100755 new mode 100644 index 77e3d319..aee80ed4 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -116,7 +116,8 @@ "Multiple Bombe", "Typex", "Lorenz", - "Colossus" + "Colossus", + "SIGABA" ] }, { diff --git a/tests/operations/tests/SIGABA.mjs b/tests/operations/tests/SIGABA.mjs new file mode 100644 index 00000000..f8b19c9d --- /dev/null +++ b/tests/operations/tests/SIGABA.mjs @@ -0,0 +1,67 @@ +/** +SIGABA machine tests + +@author hettysymes +@copyright hettysymes 2020 +@license Apache-2.0 +*/ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "SIGABA: encrypt", + input: "hello world testing the sigaba machine", + expectedOutput: "ULBECJCZJBJFVUDLIXGLGIVXSYGMFRJVCERGOX", + 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: decrypt", + input: "helloxworldxtestingxthexsigabaxmachine", + expectedOutput: "XWCIWSAIQKNPBUKAP QXVYW RRNYAWXKRBGCQS", + recipeConfig: [ + { + "op": "SIGABA", + "args": [ + "ZECIPSQVBYKJTNRLOXUFGAWHMD", false, "C", + "IPHECDYSZTRXQUKWNVGOBLFJAM", true, "J", + "YHXUSRKIJVQWTPLAZOMDCGNEFB", true, "Z", + "TDPVSOBXULANZQYEHIGFMCRWJK", false, "W", + "THZGFXQRVBSDUICNYJWPAEMOKL", false, "F", + "KOVUTBMZQWGYDNAICSPHERXJLF", false, "F", + "DSTRLAUFXGWCEOKQPVMBZNIYJH", true, "A", + "KCULNSIXJDPEHGQYRTFZVWOBAM", false, "H", + "DZANEQLOWYRXKGUSIVJFMPBCHT", true, "M", + "MVRLHTPFWCAOKEGXZBJYIQUNSD", false, "E", + "9421765830", "3", + "3476815902", "2", + "5701842693", "7", + "4178920536", "0", + "5243709861", "1", + "Decrypt" + ] + } + ] + } +]); From 3c68ad13024b8e08f8f02e26504d6de6f019cc58 Mon Sep 17 00:00:00 2001 From: hettysymes Date: Sun, 7 Jun 2020 17:45:17 +0100 Subject: [PATCH 5/6] Modified control rotor stepping so the next control rotor steps once the previous rotor reaches "O" and added tests --- src/core/lib/SIGABA.mjs | 13 +++--- tests/operations/index.mjs | 1 + tests/operations/tests/SIGABA.mjs | 74 ++++++++++++++++++++----------- 3 files changed, 56 insertions(+), 32 deletions(-) diff --git a/src/core/lib/SIGABA.mjs b/src/core/lib/SIGABA.mjs index b69c7739..30166ad4 100644 --- a/src/core/lib/SIGABA.mjs +++ b/src/core/lib/SIGABA.mjs @@ -216,7 +216,6 @@ export class ControlBank { */ constructor(rotors) { this.rotors = [...rotors].reverse(); - this.numberOfMoves = 1; } /** @@ -258,14 +257,14 @@ export class ControlBank { */ step() { const MRotor = this.rotors[1], FRotor = this.rotors[2], SRotor = this.rotors[3]; - this.numberOfMoves ++; - FRotor.step(); - if (this.numberOfMoves%26 === 0) { + // 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(); } - if (this.numberOfMoves%(26*26) === 0) { - SRotor.step(); - } + FRotor.step(); } /** diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 8d3cd623..832b9ddd 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -101,6 +101,7 @@ import "./tests/LuhnChecksum.mjs"; import "./tests/CipherSaber2.mjs"; import "./tests/Colossus.mjs"; import "./tests/ParseObjectIDTimestamp.mjs"; +import "./tests/SIGABA.mjs"; // Cannot test operations that use the File type yet diff --git a/tests/operations/tests/SIGABA.mjs b/tests/operations/tests/SIGABA.mjs index f8b19c9d..7bf196be 100644 --- a/tests/operations/tests/SIGABA.mjs +++ b/tests/operations/tests/SIGABA.mjs @@ -9,9 +9,9 @@ import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { - name: "SIGABA: encrypt", - input: "hello world testing the sigaba machine", - expectedOutput: "ULBECJCZJBJFVUDLIXGLGIVXSYGMFRJVCERGOX", + name: "SIGABA: encrypt test 1", + input: "HELLO WORLD TESTING THE SIGABA MACHINE", + expectedOutput: "ULBECJCZJBJFVUDWAVRGRBMPSQHOTTNVQEESKN", recipeConfig: [ { "op": "SIGABA", @@ -37,30 +37,54 @@ TestRegister.addTests([ ] }, { - name: "SIGABA: decrypt", - input: "helloxworldxtestingxthexsigabaxmachine", - expectedOutput: "XWCIWSAIQKNPBUKAP QXVYW RRNYAWXKRBGCQS", + name: "SIGABA: encrypt test 2", + input: "PCRPJZWSPNOHMWANBFBEIVZOXDQESPYDEFBNTHXLSICIRPKUATJVDUQFLZOKGHHHDUDIBRKUHVCGAGLBWVGFFXNDHKPFSPSCIIPCXUFRRHNYWIJFEJWQSGMSNJHWSLPKVXHUQUWIURHDIHIUTWGQFIYLTKEZAUESWYEKIWXUSSXWXBEHCXCUDQWKCISVPKXJVPOIJZWTUGKAORBMKBAQUZOPTSUSYZRROWQUYKNCLHVIHEGWCCONGVHEKCEXVYIPNILIXTXDELNGLJGMEQKKQJWZLPNXPOGIOSVAEAJYKWYJXXGKKPLVYAZGDCMNHMPLCYWDQSRBEMVVVZVFYJMRYGHJOTDOEQVRQOVXOGOVYGTXETFHAYELRYVDGWOFVGAOWPMHQYRZMNXVTAHWSKZLJDFVQPZGMHZWFNOBHSZHEDAEXIFCEEJYZDOEFOQWCXTKPJRUEITKHVCITCLKBUFNAFBYXELAYPBRGGGOCCAGLXXJXTSWCJHMHQPVUIBAGBDKAGEEEPKRGGICJQXSYHBNNAKGYODRAUWAEYHWCKHEQIBAONWQJYQCIFKDTOCTJMBJULWKMSNNMPXINHZQWUMJQLQKIPVZVRGYPCJJZMENWTFTUSPCSPRXHMZPCHCNQTUDCOUJHRKYQIUWWVEVVRYFDIYRQISNGPMQLNMCNMVBEWHNCUODHAGEVEUMKVZLEIKYAMPGVVSBYNRJMFCATDXTQCYXIBCXXKYEYHHYERQGQWZTWCEJBFQLRFFCIVVSZUKGLOTLNGLQNTIKTBBWVFMONUFKRLCJASEKUEEDDQDIVQMFRSJRNHYZJODFHSCJSDAIRUXOSDNFUFUFMNZYQIEGRUXKUPCHENUEZHRKYHDRJYSHLZNYRBWVXORMJMJRIRNSAJQRUMPCXUDFYRGKEAXQXJHPEWNIYIDURDGWIFEMSOFYYCFRZGMZXJNTLTJBBSZIULQSOMEVGCTCVXUHTIEHSPOPQYCJLPAJAPQPAQXE", + expectedOutput: "GMEXPPCMFGKUVGXZHVTCKXRSTJUYWNOKFVELWAHHSJBXGOEXCMLOVSIMCDMGEYMWWTFDUMCDUJEZITNPVVBGQDJEVHJXSKJAAUZWBELMSPUTXCUYPDTJCQXEBGWPWRSQLSNFMASCTJZDSFNKDDTAXLRGUPKCBNXMZPADJSFGGNYKRPYBNTYPTGVPACBEINILNACWFVKMJPGCEZFROEYYKTGYSQYMFSGVDOJJONNYEYSCCIXWLKUSJZDRVAQSNUWHMDJVDNNMPGOYRGQRSBGSPQKGCTFZQWSOXBWSQZDCRQJQAWZDPQEILGMMABIMCDPNSKAFCLPQGIRJCMGQREBEUHBYREXFABFMVZTZBDUMASVNUMHIYRSZLGNZFMVAIABLCUZLJLKKZPWEXDHYZFVSNRLCLNDRKLKSWRHQVQJRTHCNFZXDEXSLAXXOGMFVSGCJGAWOLGDMTLWSFNTCUVCCEACINRZAZZOGLEHHXLPHVKILBBJDPOOCILQKKGODSXOBDPZZDXHJLLBOBVFCHJVMUBUZZIKGCWGCYGXVEHHIJGPEQERWEZLILQNHPHALFKFMGADNELGBKILKIUETGDCBQUEOECWVFNOXTJKUYPWBNEKYSIKMVSAMBZGLIKDAOELRSTKFASEKABTUCPSFEGXXQGDFPSPVOLBHGLZSLLWCABSRKZDQQRKVCKXDGTIHPDNMPDZEXYFYKXZTPJPLYOFNLWAGKJEOHOYLMZELXIDWWNXPKEPUCKNNNHJLFYHPQNHMMCGMUPHSUSYYIVWTIMFKKKTFPGFTLTWWSQBRBMGBTZXPVULKNZIIKVTYLJFISGPTLZFTCLGNZOMVKZOIMUDGXRDDSVFRHRYWBEWHYLCUISYMRWAZZAQPJYXZQQKZLILOSHXUTQJFPTXQSREKSUDZTLGUDLUGOJMQHJRJHXCHQTKJULTWWQOXIRFRQEYBPJPEKXFIRMNATWNFBADOSIJVZYRYDBHDAEDJUVDHLDAU", recipeConfig: [ - { - "op": "SIGABA", + { "op": "SIGABA", "args": [ - "ZECIPSQVBYKJTNRLOXUFGAWHMD", false, "C", - "IPHECDYSZTRXQUKWNVGOBLFJAM", true, "J", - "YHXUSRKIJVQWTPLAZOMDCGNEFB", true, "Z", - "TDPVSOBXULANZQYEHIGFMCRWJK", false, "W", - "THZGFXQRVBSDUICNYJWPAEMOKL", false, "F", - "KOVUTBMZQWGYDNAICSPHERXJLF", false, "F", - "DSTRLAUFXGWCEOKQPVMBZNIYJH", true, "A", - "KCULNSIXJDPEHGQYRTFZVWOBAM", false, "H", - "DZANEQLOWYRXKGUSIVJFMPBCHT", true, "M", - "MVRLHTPFWCAOKEGXZBJYIQUNSD", false, "E", - "9421765830", "3", - "3476815902", "2", - "5701842693", "7", - "4178920536", "0", - "5243709861", "1", - "Decrypt" - ] + "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"] } ] } From 88947b9d42bd9e4ae085406db3f3301385da68ac Mon Sep 17 00:00:00 2001 From: hettysymes Date: Mon, 8 Jun 2020 12:27:40 +0100 Subject: [PATCH 6/6] Added operation description note and modified comment formatting --- src/core/lib/SIGABA.mjs | 338 +++++++++++++++--------------- src/core/operations/SIGABA.mjs | 31 +-- tests/operations/tests/SIGABA.mjs | 12 +- 3 files changed, 193 insertions(+), 188 deletions(-) diff --git a/src/core/lib/SIGABA.mjs b/src/core/lib/SIGABA.mjs index 30166ad4..09951c4f 100644 --- a/src/core/lib/SIGABA.mjs +++ b/src/core/lib/SIGABA.mjs @@ -1,15 +1,14 @@ /** -Emulation of the SIGABA machine - -@author hettysymes -@copyright hettysymes 2020 -@license Apache-2.0 -*/ + * 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. -*/ - + * 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"}, @@ -24,9 +23,8 @@ export const CR_ROTORS = [ ]; /** -A set of randomised example SIGABA index rotors (may be referred to as I rotors). -*/ - + * 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"}, @@ -38,11 +36,11 @@ export const I_ROTORS = [ export const NUMBERS = "0123456789".split(""); /** -Converts a letter to uppercase (if it already isn't) - -@param {char} letter - letter to convert to upper case -@returns {char} -*/ + * 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) { @@ -52,16 +50,17 @@ export function convToUpperCase(letter) { } /** -The SIGABA machine consisting of the 3 rotor banks: cipher, control and index banks. -*/ + * 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 - */ + /** + * 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); @@ -69,8 +68,8 @@ export class SigabaMachine { } /** - Steps all the correct rotors in the machine. - */ + * Steps all the correct rotors in the machine. + */ step() { const controlOut = this.controlBank.goThroughControl(); const indexOut = this.indexBank.goThroughIndex(controlOut); @@ -78,11 +77,11 @@ export class SigabaMachine { } /** - 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} - */ + * 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 === " ") { @@ -96,11 +95,11 @@ export class SigabaMachine { } /** - 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} - */ + * 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); @@ -112,11 +111,11 @@ export class SigabaMachine { } /** - Encrypts a message of one or more letters - - @param {string} msg - message to encrypt - @returns {string} - */ + * 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) { @@ -126,11 +125,11 @@ export class SigabaMachine { } /** - Decrypts a message of one or more letters - - @param {string} msg - message to decrypt - @returns {string} - */ + * 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) { @@ -142,24 +141,25 @@ export class SigabaMachine { } /** -The cipher rotor bank consists of 5 cipher rotors in either a forward or reversed orientation. -*/ + * 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 - */ + /** + * 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} - */ + * 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"); @@ -168,11 +168,11 @@ export class CipherBank { } /** - 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} - */ + * 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) { @@ -182,10 +182,10 @@ export class CipherBank { } /** - Step the cipher rotors forward according to the inputs from the index rotors - - @param {number[]} indexInputs - the inputs from the index rotors - */ + * 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 = []; @@ -206,24 +206,25 @@ export class CipherBank { } /** -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. -*/ + * 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 - */ + /** + * 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} - */ + * 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"); @@ -232,10 +233,10 @@ export class ControlBank { } /** - Gets the outputs of the control rotors. The inputs to the control rotors are always "F", "G", "H" and "I". - - @returns {number[]} - */ + * 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"}; @@ -253,8 +254,8 @@ export class ControlBank { } /** - 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. - */ + * 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" @@ -268,10 +269,10 @@ export class ControlBank { } /** - The goThroughControl function combines getting the outputs from the control rotor bank and then stepping them. - - @returns {number[]} - */ + * 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(); @@ -281,24 +282,25 @@ export class ControlBank { } /** -The index rotor bank consists of 5 index rotors all placed in the forwards orientation. -*/ + * 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 - */ + /** + * 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} - */ + * 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); @@ -307,11 +309,11 @@ export class IndexBank { } /** - 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[]} - inputs from the control rotors - @returns {number[]} - */ + * 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) { @@ -323,16 +325,17 @@ export class IndexBank { } /** -Rotor class -*/ + * 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 - */ + /** + * 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); @@ -340,12 +343,12 @@ export class Rotor { } /** - 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[]} - */ + * 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; @@ -360,11 +363,11 @@ export class Rotor { } /** - 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[]} - */ + * 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 = []; @@ -389,12 +392,12 @@ export class Rotor { } /** - 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} - */ + * 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; @@ -408,8 +411,8 @@ export class Rotor { } /** - Steps the rotor. The number at position 0 will be moved to position 1 etc. - */ + * 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); @@ -419,49 +422,49 @@ export class Rotor { } /** -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. -*/ + * 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 - */ + * 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 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 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} - */ + * 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); @@ -471,26 +474,27 @@ export class CRRotor extends Rotor { } /** -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. -*/ + * 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 - */ + /** + * 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} - */ + * Encrypts a number + * + * @param {number} inputPos - the input position of the signal + * @returns {number} + */ crypt(inputPos) { return this.cryptNum(inputPos, "leftToRight"); } diff --git a/src/core/operations/SIGABA.mjs b/src/core/operations/SIGABA.mjs index d82ee09a..42d1a9f3 100644 --- a/src/core/operations/SIGABA.mjs +++ b/src/core/operations/SIGABA.mjs @@ -1,28 +1,29 @@ /** -Emulation of the SIGABA machine. - -@author hettysymes -@copyright hettysymes 2020 -@license Apache-2.0 -*/ + * 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 -*/ + * Sigaba operation + */ class Sigaba extends Operation { + /** - Sigaba constructor - */ + * Sigaba constructor + */ constructor() { super(); this.name = "SIGABA"; this.module = "SIGABA"; - this.description = "Encipher/decipher with the WW2 SIGABA machine.

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.

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."; + this.description = "Encipher/decipher with the WW2 SIGABA machine.

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.

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.

Note: Whilst this has been tested against other software emulators, it has not been tested against hardware."; this.infoURL = "https://en.wikipedia.org/wiki/SIGABA"; this.inputType = "string"; this.outputType = "string"; @@ -251,10 +252,10 @@ class Sigaba extends Operation { } /** - @param {string} input - @param {Object[]} args - @returns {string} - */ + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ run(input, args) { const sigabaSwitch = args[40]; const cipherRotors = []; diff --git a/tests/operations/tests/SIGABA.mjs b/tests/operations/tests/SIGABA.mjs index 7bf196be..5f07ce20 100644 --- a/tests/operations/tests/SIGABA.mjs +++ b/tests/operations/tests/SIGABA.mjs @@ -1,10 +1,10 @@ /** -SIGABA machine tests - -@author hettysymes -@copyright hettysymes 2020 -@license Apache-2.0 -*/ + * SIGABA machine tests + * + * @author hettysymes + * @copyright hettysymes 2020 + * @license Apache-2.0 + */ import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([