cyberchef 10.22.0 → 10.23.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 +131 -0
- package/CONTRIBUTING.md +37 -0
- package/Dockerfile +2 -0
- package/Gruntfile.js +0 -12
- package/README.md +1 -1
- package/babel.config.js +0 -6
- package/package.json +63 -62
- package/src/core/Chef.mjs +8 -1
- package/src/core/Ingredient.mjs +5 -2
- package/src/core/Operation.mjs +6 -1
- package/src/core/Recipe.mjs +10 -5
- package/src/core/config/Categories.json +18 -3
- package/src/core/config/OperationConfig.json +22310 -0
- package/src/core/config/modules/Bletchley.mjs +28 -0
- package/src/core/config/modules/Charts.mjs +24 -0
- package/src/core/config/modules/Ciphers.mjs +128 -0
- package/src/core/config/modules/Code.mjs +60 -0
- package/src/core/config/modules/Compression.mjs +52 -0
- package/src/core/config/modules/Crypto.mjs +134 -0
- package/src/core/config/modules/Default.mjs +424 -0
- package/src/core/config/modules/Diff.mjs +16 -0
- package/src/core/config/modules/Encodings.mjs +42 -0
- package/src/core/config/modules/Handlebars.mjs +16 -0
- package/src/core/config/modules/Hashing.mjs +30 -0
- package/src/core/config/modules/Image.mjs +70 -0
- package/src/core/config/modules/Jq.mjs +16 -0
- package/src/core/config/modules/OCR.mjs +16 -0
- package/src/core/config/modules/OpModules.mjs +63 -0
- package/src/core/config/modules/PGP.mjs +26 -0
- package/src/core/config/modules/Protobuf.mjs +18 -0
- package/src/core/config/modules/PublicKey.mjs +36 -0
- package/src/core/config/modules/Regex.mjs +40 -0
- package/src/core/config/modules/Serialise.mjs +26 -0
- package/src/core/config/modules/Shellcode.mjs +18 -0
- package/src/core/config/modules/URL.mjs +20 -0
- package/src/core/config/modules/UserAgent.mjs +16 -0
- package/src/core/config/modules/Yara.mjs +16 -0
- package/src/core/lib/AudioBytes.mjs +103 -0
- package/src/core/lib/AudioMetaSchema.mjs +82 -0
- package/src/core/lib/AudioParsers.mjs +630 -0
- package/src/core/lib/BigIntUtils.mjs +73 -0
- package/src/core/lib/Modhex.mjs +2 -0
- package/src/core/lib/QRCode.mjs +30 -10
- package/src/core/lib/RC6.mjs +625 -0
- package/src/core/operations/A1Z26CipherDecode.mjs +1 -1
- package/src/core/operations/AddTextToImage.mjs +116 -64
- package/src/core/operations/BlurImage.mjs +10 -12
- package/src/core/operations/ContainImage.mjs +50 -40
- package/src/core/operations/ConvertImageFormat.mjs +33 -39
- package/src/core/operations/CoverImage.mjs +39 -37
- package/src/core/operations/CropImage.mjs +35 -21
- package/src/core/operations/DisassembleARM.mjs +193 -0
- package/src/core/operations/DitherImage.mjs +8 -8
- package/src/core/operations/EscapeUnicodeCharacters.mjs +0 -17
- package/src/core/operations/ExtractAudioMetadata.mjs +175 -0
- package/src/core/operations/ExtractLSB.mjs +17 -11
- package/src/core/operations/ExtractRGBA.mjs +12 -10
- package/src/core/operations/FlaskSessionDecode.mjs +80 -0
- package/src/core/operations/FlaskSessionSign.mjs +89 -0
- package/src/core/operations/FlaskSessionVerify.mjs +136 -0
- package/src/core/operations/FlipImage.mjs +14 -10
- package/src/core/operations/GenerateImage.mjs +39 -32
- package/src/core/operations/ImageBrightnessContrast.mjs +10 -10
- package/src/core/operations/ImageFilter.mjs +14 -13
- package/src/core/operations/ImageHueSaturationLightness.mjs +22 -20
- package/src/core/operations/ImageOpacity.mjs +6 -8
- package/src/core/operations/InvertImage.mjs +4 -6
- package/src/core/operations/Jq.mjs +12 -4
- package/src/core/operations/NormaliseImage.mjs +5 -7
- package/src/core/operations/OffsetChecker.mjs +1 -1
- package/src/core/operations/ParseEthernetFrame.mjs +112 -0
- package/src/core/operations/ParseIPv4Header.mjs +23 -6
- package/src/core/operations/ParseQRCode.mjs +13 -13
- package/src/core/operations/PseudoRandomIntegerGenerator.mjs +164 -0
- package/src/core/operations/RC6Decrypt.mjs +119 -0
- package/src/core/operations/RC6Encrypt.mjs +119 -0
- package/src/core/operations/RandomizeColourPalette.mjs +11 -11
- package/src/core/operations/ResizeImage.mjs +30 -23
- package/src/core/operations/RotateImage.mjs +8 -9
- package/src/core/operations/SQLBeautify.mjs +21 -3
- package/src/core/operations/SharpenImage.mjs +94 -62
- package/src/core/operations/SplitColourChannels.mjs +47 -21
- package/src/core/operations/TextIntegerConverter.mjs +123 -0
- package/src/core/operations/UnescapeUnicodeCharacters.mjs +17 -0
- package/src/core/operations/ViewBitPlane.mjs +16 -20
- package/src/core/operations/index.mjs +960 -0
- package/src/node/index.mjs +2423 -0
- package/src/web/HTMLIngredient.mjs +24 -43
- package/src/web/Manager.mjs +1 -0
- package/src/web/html/index.html +6 -6
- package/src/web/static/fonts/bmfonts/Roboto72White.fnt +491 -485
- package/src/web/static/fonts/bmfonts/RobotoBlack72White.fnt +494 -488
- package/src/web/static/fonts/bmfonts/RobotoMono72White.fnt +110 -103
- package/src/web/static/fonts/bmfonts/RobotoSlab72White.fnt +498 -492
- package/src/web/stylesheets/layout/_banner.css +30 -0
- package/src/web/stylesheets/layout/_modals.css +5 -0
- package/src/web/stylesheets/utils/_overrides.css +7 -0
- package/src/web/waiters/ControlsWaiter.mjs +82 -0
- package/src/web/waiters/InputWaiter.mjs +12 -6
- package/src/web/waiters/RecipeWaiter.mjs +2 -2
- package/tests/browser/02_ops.js +23 -3
- package/tests/node/index.mjs +1 -0
- package/tests/node/tests/lib/BigIntUtils.mjs +150 -0
- package/tests/node/tests/operations.mjs +9 -7
- package/tests/operations/index.mjs +8 -0
- package/tests/operations/tests/A1Z26CipherDecode.mjs +33 -0
- package/tests/operations/tests/DisassembleARM.mjs +377 -0
- package/tests/operations/tests/ExtractAudioMetadata.mjs +287 -0
- package/tests/operations/tests/FlaskSession.mjs +246 -0
- package/tests/operations/tests/GenerateQRCode.mjs +67 -0
- package/tests/operations/tests/JWTSign.mjs +83 -8
- package/tests/operations/tests/Jq.mjs +32 -0
- package/tests/operations/tests/Modhex.mjs +20 -0
- package/tests/operations/tests/ParseEthernetFrame.mjs +45 -0
- package/tests/operations/tests/RC6.mjs +487 -0
- package/tests/operations/tests/SQLBeautify.mjs +54 -0
- package/tests/operations/tests/TextIntegerConverter.mjs +199 -0
- package/tests/samples/Audio.mjs +73 -0
- package/tests/samples/Images.mjs +0 -12
- package/webpack.config.js +10 -7
- package/src/core/lib/ImageManipulation.mjs +0 -251
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author tedk [tedk@ted.do]
|
|
3
|
+
* @copyright Crown Copyright 2024
|
|
4
|
+
* @license Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import Operation from "../Operation.mjs";
|
|
8
|
+
import OperationError from "../errors/OperationError.mjs";
|
|
9
|
+
import Utils from "../Utils.mjs";
|
|
10
|
+
import {fromHex, toHex} from "../lib/Hex.mjs";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parse Ethernet frame operation
|
|
14
|
+
*/
|
|
15
|
+
class ParseEthernetFrame extends Operation {
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* ParseEthernetFrame constructor
|
|
19
|
+
*/
|
|
20
|
+
constructor() {
|
|
21
|
+
super();
|
|
22
|
+
|
|
23
|
+
this.name = "Parse Ethernet frame";
|
|
24
|
+
this.module = "Default";
|
|
25
|
+
this.description = "Parses an Ethernet frame and either shows the deduced values (Source and destination MAC, VLANs) or returns the packet data.<br /><br />Good for use in conjunction with the Parse IPv4, and Parse TCP/UDP recipes.";
|
|
26
|
+
this.infoURL = "https://en.wikipedia.org/wiki/Ethernet_frame#Frame_%E2%80%93_data_link_layer";
|
|
27
|
+
this.inputType = "string";
|
|
28
|
+
this.outputType = "html";
|
|
29
|
+
this.args = [
|
|
30
|
+
{
|
|
31
|
+
name: "Input type",
|
|
32
|
+
type: "option",
|
|
33
|
+
value: [
|
|
34
|
+
"Raw", "Hex"
|
|
35
|
+
],
|
|
36
|
+
defaultIndex: 0,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "Return type",
|
|
40
|
+
type: "option",
|
|
41
|
+
value: [
|
|
42
|
+
"Text output", "Packet data", "Packet data (hex)",
|
|
43
|
+
],
|
|
44
|
+
defaultIndex: 0,
|
|
45
|
+
}
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} input
|
|
52
|
+
* @param {Object[]} args
|
|
53
|
+
* @returns {html}
|
|
54
|
+
*/
|
|
55
|
+
run(input, args) {
|
|
56
|
+
const format = args[0];
|
|
57
|
+
const outputFormat = args[1];
|
|
58
|
+
|
|
59
|
+
if (format === "Hex") {
|
|
60
|
+
input = fromHex(input);
|
|
61
|
+
} else if (format === "Raw") {
|
|
62
|
+
input = new Uint8Array(Utils.strToArrayBuffer(input));
|
|
63
|
+
} else {
|
|
64
|
+
throw new OperationError("Invalid input format selected.");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const destinationMac = input.slice(0, 6);
|
|
68
|
+
const sourceMac = input.slice(6, 12);
|
|
69
|
+
|
|
70
|
+
let offset = 12;
|
|
71
|
+
const vlans = [];
|
|
72
|
+
|
|
73
|
+
while (offset < input.length) {
|
|
74
|
+
const ethType = Utils.byteArrayToChars(input.slice(offset, offset+2));
|
|
75
|
+
offset += 2;
|
|
76
|
+
|
|
77
|
+
if (ethType === "\x81\x00" || ethType === "\x88\xA8") {
|
|
78
|
+
// Parse the VLAN tag:
|
|
79
|
+
// [0000] 0000 0000 0000
|
|
80
|
+
// ^^^ PRIO - Ignored
|
|
81
|
+
// ^ DEI - Ignored
|
|
82
|
+
// ^^^^ ^^^^ ^^^^ VLAN ID
|
|
83
|
+
const vlanTag = input.slice(offset, offset+2);
|
|
84
|
+
vlans.push(((vlanTag[0] & 0b00001111) << 8) | vlanTag[1]);
|
|
85
|
+
|
|
86
|
+
offset += 2;
|
|
87
|
+
} else {
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const packetData = input.slice(offset);
|
|
93
|
+
|
|
94
|
+
if (outputFormat === "Packet data") {
|
|
95
|
+
return Utils.byteArrayToChars(packetData);
|
|
96
|
+
} else if (outputFormat === "Packet data (hex)") {
|
|
97
|
+
return toHex(packetData);
|
|
98
|
+
} else if (outputFormat === "Text output") {
|
|
99
|
+
let retval = `Source MAC: ${toHex(sourceMac, ":")}\nDestination MAC: ${toHex(destinationMac, ":")}\n`;
|
|
100
|
+
if (vlans.length > 0) {
|
|
101
|
+
retval += `VLAN: ${vlans.join(", ")}\n`;
|
|
102
|
+
}
|
|
103
|
+
retval += `Data:\n${toHex(packetData)}`;
|
|
104
|
+
return retval;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export default ParseEthernetFrame;
|
|
@@ -33,6 +33,12 @@ class ParseIPv4Header extends Operation {
|
|
|
33
33
|
"name": "Input format",
|
|
34
34
|
"type": "option",
|
|
35
35
|
"value": ["Hex", "Raw"]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "Output format",
|
|
39
|
+
"type": "option",
|
|
40
|
+
"value": ["Table", "Data (hex)", "Data (raw)"],
|
|
41
|
+
defaultIndex: 0,
|
|
36
42
|
}
|
|
37
43
|
];
|
|
38
44
|
}
|
|
@@ -44,6 +50,8 @@ class ParseIPv4Header extends Operation {
|
|
|
44
50
|
*/
|
|
45
51
|
run(input, args) {
|
|
46
52
|
const format = args[0];
|
|
53
|
+
const outputFormat = args[1];
|
|
54
|
+
|
|
47
55
|
let output;
|
|
48
56
|
|
|
49
57
|
if (format === "Hex") {
|
|
@@ -98,7 +106,10 @@ class ParseIPv4Header extends Operation {
|
|
|
98
106
|
checksumResult = givenChecksum + " (incorrect, should be " + correctChecksum + ")";
|
|
99
107
|
}
|
|
100
108
|
|
|
101
|
-
|
|
109
|
+
const data = input.slice(ihl * 4);
|
|
110
|
+
|
|
111
|
+
if (outputFormat === "Table") {
|
|
112
|
+
output = `<table class='table table-hover table-sm table-bordered table-nonfluid'><tr><th>Field</th><th>Value</th></tr>
|
|
102
113
|
<tr><td>Version</td><td>${version}</td></tr>
|
|
103
114
|
<tr><td>Internet Header Length (IHL)</td><td>${ihl} (${ihl * 4} bytes)</td></tr>
|
|
104
115
|
<tr><td>Differentiated Services Code Point (DSCP)</td><td>${dscp}</td></tr>
|
|
@@ -116,13 +127,19 @@ class ParseIPv4Header extends Operation {
|
|
|
116
127
|
<tr><td>Protocol</td><td>${protocol}, ${protocolInfo.protocol} (${protocolInfo.keyword})</td></tr>
|
|
117
128
|
<tr><td>Header checksum</td><td>${checksumResult}</td></tr>
|
|
118
129
|
<tr><td>Source IP address</td><td>${ipv4ToStr(srcIP)}</td></tr>
|
|
119
|
-
<tr><td>Destination IP address</td><td>${ipv4ToStr(dstIP)}</td></tr
|
|
130
|
+
<tr><td>Destination IP address</td><td>${ipv4ToStr(dstIP)}</td></tr>
|
|
131
|
+
<tr><td>Data (hex)</td><td>${toHex(data)}</td></tr>`;
|
|
120
132
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
133
|
+
if (ihl > 5) {
|
|
134
|
+
output += `<tr><td>Options</td><td>${toHex(options)}</td></tr>`;
|
|
135
|
+
}
|
|
124
136
|
|
|
125
|
-
|
|
137
|
+
return output + "</table>";
|
|
138
|
+
} else if (outputFormat === "Data (hex)") {
|
|
139
|
+
return toHex(data);
|
|
140
|
+
} else if (outputFormat === "Data (raw)") {
|
|
141
|
+
return Utils.byteArrayToChars(data);
|
|
142
|
+
}
|
|
126
143
|
}
|
|
127
144
|
|
|
128
145
|
}
|
|
@@ -13,7 +13,6 @@ import { parseQrCode } from "../lib/QRCode.mjs";
|
|
|
13
13
|
* Parse QR Code operation
|
|
14
14
|
*/
|
|
15
15
|
class ParseQRCode extends Operation {
|
|
16
|
-
|
|
17
16
|
/**
|
|
18
17
|
* ParseQRCode constructor
|
|
19
18
|
*/
|
|
@@ -22,24 +21,26 @@ class ParseQRCode extends Operation {
|
|
|
22
21
|
|
|
23
22
|
this.name = "Parse QR Code";
|
|
24
23
|
this.module = "Image";
|
|
25
|
-
this.description =
|
|
24
|
+
this.description =
|
|
25
|
+
"Reads an image file and attempts to detect and read a Quick Response (QR) code from the image.<br><br><u>Normalise Image</u><br>Attempts to normalise the image before parsing it to improve detection of a QR code.";
|
|
26
26
|
this.infoURL = "https://wikipedia.org/wiki/QR_code";
|
|
27
27
|
this.inputType = "ArrayBuffer";
|
|
28
28
|
this.outputType = "string";
|
|
29
29
|
this.args = [
|
|
30
30
|
{
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
31
|
+
name: "Normalise image",
|
|
32
|
+
type: "boolean",
|
|
33
|
+
value: false,
|
|
34
|
+
},
|
|
35
35
|
];
|
|
36
36
|
this.checks = [
|
|
37
37
|
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
pattern:
|
|
39
|
+
"^(?:\\xff\\xd8\\xff|\\x89\\x50\\x4e\\x47|\\x47\\x49\\x46|.{8}\\x57\\x45\\x42\\x50|\\x42\\x4d)",
|
|
40
|
+
flags: "",
|
|
41
|
+
args: [false],
|
|
42
|
+
useful: true,
|
|
43
|
+
},
|
|
43
44
|
];
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -54,9 +55,8 @@ class ParseQRCode extends Operation {
|
|
|
54
55
|
if (!isImage(input)) {
|
|
55
56
|
throw new OperationError("Invalid file type.");
|
|
56
57
|
}
|
|
57
|
-
return
|
|
58
|
+
return parseQrCode(input, normalise);
|
|
58
59
|
}
|
|
59
|
-
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export default ParseQRCode;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author cktgh [chankaitung@gmail.com]
|
|
3
|
+
* @copyright Crown Copyright 2026
|
|
4
|
+
* @license Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import Operation from "../Operation.mjs";
|
|
8
|
+
import OperationError from "../errors/OperationError.mjs";
|
|
9
|
+
import forge from "node-forge";
|
|
10
|
+
import Utils, { isWorkerEnvironment } from "../Utils.mjs";
|
|
11
|
+
import { DELIM_OPTIONS } from "../lib/Delim.mjs";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Pseudo-Random Integer Generator operation
|
|
15
|
+
*/
|
|
16
|
+
class PseudoRandomIntegerGenerator extends Operation {
|
|
17
|
+
|
|
18
|
+
// in theory 2**53 is the max range, but we use Number.MAX_SAFE_INTEGER (2**53 - 1) as it is more consistent.
|
|
19
|
+
static MAX_RANGE = Number.MAX_SAFE_INTEGER;
|
|
20
|
+
// arbitrary choice
|
|
21
|
+
static BUFFER_SIZE = 1024;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* PseudoRandomIntegerGenerator constructor
|
|
25
|
+
*/
|
|
26
|
+
constructor() {
|
|
27
|
+
super();
|
|
28
|
+
|
|
29
|
+
this.name = "Pseudo-Random Integer Generator";
|
|
30
|
+
this.module = "Ciphers";
|
|
31
|
+
this.description = "A cryptographically-secure pseudo-random number generator (PRNG).<br><br>Generates random integers within a specified range using the browser's built-in <code>crypto.getRandomValues()</code> method if available.<br><br>The supported range of integers is from <code>-(2^53 - 1)</code> to <code>(2^53 - 1)</code>.";
|
|
32
|
+
this.infoURL = "https://wikipedia.org/wiki/Pseudorandom_number_generator";
|
|
33
|
+
this.inputType = "string";
|
|
34
|
+
this.outputType = "string";
|
|
35
|
+
this.args = [
|
|
36
|
+
{
|
|
37
|
+
"name": "Number of Integers",
|
|
38
|
+
"type": "number",
|
|
39
|
+
"value": 1,
|
|
40
|
+
"min": 1
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "Min Value",
|
|
44
|
+
"type": "number",
|
|
45
|
+
"value": 0,
|
|
46
|
+
"min": Number.MIN_SAFE_INTEGER,
|
|
47
|
+
"max": Number.MAX_SAFE_INTEGER
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "Max Value",
|
|
51
|
+
"type": "number",
|
|
52
|
+
"value": 99,
|
|
53
|
+
"min": Number.MIN_SAFE_INTEGER,
|
|
54
|
+
"max": Number.MAX_SAFE_INTEGER
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "Delimiter",
|
|
58
|
+
"type": "option",
|
|
59
|
+
"value": DELIM_OPTIONS
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"name": "Output",
|
|
63
|
+
"type": "option",
|
|
64
|
+
"value": ["Raw", "Hex", "Decimal"]
|
|
65
|
+
}
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
// not using BigUint64Array to avoid BigInt handling overhead
|
|
69
|
+
this.randomBuffer = new Uint32Array(PseudoRandomIntegerGenerator.BUFFER_SIZE);
|
|
70
|
+
this.randomBufferOffset = PseudoRandomIntegerGenerator.BUFFER_SIZE;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {string} input
|
|
75
|
+
* @param {Object[]} args
|
|
76
|
+
* @returns {string}
|
|
77
|
+
*/
|
|
78
|
+
run(input, args) {
|
|
79
|
+
const [numInts, minInt, maxInt, delimiter, outputType] = args;
|
|
80
|
+
|
|
81
|
+
if (minInt === null || maxInt === null) return "";
|
|
82
|
+
|
|
83
|
+
const min = Math.ceil(minInt);
|
|
84
|
+
const max = Math.floor(maxInt);
|
|
85
|
+
const delim = Utils.charRep(delimiter || "Space");
|
|
86
|
+
|
|
87
|
+
if (!Number.isSafeInteger(min) || !Number.isSafeInteger(max)) {
|
|
88
|
+
throw new OperationError("Min and Max must be between `-(2^53 - 1)` and `2^53 - 1`.");
|
|
89
|
+
}
|
|
90
|
+
if (min > max) {
|
|
91
|
+
throw new OperationError("Min cannot be larger than Max.");
|
|
92
|
+
}
|
|
93
|
+
const range = max - min + 1; // inclusive range
|
|
94
|
+
if (range > PseudoRandomIntegerGenerator.MAX_RANGE) {
|
|
95
|
+
throw new OperationError("Range between Min and Max cannot be larger than `2^53`");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// as large as possible while divisible by range
|
|
99
|
+
const rejectionThreshold = PseudoRandomIntegerGenerator.MAX_RANGE - (PseudoRandomIntegerGenerator.MAX_RANGE % range);
|
|
100
|
+
const output = [];
|
|
101
|
+
for (let i = 0; i < numInts; i++) {
|
|
102
|
+
const result = this._generateRandomValue(rejectionThreshold);
|
|
103
|
+
const intValue = min + (result % range);
|
|
104
|
+
|
|
105
|
+
switch (outputType) {
|
|
106
|
+
case "Hex":
|
|
107
|
+
output.push(intValue.toString(16));
|
|
108
|
+
break;
|
|
109
|
+
case "Decimal":
|
|
110
|
+
output.push(intValue.toString(10));
|
|
111
|
+
break;
|
|
112
|
+
case "Raw":
|
|
113
|
+
default:
|
|
114
|
+
output.push(Utils.chr(intValue));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (outputType === "Raw") {
|
|
119
|
+
return output.join("");
|
|
120
|
+
}
|
|
121
|
+
return output.join(delim);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Generate a random value, result will be less than the rejection threshold (exclusive).
|
|
126
|
+
*
|
|
127
|
+
* @param {number} rejectionThreshold
|
|
128
|
+
* @returns {number}
|
|
129
|
+
*/
|
|
130
|
+
_generateRandomValue(rejectionThreshold) {
|
|
131
|
+
let result;
|
|
132
|
+
do {
|
|
133
|
+
if (this.randomBufferOffset + 2 > this.randomBuffer.length) {
|
|
134
|
+
this._resetRandomBuffer();
|
|
135
|
+
}
|
|
136
|
+
// stitching a 53 bit number; not using BigUint64Array to avoid BigInt handling overhead
|
|
137
|
+
result = (this.randomBuffer[this.randomBufferOffset++] & 0x1f_ffff) * 0x1_0000_0000 +
|
|
138
|
+
this.randomBuffer[this.randomBufferOffset++];
|
|
139
|
+
} while (result >= rejectionThreshold);
|
|
140
|
+
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Fill random buffer with new random values and rseet the offset.
|
|
146
|
+
*/
|
|
147
|
+
_resetRandomBuffer() {
|
|
148
|
+
if (isWorkerEnvironment() && self.crypto) {
|
|
149
|
+
self.crypto.getRandomValues(this.randomBuffer);
|
|
150
|
+
} else {
|
|
151
|
+
const bytes = forge.random.getBytesSync(this.randomBuffer.length * 4);
|
|
152
|
+
for (let j = 0; j < this.randomBuffer.length; j++) {
|
|
153
|
+
this.randomBuffer[j] = (bytes.charCodeAt(j * 4) << 24) |
|
|
154
|
+
(bytes.charCodeAt(j * 4 + 1) << 16) |
|
|
155
|
+
(bytes.charCodeAt(j * 4 + 2) << 8) |
|
|
156
|
+
bytes.charCodeAt(j * 4 + 3);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
this.randomBufferOffset = 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export default PseudoRandomIntegerGenerator;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Medjedtxm
|
|
3
|
+
* @copyright Crown Copyright 2026
|
|
4
|
+
* @license Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import Operation from "../Operation.mjs";
|
|
8
|
+
import Utils from "../Utils.mjs";
|
|
9
|
+
import OperationError from "../errors/OperationError.mjs";
|
|
10
|
+
import { toHex } from "../lib/Hex.mjs";
|
|
11
|
+
import { decryptRC6, getBlockSize, getDefaultRounds } from "../lib/RC6.mjs";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* RC6 Decrypt operation
|
|
15
|
+
*/
|
|
16
|
+
class RC6Decrypt extends Operation {
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* RC6Decrypt constructor
|
|
20
|
+
*/
|
|
21
|
+
constructor() {
|
|
22
|
+
super();
|
|
23
|
+
|
|
24
|
+
this.name = "RC6 Decrypt";
|
|
25
|
+
this.module = "Ciphers";
|
|
26
|
+
this.description = "RC6 is a symmetric key block cipher derived from RC5. It was designed by Ron Rivest, Matt Robshaw, Ray Sidney, and Yiqun Lisa Yin to meet the requirements of the AES competition, and was one of the five finalists.<br><br>RC6 is parameterised as RC6-w/r/b where w is word size in bits (any multiple of 8 from 8-256), r is the number of rounds (1-255), and b is the key length in bytes. The standard AES submission uses w=32, r=20. Common word sizes: 8, 16, 32 (standard), 64, 128.<br><br><b>IV:</b> The Initialisation Vector should be 4*w/8 bytes (e.g. 16 bytes for w=32). If not entered, it will default to null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, the PKCS#7 padding scheme is used.";
|
|
27
|
+
this.infoURL = "https://wikipedia.org/wiki/RC6";
|
|
28
|
+
this.inputType = "string";
|
|
29
|
+
this.outputType = "string";
|
|
30
|
+
this.args = [
|
|
31
|
+
{
|
|
32
|
+
"name": "Key",
|
|
33
|
+
"type": "toggleString",
|
|
34
|
+
"value": "",
|
|
35
|
+
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "IV",
|
|
39
|
+
"type": "toggleString",
|
|
40
|
+
"value": "",
|
|
41
|
+
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "Mode",
|
|
45
|
+
"type": "option",
|
|
46
|
+
"value": ["CBC", "CFB", "OFB", "CTR", "ECB"]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "Input",
|
|
50
|
+
"type": "option",
|
|
51
|
+
"value": ["Hex", "Raw"]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"name": "Output",
|
|
55
|
+
"type": "option",
|
|
56
|
+
"value": ["Raw", "Hex"]
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "Padding",
|
|
60
|
+
"type": "option",
|
|
61
|
+
"value": ["PKCS5", "NO", "ZERO", "RANDOM", "BIT"]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "Word Size",
|
|
65
|
+
"type": "number",
|
|
66
|
+
"value": 32,
|
|
67
|
+
"min": 8,
|
|
68
|
+
"max": 256,
|
|
69
|
+
"step": 8
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "Rounds",
|
|
73
|
+
"type": "number",
|
|
74
|
+
"value": 20,
|
|
75
|
+
"min": 1,
|
|
76
|
+
"max": 255
|
|
77
|
+
}
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {string} input
|
|
83
|
+
* @param {Object[]} args
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
run(input, args) {
|
|
87
|
+
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
|
88
|
+
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
|
89
|
+
[,, mode, inputType, outputType, padding, wordSize, rounds] = args;
|
|
90
|
+
|
|
91
|
+
// Validate word size
|
|
92
|
+
if (!Number.isInteger(wordSize) || wordSize < 8 || wordSize > 256 || wordSize % 8 !== 0)
|
|
93
|
+
throw new OperationError(`Invalid word size: ${wordSize}. Must be a multiple of 8 between 8 and 256.`);
|
|
94
|
+
|
|
95
|
+
const blockSize = getBlockSize(wordSize);
|
|
96
|
+
const defaultRounds = getDefaultRounds(wordSize);
|
|
97
|
+
|
|
98
|
+
if (iv.length !== blockSize && iv.length !== 0 && mode !== "ECB")
|
|
99
|
+
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
|
100
|
+
|
|
101
|
+
RC6-${wordSize} uses an IV length of ${blockSize} bytes (${blockSize * 8} bits).
|
|
102
|
+
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
|
|
103
|
+
|
|
104
|
+
if (!Number.isInteger(rounds) || rounds < 1 || rounds > 255)
|
|
105
|
+
throw new OperationError(`Invalid number of rounds: ${rounds}
|
|
106
|
+
|
|
107
|
+
Rounds must be an integer between 1 and 255. Standard for w=${wordSize} is ${defaultRounds}.`);
|
|
108
|
+
|
|
109
|
+
// Default IV to null bytes if empty (like AES)
|
|
110
|
+
const actualIv = iv.length === 0 ? new Array(blockSize).fill(0) : iv;
|
|
111
|
+
|
|
112
|
+
input = Utils.convertToByteArray(input, inputType);
|
|
113
|
+
const output = decryptRC6(input, key, actualIv, mode, padding, rounds, wordSize);
|
|
114
|
+
return outputType === "Hex" ? toHex(output, "") : Utils.byteArrayToUtf8(output);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export default RC6Decrypt;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Medjedtxm
|
|
3
|
+
* @copyright Crown Copyright 2026
|
|
4
|
+
* @license Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import Operation from "../Operation.mjs";
|
|
8
|
+
import Utils from "../Utils.mjs";
|
|
9
|
+
import OperationError from "../errors/OperationError.mjs";
|
|
10
|
+
import { toHex } from "../lib/Hex.mjs";
|
|
11
|
+
import { encryptRC6, getBlockSize, getDefaultRounds } from "../lib/RC6.mjs";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* RC6 Encrypt operation
|
|
15
|
+
*/
|
|
16
|
+
class RC6Encrypt extends Operation {
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* RC6Encrypt constructor
|
|
20
|
+
*/
|
|
21
|
+
constructor() {
|
|
22
|
+
super();
|
|
23
|
+
|
|
24
|
+
this.name = "RC6 Encrypt";
|
|
25
|
+
this.module = "Ciphers";
|
|
26
|
+
this.description = "RC6 is a symmetric key block cipher derived from RC5. It was designed by Ron Rivest, Matt Robshaw, Ray Sidney, and Yiqun Lisa Yin to meet the requirements of the AES competition, and was one of the five finalists.<br><br>RC6 is parameterised as RC6-w/r/b where w is word size in bits (any multiple of 8 from 8-256), r is the number of rounds (1-255), and b is the key length in bytes. The standard AES submission uses w=32, r=20. Common word sizes: 8, 16, 32 (standard), 64, 128.<br><br><b>IV:</b> The Initialisation Vector should be 4*w/8 bytes (e.g. 16 bytes for w=32). If not entered, it will default to null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, the PKCS#7 padding scheme is used.";
|
|
27
|
+
this.infoURL = "https://wikipedia.org/wiki/RC6";
|
|
28
|
+
this.inputType = "string";
|
|
29
|
+
this.outputType = "string";
|
|
30
|
+
this.args = [
|
|
31
|
+
{
|
|
32
|
+
"name": "Key",
|
|
33
|
+
"type": "toggleString",
|
|
34
|
+
"value": "",
|
|
35
|
+
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "IV",
|
|
39
|
+
"type": "toggleString",
|
|
40
|
+
"value": "",
|
|
41
|
+
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "Mode",
|
|
45
|
+
"type": "option",
|
|
46
|
+
"value": ["CBC", "CFB", "OFB", "CTR", "ECB"]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "Input",
|
|
50
|
+
"type": "option",
|
|
51
|
+
"value": ["Raw", "Hex"]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"name": "Output",
|
|
55
|
+
"type": "option",
|
|
56
|
+
"value": ["Hex", "Raw"]
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "Padding",
|
|
60
|
+
"type": "option",
|
|
61
|
+
"value": ["PKCS5", "NO", "ZERO", "RANDOM", "BIT"]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "Word Size",
|
|
65
|
+
"type": "number",
|
|
66
|
+
"value": 32,
|
|
67
|
+
"min": 8,
|
|
68
|
+
"max": 256,
|
|
69
|
+
"step": 8
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "Rounds",
|
|
73
|
+
"type": "number",
|
|
74
|
+
"value": 20,
|
|
75
|
+
"min": 1,
|
|
76
|
+
"max": 255
|
|
77
|
+
}
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {string} input
|
|
83
|
+
* @param {Object[]} args
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
run(input, args) {
|
|
87
|
+
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
|
88
|
+
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
|
89
|
+
[,, mode, inputType, outputType, padding, wordSize, rounds] = args;
|
|
90
|
+
|
|
91
|
+
// Validate word size
|
|
92
|
+
if (!Number.isInteger(wordSize) || wordSize < 8 || wordSize > 256 || wordSize % 8 !== 0)
|
|
93
|
+
throw new OperationError(`Invalid word size: ${wordSize}. Must be a multiple of 8 between 8 and 256.`);
|
|
94
|
+
|
|
95
|
+
const blockSize = getBlockSize(wordSize);
|
|
96
|
+
const defaultRounds = getDefaultRounds(wordSize);
|
|
97
|
+
|
|
98
|
+
if (iv.length !== blockSize && iv.length !== 0 && mode !== "ECB")
|
|
99
|
+
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
|
100
|
+
|
|
101
|
+
RC6-${wordSize} uses an IV length of ${blockSize} bytes (${blockSize * 8} bits).
|
|
102
|
+
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
|
|
103
|
+
|
|
104
|
+
if (!Number.isInteger(rounds) || rounds < 1 || rounds > 255)
|
|
105
|
+
throw new OperationError(`Invalid number of rounds: ${rounds}
|
|
106
|
+
|
|
107
|
+
Rounds must be an integer between 1 and 255. Standard for w=${wordSize} is ${defaultRounds}.`);
|
|
108
|
+
|
|
109
|
+
// Default IV to null bytes if empty (like AES)
|
|
110
|
+
const actualIv = iv.length === 0 ? new Array(blockSize).fill(0) : iv;
|
|
111
|
+
|
|
112
|
+
input = Utils.convertToByteArray(input, inputType);
|
|
113
|
+
const output = encryptRC6(input, key, actualIv, mode, padding, rounds, wordSize);
|
|
114
|
+
return outputType === "Hex" ? toHex(output, "") : Utils.byteArrayToUtf8(output);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export default RC6Encrypt;
|