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,608 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Complete implementation of Twofish block cipher encryption/decryption with
|
|
3
|
+
* ECB, CBC, CFB, OFB, CTR block modes.
|
|
4
|
+
*
|
|
5
|
+
* Twofish was an AES finalist designed by Bruce Schneier et al.
|
|
6
|
+
* Reference: https://www.schneier.com/academic/twofish/
|
|
7
|
+
*
|
|
8
|
+
* @author Medjedtxm
|
|
9
|
+
* @copyright Crown Copyright 2026
|
|
10
|
+
* @license Apache-2.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import OperationError from "../errors/OperationError.mjs";
|
|
14
|
+
|
|
15
|
+
/** Number of rounds */
|
|
16
|
+
const NROUNDS = 16;
|
|
17
|
+
|
|
18
|
+
/** Block size in bytes (128 bits) */
|
|
19
|
+
const BLOCKSIZE = 16;
|
|
20
|
+
|
|
21
|
+
/** Q0 permutation */
|
|
22
|
+
const Q0 = [
|
|
23
|
+
0xa9, 0x67, 0xb3, 0xe8, 0x04, 0xfd, 0xa3, 0x76, 0x9a, 0x92, 0x80, 0x78, 0xe4, 0xdd, 0xd1, 0x38,
|
|
24
|
+
0x0d, 0xc6, 0x35, 0x98, 0x18, 0xf7, 0xec, 0x6c, 0x43, 0x75, 0x37, 0x26, 0xfa, 0x13, 0x94, 0x48,
|
|
25
|
+
0xf2, 0xd0, 0x8b, 0x30, 0x84, 0x54, 0xdf, 0x23, 0x19, 0x5b, 0x3d, 0x59, 0xf3, 0xae, 0xa2, 0x82,
|
|
26
|
+
0x63, 0x01, 0x83, 0x2e, 0xd9, 0x51, 0x9b, 0x7c, 0xa6, 0xeb, 0xa5, 0xbe, 0x16, 0x0c, 0xe3, 0x61,
|
|
27
|
+
0xc0, 0x8c, 0x3a, 0xf5, 0x73, 0x2c, 0x25, 0x0b, 0xbb, 0x4e, 0x89, 0x6b, 0x53, 0x6a, 0xb4, 0xf1,
|
|
28
|
+
0xe1, 0xe6, 0xbd, 0x45, 0xe2, 0xf4, 0xb6, 0x66, 0xcc, 0x95, 0x03, 0x56, 0xd4, 0x1c, 0x1e, 0xd7,
|
|
29
|
+
0xfb, 0xc3, 0x8e, 0xb5, 0xe9, 0xcf, 0xbf, 0xba, 0xea, 0x77, 0x39, 0xaf, 0x33, 0xc9, 0x62, 0x71,
|
|
30
|
+
0x81, 0x79, 0x09, 0xad, 0x24, 0xcd, 0xf9, 0xd8, 0xe5, 0xc5, 0xb9, 0x4d, 0x44, 0x08, 0x86, 0xe7,
|
|
31
|
+
0xa1, 0x1d, 0xaa, 0xed, 0x06, 0x70, 0xb2, 0xd2, 0x41, 0x7b, 0xa0, 0x11, 0x31, 0xc2, 0x27, 0x90,
|
|
32
|
+
0x20, 0xf6, 0x60, 0xff, 0x96, 0x5c, 0xb1, 0xab, 0x9e, 0x9c, 0x52, 0x1b, 0x5f, 0x93, 0x0a, 0xef,
|
|
33
|
+
0x91, 0x85, 0x49, 0xee, 0x2d, 0x4f, 0x8f, 0x3b, 0x47, 0x87, 0x6d, 0x46, 0xd6, 0x3e, 0x69, 0x64,
|
|
34
|
+
0x2a, 0xce, 0xcb, 0x2f, 0xfc, 0x97, 0x05, 0x7a, 0xac, 0x7f, 0xd5, 0x1a, 0x4b, 0x0e, 0xa7, 0x5a,
|
|
35
|
+
0x28, 0x14, 0x3f, 0x29, 0x88, 0x3c, 0x4c, 0x02, 0xb8, 0xda, 0xb0, 0x17, 0x55, 0x1f, 0x8a, 0x7d,
|
|
36
|
+
0x57, 0xc7, 0x8d, 0x74, 0xb7, 0xc4, 0x9f, 0x72, 0x7e, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34,
|
|
37
|
+
0x6e, 0x50, 0xde, 0x68, 0x65, 0xbc, 0xdb, 0xf8, 0xc8, 0xa8, 0x2b, 0x40, 0xdc, 0xfe, 0x32, 0xa4,
|
|
38
|
+
0xca, 0x10, 0x21, 0xf0, 0xd3, 0x5d, 0x0f, 0x00, 0x6f, 0x9d, 0x36, 0x42, 0x4a, 0x5e, 0xc1, 0xe0
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
/** Q1 permutation */
|
|
42
|
+
const Q1 = [
|
|
43
|
+
0x75, 0xf3, 0xc6, 0xf4, 0xdb, 0x7b, 0xfb, 0xc8, 0x4a, 0xd3, 0xe6, 0x6b, 0x45, 0x7d, 0xe8, 0x4b,
|
|
44
|
+
0xd6, 0x32, 0xd8, 0xfd, 0x37, 0x71, 0xf1, 0xe1, 0x30, 0x0f, 0xf8, 0x1b, 0x87, 0xfa, 0x06, 0x3f,
|
|
45
|
+
0x5e, 0xba, 0xae, 0x5b, 0x8a, 0x00, 0xbc, 0x9d, 0x6d, 0xc1, 0xb1, 0x0e, 0x80, 0x5d, 0xd2, 0xd5,
|
|
46
|
+
0xa0, 0x84, 0x07, 0x14, 0xb5, 0x90, 0x2c, 0xa3, 0xb2, 0x73, 0x4c, 0x54, 0x92, 0x74, 0x36, 0x51,
|
|
47
|
+
0x38, 0xb0, 0xbd, 0x5a, 0xfc, 0x60, 0x62, 0x96, 0x6c, 0x42, 0xf7, 0x10, 0x7c, 0x28, 0x27, 0x8c,
|
|
48
|
+
0x13, 0x95, 0x9c, 0xc7, 0x24, 0x46, 0x3b, 0x70, 0xca, 0xe3, 0x85, 0xcb, 0x11, 0xd0, 0x93, 0xb8,
|
|
49
|
+
0xa6, 0x83, 0x20, 0xff, 0x9f, 0x77, 0xc3, 0xcc, 0x03, 0x6f, 0x08, 0xbf, 0x40, 0xe7, 0x2b, 0xe2,
|
|
50
|
+
0x79, 0x0c, 0xaa, 0x82, 0x41, 0x3a, 0xea, 0xb9, 0xe4, 0x9a, 0xa4, 0x97, 0x7e, 0xda, 0x7a, 0x17,
|
|
51
|
+
0x66, 0x94, 0xa1, 0x1d, 0x3d, 0xf0, 0xde, 0xb3, 0x0b, 0x72, 0xa7, 0x1c, 0xef, 0xd1, 0x53, 0x3e,
|
|
52
|
+
0x8f, 0x33, 0x26, 0x5f, 0xec, 0x76, 0x2a, 0x49, 0x81, 0x88, 0xee, 0x21, 0xc4, 0x1a, 0xeb, 0xd9,
|
|
53
|
+
0xc5, 0x39, 0x99, 0xcd, 0xad, 0x31, 0x8b, 0x01, 0x18, 0x23, 0xdd, 0x1f, 0x4e, 0x2d, 0xf9, 0x48,
|
|
54
|
+
0x4f, 0xf2, 0x65, 0x8e, 0x78, 0x5c, 0x58, 0x19, 0x8d, 0xe5, 0x98, 0x57, 0x67, 0x7f, 0x05, 0x64,
|
|
55
|
+
0xaf, 0x63, 0xb6, 0xfe, 0xf5, 0xb7, 0x3c, 0xa5, 0xce, 0xe9, 0x68, 0x44, 0xe0, 0x4d, 0x43, 0x69,
|
|
56
|
+
0x29, 0x2e, 0xac, 0x15, 0x59, 0xa8, 0x0a, 0x9e, 0x6e, 0x47, 0xdf, 0x34, 0x35, 0x6a, 0xcf, 0xdc,
|
|
57
|
+
0x22, 0xc9, 0xc0, 0x9b, 0x89, 0xd4, 0xed, 0xab, 0x12, 0xa2, 0x0d, 0x52, 0xbb, 0x02, 0x2f, 0xa9,
|
|
58
|
+
0xd7, 0x61, 0x1e, 0xb4, 0x50, 0x04, 0xf6, 0xc2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xbe, 0x91
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
/** Reed-Solomon matrix for key schedule */
|
|
62
|
+
const RS = [
|
|
63
|
+
[0x01, 0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E],
|
|
64
|
+
[0xA4, 0x56, 0x82, 0xF3, 0x1E, 0xC6, 0x68, 0xE5],
|
|
65
|
+
[0x02, 0xA1, 0xFC, 0xC1, 0x47, 0xAE, 0x3D, 0x19],
|
|
66
|
+
[0xA4, 0x55, 0x87, 0x5A, 0x58, 0xDB, 0x9E, 0x03]
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Galois Field multiplication in GF(2^8) with polynomial 0x169
|
|
71
|
+
*/
|
|
72
|
+
function gfMult(a, b, poly) {
|
|
73
|
+
let result = 0;
|
|
74
|
+
while (b) {
|
|
75
|
+
if (b & 1) result ^= a;
|
|
76
|
+
a <<= 1;
|
|
77
|
+
if (a & 0x100) a ^= poly;
|
|
78
|
+
b >>>= 1;
|
|
79
|
+
}
|
|
80
|
+
return result & 0xFF;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* MDS multiplication
|
|
85
|
+
*/
|
|
86
|
+
function mdsMultiply(x) {
|
|
87
|
+
const b0 = x & 0xFF;
|
|
88
|
+
const b1 = (x >>> 8) & 0xFF;
|
|
89
|
+
const b2 = (x >>> 16) & 0xFF;
|
|
90
|
+
const b3 = (x >>> 24) & 0xFF;
|
|
91
|
+
|
|
92
|
+
// MDS matrix multiplication in GF(2^8) with polynomial 0x169
|
|
93
|
+
const r0 = gfMult(b0, 0x01, 0x169) ^ gfMult(b1, 0xEF, 0x169) ^ gfMult(b2, 0x5B, 0x169) ^ gfMult(b3, 0x5B, 0x169);
|
|
94
|
+
const r1 = gfMult(b0, 0x5B, 0x169) ^ gfMult(b1, 0xEF, 0x169) ^ gfMult(b2, 0xEF, 0x169) ^ gfMult(b3, 0x01, 0x169);
|
|
95
|
+
const r2 = gfMult(b0, 0xEF, 0x169) ^ gfMult(b1, 0x5B, 0x169) ^ gfMult(b2, 0x01, 0x169) ^ gfMult(b3, 0xEF, 0x169);
|
|
96
|
+
const r3 = gfMult(b0, 0xEF, 0x169) ^ gfMult(b1, 0x01, 0x169) ^ gfMult(b2, 0xEF, 0x169) ^ gfMult(b3, 0x5B, 0x169);
|
|
97
|
+
|
|
98
|
+
return (r3 << 24) | (r2 << 16) | (r1 << 8) | r0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Reed-Solomon multiplication for key schedule
|
|
103
|
+
*/
|
|
104
|
+
function rsMultiply(key8) {
|
|
105
|
+
let result = 0;
|
|
106
|
+
for (let i = 0; i < 4; i++) {
|
|
107
|
+
let x = 0;
|
|
108
|
+
for (let j = 0; j < 8; j++) {
|
|
109
|
+
x ^= gfMult(RS[i][j], key8[j], 0x14D);
|
|
110
|
+
}
|
|
111
|
+
result |= x << (i * 8);
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Apply h function (the main keyed permutation)
|
|
118
|
+
*/
|
|
119
|
+
function h(x, L, k) {
|
|
120
|
+
const y = new Array(4);
|
|
121
|
+
y[0] = x & 0xFF;
|
|
122
|
+
y[1] = (x >>> 8) & 0xFF;
|
|
123
|
+
y[2] = (x >>> 16) & 0xFF;
|
|
124
|
+
y[3] = (x >>> 24) & 0xFF;
|
|
125
|
+
|
|
126
|
+
if (k === 4) {
|
|
127
|
+
y[0] = Q1[y[0]] ^ (L[3] & 0xFF);
|
|
128
|
+
y[1] = Q0[y[1]] ^ ((L[3] >>> 8) & 0xFF);
|
|
129
|
+
y[2] = Q0[y[2]] ^ ((L[3] >>> 16) & 0xFF);
|
|
130
|
+
y[3] = Q1[y[3]] ^ ((L[3] >>> 24) & 0xFF);
|
|
131
|
+
}
|
|
132
|
+
if (k >= 3) {
|
|
133
|
+
y[0] = Q1[y[0]] ^ (L[2] & 0xFF);
|
|
134
|
+
y[1] = Q1[y[1]] ^ ((L[2] >>> 8) & 0xFF);
|
|
135
|
+
y[2] = Q0[y[2]] ^ ((L[2] >>> 16) & 0xFF);
|
|
136
|
+
y[3] = Q0[y[3]] ^ ((L[2] >>> 24) & 0xFF);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Always do k >= 2
|
|
140
|
+
y[0] = Q0[Q0[y[0]] ^ (L[1] & 0xFF)] ^ (L[0] & 0xFF);
|
|
141
|
+
y[1] = Q0[Q1[y[1]] ^ ((L[1] >>> 8) & 0xFF)] ^ ((L[0] >>> 8) & 0xFF);
|
|
142
|
+
y[2] = Q1[Q0[y[2]] ^ ((L[1] >>> 16) & 0xFF)] ^ ((L[0] >>> 16) & 0xFF);
|
|
143
|
+
y[3] = Q1[Q1[y[3]] ^ ((L[1] >>> 24) & 0xFF)] ^ ((L[0] >>> 24) & 0xFF);
|
|
144
|
+
|
|
145
|
+
// Final q-box lookup
|
|
146
|
+
y[0] = Q1[y[0]];
|
|
147
|
+
y[1] = Q0[y[1]];
|
|
148
|
+
y[2] = Q1[y[2]];
|
|
149
|
+
y[3] = Q0[y[3]];
|
|
150
|
+
|
|
151
|
+
return mdsMultiply((y[3] << 24) | (y[2] << 16) | (y[1] << 8) | y[0]);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Rotate left 32-bit
|
|
156
|
+
*/
|
|
157
|
+
function ROL(x, n) {
|
|
158
|
+
return ((x << n) | (x >>> (32 - n))) >>> 0;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Rotate right 32-bit
|
|
163
|
+
*/
|
|
164
|
+
function ROR(x, n) {
|
|
165
|
+
return ((x >>> n) | (x << (32 - n))) >>> 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Generate subkeys from the key
|
|
170
|
+
*/
|
|
171
|
+
function generateSubkeys(key) {
|
|
172
|
+
const keyLen = key.length;
|
|
173
|
+
const k = keyLen / 8; // 2, 3, or 4
|
|
174
|
+
|
|
175
|
+
// Split key into Me (even words) and Mo (odd words)
|
|
176
|
+
const Me = new Array(k);
|
|
177
|
+
const Mo = new Array(k);
|
|
178
|
+
|
|
179
|
+
for (let i = 0; i < k; i++) {
|
|
180
|
+
const offset = i * 8;
|
|
181
|
+
Me[i] = (key[offset]) | (key[offset + 1] << 8) |
|
|
182
|
+
(key[offset + 2] << 16) | (key[offset + 3] << 24);
|
|
183
|
+
Mo[i] = (key[offset + 4]) | (key[offset + 5] << 8) |
|
|
184
|
+
(key[offset + 6] << 16) | (key[offset + 7] << 24);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Generate S-box keys using Reed-Solomon
|
|
188
|
+
const S = new Array(k);
|
|
189
|
+
for (let i = 0; i < k; i++) {
|
|
190
|
+
const offset = (k - 1 - i) * 8;
|
|
191
|
+
S[i] = rsMultiply(key.slice(offset, offset + 8));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Generate round subkeys
|
|
195
|
+
const subkeys = new Array(40);
|
|
196
|
+
const rho = 0x01010101;
|
|
197
|
+
|
|
198
|
+
for (let i = 0; i < 20; i++) {
|
|
199
|
+
const A = h(2 * i * rho, Me, k);
|
|
200
|
+
const B = ROL(h((2 * i + 1) * rho, Mo, k), 8);
|
|
201
|
+
subkeys[2 * i] = (A + B) >>> 0;
|
|
202
|
+
subkeys[2 * i + 1] = ROL((A + 2 * B) >>> 0, 9);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return { subkeys, S, k };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* g function using precomputed S-box keys
|
|
210
|
+
*/
|
|
211
|
+
function g(x, S, k) {
|
|
212
|
+
return h(x, S, k);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Encrypt a single 128-bit block
|
|
217
|
+
*/
|
|
218
|
+
function encryptBlock(block, keyData) {
|
|
219
|
+
const { subkeys, S, k } = keyData;
|
|
220
|
+
|
|
221
|
+
// Split block into 4 words (little-endian)
|
|
222
|
+
let R0 = (block[0]) | (block[1] << 8) | (block[2] << 16) | (block[3] << 24);
|
|
223
|
+
let R1 = (block[4]) | (block[5] << 8) | (block[6] << 16) | (block[7] << 24);
|
|
224
|
+
let R2 = (block[8]) | (block[9] << 8) | (block[10] << 16) | (block[11] << 24);
|
|
225
|
+
let R3 = (block[12]) | (block[13] << 8) | (block[14] << 16) | (block[15] << 24);
|
|
226
|
+
|
|
227
|
+
// Input whitening
|
|
228
|
+
R0 ^= subkeys[0];
|
|
229
|
+
R1 ^= subkeys[1];
|
|
230
|
+
R2 ^= subkeys[2];
|
|
231
|
+
R3 ^= subkeys[3];
|
|
232
|
+
|
|
233
|
+
// 16 rounds
|
|
234
|
+
for (let r = 0; r < NROUNDS; r += 2) {
|
|
235
|
+
let T0 = g(R0, S, k);
|
|
236
|
+
let T1 = g(ROL(R1, 8), S, k);
|
|
237
|
+
R2 = ROR(R2 ^ ((T0 + T1 + subkeys[8 + 2 * r]) >>> 0), 1);
|
|
238
|
+
R3 = ROL(R3, 1) ^ ((T0 + 2 * T1 + subkeys[9 + 2 * r]) >>> 0);
|
|
239
|
+
|
|
240
|
+
T0 = g(R2, S, k);
|
|
241
|
+
T1 = g(ROL(R3, 8), S, k);
|
|
242
|
+
R0 = ROR(R0 ^ ((T0 + T1 + subkeys[8 + 2 * r + 2]) >>> 0), 1);
|
|
243
|
+
R1 = ROL(R1, 1) ^ ((T0 + 2 * T1 + subkeys[9 + 2 * r + 2]) >>> 0);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Output whitening (with undo of last swap)
|
|
247
|
+
R2 ^= subkeys[4];
|
|
248
|
+
R3 ^= subkeys[5];
|
|
249
|
+
R0 ^= subkeys[6];
|
|
250
|
+
R1 ^= subkeys[7];
|
|
251
|
+
|
|
252
|
+
// Convert back to bytes (little-endian)
|
|
253
|
+
return [
|
|
254
|
+
R2 & 0xFF, (R2 >>> 8) & 0xFF, (R2 >>> 16) & 0xFF, (R2 >>> 24) & 0xFF,
|
|
255
|
+
R3 & 0xFF, (R3 >>> 8) & 0xFF, (R3 >>> 16) & 0xFF, (R3 >>> 24) & 0xFF,
|
|
256
|
+
R0 & 0xFF, (R0 >>> 8) & 0xFF, (R0 >>> 16) & 0xFF, (R0 >>> 24) & 0xFF,
|
|
257
|
+
R1 & 0xFF, (R1 >>> 8) & 0xFF, (R1 >>> 16) & 0xFF, (R1 >>> 24) & 0xFF
|
|
258
|
+
];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Decrypt a single 128-bit block
|
|
263
|
+
*/
|
|
264
|
+
function decryptBlock(block, keyData) {
|
|
265
|
+
const { subkeys, S, k } = keyData;
|
|
266
|
+
|
|
267
|
+
// Split block into 4 words (little-endian)
|
|
268
|
+
let R0 = (block[0]) | (block[1] << 8) | (block[2] << 16) | (block[3] << 24);
|
|
269
|
+
let R1 = (block[4]) | (block[5] << 8) | (block[6] << 16) | (block[7] << 24);
|
|
270
|
+
let R2 = (block[8]) | (block[9] << 8) | (block[10] << 16) | (block[11] << 24);
|
|
271
|
+
let R3 = (block[12]) | (block[13] << 8) | (block[14] << 16) | (block[15] << 24);
|
|
272
|
+
|
|
273
|
+
// Input whitening (reverse of output whitening)
|
|
274
|
+
R0 ^= subkeys[4];
|
|
275
|
+
R1 ^= subkeys[5];
|
|
276
|
+
R2 ^= subkeys[6];
|
|
277
|
+
R3 ^= subkeys[7];
|
|
278
|
+
|
|
279
|
+
// 16 rounds in reverse
|
|
280
|
+
for (let r = NROUNDS - 2; r >= 0; r -= 2) {
|
|
281
|
+
let T0 = g(R0, S, k);
|
|
282
|
+
let T1 = g(ROL(R1, 8), S, k);
|
|
283
|
+
R2 = ROL(R2, 1) ^ ((T0 + T1 + subkeys[8 + 2 * r + 2]) >>> 0);
|
|
284
|
+
R3 = ROR(R3 ^ ((T0 + 2 * T1 + subkeys[9 + 2 * r + 2]) >>> 0), 1);
|
|
285
|
+
|
|
286
|
+
T0 = g(R2, S, k);
|
|
287
|
+
T1 = g(ROL(R3, 8), S, k);
|
|
288
|
+
R0 = ROL(R0, 1) ^ ((T0 + T1 + subkeys[8 + 2 * r]) >>> 0);
|
|
289
|
+
R1 = ROR(R1 ^ ((T0 + 2 * T1 + subkeys[9 + 2 * r]) >>> 0), 1);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Output whitening (reverse of input whitening)
|
|
293
|
+
R2 ^= subkeys[0];
|
|
294
|
+
R3 ^= subkeys[1];
|
|
295
|
+
R0 ^= subkeys[2];
|
|
296
|
+
R1 ^= subkeys[3];
|
|
297
|
+
|
|
298
|
+
// Convert back to bytes (little-endian)
|
|
299
|
+
return [
|
|
300
|
+
R2 & 0xFF, (R2 >>> 8) & 0xFF, (R2 >>> 16) & 0xFF, (R2 >>> 24) & 0xFF,
|
|
301
|
+
R3 & 0xFF, (R3 >>> 8) & 0xFF, (R3 >>> 16) & 0xFF, (R3 >>> 24) & 0xFF,
|
|
302
|
+
R0 & 0xFF, (R0 >>> 8) & 0xFF, (R0 >>> 16) & 0xFF, (R0 >>> 24) & 0xFF,
|
|
303
|
+
R1 & 0xFF, (R1 >>> 8) & 0xFF, (R1 >>> 16) & 0xFF, (R1 >>> 24) & 0xFF
|
|
304
|
+
];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* XOR two 16-byte blocks
|
|
309
|
+
*/
|
|
310
|
+
function xorBlocks(a, b) {
|
|
311
|
+
const result = new Array(16);
|
|
312
|
+
for (let i = 0; i < 16; i++) {
|
|
313
|
+
result[i] = a[i] ^ b[i];
|
|
314
|
+
}
|
|
315
|
+
return result;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Increment counter (little-endian)
|
|
320
|
+
*/
|
|
321
|
+
function incrementCounter(counter) {
|
|
322
|
+
const result = [...counter];
|
|
323
|
+
for (let i = 0; i < 16; i++) {
|
|
324
|
+
result[i]++;
|
|
325
|
+
if (result[i] <= 255) break;
|
|
326
|
+
result[i] = 0;
|
|
327
|
+
}
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Apply padding to message
|
|
333
|
+
* @param {number[]} message - Original message
|
|
334
|
+
* @param {string} padding - Padding type ("NO", "PKCS5", "ZERO", "RANDOM", "BIT")
|
|
335
|
+
* @param {number} blockSize - Block size in bytes
|
|
336
|
+
* @returns {number[]} - Padded message
|
|
337
|
+
*/
|
|
338
|
+
function applyPadding(message, padding, blockSize) {
|
|
339
|
+
const remainder = message.length % blockSize;
|
|
340
|
+
let nPadding = remainder === 0 ? 0 : blockSize - remainder;
|
|
341
|
+
|
|
342
|
+
// For PKCS5, always add at least one byte (full block if already aligned)
|
|
343
|
+
if (padding === "PKCS5" && remainder === 0) {
|
|
344
|
+
nPadding = blockSize;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (nPadding === 0) return [...message];
|
|
348
|
+
|
|
349
|
+
const paddedMessage = [...message];
|
|
350
|
+
|
|
351
|
+
switch (padding) {
|
|
352
|
+
case "NO":
|
|
353
|
+
throw new OperationError(`No padding requested but input is not a ${blockSize}-byte multiple.`);
|
|
354
|
+
|
|
355
|
+
case "PKCS5":
|
|
356
|
+
for (let i = 0; i < nPadding; i++) {
|
|
357
|
+
paddedMessage.push(nPadding);
|
|
358
|
+
}
|
|
359
|
+
break;
|
|
360
|
+
|
|
361
|
+
case "ZERO":
|
|
362
|
+
for (let i = 0; i < nPadding; i++) {
|
|
363
|
+
paddedMessage.push(0);
|
|
364
|
+
}
|
|
365
|
+
break;
|
|
366
|
+
|
|
367
|
+
case "RANDOM":
|
|
368
|
+
for (let i = 0; i < nPadding; i++) {
|
|
369
|
+
paddedMessage.push(Math.floor(Math.random() * 256));
|
|
370
|
+
}
|
|
371
|
+
break;
|
|
372
|
+
|
|
373
|
+
case "BIT":
|
|
374
|
+
paddedMessage.push(0x80);
|
|
375
|
+
for (let i = 1; i < nPadding; i++) {
|
|
376
|
+
paddedMessage.push(0);
|
|
377
|
+
}
|
|
378
|
+
break;
|
|
379
|
+
|
|
380
|
+
default:
|
|
381
|
+
throw new OperationError(`Unknown padding type: ${padding}`);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return paddedMessage;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Remove padding from message
|
|
389
|
+
* @param {number[]} message - Padded message
|
|
390
|
+
* @param {string} padding - Padding type ("NO", "PKCS5", "ZERO", "RANDOM", "BIT")
|
|
391
|
+
* @param {number} blockSize - Block size in bytes
|
|
392
|
+
* @returns {number[]} - Unpadded message
|
|
393
|
+
*/
|
|
394
|
+
function removePadding(message, padding, blockSize) {
|
|
395
|
+
if (message.length === 0) return message;
|
|
396
|
+
|
|
397
|
+
switch (padding) {
|
|
398
|
+
case "NO":
|
|
399
|
+
case "ZERO":
|
|
400
|
+
case "RANDOM":
|
|
401
|
+
// These padding types cannot be reliably removed
|
|
402
|
+
return message;
|
|
403
|
+
|
|
404
|
+
case "PKCS5": {
|
|
405
|
+
const padByte = message[message.length - 1];
|
|
406
|
+
if (padByte > 0 && padByte <= blockSize) {
|
|
407
|
+
// Verify padding
|
|
408
|
+
for (let i = 0; i < padByte; i++) {
|
|
409
|
+
if (message[message.length - 1 - i] !== padByte) {
|
|
410
|
+
throw new OperationError("Invalid PKCS#5 padding.");
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return message.slice(0, message.length - padByte);
|
|
414
|
+
}
|
|
415
|
+
throw new OperationError("Invalid PKCS#5 padding.");
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
case "BIT": {
|
|
419
|
+
// Find 0x80 byte working backwards, skipping zeros
|
|
420
|
+
for (let i = message.length - 1; i >= 0; i--) {
|
|
421
|
+
if (message[i] === 0x80) {
|
|
422
|
+
return message.slice(0, i);
|
|
423
|
+
} else if (message[i] !== 0) {
|
|
424
|
+
throw new OperationError("Invalid BIT padding.");
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
throw new OperationError("Invalid BIT padding.");
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
default:
|
|
431
|
+
throw new OperationError(`Unknown padding type: ${padding}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Encrypt using Twofish cipher with specified block mode
|
|
437
|
+
*
|
|
438
|
+
* @param {number[]} message - Plaintext as byte array
|
|
439
|
+
* @param {number[]} key - Key (16, 24, or 32 bytes)
|
|
440
|
+
* @param {number[]} iv - IV (16 bytes, not used for ECB)
|
|
441
|
+
* @param {string} mode - Block cipher mode ("ECB", "CBC", "CFB", "OFB", "CTR")
|
|
442
|
+
* @param {string} padding - Padding type ("NO", "PKCS5", "ZERO", "RANDOM", "BIT")
|
|
443
|
+
* @returns {number[]} - Ciphertext as byte array
|
|
444
|
+
*/
|
|
445
|
+
export function encryptTwofish(message, key, iv, mode = "ECB", padding = "PKCS5") {
|
|
446
|
+
const messageLength = message.length;
|
|
447
|
+
if (messageLength === 0) return [];
|
|
448
|
+
|
|
449
|
+
const keyData = generateSubkeys(key);
|
|
450
|
+
|
|
451
|
+
// Apply padding for ECB/CBC modes
|
|
452
|
+
let paddedMessage;
|
|
453
|
+
if (mode === "ECB" || mode === "CBC") {
|
|
454
|
+
paddedMessage = applyPadding(message, padding, BLOCKSIZE);
|
|
455
|
+
} else {
|
|
456
|
+
// Stream modes (CFB, OFB, CTR) don't need padding
|
|
457
|
+
paddedMessage = [...message];
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const cipherText = [];
|
|
461
|
+
|
|
462
|
+
switch (mode) {
|
|
463
|
+
case "ECB":
|
|
464
|
+
for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) {
|
|
465
|
+
const block = paddedMessage.slice(i, i + BLOCKSIZE);
|
|
466
|
+
cipherText.push(...encryptBlock(block, keyData));
|
|
467
|
+
}
|
|
468
|
+
break;
|
|
469
|
+
|
|
470
|
+
case "CBC": {
|
|
471
|
+
let ivBlock = [...iv];
|
|
472
|
+
for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) {
|
|
473
|
+
const block = paddedMessage.slice(i, i + BLOCKSIZE);
|
|
474
|
+
const xored = xorBlocks(block, ivBlock);
|
|
475
|
+
ivBlock = encryptBlock(xored, keyData);
|
|
476
|
+
cipherText.push(...ivBlock);
|
|
477
|
+
}
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
case "CFB": {
|
|
482
|
+
let ivBlock = [...iv];
|
|
483
|
+
for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) {
|
|
484
|
+
const encrypted = encryptBlock(ivBlock, keyData);
|
|
485
|
+
const block = paddedMessage.slice(i, i + BLOCKSIZE);
|
|
486
|
+
ivBlock = xorBlocks(encrypted, block);
|
|
487
|
+
cipherText.push(...ivBlock);
|
|
488
|
+
}
|
|
489
|
+
return cipherText.slice(0, messageLength);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
case "OFB": {
|
|
493
|
+
let ivBlock = [...iv];
|
|
494
|
+
for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) {
|
|
495
|
+
ivBlock = encryptBlock(ivBlock, keyData);
|
|
496
|
+
const block = paddedMessage.slice(i, i + BLOCKSIZE);
|
|
497
|
+
cipherText.push(...xorBlocks(ivBlock, block));
|
|
498
|
+
}
|
|
499
|
+
return cipherText.slice(0, messageLength);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
case "CTR": {
|
|
503
|
+
let counter = [...iv];
|
|
504
|
+
for (let i = 0; i < paddedMessage.length; i += BLOCKSIZE) {
|
|
505
|
+
const encrypted = encryptBlock(counter, keyData);
|
|
506
|
+
const block = paddedMessage.slice(i, i + BLOCKSIZE);
|
|
507
|
+
cipherText.push(...xorBlocks(encrypted, block));
|
|
508
|
+
counter = incrementCounter(counter);
|
|
509
|
+
}
|
|
510
|
+
return cipherText.slice(0, messageLength);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
default:
|
|
514
|
+
throw new OperationError(`Invalid block cipher mode: ${mode}`);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return cipherText;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Decrypt using Twofish cipher with specified block mode
|
|
522
|
+
*
|
|
523
|
+
* @param {number[]} cipherText - Ciphertext as byte array
|
|
524
|
+
* @param {number[]} key - Key (16, 24, or 32 bytes)
|
|
525
|
+
* @param {number[]} iv - IV (16 bytes, not used for ECB)
|
|
526
|
+
* @param {string} mode - Block cipher mode ("ECB", "CBC", "CFB", "OFB", "CTR")
|
|
527
|
+
* @param {string} padding - Padding type ("NO", "PKCS5", "ZERO", "RANDOM", "BIT")
|
|
528
|
+
* @returns {number[]} - Plaintext as byte array
|
|
529
|
+
*/
|
|
530
|
+
export function decryptTwofish(cipherText, key, iv, mode = "ECB", padding = "PKCS5") {
|
|
531
|
+
const originalLength = cipherText.length;
|
|
532
|
+
if (originalLength === 0) return [];
|
|
533
|
+
|
|
534
|
+
const keyData = generateSubkeys(key);
|
|
535
|
+
|
|
536
|
+
if (mode === "ECB" || mode === "CBC") {
|
|
537
|
+
if ((originalLength % BLOCKSIZE) !== 0)
|
|
538
|
+
throw new OperationError(`Invalid ciphertext length: ${originalLength} bytes. Must be a multiple of 16.`);
|
|
539
|
+
} else {
|
|
540
|
+
// Pad for stream modes
|
|
541
|
+
while ((cipherText.length % BLOCKSIZE) !== 0)
|
|
542
|
+
cipherText.push(0);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const plainText = [];
|
|
546
|
+
|
|
547
|
+
switch (mode) {
|
|
548
|
+
case "ECB":
|
|
549
|
+
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
|
550
|
+
const block = cipherText.slice(i, i + BLOCKSIZE);
|
|
551
|
+
plainText.push(...decryptBlock(block, keyData));
|
|
552
|
+
}
|
|
553
|
+
break;
|
|
554
|
+
|
|
555
|
+
case "CBC": {
|
|
556
|
+
let ivBlock = [...iv];
|
|
557
|
+
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
|
558
|
+
const block = cipherText.slice(i, i + BLOCKSIZE);
|
|
559
|
+
const decrypted = decryptBlock(block, keyData);
|
|
560
|
+
plainText.push(...xorBlocks(decrypted, ivBlock));
|
|
561
|
+
ivBlock = block;
|
|
562
|
+
}
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
case "CFB": {
|
|
567
|
+
let ivBlock = [...iv];
|
|
568
|
+
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
|
569
|
+
const encrypted = encryptBlock(ivBlock, keyData);
|
|
570
|
+
const block = cipherText.slice(i, i + BLOCKSIZE);
|
|
571
|
+
plainText.push(...xorBlocks(encrypted, block));
|
|
572
|
+
ivBlock = block;
|
|
573
|
+
}
|
|
574
|
+
return plainText.slice(0, originalLength);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
case "OFB": {
|
|
578
|
+
let ivBlock = [...iv];
|
|
579
|
+
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
|
580
|
+
ivBlock = encryptBlock(ivBlock, keyData);
|
|
581
|
+
const block = cipherText.slice(i, i + BLOCKSIZE);
|
|
582
|
+
plainText.push(...xorBlocks(ivBlock, block));
|
|
583
|
+
}
|
|
584
|
+
return plainText.slice(0, originalLength);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
case "CTR": {
|
|
588
|
+
let counter = [...iv];
|
|
589
|
+
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
|
590
|
+
const encrypted = encryptBlock(counter, keyData);
|
|
591
|
+
const block = cipherText.slice(i, i + BLOCKSIZE);
|
|
592
|
+
plainText.push(...xorBlocks(encrypted, block));
|
|
593
|
+
counter = incrementCounter(counter);
|
|
594
|
+
}
|
|
595
|
+
return plainText.slice(0, originalLength);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
default:
|
|
599
|
+
throw new OperationError(`Invalid block cipher mode: ${mode}`);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Remove padding for ECB/CBC modes
|
|
603
|
+
if (mode === "ECB" || mode === "CBC") {
|
|
604
|
+
return removePadding(plainText, padding, BLOCKSIZE);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return plainText.slice(0, originalLength);
|
|
608
|
+
}
|