@gjsify/crypto 0.1.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/README.md +27 -0
- package/lib/esm/asn1.js +504 -0
- package/lib/esm/bigint-math.js +34 -0
- package/lib/esm/cipher.js +1272 -0
- package/lib/esm/constants.js +15 -0
- package/lib/esm/crypto-utils.js +47 -0
- package/lib/esm/dh.js +411 -0
- package/lib/esm/ecdh.js +356 -0
- package/lib/esm/ecdsa.js +125 -0
- package/lib/esm/hash.js +100 -0
- package/lib/esm/hkdf.js +58 -0
- package/lib/esm/hmac.js +93 -0
- package/lib/esm/index.js +158 -0
- package/lib/esm/key-object.js +330 -0
- package/lib/esm/mgf1.js +27 -0
- package/lib/esm/pbkdf2.js +68 -0
- package/lib/esm/public-encrypt.js +175 -0
- package/lib/esm/random.js +138 -0
- package/lib/esm/rsa-oaep.js +95 -0
- package/lib/esm/rsa-pss.js +100 -0
- package/lib/esm/scrypt.js +134 -0
- package/lib/esm/sign.js +248 -0
- package/lib/esm/timing-safe-equal.js +13 -0
- package/lib/esm/x509.js +214 -0
- package/lib/types/asn1.d.ts +87 -0
- package/lib/types/bigint-math.d.ts +13 -0
- package/lib/types/cipher.d.ts +84 -0
- package/lib/types/constants.d.ts +10 -0
- package/lib/types/crypto-utils.d.ts +22 -0
- package/lib/types/dh.d.ts +79 -0
- package/lib/types/ecdh.d.ts +96 -0
- package/lib/types/ecdsa.d.ts +21 -0
- package/lib/types/hash.d.ts +25 -0
- package/lib/types/hkdf.d.ts +9 -0
- package/lib/types/hmac.d.ts +20 -0
- package/lib/types/index.d.ts +105 -0
- package/lib/types/key-object.d.ts +36 -0
- package/lib/types/mgf1.d.ts +5 -0
- package/lib/types/pbkdf2.d.ts +9 -0
- package/lib/types/public-encrypt.d.ts +42 -0
- package/lib/types/random.d.ts +22 -0
- package/lib/types/rsa-oaep.d.ts +8 -0
- package/lib/types/rsa-pss.d.ts +8 -0
- package/lib/types/scrypt.d.ts +11 -0
- package/lib/types/sign.d.ts +61 -0
- package/lib/types/timing-safe-equal.d.ts +6 -0
- package/lib/types/x509.d.ts +72 -0
- package/package.json +45 -0
- package/src/asn1.ts +797 -0
- package/src/bigint-math.ts +45 -0
- package/src/cipher.spec.ts +332 -0
- package/src/cipher.ts +952 -0
- package/src/constants.ts +16 -0
- package/src/crypto-utils.ts +64 -0
- package/src/dh.spec.ts +111 -0
- package/src/dh.ts +761 -0
- package/src/ecdh.spec.ts +116 -0
- package/src/ecdh.ts +624 -0
- package/src/ecdsa.ts +243 -0
- package/src/extended.spec.ts +444 -0
- package/src/gcm.spec.ts +141 -0
- package/src/hash.spec.ts +86 -0
- package/src/hash.ts +119 -0
- package/src/hkdf.ts +99 -0
- package/src/hmac.spec.ts +64 -0
- package/src/hmac.ts +123 -0
- package/src/index.ts +93 -0
- package/src/key-object.spec.ts +202 -0
- package/src/key-object.ts +401 -0
- package/src/mgf1.ts +37 -0
- package/src/pbkdf2.spec.ts +76 -0
- package/src/pbkdf2.ts +106 -0
- package/src/public-encrypt.ts +288 -0
- package/src/random.spec.ts +133 -0
- package/src/random.ts +183 -0
- package/src/rsa-oaep.ts +167 -0
- package/src/rsa-pss.ts +190 -0
- package/src/scrypt.spec.ts +90 -0
- package/src/scrypt.ts +191 -0
- package/src/sign.spec.ts +160 -0
- package/src/sign.ts +319 -0
- package/src/test.mts +19 -0
- package/src/timing-safe-equal.ts +21 -0
- package/src/x509.spec.ts +210 -0
- package/src/x509.ts +262 -0
- package/tsconfig.json +31 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,1272 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
const SBOX = new Uint8Array([
|
|
3
|
+
99,
|
|
4
|
+
124,
|
|
5
|
+
119,
|
|
6
|
+
123,
|
|
7
|
+
242,
|
|
8
|
+
107,
|
|
9
|
+
111,
|
|
10
|
+
197,
|
|
11
|
+
48,
|
|
12
|
+
1,
|
|
13
|
+
103,
|
|
14
|
+
43,
|
|
15
|
+
254,
|
|
16
|
+
215,
|
|
17
|
+
171,
|
|
18
|
+
118,
|
|
19
|
+
202,
|
|
20
|
+
130,
|
|
21
|
+
201,
|
|
22
|
+
125,
|
|
23
|
+
250,
|
|
24
|
+
89,
|
|
25
|
+
71,
|
|
26
|
+
240,
|
|
27
|
+
173,
|
|
28
|
+
212,
|
|
29
|
+
162,
|
|
30
|
+
175,
|
|
31
|
+
156,
|
|
32
|
+
164,
|
|
33
|
+
114,
|
|
34
|
+
192,
|
|
35
|
+
183,
|
|
36
|
+
253,
|
|
37
|
+
147,
|
|
38
|
+
38,
|
|
39
|
+
54,
|
|
40
|
+
63,
|
|
41
|
+
247,
|
|
42
|
+
204,
|
|
43
|
+
52,
|
|
44
|
+
165,
|
|
45
|
+
229,
|
|
46
|
+
241,
|
|
47
|
+
113,
|
|
48
|
+
216,
|
|
49
|
+
49,
|
|
50
|
+
21,
|
|
51
|
+
4,
|
|
52
|
+
199,
|
|
53
|
+
35,
|
|
54
|
+
195,
|
|
55
|
+
24,
|
|
56
|
+
150,
|
|
57
|
+
5,
|
|
58
|
+
154,
|
|
59
|
+
7,
|
|
60
|
+
18,
|
|
61
|
+
128,
|
|
62
|
+
226,
|
|
63
|
+
235,
|
|
64
|
+
39,
|
|
65
|
+
178,
|
|
66
|
+
117,
|
|
67
|
+
9,
|
|
68
|
+
131,
|
|
69
|
+
44,
|
|
70
|
+
26,
|
|
71
|
+
27,
|
|
72
|
+
110,
|
|
73
|
+
90,
|
|
74
|
+
160,
|
|
75
|
+
82,
|
|
76
|
+
59,
|
|
77
|
+
214,
|
|
78
|
+
179,
|
|
79
|
+
41,
|
|
80
|
+
227,
|
|
81
|
+
47,
|
|
82
|
+
132,
|
|
83
|
+
83,
|
|
84
|
+
209,
|
|
85
|
+
0,
|
|
86
|
+
237,
|
|
87
|
+
32,
|
|
88
|
+
252,
|
|
89
|
+
177,
|
|
90
|
+
91,
|
|
91
|
+
106,
|
|
92
|
+
203,
|
|
93
|
+
190,
|
|
94
|
+
57,
|
|
95
|
+
74,
|
|
96
|
+
76,
|
|
97
|
+
88,
|
|
98
|
+
207,
|
|
99
|
+
208,
|
|
100
|
+
239,
|
|
101
|
+
170,
|
|
102
|
+
251,
|
|
103
|
+
67,
|
|
104
|
+
77,
|
|
105
|
+
51,
|
|
106
|
+
133,
|
|
107
|
+
69,
|
|
108
|
+
249,
|
|
109
|
+
2,
|
|
110
|
+
127,
|
|
111
|
+
80,
|
|
112
|
+
60,
|
|
113
|
+
159,
|
|
114
|
+
168,
|
|
115
|
+
81,
|
|
116
|
+
163,
|
|
117
|
+
64,
|
|
118
|
+
143,
|
|
119
|
+
146,
|
|
120
|
+
157,
|
|
121
|
+
56,
|
|
122
|
+
245,
|
|
123
|
+
188,
|
|
124
|
+
182,
|
|
125
|
+
218,
|
|
126
|
+
33,
|
|
127
|
+
16,
|
|
128
|
+
255,
|
|
129
|
+
243,
|
|
130
|
+
210,
|
|
131
|
+
205,
|
|
132
|
+
12,
|
|
133
|
+
19,
|
|
134
|
+
236,
|
|
135
|
+
95,
|
|
136
|
+
151,
|
|
137
|
+
68,
|
|
138
|
+
23,
|
|
139
|
+
196,
|
|
140
|
+
167,
|
|
141
|
+
126,
|
|
142
|
+
61,
|
|
143
|
+
100,
|
|
144
|
+
93,
|
|
145
|
+
25,
|
|
146
|
+
115,
|
|
147
|
+
96,
|
|
148
|
+
129,
|
|
149
|
+
79,
|
|
150
|
+
220,
|
|
151
|
+
34,
|
|
152
|
+
42,
|
|
153
|
+
144,
|
|
154
|
+
136,
|
|
155
|
+
70,
|
|
156
|
+
238,
|
|
157
|
+
184,
|
|
158
|
+
20,
|
|
159
|
+
222,
|
|
160
|
+
94,
|
|
161
|
+
11,
|
|
162
|
+
219,
|
|
163
|
+
224,
|
|
164
|
+
50,
|
|
165
|
+
58,
|
|
166
|
+
10,
|
|
167
|
+
73,
|
|
168
|
+
6,
|
|
169
|
+
36,
|
|
170
|
+
92,
|
|
171
|
+
194,
|
|
172
|
+
211,
|
|
173
|
+
172,
|
|
174
|
+
98,
|
|
175
|
+
145,
|
|
176
|
+
149,
|
|
177
|
+
228,
|
|
178
|
+
121,
|
|
179
|
+
231,
|
|
180
|
+
200,
|
|
181
|
+
55,
|
|
182
|
+
109,
|
|
183
|
+
141,
|
|
184
|
+
213,
|
|
185
|
+
78,
|
|
186
|
+
169,
|
|
187
|
+
108,
|
|
188
|
+
86,
|
|
189
|
+
244,
|
|
190
|
+
234,
|
|
191
|
+
101,
|
|
192
|
+
122,
|
|
193
|
+
174,
|
|
194
|
+
8,
|
|
195
|
+
186,
|
|
196
|
+
120,
|
|
197
|
+
37,
|
|
198
|
+
46,
|
|
199
|
+
28,
|
|
200
|
+
166,
|
|
201
|
+
180,
|
|
202
|
+
198,
|
|
203
|
+
232,
|
|
204
|
+
221,
|
|
205
|
+
116,
|
|
206
|
+
31,
|
|
207
|
+
75,
|
|
208
|
+
189,
|
|
209
|
+
139,
|
|
210
|
+
138,
|
|
211
|
+
112,
|
|
212
|
+
62,
|
|
213
|
+
181,
|
|
214
|
+
102,
|
|
215
|
+
72,
|
|
216
|
+
3,
|
|
217
|
+
246,
|
|
218
|
+
14,
|
|
219
|
+
97,
|
|
220
|
+
53,
|
|
221
|
+
87,
|
|
222
|
+
185,
|
|
223
|
+
134,
|
|
224
|
+
193,
|
|
225
|
+
29,
|
|
226
|
+
158,
|
|
227
|
+
225,
|
|
228
|
+
248,
|
|
229
|
+
152,
|
|
230
|
+
17,
|
|
231
|
+
105,
|
|
232
|
+
217,
|
|
233
|
+
142,
|
|
234
|
+
148,
|
|
235
|
+
155,
|
|
236
|
+
30,
|
|
237
|
+
135,
|
|
238
|
+
233,
|
|
239
|
+
206,
|
|
240
|
+
85,
|
|
241
|
+
40,
|
|
242
|
+
223,
|
|
243
|
+
140,
|
|
244
|
+
161,
|
|
245
|
+
137,
|
|
246
|
+
13,
|
|
247
|
+
191,
|
|
248
|
+
230,
|
|
249
|
+
66,
|
|
250
|
+
104,
|
|
251
|
+
65,
|
|
252
|
+
153,
|
|
253
|
+
45,
|
|
254
|
+
15,
|
|
255
|
+
176,
|
|
256
|
+
84,
|
|
257
|
+
187,
|
|
258
|
+
22
|
|
259
|
+
]);
|
|
260
|
+
const INV_SBOX = new Uint8Array([
|
|
261
|
+
82,
|
|
262
|
+
9,
|
|
263
|
+
106,
|
|
264
|
+
213,
|
|
265
|
+
48,
|
|
266
|
+
54,
|
|
267
|
+
165,
|
|
268
|
+
56,
|
|
269
|
+
191,
|
|
270
|
+
64,
|
|
271
|
+
163,
|
|
272
|
+
158,
|
|
273
|
+
129,
|
|
274
|
+
243,
|
|
275
|
+
215,
|
|
276
|
+
251,
|
|
277
|
+
124,
|
|
278
|
+
227,
|
|
279
|
+
57,
|
|
280
|
+
130,
|
|
281
|
+
155,
|
|
282
|
+
47,
|
|
283
|
+
255,
|
|
284
|
+
135,
|
|
285
|
+
52,
|
|
286
|
+
142,
|
|
287
|
+
67,
|
|
288
|
+
68,
|
|
289
|
+
196,
|
|
290
|
+
222,
|
|
291
|
+
233,
|
|
292
|
+
203,
|
|
293
|
+
84,
|
|
294
|
+
123,
|
|
295
|
+
148,
|
|
296
|
+
50,
|
|
297
|
+
166,
|
|
298
|
+
194,
|
|
299
|
+
35,
|
|
300
|
+
61,
|
|
301
|
+
238,
|
|
302
|
+
76,
|
|
303
|
+
149,
|
|
304
|
+
11,
|
|
305
|
+
66,
|
|
306
|
+
250,
|
|
307
|
+
195,
|
|
308
|
+
78,
|
|
309
|
+
8,
|
|
310
|
+
46,
|
|
311
|
+
161,
|
|
312
|
+
102,
|
|
313
|
+
40,
|
|
314
|
+
217,
|
|
315
|
+
36,
|
|
316
|
+
178,
|
|
317
|
+
118,
|
|
318
|
+
91,
|
|
319
|
+
162,
|
|
320
|
+
73,
|
|
321
|
+
109,
|
|
322
|
+
139,
|
|
323
|
+
209,
|
|
324
|
+
37,
|
|
325
|
+
114,
|
|
326
|
+
248,
|
|
327
|
+
246,
|
|
328
|
+
100,
|
|
329
|
+
134,
|
|
330
|
+
104,
|
|
331
|
+
152,
|
|
332
|
+
22,
|
|
333
|
+
212,
|
|
334
|
+
164,
|
|
335
|
+
92,
|
|
336
|
+
204,
|
|
337
|
+
93,
|
|
338
|
+
101,
|
|
339
|
+
182,
|
|
340
|
+
146,
|
|
341
|
+
108,
|
|
342
|
+
112,
|
|
343
|
+
72,
|
|
344
|
+
80,
|
|
345
|
+
253,
|
|
346
|
+
237,
|
|
347
|
+
185,
|
|
348
|
+
218,
|
|
349
|
+
94,
|
|
350
|
+
21,
|
|
351
|
+
70,
|
|
352
|
+
87,
|
|
353
|
+
167,
|
|
354
|
+
141,
|
|
355
|
+
157,
|
|
356
|
+
132,
|
|
357
|
+
144,
|
|
358
|
+
216,
|
|
359
|
+
171,
|
|
360
|
+
0,
|
|
361
|
+
140,
|
|
362
|
+
188,
|
|
363
|
+
211,
|
|
364
|
+
10,
|
|
365
|
+
247,
|
|
366
|
+
228,
|
|
367
|
+
88,
|
|
368
|
+
5,
|
|
369
|
+
184,
|
|
370
|
+
179,
|
|
371
|
+
69,
|
|
372
|
+
6,
|
|
373
|
+
208,
|
|
374
|
+
44,
|
|
375
|
+
30,
|
|
376
|
+
143,
|
|
377
|
+
202,
|
|
378
|
+
63,
|
|
379
|
+
15,
|
|
380
|
+
2,
|
|
381
|
+
193,
|
|
382
|
+
175,
|
|
383
|
+
189,
|
|
384
|
+
3,
|
|
385
|
+
1,
|
|
386
|
+
19,
|
|
387
|
+
138,
|
|
388
|
+
107,
|
|
389
|
+
58,
|
|
390
|
+
145,
|
|
391
|
+
17,
|
|
392
|
+
65,
|
|
393
|
+
79,
|
|
394
|
+
103,
|
|
395
|
+
220,
|
|
396
|
+
234,
|
|
397
|
+
151,
|
|
398
|
+
242,
|
|
399
|
+
207,
|
|
400
|
+
206,
|
|
401
|
+
240,
|
|
402
|
+
180,
|
|
403
|
+
230,
|
|
404
|
+
115,
|
|
405
|
+
150,
|
|
406
|
+
172,
|
|
407
|
+
116,
|
|
408
|
+
34,
|
|
409
|
+
231,
|
|
410
|
+
173,
|
|
411
|
+
53,
|
|
412
|
+
133,
|
|
413
|
+
226,
|
|
414
|
+
249,
|
|
415
|
+
55,
|
|
416
|
+
232,
|
|
417
|
+
28,
|
|
418
|
+
117,
|
|
419
|
+
223,
|
|
420
|
+
110,
|
|
421
|
+
71,
|
|
422
|
+
241,
|
|
423
|
+
26,
|
|
424
|
+
113,
|
|
425
|
+
29,
|
|
426
|
+
41,
|
|
427
|
+
197,
|
|
428
|
+
137,
|
|
429
|
+
111,
|
|
430
|
+
183,
|
|
431
|
+
98,
|
|
432
|
+
14,
|
|
433
|
+
170,
|
|
434
|
+
24,
|
|
435
|
+
190,
|
|
436
|
+
27,
|
|
437
|
+
252,
|
|
438
|
+
86,
|
|
439
|
+
62,
|
|
440
|
+
75,
|
|
441
|
+
198,
|
|
442
|
+
210,
|
|
443
|
+
121,
|
|
444
|
+
32,
|
|
445
|
+
154,
|
|
446
|
+
219,
|
|
447
|
+
192,
|
|
448
|
+
254,
|
|
449
|
+
120,
|
|
450
|
+
205,
|
|
451
|
+
90,
|
|
452
|
+
244,
|
|
453
|
+
31,
|
|
454
|
+
221,
|
|
455
|
+
168,
|
|
456
|
+
51,
|
|
457
|
+
136,
|
|
458
|
+
7,
|
|
459
|
+
199,
|
|
460
|
+
49,
|
|
461
|
+
177,
|
|
462
|
+
18,
|
|
463
|
+
16,
|
|
464
|
+
89,
|
|
465
|
+
39,
|
|
466
|
+
128,
|
|
467
|
+
236,
|
|
468
|
+
95,
|
|
469
|
+
96,
|
|
470
|
+
81,
|
|
471
|
+
127,
|
|
472
|
+
169,
|
|
473
|
+
25,
|
|
474
|
+
181,
|
|
475
|
+
74,
|
|
476
|
+
13,
|
|
477
|
+
45,
|
|
478
|
+
229,
|
|
479
|
+
122,
|
|
480
|
+
159,
|
|
481
|
+
147,
|
|
482
|
+
201,
|
|
483
|
+
156,
|
|
484
|
+
239,
|
|
485
|
+
160,
|
|
486
|
+
224,
|
|
487
|
+
59,
|
|
488
|
+
77,
|
|
489
|
+
174,
|
|
490
|
+
42,
|
|
491
|
+
245,
|
|
492
|
+
176,
|
|
493
|
+
200,
|
|
494
|
+
235,
|
|
495
|
+
187,
|
|
496
|
+
60,
|
|
497
|
+
131,
|
|
498
|
+
83,
|
|
499
|
+
153,
|
|
500
|
+
97,
|
|
501
|
+
23,
|
|
502
|
+
43,
|
|
503
|
+
4,
|
|
504
|
+
126,
|
|
505
|
+
186,
|
|
506
|
+
119,
|
|
507
|
+
214,
|
|
508
|
+
38,
|
|
509
|
+
225,
|
|
510
|
+
105,
|
|
511
|
+
20,
|
|
512
|
+
99,
|
|
513
|
+
85,
|
|
514
|
+
33,
|
|
515
|
+
12,
|
|
516
|
+
125
|
|
517
|
+
]);
|
|
518
|
+
const RCON = [1, 2, 4, 8, 16, 32, 64, 128, 27, 54];
|
|
519
|
+
function gmul(a, b) {
|
|
520
|
+
let p = 0;
|
|
521
|
+
for (let i = 0; i < 8; i++) {
|
|
522
|
+
if (b & 1) p ^= a;
|
|
523
|
+
const hi = a & 128;
|
|
524
|
+
a = a << 1 & 255;
|
|
525
|
+
if (hi) a ^= 27;
|
|
526
|
+
b >>= 1;
|
|
527
|
+
}
|
|
528
|
+
return p;
|
|
529
|
+
}
|
|
530
|
+
function keyExpansion(key) {
|
|
531
|
+
const nk = key.length / 4;
|
|
532
|
+
const nr = nk + 6;
|
|
533
|
+
const nw = 4 * (nr + 1);
|
|
534
|
+
const w = new Array(nw);
|
|
535
|
+
for (let i = 0; i < nk; i++) {
|
|
536
|
+
w[i] = new Uint8Array([key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]]);
|
|
537
|
+
}
|
|
538
|
+
for (let i = nk; i < nw; i++) {
|
|
539
|
+
let temp = new Uint8Array(w[i - 1]);
|
|
540
|
+
if (i % nk === 0) {
|
|
541
|
+
temp = new Uint8Array([
|
|
542
|
+
SBOX[temp[1]] ^ RCON[i / nk - 1],
|
|
543
|
+
SBOX[temp[2]],
|
|
544
|
+
SBOX[temp[3]],
|
|
545
|
+
SBOX[temp[0]]
|
|
546
|
+
]);
|
|
547
|
+
} else if (nk > 6 && i % nk === 4) {
|
|
548
|
+
temp = new Uint8Array([SBOX[temp[0]], SBOX[temp[1]], SBOX[temp[2]], SBOX[temp[3]]]);
|
|
549
|
+
}
|
|
550
|
+
w[i] = new Uint8Array(4);
|
|
551
|
+
for (let j = 0; j < 4; j++) w[i][j] = w[i - nk][j] ^ temp[j];
|
|
552
|
+
}
|
|
553
|
+
const roundKeys = [];
|
|
554
|
+
for (let r = 0; r <= nr; r++) {
|
|
555
|
+
const rk = new Uint8Array(16);
|
|
556
|
+
for (let c = 0; c < 4; c++) {
|
|
557
|
+
rk[4 * c] = w[4 * r + c][0];
|
|
558
|
+
rk[4 * c + 1] = w[4 * r + c][1];
|
|
559
|
+
rk[4 * c + 2] = w[4 * r + c][2];
|
|
560
|
+
rk[4 * c + 3] = w[4 * r + c][3];
|
|
561
|
+
}
|
|
562
|
+
roundKeys.push(rk);
|
|
563
|
+
}
|
|
564
|
+
return roundKeys;
|
|
565
|
+
}
|
|
566
|
+
function aesEncryptBlock(block, roundKeys) {
|
|
567
|
+
const state = new Uint8Array(block);
|
|
568
|
+
const nr = roundKeys.length - 1;
|
|
569
|
+
for (let i = 0; i < 16; i++) state[i] ^= roundKeys[0][i];
|
|
570
|
+
for (let round = 1; round < nr; round++) {
|
|
571
|
+
for (let i = 0; i < 16; i++) state[i] = SBOX[state[i]];
|
|
572
|
+
const t1 = state[1];
|
|
573
|
+
state[1] = state[5];
|
|
574
|
+
state[5] = state[9];
|
|
575
|
+
state[9] = state[13];
|
|
576
|
+
state[13] = t1;
|
|
577
|
+
const t2a = state[2];
|
|
578
|
+
const t2b = state[6];
|
|
579
|
+
state[2] = state[10];
|
|
580
|
+
state[6] = state[14];
|
|
581
|
+
state[10] = t2a;
|
|
582
|
+
state[14] = t2b;
|
|
583
|
+
const t3 = state[15];
|
|
584
|
+
state[15] = state[11];
|
|
585
|
+
state[11] = state[7];
|
|
586
|
+
state[7] = state[3];
|
|
587
|
+
state[3] = t3;
|
|
588
|
+
for (let c = 0; c < 4; c++) {
|
|
589
|
+
const i = c * 4;
|
|
590
|
+
const a0 = state[i], a1 = state[i + 1], a2 = state[i + 2], a3 = state[i + 3];
|
|
591
|
+
state[i] = gmul(2, a0) ^ gmul(3, a1) ^ a2 ^ a3;
|
|
592
|
+
state[i + 1] = a0 ^ gmul(2, a1) ^ gmul(3, a2) ^ a3;
|
|
593
|
+
state[i + 2] = a0 ^ a1 ^ gmul(2, a2) ^ gmul(3, a3);
|
|
594
|
+
state[i + 3] = gmul(3, a0) ^ a1 ^ a2 ^ gmul(2, a3);
|
|
595
|
+
}
|
|
596
|
+
for (let i = 0; i < 16; i++) state[i] ^= roundKeys[round][i];
|
|
597
|
+
}
|
|
598
|
+
for (let i = 0; i < 16; i++) state[i] = SBOX[state[i]];
|
|
599
|
+
const t1f = state[1];
|
|
600
|
+
state[1] = state[5];
|
|
601
|
+
state[5] = state[9];
|
|
602
|
+
state[9] = state[13];
|
|
603
|
+
state[13] = t1f;
|
|
604
|
+
const t2af = state[2];
|
|
605
|
+
const t2bf = state[6];
|
|
606
|
+
state[2] = state[10];
|
|
607
|
+
state[6] = state[14];
|
|
608
|
+
state[10] = t2af;
|
|
609
|
+
state[14] = t2bf;
|
|
610
|
+
const t3f = state[15];
|
|
611
|
+
state[15] = state[11];
|
|
612
|
+
state[11] = state[7];
|
|
613
|
+
state[7] = state[3];
|
|
614
|
+
state[3] = t3f;
|
|
615
|
+
for (let i = 0; i < 16; i++) state[i] ^= roundKeys[nr][i];
|
|
616
|
+
return state;
|
|
617
|
+
}
|
|
618
|
+
function aesDecryptBlock(block, roundKeys) {
|
|
619
|
+
const state = new Uint8Array(block);
|
|
620
|
+
const nr = roundKeys.length - 1;
|
|
621
|
+
for (let i = 0; i < 16; i++) state[i] ^= roundKeys[nr][i];
|
|
622
|
+
for (let round = nr - 1; round > 0; round--) {
|
|
623
|
+
const t1 = state[13];
|
|
624
|
+
state[13] = state[9];
|
|
625
|
+
state[9] = state[5];
|
|
626
|
+
state[5] = state[1];
|
|
627
|
+
state[1] = t1;
|
|
628
|
+
const t2a = state[10];
|
|
629
|
+
const t2b = state[14];
|
|
630
|
+
state[10] = state[2];
|
|
631
|
+
state[14] = state[6];
|
|
632
|
+
state[2] = t2a;
|
|
633
|
+
state[6] = t2b;
|
|
634
|
+
const t3 = state[3];
|
|
635
|
+
state[3] = state[7];
|
|
636
|
+
state[7] = state[11];
|
|
637
|
+
state[11] = state[15];
|
|
638
|
+
state[15] = t3;
|
|
639
|
+
for (let i = 0; i < 16; i++) state[i] = INV_SBOX[state[i]];
|
|
640
|
+
for (let i = 0; i < 16; i++) state[i] ^= roundKeys[round][i];
|
|
641
|
+
for (let c = 0; c < 4; c++) {
|
|
642
|
+
const i = c * 4;
|
|
643
|
+
const a0 = state[i], a1 = state[i + 1], a2 = state[i + 2], a3 = state[i + 3];
|
|
644
|
+
state[i] = gmul(14, a0) ^ gmul(11, a1) ^ gmul(13, a2) ^ gmul(9, a3);
|
|
645
|
+
state[i + 1] = gmul(9, a0) ^ gmul(14, a1) ^ gmul(11, a2) ^ gmul(13, a3);
|
|
646
|
+
state[i + 2] = gmul(13, a0) ^ gmul(9, a1) ^ gmul(14, a2) ^ gmul(11, a3);
|
|
647
|
+
state[i + 3] = gmul(11, a0) ^ gmul(13, a1) ^ gmul(9, a2) ^ gmul(14, a3);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const t1f = state[13];
|
|
651
|
+
state[13] = state[9];
|
|
652
|
+
state[9] = state[5];
|
|
653
|
+
state[5] = state[1];
|
|
654
|
+
state[1] = t1f;
|
|
655
|
+
const t2af = state[10];
|
|
656
|
+
const t2bf = state[14];
|
|
657
|
+
state[10] = state[2];
|
|
658
|
+
state[14] = state[6];
|
|
659
|
+
state[2] = t2af;
|
|
660
|
+
state[6] = t2bf;
|
|
661
|
+
const t3f = state[3];
|
|
662
|
+
state[3] = state[7];
|
|
663
|
+
state[7] = state[11];
|
|
664
|
+
state[11] = state[15];
|
|
665
|
+
state[15] = t3f;
|
|
666
|
+
for (let i = 0; i < 16; i++) state[i] = INV_SBOX[state[i]];
|
|
667
|
+
for (let i = 0; i < 16; i++) state[i] ^= roundKeys[0][i];
|
|
668
|
+
return state;
|
|
669
|
+
}
|
|
670
|
+
function incrementCounter(counter) {
|
|
671
|
+
for (let i = 15; i >= 0; i--) {
|
|
672
|
+
if (++counter[i] !== 0) break;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
function gcmIncrementCounter(counter) {
|
|
676
|
+
for (let i = 15; i >= 12; i--) {
|
|
677
|
+
if (++counter[i] !== 0) break;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
function gfMul(X, Y) {
|
|
681
|
+
const Z = new Uint8Array(16);
|
|
682
|
+
const V = new Uint8Array(X);
|
|
683
|
+
for (let i = 0; i < 128; i++) {
|
|
684
|
+
if (Y[i >>> 3] & 1 << 7 - (i & 7)) {
|
|
685
|
+
for (let j = 0; j < 16; j++) Z[j] ^= V[j];
|
|
686
|
+
}
|
|
687
|
+
const lsb = V[15] & 1;
|
|
688
|
+
for (let j = 15; j > 0; j--) {
|
|
689
|
+
V[j] = V[j] >>> 1 | (V[j - 1] & 1) << 7;
|
|
690
|
+
}
|
|
691
|
+
V[0] = V[0] >>> 1;
|
|
692
|
+
if (lsb) {
|
|
693
|
+
V[0] ^= 225;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return Z;
|
|
697
|
+
}
|
|
698
|
+
function ghash(H, aad, ciphertext) {
|
|
699
|
+
const X = new Uint8Array(16);
|
|
700
|
+
const aadBlocks = Math.ceil(aad.length / 16) || 0;
|
|
701
|
+
for (let i = 0; i < aadBlocks; i++) {
|
|
702
|
+
const start = i * 16;
|
|
703
|
+
const end = Math.min(start + 16, aad.length);
|
|
704
|
+
for (let j = 0; j < 16; j++) {
|
|
705
|
+
const idx = start + j;
|
|
706
|
+
if (idx < end) {
|
|
707
|
+
X[j] ^= aad[idx];
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
const product2 = gfMul(X, H);
|
|
711
|
+
X.set(product2);
|
|
712
|
+
}
|
|
713
|
+
const ctBlocks = Math.ceil(ciphertext.length / 16) || 0;
|
|
714
|
+
for (let i = 0; i < ctBlocks; i++) {
|
|
715
|
+
const start = i * 16;
|
|
716
|
+
const end = Math.min(start + 16, ciphertext.length);
|
|
717
|
+
for (let j = 0; j < 16; j++) {
|
|
718
|
+
const idx = start + j;
|
|
719
|
+
if (idx < end) {
|
|
720
|
+
X[j] ^= ciphertext[idx];
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
const product2 = gfMul(X, H);
|
|
724
|
+
X.set(product2);
|
|
725
|
+
}
|
|
726
|
+
const lenBlock = new Uint8Array(16);
|
|
727
|
+
const aadBits = aad.length * 8;
|
|
728
|
+
const ctBits = ciphertext.length * 8;
|
|
729
|
+
const aadHi = Math.floor(aadBits / 4294967296);
|
|
730
|
+
const aadLo = aadBits >>> 0;
|
|
731
|
+
lenBlock[0] = aadHi >>> 24 & 255;
|
|
732
|
+
lenBlock[1] = aadHi >>> 16 & 255;
|
|
733
|
+
lenBlock[2] = aadHi >>> 8 & 255;
|
|
734
|
+
lenBlock[3] = aadHi & 255;
|
|
735
|
+
lenBlock[4] = aadLo >>> 24 & 255;
|
|
736
|
+
lenBlock[5] = aadLo >>> 16 & 255;
|
|
737
|
+
lenBlock[6] = aadLo >>> 8 & 255;
|
|
738
|
+
lenBlock[7] = aadLo & 255;
|
|
739
|
+
const ctHi = Math.floor(ctBits / 4294967296);
|
|
740
|
+
const ctLo = ctBits >>> 0;
|
|
741
|
+
lenBlock[8] = ctHi >>> 24 & 255;
|
|
742
|
+
lenBlock[9] = ctHi >>> 16 & 255;
|
|
743
|
+
lenBlock[10] = ctHi >>> 8 & 255;
|
|
744
|
+
lenBlock[11] = ctHi & 255;
|
|
745
|
+
lenBlock[12] = ctLo >>> 24 & 255;
|
|
746
|
+
lenBlock[13] = ctLo >>> 16 & 255;
|
|
747
|
+
lenBlock[14] = ctLo >>> 8 & 255;
|
|
748
|
+
lenBlock[15] = ctLo & 255;
|
|
749
|
+
for (let j = 0; j < 16; j++) X[j] ^= lenBlock[j];
|
|
750
|
+
const product = gfMul(X, H);
|
|
751
|
+
X.set(product);
|
|
752
|
+
return X;
|
|
753
|
+
}
|
|
754
|
+
function parseAlgorithm(algorithm) {
|
|
755
|
+
const lower = algorithm.toLowerCase();
|
|
756
|
+
const match = lower.match(/^aes-(128|192|256)-(cbc|ctr|ecb|cfb|ofb|gcm)$/);
|
|
757
|
+
if (!match) {
|
|
758
|
+
throw new Error(`Unsupported cipher algorithm: ${algorithm}`);
|
|
759
|
+
}
|
|
760
|
+
const keyBits = parseInt(match[1]);
|
|
761
|
+
const mode = match[2];
|
|
762
|
+
return {
|
|
763
|
+
keySize: keyBits / 8,
|
|
764
|
+
ivSize: mode === "ecb" ? 0 : mode === "gcm" ? 12 : 16,
|
|
765
|
+
mode
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
function toBuffer(data, encoding) {
|
|
769
|
+
if (typeof data === "string") {
|
|
770
|
+
return Buffer.from(data, encoding || "utf8");
|
|
771
|
+
}
|
|
772
|
+
return Buffer.from(data);
|
|
773
|
+
}
|
|
774
|
+
function encodeOutput(data, encoding) {
|
|
775
|
+
if (!encoding) return Buffer.from(data);
|
|
776
|
+
return Buffer.from(data).toString(encoding);
|
|
777
|
+
}
|
|
778
|
+
function incompleteUtf8Tail(buf) {
|
|
779
|
+
if (buf.length === 0) return 0;
|
|
780
|
+
const end = buf.length;
|
|
781
|
+
for (let back = 1; back <= Math.min(4, end); back++) {
|
|
782
|
+
const b = buf[end - back];
|
|
783
|
+
if ((b & 128) === 0) {
|
|
784
|
+
return 0;
|
|
785
|
+
}
|
|
786
|
+
if ((b & 192) === 128) {
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
let expected;
|
|
790
|
+
if ((b & 224) === 192) expected = 2;
|
|
791
|
+
else if ((b & 240) === 224) expected = 3;
|
|
792
|
+
else if ((b & 248) === 240) expected = 4;
|
|
793
|
+
else return 0;
|
|
794
|
+
return back < expected ? back : 0;
|
|
795
|
+
}
|
|
796
|
+
return 0;
|
|
797
|
+
}
|
|
798
|
+
function pkcs7Pad(data) {
|
|
799
|
+
const padLen = 16 - data.length % 16;
|
|
800
|
+
const padded = new Uint8Array(data.length + padLen);
|
|
801
|
+
padded.set(data);
|
|
802
|
+
for (let i = data.length; i < padded.length; i++) padded[i] = padLen;
|
|
803
|
+
return padded;
|
|
804
|
+
}
|
|
805
|
+
function pkcs7Unpad(data) {
|
|
806
|
+
if (data.length === 0 || data.length % 16 !== 0) {
|
|
807
|
+
throw new Error("bad decrypt");
|
|
808
|
+
}
|
|
809
|
+
const padLen = data[data.length - 1];
|
|
810
|
+
if (padLen === 0 || padLen > 16) {
|
|
811
|
+
throw new Error("bad decrypt");
|
|
812
|
+
}
|
|
813
|
+
for (let i = data.length - padLen; i < data.length; i++) {
|
|
814
|
+
if (data[i] !== padLen) throw new Error("bad decrypt");
|
|
815
|
+
}
|
|
816
|
+
return new Uint8Array(data.slice(0, data.length - padLen));
|
|
817
|
+
}
|
|
818
|
+
class CipherBase {
|
|
819
|
+
_roundKeys;
|
|
820
|
+
_iv;
|
|
821
|
+
_mode;
|
|
822
|
+
_buffer = new Uint8Array(0);
|
|
823
|
+
_autoPadding = true;
|
|
824
|
+
_finalized = false;
|
|
825
|
+
constructor(algorithm, key, iv) {
|
|
826
|
+
const info = parseAlgorithm(algorithm);
|
|
827
|
+
if (key.length !== info.keySize) {
|
|
828
|
+
throw new Error(`Invalid key length ${key.length}, expected ${info.keySize} for ${algorithm}`);
|
|
829
|
+
}
|
|
830
|
+
if (info.ivSize > 0 && (!iv || iv.length !== info.ivSize)) {
|
|
831
|
+
throw new Error(`Invalid IV length ${iv?.length ?? 0}, expected ${info.ivSize} for ${algorithm}`);
|
|
832
|
+
}
|
|
833
|
+
this._roundKeys = keyExpansion(key);
|
|
834
|
+
this._iv = iv ? new Uint8Array(iv) : new Uint8Array(16);
|
|
835
|
+
this._mode = info.mode;
|
|
836
|
+
}
|
|
837
|
+
setAutoPadding(autoPadding) {
|
|
838
|
+
this._autoPadding = autoPadding;
|
|
839
|
+
return this;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
class Cipher extends CipherBase {
|
|
843
|
+
_prevBlock;
|
|
844
|
+
_counter;
|
|
845
|
+
// GCM state
|
|
846
|
+
_gcmH = null;
|
|
847
|
+
// Hash subkey H = AES_K(0^128)
|
|
848
|
+
_gcmJ0 = null;
|
|
849
|
+
// Initial counter J0
|
|
850
|
+
_gcmAAD = new Uint8Array(0);
|
|
851
|
+
// Additional authenticated data
|
|
852
|
+
_gcmCiphertext = [];
|
|
853
|
+
// Accumulated ciphertext for GHASH
|
|
854
|
+
_gcmCiphertextLen = 0;
|
|
855
|
+
// Total ciphertext length
|
|
856
|
+
_gcmAuthTag = null;
|
|
857
|
+
// Computed authentication tag
|
|
858
|
+
_gcmAADSet = false;
|
|
859
|
+
// Whether setAAD was called
|
|
860
|
+
constructor(algorithm, key, iv) {
|
|
861
|
+
super(algorithm, key, iv);
|
|
862
|
+
this._prevBlock = new Uint8Array(this._iv);
|
|
863
|
+
if (this._mode === "gcm") {
|
|
864
|
+
this._gcmH = aesEncryptBlock(new Uint8Array(16), this._roundKeys);
|
|
865
|
+
this._gcmJ0 = new Uint8Array(16);
|
|
866
|
+
this._gcmJ0.set(this._iv.subarray(0, 12));
|
|
867
|
+
this._gcmJ0[15] = 1;
|
|
868
|
+
this._counter = new Uint8Array(this._gcmJ0);
|
|
869
|
+
gcmIncrementCounter(this._counter);
|
|
870
|
+
} else {
|
|
871
|
+
this._counter = new Uint8Array(this._iv);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Set Additional Authenticated Data for GCM mode.
|
|
876
|
+
* Must be called before any update() calls.
|
|
877
|
+
*/
|
|
878
|
+
setAAD(data) {
|
|
879
|
+
if (this._mode !== "gcm") {
|
|
880
|
+
throw new Error("setAAD is only supported in GCM mode");
|
|
881
|
+
}
|
|
882
|
+
if (this._gcmCiphertextLen > 0) {
|
|
883
|
+
throw new Error("setAAD must be called before update()");
|
|
884
|
+
}
|
|
885
|
+
this._gcmAAD = new Uint8Array(data);
|
|
886
|
+
this._gcmAADSet = true;
|
|
887
|
+
return this;
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Get the authentication tag after final() has been called.
|
|
891
|
+
* Only valid for GCM mode.
|
|
892
|
+
*/
|
|
893
|
+
getAuthTag() {
|
|
894
|
+
if (this._mode !== "gcm") {
|
|
895
|
+
throw new Error("getAuthTag is only supported in GCM mode");
|
|
896
|
+
}
|
|
897
|
+
if (!this._gcmAuthTag) {
|
|
898
|
+
throw new Error("getAuthTag must be called after final()");
|
|
899
|
+
}
|
|
900
|
+
return Buffer.from(this._gcmAuthTag);
|
|
901
|
+
}
|
|
902
|
+
update(data, inputEncoding, outputEncoding) {
|
|
903
|
+
const input = toBuffer(data, inputEncoding);
|
|
904
|
+
const combined = new Uint8Array(this._buffer.length + input.length);
|
|
905
|
+
combined.set(this._buffer);
|
|
906
|
+
combined.set(input, this._buffer.length);
|
|
907
|
+
if (this._mode === "gcm") {
|
|
908
|
+
const output2 = this._processGcmEncrypt(combined);
|
|
909
|
+
this._buffer = new Uint8Array(0);
|
|
910
|
+
return encodeOutput(output2, outputEncoding);
|
|
911
|
+
}
|
|
912
|
+
if (this._mode === "ctr" || this._mode === "cfb" || this._mode === "ofb") {
|
|
913
|
+
const output2 = this._processStream(combined);
|
|
914
|
+
this._buffer = new Uint8Array(0);
|
|
915
|
+
return encodeOutput(output2, outputEncoding);
|
|
916
|
+
}
|
|
917
|
+
const fullBlocks = Math.floor(combined.length / 16);
|
|
918
|
+
const processLen = fullBlocks * 16;
|
|
919
|
+
const output = [];
|
|
920
|
+
for (let i = 0; i < processLen; i += 16) {
|
|
921
|
+
const block = combined.slice(i, i + 16);
|
|
922
|
+
output.push(this._encryptBlock(block));
|
|
923
|
+
}
|
|
924
|
+
this._buffer = combined.slice(processLen);
|
|
925
|
+
const result = new Uint8Array(output.length * 16);
|
|
926
|
+
for (let i = 0; i < output.length; i++) result.set(output[i], i * 16);
|
|
927
|
+
return encodeOutput(result, outputEncoding);
|
|
928
|
+
}
|
|
929
|
+
final(outputEncoding) {
|
|
930
|
+
if (this._finalized) throw new Error("Cipher already finalized");
|
|
931
|
+
this._finalized = true;
|
|
932
|
+
if (this._mode === "gcm") {
|
|
933
|
+
let finalOutput = new Uint8Array(0);
|
|
934
|
+
if (this._buffer.length > 0) {
|
|
935
|
+
finalOutput = this._processGcmEncrypt(this._buffer);
|
|
936
|
+
this._buffer = new Uint8Array(0);
|
|
937
|
+
}
|
|
938
|
+
const allCiphertext = new Uint8Array(this._gcmCiphertextLen);
|
|
939
|
+
let offset = 0;
|
|
940
|
+
for (const chunk of this._gcmCiphertext) {
|
|
941
|
+
allCiphertext.set(chunk, offset);
|
|
942
|
+
offset += chunk.length;
|
|
943
|
+
}
|
|
944
|
+
const ghashResult = ghash(this._gcmH, this._gcmAAD, allCiphertext);
|
|
945
|
+
const encJ0 = aesEncryptBlock(this._gcmJ0, this._roundKeys);
|
|
946
|
+
const tag = new Uint8Array(16);
|
|
947
|
+
for (let i = 0; i < 16; i++) tag[i] = ghashResult[i] ^ encJ0[i];
|
|
948
|
+
this._gcmAuthTag = Buffer.from(tag);
|
|
949
|
+
return encodeOutput(finalOutput, outputEncoding);
|
|
950
|
+
}
|
|
951
|
+
if (this._mode === "ctr" || this._mode === "cfb" || this._mode === "ofb") {
|
|
952
|
+
if (this._buffer.length > 0) {
|
|
953
|
+
const output2 = this._processStream(this._buffer);
|
|
954
|
+
this._buffer = new Uint8Array(0);
|
|
955
|
+
return encodeOutput(output2, outputEncoding);
|
|
956
|
+
}
|
|
957
|
+
return encodeOutput(new Uint8Array(0), outputEncoding);
|
|
958
|
+
}
|
|
959
|
+
let data = this._buffer;
|
|
960
|
+
if (this._autoPadding) {
|
|
961
|
+
data = pkcs7Pad(data);
|
|
962
|
+
} else if (data.length % 16 !== 0) {
|
|
963
|
+
throw new Error("data not multiple of block size");
|
|
964
|
+
}
|
|
965
|
+
const output = [];
|
|
966
|
+
for (let i = 0; i < data.length; i += 16) {
|
|
967
|
+
output.push(this._encryptBlock(data.slice(i, i + 16)));
|
|
968
|
+
}
|
|
969
|
+
this._buffer = new Uint8Array(0);
|
|
970
|
+
if (output.length === 0) return encodeOutput(new Uint8Array(0), outputEncoding);
|
|
971
|
+
const result = new Uint8Array(output.length * 16);
|
|
972
|
+
for (let i = 0; i < output.length; i++) result.set(output[i], i * 16);
|
|
973
|
+
return encodeOutput(result, outputEncoding);
|
|
974
|
+
}
|
|
975
|
+
_encryptBlock(block) {
|
|
976
|
+
if (this._mode === "cbc") {
|
|
977
|
+
const xored = new Uint8Array(16);
|
|
978
|
+
for (let i = 0; i < 16; i++) xored[i] = block[i] ^ this._prevBlock[i];
|
|
979
|
+
const encrypted = aesEncryptBlock(xored, this._roundKeys);
|
|
980
|
+
this._prevBlock = encrypted;
|
|
981
|
+
return encrypted;
|
|
982
|
+
} else if (this._mode === "ecb") {
|
|
983
|
+
return aesEncryptBlock(block, this._roundKeys);
|
|
984
|
+
}
|
|
985
|
+
throw new Error(`Block encryption not supported for mode: ${this._mode}`);
|
|
986
|
+
}
|
|
987
|
+
_processStream(data) {
|
|
988
|
+
const output = new Uint8Array(data.length);
|
|
989
|
+
for (let i = 0; i < data.length; i += 16) {
|
|
990
|
+
const keystream = aesEncryptBlock(this._counter, this._roundKeys);
|
|
991
|
+
const remaining = Math.min(16, data.length - i);
|
|
992
|
+
for (let j = 0; j < remaining; j++) {
|
|
993
|
+
output[i + j] = data[i + j] ^ keystream[j];
|
|
994
|
+
}
|
|
995
|
+
incrementCounter(this._counter);
|
|
996
|
+
}
|
|
997
|
+
return output;
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* GCM encryption: CTR mode encryption, also accumulates ciphertext for GHASH.
|
|
1001
|
+
*/
|
|
1002
|
+
_processGcmEncrypt(data) {
|
|
1003
|
+
const output = new Uint8Array(data.length);
|
|
1004
|
+
for (let i = 0; i < data.length; i += 16) {
|
|
1005
|
+
const keystream = aesEncryptBlock(this._counter, this._roundKeys);
|
|
1006
|
+
const remaining = Math.min(16, data.length - i);
|
|
1007
|
+
for (let j = 0; j < remaining; j++) {
|
|
1008
|
+
output[i + j] = data[i + j] ^ keystream[j];
|
|
1009
|
+
}
|
|
1010
|
+
gcmIncrementCounter(this._counter);
|
|
1011
|
+
}
|
|
1012
|
+
this._gcmCiphertext.push(new Uint8Array(output));
|
|
1013
|
+
this._gcmCiphertextLen += output.length;
|
|
1014
|
+
return output;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
class Decipher extends CipherBase {
|
|
1018
|
+
_prevBlock;
|
|
1019
|
+
_counter;
|
|
1020
|
+
_pendingUtf8 = new Uint8Array(0);
|
|
1021
|
+
// GCM state
|
|
1022
|
+
_gcmH = null;
|
|
1023
|
+
// Hash subkey H = AES_K(0^128)
|
|
1024
|
+
_gcmJ0 = null;
|
|
1025
|
+
// Initial counter J0
|
|
1026
|
+
_gcmAAD = new Uint8Array(0);
|
|
1027
|
+
// Additional authenticated data
|
|
1028
|
+
_gcmCiphertext = [];
|
|
1029
|
+
// Accumulated ciphertext for GHASH
|
|
1030
|
+
_gcmCiphertextLen = 0;
|
|
1031
|
+
// Total ciphertext length
|
|
1032
|
+
_gcmExpectedTag = null;
|
|
1033
|
+
// Expected authentication tag
|
|
1034
|
+
_gcmAADSet = false;
|
|
1035
|
+
// Whether setAAD was called
|
|
1036
|
+
constructor(algorithm, key, iv) {
|
|
1037
|
+
super(algorithm, key, iv);
|
|
1038
|
+
this._prevBlock = new Uint8Array(this._iv);
|
|
1039
|
+
if (this._mode === "gcm") {
|
|
1040
|
+
this._gcmH = aesEncryptBlock(new Uint8Array(16), this._roundKeys);
|
|
1041
|
+
this._gcmJ0 = new Uint8Array(16);
|
|
1042
|
+
this._gcmJ0.set(this._iv.subarray(0, 12));
|
|
1043
|
+
this._gcmJ0[15] = 1;
|
|
1044
|
+
this._counter = new Uint8Array(this._gcmJ0);
|
|
1045
|
+
gcmIncrementCounter(this._counter);
|
|
1046
|
+
} else {
|
|
1047
|
+
this._counter = new Uint8Array(this._iv);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Set Additional Authenticated Data for GCM mode.
|
|
1052
|
+
* Must be called before any update() calls.
|
|
1053
|
+
*/
|
|
1054
|
+
setAAD(data) {
|
|
1055
|
+
if (this._mode !== "gcm") {
|
|
1056
|
+
throw new Error("setAAD is only supported in GCM mode");
|
|
1057
|
+
}
|
|
1058
|
+
if (this._gcmCiphertextLen > 0) {
|
|
1059
|
+
throw new Error("setAAD must be called before update()");
|
|
1060
|
+
}
|
|
1061
|
+
this._gcmAAD = new Uint8Array(data);
|
|
1062
|
+
this._gcmAADSet = true;
|
|
1063
|
+
return this;
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Set the expected authentication tag for GCM decryption.
|
|
1067
|
+
* Must be called before final().
|
|
1068
|
+
*/
|
|
1069
|
+
setAuthTag(tag) {
|
|
1070
|
+
if (this._mode !== "gcm") {
|
|
1071
|
+
throw new Error("setAuthTag is only supported in GCM mode");
|
|
1072
|
+
}
|
|
1073
|
+
this._gcmExpectedTag = Buffer.from(tag);
|
|
1074
|
+
return this;
|
|
1075
|
+
}
|
|
1076
|
+
_encodeWithUtf8Handling(bytes, encoding, isFinal) {
|
|
1077
|
+
if (!encoding || encoding !== "utf8" && encoding !== "utf-8") {
|
|
1078
|
+
return encodeOutput(bytes, encoding);
|
|
1079
|
+
}
|
|
1080
|
+
let data;
|
|
1081
|
+
if (this._pendingUtf8.length > 0) {
|
|
1082
|
+
data = new Uint8Array(this._pendingUtf8.length + bytes.length);
|
|
1083
|
+
data.set(this._pendingUtf8);
|
|
1084
|
+
data.set(bytes, this._pendingUtf8.length);
|
|
1085
|
+
this._pendingUtf8 = new Uint8Array(0);
|
|
1086
|
+
} else {
|
|
1087
|
+
data = bytes;
|
|
1088
|
+
}
|
|
1089
|
+
if (!isFinal) {
|
|
1090
|
+
const tail = incompleteUtf8Tail(data);
|
|
1091
|
+
if (tail > 0) {
|
|
1092
|
+
this._pendingUtf8 = new Uint8Array(data.slice(data.length - tail));
|
|
1093
|
+
data = new Uint8Array(data.slice(0, data.length - tail));
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
return Buffer.from(data).toString("utf8");
|
|
1097
|
+
}
|
|
1098
|
+
update(data, inputEncoding, outputEncoding) {
|
|
1099
|
+
const input = toBuffer(data, inputEncoding);
|
|
1100
|
+
const combined = new Uint8Array(this._buffer.length + input.length);
|
|
1101
|
+
combined.set(this._buffer);
|
|
1102
|
+
combined.set(input, this._buffer.length);
|
|
1103
|
+
if (this._mode === "gcm") {
|
|
1104
|
+
this._gcmCiphertext.push(new Uint8Array(combined));
|
|
1105
|
+
this._gcmCiphertextLen += combined.length;
|
|
1106
|
+
const output2 = this._processGcmDecrypt(combined);
|
|
1107
|
+
this._buffer = new Uint8Array(0);
|
|
1108
|
+
return this._encodeWithUtf8Handling(output2, outputEncoding, false);
|
|
1109
|
+
}
|
|
1110
|
+
if (this._mode === "ctr" || this._mode === "cfb" || this._mode === "ofb") {
|
|
1111
|
+
const output2 = this._processStream(combined);
|
|
1112
|
+
this._buffer = new Uint8Array(0);
|
|
1113
|
+
return this._encodeWithUtf8Handling(output2, outputEncoding, false);
|
|
1114
|
+
}
|
|
1115
|
+
const fullBlocks = Math.floor(combined.length / 16);
|
|
1116
|
+
if (fullBlocks === 0) {
|
|
1117
|
+
this._buffer = combined;
|
|
1118
|
+
return this._encodeWithUtf8Handling(new Uint8Array(0), outputEncoding, false);
|
|
1119
|
+
}
|
|
1120
|
+
const processBlocks = this._autoPadding ? fullBlocks - 1 : fullBlocks;
|
|
1121
|
+
const processLen = processBlocks * 16;
|
|
1122
|
+
const output = [];
|
|
1123
|
+
for (let i = 0; i < processLen; i += 16) {
|
|
1124
|
+
const block = combined.slice(i, i + 16);
|
|
1125
|
+
output.push(this._decryptBlock(block));
|
|
1126
|
+
}
|
|
1127
|
+
this._buffer = combined.slice(processLen);
|
|
1128
|
+
const result = new Uint8Array(output.length * 16);
|
|
1129
|
+
for (let i = 0; i < output.length; i++) result.set(output[i], i * 16);
|
|
1130
|
+
return this._encodeWithUtf8Handling(result, outputEncoding, false);
|
|
1131
|
+
}
|
|
1132
|
+
final(outputEncoding) {
|
|
1133
|
+
if (this._finalized) throw new Error("Decipher already finalized");
|
|
1134
|
+
this._finalized = true;
|
|
1135
|
+
if (this._mode === "gcm") {
|
|
1136
|
+
let finalOutput = new Uint8Array(0);
|
|
1137
|
+
if (this._buffer.length > 0) {
|
|
1138
|
+
this._gcmCiphertext.push(new Uint8Array(this._buffer));
|
|
1139
|
+
this._gcmCiphertextLen += this._buffer.length;
|
|
1140
|
+
finalOutput = this._processGcmDecrypt(this._buffer);
|
|
1141
|
+
this._buffer = new Uint8Array(0);
|
|
1142
|
+
}
|
|
1143
|
+
if (!this._gcmExpectedTag) {
|
|
1144
|
+
throw new Error("Unsupported state or unable to authenticate data");
|
|
1145
|
+
}
|
|
1146
|
+
const allCiphertext = new Uint8Array(this._gcmCiphertextLen);
|
|
1147
|
+
let offset = 0;
|
|
1148
|
+
for (const chunk of this._gcmCiphertext) {
|
|
1149
|
+
allCiphertext.set(chunk, offset);
|
|
1150
|
+
offset += chunk.length;
|
|
1151
|
+
}
|
|
1152
|
+
const ghashResult = ghash(this._gcmH, this._gcmAAD, allCiphertext);
|
|
1153
|
+
const encJ0 = aesEncryptBlock(this._gcmJ0, this._roundKeys);
|
|
1154
|
+
const computedTag = new Uint8Array(16);
|
|
1155
|
+
for (let i = 0; i < 16; i++) computedTag[i] = ghashResult[i] ^ encJ0[i];
|
|
1156
|
+
const expectedTag = this._gcmExpectedTag;
|
|
1157
|
+
const tagLen = Math.min(expectedTag.length, 16);
|
|
1158
|
+
let diff = 0;
|
|
1159
|
+
for (let i = 0; i < tagLen; i++) {
|
|
1160
|
+
diff |= computedTag[i] ^ expectedTag[i];
|
|
1161
|
+
}
|
|
1162
|
+
if (diff !== 0) {
|
|
1163
|
+
throw new Error("Unsupported state or unable to authenticate data");
|
|
1164
|
+
}
|
|
1165
|
+
return this._encodeWithUtf8Handling(finalOutput, outputEncoding, true);
|
|
1166
|
+
}
|
|
1167
|
+
if (this._mode === "ctr" || this._mode === "cfb" || this._mode === "ofb") {
|
|
1168
|
+
if (this._buffer.length > 0) {
|
|
1169
|
+
const output2 = this._processStream(this._buffer);
|
|
1170
|
+
this._buffer = new Uint8Array(0);
|
|
1171
|
+
return this._encodeWithUtf8Handling(output2, outputEncoding, true);
|
|
1172
|
+
}
|
|
1173
|
+
return this._encodeWithUtf8Handling(new Uint8Array(0), outputEncoding, true);
|
|
1174
|
+
}
|
|
1175
|
+
if (this._buffer.length === 0) {
|
|
1176
|
+
return this._encodeWithUtf8Handling(new Uint8Array(0), outputEncoding, true);
|
|
1177
|
+
}
|
|
1178
|
+
if (this._buffer.length % 16 !== 0) {
|
|
1179
|
+
throw new Error("bad decrypt");
|
|
1180
|
+
}
|
|
1181
|
+
const output = [];
|
|
1182
|
+
for (let i = 0; i < this._buffer.length; i += 16) {
|
|
1183
|
+
output.push(this._decryptBlock(this._buffer.slice(i, i + 16)));
|
|
1184
|
+
}
|
|
1185
|
+
const combined = new Uint8Array(output.length * 16);
|
|
1186
|
+
for (let i = 0; i < output.length; i++) combined.set(output[i], i * 16);
|
|
1187
|
+
const result = this._autoPadding ? pkcs7Unpad(combined) : combined;
|
|
1188
|
+
this._buffer = new Uint8Array(0);
|
|
1189
|
+
return this._encodeWithUtf8Handling(result, outputEncoding, true);
|
|
1190
|
+
}
|
|
1191
|
+
_decryptBlock(block) {
|
|
1192
|
+
if (this._mode === "cbc") {
|
|
1193
|
+
const decrypted = aesDecryptBlock(block, this._roundKeys);
|
|
1194
|
+
const output = new Uint8Array(16);
|
|
1195
|
+
for (let i = 0; i < 16; i++) output[i] = decrypted[i] ^ this._prevBlock[i];
|
|
1196
|
+
this._prevBlock = new Uint8Array(block);
|
|
1197
|
+
return output;
|
|
1198
|
+
} else if (this._mode === "ecb") {
|
|
1199
|
+
return aesDecryptBlock(block, this._roundKeys);
|
|
1200
|
+
}
|
|
1201
|
+
throw new Error(`Block decryption not supported for mode: ${this._mode}`);
|
|
1202
|
+
}
|
|
1203
|
+
_processStream(data) {
|
|
1204
|
+
const output = new Uint8Array(data.length);
|
|
1205
|
+
for (let i = 0; i < data.length; i += 16) {
|
|
1206
|
+
const keystream = aesEncryptBlock(this._counter, this._roundKeys);
|
|
1207
|
+
const remaining = Math.min(16, data.length - i);
|
|
1208
|
+
for (let j = 0; j < remaining; j++) {
|
|
1209
|
+
output[i + j] = data[i + j] ^ keystream[j];
|
|
1210
|
+
}
|
|
1211
|
+
incrementCounter(this._counter);
|
|
1212
|
+
}
|
|
1213
|
+
return output;
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* GCM decryption: CTR mode decryption (same as encryption, since CTR is symmetric).
|
|
1217
|
+
*/
|
|
1218
|
+
_processGcmDecrypt(data) {
|
|
1219
|
+
const output = new Uint8Array(data.length);
|
|
1220
|
+
for (let i = 0; i < data.length; i += 16) {
|
|
1221
|
+
const keystream = aesEncryptBlock(this._counter, this._roundKeys);
|
|
1222
|
+
const remaining = Math.min(16, data.length - i);
|
|
1223
|
+
for (let j = 0; j < remaining; j++) {
|
|
1224
|
+
output[i + j] = data[i + j] ^ keystream[j];
|
|
1225
|
+
}
|
|
1226
|
+
gcmIncrementCounter(this._counter);
|
|
1227
|
+
}
|
|
1228
|
+
return output;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
function createCipher(_algorithm, _password) {
|
|
1232
|
+
throw new Error("crypto.createCipher() is deprecated. Use createCipheriv() instead.");
|
|
1233
|
+
}
|
|
1234
|
+
function createCipheriv(algorithm, key, iv) {
|
|
1235
|
+
const keyBuf = typeof key === "string" ? Buffer.from(key) : new Uint8Array(key);
|
|
1236
|
+
const ivBuf = iv == null ? null : typeof iv === "string" ? Buffer.from(iv) : new Uint8Array(iv);
|
|
1237
|
+
return new Cipher(algorithm, keyBuf, ivBuf);
|
|
1238
|
+
}
|
|
1239
|
+
function createDecipher(_algorithm, _password) {
|
|
1240
|
+
throw new Error("crypto.createDecipher() is deprecated. Use createDecipheriv() instead.");
|
|
1241
|
+
}
|
|
1242
|
+
function createDecipheriv(algorithm, key, iv) {
|
|
1243
|
+
const keyBuf = typeof key === "string" ? Buffer.from(key) : new Uint8Array(key);
|
|
1244
|
+
const ivBuf = iv == null ? null : typeof iv === "string" ? Buffer.from(iv) : new Uint8Array(iv);
|
|
1245
|
+
return new Decipher(algorithm, keyBuf, ivBuf);
|
|
1246
|
+
}
|
|
1247
|
+
function getCiphers() {
|
|
1248
|
+
return [
|
|
1249
|
+
"aes-128-cbc",
|
|
1250
|
+
"aes-128-ecb",
|
|
1251
|
+
"aes-192-cbc",
|
|
1252
|
+
"aes-192-ecb",
|
|
1253
|
+
"aes-256-cbc",
|
|
1254
|
+
"aes-256-ecb",
|
|
1255
|
+
"aes-128-ctr",
|
|
1256
|
+
"aes-192-ctr",
|
|
1257
|
+
"aes-256-ctr",
|
|
1258
|
+
"aes-128-cfb",
|
|
1259
|
+
"aes-192-cfb",
|
|
1260
|
+
"aes-256-cfb",
|
|
1261
|
+
"aes-128-gcm",
|
|
1262
|
+
"aes-192-gcm",
|
|
1263
|
+
"aes-256-gcm"
|
|
1264
|
+
];
|
|
1265
|
+
}
|
|
1266
|
+
export {
|
|
1267
|
+
createCipher,
|
|
1268
|
+
createCipheriv,
|
|
1269
|
+
createDecipher,
|
|
1270
|
+
createDecipheriv,
|
|
1271
|
+
getCiphers
|
|
1272
|
+
};
|