unjwt 0.4.0 → 0.5.1

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 (49) hide show
  1. package/README.md +445 -190
  2. package/dist/index.d.mts +6 -5
  3. package/dist/index.d.ts +6 -5
  4. package/dist/index.mjs +5 -5
  5. package/dist/jwe.d.mts +3 -3
  6. package/dist/jwe.d.ts +3 -3
  7. package/dist/jwe.mjs +4 -3
  8. package/dist/jwk.d.mts +2 -2
  9. package/dist/jwk.d.ts +2 -2
  10. package/dist/jwk.mjs +2 -2
  11. package/dist/jws.d.mts +3 -3
  12. package/dist/jws.d.ts +3 -3
  13. package/dist/jws.mjs +4 -4
  14. package/dist/shared/unjwt.1gbuWbc6.mjs +1507 -0
  15. package/dist/shared/unjwt.B0XDPf8a.d.ts +189 -0
  16. package/dist/shared/unjwt.B4fFiy1c.d.mts +52 -0
  17. package/dist/shared/unjwt.BG_R52ey.d.mts +100 -0
  18. package/dist/shared/unjwt.BISJ5yt1.d.ts +52 -0
  19. package/dist/shared/unjwt.BxqF-n1S.mjs +167 -0
  20. package/dist/shared/unjwt.C9Xhi7hE.mjs +101 -0
  21. package/dist/shared/unjwt.CVKbKOFy.d.ts +100 -0
  22. package/dist/shared/unjwt.Cd9nTEWg.d.mts +52 -0
  23. package/dist/shared/unjwt.Ce72NmIz.mjs +453 -0
  24. package/dist/shared/unjwt.CxhGjEAX.d.mts +202 -0
  25. package/dist/shared/unjwt.CxhGjEAX.d.ts +202 -0
  26. package/dist/shared/unjwt.D1J1DelP.d.mts +198 -0
  27. package/dist/shared/unjwt.DHdDAH0L.mjs +83 -0
  28. package/dist/shared/unjwt.DQIYSlHj.d.mts +189 -0
  29. package/dist/shared/unjwt.DqmNvXMd.d.ts +52 -0
  30. package/dist/shared/unjwt.WvWb2zNM.d.ts +198 -0
  31. package/dist/utils.d.mts +2 -0
  32. package/dist/utils.d.ts +2 -0
  33. package/dist/utils.mjs +1 -0
  34. package/package.json +8 -4
  35. package/dist/shared/unjwt.4uzJv0cA.d.mts +0 -110
  36. package/dist/shared/unjwt.A7HKFx3n.d.mts +0 -52
  37. package/dist/shared/unjwt.BdjvpIUY.d.mts +0 -79
  38. package/dist/shared/unjwt.BmjmfFWo.d.ts +0 -79
  39. package/dist/shared/unjwt.C9ELsKeK.mjs +0 -120
  40. package/dist/shared/unjwt.CVUxLNoy.d.ts +0 -52
  41. package/dist/shared/unjwt.ChP7VhqI.d.ts +0 -110
  42. package/dist/shared/unjwt.DD0p18Gd.mjs +0 -81
  43. package/dist/shared/unjwt.DGsIvgyn.d.ts +0 -106
  44. package/dist/shared/unjwt.DPTwlVpK.mjs +0 -28
  45. package/dist/shared/unjwt.Dr-mxtq9.d.mts +0 -106
  46. package/dist/shared/unjwt.KOd0vXyx.mjs +0 -90
  47. package/dist/shared/unjwt._GivNhs2.mjs +0 -213
  48. package/dist/shared/unjwt.zb2wBNCJ.d.mts +0 -82
  49. package/dist/shared/unjwt.zb2wBNCJ.d.ts +0 -82
@@ -0,0 +1,1507 @@
1
+ import { g as isCryptoKey, e as concatUint8Arrays, b as base64UrlEncode, h as base64Decode, j as base64Encode, t as textEncoder, r as randomBytes, a as isJWK, d as base64UrlDecode } from './unjwt.DHdDAH0L.mjs';
2
+
3
+ /**
4
+ * This is a fork of Jose library's utility functions.
5
+ * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/buffer_utils.ts
6
+ * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
7
+ */
8
+ const MAX_INT32 = 2 ** 32;
9
+ function writeUInt32BE(buf, value, offset) {
10
+ if (value < 0 || value >= MAX_INT32) {
11
+ throw new RangeError(
12
+ `value must be >= 0 and <= ${MAX_INT32 - 1}. Received ${value}`
13
+ );
14
+ }
15
+ buf.set([value >>> 24, value >>> 16, value >>> 8, value & 255], offset);
16
+ }
17
+ function uint64be(value) {
18
+ const high = Math.floor(value / MAX_INT32);
19
+ const low = value % MAX_INT32;
20
+ const buf = new Uint8Array(8);
21
+ writeUInt32BE(buf, high, 0);
22
+ writeUInt32BE(buf, low, 4);
23
+ return buf;
24
+ }
25
+ function uint32be(value) {
26
+ const buf = new Uint8Array(4);
27
+ writeUInt32BE(buf, value);
28
+ return buf;
29
+ }
30
+
31
+ /**
32
+ * This is a fork of Jose library's utility functions.
33
+ * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/cek.ts
34
+ * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/iv.ts
35
+ * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
36
+ */
37
+ function bitLengthIV(alg) {
38
+ switch (alg) {
39
+ case "A128GCM":
40
+ case "A128GCMKW":
41
+ case "A192GCM":
42
+ case "A192GCMKW":
43
+ case "A256GCM":
44
+ case "A256GCMKW": {
45
+ return 96;
46
+ }
47
+ case "A128CBC-HS256":
48
+ case "A192CBC-HS384":
49
+ case "A256CBC-HS512": {
50
+ return 128;
51
+ }
52
+ default: {
53
+ throw new Error(`Unsupported JWE Algorithm: ${alg}`);
54
+ }
55
+ }
56
+ }
57
+ function generateIV(alg) {
58
+ return crypto.getRandomValues(new Uint8Array(bitLengthIV(alg) >> 3));
59
+ }
60
+ function checkIvLength(enc, iv) {
61
+ if (iv.length << 3 !== bitLengthIV(enc)) {
62
+ throw new Error("Invalid Initialization Vector length");
63
+ }
64
+ }
65
+ function bitLengthCEK(alg) {
66
+ switch (alg) {
67
+ case "A128GCM": {
68
+ return 128;
69
+ }
70
+ case "A192GCM": {
71
+ return 192;
72
+ }
73
+ case "A256GCM":
74
+ case "A128CBC-HS256": {
75
+ return 256;
76
+ }
77
+ case "A192CBC-HS384": {
78
+ return 384;
79
+ }
80
+ case "A256CBC-HS512": {
81
+ return 512;
82
+ }
83
+ default: {
84
+ throw new Error(`Unsupported JWE Algorithm: ${alg}`);
85
+ }
86
+ }
87
+ }
88
+ function generateCEK(alg) {
89
+ return crypto.getRandomValues(new Uint8Array(bitLengthCEK(alg) >> 3));
90
+ }
91
+ function checkCEKLength(cek, expected) {
92
+ const actual = cek.byteLength << 3;
93
+ if (actual !== expected) {
94
+ throw new Error(
95
+ `Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`
96
+ );
97
+ }
98
+ }
99
+
100
+ /**
101
+ * This is a fork of Jose library's utility functions.
102
+ * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/crypto_key.ts
103
+ * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
104
+ */
105
+ function unusable(name, prop = "algorithm.name") {
106
+ return new TypeError(
107
+ `CryptoKey does not support this operation, its ${prop} must be ${name}`
108
+ );
109
+ }
110
+ function isAlgorithm(algorithm, name) {
111
+ return algorithm.name === name;
112
+ }
113
+ function getHashLength(hash) {
114
+ return Number.parseInt(hash.name.slice(4), 10);
115
+ }
116
+ function getNamedCurve$1(alg) {
117
+ switch (alg) {
118
+ case "ES256": {
119
+ return "P-256";
120
+ }
121
+ case "ES384": {
122
+ return "P-384";
123
+ }
124
+ case "ES512": {
125
+ return "P-521";
126
+ }
127
+ default: {
128
+ throw new Error("unreachable");
129
+ }
130
+ }
131
+ }
132
+ function checkUsage(key, usage) {
133
+ if (usage && !key.usages.includes(usage)) {
134
+ throw new TypeError(
135
+ `CryptoKey does not support this operation, its usages must include ${usage}.`
136
+ );
137
+ }
138
+ }
139
+ function checkSigCryptoKey(key, alg, usage) {
140
+ switch (alg) {
141
+ case "HS256":
142
+ case "HS384":
143
+ case "HS512": {
144
+ if (!isAlgorithm(key.algorithm, "HMAC"))
145
+ throw unusable("HMAC");
146
+ const expected = Number.parseInt(alg.slice(2), 10);
147
+ const actual = getHashLength(key.algorithm.hash);
148
+ if (actual !== expected)
149
+ throw unusable(`SHA-${expected}`, "algorithm.hash");
150
+ break;
151
+ }
152
+ case "RS256":
153
+ case "RS384":
154
+ case "RS512": {
155
+ if (!isAlgorithm(key.algorithm, "RSASSA-PKCS1-v1_5"))
156
+ throw unusable("RSASSA-PKCS1-v1_5");
157
+ const expected = Number.parseInt(alg.slice(2), 10);
158
+ const actual = getHashLength(key.algorithm.hash);
159
+ if (actual !== expected)
160
+ throw unusable(`SHA-${expected}`, "algorithm.hash");
161
+ break;
162
+ }
163
+ case "PS256":
164
+ case "PS384":
165
+ case "PS512": {
166
+ if (!isAlgorithm(key.algorithm, "RSA-PSS"))
167
+ throw unusable("RSA-PSS");
168
+ const expected = Number.parseInt(alg.slice(2), 10);
169
+ const actual = getHashLength(key.algorithm.hash);
170
+ if (actual !== expected)
171
+ throw unusable(`SHA-${expected}`, "algorithm.hash");
172
+ break;
173
+ }
174
+ case "Ed25519":
175
+ // Fall through
176
+ case "EdDSA": {
177
+ if (!isAlgorithm(key.algorithm, "Ed25519")) throw unusable("Ed25519");
178
+ break;
179
+ }
180
+ case "ES256":
181
+ case "ES384":
182
+ case "ES512": {
183
+ if (!isAlgorithm(key.algorithm, "ECDSA"))
184
+ throw unusable("ECDSA");
185
+ const expected = getNamedCurve$1(alg);
186
+ const actual = key.algorithm.namedCurve;
187
+ if (actual !== expected) throw unusable(expected, "algorithm.namedCurve");
188
+ break;
189
+ }
190
+ default: {
191
+ throw new TypeError("CryptoKey does not support this operation");
192
+ }
193
+ }
194
+ checkUsage(key, usage);
195
+ }
196
+ function checkEncCryptoKey(key, alg, usage) {
197
+ switch (alg) {
198
+ case "A128GCM":
199
+ case "A192GCM":
200
+ case "A256GCM": {
201
+ if (!isAlgorithm(key.algorithm, "AES-GCM"))
202
+ throw unusable("AES-GCM");
203
+ const expected = Number.parseInt(alg.slice(1, 4), 10);
204
+ const actual = key.algorithm.length;
205
+ if (actual !== expected) throw unusable(expected, "algorithm.length");
206
+ break;
207
+ }
208
+ case "A128KW":
209
+ case "A192KW":
210
+ case "A256KW": {
211
+ if (!isAlgorithm(key.algorithm, "AES-KW"))
212
+ throw unusable("AES-KW");
213
+ const expected = Number.parseInt(alg.slice(1, 4), 10);
214
+ const actual = key.algorithm.length;
215
+ if (actual !== expected) throw unusable(expected, "algorithm.length");
216
+ break;
217
+ }
218
+ case "ECDH": {
219
+ switch (key.algorithm.name) {
220
+ case "ECDH":
221
+ case "X25519": {
222
+ break;
223
+ }
224
+ default: {
225
+ throw unusable("ECDH or X25519");
226
+ }
227
+ }
228
+ break;
229
+ }
230
+ case "PBES2-HS256+A128KW":
231
+ case "PBES2-HS384+A192KW":
232
+ case "PBES2-HS512+A256KW": {
233
+ if (!isAlgorithm(key.algorithm, "PBKDF2")) throw unusable("PBKDF2");
234
+ break;
235
+ }
236
+ case "RSA-OAEP":
237
+ case "RSA-OAEP-256":
238
+ case "RSA-OAEP-384":
239
+ case "RSA-OAEP-512": {
240
+ if (!isAlgorithm(key.algorithm, "RSA-OAEP"))
241
+ throw unusable("RSA-OAEP");
242
+ const expected = Number.parseInt(alg.slice(9), 10) || 1;
243
+ const actual = getHashLength(key.algorithm.hash);
244
+ if (actual !== expected)
245
+ throw unusable(`SHA-${expected}`, "algorithm.hash");
246
+ break;
247
+ }
248
+ default: {
249
+ throw new TypeError("CryptoKey does not support this operation");
250
+ }
251
+ }
252
+ checkUsage(key, usage);
253
+ }
254
+
255
+ /**
256
+ * This is a fork of Jose library's utility functions.
257
+ * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/encrypt.ts
258
+ * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/decrypt.ts
259
+ * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
260
+ */
261
+ async function cbcEncrypt(enc, plaintext, cek, iv, aad) {
262
+ if (!(cek instanceof Uint8Array)) {
263
+ throw new TypeError(`Key must be ${cek} of type: Uint8Array`);
264
+ }
265
+ const keySize = Number.parseInt(enc.slice(1, 4), 10);
266
+ const encKey = await crypto.subtle.importKey(
267
+ "raw",
268
+ cek.subarray(keySize >> 3),
269
+ "AES-CBC",
270
+ false,
271
+ ["encrypt"]
272
+ );
273
+ const macKey = await crypto.subtle.importKey(
274
+ "raw",
275
+ cek.subarray(0, keySize >> 3),
276
+ {
277
+ hash: `SHA-${keySize << 1}`,
278
+ name: "HMAC"
279
+ },
280
+ false,
281
+ ["sign"]
282
+ );
283
+ const ciphertext = new Uint8Array(
284
+ await crypto.subtle.encrypt(
285
+ {
286
+ iv,
287
+ name: "AES-CBC"
288
+ },
289
+ encKey,
290
+ plaintext
291
+ )
292
+ );
293
+ const macData = concatUint8Arrays(
294
+ aad,
295
+ iv,
296
+ ciphertext,
297
+ uint64be(aad.length << 3)
298
+ );
299
+ const tag = new Uint8Array(
300
+ (await crypto.subtle.sign("HMAC", macKey, macData)).slice(0, keySize >> 3)
301
+ );
302
+ return { ciphertext, tag, iv };
303
+ }
304
+ async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
305
+ let encKey;
306
+ if (cek instanceof Uint8Array) {
307
+ encKey = await crypto.subtle.importKey("raw", cek, "AES-GCM", false, [
308
+ "encrypt"
309
+ ]);
310
+ } else {
311
+ checkEncCryptoKey(cek, enc, "encrypt");
312
+ encKey = cek;
313
+ }
314
+ const encrypted = new Uint8Array(
315
+ await crypto.subtle.encrypt(
316
+ {
317
+ additionalData: aad,
318
+ iv,
319
+ name: "AES-GCM",
320
+ tagLength: 128
321
+ },
322
+ encKey,
323
+ plaintext
324
+ )
325
+ );
326
+ const tag = encrypted.slice(-16);
327
+ const ciphertext = encrypted.slice(0, -16);
328
+ return { ciphertext, tag, iv };
329
+ }
330
+ async function encrypt(enc, plaintext, cek, iv, aad) {
331
+ if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) {
332
+ throw new TypeError(
333
+ `Key must be ${cek} one of type: CryptoKey, Uint8Array, or JSON Web Key`
334
+ );
335
+ }
336
+ if (iv) {
337
+ checkIvLength(enc, iv);
338
+ } else {
339
+ iv = generateIV(enc);
340
+ }
341
+ switch (enc) {
342
+ case "A128CBC-HS256":
343
+ case "A192CBC-HS384":
344
+ case "A256CBC-HS512": {
345
+ if (cek instanceof Uint8Array) {
346
+ checkCEKLength(cek, Number.parseInt(enc.slice(-3), 10));
347
+ }
348
+ return cbcEncrypt(enc, plaintext, cek, iv, aad);
349
+ }
350
+ case "A128GCM":
351
+ case "A192GCM":
352
+ case "A256GCM": {
353
+ if (cek instanceof Uint8Array) {
354
+ checkCEKLength(cek, Number.parseInt(enc.slice(1, 4), 10));
355
+ }
356
+ return gcmEncrypt(enc, plaintext, cek, iv, aad);
357
+ }
358
+ default: {
359
+ throw new Error("Unsupported JWE Content Encryption Algorithm");
360
+ }
361
+ }
362
+ }
363
+ async function timingSafeEqual(a, b) {
364
+ if (!(a instanceof Uint8Array)) {
365
+ throw new TypeError("First argument must be a buffer");
366
+ }
367
+ if (!(b instanceof Uint8Array)) {
368
+ throw new TypeError("Second argument must be a buffer");
369
+ }
370
+ const algorithm = { name: "HMAC", hash: "SHA-256" };
371
+ const key = await crypto.subtle.generateKey(algorithm, false, [
372
+ "sign"
373
+ ]);
374
+ const aHmac = new Uint8Array(await crypto.subtle.sign(algorithm, key, a));
375
+ const bHmac = new Uint8Array(await crypto.subtle.sign(algorithm, key, b));
376
+ let out = 0;
377
+ let i = -1;
378
+ while (++i < 32) {
379
+ out |= aHmac[i] ^ bHmac[i];
380
+ }
381
+ return out === 0;
382
+ }
383
+ async function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) {
384
+ if (!(cek instanceof Uint8Array)) {
385
+ throw new TypeError(`Key must be ${cek} of type: Uint8Array`);
386
+ }
387
+ const keySize = Number.parseInt(enc.slice(1, 4), 10);
388
+ const encKey = await crypto.subtle.importKey(
389
+ "raw",
390
+ cek.subarray(keySize >> 3),
391
+ "AES-CBC",
392
+ false,
393
+ ["decrypt"]
394
+ );
395
+ const macKey = await crypto.subtle.importKey(
396
+ "raw",
397
+ cek.subarray(0, keySize >> 3),
398
+ {
399
+ hash: `SHA-${keySize << 1}`,
400
+ name: "HMAC"
401
+ },
402
+ false,
403
+ ["sign"]
404
+ );
405
+ const macData = concatUint8Arrays(
406
+ aad,
407
+ iv,
408
+ ciphertext,
409
+ uint64be(aad.length << 3)
410
+ );
411
+ const expectedTag = new Uint8Array(
412
+ (await crypto.subtle.sign("HMAC", macKey, macData)).slice(0, keySize >> 3)
413
+ );
414
+ let macCheckPassed;
415
+ try {
416
+ macCheckPassed = await timingSafeEqual(tag, expectedTag);
417
+ } catch {
418
+ }
419
+ if (!macCheckPassed) {
420
+ throw new Error("JWE Decryption Failed");
421
+ }
422
+ let plaintext;
423
+ try {
424
+ plaintext = new Uint8Array(
425
+ await crypto.subtle.decrypt({ iv, name: "AES-CBC" }, encKey, ciphertext)
426
+ );
427
+ } catch {
428
+ }
429
+ if (!plaintext) {
430
+ throw new Error("JWE Decryption Failed");
431
+ }
432
+ return plaintext;
433
+ }
434
+ async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) {
435
+ let encKey;
436
+ if (cek instanceof Uint8Array) {
437
+ encKey = await crypto.subtle.importKey("raw", cek, "AES-GCM", false, [
438
+ "decrypt"
439
+ ]);
440
+ } else {
441
+ checkEncCryptoKey(cek, enc, "decrypt");
442
+ encKey = cek;
443
+ }
444
+ try {
445
+ return new Uint8Array(
446
+ await crypto.subtle.decrypt(
447
+ {
448
+ additionalData: aad,
449
+ iv,
450
+ name: "AES-GCM",
451
+ tagLength: 128
452
+ },
453
+ encKey,
454
+ concatUint8Arrays(ciphertext, tag)
455
+ )
456
+ );
457
+ } catch {
458
+ throw new Error("JWE Decryption Failed");
459
+ }
460
+ }
461
+ async function decrypt(enc, cek, ciphertext, iv, tag, aad) {
462
+ if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) {
463
+ throw new TypeError(
464
+ `Key must be ${cek} one of type: CryptoKey, Uint8Array, or JSON Web Key`
465
+ );
466
+ }
467
+ if (!iv) {
468
+ throw new Error("JWE Initialization Vector missing");
469
+ }
470
+ if (!tag) {
471
+ throw new Error("JWE Authentication Tag missing");
472
+ }
473
+ checkIvLength(enc, iv);
474
+ switch (enc) {
475
+ case "A128CBC-HS256":
476
+ case "A192CBC-HS384":
477
+ case "A256CBC-HS512": {
478
+ if (cek instanceof Uint8Array)
479
+ checkCEKLength(cek, Number.parseInt(enc.slice(-3), 10));
480
+ return cbcDecrypt(enc, cek, ciphertext, iv, tag, aad);
481
+ }
482
+ case "A128GCM":
483
+ case "A192GCM":
484
+ case "A256GCM": {
485
+ if (cek instanceof Uint8Array)
486
+ checkCEKLength(cek, Number.parseInt(enc.slice(1, 4), 10));
487
+ return gcmDecrypt(enc, cek, ciphertext, iv, tag, aad);
488
+ }
489
+ default: {
490
+ throw new Error("Unsupported JWE Content Encryption Algorithm");
491
+ }
492
+ }
493
+ }
494
+
495
+ /**
496
+ * This is a fork of Jose library's utility functions.
497
+ * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/aesgcmkw.ts
498
+ * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
499
+ */
500
+ async function encryptIV(alg, key, cek, iv) {
501
+ const jweAlgorithm = alg.slice(0, 7);
502
+ const wrapped = await encrypt(jweAlgorithm, cek, key, iv, new Uint8Array(0));
503
+ return {
504
+ encryptedKey: wrapped.ciphertext,
505
+ iv: base64UrlEncode(wrapped.iv),
506
+ tag: base64UrlEncode(wrapped.tag)
507
+ };
508
+ }
509
+ async function decryptIV(alg, key, encryptedKey, iv, tag) {
510
+ const jweAlgorithm = alg.slice(0, 7);
511
+ return decrypt(jweAlgorithm, key, encryptedKey, iv, tag, new Uint8Array(0));
512
+ }
513
+
514
+ /**
515
+ * This is a fork of Jose library's utility functions.
516
+ * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/asn1.ts
517
+ * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
518
+ */
519
+ const formatPEM = (b64, descriptor) => {
520
+ const newlined = (b64.match(/.{1,64}/g) || []).join("\n");
521
+ return `-----BEGIN ${descriptor}-----
522
+ ${newlined}
523
+ -----END ${descriptor}-----`;
524
+ };
525
+ const genericExport = async (keyType, keyFormat, key) => {
526
+ if (!isCryptoKey(key)) {
527
+ throw new TypeError(`Key must be ${key} of type CryptoKey`);
528
+ }
529
+ if (!key.extractable) {
530
+ throw new TypeError("CryptoKey is not extractable");
531
+ }
532
+ if (key.type !== keyType) {
533
+ throw new TypeError(`key is not a ${keyType} key`);
534
+ }
535
+ return formatPEM(
536
+ base64Encode(new Uint8Array(await crypto.subtle.exportKey(keyFormat, key))),
537
+ `${keyType.toUpperCase()} KEY`
538
+ );
539
+ };
540
+ const toSPKI = (key) => {
541
+ return genericExport("public", "spki", key);
542
+ };
543
+ const toPKCS8 = (key) => {
544
+ return genericExport("private", "pkcs8", key);
545
+ };
546
+ const findOid = (keyData, oid, from = 0) => {
547
+ if (from === 0) {
548
+ oid.unshift(oid.length);
549
+ oid.unshift(6);
550
+ }
551
+ const i = keyData.indexOf(oid[0], from);
552
+ if (i === -1) return false;
553
+ const sub = keyData.subarray(i, i + oid.length);
554
+ if (sub.length !== oid.length) return false;
555
+ return sub.every((value, index) => value === oid[index]) || findOid(keyData, oid, i + 1);
556
+ };
557
+ const getNamedCurve = (keyData) => {
558
+ switch (true) {
559
+ case findOid(keyData, [42, 134, 72, 206, 61, 3, 1, 7]): {
560
+ return "P-256";
561
+ }
562
+ case findOid(keyData, [43, 129, 4, 0, 34]): {
563
+ return "P-384";
564
+ }
565
+ case findOid(keyData, [43, 129, 4, 0, 35]): {
566
+ return "P-521";
567
+ }
568
+ default: {
569
+ return void 0;
570
+ }
571
+ }
572
+ };
573
+ const genericImport = async (replace, keyFormat, pem, alg, options) => {
574
+ let algorithm;
575
+ let keyUsages;
576
+ const cleanedPem = pem.replace(replace, "");
577
+ const keyData = base64Decode(cleanedPem, false);
578
+ const isPublic = keyFormat === "spki";
579
+ switch (alg) {
580
+ case "PS256":
581
+ case "PS384":
582
+ case "PS512": {
583
+ algorithm = { name: "RSA-PSS", hash: `SHA-${alg.slice(-3)}` };
584
+ keyUsages = isPublic ? ["verify"] : ["sign"];
585
+ break;
586
+ }
587
+ case "RS256":
588
+ case "RS384":
589
+ case "RS512": {
590
+ algorithm = { name: "RSASSA-PKCS1-v1_5", hash: `SHA-${alg.slice(-3)}` };
591
+ keyUsages = isPublic ? ["verify"] : ["sign"];
592
+ break;
593
+ }
594
+ case "RSA-OAEP":
595
+ case "RSA-OAEP-256":
596
+ case "RSA-OAEP-384":
597
+ case "RSA-OAEP-512": {
598
+ algorithm = {
599
+ name: "RSA-OAEP",
600
+ hash: `SHA-${Number.parseInt(alg.slice(-3), 10) || 1}`
601
+ };
602
+ keyUsages = isPublic ? ["encrypt", "wrapKey"] : ["decrypt", "unwrapKey"];
603
+ break;
604
+ }
605
+ case "ES256": {
606
+ algorithm = { name: "ECDSA", namedCurve: "P-256" };
607
+ keyUsages = isPublic ? ["verify"] : ["sign"];
608
+ break;
609
+ }
610
+ case "ES384": {
611
+ algorithm = { name: "ECDSA", namedCurve: "P-384" };
612
+ keyUsages = isPublic ? ["verify"] : ["sign"];
613
+ break;
614
+ }
615
+ case "ES512": {
616
+ algorithm = { name: "ECDSA", namedCurve: "P-521" };
617
+ keyUsages = isPublic ? ["verify"] : ["sign"];
618
+ break;
619
+ }
620
+ case "ECDH-ES":
621
+ case "ECDH-ES+A128KW":
622
+ case "ECDH-ES+A192KW":
623
+ case "ECDH-ES+A256KW": {
624
+ const namedCurve = getNamedCurve(keyData);
625
+ algorithm = namedCurve?.startsWith("P-") ? { name: "ECDH", namedCurve } : { name: "X25519" };
626
+ keyUsages = isPublic ? [] : ["deriveBits"];
627
+ break;
628
+ }
629
+ case "Ed25519":
630
+ case "EdDSA": {
631
+ algorithm = { name: "Ed25519" };
632
+ keyUsages = isPublic ? ["verify"] : ["sign"];
633
+ break;
634
+ }
635
+ default: {
636
+ throw new Error('Invalid or unsupported "alg" (Algorithm) value');
637
+ }
638
+ }
639
+ return crypto.subtle.importKey(
640
+ keyFormat,
641
+ keyData,
642
+ algorithm,
643
+ options?.extractable ?? (isPublic ? true : false),
644
+ keyUsages
645
+ );
646
+ };
647
+ const fromPKCS8 = (pem, alg, options) => {
648
+ return genericImport(
649
+ /(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g,
650
+ "pkcs8",
651
+ pem,
652
+ alg,
653
+ options
654
+ );
655
+ };
656
+ const fromSPKI = (pem, alg, options) => {
657
+ return genericImport(
658
+ /(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g,
659
+ "spki",
660
+ pem,
661
+ alg,
662
+ options
663
+ );
664
+ };
665
+ function getElement(seq) {
666
+ const result = [];
667
+ let next = 0;
668
+ while (next < seq.length) {
669
+ const nextPart = parseElement(seq.subarray(next));
670
+ result.push(nextPart);
671
+ next += nextPart.byteLength;
672
+ }
673
+ return result;
674
+ }
675
+ function parseElement(bytes) {
676
+ let position = 0;
677
+ let tag = bytes[0] & 31;
678
+ position++;
679
+ if (tag === 31) {
680
+ tag = 0;
681
+ while (bytes[position] >= 128) {
682
+ tag = tag * 128 + bytes[position] - 128;
683
+ position++;
684
+ }
685
+ tag = tag * 128 + bytes[position] - 128;
686
+ position++;
687
+ }
688
+ let length = 0;
689
+ if (bytes[position] < 128) {
690
+ length = bytes[position];
691
+ position++;
692
+ } else if (length === 128) {
693
+ length = 0;
694
+ while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) {
695
+ if (length > bytes.byteLength) {
696
+ throw new TypeError("invalid indefinite form length");
697
+ }
698
+ length++;
699
+ }
700
+ const byteLength2 = position + length + 2;
701
+ return {
702
+ byteLength: byteLength2,
703
+ contents: bytes.subarray(position, position + length),
704
+ raw: bytes.subarray(0, byteLength2)
705
+ };
706
+ } else {
707
+ const numberOfDigits = bytes[position] & 127;
708
+ position++;
709
+ length = 0;
710
+ for (let i = 0; i < numberOfDigits; i++) {
711
+ length = length * 256 + bytes[position];
712
+ position++;
713
+ }
714
+ }
715
+ const byteLength = position + length;
716
+ return {
717
+ byteLength,
718
+ contents: bytes.subarray(position, byteLength),
719
+ raw: bytes.subarray(0, byteLength)
720
+ };
721
+ }
722
+ function spkiFromX509(buf) {
723
+ const tbsCertificate = getElement(
724
+ getElement(parseElement(buf).contents)[0].contents
725
+ );
726
+ return base64Encode(
727
+ tbsCertificate[tbsCertificate[0].raw[0] === 160 ? 6 : 5].raw
728
+ );
729
+ }
730
+ let createPublicKey;
731
+ function getSPKI(x509) {
732
+ try {
733
+ createPublicKey ??= globalThis.process?.getBuiltinModule?.("node:crypto")?.createPublicKey;
734
+ } catch {
735
+ createPublicKey = 0;
736
+ }
737
+ if (createPublicKey) {
738
+ try {
739
+ return new createPublicKey(x509).export({ format: "pem", type: "spki" });
740
+ } catch {
741
+ }
742
+ }
743
+ const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, "");
744
+ const raw = base64Decode(pem, false);
745
+ return formatPEM(spkiFromX509(raw), "PUBLIC KEY");
746
+ }
747
+ const fromX509 = (pem, alg, options) => {
748
+ let spki;
749
+ try {
750
+ spki = getSPKI(pem);
751
+ } catch (error_) {
752
+ throw new TypeError("Failed to parse the X.509 certificate", {
753
+ cause: error_
754
+ });
755
+ }
756
+ return fromSPKI(spki, alg, options);
757
+ };
758
+
759
+ /**
760
+ * This is a fork of Jose library's utility functions.
761
+ * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/jwt_to_key.ts
762
+ * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
763
+ */
764
+ function subtleMapping(jwk) {
765
+ let algorithm;
766
+ let keyUsages;
767
+ switch (jwk.kty) {
768
+ case "RSA": {
769
+ switch (jwk.alg) {
770
+ case "PS256":
771
+ case "PS384":
772
+ case "PS512": {
773
+ algorithm = { name: "RSA-PSS", hash: `SHA-${jwk.alg.slice(-3)}` };
774
+ keyUsages = jwk.d ? ["sign"] : ["verify"];
775
+ break;
776
+ }
777
+ case "RS256":
778
+ case "RS384":
779
+ case "RS512": {
780
+ algorithm = {
781
+ name: "RSASSA-PKCS1-v1_5",
782
+ hash: `SHA-${jwk.alg.slice(-3)}`
783
+ };
784
+ keyUsages = jwk.d ? ["sign"] : ["verify"];
785
+ break;
786
+ }
787
+ case "RSA-OAEP":
788
+ case "RSA-OAEP-256":
789
+ case "RSA-OAEP-384":
790
+ case "RSA-OAEP-512": {
791
+ algorithm = {
792
+ name: "RSA-OAEP",
793
+ hash: `SHA-${Number.parseInt(jwk.alg.slice(-3), 10) || 1}`
794
+ };
795
+ keyUsages = jwk.d ? ["decrypt", "unwrapKey"] : ["encrypt", "wrapKey"];
796
+ break;
797
+ }
798
+ default: {
799
+ throw new Error(
800
+ 'Invalid or unsupported JWK "alg" (Algorithm) Parameter value'
801
+ );
802
+ }
803
+ }
804
+ break;
805
+ }
806
+ case "EC": {
807
+ switch (jwk.alg) {
808
+ case "ES256": {
809
+ algorithm = { name: "ECDSA", namedCurve: "P-256" };
810
+ keyUsages = jwk.d ? ["sign"] : ["verify"];
811
+ break;
812
+ }
813
+ case "ES384": {
814
+ algorithm = { name: "ECDSA", namedCurve: "P-384" };
815
+ keyUsages = jwk.d ? ["sign"] : ["verify"];
816
+ break;
817
+ }
818
+ case "ES512": {
819
+ algorithm = { name: "ECDSA", namedCurve: "P-521" };
820
+ keyUsages = jwk.d ? ["sign"] : ["verify"];
821
+ break;
822
+ }
823
+ case "ECDH-ES":
824
+ case "ECDH-ES+A128KW":
825
+ case "ECDH-ES+A192KW":
826
+ case "ECDH-ES+A256KW": {
827
+ algorithm = { name: "ECDH", namedCurve: jwk.crv };
828
+ keyUsages = jwk.d ? ["deriveBits"] : [];
829
+ break;
830
+ }
831
+ default: {
832
+ throw new Error(
833
+ 'Invalid or unsupported JWK "alg" (Algorithm) Parameter value'
834
+ );
835
+ }
836
+ }
837
+ break;
838
+ }
839
+ case "OKP": {
840
+ switch (jwk.alg) {
841
+ case "Ed25519":
842
+ // Fall through
843
+ case "EdDSA": {
844
+ algorithm = { name: "Ed25519" };
845
+ keyUsages = jwk.d ? ["sign"] : ["verify"];
846
+ break;
847
+ }
848
+ case "ECDH-ES":
849
+ case "ECDH-ES+A128KW":
850
+ case "ECDH-ES+A192KW":
851
+ case "ECDH-ES+A256KW": {
852
+ algorithm = { name: jwk.crv };
853
+ keyUsages = jwk.d ? ["deriveBits"] : [];
854
+ break;
855
+ }
856
+ default: {
857
+ throw new Error(
858
+ 'Invalid or unsupported JWK "alg" (Algorithm) Parameter value'
859
+ );
860
+ }
861
+ }
862
+ break;
863
+ }
864
+ default: {
865
+ throw new Error(
866
+ 'Invalid or unsupported JWK "kty" (Key Type) Parameter value'
867
+ );
868
+ }
869
+ }
870
+ return { algorithm, keyUsages };
871
+ }
872
+ async function jwkTokey(jwk) {
873
+ if (!jwk.alg) {
874
+ throw new TypeError(
875
+ '"alg" argument is required when "jwk.alg" is not present'
876
+ );
877
+ }
878
+ const { algorithm, keyUsages } = subtleMapping(jwk);
879
+ const keyData = { ...jwk };
880
+ delete keyData.alg;
881
+ delete keyData.use;
882
+ return crypto.subtle.importKey(
883
+ "jwk",
884
+ keyData,
885
+ algorithm,
886
+ jwk.ext ?? (jwk.d ? false : true),
887
+ jwk.key_ops ?? keyUsages
888
+ );
889
+ }
890
+
891
+ /**
892
+ * This is a fork of Jose library's utility functions.
893
+ * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/key_to_jwt.ts
894
+ * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
895
+ */
896
+ async function keyToJWK(key) {
897
+ if (key instanceof Uint8Array) {
898
+ return {
899
+ kty: "oct",
900
+ k: base64UrlEncode(key)
901
+ };
902
+ }
903
+ if (!isCryptoKey(key)) {
904
+ throw new TypeError(
905
+ `Key must be ${key} one of type: CryptoKey, or Uint8Array`
906
+ );
907
+ }
908
+ if (!key.extractable) {
909
+ throw new TypeError(
910
+ "non-extractable CryptoKey cannot be exported as a JWK"
911
+ );
912
+ }
913
+ const jwk = await crypto.subtle.exportKey("jwk", key);
914
+ return jwk;
915
+ }
916
+
917
+ /**
918
+ * This is a fork of Jose library's utility functions.
919
+ * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/pbes2kw.ts
920
+ * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
921
+ */
922
+ async function wrap(alg, key, cek, p2c, p2s) {
923
+ if (p2c && p2s) {
924
+ const derived = await deriveKey(p2s, alg, p2c, key);
925
+ const encryptedKey = await _wrap(alg.slice(-6), derived, cek);
926
+ return { encryptedKey, p2c, p2s: base64UrlEncode(p2s) };
927
+ } else {
928
+ const encryptedKey = await _wrap(alg, key, cek);
929
+ return { encryptedKey };
930
+ }
931
+ }
932
+ async function unwrap(alg, key, encryptedKey, p2c, p2s) {
933
+ if (p2c && p2s) {
934
+ const derived = await deriveKey(p2s, alg, p2c, key);
935
+ return _unwrap(alg.slice(-6), derived, encryptedKey);
936
+ } else {
937
+ return _unwrap(alg, key, encryptedKey);
938
+ }
939
+ }
940
+ function getCryptoKey(key, alg, options = {}) {
941
+ const {
942
+ importAlg = "PBKDF2",
943
+ extractable = false,
944
+ usage = "deriveBits"
945
+ } = options;
946
+ if (key instanceof Uint8Array) {
947
+ return crypto.subtle.importKey("raw", key, importAlg, extractable, [usage]);
948
+ }
949
+ checkEncCryptoKey(key, alg, usage);
950
+ return key;
951
+ }
952
+ async function deriveKey(p2s, alg, p2c, key) {
953
+ if (!(p2s instanceof Uint8Array) || p2s.length < 8) {
954
+ throw new Error("PBES2 Salt Input must be 8 or more octets");
955
+ }
956
+ if (p2c === void 0 || p2c < 1) {
957
+ throw new Error(
958
+ "PBES2 Iteration Count Parameter must be a positive integer"
959
+ );
960
+ }
961
+ const salt = concatUint8Arrays(
962
+ textEncoder.encode(alg),
963
+ new Uint8Array([0]),
964
+ p2s
965
+ );
966
+ const keylen = Number.parseInt(alg.slice(13, 16), 10);
967
+ const subtleAlg = {
968
+ hash: `SHA-${alg.slice(8, 11)}`,
969
+ iterations: p2c,
970
+ name: "PBKDF2",
971
+ salt
972
+ };
973
+ const cryptoKey = await getCryptoKey(key, alg);
974
+ return new Uint8Array(
975
+ await crypto.subtle.deriveBits(subtleAlg, cryptoKey, keylen)
976
+ );
977
+ }
978
+ function checkKeySize(key, alg) {
979
+ if (key.algorithm.length !== Number.parseInt(alg.slice(1, 4), 10)) {
980
+ throw new TypeError(`Invalid key size for alg: ${alg}`);
981
+ }
982
+ }
983
+ async function _wrap(alg, key, cek) {
984
+ const cryptoKey = await getCryptoKey(key, alg, {
985
+ importAlg: "AES-KW",
986
+ extractable: true,
987
+ usage: "wrapKey"
988
+ });
989
+ checkKeySize(cryptoKey, alg);
990
+ const cryptoKeyCek = await crypto.subtle.importKey(
991
+ "raw",
992
+ cek,
993
+ { hash: "SHA-256", name: "HMAC" },
994
+ true,
995
+ ["sign"]
996
+ );
997
+ return new Uint8Array(
998
+ await crypto.subtle.wrapKey("raw", cryptoKeyCek, cryptoKey, "AES-KW")
999
+ );
1000
+ }
1001
+ async function _unwrap(alg, key, encryptedKey) {
1002
+ const cryptoKey = await getCryptoKey(key, alg, {
1003
+ importAlg: "AES-KW",
1004
+ extractable: true,
1005
+ usage: "unwrapKey"
1006
+ });
1007
+ checkKeySize(cryptoKey, alg);
1008
+ const cryptoKeyCek = await crypto.subtle.unwrapKey(
1009
+ "raw",
1010
+ encryptedKey,
1011
+ cryptoKey,
1012
+ "AES-KW",
1013
+ { hash: "SHA-256", name: "HMAC" },
1014
+ true,
1015
+ ["sign"]
1016
+ );
1017
+ return new Uint8Array(await crypto.subtle.exportKey("raw", cryptoKeyCek));
1018
+ }
1019
+
1020
+ async function generateKey(alg, options = {}) {
1021
+ const exportToJWK = options.toJWK === true;
1022
+ const defaultExtractable = options.extractable !== false;
1023
+ if (alg === "A128CBC-HS256" || alg === "A192CBC-HS384" || alg === "A256CBC-HS512") {
1024
+ const keyLength = bitLengthCEK(alg);
1025
+ const keyBytes = randomBytes(keyLength >> 3);
1026
+ if (exportToJWK) {
1027
+ return keyToJWK(keyBytes);
1028
+ }
1029
+ return keyBytes;
1030
+ }
1031
+ const { algorithm, keyUsages } = getGenerateKeyParams(alg, options);
1032
+ const key = await crypto.subtle.generateKey(
1033
+ algorithm,
1034
+ defaultExtractable,
1035
+ keyUsages
1036
+ );
1037
+ if (exportToJWK) {
1038
+ if (key instanceof CryptoKey) {
1039
+ return exportKey(key, { alg });
1040
+ } else {
1041
+ const [publicKey, privateKey] = await Promise.all([
1042
+ exportKey(key.publicKey, { alg }),
1043
+ exportKey(key.privateKey, { alg })
1044
+ ]);
1045
+ return { privateKey, publicKey };
1046
+ }
1047
+ }
1048
+ return key;
1049
+ }
1050
+ async function deriveKeyFromPassword(password, alg, options) {
1051
+ const { salt, iterations, toJWK, extractable, keyUsage } = options;
1052
+ if (!(salt instanceof Uint8Array) || salt.length < 8) {
1053
+ throw new Error("PBES2 Salt Input (salt) must be 8 or more octets");
1054
+ }
1055
+ if (typeof iterations !== "number" || iterations < 1) {
1056
+ throw new Error(
1057
+ "PBES2 Iteration Count (iterations) must be a positive integer"
1058
+ );
1059
+ }
1060
+ const passwordBytes = typeof password === "string" ? textEncoder.encode(password) : password;
1061
+ const derivedBytes = await deriveKey(
1062
+ salt,
1063
+ alg,
1064
+ iterations,
1065
+ passwordBytes
1066
+ );
1067
+ const wrappingAlg = alg.slice(-6);
1068
+ const defaultUsages = ["wrapKey", "unwrapKey"];
1069
+ const finalUsages = keyUsage ?? defaultUsages;
1070
+ const finalExtractable = toJWK === true ? true : extractable === true;
1071
+ const derivedKey = await crypto.subtle.importKey(
1072
+ "raw",
1073
+ derivedBytes,
1074
+ { name: "AES-KW" },
1075
+ finalExtractable,
1076
+ finalUsages
1077
+ );
1078
+ if (toJWK === true) {
1079
+ const jwk = await keyToJWK(derivedKey);
1080
+ return { ...jwk, alg: wrappingAlg, kty: "oct" };
1081
+ }
1082
+ return derivedKey;
1083
+ }
1084
+ async function importKey(key, alg) {
1085
+ if (typeof key === "string") {
1086
+ key = textEncoder.encode(key);
1087
+ }
1088
+ if (key instanceof Uint8Array) {
1089
+ return key;
1090
+ }
1091
+ if (isCryptoKey(key)) {
1092
+ return key;
1093
+ }
1094
+ if (isJWK(key)) {
1095
+ if ("k" in key && typeof key.k === "string") {
1096
+ return base64UrlDecode(key.k, false);
1097
+ } else {
1098
+ if (!alg) {
1099
+ throw new TypeError(
1100
+ "Algorithm must be provided when importing non-oct JWK"
1101
+ );
1102
+ }
1103
+ return jwkTokey({ ...key, alg });
1104
+ }
1105
+ }
1106
+ throw new Error("Invalid key type provided to importKey");
1107
+ }
1108
+ async function exportKey(key, jwk) {
1109
+ const exportedJwk = await keyToJWK(key);
1110
+ if (jwk) {
1111
+ return { ...exportedJwk, ...jwk };
1112
+ }
1113
+ return exportedJwk;
1114
+ }
1115
+ async function wrapKey(alg, keyToWrap, wrappingKey, options = {}) {
1116
+ const cekBytes = keyToWrap instanceof Uint8Array ? keyToWrap : new Uint8Array(await crypto.subtle.exportKey("raw", keyToWrap));
1117
+ let importedWrappingKey;
1118
+ const isPbes = alg.startsWith("PBES2");
1119
+ const isAesKw = ["A128KW", "A192KW", "A256KW"].includes(alg);
1120
+ if (isPbes && (typeof wrappingKey === "string" || wrappingKey instanceof Uint8Array)) {
1121
+ importedWrappingKey = typeof wrappingKey === "string" ? textEncoder.encode(wrappingKey) : wrappingKey;
1122
+ } else if (isPbes || isAesKw) {
1123
+ if (typeof wrappingKey === "string") {
1124
+ throw new TypeError(
1125
+ "Wrapping key must be a CryptoKey, JWK, or Uint8Array for AES-KW or non-password PBES2"
1126
+ );
1127
+ }
1128
+ importedWrappingKey = await importKey(wrappingKey, alg);
1129
+ } else {
1130
+ if (typeof wrappingKey === "string") {
1131
+ throw new TypeError(
1132
+ "Wrapping key must be a CryptoKey, JWK, or Uint8Array for non-PBES2 algorithms"
1133
+ );
1134
+ }
1135
+ importedWrappingKey = await importKey(wrappingKey, alg);
1136
+ }
1137
+ switch (alg) {
1138
+ // AES Key Wrap and PBES2 are handled by the same helper
1139
+ case "A128KW":
1140
+ case "A192KW":
1141
+ case "A256KW":
1142
+ case "PBES2-HS256+A128KW":
1143
+ case "PBES2-HS384+A192KW":
1144
+ case "PBES2-HS512+A256KW": {
1145
+ const { p2s, p2c } = options;
1146
+ if (isPbes && (!p2s || typeof p2c !== "number")) {
1147
+ throw new Error(
1148
+ "PBES2 requires 'p2s' (salt) and 'p2c' (count) options"
1149
+ );
1150
+ }
1151
+ return wrap(alg, importedWrappingKey, cekBytes, p2c, p2s);
1152
+ }
1153
+ case "A128GCMKW":
1154
+ case "A192GCMKW":
1155
+ case "A256GCMKW": {
1156
+ const { encryptedKey, iv, tag } = await encryptIV(
1157
+ alg,
1158
+ importedWrappingKey,
1159
+ cekBytes,
1160
+ options.iv
1161
+ );
1162
+ return { encryptedKey, iv, tag };
1163
+ }
1164
+ case "RSA-OAEP":
1165
+ case "RSA-OAEP-256":
1166
+ case "RSA-OAEP-384":
1167
+ case "RSA-OAEP-512": {
1168
+ const keyToWrapImported = await crypto.subtle.importKey(
1169
+ "raw",
1170
+ cekBytes,
1171
+ { name: "AES-GCM", length: cekBytes.length * 8 },
1172
+ true,
1173
+ ["encrypt", "decrypt"]
1174
+ );
1175
+ const encryptedKey = new Uint8Array(
1176
+ await crypto.subtle.wrapKey(
1177
+ "raw",
1178
+ keyToWrapImported,
1179
+ importedWrappingKey,
1180
+ { name: "RSA-OAEP" }
1181
+ )
1182
+ );
1183
+ return { encryptedKey };
1184
+ }
1185
+ case "ECDH-ES":
1186
+ case "ECDH-ES+A128KW":
1187
+ case "ECDH-ES+A192KW":
1188
+ case "ECDH-ES+A256KW": {
1189
+ throw new Error(`Algorithm ${alg} not yet implemented in wrapKey`);
1190
+ }
1191
+ default: {
1192
+ throw new Error(`Unsupported key wrapping algorithm: ${alg}`);
1193
+ }
1194
+ }
1195
+ }
1196
+ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
1197
+ const { returnAs = true } = options;
1198
+ const defaultExtractable = options.extractable !== false;
1199
+ let importedUnwrappingKey;
1200
+ const isPbes = alg.startsWith("PBES2");
1201
+ const isAesKw = ["A128KW", "A192KW", "A256KW"].includes(alg);
1202
+ if (isPbes && (typeof unwrappingKey === "string" || unwrappingKey instanceof Uint8Array)) {
1203
+ importedUnwrappingKey = typeof unwrappingKey === "string" ? textEncoder.encode(unwrappingKey) : unwrappingKey;
1204
+ } else if (isPbes || isAesKw) {
1205
+ if (typeof unwrappingKey === "string") {
1206
+ throw new TypeError(
1207
+ "Unwrapping key must be a CryptoKey, JWK, or Uint8Array for AES-KW or non-password PBES2"
1208
+ );
1209
+ }
1210
+ importedUnwrappingKey = await importKey(unwrappingKey, alg);
1211
+ } else {
1212
+ if (typeof unwrappingKey === "string") {
1213
+ throw new TypeError(
1214
+ "Unwrapping key must be a CryptoKey, JWK, or Uint8Array for non-PBES2 algorithms"
1215
+ );
1216
+ }
1217
+ importedUnwrappingKey = await importKey(unwrappingKey, alg);
1218
+ }
1219
+ let unwrappedCekBytes;
1220
+ switch (alg) {
1221
+ // AES Key Wrap and PBES2 are handled by the same helper
1222
+ case "A128KW":
1223
+ case "A192KW":
1224
+ case "A256KW":
1225
+ case "PBES2-HS256+A128KW":
1226
+ case "PBES2-HS384+A192KW":
1227
+ case "PBES2-HS512+A256KW": {
1228
+ const { p2s, p2c } = options;
1229
+ let p2sBytes;
1230
+ if (isPbes) {
1231
+ if (!p2s || typeof p2c !== "number") {
1232
+ throw new Error(
1233
+ "PBES2 requires 'p2s' (salt) and 'p2c' (count) options"
1234
+ );
1235
+ }
1236
+ p2sBytes = typeof p2s === "string" ? base64UrlDecode(p2s, false) : p2s;
1237
+ }
1238
+ unwrappedCekBytes = await unwrap(
1239
+ alg,
1240
+ importedUnwrappingKey,
1241
+ wrappedKey,
1242
+ p2c,
1243
+ p2sBytes
1244
+ );
1245
+ break;
1246
+ }
1247
+ case "A128GCMKW":
1248
+ case "A192GCMKW":
1249
+ case "A256GCMKW": {
1250
+ if (!options.iv || !options.tag) {
1251
+ throw new Error(
1252
+ "AES-GCMKW requires 'iv' and 'tag' options for unwrapping"
1253
+ );
1254
+ }
1255
+ const ivBytes = typeof options.iv === "string" ? base64UrlDecode(options.iv, false) : options.iv;
1256
+ const tagBytes = typeof options.tag === "string" ? base64UrlDecode(options.tag, false) : options.tag;
1257
+ unwrappedCekBytes = await decryptIV(
1258
+ alg,
1259
+ importedUnwrappingKey,
1260
+ wrappedKey,
1261
+ ivBytes,
1262
+ tagBytes
1263
+ );
1264
+ break;
1265
+ }
1266
+ case "RSA-OAEP":
1267
+ case "RSA-OAEP-256":
1268
+ case "RSA-OAEP-384":
1269
+ case "RSA-OAEP-512": {
1270
+ const unwrappedKey = await crypto.subtle.unwrapKey(
1271
+ "raw",
1272
+ wrappedKey,
1273
+ importedUnwrappingKey,
1274
+ { name: "RSA-OAEP" },
1275
+ options.unwrappedKeyAlgorithm || { name: "AES-GCM" },
1276
+ // Fallback
1277
+ defaultExtractable,
1278
+ options.keyUsage || ["encrypt", "decrypt"]
1279
+ // Default usages
1280
+ );
1281
+ if (returnAs) return unwrappedKey;
1282
+ unwrappedCekBytes = new Uint8Array(
1283
+ await crypto.subtle.exportKey("raw", unwrappedKey)
1284
+ );
1285
+ break;
1286
+ }
1287
+ case "ECDH-ES":
1288
+ case "ECDH-ES+A128KW":
1289
+ case "ECDH-ES+A192KW":
1290
+ case "ECDH-ES+A256KW": {
1291
+ if (!options.epk) {
1292
+ throw new Error("ECDH-ES requires 'epk' (Ephemeral Public Key) option");
1293
+ }
1294
+ throw new Error(`Algorithm ${alg} not yet implemented in unwrapKey`);
1295
+ }
1296
+ default: {
1297
+ throw new Error(`Unsupported key unwrapping algorithm: ${alg}`);
1298
+ }
1299
+ }
1300
+ if (!returnAs) {
1301
+ return unwrappedCekBytes;
1302
+ }
1303
+ const finalKey = await crypto.subtle.importKey(
1304
+ "raw",
1305
+ unwrappedCekBytes,
1306
+ options.unwrappedKeyAlgorithm || { name: "AES-GCM" },
1307
+ defaultExtractable,
1308
+ options.keyUsage || ["encrypt", "decrypt"]
1309
+ );
1310
+ return finalKey;
1311
+ }
1312
+ async function importJWKFromPEM(pem, pemType, alg, importOptions, jwkExtras) {
1313
+ let cryptoKey;
1314
+ const defaultExtractable = importOptions?.extractable !== false;
1315
+ switch (pemType) {
1316
+ case "pkcs8": {
1317
+ cryptoKey = await fromPKCS8(pem, alg, {
1318
+ ...importOptions,
1319
+ extractable: defaultExtractable
1320
+ });
1321
+ break;
1322
+ }
1323
+ case "spki": {
1324
+ cryptoKey = await fromSPKI(pem, alg, {
1325
+ ...importOptions,
1326
+ extractable: defaultExtractable
1327
+ });
1328
+ break;
1329
+ }
1330
+ case "x509": {
1331
+ cryptoKey = await fromX509(pem, alg, {
1332
+ ...importOptions,
1333
+ extractable: defaultExtractable
1334
+ });
1335
+ break;
1336
+ }
1337
+ default: {
1338
+ throw new TypeError(`Unsupported PEM type: ${pemType}`);
1339
+ }
1340
+ }
1341
+ const finalJWKExtras = { alg, ...jwkExtras };
1342
+ return exportKey(cryptoKey, finalJWKExtras);
1343
+ }
1344
+ async function exportJWKToPEM(jwk, pemFormat, algForCryptoKeyImport) {
1345
+ if (jwk.kty === "oct") {
1346
+ throw new TypeError(
1347
+ "Octet (symmetric) JWKs (kty: 'oct') cannot be exported to PKCS8 or SPKI PEM formats."
1348
+ );
1349
+ }
1350
+ const effectiveAlg = jwk.alg || algForCryptoKeyImport;
1351
+ if (!effectiveAlg && (jwk.kty === "RSA" || jwk.kty === "EC" || jwk.kty === "OKP")) {
1352
+ throw new TypeError(
1353
+ "Algorithm (alg) must be provided in the JWK or as a parameter for converting this JWK type to a CryptoKey."
1354
+ );
1355
+ }
1356
+ const jwkForImport = { ...jwk, ext: true };
1357
+ const cryptoKeyCandidate = await importKey(jwkForImport, effectiveAlg);
1358
+ if (!isCryptoKey(cryptoKeyCandidate)) {
1359
+ throw new Error(
1360
+ "Failed to convert JWK to a CryptoKey instance suitable for PEM export."
1361
+ );
1362
+ }
1363
+ const cryptoKey = cryptoKeyCandidate;
1364
+ switch (pemFormat) {
1365
+ case "pkcs8": {
1366
+ if (cryptoKey.type !== "private") {
1367
+ throw new TypeError(
1368
+ `Only 'private' type CryptoKeys can be exported to PKCS8 PEM format. Key type is '${cryptoKey.type}'.`
1369
+ );
1370
+ }
1371
+ return toPKCS8(cryptoKey);
1372
+ }
1373
+ case "spki": {
1374
+ if (cryptoKey.type !== "public") {
1375
+ throw new TypeError(
1376
+ `Only 'public' type CryptoKeys can be exported to SPKI PEM format. Key type is '${cryptoKey.type}'.`
1377
+ );
1378
+ }
1379
+ return toSPKI(cryptoKey);
1380
+ }
1381
+ default: {
1382
+ throw new TypeError(`Unsupported PEM format: ${pemFormat}`);
1383
+ }
1384
+ }
1385
+ }
1386
+ function getGenerateKeyParams(alg, options) {
1387
+ let algorithm;
1388
+ let keyUsages;
1389
+ const defaultKeyUsage = options?.keyUsage;
1390
+ switch (alg) {
1391
+ // HMAC Signatures
1392
+ case "HS256":
1393
+ case "HS384":
1394
+ case "HS512": {
1395
+ algorithm = {
1396
+ name: "HMAC",
1397
+ hash: `SHA-${alg.slice(2)}`
1398
+ };
1399
+ keyUsages = defaultKeyUsage ?? ["sign", "verify"];
1400
+ break;
1401
+ }
1402
+ // RSA Signatures
1403
+ case "RS256":
1404
+ case "RS384":
1405
+ case "RS512": {
1406
+ algorithm = {
1407
+ name: "RSASSA-PKCS1-v1_5",
1408
+ modulusLength: options?.modulusLength ?? 2048,
1409
+ publicExponent: options?.publicExponent ?? new Uint8Array([1, 0, 1]),
1410
+ hash: `SHA-${alg.slice(2)}`
1411
+ };
1412
+ keyUsages = defaultKeyUsage ?? ["sign", "verify"];
1413
+ break;
1414
+ }
1415
+ // RSA PSS Signatures
1416
+ case "PS256":
1417
+ case "PS384":
1418
+ case "PS512": {
1419
+ algorithm = {
1420
+ name: "RSA-PSS",
1421
+ modulusLength: options?.modulusLength ?? 2048,
1422
+ publicExponent: options?.publicExponent ?? new Uint8Array([1, 0, 1]),
1423
+ hash: `SHA-${alg.slice(2)}`
1424
+ };
1425
+ keyUsages = defaultKeyUsage ?? ["sign", "verify"];
1426
+ break;
1427
+ }
1428
+ // ECDSA Signatures
1429
+ case "ES256": {
1430
+ algorithm = { name: "ECDSA", namedCurve: "P-256" };
1431
+ keyUsages = defaultKeyUsage ?? ["sign", "verify"];
1432
+ break;
1433
+ }
1434
+ case "ES384": {
1435
+ algorithm = { name: "ECDSA", namedCurve: "P-384" };
1436
+ keyUsages = defaultKeyUsage ?? ["sign", "verify"];
1437
+ break;
1438
+ }
1439
+ case "ES512": {
1440
+ algorithm = { name: "ECDSA", namedCurve: "P-521" };
1441
+ keyUsages = defaultKeyUsage ?? ["sign", "verify"];
1442
+ break;
1443
+ }
1444
+ // EdDSA Signatures should be imported
1445
+ // RSA Encryption
1446
+ case "RSA-OAEP":
1447
+ case "RSA-OAEP-256":
1448
+ case "RSA-OAEP-384":
1449
+ case "RSA-OAEP-512": {
1450
+ algorithm = {
1451
+ name: "RSA-OAEP",
1452
+ modulusLength: options?.modulusLength ?? 2048,
1453
+ publicExponent: options?.publicExponent ?? new Uint8Array([1, 0, 1]),
1454
+ hash: `SHA-${Number.parseInt(alg.slice(9), 10) || 1}`
1455
+ };
1456
+ keyUsages = defaultKeyUsage ?? [
1457
+ "encrypt",
1458
+ "decrypt",
1459
+ "wrapKey",
1460
+ "unwrapKey"
1461
+ ];
1462
+ break;
1463
+ }
1464
+ // AES Key Wrap
1465
+ case "A128KW":
1466
+ case "A192KW":
1467
+ case "A256KW": {
1468
+ const length = Number.parseInt(alg.slice(1, 4), 10);
1469
+ algorithm = { name: "AES-KW", length };
1470
+ keyUsages = defaultKeyUsage ?? ["wrapKey", "unwrapKey"];
1471
+ break;
1472
+ }
1473
+ // AES GCM Encryption
1474
+ case "A128GCM":
1475
+ case "A192GCM":
1476
+ case "A256GCM": {
1477
+ const length = Number.parseInt(alg.slice(1, 4), 10);
1478
+ algorithm = { name: "AES-GCM", length };
1479
+ keyUsages = defaultKeyUsage ?? ["encrypt", "decrypt"];
1480
+ break;
1481
+ }
1482
+ // ECDH Key Agreement (requires specific curve in JWK for import, generation is simpler)
1483
+ // For generation, typically generate the EC key pair first (e.g., P-256, P-384, P-521, X25519)
1484
+ // then use deriveBits/deriveKey. Direct generation for "ECDH-ES*" alg isn't standard.
1485
+ // Handle EC key pair generation under ES256/ES384/ES512.
1486
+ default: {
1487
+ throw new Error(
1488
+ `Unsupported or invalid algorithm for key generation: ${alg}`
1489
+ );
1490
+ }
1491
+ }
1492
+ return { algorithm, keyUsages };
1493
+ }
1494
+
1495
+ const jwk = {
1496
+ __proto__: null,
1497
+ deriveKeyFromPassword: deriveKeyFromPassword,
1498
+ exportJWKToPEM: exportJWKToPEM,
1499
+ exportKey: exportKey,
1500
+ generateKey: generateKey,
1501
+ importJWKFromPEM: importJWKFromPEM,
1502
+ importKey: importKey,
1503
+ unwrapKey: unwrapKey,
1504
+ wrapKey: wrapKey
1505
+ };
1506
+
1507
+ export { checkEncCryptoKey as a, bitLengthCEK as b, checkSigCryptoKey as c, jwkTokey as d, encryptIV as e, generateIV as f, generateCEK as g, encrypt as h, importKey as i, jwk as j, keyToJWK as k, unwrapKey as l, decrypt as m, generateKey as n, deriveKeyFromPassword as o, exportKey as p, wrapKey as q, importJWKFromPEM as r, exportJWKToPEM as s, uint32be as u, wrap as w };