cyberchef 9.41.0 → 9.42.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 CHANGED
@@ -13,6 +13,9 @@ All major and minor version changes will be documented in this file. Details of
13
13
 
14
14
  ## Details
15
15
 
16
+ ### [9.41.0] - 2022-07-08
17
+ - Added 'Caesar Box Cipher' operation [@n1073645] | [#1066]
18
+
16
19
  ### [9.40.0] - 2022-07-08
17
20
  - Added 'P-list Viewer' operation [@n1073645] | [#906]
18
21
 
@@ -297,6 +300,7 @@ All major and minor version changes will be documented in this file. Details of
297
300
 
298
301
 
299
302
 
303
+ [9.41.0]: https://github.com/gchq/CyberChef/releases/tag/v9.41.0
300
304
  [9.40.0]: https://github.com/gchq/CyberChef/releases/tag/v9.40.0
301
305
  [9.39.0]: https://github.com/gchq/CyberChef/releases/tag/v9.39.0
302
306
  [9.38.0]: https://github.com/gchq/CyberChef/releases/tag/v9.38.0
@@ -511,6 +515,7 @@ All major and minor version changes will be documented in this file. Details of
511
515
  [#1045]: https://github.com/gchq/CyberChef/pull/1045
512
516
  [#1049]: https://github.com/gchq/CyberChef/pull/1049
513
517
  [#1065]: https://github.com/gchq/CyberChef/pull/1065
518
+ [#1066]: https://github.com/gchq/CyberChef/pull/1066
514
519
  [#1083]: https://github.com/gchq/CyberChef/pull/1083
515
520
  [#1189]: https://github.com/gchq/CyberChef/pull/1189
516
521
  [#1242]: https://github.com/gchq/CyberChef/pull/1242
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyberchef",
3
- "version": "9.41.0",
3
+ "version": "9.42.0",
4
4
  "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
5
5
  "author": "n1474335 <n1474335@gmail.com>",
6
6
  "homepage": "https://gchq.github.io/CyberChef",
@@ -79,6 +79,8 @@
79
79
  "DES Decrypt",
80
80
  "Triple DES Encrypt",
81
81
  "Triple DES Decrypt",
82
+ "LS47 Encrypt",
83
+ "LS47 Decrypt",
82
84
  "RC2 Encrypt",
83
85
  "RC2 Decrypt",
84
86
  "RC4",
@@ -7934,6 +7934,53 @@
7934
7934
  }
7935
7935
  ]
7936
7936
  },
7937
+ "LS47 Decrypt": {
7938
+ "module": "Crypto",
7939
+ "description": "This is a slight improvement of the ElsieFour cipher as described by Alan Kaminsky. We use 7x7 characters instead of original (barely fitting) 6x6, to be able to encrypt some structured information. We also describe a simple key-expansion algorithm, because remembering passwords is popular. Similar security considerations as with ElsieFour hold.<br>The LS47 alphabet consists of following characters: <code>_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()</code><br>An LS47 key is a permutation of the alphabet that is then represented in a 7x7 grid used for the encryption or decryption.",
7940
+ "infoURL": "https://github.com/exaexa/ls47",
7941
+ "inputType": "string",
7942
+ "outputType": "string",
7943
+ "flowControl": false,
7944
+ "manualBake": false,
7945
+ "args": [
7946
+ {
7947
+ "name": "Password",
7948
+ "type": "string",
7949
+ "value": ""
7950
+ },
7951
+ {
7952
+ "name": "Padding",
7953
+ "type": "number",
7954
+ "value": 10
7955
+ }
7956
+ ]
7957
+ },
7958
+ "LS47 Encrypt": {
7959
+ "module": "Crypto",
7960
+ "description": "This is a slight improvement of the ElsieFour cipher as described by Alan Kaminsky. We use 7x7 characters instead of original (barely fitting) 6x6, to be able to encrypt some structured information. We also describe a simple key-expansion algorithm, because remembering passwords is popular. Similar security considerations as with ElsieFour hold.<br>The LS47 alphabet consists of following characters: <code>_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()</code><br>A LS47 key is a permutation of the alphabet that is then represented in a 7x7 grid used for the encryption or decryption.",
7961
+ "infoURL": "https://github.com/exaexa/ls47",
7962
+ "inputType": "string",
7963
+ "outputType": "string",
7964
+ "flowControl": false,
7965
+ "manualBake": false,
7966
+ "args": [
7967
+ {
7968
+ "name": "Password",
7969
+ "type": "string",
7970
+ "value": ""
7971
+ },
7972
+ {
7973
+ "name": "Padding",
7974
+ "type": "number",
7975
+ "value": 10
7976
+ },
7977
+ {
7978
+ "name": "Signature",
7979
+ "type": "string",
7980
+ "value": ""
7981
+ }
7982
+ ]
7983
+ },
7937
7984
  "Label": {
7938
7985
  "module": "Default",
7939
7986
  "description": "Provides a location for conditional and fixed jumps to redirect execution to.",
@@ -34,6 +34,8 @@ import JWTDecode from "../../operations/JWTDecode.mjs";
34
34
  import JWTSign from "../../operations/JWTSign.mjs";
35
35
  import JWTVerify from "../../operations/JWTVerify.mjs";
36
36
  import Keccak from "../../operations/Keccak.mjs";
37
+ import LS47Decrypt from "../../operations/LS47Decrypt.mjs";
38
+ import LS47Encrypt from "../../operations/LS47Encrypt.mjs";
37
39
  import MD2 from "../../operations/MD2.mjs";
38
40
  import MD4 from "../../operations/MD4.mjs";
39
41
  import MD5 from "../../operations/MD5.mjs";
@@ -83,6 +85,8 @@ OpModules.Crypto = {
83
85
  "JWT Sign": JWTSign,
84
86
  "JWT Verify": JWTVerify,
85
87
  "Keccak": Keccak,
88
+ "LS47 Decrypt": LS47Decrypt,
89
+ "LS47 Encrypt": LS47Encrypt,
86
90
  "MD2": MD2,
87
91
  "MD4": MD4,
88
92
  "MD5": MD5,
@@ -0,0 +1,244 @@
1
+ /**
2
+ * @author n1073645 [n1073645@gmail.com]
3
+ * @copyright Crown Copyright 2020
4
+ * @license Apache-2.0
5
+ */
6
+
7
+ import OperationError from "../errors/OperationError.mjs";
8
+
9
+ const letters = "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()";
10
+ const tiles = [];
11
+
12
+ /**
13
+ * Initialises the tiles with values and positions.
14
+ */
15
+ export function initTiles() {
16
+ for (let i = 0; i < 49; i++)
17
+ tiles.push([letters.charAt(i), [Math.floor(i/7), i % 7]]);
18
+ }
19
+
20
+ /**
21
+ * Rotates the key "down".
22
+ *
23
+ * @param {string} key
24
+ * @param {number} col
25
+ * @param {number} n
26
+ * @returns {string}
27
+ */
28
+ function rotateDown(key, col, n) {
29
+ const lines = [];
30
+ for (let i = 0; i < 7; i++)
31
+ lines.push(key.slice(i*7, (i + 1) * 7));
32
+ const lefts = [];
33
+ let mids = [];
34
+ const rights = [];
35
+ lines.forEach((element) => {
36
+ lefts.push(element.slice(0, col));
37
+ mids.push(element.charAt(col));
38
+ rights.push(element.slice(col+1));
39
+ });
40
+ n = (7 - n % 7) % 7;
41
+ mids = mids.slice(n).concat(mids.slice(0, n));
42
+ let result = "";
43
+ for (let i = 0; i < 7; i++)
44
+ result += lefts[i] + mids[i] + rights[i];
45
+ return result;
46
+ }
47
+
48
+ /**
49
+ * Rotates the key "right".
50
+ *
51
+ * @param {string} key
52
+ * @param {number} row
53
+ * @param {number} n
54
+ * @returns {string}
55
+ */
56
+ function rotateRight(key, row, n) {
57
+ const mid = key.slice(row * 7, (row + 1) * 7);
58
+ n = (7 - n % 7) % 7;
59
+ return key.slice(0, 7 * row) + mid.slice(n) + mid.slice(0, n) + key.slice(7 * (row + 1));
60
+ }
61
+
62
+ /**
63
+ * Finds the position of a letter in the tiles.
64
+ *
65
+ * @param {string} letter
66
+ * @returns {string}
67
+ */
68
+ function findIx(letter) {
69
+ for (let i = 0; i < tiles.length; i++)
70
+ if (tiles[i][0] === letter)
71
+ return tiles[i][1];
72
+ throw new OperationError("Letter " + letter + " is not included in LS47");
73
+ }
74
+
75
+ /**
76
+ * Derives key from the input password.
77
+ *
78
+ * @param {string} password
79
+ * @returns {string}
80
+ */
81
+ export function deriveKey(password) {
82
+ let i = 0;
83
+ let k = letters;
84
+ for (const c of password) {
85
+ const [row, col] = findIx(c);
86
+ k = rotateDown(rotateRight(k, i, col), i, row);
87
+ i = (i + 1) % 7;
88
+ }
89
+ return k;
90
+ }
91
+
92
+ /**
93
+ * Checks the key is a valid key.
94
+ *
95
+ * @param {string} key
96
+ */
97
+ function checkKey(key) {
98
+ if (key.length !== letters.length)
99
+ throw new OperationError("Wrong key size");
100
+ const counts = new Array();
101
+ for (let i = 0; i < letters.length; i++)
102
+ counts[letters.charAt(i)] = 0;
103
+ for (const elem of letters) {
104
+ if (letters.indexOf(elem) === -1)
105
+ throw new OperationError("Letter " + elem + " not in LS47");
106
+ counts[elem]++;
107
+ if (counts[elem] > 1)
108
+ throw new OperationError("Letter duplicated in the key");
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Finds the position of a letter in they key.
114
+ *
115
+ * @param {letter} key
116
+ * @param {string} letter
117
+ * @returns {object}
118
+ */
119
+ function findPos (key, letter) {
120
+ const index = key.indexOf(letter);
121
+ if (index >= 0 && index < 49)
122
+ return [Math.floor(index/7), index%7];
123
+ throw new OperationError("Letter " + letter + " is not in the key");
124
+ }
125
+
126
+ /**
127
+ * Returns the character at the position on the tiles.
128
+ *
129
+ * @param {string} key
130
+ * @param {object} coord
131
+ * @returns {string}
132
+ */
133
+ function findAtPos(key, coord) {
134
+ return key.charAt(coord[1] + (coord[0] * 7));
135
+ }
136
+
137
+ /**
138
+ * Returns new position by adding two positions.
139
+ *
140
+ * @param {object} a
141
+ * @param {object} b
142
+ * @returns {object}
143
+ */
144
+ function addPos(a, b) {
145
+ return [(a[0] + b[0]) % 7, (a[1] + b[1]) % 7];
146
+ }
147
+
148
+ /**
149
+ * Returns new position by subtracting two positions.
150
+ * Note: We have to manually do the remainder division, since JS does not
151
+ * operate correctly on negative numbers (e.g. -3 % 4 = -3 when it should be 1).
152
+ *
153
+ * @param {object} a
154
+ * @param {object} b
155
+ * @returns {object}
156
+ */
157
+ function subPos(a, b) {
158
+ const asub = a[0] - b[0];
159
+ const bsub = a[1] - b[1];
160
+ return [asub - (Math.floor(asub/7) * 7), bsub - (Math.floor(bsub/7) * 7)];
161
+ }
162
+
163
+ /**
164
+ * Encrypts the plaintext string.
165
+ *
166
+ * @param {string} key
167
+ * @param {string} plaintext
168
+ * @returns {string}
169
+ */
170
+ function encrypt(key, plaintext) {
171
+ checkKey(key);
172
+ let mp = [0, 0];
173
+ let ciphertext = "";
174
+ for (const p of plaintext) {
175
+ const pp = findPos(key, p);
176
+ const mix = findIx(findAtPos(key, mp));
177
+ let cp = addPos(pp, mix);
178
+ const c = findAtPos(key, cp);
179
+ ciphertext += c;
180
+ key = rotateRight(key, pp[0], 1);
181
+ cp = findPos(key, c);
182
+ key = rotateDown(key, cp[1], 1);
183
+ mp = addPos(mp, findIx(c));
184
+ }
185
+ return ciphertext;
186
+ }
187
+
188
+ /**
189
+ * Decrypts the ciphertext string.
190
+ *
191
+ * @param {string} key
192
+ * @param {string} ciphertext
193
+ * @returns {string}
194
+ */
195
+ function decrypt(key, ciphertext) {
196
+ checkKey(key);
197
+ let mp = [0, 0];
198
+ let plaintext = "";
199
+ for (const c of ciphertext) {
200
+ let cp = findPos(key, c);
201
+ const mix = findIx(findAtPos(key, mp));
202
+ const pp = subPos(cp, mix);
203
+ const p = findAtPos(key, pp);
204
+ plaintext += p;
205
+ key = rotateRight(key, pp[0], 1);
206
+ cp = findPos(key, c);
207
+ key = rotateDown(key, cp[1], 1);
208
+ mp = addPos(mp, findIx(c));
209
+ }
210
+ return plaintext;
211
+ }
212
+
213
+ /**
214
+ * Adds padding to the input.
215
+ *
216
+ * @param {string} key
217
+ * @param {string} plaintext
218
+ * @param {string} signature
219
+ * @param {number} paddingSize
220
+ * @returns {string}
221
+ */
222
+ export function encryptPad(key, plaintext, signature, paddingSize) {
223
+ initTiles();
224
+ checkKey(key);
225
+ let padding = "";
226
+ for (let i = 0; i < paddingSize; i++) {
227
+ padding += letters.charAt(Math.floor(Math.random() * letters.length));
228
+ }
229
+ return encrypt(key, padding+plaintext+"---"+signature);
230
+ }
231
+
232
+ /**
233
+ * Removes padding from the ouput.
234
+ *
235
+ * @param {string} key
236
+ * @param {string} ciphertext
237
+ * @param {number} paddingSize
238
+ * @returns {string}
239
+ */
240
+ export function decryptPad(key, ciphertext, paddingSize) {
241
+ initTiles();
242
+ checkKey(key);
243
+ return decrypt(key, ciphertext).slice(paddingSize);
244
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @author n1073645 [n1073645@gmail.com]
3
+ * @copyright Crown Copyright 2020
4
+ * @license Apache-2.0
5
+ */
6
+
7
+ import Operation from "../Operation.mjs";
8
+ import * as LS47 from "../lib/LS47.mjs";
9
+
10
+ /**
11
+ * LS47 Decrypt operation
12
+ */
13
+ class LS47Decrypt extends Operation {
14
+
15
+ /**
16
+ * LS47Decrypt constructor
17
+ */
18
+ constructor() {
19
+ super();
20
+
21
+ this.name = "LS47 Decrypt";
22
+ this.module = "Crypto";
23
+ this.description = "This is a slight improvement of the ElsieFour cipher as described by Alan Kaminsky. We use 7x7 characters instead of original (barely fitting) 6x6, to be able to encrypt some structured information. We also describe a simple key-expansion algorithm, because remembering passwords is popular. Similar security considerations as with ElsieFour hold.<br>The LS47 alphabet consists of following characters: <code>_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()</code><br>An LS47 key is a permutation of the alphabet that is then represented in a 7x7 grid used for the encryption or decryption.";
24
+ this.infoURL = "https://github.com/exaexa/ls47";
25
+ this.inputType = "string";
26
+ this.outputType = "string";
27
+ this.args = [
28
+ {
29
+ name: "Password",
30
+ type: "string",
31
+ value: ""
32
+ },
33
+ {
34
+ name: "Padding",
35
+ type: "number",
36
+ value: 10
37
+ }
38
+ ];
39
+ }
40
+
41
+ /**
42
+ * @param {string} input
43
+ * @param {Object[]} args
44
+ * @returns {string}
45
+ */
46
+ run(input, args) {
47
+ this.paddingSize = parseInt(args[1], 10);
48
+
49
+ LS47.initTiles();
50
+
51
+ const key = LS47.deriveKey(args[0]);
52
+ return LS47.decryptPad(key, input, this.paddingSize);
53
+ }
54
+
55
+ }
56
+
57
+ export default LS47Decrypt;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @author n1073645 [n1073645@gmail.com]
3
+ * @copyright Crown Copyright 2020
4
+ * @license Apache-2.0
5
+ */
6
+
7
+ import Operation from "../Operation.mjs";
8
+ import * as LS47 from "../lib/LS47.mjs";
9
+
10
+ /**
11
+ * LS47 Encrypt operation
12
+ */
13
+ class LS47Encrypt extends Operation {
14
+
15
+ /**
16
+ * LS47Encrypt constructor
17
+ */
18
+ constructor() {
19
+ super();
20
+
21
+ this.name = "LS47 Encrypt";
22
+ this.module = "Crypto";
23
+ this.description = "This is a slight improvement of the ElsieFour cipher as described by Alan Kaminsky. We use 7x7 characters instead of original (barely fitting) 6x6, to be able to encrypt some structured information. We also describe a simple key-expansion algorithm, because remembering passwords is popular. Similar security considerations as with ElsieFour hold.<br>The LS47 alphabet consists of following characters: <code>_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()</code><br>A LS47 key is a permutation of the alphabet that is then represented in a 7x7 grid used for the encryption or decryption.";
24
+ this.infoURL = "https://github.com/exaexa/ls47";
25
+ this.inputType = "string";
26
+ this.outputType = "string";
27
+ this.args = [
28
+ {
29
+ name: "Password",
30
+ type: "string",
31
+ value: ""
32
+ },
33
+ {
34
+ name: "Padding",
35
+ type: "number",
36
+ value: 10
37
+ },
38
+ {
39
+ name: "Signature",
40
+ type: "string",
41
+ value: ""
42
+ }
43
+ ];
44
+ }
45
+
46
+ /**
47
+ * @param {string} input
48
+ * @param {Object[]} args
49
+ * @returns {string}
50
+ */
51
+ run(input, args) {
52
+ this.paddingSize = parseInt(args[1], 10);
53
+
54
+ LS47.initTiles();
55
+
56
+ const key = LS47.deriveKey(args[0]);
57
+ return LS47.encryptPad(key, input, args[2], this.paddingSize);
58
+ }
59
+
60
+ }
61
+
62
+ export default LS47Encrypt;
@@ -192,6 +192,8 @@ import JavaScriptMinify from "./JavaScriptMinify.mjs";
192
192
  import JavaScriptParser from "./JavaScriptParser.mjs";
193
193
  import Jump from "./Jump.mjs";
194
194
  import Keccak from "./Keccak.mjs";
195
+ import LS47Decrypt from "./LS47Decrypt.mjs";
196
+ import LS47Encrypt from "./LS47Encrypt.mjs";
195
197
  import Label from "./Label.mjs";
196
198
  import Lorenz from "./Lorenz.mjs";
197
199
  import LuhnChecksum from "./LuhnChecksum.mjs";
@@ -568,6 +570,8 @@ export {
568
570
  JavaScriptParser,
569
571
  Jump,
570
572
  Keccak,
573
+ LS47Decrypt,
574
+ LS47Encrypt,
571
575
  Label,
572
576
  Lorenz,
573
577
  LuhnChecksum,
@@ -195,6 +195,8 @@ import {
195
195
  JWTSign as core_JWTSign,
196
196
  JWTVerify as core_JWTVerify,
197
197
  Keccak as core_Keccak,
198
+ LS47Decrypt as core_LS47Decrypt,
199
+ LS47Encrypt as core_LS47Encrypt,
198
200
  Lorenz as core_Lorenz,
199
201
  LuhnChecksum as core_LuhnChecksum,
200
202
  MD2 as core_MD2,
@@ -571,6 +573,8 @@ function generateChef() {
571
573
  "JWTSign": _wrap(core_JWTSign),
572
574
  "JWTVerify": _wrap(core_JWTVerify),
573
575
  "keccak": _wrap(core_Keccak),
576
+ "LS47Decrypt": _wrap(core_LS47Decrypt),
577
+ "LS47Encrypt": _wrap(core_LS47Encrypt),
574
578
  "lorenz": _wrap(core_Lorenz),
575
579
  "luhnChecksum": _wrap(core_LuhnChecksum),
576
580
  "MD2": _wrap(core_MD2),
@@ -962,6 +966,8 @@ const javaScriptMinify = chef.javaScriptMinify;
962
966
  const javaScriptParser = chef.javaScriptParser;
963
967
  const jump = chef.jump;
964
968
  const keccak = chef.keccak;
969
+ const LS47Decrypt = chef.LS47Decrypt;
970
+ const LS47Encrypt = chef.LS47Encrypt;
965
971
  const label = chef.label;
966
972
  const lorenz = chef.lorenz;
967
973
  const luhnChecksum = chef.luhnChecksum;
@@ -1340,6 +1346,8 @@ const operations = [
1340
1346
  javaScriptParser,
1341
1347
  jump,
1342
1348
  keccak,
1349
+ LS47Decrypt,
1350
+ LS47Encrypt,
1343
1351
  label,
1344
1352
  lorenz,
1345
1353
  luhnChecksum,
@@ -1722,6 +1730,8 @@ export {
1722
1730
  javaScriptParser,
1723
1731
  jump,
1724
1732
  keccak,
1733
+ LS47Decrypt,
1734
+ LS47Encrypt,
1725
1735
  label,
1726
1736
  lorenz,
1727
1737
  luhnChecksum,
@@ -117,6 +117,7 @@ import "./tests/SIGABA.mjs";
117
117
  import "./tests/ELFInfo.mjs";
118
118
  import "./tests/Subsection.mjs";
119
119
  import "./tests/CaesarBoxCipher.mjs";
120
+ import "./tests/LS47.mjs";
120
121
 
121
122
 
122
123
  // Cannot test operations that use the File type yet
@@ -0,0 +1,45 @@
1
+ /**
2
+ * LS47 tests.
3
+ *
4
+ * @author n1073645 [n1073645@gmail.com]
5
+ *
6
+ * @copyright Crown Copyright 2020
7
+ * @license Apache-2.0
8
+ */
9
+ import TestRegister from "../../lib/TestRegister.mjs";
10
+
11
+ TestRegister.addTests([
12
+ {
13
+ name: "LS47 Encrypt",
14
+ input: "thequickbrownfoxjumped",
15
+ expectedOutput: "(,t74ci78cp/8trx*yesu:alp1wqy",
16
+ recipeConfig: [
17
+ {
18
+ op: "LS47 Encrypt",
19
+ args: ["helloworld", 0, "test"],
20
+ },
21
+ ],
22
+ },
23
+ {
24
+ name: "LS47 Decrypt",
25
+ input: "(,t74ci78cp/8trx*yesu:alp1wqy",
26
+ expectedOutput: "thequickbrownfoxjumped---test",
27
+ recipeConfig: [
28
+ {
29
+ op: "LS47 Decrypt",
30
+ args: ["helloworld", 0],
31
+ },
32
+ ],
33
+ },
34
+ {
35
+ name: "LS47 Encrypt",
36
+ input: "thequickbrownfoxjumped",
37
+ expectedOutput: "Letter H is not included in LS47",
38
+ recipeConfig: [
39
+ {
40
+ op: "LS47 Encrypt",
41
+ args: ["Helloworld", 0, "test"],
42
+ },
43
+ ],
44
+ }
45
+ ]);