cyberchef 9.49.1 → 9.50.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.50.0] - 2022-11-25
17
+ - Added 'Shuffle' operation [@mikecat] | [#1472]
18
+
16
19
  ### [9.49.0] - 2022-11-11
17
20
  - Added 'LZ4 Compress' and 'LZ4 Decompress' operations [@n1474335] | [31a7f83]
18
21
 
@@ -324,6 +327,7 @@ All major and minor version changes will be documented in this file. Details of
324
327
 
325
328
 
326
329
 
330
+ [9.50.0]: https://github.com/gchq/CyberChef/releases/tag/v9.50.0
327
331
  [9.49.0]: https://github.com/gchq/CyberChef/releases/tag/v9.49.0
328
332
  [9.48.0]: https://github.com/gchq/CyberChef/releases/tag/v9.48.0
329
333
  [9.47.0]: https://github.com/gchq/CyberChef/releases/tag/v9.47.0
@@ -568,4 +572,5 @@ All major and minor version changes will be documented in this file. Details of
568
572
  [#1308]: https://github.com/gchq/CyberChef/pull/1308
569
573
  [#1421]: https://github.com/gchq/CyberChef/pull/1421
570
574
  [#1427]: https://github.com/gchq/CyberChef/pull/1427
575
+ [#1472]: https://github.com/gchq/CyberChef/pull/1472
571
576
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyberchef",
3
- "version": "9.49.1",
3
+ "version": "9.50.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",
@@ -249,6 +249,7 @@
249
249
  "To Table",
250
250
  "Reverse",
251
251
  "Sort",
252
+ "Shuffle",
252
253
  "Unique",
253
254
  "Split",
254
255
  "Filter",
@@ -12981,6 +12981,30 @@
12981
12981
  }
12982
12982
  ]
12983
12983
  },
12984
+ "Shuffle": {
12985
+ "module": "Default",
12986
+ "description": "Randomly reorders input elements.",
12987
+ "infoURL": "https://wikipedia.org/wiki/Shuffling",
12988
+ "inputType": "string",
12989
+ "outputType": "string",
12990
+ "flowControl": false,
12991
+ "manualBake": false,
12992
+ "args": [
12993
+ {
12994
+ "name": "Delimiter",
12995
+ "type": "option",
12996
+ "value": [
12997
+ "Line feed",
12998
+ "CRLF",
12999
+ "Space",
13000
+ "Comma",
13001
+ "Semi-colon",
13002
+ "Colon",
13003
+ "Nothing (separate chars)"
13004
+ ]
13005
+ }
13006
+ ]
13007
+ },
12984
13008
  "Sleep": {
12985
13009
  "module": "Default",
12986
13010
  "description": "Sleep causes the recipe to wait for a specified number of milliseconds before continuing execution.",
@@ -13348,6 +13372,11 @@
13348
13372
  "name": "Ciphertext",
13349
13373
  "type": "binaryString",
13350
13374
  "value": "XYZABCDEFGHIJKLMNOPQRSTUVW"
13375
+ },
13376
+ {
13377
+ "name": "Ignore case",
13378
+ "type": "boolean",
13379
+ "value": false
13351
13380
  }
13352
13381
  ]
13353
13382
  },
@@ -125,6 +125,7 @@ import SetDifference from "../../operations/SetDifference.mjs";
125
125
  import SetIntersection from "../../operations/SetIntersection.mjs";
126
126
  import SetUnion from "../../operations/SetUnion.mjs";
127
127
  import ShowBase64Offsets from "../../operations/ShowBase64Offsets.mjs";
128
+ import Shuffle from "../../operations/Shuffle.mjs";
128
129
  import Sleep from "../../operations/Sleep.mjs";
129
130
  import Sort from "../../operations/Sort.mjs";
130
131
  import Split from "../../operations/Split.mjs";
@@ -298,6 +299,7 @@ OpModules.Default = {
298
299
  "Set Intersection": SetIntersection,
299
300
  "Set Union": SetUnion,
300
301
  "Show Base64 offsets": ShowBase64Offsets,
302
+ "Shuffle": Shuffle,
301
303
  "Sleep": Sleep,
302
304
  "Sort": Sort,
303
305
  "Split": Split,
@@ -0,0 +1,78 @@
1
+ /**
2
+ * @author mikecat
3
+ * @copyright Crown Copyright 2022
4
+ * @license Apache-2.0
5
+ */
6
+
7
+ import Operation from "../Operation.mjs";
8
+ import Utils from "../Utils.mjs";
9
+ import {INPUT_DELIM_OPTIONS} from "../lib/Delim.mjs";
10
+
11
+ /**
12
+ * Shuffle operation
13
+ */
14
+ class Shuffle extends Operation {
15
+
16
+ /**
17
+ * Shuffle constructor
18
+ */
19
+ constructor() {
20
+ super();
21
+
22
+ this.name = "Shuffle";
23
+ this.module = "Default";
24
+ this.description = "Randomly reorders input elements.";
25
+ this.infoURL = "https://wikipedia.org/wiki/Shuffling";
26
+ this.inputType = "string";
27
+ this.outputType = "string";
28
+ this.args = [
29
+ {
30
+ name: "Delimiter",
31
+ type: "option",
32
+ value: INPUT_DELIM_OPTIONS
33
+ }
34
+ ];
35
+ }
36
+
37
+ /**
38
+ * @param {string} input
39
+ * @param {Object[]} args
40
+ * @returns {string}
41
+ */
42
+ run(input, args) {
43
+ const delim = Utils.charRep(args[0]);
44
+ if (input.length === 0) return input;
45
+
46
+ // return a random number in [0, 1)
47
+ const rng = (typeof crypto) !== "undefined" && crypto.getRandomValues ? (function() {
48
+ const buf = new Uint32Array(2);
49
+ return function() {
50
+ // generate 53-bit random integer: 21 + 32 bits
51
+ crypto.getRandomValues(buf);
52
+ const value = (buf[0] >>> (32 - 21)) * ((1 << 30) * 4) + buf[1];
53
+ return value / ((1 << 23) * (1 << 30));
54
+ };
55
+ })() : Math.random;
56
+
57
+ // return a random integer in [0, max)
58
+ const randint = function(max) {
59
+ return Math.floor(rng() * max);
60
+ };
61
+
62
+ // Split input into shuffleable sections
63
+ const toShuffle = input.split(delim);
64
+
65
+ // shuffle elements
66
+ for (let i = toShuffle.length - 1; i > 0; i--) {
67
+ const idx = randint(i + 1);
68
+ const tmp = toShuffle[idx];
69
+ toShuffle[idx] = toShuffle[i];
70
+ toShuffle[i] = tmp;
71
+ }
72
+
73
+ return toShuffle.join(delim);
74
+ }
75
+
76
+ }
77
+
78
+ export default Shuffle;
@@ -34,10 +34,50 @@ class Substitute extends Operation {
34
34
  "name": "Ciphertext",
35
35
  "type": "binaryString",
36
36
  "value": "XYZABCDEFGHIJKLMNOPQRSTUVW"
37
+ },
38
+ {
39
+ "name": "Ignore case",
40
+ "type": "boolean",
41
+ "value": false
37
42
  }
38
43
  ];
39
44
  }
40
45
 
46
+ /**
47
+ * Convert a single character using the dictionary, if ignoreCase is true then
48
+ * check in the dictionary for both upper and lower case versions of the character.
49
+ * In output the input character case is preserved.
50
+ * @param {string} char
51
+ * @param {Object} dict
52
+ * @param {boolean} ignoreCase
53
+ * @returns {string}
54
+ */
55
+ cipherSingleChar(char, dict, ignoreCase) {
56
+ if (!ignoreCase)
57
+ return dict[char] || char;
58
+
59
+ const isUpperCase = char === char.toUpperCase();
60
+
61
+ // convert using the dictionary keeping the case of the input character
62
+
63
+ if (dict[char] !== undefined) {
64
+ // if the character is in the dictionary return the value with the input case
65
+ return isUpperCase ? dict[char].toUpperCase() : dict[char].toLowerCase();
66
+ }
67
+
68
+ // check for the other case, if it is in the dictionary return the value with the right case
69
+ if (isUpperCase) {
70
+ if (dict[char.toLowerCase()] !== undefined)
71
+ return dict[char.toLowerCase()].toUpperCase();
72
+ } else {
73
+ if (dict[char.toUpperCase()] !== undefined)
74
+ return dict[char.toUpperCase()].toLowerCase();
75
+ }
76
+
77
+ return char;
78
+ }
79
+
80
+
41
81
  /**
42
82
  * @param {string} input
43
83
  * @param {Object[]} args
@@ -45,17 +85,23 @@ class Substitute extends Operation {
45
85
  */
46
86
  run(input, args) {
47
87
  const plaintext = Utils.expandAlphRange([...args[0]]),
48
- ciphertext = Utils.expandAlphRange([...args[1]]);
49
- let output = "",
50
- index = -1;
88
+ ciphertext = Utils.expandAlphRange([...args[1]]),
89
+ ignoreCase = args[2];
90
+ let output = "";
51
91
 
52
92
  if (plaintext.length !== ciphertext.length) {
53
93
  output = "Warning: Plaintext and Ciphertext lengths differ\n\n";
54
94
  }
55
95
 
96
+ // create dictionary for conversion
97
+ const dict = {};
98
+ for (let i = 0; i < Math.min(ciphertext.length, plaintext.length); i++) {
99
+ dict[plaintext[i]] = ciphertext[i];
100
+ }
101
+
102
+ // map every letter with the conversion function
56
103
  for (const character of input) {
57
- index = plaintext.indexOf(character);
58
- output += index > -1 && index < ciphertext.length ? ciphertext[index] : character;
104
+ output += this.cipherSingleChar(character, dict, ignoreCase);
59
105
  }
60
106
 
61
107
  return output;
@@ -313,6 +313,7 @@ import Shake from "./Shake.mjs";
313
313
  import SharpenImage from "./SharpenImage.mjs";
314
314
  import ShowBase64Offsets from "./ShowBase64Offsets.mjs";
315
315
  import ShowOnMap from "./ShowOnMap.mjs";
316
+ import Shuffle from "./Shuffle.mjs";
316
317
  import Sleep from "./Sleep.mjs";
317
318
  import Snefru from "./Snefru.mjs";
318
319
  import Sort from "./Sort.mjs";
@@ -704,6 +705,7 @@ export {
704
705
  SharpenImage,
705
706
  ShowBase64Offsets,
706
707
  ShowOnMap,
708
+ Shuffle,
707
709
  Sleep,
708
710
  Snefru,
709
711
  Sort,
@@ -314,6 +314,7 @@ import {
314
314
  SharpenImage as core_SharpenImage,
315
315
  ShowBase64Offsets as core_ShowBase64Offsets,
316
316
  ShowOnMap as core_ShowOnMap,
317
+ Shuffle as core_Shuffle,
317
318
  Sleep as core_Sleep,
318
319
  Snefru as core_Snefru,
319
320
  Sort as core_Sort,
@@ -705,6 +706,7 @@ function generateChef() {
705
706
  "sharpenImage": _wrap(core_SharpenImage),
706
707
  "showBase64Offsets": _wrap(core_ShowBase64Offsets),
707
708
  "showOnMap": _wrap(core_ShowOnMap),
709
+ "shuffle": _wrap(core_Shuffle),
708
710
  "sleep": _wrap(core_Sleep),
709
711
  "snefru": _wrap(core_Snefru),
710
712
  "sort": _wrap(core_Sort),
@@ -1113,6 +1115,7 @@ const shake = chef.shake;
1113
1115
  const sharpenImage = chef.sharpenImage;
1114
1116
  const showBase64Offsets = chef.showBase64Offsets;
1115
1117
  const showOnMap = chef.showOnMap;
1118
+ const shuffle = chef.shuffle;
1116
1119
  const sleep = chef.sleep;
1117
1120
  const snefru = chef.snefru;
1118
1121
  const sort = chef.sort;
@@ -1506,6 +1509,7 @@ const operations = [
1506
1509
  sharpenImage,
1507
1510
  showBase64Offsets,
1508
1511
  showOnMap,
1512
+ shuffle,
1509
1513
  sleep,
1510
1514
  snefru,
1511
1515
  sort,
@@ -1903,6 +1907,7 @@ export {
1903
1907
  sharpenImage,
1904
1908
  showBase64Offsets,
1905
1909
  showOnMap,
1910
+ shuffle,
1906
1911
  sleep,
1907
1912
  snefru,
1908
1913
  sort,
@@ -124,6 +124,7 @@ import "./tests/UnescapeString.mjs";
124
124
  import "./tests/LS47.mjs";
125
125
  import "./tests/LZString.mjs";
126
126
  import "./tests/NTLM.mjs";
127
+ import "./tests/Shuffle.mjs";
127
128
 
128
129
  // Cannot test operations that use the File type yet
129
130
  // import "./tests/SplitColourChannels.mjs";
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @author mikecat
3
+ * @copyright Crown Copyright 2022
4
+ * @license Apache-2.0
5
+ */
6
+ import TestRegister from "../../lib/TestRegister.mjs";
7
+
8
+ TestRegister.addTests([
9
+ {
10
+ "name": "Shuffle empty",
11
+ "input": "",
12
+ "expectedOutput": "",
13
+ "recipeConfig": [
14
+ {
15
+ "op": "Shuffle",
16
+ "args": ["Comma"]
17
+ }
18
+ ]
19
+ },
20
+ {
21
+ "name": "Shuffle bytes",
22
+ "input": "12345678",
23
+ "expectedOutput": "31 32 33 34 35 36 37 38",
24
+ "recipeConfig": [
25
+ {
26
+ "op": "Shuffle",
27
+ "args": ["Nothing (separate chars)"]
28
+ },
29
+ {
30
+ "op": "To Hex",
31
+ "args": ["Space", 0]
32
+ },
33
+ {
34
+ "op": "Sort",
35
+ "args": ["Space", false, "Alphabetical (case sensitive)"]
36
+ }
37
+ ]
38
+ },
39
+ {
40
+ "name": "Shuffle lines",
41
+ "input": "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf\n",
42
+ "expectedOutput": "\n1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf",
43
+ "recipeConfig": [
44
+ {
45
+ "op": "Shuffle",
46
+ "args": ["Line feed"]
47
+ },
48
+ {
49
+ "op": "Sort",
50
+ "args": ["Line feed", false, "Alphabetical (case sensitive)"]
51
+ }
52
+ ]
53
+ }
54
+ ]);