-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathEnigma.js
More file actions
320 lines (259 loc) · 10.5 KB
/
Enigma.js
File metadata and controls
320 lines (259 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
function Enigma(plugboardSettings, rotorOrder, rotorSettings){
//error checking starts
if(plugboardSettings.length != 20){ //checks if plugboardsettings are the right length
throw "There must be 10 paired letters for the plugboard";
}
if(rotorOrder.length != 3){
throw "Please select 3 rotors";
}
if(!Array.isArray(rotorSettings)){
throw "rotorSettings must be an Array";
}
if(rotorSettings.length != 3){
throw "Please enter 3 rotor settings";
}
//error checking ends
//constructor begins
this.numRotors = 3;
this.pb = new Plugboard(plugboardSettings); //creates the Plugboard with the given settings.
this.rotorSet = getRotors(rotorOrder,rotorSettings);
this.shiftFirst = this.rotorSet[1].orientation == (this.rotorSet[1].turnKey-2); //determines wheteher to shift the first rotor during the first shift
function getRotors(rO,rS){//rotorOrder, rotorSettings
let rotors = []; //set of all rotors
let setting = 0; //setting of rotor to be added
let rotor;
for(i = 0; i<3; i++){
setting = rS[i];
if(!Number.isInteger(setting)){
throw "Rotor settings must be numbers";
}
if(setting > 26 || setting < 1){
throw "Rotor Settings must be between 1 and 26 inclusive"
}
switch(rO.charAt(i)){
case "1":
rotor = new RotorOne(setting);
break;
case "2":
rotor = new RotorTwo(setting);
break;
case "3":
rotor = new RotorThree(setting);
break;
case "4":
rotor = new RotorFour(setting);
break;
case "5":
rotor = new RotorFive(setting);
break;
default:
throw "A rotor selector must be a number between 1 and 5 inclusive";
}
rotors.push(rotor);
}
return rotors;
}
//constructor ends
this.scramble = function(message){
if(message === null || message === undefined){
throw "message cannot be " + message;
}
let cleaned = this.clean(message); //Enigma doesn't have spaces or special Characters. As such, those characters must be removed.
let scrambled = "";
for(i = 0; i<cleaned.length; i++){
let c = this.scrambleChar(cleaned.charAt(i));
scrambled += c;
}
return scrambled;
}
this.clean = function(originalMessage){ //removes spaces and other non-alpha characters
originalMessage = originalMessage.toUpperCase();
var cleanedString = ""; //string after cleaning
let c; //character being tested
for(i = 0; i< originalMessage.length; i++){
let c = originalMessage.charAt(i);
if(charToInt(c) > 0 && charToInt(c) <= 26){ //is a letter
cleanedString += c;
}
}
return cleanedString;
}
this.scrambleChar = function(char){//scrambles each character one by one
this.shiftRotors();
var scrambled = this.pb.runPlugboard(char);
scrambled = this.runRotorsF(scrambled); //runing rotors forwards
scrambled = this.reflector(scrambled);
scrambled = this.runRotorsB(scrambled); //running rotors backwards
scrmabled = this.pb.runPlugboard(scrambled);
return scrambled;
}
/*
The first thing to know is that Enigma rusn from the third rotor to the second to the first.
The third rotor gets shifted no matter what.
the second rotor gets shifted if the third rotor makes its turnkey shift. Every rotor has one. For example, if RotorOne is in the third spot,
and it goes from Q to R, then the second rotor will also shift.
The second rotor will also shift if the previous shift put it at the beggining of the turnkey. For example, if rotorFour is the second rotor,
and get shifted onto J, then the next turn the second rotor is guraenteed to shift the next run, and is also guarenteed to shif the first rotor as well.
*/
this.shiftRotors = function(){
if(this.shiftFirst){
this.rotorSet[1].shift();
this.rotorSet[0].shift();
this.shiftFirst = false;
}
if(this.rotorSet[this.numRotors-1].shift() && !this.shiftFirst){ //prevent from shifting twice if both 3 and 2 are at their critical points.
this.rotorSet[1].shift();
if(this.rotorSet[1].orientation == this.rotorSet[1].turnKey-2){
this.shiftFirst = true;
}
}
}
this.runRotorsF = function(char){ //running each rotor forward
var rotored = this.rotorSet[2].run(char, "F");
rotored = this.rotorSet[1].run(rotored, "F");
rotored = this.rotorSet[0].run(rotored, "F");
return rotored;
}
this.reflector = function(c){
let reflection = "YRUHQSLDPXNGOKMIEBFZCWVJAT"; //this shows where the reflection will give, where the first letter is "A", the second letter is "B", and so on.
return reflection.charAt(charToInt(c)-1);
}
this.runRotorsB = function(char){ //running each rotor forward
var rotored = this.rotorSet[0].run(char, "B");
rotored = this.rotorSet[1].run(rotored, "B");
rotored = this.rotorSet[2].run(rotored, "B");
return rotored;
}
this.showRotors = function(){
for(i = 0; i < this.numRotors; i++){
console.log(this.rotorSet[i].type + " @ (" + intToChar(this.rotorSet[i].orientation+1) + ")");
}
}
}
function Plugboard(input){
let output = ["error"];
let settings = input.toUpperCase();
let length = settings.length;
for(i = 0; i< 26; i++){//Map 1 to A, 2 to B, and so on.
output[i] = intToChar(i+1);
}
for(i = 0; i< 20; i+=2){//switches characters such that if a and b are connected, a will output B and b will output A
let charOne = settings.charAt(i);
let charTwo = settings.charAt(i+1);
let indexOne = charToInt(charOne) - 1;
let indexTwo = charToInt(charTwo) - 1;
if((indexOne > 26 || indexOne < 0) || ((indexOne > 26 || indexOne < 0) )){ //checks for invalid entries
throw "Only have letters are valid in Plugboard Settings";
}
if(output[indexOne] != charOne || output[indexTwo] != charTwo){
throw "no duplicates are allowed in the PlugboardSettings";
}
swap(indexOne,indexTwo);
}
this.runPlugboard = function(c){//will return letter as it runs through the plugboard.
return output[charToInt(c)-1];
}
function swap(indexOne, indexTwo){
let temp = output[indexOne];
output[indexOne] = output[indexTwo];
output[indexTwo] = temp;
}
}
class Rotor{
constructor(type, orientation){
this.orientation = orientation-1; //represents how far oriented this rotor is. Will not shift if is 1, as a 1 would represent a being paried with A
this.type = type;
}
run(c, direction){//c is the character to be scrambled, direction dictates if it's forwards or backwards
let num = charToInt(c); //turns the character into a number
num--; // "A" will need to go through charAt(0), "B" will go through charAT(1), and so on, so we must subtract by one
num = (num + this.orientation) % 26;//shifts by the orientation, then rounds back down
if(direction === "F"){ // run forwards
num = charToInt(this.forwardOutput.charAt(num)); //runs forwards through the wire as a char, then converts the new char back to a number
}else{ //running backwards
num = charToInt(this.backwardsOutput.charAt(num)); //runs backwards through the wire as a char, then converts the new char back to a number
}
num = (num - this.orientation +26)%26; //shifts backwards by the orientation amount, and the +26 catches any negative numbers.
return intToChar(num);
}
shift(){
if(this.orientation == 25){// If at the end, set back to start
this.orientation = 0;
}else{
this.orientation++;
}
if(this.orientation+1 == (this.turnKey)){//returns wether it is at the critical point yet (+1 because orientation is one before the letter index)
return true;
}else{
return false;
}
}
output(){
return "Rotor " + this.type + " @ " + this.orientation+1 + " (" + intToChar(this.orientation+1) + ")";
}
}
class RotorOne extends Rotor{
constructor(orientation){
super(1, orientation);
this.turnKey = 18; //turns after going from Q to R
this.forwardOutput = "EKMFLGDQVZNTOWYHXUSPAIBRCJ"; //tells how the wiring runs going forwards
this.backwardsOutput = "UWYGADFPVZBECKMTHXSLRINQOJ"; //tells how the wiring runs going backwards
}
}
class RotorTwo extends Rotor{
constructor(orientation){
super(2, orientation);
this.turnKey = 6; //turns after going from E to F
this.forwardOutput = "AJDKSIRUXBLHWTMCQGZNPYFVOE"; //tells how the wiring runs going forwards
this.backwardsOutput = "AJPCZWRLFBDKOTYUQGENHXMIVS"; //tells how the wiring runs going backwards
}
}
class RotorThree extends Rotor{
constructor(orientation){
super(3, orientation);
this.turnKey = 23; //turns after going from V to W
this.forwardOutput = "BDFHJLCPRTXVZNYEIWGAKMUSQO"; //tells how the wiring runs going forwards
this.backwardsOutput = "TAGBPCSDQEUFVNZHYIXJWLRKOM"; //tells how the wiring runs going backwards
}
}
class RotorFour extends Rotor{
constructor(orientation){
super(4, orientation);
this.turnKey = 11; //turns after going from J to K
this.forwardOutput = "ESOVPZJAYQUIRHXLNFTGKDCMWB"; //tells how the wiring runs going forwards
this.backwardsOutput = "HZWVARTNLGUPXQCEJMBSKDYOIF"; //tells how the wiring runs going backwards
}
}
class RotorFive extends Rotor{
constructor(orientation){
super(5, orientation);
this.turnKey = 1; //turns after going from Z to A
this.forwardOutput = "VZBRGITYUPSDNHLXAWMJQOFECK"; //tells how the wiring runs going forwards
this.backwardsOutput = "QCYLXWENFTZOSMVJUDKGIARPHB"; //tells how the wiring runs going backwards
}
}
//helper methods
function charToInt(char){//turns A to 1, B to 2, and so on
var upper = char.toUpperCase();
return upper.charCodeAt(0)-64;
}
function intToChar(int){//turns 1 to A, B to 2, and so on
if(int == 0){//26%26 is 0, so I might be given a 0 when I would have expected a 26.
return "Z";
}
return String.fromCharCode(int+64);
}
//main
var settingsForRotors = [10,4,22];
var message = "Hello There";
var e = new Enigma("abcdefghijklmnopqrst", "123", settingsForRotors);
console.log("\"" + message + "\"" + " scrambled is \"" + e.scramble(message) + "\".");
/*
Q -> R
E -> F
V -> W
J -> K
Z -> A
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
*/