cyberchef 9.34.1 → 9.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +0 -1
- package/package.json +1 -1
- package/src/core/Utils.mjs +26 -1
- package/src/core/config/Categories.json +4 -1
- package/src/core/config/OperationConfig.json +1246 -155
- package/src/core/config/modules/Bletchley.mjs +2 -0
- package/src/core/config/modules/Default.mjs +4 -0
- package/src/core/lib/Base45.mjs +27 -0
- package/src/core/lib/Charts.mjs +2 -1
- package/src/core/lib/SIGABA.mjs +502 -0
- package/src/core/operations/Bombe.mjs +3 -0
- package/src/core/operations/Colossus.mjs +3 -0
- package/src/core/operations/DeriveEVPKey.mjs +1 -1
- package/src/core/operations/Enigma.mjs +3 -0
- package/src/core/operations/FromBase45.mjs +89 -0
- package/src/core/operations/Lorenz.mjs +3 -0
- package/src/core/operations/SIGABA.mjs +287 -0
- package/src/core/operations/ScatterChart.mjs +1 -1
- package/src/core/operations/SeriesChart.mjs +4 -1
- package/src/core/operations/ToBase45.mjs +78 -0
- package/src/core/operations/Typex.mjs +3 -0
- package/src/core/operations/index.mjs +6 -0
- package/src/node/index.mjs +15 -0
- package/tests/operations/index.mjs +3 -0
- package/tests/operations/tests/Base45.mjs +103 -0
- package/tests/operations/tests/SIGABA.mjs +91 -0
|
@@ -10,6 +10,7 @@ import Colossus from "../../operations/Colossus.mjs";
|
|
|
10
10
|
import Enigma from "../../operations/Enigma.mjs";
|
|
11
11
|
import Lorenz from "../../operations/Lorenz.mjs";
|
|
12
12
|
import MultipleBombe from "../../operations/MultipleBombe.mjs";
|
|
13
|
+
import SIGABA from "../../operations/SIGABA.mjs";
|
|
13
14
|
import Typex from "../../operations/Typex.mjs";
|
|
14
15
|
|
|
15
16
|
const OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
|
|
@@ -20,6 +21,7 @@ OpModules.Bletchley = {
|
|
|
20
21
|
"Enigma": Enigma,
|
|
21
22
|
"Lorenz": Lorenz,
|
|
22
23
|
"Multiple Bombe": MultipleBombe,
|
|
24
|
+
"SIGABA": SIGABA,
|
|
23
25
|
"Typex": Typex,
|
|
24
26
|
};
|
|
25
27
|
|
|
@@ -45,6 +45,7 @@ import FrequencyDistribution from "../../operations/FrequencyDistribution.mjs";
|
|
|
45
45
|
import FromBCD from "../../operations/FromBCD.mjs";
|
|
46
46
|
import FromBase from "../../operations/FromBase.mjs";
|
|
47
47
|
import FromBase32 from "../../operations/FromBase32.mjs";
|
|
48
|
+
import FromBase45 from "../../operations/FromBase45.mjs";
|
|
48
49
|
import FromBase58 from "../../operations/FromBase58.mjs";
|
|
49
50
|
import FromBase62 from "../../operations/FromBase62.mjs";
|
|
50
51
|
import FromBase64 from "../../operations/FromBase64.mjs";
|
|
@@ -134,6 +135,7 @@ import TakeBytes from "../../operations/TakeBytes.mjs";
|
|
|
134
135
|
import ToBCD from "../../operations/ToBCD.mjs";
|
|
135
136
|
import ToBase from "../../operations/ToBase.mjs";
|
|
136
137
|
import ToBase32 from "../../operations/ToBase32.mjs";
|
|
138
|
+
import ToBase45 from "../../operations/ToBase45.mjs";
|
|
137
139
|
import ToBase58 from "../../operations/ToBase58.mjs";
|
|
138
140
|
import ToBase62 from "../../operations/ToBase62.mjs";
|
|
139
141
|
import ToBase64 from "../../operations/ToBase64.mjs";
|
|
@@ -209,6 +211,7 @@ OpModules.Default = {
|
|
|
209
211
|
"From BCD": FromBCD,
|
|
210
212
|
"From Base": FromBase,
|
|
211
213
|
"From Base32": FromBase32,
|
|
214
|
+
"From Base45": FromBase45,
|
|
212
215
|
"From Base58": FromBase58,
|
|
213
216
|
"From Base62": FromBase62,
|
|
214
217
|
"From Base64": FromBase64,
|
|
@@ -298,6 +301,7 @@ OpModules.Default = {
|
|
|
298
301
|
"To BCD": ToBCD,
|
|
299
302
|
"To Base": ToBase,
|
|
300
303
|
"To Base32": ToBase32,
|
|
304
|
+
"To Base45": ToBase45,
|
|
301
305
|
"To Base58": ToBase58,
|
|
302
306
|
"To Base62": ToBase62,
|
|
303
307
|
"To Base64": ToBase64,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base45 resources.
|
|
3
|
+
*
|
|
4
|
+
* @author Thomas Weißschuh [thomas@t-8ch.de]
|
|
5
|
+
* @copyright Crown Copyright 2021
|
|
6
|
+
* @license Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Highlight to Base45
|
|
11
|
+
*/
|
|
12
|
+
export function highlightToBase45(pos, args) {
|
|
13
|
+
pos[0].start = Math.floor(pos[0].start / 2) * 3;
|
|
14
|
+
pos[0].end = Math.ceil(pos[0].end / 2) * 3;
|
|
15
|
+
return pos;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Highlight from Base45
|
|
20
|
+
*/
|
|
21
|
+
export function highlightFromBase45(pos, args) {
|
|
22
|
+
pos[0].start = Math.floor(pos[0].start / 3) * 2;
|
|
23
|
+
pos[0].end = Math.ceil(pos[0].end / 3) * 2;
|
|
24
|
+
return pos;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const ALPHABET = "0-9A-Z $%*+\\-./:";
|
package/src/core/lib/Charts.mjs
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import OperationError from "../errors/OperationError.mjs";
|
|
9
|
+
import Utils from "../Utils.mjs";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @constant
|
|
@@ -128,7 +129,7 @@ export function getScatterValuesWithColour(input, recordDelimiter, fieldDelimite
|
|
|
128
129
|
if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
|
|
129
130
|
if (Number.isNaN(y)) throw new OperationError("Values must be numbers in base 10.");
|
|
130
131
|
|
|
131
|
-
return [x, y, colour];
|
|
132
|
+
return [x, y, Utils.escapeHtml(colour)];
|
|
132
133
|
});
|
|
133
134
|
|
|
134
135
|
return { headings, values };
|
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emulation of the SIGABA machine
|
|
3
|
+
*
|
|
4
|
+
* @author hettysymes
|
|
5
|
+
* @copyright hettysymes 2020
|
|
6
|
+
* @license Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 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.
|
|
11
|
+
*/
|
|
12
|
+
export const CR_ROTORS = [
|
|
13
|
+
{name: "Example 1", value: "SRGWANHPJZFXVIDQCEUKBYOLMT"},
|
|
14
|
+
{name: "Example 2", value: "THQEFSAZVKJYULBODCPXNIMWRG"},
|
|
15
|
+
{name: "Example 3", value: "XDTUYLEVFNQZBPOGIRCSMHWKAJ"},
|
|
16
|
+
{name: "Example 4", value: "LOHDMCWUPSTNGVXYFJREQIKBZA"},
|
|
17
|
+
{name: "Example 5", value: "ERXWNZQIJYLVOFUMSGHTCKPBDA"},
|
|
18
|
+
{name: "Example 6", value: "FQECYHJIOUMDZVPSLKRTGWXBAN"},
|
|
19
|
+
{name: "Example 7", value: "TBYIUMKZDJSOPEWXVANHLCFQGR"},
|
|
20
|
+
{name: "Example 8", value: "QZUPDTFNYIAOMLEBWJXCGHKRSV"},
|
|
21
|
+
{name: "Example 9", value: "CZWNHEMPOVXLKRSIDGJFYBTQAU"},
|
|
22
|
+
{name: "Example 10", value: "ENPXJVKYQBFZTICAGMOHWRLDUS"}
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A set of randomised example SIGABA index rotors (may be referred to as I rotors).
|
|
27
|
+
*/
|
|
28
|
+
export const I_ROTORS = [
|
|
29
|
+
{name: "Example 1", value: "6201348957"},
|
|
30
|
+
{name: "Example 2", value: "6147253089"},
|
|
31
|
+
{name: "Example 3", value: "8239647510"},
|
|
32
|
+
{name: "Example 4", value: "7194835260"},
|
|
33
|
+
{name: "Example 5", value: "4873205916"}
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export const NUMBERS = "0123456789".split("");
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Converts a letter to uppercase (if it already isn't)
|
|
40
|
+
*
|
|
41
|
+
* @param {char} letter - letter to convert to uppercase
|
|
42
|
+
* @returns {char}
|
|
43
|
+
*/
|
|
44
|
+
export function convToUpperCase(letter) {
|
|
45
|
+
const charCode = letter.charCodeAt();
|
|
46
|
+
if (97<=charCode && charCode<=122) {
|
|
47
|
+
return String.fromCharCode(charCode-32);
|
|
48
|
+
}
|
|
49
|
+
return letter;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The SIGABA machine consisting of the 3 rotor banks: cipher, control and index banks.
|
|
54
|
+
*/
|
|
55
|
+
export class SigabaMachine {
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* SigabaMachine constructor
|
|
59
|
+
*
|
|
60
|
+
* @param {Object[]} cipherRotors - list of CRRotors
|
|
61
|
+
* @param {Object[]} controlRotors - list of CRRotors
|
|
62
|
+
* @param {object[]} indexRotors - list of IRotors
|
|
63
|
+
*/
|
|
64
|
+
constructor(cipherRotors, controlRotors, indexRotors) {
|
|
65
|
+
this.cipherBank = new CipherBank(cipherRotors);
|
|
66
|
+
this.controlBank = new ControlBank(controlRotors);
|
|
67
|
+
this.indexBank = new IndexBank(indexRotors);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Steps all the correct rotors in the machine.
|
|
72
|
+
*/
|
|
73
|
+
step() {
|
|
74
|
+
const controlOut = this.controlBank.goThroughControl();
|
|
75
|
+
const indexOut = this.indexBank.goThroughIndex(controlOut);
|
|
76
|
+
this.cipherBank.step(indexOut);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 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.
|
|
81
|
+
*
|
|
82
|
+
* @param {char} letter - letter to encrypt
|
|
83
|
+
* @returns {char}
|
|
84
|
+
*/
|
|
85
|
+
encryptLetter(letter) {
|
|
86
|
+
letter = convToUpperCase(letter);
|
|
87
|
+
if (letter === " ") {
|
|
88
|
+
letter = "Z";
|
|
89
|
+
} else if (letter === "Z") {
|
|
90
|
+
letter = "X";
|
|
91
|
+
}
|
|
92
|
+
const encryptedLetter = this.cipherBank.encrypt(letter);
|
|
93
|
+
this.step();
|
|
94
|
+
return encryptedLetter;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 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.
|
|
99
|
+
*
|
|
100
|
+
* @param {char} letter - letter to decrypt
|
|
101
|
+
* @returns {char}
|
|
102
|
+
*/
|
|
103
|
+
decryptLetter(letter) {
|
|
104
|
+
letter = convToUpperCase(letter);
|
|
105
|
+
let decryptedLetter = this.cipherBank.decrypt(letter);
|
|
106
|
+
if (decryptedLetter === "Z") {
|
|
107
|
+
decryptedLetter = " ";
|
|
108
|
+
}
|
|
109
|
+
this.step();
|
|
110
|
+
return decryptedLetter;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Encrypts a message of one or more letters
|
|
115
|
+
*
|
|
116
|
+
* @param {string} msg - message to encrypt
|
|
117
|
+
* @returns {string}
|
|
118
|
+
*/
|
|
119
|
+
encrypt(msg) {
|
|
120
|
+
let ciphertext = "";
|
|
121
|
+
for (const letter of msg) {
|
|
122
|
+
ciphertext = ciphertext.concat(this.encryptLetter(letter));
|
|
123
|
+
}
|
|
124
|
+
return ciphertext;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Decrypts a message of one or more letters
|
|
129
|
+
*
|
|
130
|
+
* @param {string} msg - message to decrypt
|
|
131
|
+
* @returns {string}
|
|
132
|
+
*/
|
|
133
|
+
decrypt(msg) {
|
|
134
|
+
let plaintext = "";
|
|
135
|
+
for (const letter of msg) {
|
|
136
|
+
plaintext = plaintext.concat(this.decryptLetter(letter));
|
|
137
|
+
}
|
|
138
|
+
return plaintext;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The cipher rotor bank consists of 5 cipher rotors in either a forward or reversed orientation.
|
|
145
|
+
*/
|
|
146
|
+
export class CipherBank {
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* CipherBank constructor
|
|
150
|
+
*
|
|
151
|
+
* @param {Object[]} rotors - list of CRRotors
|
|
152
|
+
*/
|
|
153
|
+
constructor(rotors) {
|
|
154
|
+
this.rotors = rotors;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Encrypts a letter through the cipher rotors (signal goes from left-to-right)
|
|
159
|
+
*
|
|
160
|
+
* @param {char} inputPos - the input position of the signal (letter to be encrypted)
|
|
161
|
+
* @returns {char}
|
|
162
|
+
*/
|
|
163
|
+
encrypt(inputPos) {
|
|
164
|
+
for (const rotor of this.rotors) {
|
|
165
|
+
inputPos = rotor.crypt(inputPos, "leftToRight");
|
|
166
|
+
}
|
|
167
|
+
return inputPos;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Decrypts a letter through the cipher rotors (signal goes from right-to-left)
|
|
172
|
+
*
|
|
173
|
+
* @param {char} inputPos - the input position of the signal (letter to be decrypted)
|
|
174
|
+
* @returns {char}
|
|
175
|
+
*/
|
|
176
|
+
decrypt(inputPos) {
|
|
177
|
+
const revOrderedRotors = [...this.rotors].reverse();
|
|
178
|
+
for (const rotor of revOrderedRotors) {
|
|
179
|
+
inputPos = rotor.crypt(inputPos, "rightToLeft");
|
|
180
|
+
}
|
|
181
|
+
return inputPos;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Step the cipher rotors forward according to the inputs from the index rotors
|
|
186
|
+
*
|
|
187
|
+
* @param {number[]} indexInputs - the inputs from the index rotors
|
|
188
|
+
*/
|
|
189
|
+
step(indexInputs) {
|
|
190
|
+
const logicDict = {0: [0, 9], 1: [7, 8], 2: [5, 6], 3: [3, 4], 4: [1, 2]};
|
|
191
|
+
const rotorsToMove = [];
|
|
192
|
+
for (const key in logicDict) {
|
|
193
|
+
const item = logicDict[key];
|
|
194
|
+
for (const i of indexInputs) {
|
|
195
|
+
if (item.includes(i)) {
|
|
196
|
+
rotorsToMove.push(this.rotors[key]);
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
for (const rotor of rotorsToMove) {
|
|
202
|
+
rotor.step();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 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.
|
|
210
|
+
*/
|
|
211
|
+
export class ControlBank {
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* ControlBank constructor. The rotors have been reversed as signals go from right-to-left through the control rotors.
|
|
215
|
+
*
|
|
216
|
+
* @param {Object[]} rotors - list of CRRotors
|
|
217
|
+
*/
|
|
218
|
+
constructor(rotors) {
|
|
219
|
+
this.rotors = [...rotors].reverse();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Encrypts a letter.
|
|
224
|
+
*
|
|
225
|
+
* @param {char} inputPos - the input position of the signal
|
|
226
|
+
* @returns {char}
|
|
227
|
+
*/
|
|
228
|
+
crypt(inputPos) {
|
|
229
|
+
for (const rotor of this.rotors) {
|
|
230
|
+
inputPos = rotor.crypt(inputPos, "rightToLeft");
|
|
231
|
+
}
|
|
232
|
+
return inputPos;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Gets the outputs of the control rotors. The inputs to the control rotors are always "F", "G", "H" and "I".
|
|
237
|
+
*
|
|
238
|
+
* @returns {number[]}
|
|
239
|
+
*/
|
|
240
|
+
getOutputs() {
|
|
241
|
+
const outputs = [this.crypt("F"), this.crypt("G"), this.crypt("H"), this.crypt("I")];
|
|
242
|
+
const logicDict = {1: "B", 2: "C", 3: "DE", 4: "FGH", 5: "IJK", 6: "LMNO", 7: "PQRST", 8: "UVWXYZ", 9: "A"};
|
|
243
|
+
const numberOutputs = [];
|
|
244
|
+
for (const key in logicDict) {
|
|
245
|
+
const item = logicDict[key];
|
|
246
|
+
for (const output of outputs) {
|
|
247
|
+
if (item.includes(output)) {
|
|
248
|
+
numberOutputs.push(key);
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return numberOutputs;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 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.
|
|
258
|
+
*/
|
|
259
|
+
step() {
|
|
260
|
+
const MRotor = this.rotors[1], FRotor = this.rotors[2], SRotor = this.rotors[3];
|
|
261
|
+
// 14 is the offset of "O" from "A" - the next rotor steps once the previous rotor reaches "O"
|
|
262
|
+
if (FRotor.state === 14) {
|
|
263
|
+
if (MRotor.state === 14) {
|
|
264
|
+
SRotor.step();
|
|
265
|
+
}
|
|
266
|
+
MRotor.step();
|
|
267
|
+
}
|
|
268
|
+
FRotor.step();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* The goThroughControl function combines getting the outputs from the control rotor bank and then stepping them.
|
|
273
|
+
*
|
|
274
|
+
* @returns {number[]}
|
|
275
|
+
*/
|
|
276
|
+
goThroughControl() {
|
|
277
|
+
const outputs = this.getOutputs();
|
|
278
|
+
this.step();
|
|
279
|
+
return outputs;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* The index rotor bank consists of 5 index rotors all placed in the forwards orientation.
|
|
286
|
+
*/
|
|
287
|
+
export class IndexBank {
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* IndexBank constructor
|
|
291
|
+
*
|
|
292
|
+
* @param {Object[]} rotors - list of IRotors
|
|
293
|
+
*/
|
|
294
|
+
constructor(rotors) {
|
|
295
|
+
this.rotors = rotors;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Encrypts a number.
|
|
300
|
+
*
|
|
301
|
+
* @param {number} inputPos - the input position of the signal
|
|
302
|
+
* @returns {number}
|
|
303
|
+
*/
|
|
304
|
+
crypt(inputPos) {
|
|
305
|
+
for (const rotor of this.rotors) {
|
|
306
|
+
inputPos = rotor.crypt(inputPos);
|
|
307
|
+
}
|
|
308
|
+
return inputPos;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* The goThroughIndex function takes the inputs from the control rotor bank and returns the list of outputs after encryption through the index rotors.
|
|
313
|
+
*
|
|
314
|
+
* @param {number[]} controlInputs - inputs from the control rotors
|
|
315
|
+
* @returns {number[]}
|
|
316
|
+
*/
|
|
317
|
+
goThroughIndex(controlInputs) {
|
|
318
|
+
const outputs = [];
|
|
319
|
+
for (const inp of controlInputs) {
|
|
320
|
+
outputs.push(this.crypt(inp));
|
|
321
|
+
}
|
|
322
|
+
return outputs;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Rotor class
|
|
329
|
+
*/
|
|
330
|
+
export class Rotor {
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Rotor constructor
|
|
334
|
+
*
|
|
335
|
+
* @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
|
|
336
|
+
* @param {bool} rev - true if the rotor is reversed, false if it isn't
|
|
337
|
+
* @param {number} key - the starting position or state of the rotor
|
|
338
|
+
*/
|
|
339
|
+
constructor(wireSetting, key, rev) {
|
|
340
|
+
this.state = key;
|
|
341
|
+
this.numMapping = this.getNumMapping(wireSetting, rev);
|
|
342
|
+
this.posMapping = this.getPosMapping(rev);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Get the number mapping from the wireSetting (only different from wireSetting if rotor is reversed)
|
|
347
|
+
*
|
|
348
|
+
* @param {number[]} wireSetting - the wirings within the rotors
|
|
349
|
+
* @param {bool} rev - true if reversed, false if not
|
|
350
|
+
* @returns {number[]}
|
|
351
|
+
*/
|
|
352
|
+
getNumMapping(wireSetting, rev) {
|
|
353
|
+
if (rev===false) {
|
|
354
|
+
return wireSetting;
|
|
355
|
+
} else {
|
|
356
|
+
const length = wireSetting.length;
|
|
357
|
+
const tempMapping = new Array(length);
|
|
358
|
+
for (let i=0; i<length; i++) {
|
|
359
|
+
tempMapping[wireSetting[i]] = i;
|
|
360
|
+
}
|
|
361
|
+
return tempMapping;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Get the position mapping (how the position numbers map onto the numbers of the rotor)
|
|
367
|
+
*
|
|
368
|
+
* @param {bool} rev - true if reversed, false if not
|
|
369
|
+
* @returns {number[]}
|
|
370
|
+
*/
|
|
371
|
+
getPosMapping(rev) {
|
|
372
|
+
const length = this.numMapping.length;
|
|
373
|
+
const posMapping = [];
|
|
374
|
+
if (rev===false) {
|
|
375
|
+
for (let i = this.state; i < this.state+length; i++) {
|
|
376
|
+
let res = i%length;
|
|
377
|
+
if (res<0) {
|
|
378
|
+
res += length;
|
|
379
|
+
}
|
|
380
|
+
posMapping.push(res);
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
for (let i = this.state; i > this.state-length; i--) {
|
|
384
|
+
let res = i%length;
|
|
385
|
+
if (res<0) {
|
|
386
|
+
res += length;
|
|
387
|
+
}
|
|
388
|
+
posMapping.push(res);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return posMapping;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Encrypt/decrypt data. This process is identical to the rotors of cipher machines such as Enigma or Typex.
|
|
396
|
+
*
|
|
397
|
+
* @param {number} inputPos - the input position of the signal (the data to encrypt/decrypt)
|
|
398
|
+
* @param {string} direction - one of "leftToRight" and "rightToLeft", states the direction in which the signal passes through the rotor
|
|
399
|
+
* @returns {number}
|
|
400
|
+
*/
|
|
401
|
+
cryptNum(inputPos, direction) {
|
|
402
|
+
const inpNum = this.posMapping[inputPos];
|
|
403
|
+
let outNum;
|
|
404
|
+
if (direction === "leftToRight") {
|
|
405
|
+
outNum = this.numMapping[inpNum];
|
|
406
|
+
} else if (direction === "rightToLeft") {
|
|
407
|
+
outNum = this.numMapping.indexOf(inpNum);
|
|
408
|
+
}
|
|
409
|
+
const outPos = this.posMapping.indexOf(outNum);
|
|
410
|
+
return outPos;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Steps the rotor. The number at position 0 will be moved to position 1 etc.
|
|
415
|
+
*/
|
|
416
|
+
step() {
|
|
417
|
+
const lastNum = this.posMapping.pop();
|
|
418
|
+
this.posMapping.splice(0, 0, lastNum);
|
|
419
|
+
this.state = this.posMapping[0];
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* 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.
|
|
426
|
+
*/
|
|
427
|
+
export class CRRotor extends Rotor {
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* CRRotor constructor
|
|
431
|
+
*
|
|
432
|
+
* @param {string} wireSetting - the rotor wirings (string of letters)
|
|
433
|
+
* @param {char} key - initial state of rotor
|
|
434
|
+
* @param {bool} rev - true if reversed, false if not
|
|
435
|
+
*/
|
|
436
|
+
constructor(wireSetting, key, rev=false) {
|
|
437
|
+
wireSetting = wireSetting.split("").map(CRRotor.letterToNum);
|
|
438
|
+
super(wireSetting, CRRotor.letterToNum(key), rev);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Static function which converts a letter into its number i.e. its offset from the letter "A"
|
|
443
|
+
*
|
|
444
|
+
* @param {char} letter - letter to convert to number
|
|
445
|
+
* @returns {number}
|
|
446
|
+
*/
|
|
447
|
+
static letterToNum(letter) {
|
|
448
|
+
return letter.charCodeAt()-65;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Static function which converts a number (a letter's offset from "A") into its letter
|
|
453
|
+
*
|
|
454
|
+
* @param {number} num - number to convert to letter
|
|
455
|
+
* @returns {char}
|
|
456
|
+
*/
|
|
457
|
+
static numToLetter(num) {
|
|
458
|
+
return String.fromCharCode(num+65);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Encrypts/decrypts a letter.
|
|
463
|
+
*
|
|
464
|
+
* @param {char} inputPos - the input position of the signal ("A" refers to position 0 etc.)
|
|
465
|
+
* @param {string} direction - one of "leftToRight" and "rightToLeft"
|
|
466
|
+
* @returns {char}
|
|
467
|
+
*/
|
|
468
|
+
crypt(inputPos, direction) {
|
|
469
|
+
inputPos = CRRotor.letterToNum(inputPos);
|
|
470
|
+
const outPos = this.cryptNum(inputPos, direction);
|
|
471
|
+
return CRRotor.numToLetter(outPos);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* 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.
|
|
478
|
+
*/
|
|
479
|
+
export class IRotor extends Rotor {
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* IRotor constructor
|
|
483
|
+
*
|
|
484
|
+
* @param {string} wireSetting - the rotor wirings (string of numbers)
|
|
485
|
+
* @param {char} key - initial state of rotor
|
|
486
|
+
*/
|
|
487
|
+
constructor(wireSetting, key) {
|
|
488
|
+
wireSetting = wireSetting.split("").map(Number);
|
|
489
|
+
super(wireSetting, Number(key), false);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Encrypts a number
|
|
494
|
+
*
|
|
495
|
+
* @param {number} inputPos - the input position of the signal
|
|
496
|
+
* @returns {number}
|
|
497
|
+
*/
|
|
498
|
+
crypt(inputPos) {
|
|
499
|
+
return this.cryptNum(inputPos, "leftToRight");
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Emulation of Colossus.
|
|
3
3
|
*
|
|
4
|
+
* Tested against the Colossus Rebuild at Bletchley Park's TNMOC
|
|
5
|
+
* using a variety of inputs and settings to confirm correctness.
|
|
6
|
+
*
|
|
4
7
|
* @author VirtualColossus [martin@virtualcolossus.co.uk]
|
|
5
8
|
* @copyright Crown Copyright 2019
|
|
6
9
|
* @license Apache-2.0
|
|
@@ -67,7 +67,7 @@ class DeriveEVPKey extends Operation {
|
|
|
67
67
|
iterations = args[2],
|
|
68
68
|
hasher = args[3],
|
|
69
69
|
salt = Utils.convertToByteString(args[4].string, args[4].option),
|
|
70
|
-
key = CryptoJS.EvpKDF(passphrase, salt, {
|
|
70
|
+
key = CryptoJS.EvpKDF(passphrase, salt, { // lgtm [js/insufficient-password-hash]
|
|
71
71
|
keySize: keySize,
|
|
72
72
|
hasher: CryptoJS.algo[hasher],
|
|
73
73
|
iterations: iterations,
|