cyberchef 11.2.0 → 11.3.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/AGENTS.md +75 -0
- package/CHANGELOG.md +147 -1
- package/Dockerfile +2 -2
- package/Gruntfile.js +1 -0
- package/package.json +22 -21
- package/src/core/Dish.mjs +1 -5
- package/src/core/Ingredient.mjs +92 -0
- package/src/core/Operation.mjs +19 -0
- package/src/core/Recipe.mjs +4 -0
- package/src/core/Utils.mjs +48 -0
- package/src/core/config/Categories.json +22 -6
- package/src/core/config/OperationConfig.json +4771 -1226
- package/src/core/config/modules/Ciphers.mjs +20 -0
- package/src/core/config/modules/Crypto.mjs +10 -0
- package/src/core/config/modules/Default.mjs +8 -0
- package/src/core/config/modules/File.mjs +16 -0
- package/src/core/config/modules/OpModules.mjs +2 -0
- package/src/core/config/scripts/generateHTMLEntities.mjs +139 -0
- package/src/core/config/scripts/htmlEntityOverrides.mjs +86 -0
- package/src/core/lib/Arithmetic.mjs +21 -5
- package/src/core/lib/BigIntUtils.mjs +0 -1
- package/src/core/lib/COBS.mjs +86 -0
- package/src/core/lib/Decimal.mjs +5 -5
- package/src/core/lib/HTMLEntities.mjs +2068 -0
- package/src/core/lib/Present.mjs +422 -0
- package/src/core/lib/TEA.mjs +494 -0
- package/src/core/lib/TLVParser.mjs +16 -7
- package/src/core/lib/Twofish.mjs +608 -0
- package/src/core/operations/AsconDecrypt.mjs +112 -0
- package/src/core/operations/AsconEncrypt.mjs +108 -0
- package/src/core/operations/AsconHash.mjs +49 -0
- package/src/core/operations/AsconMAC.mjs +68 -0
- package/src/core/operations/AutomatedValidationTestOp.mjs +84 -0
- package/src/core/operations/AvroToJSON.mjs +1 -1
- package/src/core/operations/BLAKE3.mjs +5 -1
- package/src/core/operations/BcryptCompare.mjs +11 -5
- package/src/core/operations/BitShiftLeft.mjs +4 -1
- package/src/core/operations/Bzip2Compress.mjs +1 -1
- package/src/core/operations/DechunkHTTPResponse.mjs +4 -1
- package/src/core/operations/ExtendedGCD.mjs +101 -0
- package/src/core/operations/FromBase.mjs +2 -1
- package/src/core/operations/FromCOBS.mjs +38 -0
- package/src/core/operations/FromDecimal.mjs +6 -1
- package/src/core/operations/FromHTMLEntity.mjs +2 -1458
- package/src/core/operations/GenerateHOTP.mjs +21 -6
- package/src/core/operations/GenerateImage.mjs +22 -10
- package/src/core/operations/GeneratePrime.mjs +154 -0
- package/src/core/operations/GenerateTOTP.mjs +24 -7
- package/src/core/operations/Gzip.mjs +1 -3
- package/src/core/operations/Jsonata.mjs +12 -0
- package/src/core/operations/MIMEDecoding.mjs +1 -1
- package/src/core/operations/MOD.mjs +62 -0
- package/src/core/operations/ModularInverse.mjs +107 -0
- package/src/core/operations/PRESENTDecrypt.mjs +94 -0
- package/src/core/operations/PRESENTEncrypt.mjs +94 -0
- package/src/core/operations/ParseURI.mjs +18 -5
- package/src/core/operations/PseudoRandomNumberGenerator.mjs +2 -1
- package/src/core/operations/RandomPrime.mjs +154 -0
- package/src/core/operations/RenderPDF.mjs +100 -0
- package/src/core/operations/SM4Encrypt.mjs +1 -1
- package/src/core/operations/SetDifference.mjs +8 -1
- package/src/core/operations/SetIntersection.mjs +8 -1
- package/src/core/operations/ShowOnMap.mjs +14 -2
- package/src/core/operations/TEADecrypt.mjs +98 -0
- package/src/core/operations/TEAEncrypt.mjs +98 -0
- package/src/core/operations/ToBase.mjs +4 -4
- package/src/core/operations/ToBase32.mjs +20 -4
- package/src/core/operations/ToBinary.mjs +4 -1
- package/src/core/operations/ToCOBS.mjs +38 -0
- package/src/core/operations/ToHTMLEntity.mjs +7 -1430
- package/src/core/operations/TwofishDecrypt.mjs +94 -0
- package/src/core/operations/TwofishEncrypt.mjs +94 -0
- package/src/core/operations/URLEncode.mjs +22 -18
- package/src/core/operations/UnescapeUnicodeCharacters.mjs +2 -1
- package/src/core/operations/ViewBitPlane.mjs +9 -3
- package/src/core/operations/Wrap.mjs +5 -0
- package/src/core/operations/XORBruteForce.mjs +4 -1
- package/src/core/operations/XORChecksum.mjs +2 -2
- package/src/core/operations/XTEADecrypt.mjs +110 -0
- package/src/core/operations/XTEAEncrypt.mjs +110 -0
- package/src/core/operations/index.mjs +42 -0
- package/src/core/vendor/ascon.mjs +162 -0
- package/src/core/vendor/htmlEntities/entity.json +2233 -0
- package/src/core/vendor/htmlEntities/entity.txt +14 -0
- package/src/node/api.mjs +4 -1
- package/src/node/index.mjs +105 -0
- package/src/web/HTMLOperation.mjs +2 -1
- package/src/web/stylesheets/layout/_io.css +8 -0
- package/tests/browser/00_nightwatch.js +26 -0
- package/tests/browser/02_ops.js +20 -4
- package/tests/node/index.mjs +1 -0
- package/tests/node/tests/Dish.mjs +19 -0
- package/tests/node/tests/NodeDish.mjs +36 -0
- package/tests/node/tests/ToHTMLEntity.mjs +82 -0
- package/tests/node/tests/Utils.mjs +77 -0
- package/tests/node/tests/nodeApi.mjs +33 -1
- package/tests/node/tests/operations.mjs +26 -1
- package/tests/operations/index.mjs +17 -0
- package/tests/operations/tests/Arithmetic.mjs +33 -0
- package/tests/operations/tests/Ascon.mjs +501 -0
- package/tests/operations/tests/AutomatedValidation.mjs +154 -0
- package/tests/operations/tests/BLAKE3.mjs +39 -1
- package/tests/operations/tests/Base32.mjs +22 -0
- package/tests/operations/tests/COBS.mjs +476 -0
- package/tests/operations/tests/CharEnc.mjs +2 -2
- package/tests/operations/tests/DechunkHTTPResponse.mjs +66 -0
- package/tests/operations/tests/ExtendedGCD.mjs +78 -0
- package/tests/operations/tests/FromBase.mjs +66 -0
- package/tests/operations/tests/FromDecimal.mjs +55 -0
- package/tests/operations/tests/GenerateLoremIpsum.mjs +2 -2
- package/tests/operations/tests/Gzip.mjs +60 -0
- package/tests/operations/tests/HTMLEntity.mjs +126 -0
- package/tests/operations/tests/Hash.mjs +44 -0
- package/tests/operations/tests/Hexdump.mjs +1 -1
- package/tests/operations/tests/Image.mjs +26 -0
- package/tests/operations/tests/Jsonata.mjs +23 -0
- package/tests/operations/tests/MIMEDecoding.mjs +33 -0
- package/tests/operations/tests/MOD.mjs +208 -0
- package/tests/operations/tests/Median.mjs +33 -0
- package/tests/operations/tests/ModularInverse.mjs +78 -0
- package/tests/operations/tests/OTP.mjs +167 -2
- package/tests/operations/tests/PRESENT.mjs +465 -0
- package/tests/operations/tests/ParseTLV.mjs +41 -0
- package/tests/operations/tests/RenderPDF.mjs +55 -0
- package/tests/operations/tests/SM2.mjs +1 -1
- package/tests/operations/tests/SetDifference.mjs +22 -0
- package/tests/operations/tests/SetIntersection.mjs +23 -1
- package/tests/operations/tests/ShowOnMap.mjs +61 -0
- package/tests/operations/tests/TEA.mjs +566 -0
- package/tests/operations/tests/Twofish.mjs +486 -0
- package/tests/operations/tests/URLEncodeDecode.mjs +26 -0
- package/tests/operations/tests/UnescapeUnicodeCharacters.mjs +88 -0
- package/tests/operations/tests/Wrap.mjs +44 -0
- package/webpack.config.js +3 -3
|
@@ -0,0 +1,94 @@
|
|
|
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 { encryptPRESENT } from "../lib/Present.mjs";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* PRESENT Encrypt operation
|
|
15
|
+
*/
|
|
16
|
+
class PRESENTEncrypt extends Operation {
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* PRESENTEncrypt constructor
|
|
20
|
+
*/
|
|
21
|
+
constructor() {
|
|
22
|
+
super();
|
|
23
|
+
|
|
24
|
+
this.name = "PRESENT Encrypt";
|
|
25
|
+
this.module = "Ciphers";
|
|
26
|
+
this.description = "PRESENT is an ultra-lightweight block cipher designed for constrained environments such as RFID tags and sensor networks. It operates on 64-bit blocks and supports 80-bit or 128-bit keys with 31 rounds. Standardised in ISO/IEC 29192-2:2019.<br><br>When using CBC mode, the PKCS#7 padding scheme is used.";
|
|
27
|
+
this.infoURL = "https://wikipedia.org/wiki/PRESENT_(cipher)";
|
|
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", "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
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {string} input
|
|
68
|
+
* @param {Object[]} args
|
|
69
|
+
* @returns {string}
|
|
70
|
+
*/
|
|
71
|
+
run(input, args) {
|
|
72
|
+
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
|
73
|
+
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
|
74
|
+
[,, mode, inputType, outputType, padding] = args;
|
|
75
|
+
|
|
76
|
+
if (key.length !== 10 && key.length !== 16)
|
|
77
|
+
throw new OperationError(`Invalid key length: ${key.length} bytes
|
|
78
|
+
|
|
79
|
+
PRESENT uses a key length of 10 bytes (80 bits) or 16 bytes (128 bits).`);
|
|
80
|
+
|
|
81
|
+
if (iv.length !== 8 && mode !== "ECB")
|
|
82
|
+
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
|
83
|
+
|
|
84
|
+
PRESENT uses an IV length of 8 bytes (64 bits).
|
|
85
|
+
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
|
|
86
|
+
|
|
87
|
+
input = Utils.convertToByteArray(input, inputType);
|
|
88
|
+
const output = encryptPRESENT(input, key, iv, mode, padding);
|
|
89
|
+
return outputType === "Hex" ? toHex(output, "") : Utils.byteArrayToUtf8(output);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default PRESENTEncrypt;
|
|
@@ -33,7 +33,7 @@ class ParseURI extends Operation {
|
|
|
33
33
|
* @returns {string}
|
|
34
34
|
*/
|
|
35
35
|
run(input, args) {
|
|
36
|
-
const uri = url.parse(input,
|
|
36
|
+
const uri = url.parse(input, false);
|
|
37
37
|
|
|
38
38
|
let output = "";
|
|
39
39
|
|
|
@@ -43,7 +43,20 @@ class ParseURI extends Operation {
|
|
|
43
43
|
if (uri.port) output += "Port:\t\t" + uri.port + "\n";
|
|
44
44
|
if (uri.pathname) output += "Path name:\t" + uri.pathname + "\n";
|
|
45
45
|
if (uri.query) {
|
|
46
|
-
const
|
|
46
|
+
const queryObj = Object.create(null);
|
|
47
|
+
for (const [key, value] of new URLSearchParams(uri.query)) {
|
|
48
|
+
if (Object.prototype.hasOwnProperty.call(queryObj, key)) {
|
|
49
|
+
if (Array.isArray(queryObj[key])) {
|
|
50
|
+
queryObj[key].push(value);
|
|
51
|
+
} else {
|
|
52
|
+
queryObj[key] = [queryObj[key], value];
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
queryObj[key] = value;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const keys = Object.keys(queryObj);
|
|
47
60
|
let padding = 0;
|
|
48
61
|
|
|
49
62
|
keys.forEach(k => {
|
|
@@ -51,10 +64,10 @@ class ParseURI extends Operation {
|
|
|
51
64
|
});
|
|
52
65
|
|
|
53
66
|
output += "Arguments:\n";
|
|
54
|
-
for (const key in
|
|
67
|
+
for (const key in queryObj) {
|
|
55
68
|
output += "\t" + key.padEnd(padding, " ");
|
|
56
|
-
if (
|
|
57
|
-
output += " = " +
|
|
69
|
+
if (queryObj[key].length) {
|
|
70
|
+
output += " = " + queryObj[key] + "\n";
|
|
58
71
|
} else {
|
|
59
72
|
output += "\n";
|
|
60
73
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author p-leriche [philip.leriche@cantab.net]
|
|
3
|
+
* @copyright Crown Copyright 2025
|
|
4
|
+
* @license Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import Operation from "../Operation.mjs";
|
|
8
|
+
import OperationError from "../errors/OperationError.mjs";
|
|
9
|
+
import { modPow } from "../lib/BigIntUtils.mjs";
|
|
10
|
+
|
|
11
|
+
/* ---------- helper functions ---------- */
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Generate random BigInt with specified bit length
|
|
15
|
+
*/
|
|
16
|
+
function randBigInt(bits) {
|
|
17
|
+
const bytes = Math.ceil(bits / 8);
|
|
18
|
+
const a = new Uint8Array(bytes);
|
|
19
|
+
crypto.getRandomValues(a);
|
|
20
|
+
|
|
21
|
+
// Set high bit to ensure correct bit length
|
|
22
|
+
a[0] |= 1 << (7 - ((8 * bytes - bits)));
|
|
23
|
+
// Set low bit to ensure odd (primes > 2 are odd)
|
|
24
|
+
a[bytes - 1] |= 1;
|
|
25
|
+
|
|
26
|
+
let h = "";
|
|
27
|
+
for (const b of a) h += b.toString(16).padStart(2, "0");
|
|
28
|
+
return BigInt("0x" + h);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Miller-Rabin primality test
|
|
33
|
+
*/
|
|
34
|
+
function isProbablePrime(n, rounds) {
|
|
35
|
+
if (n < 2n) return false;
|
|
36
|
+
if (n === 2n || n === 3n) return true;
|
|
37
|
+
if (n % 2n === 0n) return false;
|
|
38
|
+
|
|
39
|
+
// Write n-1 as 2^r * d
|
|
40
|
+
let d = n - 1n;
|
|
41
|
+
let r = 0n;
|
|
42
|
+
while (d % 2n === 0n) {
|
|
43
|
+
d /= 2n;
|
|
44
|
+
r++;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Witness loop
|
|
48
|
+
for (let i = 0; i < rounds; i++) {
|
|
49
|
+
const a = randBigInt(n.toString(2).length - 1) % (n - 3n) + 2n;
|
|
50
|
+
let x = modPow(a, d, n);
|
|
51
|
+
|
|
52
|
+
if (x === 1n || x === n - 1n) continue;
|
|
53
|
+
|
|
54
|
+
let composite = true;
|
|
55
|
+
for (let j = 0n; j < r - 1n; j++) {
|
|
56
|
+
x = modPow(x, 2n, n);
|
|
57
|
+
if (x === n - 1n) {
|
|
58
|
+
composite = false;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (composite) return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* ---------- operation class ---------- */
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Generate Prime Number operation
|
|
73
|
+
*/
|
|
74
|
+
class GeneratePrime extends Operation {
|
|
75
|
+
/**
|
|
76
|
+
* GeneratePrime constructor
|
|
77
|
+
*/
|
|
78
|
+
constructor() {
|
|
79
|
+
super();
|
|
80
|
+
|
|
81
|
+
this.name = "Pseudo-Random Prime Generator";
|
|
82
|
+
this.module = "Crypto";
|
|
83
|
+
this.description =
|
|
84
|
+
"Generates a random probable prime number of specified bit length using the Miller-Rabin primality test.<br><br>" +
|
|
85
|
+
"<b>Primality guarantee:</b><br>" +
|
|
86
|
+
"For numbers . 3,317, the result is guaranteed prime (deterministic test).<br>" +
|
|
87
|
+
"For larger numbers, uses probabilistic testing:<br>" +
|
|
88
|
+
"- <b>Standard (7 rounds):</b> Probability of composite approx 1 in 16,000)<br><br>" +
|
|
89
|
+
"- <b>Crypto grade (40 rounds):</b> Probability of composite(approx 1 in 10^24)<br>" +
|
|
90
|
+
"Crypto grade is recommended for cryptographic applications (RSA, Diffie-Hellman, etc.).<br><br>" ;
|
|
91
|
+
this.infoURL = "https://wikipedia.org/wiki/Miller-Rabin_primality_test";
|
|
92
|
+
this.inputType = "string";
|
|
93
|
+
this.outputType = "string";
|
|
94
|
+
this.args = [
|
|
95
|
+
{
|
|
96
|
+
name: "Bit length",
|
|
97
|
+
type: "number",
|
|
98
|
+
value: 512,
|
|
99
|
+
min: 2
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "Crypto grade",
|
|
103
|
+
type: "boolean",
|
|
104
|
+
value: false
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "Output format",
|
|
108
|
+
type: "option",
|
|
109
|
+
value: ["Decimal", "Hexadecimal"]
|
|
110
|
+
}
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param {string} input
|
|
116
|
+
* @param {Object[]} args
|
|
117
|
+
* @returns {string}
|
|
118
|
+
*/
|
|
119
|
+
run(input, args) {
|
|
120
|
+
const [bits, cryptoGrade, outputFormat] = args;
|
|
121
|
+
|
|
122
|
+
if (bits < 2) {
|
|
123
|
+
throw new OperationError("Bit length must be at least 2");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (bits > 4096) {
|
|
127
|
+
throw new OperationError("Bit length limited to 4096 bits for performance reasons");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const rounds = cryptoGrade ? 40 : 7;
|
|
131
|
+
let attempts = 0;
|
|
132
|
+
const maxAttempts = 10000;
|
|
133
|
+
|
|
134
|
+
let n = randBigInt(bits);
|
|
135
|
+
|
|
136
|
+
while (!isProbablePrime(n, rounds)) {
|
|
137
|
+
n = randBigInt(bits);
|
|
138
|
+
attempts++;
|
|
139
|
+
|
|
140
|
+
if (attempts > maxAttempts) {
|
|
141
|
+
throw new OperationError(`Failed to generate prime after ${maxAttempts} attempts. Try a different bit length.`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Return only the prime for pipeability
|
|
146
|
+
if (outputFormat === "Hexadecimal") {
|
|
147
|
+
return "0x" + n.toString(16);
|
|
148
|
+
} else {
|
|
149
|
+
return n.toString();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export default GeneratePrime;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Shailendra [singhshailendra.in]
|
|
3
|
+
* @copyright Crown Copyright 2017
|
|
4
|
+
* @license Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { fromBase64, toBase64 } from "../lib/Base64.mjs";
|
|
8
|
+
import Operation from "../Operation.mjs";
|
|
9
|
+
import OperationError from "../errors/OperationError.mjs";
|
|
10
|
+
import Utils from "../Utils.mjs";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Render PDF operation
|
|
14
|
+
*/
|
|
15
|
+
class RenderPDF extends Operation {
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* RenderPDF constructor
|
|
19
|
+
*/
|
|
20
|
+
constructor() {
|
|
21
|
+
super();
|
|
22
|
+
|
|
23
|
+
this.name = "Render PDF";
|
|
24
|
+
this.module = "File";
|
|
25
|
+
this.description = "Displays the input as a PDF preview. Supports Raw and Base64 input formats.";
|
|
26
|
+
this.inputType = "string";
|
|
27
|
+
this.outputType = "byteArray";
|
|
28
|
+
this.presentType = "html";
|
|
29
|
+
this.args = [
|
|
30
|
+
{
|
|
31
|
+
"name": "Input format",
|
|
32
|
+
"type": "option",
|
|
33
|
+
"value": ["Base64", "Raw"],
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
this.checks = [
|
|
37
|
+
{
|
|
38
|
+
pattern: "^%PDF-",
|
|
39
|
+
flags: "",
|
|
40
|
+
args: ["Raw"],
|
|
41
|
+
useful: true,
|
|
42
|
+
output: {
|
|
43
|
+
mime: "application/pdf"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {string} input
|
|
51
|
+
* @param {Object[]} args
|
|
52
|
+
* @returns {byteArray}
|
|
53
|
+
*/
|
|
54
|
+
run(input, args) {
|
|
55
|
+
const inputFormat = args[0];
|
|
56
|
+
|
|
57
|
+
if (!input.length) return [];
|
|
58
|
+
|
|
59
|
+
// Convert input to raw bytes
|
|
60
|
+
switch (inputFormat) {
|
|
61
|
+
case "Base64":
|
|
62
|
+
input = fromBase64(input, undefined, "byteArray");
|
|
63
|
+
break;
|
|
64
|
+
case "Raw":
|
|
65
|
+
default:
|
|
66
|
+
input = Utils.strToByteArray(input);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Check PDF signature
|
|
71
|
+
if (
|
|
72
|
+
input[0] !== 0x25 || // %
|
|
73
|
+
input[1] !== 0x50 || // P
|
|
74
|
+
input[2] !== 0x44 || // D
|
|
75
|
+
input[3] !== 0x46 // F
|
|
76
|
+
) {
|
|
77
|
+
throw new OperationError("Input does not appear to be a PDF file.");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return input;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Displays the PDF using HTML for web apps.
|
|
85
|
+
*
|
|
86
|
+
* @param {byteArray} data
|
|
87
|
+
* @returns {html}
|
|
88
|
+
*/
|
|
89
|
+
async present(data) {
|
|
90
|
+
if (!data.length) return "";
|
|
91
|
+
|
|
92
|
+
const base64 = toBase64(data);
|
|
93
|
+
const dataURI = "data:application/pdf;base64," + base64;
|
|
94
|
+
|
|
95
|
+
return `<iframe src="${dataURI}" style="width:100%;height:100%;border:1px solid #ccc;"></iframe>`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export default RenderPDF;
|
|
@@ -75,9 +75,16 @@ class SetDifference extends Operation {
|
|
|
75
75
|
* @returns {Object[]}
|
|
76
76
|
*/
|
|
77
77
|
runSetDifference(a, b) {
|
|
78
|
+
const excluded = new Set(b);
|
|
79
|
+
const seen = new Set();
|
|
80
|
+
|
|
78
81
|
return a
|
|
79
82
|
.filter((item) => {
|
|
80
|
-
|
|
83
|
+
if (excluded.has(item) || seen.has(item)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
seen.add(item);
|
|
87
|
+
return true;
|
|
81
88
|
})
|
|
82
89
|
.join(this.itemDelimiter);
|
|
83
90
|
}
|
|
@@ -75,9 +75,16 @@ class SetIntersection extends Operation {
|
|
|
75
75
|
* @returns {Object[]}
|
|
76
76
|
*/
|
|
77
77
|
runIntersect(a, b) {
|
|
78
|
+
const included = new Set(b);
|
|
79
|
+
const seen = new Set();
|
|
80
|
+
|
|
78
81
|
return a
|
|
79
82
|
.filter((item) => {
|
|
80
|
-
|
|
83
|
+
if (!included.has(item) || seen.has(item)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
seen.add(item);
|
|
87
|
+
return true;
|
|
81
88
|
})
|
|
82
89
|
.join(this.itemDelimiter);
|
|
83
90
|
}
|
|
@@ -36,7 +36,8 @@ class ShowOnMap extends Operation {
|
|
|
36
36
|
{
|
|
37
37
|
name: "Input Format",
|
|
38
38
|
type: "option",
|
|
39
|
-
value: ["Auto"].concat(FORMATS)
|
|
39
|
+
value: ["Auto"].concat(FORMATS),
|
|
40
|
+
allowEmpty: false
|
|
40
41
|
},
|
|
41
42
|
{
|
|
42
43
|
name: "Input Delimiter",
|
|
@@ -49,7 +50,8 @@ class ShowOnMap extends Operation {
|
|
|
49
50
|
"Comma",
|
|
50
51
|
"Semi-colon",
|
|
51
52
|
"Colon"
|
|
52
|
-
]
|
|
53
|
+
],
|
|
54
|
+
allowEmpty: false
|
|
53
55
|
}
|
|
54
56
|
];
|
|
55
57
|
}
|
|
@@ -71,6 +73,16 @@ class ShowOnMap extends Operation {
|
|
|
71
73
|
}
|
|
72
74
|
latLong = latLong.replace(/[,]$/, "");
|
|
73
75
|
latLong = latLong.replace(/°/g, "");
|
|
76
|
+
|
|
77
|
+
// The map requires a latitude and longitude pair. If the conversion only produced a
|
|
78
|
+
// single value (e.g. because the chosen input delimiter didn't match the input), bail
|
|
79
|
+
// out with a helpful message rather than passing it on to the map, which would throw an
|
|
80
|
+
// uncaught TypeError in the browser.
|
|
81
|
+
const coords = latLong.split(",").map(v => v.trim());
|
|
82
|
+
if (coords.length !== 2 || coords.some(v => v === "" || isNaN(Number(v)))) {
|
|
83
|
+
throw new OperationError(`Could not show coordinates '${latLong}' on the map. Expected a latitude and longitude pair - check that the input format and delimiter are correct.`);
|
|
84
|
+
}
|
|
85
|
+
|
|
74
86
|
return latLong;
|
|
75
87
|
}
|
|
76
88
|
return input;
|
|
@@ -0,0 +1,98 @@
|
|
|
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 { decryptTEA, TEA_BLOCK_SIZE } from "../lib/TEA.mjs";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* TEA Decrypt operation
|
|
15
|
+
*/
|
|
16
|
+
class TEADecrypt extends Operation {
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* TEADecrypt constructor
|
|
20
|
+
*/
|
|
21
|
+
constructor() {
|
|
22
|
+
super();
|
|
23
|
+
|
|
24
|
+
this.name = "TEA Decrypt";
|
|
25
|
+
this.module = "Ciphers";
|
|
26
|
+
this.description = "TEA (Tiny Encryption Algorithm) is a block cipher designed by David Wheeler and Roger Needham in 1994. It operates on 64-bit blocks using a 128-bit key and performs 32 cycles (64 Feistel rounds) with the DELTA constant 0x9E3779B9 derived from the golden ratio.<br><br>TEA is notable for its simplicity and compact implementation, making it frequently encountered in malware analysis and CTF challenges. Despite its elegance, TEA has known weaknesses including equivalent keys and susceptibility to related-key attacks, leading to successors XTEA and XXTEA.<br><br><b>Key:</b> Must be exactly 16 bytes (128 bits).<br><br><b>IV:</b> The Initialisation Vector should be 8 bytes (64 bits). If not entered, it will default to null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, the PKCS#5 padding scheme is used.";
|
|
27
|
+
this.infoURL = "https://wikipedia.org/wiki/Tiny_Encryption_Algorithm";
|
|
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
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {string} input
|
|
68
|
+
* @param {Object[]} args
|
|
69
|
+
* @returns {string}
|
|
70
|
+
*/
|
|
71
|
+
run(input, args) {
|
|
72
|
+
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
|
73
|
+
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
|
74
|
+
[,, mode, inputType, outputType, padding] = args;
|
|
75
|
+
|
|
76
|
+
if (key.length !== 16)
|
|
77
|
+
throw new OperationError(`Invalid key length: ${key.length} bytes
|
|
78
|
+
|
|
79
|
+
TEA requires a key length of 16 bytes (128 bits).
|
|
80
|
+
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
|
|
81
|
+
|
|
82
|
+
if (iv.length !== TEA_BLOCK_SIZE && iv.length !== 0 && mode !== "ECB")
|
|
83
|
+
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
|
84
|
+
|
|
85
|
+
TEA uses an IV length of ${TEA_BLOCK_SIZE} bytes (${TEA_BLOCK_SIZE * 8} bits).
|
|
86
|
+
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
|
|
87
|
+
|
|
88
|
+
// Default IV to null bytes if empty (like AES)
|
|
89
|
+
const actualIv = iv.length === 0 ? new Array(TEA_BLOCK_SIZE).fill(0) : iv;
|
|
90
|
+
|
|
91
|
+
input = Utils.convertToByteArray(input, inputType);
|
|
92
|
+
const output = decryptTEA(input, key, actualIv, mode, padding);
|
|
93
|
+
return outputType === "Hex" ? toHex(output, "") : Utils.byteArrayToUtf8(output);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export default TEADecrypt;
|
|
@@ -0,0 +1,98 @@
|
|
|
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 { encryptTEA, TEA_BLOCK_SIZE } from "../lib/TEA.mjs";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* TEA Encrypt operation
|
|
15
|
+
*/
|
|
16
|
+
class TEAEncrypt extends Operation {
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* TEAEncrypt constructor
|
|
20
|
+
*/
|
|
21
|
+
constructor() {
|
|
22
|
+
super();
|
|
23
|
+
|
|
24
|
+
this.name = "TEA Encrypt";
|
|
25
|
+
this.module = "Ciphers";
|
|
26
|
+
this.description = "TEA (Tiny Encryption Algorithm) is a block cipher designed by David Wheeler and Roger Needham in 1994. It operates on 64-bit blocks using a 128-bit key and performs 32 cycles (64 Feistel rounds) with the DELTA constant 0x9E3779B9 derived from the golden ratio.<br><br>TEA is notable for its simplicity and compact implementation, making it frequently encountered in malware analysis and CTF challenges. Despite its elegance, TEA has known weaknesses including equivalent keys and susceptibility to related-key attacks, leading to successors XTEA and XXTEA.<br><br><b>Key:</b> Must be exactly 16 bytes (128 bits).<br><br><b>IV:</b> The Initialisation Vector should be 8 bytes (64 bits). If not entered, it will default to null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, the PKCS#5 padding scheme is used.";
|
|
27
|
+
this.infoURL = "https://wikipedia.org/wiki/Tiny_Encryption_Algorithm";
|
|
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
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {string} input
|
|
68
|
+
* @param {Object[]} args
|
|
69
|
+
* @returns {string}
|
|
70
|
+
*/
|
|
71
|
+
run(input, args) {
|
|
72
|
+
const key = Utils.convertToByteArray(args[0].string, args[0].option),
|
|
73
|
+
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
|
74
|
+
[,, mode, inputType, outputType, padding] = args;
|
|
75
|
+
|
|
76
|
+
if (key.length !== 16)
|
|
77
|
+
throw new OperationError(`Invalid key length: ${key.length} bytes
|
|
78
|
+
|
|
79
|
+
TEA requires a key length of 16 bytes (128 bits).
|
|
80
|
+
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
|
|
81
|
+
|
|
82
|
+
if (iv.length !== TEA_BLOCK_SIZE && iv.length !== 0 && mode !== "ECB")
|
|
83
|
+
throw new OperationError(`Invalid IV length: ${iv.length} bytes
|
|
84
|
+
|
|
85
|
+
TEA uses an IV length of ${TEA_BLOCK_SIZE} bytes (${TEA_BLOCK_SIZE * 8} bits).
|
|
86
|
+
Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
|
|
87
|
+
|
|
88
|
+
// Default IV to null bytes if empty (like AES)
|
|
89
|
+
const actualIv = iv.length === 0 ? new Array(TEA_BLOCK_SIZE).fill(0) : iv;
|
|
90
|
+
|
|
91
|
+
input = Utils.convertToByteArray(input, inputType);
|
|
92
|
+
const output = encryptTEA(input, key, actualIv, mode, padding);
|
|
93
|
+
return outputType === "Hex" ? toHex(output, "") : Utils.byteArrayToUtf8(output);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export default TEAEncrypt;
|