cyberchef 11.1.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.
Files changed (148) hide show
  1. package/AGENTS.md +75 -0
  2. package/CHANGELOG.md +173 -2
  3. package/Dockerfile +2 -2
  4. package/Gruntfile.js +1 -0
  5. package/README.md +7 -6
  6. package/SECURITY.md +3 -1
  7. package/package.json +22 -21
  8. package/src/core/Chef.mjs +2 -0
  9. package/src/core/Dish.mjs +1 -5
  10. package/src/core/Ingredient.mjs +92 -0
  11. package/src/core/Operation.mjs +19 -0
  12. package/src/core/Recipe.mjs +4 -0
  13. package/src/core/Utils.mjs +48 -0
  14. package/src/core/config/Categories.json +22 -6
  15. package/src/core/config/OperationConfig.json +4774 -1228
  16. package/src/core/config/modules/Ciphers.mjs +20 -0
  17. package/src/core/config/modules/Crypto.mjs +10 -0
  18. package/src/core/config/modules/Default.mjs +8 -0
  19. package/src/core/config/modules/File.mjs +16 -0
  20. package/src/core/config/modules/OpModules.mjs +2 -0
  21. package/src/core/config/scripts/generateHTMLEntities.mjs +139 -0
  22. package/src/core/config/scripts/htmlEntityOverrides.mjs +86 -0
  23. package/src/core/dishTypes/DishType.mjs +1 -1
  24. package/src/core/errors/ExcludedOperationError.mjs +1 -1
  25. package/src/core/lib/Arithmetic.mjs +21 -5
  26. package/src/core/lib/BigIntUtils.mjs +0 -1
  27. package/src/core/lib/COBS.mjs +86 -0
  28. package/src/core/lib/Charts.mjs +3 -3
  29. package/src/core/lib/Decimal.mjs +5 -5
  30. package/src/core/lib/HTMLEntities.mjs +2068 -0
  31. package/src/core/lib/Present.mjs +422 -0
  32. package/src/core/lib/Protocol.mjs +8 -6
  33. package/src/core/lib/TEA.mjs +494 -0
  34. package/src/core/lib/TLVParser.mjs +16 -7
  35. package/src/core/lib/Twofish.mjs +608 -0
  36. package/src/core/operations/AsconDecrypt.mjs +112 -0
  37. package/src/core/operations/AsconEncrypt.mjs +108 -0
  38. package/src/core/operations/AsconHash.mjs +49 -0
  39. package/src/core/operations/AsconMAC.mjs +68 -0
  40. package/src/core/operations/AutomatedValidationTestOp.mjs +84 -0
  41. package/src/core/operations/AvroToJSON.mjs +1 -1
  42. package/src/core/operations/BLAKE3.mjs +5 -1
  43. package/src/core/operations/BcryptCompare.mjs +11 -5
  44. package/src/core/operations/BitShiftLeft.mjs +4 -1
  45. package/src/core/operations/Bzip2Compress.mjs +1 -1
  46. package/src/core/operations/DechunkHTTPResponse.mjs +4 -1
  47. package/src/core/operations/ExtendedGCD.mjs +101 -0
  48. package/src/core/operations/FromBase.mjs +2 -1
  49. package/src/core/operations/FromCOBS.mjs +38 -0
  50. package/src/core/operations/FromDecimal.mjs +6 -1
  51. package/src/core/operations/FromHTMLEntity.mjs +2 -1458
  52. package/src/core/operations/GenerateDeBruijnSequence.mjs +8 -0
  53. package/src/core/operations/GenerateHOTP.mjs +21 -6
  54. package/src/core/operations/GenerateImage.mjs +22 -10
  55. package/src/core/operations/GeneratePrime.mjs +154 -0
  56. package/src/core/operations/GenerateTOTP.mjs +24 -7
  57. package/src/core/operations/Gzip.mjs +1 -3
  58. package/src/core/operations/Jsonata.mjs +12 -0
  59. package/src/core/operations/MIMEDecoding.mjs +1 -1
  60. package/src/core/operations/MOD.mjs +62 -0
  61. package/src/core/operations/ModularInverse.mjs +107 -0
  62. package/src/core/operations/PRESENTDecrypt.mjs +94 -0
  63. package/src/core/operations/PRESENTEncrypt.mjs +94 -0
  64. package/src/core/operations/ParityBit.mjs +1 -1
  65. package/src/core/operations/ParseIPv4Header.mjs +1 -1
  66. package/src/core/operations/ParseURI.mjs +18 -5
  67. package/src/core/operations/PseudoRandomNumberGenerator.mjs +2 -1
  68. package/src/core/operations/RandomPrime.mjs +154 -0
  69. package/src/core/operations/RenderPDF.mjs +100 -0
  70. package/src/core/operations/SHA2.mjs +1 -1
  71. package/src/core/operations/SM4Encrypt.mjs +1 -1
  72. package/src/core/operations/SetDifference.mjs +8 -1
  73. package/src/core/operations/SetIntersection.mjs +8 -1
  74. package/src/core/operations/ShowOnMap.mjs +14 -2
  75. package/src/core/operations/TEADecrypt.mjs +98 -0
  76. package/src/core/operations/TEAEncrypt.mjs +98 -0
  77. package/src/core/operations/ToBase.mjs +4 -4
  78. package/src/core/operations/ToBase32.mjs +20 -4
  79. package/src/core/operations/ToBinary.mjs +4 -1
  80. package/src/core/operations/ToCOBS.mjs +38 -0
  81. package/src/core/operations/ToHTMLEntity.mjs +7 -1430
  82. package/src/core/operations/ToHexdump.mjs +7 -1
  83. package/src/core/operations/TwofishDecrypt.mjs +94 -0
  84. package/src/core/operations/TwofishEncrypt.mjs +94 -0
  85. package/src/core/operations/URLEncode.mjs +22 -18
  86. package/src/core/operations/UnescapeUnicodeCharacters.mjs +2 -1
  87. package/src/core/operations/ViewBitPlane.mjs +9 -3
  88. package/src/core/operations/Wrap.mjs +5 -0
  89. package/src/core/operations/XORBruteForce.mjs +4 -1
  90. package/src/core/operations/XORChecksum.mjs +10 -3
  91. package/src/core/operations/XTEADecrypt.mjs +110 -0
  92. package/src/core/operations/XTEAEncrypt.mjs +110 -0
  93. package/src/core/operations/index.mjs +42 -0
  94. package/src/core/vendor/ascon.mjs +162 -0
  95. package/src/core/vendor/htmlEntities/entity.json +2233 -0
  96. package/src/core/vendor/htmlEntities/entity.txt +14 -0
  97. package/src/node/api.mjs +4 -1
  98. package/src/node/index.mjs +105 -0
  99. package/src/web/HTMLOperation.mjs +2 -1
  100. package/src/web/stylesheets/layout/_io.css +8 -0
  101. package/tests/browser/00_nightwatch.js +26 -0
  102. package/tests/browser/02_ops.js +20 -4
  103. package/tests/node/index.mjs +2 -0
  104. package/tests/node/tests/Dish.mjs +19 -0
  105. package/tests/node/tests/NodeDish.mjs +36 -0
  106. package/tests/node/tests/ToHTMLEntity.mjs +82 -0
  107. package/tests/node/tests/Utils.mjs +77 -0
  108. package/tests/node/tests/lib/ChartsProtocolPrototypePollution.mjs +90 -0
  109. package/tests/node/tests/nodeApi.mjs +33 -1
  110. package/tests/node/tests/operations.mjs +26 -1
  111. package/tests/operations/index.mjs +17 -0
  112. package/tests/operations/tests/Arithmetic.mjs +33 -0
  113. package/tests/operations/tests/Ascon.mjs +501 -0
  114. package/tests/operations/tests/AutomatedValidation.mjs +154 -0
  115. package/tests/operations/tests/BLAKE3.mjs +39 -1
  116. package/tests/operations/tests/Base32.mjs +22 -0
  117. package/tests/operations/tests/COBS.mjs +476 -0
  118. package/tests/operations/tests/CharEnc.mjs +2 -2
  119. package/tests/operations/tests/DechunkHTTPResponse.mjs +66 -0
  120. package/tests/operations/tests/ExtendedGCD.mjs +78 -0
  121. package/tests/operations/tests/FromBase.mjs +66 -0
  122. package/tests/operations/tests/FromDecimal.mjs +55 -0
  123. package/tests/operations/tests/GenerateLoremIpsum.mjs +2 -2
  124. package/tests/operations/tests/Gzip.mjs +60 -0
  125. package/tests/operations/tests/HTMLEntity.mjs +126 -0
  126. package/tests/operations/tests/Hash.mjs +44 -0
  127. package/tests/operations/tests/Hexdump.mjs +11 -0
  128. package/tests/operations/tests/Image.mjs +26 -0
  129. package/tests/operations/tests/Jsonata.mjs +23 -0
  130. package/tests/operations/tests/MIMEDecoding.mjs +33 -0
  131. package/tests/operations/tests/MOD.mjs +208 -0
  132. package/tests/operations/tests/Median.mjs +33 -0
  133. package/tests/operations/tests/ModularInverse.mjs +78 -0
  134. package/tests/operations/tests/OTP.mjs +167 -2
  135. package/tests/operations/tests/PRESENT.mjs +465 -0
  136. package/tests/operations/tests/ParseIPv4Header.mjs +11 -0
  137. package/tests/operations/tests/ParseTLV.mjs +41 -0
  138. package/tests/operations/tests/RenderPDF.mjs +55 -0
  139. package/tests/operations/tests/SM2.mjs +1 -1
  140. package/tests/operations/tests/SetDifference.mjs +22 -0
  141. package/tests/operations/tests/SetIntersection.mjs +23 -1
  142. package/tests/operations/tests/ShowOnMap.mjs +61 -0
  143. package/tests/operations/tests/TEA.mjs +566 -0
  144. package/tests/operations/tests/Twofish.mjs +486 -0
  145. package/tests/operations/tests/URLEncodeDecode.mjs +26 -0
  146. package/tests/operations/tests/UnescapeUnicodeCharacters.mjs +88 -0
  147. package/tests/operations/tests/Wrap.mjs +44 -0
  148. package/webpack.config.js +3 -3
@@ -0,0 +1,494 @@
1
+ /**
2
+ * TEA and XTEA block cipher implementation.
3
+ *
4
+ * TEA (Tiny Encryption Algorithm) — Wheeler & Needham, 1994.
5
+ * XTEA (Extended TEA) — Wheeler & Needham, 1997.
6
+ *
7
+ * Both operate on 64-bit blocks with 128-bit keys.
8
+ * TEA uses 32 cycles (64 Feistel rounds).
9
+ * XTEA uses 32 cycles (64 Feistel rounds) with improved key schedule.
10
+ *
11
+ * References:
12
+ * https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
13
+ * https://en.wikipedia.org/wiki/XTEA
14
+ * https://www.cix.co.uk/~klockstone/teavect.htm
15
+ *
16
+ * @author Medjedtxm
17
+ * @copyright Crown Copyright 2026
18
+ * @license Apache-2.0
19
+ */
20
+
21
+ import OperationError from "../errors/OperationError.mjs";
22
+
23
+ /** TEA/XTEA constants */
24
+ const DELTA = 0x9E3779B9;
25
+ const BLOCK_SIZE = 8; // 64-bit block = 8 bytes
26
+ const ROUNDS = 32; // 32 cycles
27
+
28
+ /**
29
+ * Convert byte array to array of 32-bit unsigned integers (big-endian)
30
+ * @param {number[]} bytes
31
+ * @returns {number[]}
32
+ */
33
+ function bytesToUint32(bytes) {
34
+ const words = [];
35
+ for (let i = 0; i < bytes.length; i += 4) {
36
+ words.push(
37
+ ((bytes[i] << 24) | (bytes[i + 1] << 16) |
38
+ (bytes[i + 2] << 8) | bytes[i + 3]) >>> 0
39
+ );
40
+ }
41
+ return words;
42
+ }
43
+
44
+ /**
45
+ * Convert array of 32-bit unsigned integers to byte array (big-endian)
46
+ * @param {number[]} words
47
+ * @returns {number[]}
48
+ */
49
+ function uint32ToBytes(words) {
50
+ const bytes = [];
51
+ for (const w of words) {
52
+ bytes.push((w >>> 24) & 0xFF);
53
+ bytes.push((w >>> 16) & 0xFF);
54
+ bytes.push((w >>> 8) & 0xFF);
55
+ bytes.push(w & 0xFF);
56
+ }
57
+ return bytes;
58
+ }
59
+
60
+ /**
61
+ * TEA encrypt a single 64-bit block
62
+ * Reference: Wheeler & Needham, 1994
63
+ *
64
+ * @param {number[]} block - 8 bytes (plaintext)
65
+ * @param {number[]} key - 16 bytes (128-bit key)
66
+ * @returns {number[]} - 8 bytes (ciphertext)
67
+ */
68
+ function teaEncryptBlock(block, key) {
69
+ const v = bytesToUint32(block);
70
+ const k = bytesToUint32(key);
71
+ let v0 = v[0], v1 = v[1];
72
+ let sum = 0;
73
+
74
+ for (let i = 0; i < ROUNDS; i++) {
75
+ sum = (sum + DELTA) >>> 0;
76
+ v0 = (v0 + ((((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >>> 5) + k[1])))) >>> 0;
77
+ v1 = (v1 + ((((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >>> 5) + k[3])))) >>> 0;
78
+ }
79
+
80
+ return uint32ToBytes([v0, v1]);
81
+ }
82
+
83
+ /**
84
+ * TEA decrypt a single 64-bit block
85
+ *
86
+ * @param {number[]} block - 8 bytes (ciphertext)
87
+ * @param {number[]} key - 16 bytes (128-bit key)
88
+ * @returns {number[]} - 8 bytes (plaintext)
89
+ */
90
+ function teaDecryptBlock(block, key) {
91
+ const v = bytesToUint32(block);
92
+ const k = bytesToUint32(key);
93
+ let v0 = v[0], v1 = v[1];
94
+ let sum = (DELTA * ROUNDS) >>> 0;
95
+
96
+ for (let i = 0; i < ROUNDS; i++) {
97
+ v1 = (v1 - ((((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >>> 5) + k[3])))) >>> 0;
98
+ v0 = (v0 - ((((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >>> 5) + k[1])))) >>> 0;
99
+ sum = (sum - DELTA) >>> 0;
100
+ }
101
+
102
+ return uint32ToBytes([v0, v1]);
103
+ }
104
+
105
+ /**
106
+ * XTEA encrypt a single 64-bit block
107
+ * Reference: Wheeler & Needham, 1997
108
+ *
109
+ * @param {number[]} block - 8 bytes (plaintext)
110
+ * @param {number[]} key - 16 bytes (128-bit key)
111
+ * @param {number} rounds - Number of rounds (default 32)
112
+ * @returns {number[]} - 8 bytes (ciphertext)
113
+ */
114
+ function xteaEncryptBlock(block, key, rounds) {
115
+ const v = bytesToUint32(block);
116
+ const k = bytesToUint32(key);
117
+ let v0 = v[0], v1 = v[1];
118
+ let sum = 0;
119
+
120
+ for (let i = 0; i < rounds; i++) {
121
+ v0 = (v0 + ((((v1 << 4) ^ (v1 >>> 5)) + v1) ^ (sum + k[sum & 3]))) >>> 0;
122
+ sum = (sum + DELTA) >>> 0;
123
+ v1 = (v1 + ((((v0 << 4) ^ (v0 >>> 5)) + v0) ^ (sum + k[(sum >>> 11) & 3]))) >>> 0;
124
+ }
125
+
126
+ return uint32ToBytes([v0, v1]);
127
+ }
128
+
129
+ /**
130
+ * XTEA decrypt a single 64-bit block
131
+ *
132
+ * @param {number[]} block - 8 bytes (ciphertext)
133
+ * @param {number[]} key - 16 bytes (128-bit key)
134
+ * @param {number} rounds - Number of rounds (default 32)
135
+ * @returns {number[]} - 8 bytes (plaintext)
136
+ */
137
+ function xteaDecryptBlock(block, key, rounds) {
138
+ const v = bytesToUint32(block);
139
+ const k = bytesToUint32(key);
140
+ let v0 = v[0], v1 = v[1];
141
+ let sum = (DELTA * rounds) >>> 0;
142
+
143
+ for (let i = 0; i < rounds; i++) {
144
+ v1 = (v1 - ((((v0 << 4) ^ (v0 >>> 5)) + v0) ^ (sum + k[(sum >>> 11) & 3]))) >>> 0;
145
+ sum = (sum - DELTA) >>> 0;
146
+ v0 = (v0 - ((((v1 << 4) ^ (v1 >>> 5)) + v1) ^ (sum + k[sum & 3]))) >>> 0;
147
+ }
148
+
149
+ return uint32ToBytes([v0, v1]);
150
+ }
151
+
152
+ /**
153
+ * XOR two byte arrays of equal length
154
+ * @param {number[]} a
155
+ * @param {number[]} b
156
+ * @returns {number[]}
157
+ */
158
+ function xorBlocks(a, b) {
159
+ return a.map((byte, i) => byte ^ b[i]);
160
+ }
161
+
162
+ /**
163
+ * Increment a byte array as a big-endian counter
164
+ * @param {number[]} counter
165
+ * @returns {number[]}
166
+ */
167
+ function incrementCounter(counter) {
168
+ const result = [...counter];
169
+ for (let i = result.length - 1; i >= 0; i--) {
170
+ result[i] = (result[i] + 1) & 0xFF;
171
+ if (result[i] !== 0) break;
172
+ }
173
+ return result;
174
+ }
175
+
176
+ /**
177
+ * Apply padding to message
178
+ * @param {number[]} message
179
+ * @param {string} padding - "NO", "PKCS5", "ZERO", "RANDOM", "BIT"
180
+ * @returns {number[]}
181
+ */
182
+ function applyPadding(message, padding) {
183
+ const remainder = message.length % BLOCK_SIZE;
184
+ if (remainder === 0 && padding !== "PKCS5") return [...message];
185
+
186
+ const nPadding = (remainder === 0 && padding === "PKCS5") ?
187
+ BLOCK_SIZE :
188
+ BLOCK_SIZE - remainder;
189
+
190
+ if (nPadding === 0) return [...message];
191
+
192
+ const padded = [...message];
193
+
194
+ switch (padding) {
195
+ case "NO":
196
+ throw new OperationError(
197
+ `No padding requested but input length (${message.length} bytes) is not a multiple of ${BLOCK_SIZE} bytes.`
198
+ );
199
+ case "PKCS5":
200
+ for (let i = 0; i < nPadding; i++) padded.push(nPadding);
201
+ break;
202
+ case "ZERO":
203
+ for (let i = 0; i < nPadding; i++) padded.push(0);
204
+ break;
205
+ case "RANDOM":
206
+ for (let i = 0; i < nPadding; i++) padded.push(Math.floor(Math.random() * 256));
207
+ break;
208
+ case "BIT":
209
+ padded.push(0x80);
210
+ for (let i = 1; i < nPadding; i++) padded.push(0);
211
+ break;
212
+ default:
213
+ throw new OperationError(`Unknown padding type: ${padding}`);
214
+ }
215
+
216
+ return padded;
217
+ }
218
+
219
+ /**
220
+ * Remove padding from message
221
+ * @param {number[]} message
222
+ * @param {string} padding
223
+ * @returns {number[]}
224
+ */
225
+ function removePadding(message, padding) {
226
+ if (message.length === 0) return message;
227
+
228
+ switch (padding) {
229
+ case "NO":
230
+ case "ZERO":
231
+ case "RANDOM":
232
+ return message;
233
+
234
+ case "PKCS5": {
235
+ const padByte = message[message.length - 1];
236
+ if (padByte > 0 && padByte <= BLOCK_SIZE) {
237
+ for (let i = 0; i < padByte; i++) {
238
+ if (message[message.length - 1 - i] !== padByte) {
239
+ throw new OperationError("Invalid PKCS#5 padding.");
240
+ }
241
+ }
242
+ return message.slice(0, message.length - padByte);
243
+ }
244
+ throw new OperationError("Invalid PKCS#5 padding.");
245
+ }
246
+
247
+ case "BIT": {
248
+ for (let i = message.length - 1; i >= 0; i--) {
249
+ if (message[i] === 0x80) return message.slice(0, i);
250
+ if (message[i] !== 0) throw new OperationError("Invalid BIT padding.");
251
+ }
252
+ throw new OperationError("Invalid BIT padding.");
253
+ }
254
+
255
+ default:
256
+ throw new OperationError(`Unknown padding type: ${padding}`);
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Encrypt with block cipher modes
262
+ *
263
+ * @param {number[]} message - Plaintext bytes
264
+ * @param {number[]} key - 16-byte key
265
+ * @param {number[]} iv - 8-byte IV (ignored for ECB)
266
+ * @param {string} mode - "ECB", "CBC", "CFB", "OFB", "CTR"
267
+ * @param {string} padding - "PKCS5", "NO", "ZERO", "RANDOM", "BIT"
268
+ * @param {Function} encryptBlockFn - Block encrypt function
269
+ * @returns {number[]} - Ciphertext bytes
270
+ */
271
+ function encryptWithMode(message, key, iv, mode, padding, encryptBlockFn) {
272
+ const messageLength = message.length;
273
+ if (messageLength === 0) return [];
274
+
275
+ let data;
276
+ if (mode === "ECB" || mode === "CBC") {
277
+ data = applyPadding(message, padding);
278
+ } else {
279
+ data = [...message];
280
+ }
281
+
282
+ const cipherText = [];
283
+
284
+ switch (mode) {
285
+ case "ECB":
286
+ for (let i = 0; i < data.length; i += BLOCK_SIZE) {
287
+ cipherText.push(...encryptBlockFn(data.slice(i, i + BLOCK_SIZE), key));
288
+ }
289
+ break;
290
+
291
+ case "CBC": {
292
+ let ivBlock = [...iv];
293
+ for (let i = 0; i < data.length; i += BLOCK_SIZE) {
294
+ const block = data.slice(i, i + BLOCK_SIZE);
295
+ const xored = xorBlocks(block, ivBlock);
296
+ ivBlock = encryptBlockFn(xored, key);
297
+ cipherText.push(...ivBlock);
298
+ }
299
+ break;
300
+ }
301
+
302
+ case "CFB": {
303
+ let ivBlock = [...iv];
304
+ for (let i = 0; i < data.length; i += BLOCK_SIZE) {
305
+ const encrypted = encryptBlockFn(ivBlock, key);
306
+ const block = data.slice(i, i + BLOCK_SIZE);
307
+ while (block.length < BLOCK_SIZE) block.push(0);
308
+ ivBlock = xorBlocks(encrypted, block);
309
+ cipherText.push(...ivBlock);
310
+ }
311
+ return cipherText.slice(0, messageLength);
312
+ }
313
+
314
+ case "OFB": {
315
+ let ivBlock = [...iv];
316
+ for (let i = 0; i < data.length; i += BLOCK_SIZE) {
317
+ ivBlock = encryptBlockFn(ivBlock, key);
318
+ const block = data.slice(i, i + BLOCK_SIZE);
319
+ while (block.length < BLOCK_SIZE) block.push(0);
320
+ cipherText.push(...xorBlocks(ivBlock, block));
321
+ }
322
+ return cipherText.slice(0, messageLength);
323
+ }
324
+
325
+ case "CTR": {
326
+ let counter = [...iv];
327
+ for (let i = 0; i < data.length; i += BLOCK_SIZE) {
328
+ const encrypted = encryptBlockFn(counter, key);
329
+ const block = data.slice(i, i + BLOCK_SIZE);
330
+ while (block.length < BLOCK_SIZE) block.push(0);
331
+ cipherText.push(...xorBlocks(encrypted, block));
332
+ counter = incrementCounter(counter);
333
+ }
334
+ return cipherText.slice(0, messageLength);
335
+ }
336
+
337
+ default:
338
+ throw new OperationError(`Invalid block cipher mode: ${mode}`);
339
+ }
340
+
341
+ return cipherText;
342
+ }
343
+
344
+ /**
345
+ * Decrypt with block cipher modes
346
+ *
347
+ * @param {number[]} cipherText - Ciphertext bytes
348
+ * @param {number[]} key - 16-byte key
349
+ * @param {number[]} iv - 8-byte IV (ignored for ECB)
350
+ * @param {string} mode - "ECB", "CBC", "CFB", "OFB", "CTR"
351
+ * @param {string} padding - "PKCS5", "NO", "ZERO", "RANDOM", "BIT"
352
+ * @param {Function} encryptBlockFn - Block encrypt function (used for stream modes)
353
+ * @param {Function} decryptBlockFn - Block decrypt function (used for ECB/CBC)
354
+ * @returns {number[]} - Plaintext bytes
355
+ */
356
+ function decryptWithMode(cipherText, key, iv, mode, padding, encryptBlockFn, decryptBlockFn) {
357
+ const originalLength = cipherText.length;
358
+ if (originalLength === 0) return [];
359
+
360
+ if (mode === "ECB" || mode === "CBC") {
361
+ if ((originalLength % BLOCK_SIZE) !== 0)
362
+ throw new OperationError(
363
+ `Invalid ciphertext length: ${originalLength} bytes. Must be a multiple of ${BLOCK_SIZE}.`
364
+ );
365
+ } else {
366
+ while ((cipherText.length % BLOCK_SIZE) !== 0)
367
+ cipherText.push(0);
368
+ }
369
+
370
+ const plainText = [];
371
+
372
+ switch (mode) {
373
+ case "ECB":
374
+ for (let i = 0; i < cipherText.length; i += BLOCK_SIZE) {
375
+ plainText.push(...decryptBlockFn(cipherText.slice(i, i + BLOCK_SIZE), key));
376
+ }
377
+ break;
378
+
379
+ case "CBC": {
380
+ let ivBlock = [...iv];
381
+ for (let i = 0; i < cipherText.length; i += BLOCK_SIZE) {
382
+ const block = cipherText.slice(i, i + BLOCK_SIZE);
383
+ const decrypted = decryptBlockFn(block, key);
384
+ plainText.push(...xorBlocks(decrypted, ivBlock));
385
+ ivBlock = block;
386
+ }
387
+ break;
388
+ }
389
+
390
+ case "CFB": {
391
+ let ivBlock = [...iv];
392
+ for (let i = 0; i < cipherText.length; i += BLOCK_SIZE) {
393
+ const encrypted = encryptBlockFn(ivBlock, key);
394
+ const block = cipherText.slice(i, i + BLOCK_SIZE);
395
+ plainText.push(...xorBlocks(encrypted, block));
396
+ ivBlock = block;
397
+ }
398
+ return plainText.slice(0, originalLength);
399
+ }
400
+
401
+ case "OFB": {
402
+ let ivBlock = [...iv];
403
+ for (let i = 0; i < cipherText.length; i += BLOCK_SIZE) {
404
+ ivBlock = encryptBlockFn(ivBlock, key);
405
+ const block = cipherText.slice(i, i + BLOCK_SIZE);
406
+ plainText.push(...xorBlocks(ivBlock, block));
407
+ }
408
+ return plainText.slice(0, originalLength);
409
+ }
410
+
411
+ case "CTR": {
412
+ let counter = [...iv];
413
+ for (let i = 0; i < cipherText.length; i += BLOCK_SIZE) {
414
+ const encrypted = encryptBlockFn(counter, key);
415
+ const block = cipherText.slice(i, i + BLOCK_SIZE);
416
+ plainText.push(...xorBlocks(encrypted, block));
417
+ counter = incrementCounter(counter);
418
+ }
419
+ return plainText.slice(0, originalLength);
420
+ }
421
+
422
+ default:
423
+ throw new OperationError(`Invalid block cipher mode: ${mode}`);
424
+ }
425
+
426
+ if (mode === "ECB" || mode === "CBC") {
427
+ return removePadding(plainText, padding);
428
+ }
429
+
430
+ return plainText.slice(0, originalLength);
431
+ }
432
+
433
+
434
+ // ==================== PUBLIC API ====================
435
+
436
+ /**
437
+ * Encrypt using TEA cipher
438
+ * @param {number[]} message - Plaintext bytes
439
+ * @param {number[]} key - 16-byte key
440
+ * @param {number[]} iv - 8-byte IV
441
+ * @param {string} mode - Block cipher mode
442
+ * @param {string} padding - Padding type
443
+ * @returns {number[]} - Ciphertext bytes
444
+ */
445
+ export function encryptTEA(message, key, iv, mode = "ECB", padding = "PKCS5") {
446
+ return encryptWithMode(message, key, iv, mode, padding, teaEncryptBlock);
447
+ }
448
+
449
+ /**
450
+ * Decrypt using TEA cipher
451
+ * @param {number[]} cipherText - Ciphertext bytes
452
+ * @param {number[]} key - 16-byte key
453
+ * @param {number[]} iv - 8-byte IV
454
+ * @param {string} mode - Block cipher mode
455
+ * @param {string} padding - Padding type
456
+ * @returns {number[]} - Plaintext bytes
457
+ */
458
+ export function decryptTEA(cipherText, key, iv, mode = "ECB", padding = "PKCS5") {
459
+ return decryptWithMode(cipherText, key, iv, mode, padding, teaEncryptBlock, teaDecryptBlock);
460
+ }
461
+
462
+ /**
463
+ * Encrypt using XTEA cipher
464
+ * @param {number[]} message - Plaintext bytes
465
+ * @param {number[]} key - 16-byte key
466
+ * @param {number[]} iv - 8-byte IV
467
+ * @param {string} mode - Block cipher mode
468
+ * @param {string} padding - Padding type
469
+ * @param {number} rounds - Number of rounds (default 32)
470
+ * @returns {number[]} - Ciphertext bytes
471
+ */
472
+ export function encryptXTEA(message, key, iv, mode = "ECB", padding = "PKCS5", rounds = 32) {
473
+ const encFn = (block, k) => xteaEncryptBlock(block, k, rounds);
474
+ return encryptWithMode(message, key, iv, mode, padding, encFn);
475
+ }
476
+
477
+ /**
478
+ * Decrypt using XTEA cipher
479
+ * @param {number[]} cipherText - Ciphertext bytes
480
+ * @param {number[]} key - 16-byte key
481
+ * @param {number[]} iv - 8-byte IV
482
+ * @param {string} mode - Block cipher mode
483
+ * @param {string} padding - Padding type
484
+ * @param {number} rounds - Number of rounds (default 32)
485
+ * @returns {number[]} - Plaintext bytes
486
+ */
487
+ export function decryptXTEA(cipherText, key, iv, mode = "ECB", padding = "PKCS5", rounds = 32) {
488
+ const encFn = (block, k) => xteaEncryptBlock(block, k, rounds);
489
+ const decFn = (block, k) => xteaDecryptBlock(block, k, rounds);
490
+ return decryptWithMode(cipherText, key, iv, mode, padding, encFn, decFn);
491
+ }
492
+
493
+ /** Block size in bytes (exported for operation validation) */
494
+ export const TEA_BLOCK_SIZE = BLOCK_SIZE;
@@ -33,20 +33,29 @@ export default class TLVParser {
33
33
  * @returns {number}
34
34
  */
35
35
  getLength() {
36
+ let bytesInLength = this.bytesInLength;
37
+ let bigEndian = false;
38
+
36
39
  if (this.basicEncodingRules) {
37
- const bit = this.input[this.location];
38
- if (bit & 0x80) {
39
- this.bytesInLength = bit & ~0x80;
40
+ const firstLengthByte = this.input[this.location];
41
+ this.location++;
42
+
43
+ if (firstLengthByte & 0x80) {
44
+ bytesInLength = firstLengthByte & ~0x80;
45
+ bigEndian = true;
40
46
  } else {
41
- this.location++;
42
- return bit & ~0x80;
47
+ return firstLengthByte & ~0x80;
43
48
  }
44
49
  }
45
50
 
46
51
  let length = 0;
47
52
 
48
- for (let i = 0; i < this.bytesInLength; i++) {
49
- length += this.input[this.location] * Math.pow(Math.pow(2, 8), i);
53
+ for (let i = 0; i < bytesInLength; i++) {
54
+ if (bigEndian) {
55
+ length = (length << 8) + this.input[this.location];
56
+ } else {
57
+ length += this.input[this.location] * Math.pow(Math.pow(2, 8), i);
58
+ }
50
59
  this.location++;
51
60
  }
52
61