unjwt 0.5.39 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +156 -13
  2. package/dist/_chunks/_crypto.mjs +1014 -0
  3. package/dist/_chunks/error.d.mts +67 -0
  4. package/dist/_chunks/index.d.mts +631 -219
  5. package/dist/_chunks/jwk.d.mts +220 -87
  6. package/dist/_chunks/jws.mjs +729 -682
  7. package/dist/_chunks/utils.mjs +74 -69
  8. package/dist/adapters/h3v1/index.d.mts +282 -0
  9. package/dist/{_chunks/h3v1.mjs → adapters/h3v1/index.mjs} +204 -91
  10. package/dist/adapters/h3v2/index.d.mts +213 -0
  11. package/dist/adapters/{h3v2.mjs → h3v2/index.mjs} +215 -113
  12. package/dist/core/_crypto/index.d.mts +71 -0
  13. package/dist/core/_crypto/index.mjs +2 -0
  14. package/dist/core/jwe.d.mts +40 -0
  15. package/dist/{_chunks → core}/jwe.mjs +61 -32
  16. package/dist/core/jwk.d.mts +231 -0
  17. package/dist/core/jwk.mjs +686 -0
  18. package/dist/core/jws.d.mts +31 -0
  19. package/dist/core/jws.mjs +104 -0
  20. package/dist/{_chunks/index2.d.mts → core/utils/index.d.mts} +13 -12
  21. package/dist/core/utils/index.mjs +2 -0
  22. package/dist/index.d.mts +7 -6
  23. package/dist/index.mjs +5 -5
  24. package/package.json +50 -41
  25. package/dist/_chunks/chunk.mjs +0 -13
  26. package/dist/_chunks/jwe.d.mts +0 -43
  27. package/dist/_chunks/jwk.mjs +0 -1592
  28. package/dist/_chunks/jws.d.mts +0 -46
  29. package/dist/adapters/h3.d.mts +0 -3
  30. package/dist/adapters/h3.mjs +0 -3
  31. package/dist/adapters/h3v1.d.mts +0 -3
  32. package/dist/adapters/h3v1.mjs +0 -3
  33. package/dist/adapters/h3v2.d.mts +0 -184
  34. package/dist/jwe.d.mts +0 -3
  35. package/dist/jwe.mjs +0 -4
  36. package/dist/jwk.d.mts +0 -3
  37. package/dist/jwk.mjs +0 -3
  38. package/dist/jws.d.mts +0 -3
  39. package/dist/jws.mjs +0 -4
  40. package/dist/utils.d.mts +0 -3
  41. package/dist/utils.mjs +0 -2
@@ -1,3 +1,36 @@
1
+ //#region src/core/error.ts
2
+ /**
3
+ * Base error class for all unjwt errors. Every error thrown by the library is
4
+ * an instance of `JWTError`, so a single `instanceof` check is sufficient to
5
+ * distinguish library errors from other thrown values. Use the `code` property
6
+ * (or the `isJWTError` type guard) to narrow to a specific error condition.
7
+ *
8
+ * @example
9
+ * import { JWTError, isJWTError } from "unjwt/jws";
10
+ *
11
+ * try {
12
+ * await verify(token, key);
13
+ * } catch (err) {
14
+ * if (isJWTError(err, "ERR_JWT_EXPIRED")) {
15
+ * console.log(err.cause.jti); // string | undefined
16
+ * }
17
+ * }
18
+ */
19
+ var JWTError = class extends Error {
20
+ code;
21
+ cause;
22
+ constructor(message, code, cause) {
23
+ super(message);
24
+ this.name = "JWTError";
25
+ this.code = code;
26
+ this.cause = cause;
27
+ }
28
+ };
29
+ function isJWTError(error, code) {
30
+ if (!(error instanceof JWTError)) return false;
31
+ return code === void 0 || error.code === code;
32
+ }
33
+ //#endregion
1
34
  //#region src/core/utils/jwt.ts
2
35
  /**
3
36
  * Apply default typ/cty semantics shared by JWS & JWE.
@@ -106,23 +139,31 @@ function validateJwtClaims(jwtClaims, options = {}) {
106
139
  if (options.subject) allRequiredClaims.add("sub");
107
140
  if (options.maxTokenAge) allRequiredClaims.add("iat");
108
141
  for (const claimName of allRequiredClaims) if (!(claimName in jwtClaims)) missingClaims.add(claimName);
109
- if (missingClaims.size > 0) throw new Error(`Missing required JWT Claims: ${[...missingClaims].join(", ")}`);
142
+ if (missingClaims.size > 0) throw new JWTError(`Missing required JWT Claims: ${[...missingClaims].join(", ")}`, "ERR_JWT_CLAIM_MISSING");
110
143
  if (options.issuer) {
111
144
  const expectedIssuers = maybeArray(options.issuer);
112
- if (!jwtClaims.iss || !expectedIssuers.includes(jwtClaims.iss)) throw new Error(`Invalid JWT "iss" (Issuer) Claim: Expected ${expectedIssuers.join(" or ")}, got ${jwtClaims.iss}`);
145
+ if (!jwtClaims.iss || !expectedIssuers.includes(jwtClaims.iss)) throw new JWTError(`Invalid JWT "iss" (Issuer) Claim: Expected ${expectedIssuers.join(" or ")}, got ${jwtClaims.iss}`, "ERR_JWT_CLAIM_INVALID");
113
146
  }
114
- if (options.subject && jwtClaims.sub !== options.subject) throw new Error(`Invalid JWT "sub" (Subject) Claim: Expected ${options.subject}, got ${jwtClaims.sub}`);
147
+ if (options.subject && jwtClaims.sub !== options.subject) throw new JWTError(`Invalid JWT "sub" (Subject) Claim: Expected ${options.subject}, got ${jwtClaims.sub}`, "ERR_JWT_CLAIM_INVALID");
115
148
  if (options.audience) {
116
149
  const expectedAudiences = maybeArray(options.audience);
117
150
  const claimAudience = maybeArray(jwtClaims.aud || []);
118
- if (!claimAudience.some((aud) => expectedAudiences.includes(aud))) throw new Error(`Invalid JWT "aud" (Audience) Claim: Expected ${expectedAudiences.join(" or ")}, got ${claimAudience.join(", ")}`);
151
+ if (!claimAudience.some((aud) => expectedAudiences.includes(aud))) throw new JWTError(`Invalid JWT "aud" (Audience) Claim: Expected ${expectedAudiences.join(" or ")}, got ${claimAudience.join(", ")}`, "ERR_JWT_CLAIM_INVALID");
119
152
  }
120
- if (typeof jwtClaims.nbf === "number" && jwtClaims.nbf > currentTime + clockTolerance) throw new Error(`JWT "nbf" (Not Before) Claim validation failed: Token is not yet valid (nbf: ${(/* @__PURE__ */ new Date(jwtClaims.nbf * 1e3)).toISOString()})`);
121
- if (typeof jwtClaims.exp === "number" && jwtClaims.exp <= currentTime - clockTolerance) throw new Error(`JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${(/* @__PURE__ */ new Date(jwtClaims.exp * 1e3)).toISOString()})`);
153
+ if (typeof jwtClaims.nbf === "number" && jwtClaims.nbf > currentTime + clockTolerance) throw new JWTError(`JWT "nbf" (Not Before) Claim validation failed: Token is not yet valid (nbf: ${(/* @__PURE__ */ new Date(jwtClaims.nbf * 1e3)).toISOString()})`, "ERR_JWT_NBF");
154
+ if (typeof jwtClaims.exp === "number" && jwtClaims.exp <= currentTime - clockTolerance) throw new JWTError(`JWT "exp" (Expiration Time) Claim validation failed: Token has expired (exp: ${(/* @__PURE__ */ new Date(jwtClaims.exp * 1e3)).toISOString()})`, "ERR_JWT_EXPIRED", {
155
+ jti: jwtClaims.jti,
156
+ iat: jwtClaims.iat,
157
+ exp: jwtClaims.exp
158
+ });
122
159
  if (options.maxTokenAge) {
123
- if (typeof jwtClaims.iat !== "number") throw new TypeError("JWT \"iat\" (Issued At) Claim must be a number when maxTokenAge is set.");
124
- if (jwtClaims.iat > currentTime + clockTolerance) throw new Error(`JWT "iat" (Issued At) Claim validation failed: Token was issued in the future (iat: ${(/* @__PURE__ */ new Date(jwtClaims.iat * 1e3)).toISOString()})`);
125
- if (jwtClaims.iat < currentTime - computeMaxTokenAgeSeconds(options.maxTokenAge) - clockTolerance) throw new Error(`JWT "iat" (Issued At) Claim validation failed: Token is too old (maxTokenAge: ${options.maxTokenAge}s, iat: ${(/* @__PURE__ */ new Date(jwtClaims.iat * 1e3)).toISOString()})`);
160
+ if (typeof jwtClaims.iat !== "number") throw new JWTError("JWT \"iat\" (Issued At) Claim must be a number when maxTokenAge is set.", "ERR_JWT_CLAIM_INVALID");
161
+ if (jwtClaims.iat > currentTime + clockTolerance) throw new JWTError(`JWT "iat" (Issued At) Claim validation failed: Token was issued in the future (iat: ${(/* @__PURE__ */ new Date(jwtClaims.iat * 1e3)).toISOString()})`, "ERR_JWT_CLAIM_INVALID");
162
+ if (jwtClaims.iat < currentTime - computeMaxTokenAgeSeconds(options.maxTokenAge) - clockTolerance) throw new JWTError(`JWT "iat" (Issued At) Claim validation failed: Token is too old (maxTokenAge: ${options.maxTokenAge}s, iat: ${(/* @__PURE__ */ new Date(jwtClaims.iat * 1e3)).toISOString()})`, "ERR_JWT_EXPIRED", {
163
+ jti: jwtClaims.jti,
164
+ iat: jwtClaims.iat,
165
+ exp: jwtClaims.exp
166
+ });
126
167
  }
127
168
  }
128
169
  const BASE_HEADER_PARAMS = [
@@ -137,11 +178,11 @@ const BASE_HEADER_PARAMS = [
137
178
  "x5u"
138
179
  ];
139
180
  /** Validate critical headers in JWS semantics. */
140
- function validateCriticalHeadersJWS(protectedHeader, requiredHeaders = []) {
181
+ function validateCriticalHeadersJWS(protectedHeader, recognizedHeaders = []) {
141
182
  if (!protectedHeader.crit) return;
142
183
  const missingHeaderParams = /* @__PURE__ */ new Set();
143
184
  const recognizedParams = new Set([
144
- ...requiredHeaders,
185
+ ...recognizedHeaders,
145
186
  ...BASE_HEADER_PARAMS,
146
187
  "b64"
147
188
  ]);
@@ -149,14 +190,14 @@ function validateCriticalHeadersJWS(protectedHeader, requiredHeaders = []) {
149
190
  if (recognizedParams.has(param) && (param in protectedHeader || param === "b64")) continue;
150
191
  missingHeaderParams.add(param);
151
192
  }
152
- if (missingHeaderParams.size > 0) throw new Error(`Missing critical header parameters: ${[...missingHeaderParams].join(", ")}`);
193
+ if (missingHeaderParams.size > 0) throw new JWTError(`Missing critical header parameters: ${[...missingHeaderParams].join(", ")}`, "ERR_JWS_INVALID");
153
194
  }
154
195
  /** Validate critical headers in JWE semantics. */
155
- function validateCriticalHeadersJWE(protectedHeader, understoodFromOptions) {
156
- if (protectedHeader.crit && !understoodFromOptions) throw new Error(`Unprocessed critical header parameters: ${protectedHeader.crit.join(", ")}`);
196
+ function validateCriticalHeadersJWE(protectedHeader, recognizedHeaders) {
197
+ if (protectedHeader.crit && !recognizedHeaders) throw new JWTError(`Unprocessed critical header parameters: ${protectedHeader.crit.join(", ")}`, "ERR_JWE_INVALID");
157
198
  if (!protectedHeader.crit) return;
158
199
  const understoodParams = new Set([
159
- ...understoodFromOptions || [],
200
+ ...recognizedHeaders || [],
160
201
  ...BASE_HEADER_PARAMS,
161
202
  "enc",
162
203
  "iv",
@@ -168,40 +209,38 @@ function validateCriticalHeadersJWE(protectedHeader, understoodFromOptions) {
168
209
  "apv"
169
210
  ]);
170
211
  for (const critParam of protectedHeader.crit) {
171
- if (!Object.prototype.hasOwnProperty.call(protectedHeader, critParam)) throw new Error(`Critical header parameter "${critParam}" listed in "crit" but not present in the protected header.`);
172
- if (!understoodParams.has(critParam)) throw new Error(`Unprocessed critical header parameters: ${critParam}`);
212
+ if (!Object.prototype.hasOwnProperty.call(protectedHeader, critParam)) throw new JWTError(`Critical header parameter "${critParam}" listed in "crit" but not present in the protected header.`, "ERR_JWE_INVALID");
213
+ if (!understoodParams.has(critParam)) throw new JWTError(`Unprocessed critical header parameters: ${critParam}`, "ERR_JWE_INVALID");
173
214
  }
174
215
  }
175
216
  //#endregion
176
217
  //#region src/core/utils/sanitize.ts
177
218
  /**
178
- * Forked from unsecure@0.0.4
219
+ * Originally derived from unsecure@0.0.4
179
220
  * Licensed under the MIT License.
180
221
  *
181
222
  * Source: https://github.com/sandros94/unsecure/blob/913ed5580c551153a19154b4916c67a8efb2be85/src/sanitize.ts
182
223
  */
183
224
  /**
184
- * Remove prototype-pollution vectors from a plain record in-place.
185
- * This strips __proto__, prototype, and constructor own properties.
186
- * Returns the same reference for convenience.
225
+ * Returns a deep structural copy of `obj` with prototype-pollution vectors
226
+ * stripped at every level (`__proto__`, `prototype`, `constructor`).
227
+ *
228
+ * The input is never modified. Arrays are copied element-by-element;
229
+ * nested objects are recursed with a WeakSet cycle guard.
187
230
  */
188
231
  function sanitizeObject(obj) {
189
232
  if (!obj || typeof obj !== "object") return obj;
190
- sanitizeKeys(obj);
191
- return obj;
192
- }
193
- function sanitizeKeys(current, seen) {
194
- if (Object.prototype.hasOwnProperty.call(current, "__proto__")) delete current["__proto__"];
195
- if (Object.prototype.hasOwnProperty.call(current, "prototype")) delete current["prototype"];
196
- if (Object.prototype.hasOwnProperty.call(current, "constructor")) delete current["constructor"];
197
- const items = Array.isArray(current) ? current : Object.values(current);
198
- for (const v of items) if (v && typeof v === "object") {
199
- seen ??= /* @__PURE__ */ new WeakSet();
200
- if (!seen.has(v)) {
201
- seen.add(v);
202
- sanitizeKeys(v, seen);
203
- }
233
+ return _sanitizeCopy(obj, /* @__PURE__ */ new WeakSet());
234
+ }
235
+ function _sanitizeCopy(current, seen) {
236
+ seen.add(current);
237
+ if (Array.isArray(current)) return current.map((item) => item && typeof item === "object" && !seen.has(item) ? _sanitizeCopy(item, seen) : item);
238
+ const result = {};
239
+ for (const [key, value] of Object.entries(current)) {
240
+ if (key === "__proto__" || key === "prototype" || key === "constructor") continue;
241
+ result[key] = value && typeof value === "object" && !seen.has(value) ? _sanitizeCopy(value, seen) : value;
204
242
  }
243
+ return result;
205
244
  }
206
245
  //#endregion
207
246
  //#region src/core/utils/index.ts
@@ -337,157 +376,61 @@ function isPublicJWK(key) {
337
376
  return false;
338
377
  }
339
378
  //#endregion
340
- //#region src/core/jose/crypto_key.ts
341
- /**
342
- * This is a fork of Jose library's utility functions.
343
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/crypto_key.ts
344
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
345
- */
346
- function unusable(name, prop = "algorithm.name") {
347
- return /* @__PURE__ */ new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
348
- }
349
- function isAlgorithm(algorithm, name) {
350
- return algorithm.name === name;
351
- }
352
- function getHashLength(hash) {
353
- return Number.parseInt(hash.name.slice(4), 10);
379
+ //#region src/core/_crypto/_sign-verify.ts
380
+ async function sign$1(alg, key, data) {
381
+ checkSigCryptoKey(key, alg, "sign");
382
+ const signature = await crypto.subtle.sign(subtleAlgorithm(alg, key.algorithm), key, data);
383
+ return new Uint8Array(signature);
354
384
  }
355
- function getNamedCurve$1(alg) {
356
- switch (alg) {
357
- case "ES256": return "P-256";
358
- case "ES384": return "P-384";
359
- case "ES512": return "P-521";
360
- default: throw new Error("unreachable");
385
+ async function verify$1(alg, key, signature, data) {
386
+ checkSigCryptoKey(key, alg, "verify");
387
+ try {
388
+ return await crypto.subtle.verify(subtleAlgorithm(alg, key.algorithm), key, signature, data);
389
+ } catch {
390
+ return false;
361
391
  }
362
392
  }
363
- function checkUsage(key, usage) {
364
- if (usage && !key.usages.includes(usage)) throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
365
- }
366
393
  function checkSigCryptoKey(key, alg, usage) {
367
394
  switch (alg) {
368
395
  case "HS256":
369
396
  case "HS384":
370
397
  case "HS512": {
371
- if (!isAlgorithm(key.algorithm, "HMAC")) throw unusable("HMAC");
398
+ if (!isAlgorithm$3(key.algorithm, "HMAC")) throw unusable$4("HMAC");
372
399
  const expected = Number.parseInt(alg.slice(2), 10);
373
- if (getHashLength(key.algorithm.hash) !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
400
+ if (getHashLength$1(key.algorithm.hash) !== expected) throw unusable$4(`SHA-${expected}`, "algorithm.hash");
374
401
  break;
375
402
  }
376
403
  case "RS256":
377
404
  case "RS384":
378
405
  case "RS512": {
379
- if (!isAlgorithm(key.algorithm, "RSASSA-PKCS1-v1_5")) throw unusable("RSASSA-PKCS1-v1_5");
406
+ if (!isAlgorithm$3(key.algorithm, "RSASSA-PKCS1-v1_5")) throw unusable$4("RSASSA-PKCS1-v1_5");
380
407
  const expected = Number.parseInt(alg.slice(2), 10);
381
- if (getHashLength(key.algorithm.hash) !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
408
+ if (getHashLength$1(key.algorithm.hash) !== expected) throw unusable$4(`SHA-${expected}`, "algorithm.hash");
382
409
  break;
383
410
  }
384
411
  case "PS256":
385
412
  case "PS384":
386
413
  case "PS512": {
387
- if (!isAlgorithm(key.algorithm, "RSA-PSS")) throw unusable("RSA-PSS");
414
+ if (!isAlgorithm$3(key.algorithm, "RSA-PSS")) throw unusable$4("RSA-PSS");
388
415
  const expected = Number.parseInt(alg.slice(2), 10);
389
- if (getHashLength(key.algorithm.hash) !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
416
+ if (getHashLength$1(key.algorithm.hash) !== expected) throw unusable$4(`SHA-${expected}`, "algorithm.hash");
390
417
  break;
391
418
  }
392
419
  case "Ed25519":
393
420
  case "EdDSA":
394
- if (!isAlgorithm(key.algorithm, "Ed25519")) throw unusable("Ed25519");
421
+ if (!isAlgorithm$3(key.algorithm, "Ed25519")) throw unusable$4("Ed25519");
395
422
  break;
396
423
  case "ES256":
397
424
  case "ES384":
398
425
  case "ES512": {
399
- if (!isAlgorithm(key.algorithm, "ECDSA")) throw unusable("ECDSA");
426
+ if (!isAlgorithm$3(key.algorithm, "ECDSA")) throw unusable$4("ECDSA");
400
427
  const expected = getNamedCurve$1(alg);
401
- if (key.algorithm.namedCurve !== expected) throw unusable(expected, "algorithm.namedCurve");
402
- break;
403
- }
404
- default: throw new TypeError("CryptoKey does not support this operation");
405
- }
406
- checkUsage(key, usage);
407
- }
408
- function checkEncCryptoKey(key, alg, usage) {
409
- switch (alg) {
410
- case "A128GCM":
411
- case "A192GCM":
412
- case "A256GCM": {
413
- if (!isAlgorithm(key.algorithm, "AES-GCM")) throw unusable("AES-GCM");
414
- const expected = Number.parseInt(alg.slice(1, 4), 10);
415
- if (key.algorithm.length !== expected) throw unusable(expected, "algorithm.length");
416
- break;
417
- }
418
- case "A128KW":
419
- case "A192KW":
420
- case "A256KW": {
421
- if (!isAlgorithm(key.algorithm, "AES-KW")) throw unusable("AES-KW");
422
- const expected = Number.parseInt(alg.slice(1, 4), 10);
423
- if (key.algorithm.length !== expected) throw unusable(expected, "algorithm.length");
424
- break;
425
- }
426
- case "ECDH":
427
- switch (key.algorithm.name) {
428
- case "ECDH":
429
- case "X25519": break;
430
- default: throw unusable("ECDH or X25519");
431
- }
432
- break;
433
- case "PBES2-HS256+A128KW":
434
- case "PBES2-HS384+A192KW":
435
- case "PBES2-HS512+A256KW":
436
- if (!isAlgorithm(key.algorithm, "PBKDF2")) throw unusable("PBKDF2");
437
- break;
438
- case "RSA-OAEP":
439
- case "RSA-OAEP-256":
440
- case "RSA-OAEP-384":
441
- case "RSA-OAEP-512": {
442
- if (!isAlgorithm(key.algorithm, "RSA-OAEP")) throw unusable("RSA-OAEP");
443
- const expected = Number.parseInt(alg.slice(9), 10) || 1;
444
- if (getHashLength(key.algorithm.hash) !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
428
+ if (key.algorithm.namedCurve !== expected) throw unusable$4(expected, "algorithm.namedCurve");
445
429
  break;
446
430
  }
447
431
  default: throw new TypeError("CryptoKey does not support this operation");
448
432
  }
449
- checkUsage(key, usage);
450
- }
451
- //#endregion
452
- //#region src/core/jose/sign-verify.ts
453
- /**
454
- * This is a fork of Jose library's utility functions.
455
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/sign.ts
456
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/verify.ts
457
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
458
- */
459
- async function sign$1(alg, key, data) {
460
- const cryptoKey = await getSignVerifyKey(alg, key, "sign");
461
- checkKeyLength(alg, cryptoKey);
462
- const signature = await crypto.subtle.sign(subtleAlgorithm(alg, cryptoKey.algorithm), cryptoKey, data);
463
- return new Uint8Array(signature);
464
- }
465
- async function verify$1(alg, key, signature, data) {
466
- const cryptoKey = await getSignVerifyKey(alg, key, "verify");
467
- checkKeyLength(alg, cryptoKey);
468
- const algorithm = subtleAlgorithm(alg, cryptoKey.algorithm);
469
- try {
470
- return await crypto.subtle.verify(algorithm, cryptoKey, signature, data);
471
- } catch {
472
- return false;
473
- }
474
- }
475
- function checkKeyLength(alg, key) {
476
- if (alg.startsWith("RS") || alg.startsWith("PS")) {
477
- const { modulusLength } = key.algorithm;
478
- if (typeof modulusLength !== "number" || modulusLength < 2048) throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
479
- }
480
- }
481
- async function getSignVerifyKey(alg, key, usage) {
482
- if (key instanceof Uint8Array) {
483
- if (!alg.startsWith("HS")) throw new TypeError("Key must be of type CryptoKey or JSON Web Key for non-HMAC algorithms");
484
- return crypto.subtle.importKey("raw", key, {
485
- hash: `SHA-${alg.slice(-3)}`,
486
- name: "HMAC"
487
- }, false, [usage]);
488
- }
489
- checkSigCryptoKey(key, alg, usage);
490
- return key;
433
+ checkUsage$4(key, usage);
491
434
  }
492
435
  function subtleAlgorithm(alg, algorithm) {
493
436
  const hash = `SHA-${alg.slice(-3)}`;
@@ -523,44 +466,28 @@ function subtleAlgorithm(alg, algorithm) {
523
466
  default: throw new Error(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
524
467
  }
525
468
  }
526
- //#endregion
527
- //#region src/core/jose/buffer_utils.ts
528
- /**
529
- * This is a fork of Jose library's utility functions.
530
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/buffer_utils.ts
531
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
532
- */
533
- const MAX_INT32 = 2 ** 32;
534
- function writeUInt32BE(buf, value, offset) {
535
- if (value < 0 || value >= MAX_INT32) throw new RangeError(`value must be >= 0 and <= ${MAX_INT32 - 1}. Received ${value}`);
536
- buf.set([
537
- value >>> 24,
538
- value >>> 16,
539
- value >>> 8,
540
- value & 255
541
- ], offset);
469
+ function unusable$4(name, prop = "algorithm.name") {
470
+ return /* @__PURE__ */ new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
542
471
  }
543
- function uint64be(value) {
544
- const high = Math.floor(value / MAX_INT32);
545
- const low = value % MAX_INT32;
546
- const buf = new Uint8Array(8);
547
- writeUInt32BE(buf, high, 0);
548
- writeUInt32BE(buf, low, 4);
549
- return buf;
472
+ function isAlgorithm$3(algorithm, name) {
473
+ return algorithm.name === name;
550
474
  }
551
- function uint32be(value) {
552
- const buf = new Uint8Array(4);
553
- writeUInt32BE(buf, value);
554
- return buf;
475
+ function getHashLength$1(hash) {
476
+ return Number.parseInt(hash.name.slice(4), 10);
477
+ }
478
+ function getNamedCurve$1(alg) {
479
+ switch (alg) {
480
+ case "ES256": return "P-256";
481
+ case "ES384": return "P-384";
482
+ case "ES512": return "P-521";
483
+ default: throw new Error("unreachable");
484
+ }
485
+ }
486
+ function checkUsage$4(key, usage) {
487
+ if (usage && !key.usages.includes(usage)) throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
555
488
  }
556
489
  //#endregion
557
- //#region src/core/jose/cek-iv.ts
558
- /**
559
- * This is a fork of Jose library's utility functions.
560
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/cek.ts
561
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/iv.ts
562
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
563
- */
490
+ //#region src/core/_crypto/_aes.ts
564
491
  const IV_BIT_LENGTHS = {
565
492
  A128GCM: 96,
566
493
  A128GCMKW: 96,
@@ -603,14 +530,112 @@ function checkCEKLength(cek, expected) {
603
530
  const actual = cek.byteLength << 3;
604
531
  if (actual !== expected) throw new Error(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`);
605
532
  }
606
- //#endregion
607
- //#region src/core/jose/encrypt-decrypt.ts
608
- /**
609
- * This is a fork of Jose library's utility functions.
610
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/encrypt.ts
611
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/decrypt.ts
612
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
613
- */
533
+ function checkAESCryptoKey(key, alg, usage) {
534
+ switch (alg) {
535
+ case "A128GCM":
536
+ case "A192GCM":
537
+ case "A256GCM": {
538
+ if (!isAlgorithm$2(key.algorithm, "AES-GCM")) throw unusable$3("AES-GCM");
539
+ const expected = Number.parseInt(alg.slice(1, 4), 10);
540
+ if (key.algorithm.length !== expected) throw unusable$3(expected, "algorithm.length");
541
+ break;
542
+ }
543
+ case "A128KW":
544
+ case "A192KW":
545
+ case "A256KW": {
546
+ if (!isAlgorithm$2(key.algorithm, "AES-KW")) throw unusable$3("AES-KW");
547
+ const expected = Number.parseInt(alg.slice(1, 4), 10);
548
+ if (key.algorithm.length !== expected) throw unusable$3(expected, "algorithm.length");
549
+ break;
550
+ }
551
+ default: throw new TypeError("CryptoKey does not support this operation");
552
+ }
553
+ checkUsage$3(key, usage);
554
+ }
555
+ async function aesKwWrap(alg, key, cek) {
556
+ const cryptoKey = await getAESKWCryptoKey(key, alg, { usage: "wrapKey" });
557
+ checkAESKWKeySize(cryptoKey, alg);
558
+ const cryptoKeyCek = await crypto.subtle.importKey("raw", cek, {
559
+ hash: "SHA-256",
560
+ name: "HMAC"
561
+ }, true, ["sign"]);
562
+ return new Uint8Array(await crypto.subtle.wrapKey("raw", cryptoKeyCek, cryptoKey, "AES-KW"));
563
+ }
564
+ async function aesKwUnwrap(alg, key, encryptedKey) {
565
+ const cryptoKey = await getAESKWCryptoKey(key, alg, { usage: "unwrapKey" });
566
+ checkAESKWKeySize(cryptoKey, alg);
567
+ const cryptoKeyCek = await crypto.subtle.unwrapKey("raw", encryptedKey, cryptoKey, "AES-KW", {
568
+ hash: "SHA-256",
569
+ name: "HMAC"
570
+ }, true, ["sign"]);
571
+ return new Uint8Array(await crypto.subtle.exportKey("raw", cryptoKeyCek));
572
+ }
573
+ async function gcmkwEncrypt(alg, key, plaintext, iv) {
574
+ const wrapped = await encrypt$1(alg.slice(0, 7), plaintext, key, iv, new Uint8Array(0));
575
+ return {
576
+ encryptedKey: wrapped.ciphertext,
577
+ iv: base64UrlEncode(wrapped.iv),
578
+ tag: base64UrlEncode(wrapped.tag)
579
+ };
580
+ }
581
+ async function gcmkwDecrypt(alg, key, ciphertext, iv, tag) {
582
+ return decrypt$1(alg.slice(0, 7), key, ciphertext, iv, tag, new Uint8Array(0));
583
+ }
584
+ async function encrypt$1(enc, plaintext, cek, iv, aad) {
585
+ if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) throw new TypeError("Key must be one of type: CryptoKey or Uint8Array");
586
+ if (iv) checkIvLength(enc, iv);
587
+ else iv = generateIV(enc);
588
+ switch (enc) {
589
+ case "A128CBC-HS256":
590
+ case "A192CBC-HS384":
591
+ case "A256CBC-HS512":
592
+ if (cek instanceof Uint8Array) checkCEKLength(cek, Number.parseInt(enc.slice(-3), 10));
593
+ return cbcEncrypt(enc, plaintext, cek, iv, aad);
594
+ case "A128GCM":
595
+ case "A192GCM":
596
+ case "A256GCM":
597
+ if (cek instanceof Uint8Array) checkCEKLength(cek, Number.parseInt(enc.slice(1, 4), 10));
598
+ return gcmEncrypt(enc, plaintext, cek, iv, aad);
599
+ default: throw new Error("Unsupported JWE Content Encryption Algorithm");
600
+ }
601
+ }
602
+ async function decrypt$1(enc, cek, ciphertext, iv, tag, aad) {
603
+ if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) throw new TypeError("Key must be one of type: CryptoKey or Uint8Array");
604
+ if (!iv) throw new Error("JWE Initialization Vector missing");
605
+ if (!tag) throw new Error("JWE Authentication Tag missing");
606
+ checkIvLength(enc, iv);
607
+ switch (enc) {
608
+ case "A128CBC-HS256":
609
+ case "A192CBC-HS384":
610
+ case "A256CBC-HS512":
611
+ if (cek instanceof Uint8Array) checkCEKLength(cek, Number.parseInt(enc.slice(-3), 10));
612
+ return cbcDecrypt(enc, cek, ciphertext, iv, tag, aad);
613
+ case "A128GCM":
614
+ case "A192GCM":
615
+ case "A256GCM":
616
+ if (cek instanceof Uint8Array) checkCEKLength(cek, Number.parseInt(enc.slice(1, 4), 10));
617
+ return gcmDecrypt(enc, cek, ciphertext, iv, tag, aad);
618
+ default: throw new Error("Unsupported JWE Content Encryption Algorithm");
619
+ }
620
+ }
621
+ function unusable$3(name, prop = "algorithm.name") {
622
+ return /* @__PURE__ */ new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
623
+ }
624
+ function isAlgorithm$2(algorithm, name) {
625
+ return algorithm.name === name;
626
+ }
627
+ function checkUsage$3(key, usage) {
628
+ if (usage && !key.usages.includes(usage)) throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
629
+ }
630
+ async function getAESKWCryptoKey(key, alg, options = {}) {
631
+ const { extractable = true, usage } = options;
632
+ if (key instanceof Uint8Array) return crypto.subtle.importKey("raw", key, "AES-KW", extractable, [usage ?? "wrapKey"]);
633
+ checkAESCryptoKey(key, alg, usage);
634
+ return key;
635
+ }
636
+ function checkAESKWKeySize(key, alg) {
637
+ if (key.algorithm.length !== Number.parseInt(alg.slice(1, 4), 10)) throw new TypeError(`Invalid key size for alg: ${alg}`);
638
+ }
614
639
  async function importCBCKeys(enc, cek, usage) {
615
640
  const keySize = Number.parseInt(enc.slice(1, 4), 10);
616
641
  const [encKey, macKey] = await Promise.all([crypto.subtle.importKey("raw", cek.subarray(keySize >> 3), "AES-CBC", false, [usage]), crypto.subtle.importKey("raw", cek.subarray(0, keySize >> 3), {
@@ -644,7 +669,7 @@ async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
644
669
  let encKey;
645
670
  if (cek instanceof Uint8Array) encKey = await crypto.subtle.importKey("raw", cek, "AES-GCM", false, ["encrypt"]);
646
671
  else {
647
- checkEncCryptoKey(cek, enc, "encrypt");
672
+ checkAESCryptoKey(cek, enc, "encrypt");
648
673
  encKey = cek;
649
674
  }
650
675
  const encrypted = new Uint8Array(await crypto.subtle.encrypt({
@@ -660,50 +685,6 @@ async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
660
685
  iv
661
686
  };
662
687
  }
663
- async function encrypt$1(enc, plaintext, cek, iv, aad) {
664
- if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) throw new TypeError("Key must be one of type: CryptoKey or Uint8Array");
665
- if (iv) checkIvLength(enc, iv);
666
- else iv = generateIV(enc);
667
- switch (enc) {
668
- case "A128CBC-HS256":
669
- case "A192CBC-HS384":
670
- case "A256CBC-HS512":
671
- if (cek instanceof Uint8Array) checkCEKLength(cek, Number.parseInt(enc.slice(-3), 10));
672
- return cbcEncrypt(enc, plaintext, cek, iv, aad);
673
- case "A128GCM":
674
- case "A192GCM":
675
- case "A256GCM":
676
- if (cek instanceof Uint8Array) checkCEKLength(cek, Number.parseInt(enc.slice(1, 4), 10));
677
- return gcmEncrypt(enc, plaintext, cek, iv, aad);
678
- default: throw new Error("Unsupported JWE Content Encryption Algorithm");
679
- }
680
- }
681
- /**
682
- * Decrypt fork
683
- */
684
- let _timingSafeKey;
685
- function getTimingSafeKey() {
686
- if (!_timingSafeKey) _timingSafeKey = crypto.subtle.generateKey({
687
- name: "HMAC",
688
- hash: "SHA-256"
689
- }, false, ["sign"]);
690
- return _timingSafeKey;
691
- }
692
- async function timingSafeEqual(a, b) {
693
- if (!(a instanceof Uint8Array)) throw new TypeError("First argument must be a buffer");
694
- if (!(b instanceof Uint8Array)) throw new TypeError("Second argument must be a buffer");
695
- const algorithm = {
696
- name: "HMAC",
697
- hash: "SHA-256"
698
- };
699
- const key = await getTimingSafeKey();
700
- const aHmac = new Uint8Array(await crypto.subtle.sign(algorithm, key, a));
701
- const bHmac = new Uint8Array(await crypto.subtle.sign(algorithm, key, b));
702
- let out = 0;
703
- let i = -1;
704
- while (++i < 32) out |= aHmac[i] ^ bHmac[i];
705
- return out === 0;
706
- }
707
688
  async function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) {
708
689
  if (!(cek instanceof Uint8Array)) throw new TypeError("CBC key must be of type Uint8Array");
709
690
  const { encKey, macKey, keySize } = await importCBCKeys(enc, cek, "decrypt");
@@ -728,7 +709,7 @@ async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) {
728
709
  let encKey;
729
710
  if (cek instanceof Uint8Array) encKey = await crypto.subtle.importKey("raw", cek, "AES-GCM", false, ["decrypt"]);
730
711
  else {
731
- checkEncCryptoKey(cek, enc, "decrypt");
712
+ checkAESCryptoKey(cek, enc, "decrypt");
732
713
  encKey = cek;
733
714
  }
734
715
  try {
@@ -742,27 +723,62 @@ async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) {
742
723
  throw new Error("JWE Decryption Failed");
743
724
  }
744
725
  }
745
- async function decrypt$1(enc, cek, ciphertext, iv, tag, aad) {
746
- if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) throw new TypeError("Key must be one of type: CryptoKey or Uint8Array");
747
- if (!iv) throw new Error("JWE Initialization Vector missing");
748
- if (!tag) throw new Error("JWE Authentication Tag missing");
749
- checkIvLength(enc, iv);
750
- switch (enc) {
751
- case "A128CBC-HS256":
752
- case "A192CBC-HS384":
753
- case "A256CBC-HS512":
754
- if (cek instanceof Uint8Array) checkCEKLength(cek, Number.parseInt(enc.slice(-3), 10));
755
- return cbcDecrypt(enc, cek, ciphertext, iv, tag, aad);
756
- case "A128GCM":
757
- case "A192GCM":
758
- case "A256GCM":
759
- if (cek instanceof Uint8Array) checkCEKLength(cek, Number.parseInt(enc.slice(1, 4), 10));
760
- return gcmDecrypt(enc, cek, ciphertext, iv, tag, aad);
761
- default: throw new Error("Unsupported JWE Content Encryption Algorithm");
762
- }
726
+ let _timingSafeKey;
727
+ function getTimingSafeKey() {
728
+ if (!_timingSafeKey) _timingSafeKey = crypto.subtle.generateKey({
729
+ name: "HMAC",
730
+ hash: "SHA-256"
731
+ }, false, ["sign"]);
732
+ return _timingSafeKey;
733
+ }
734
+ async function timingSafeEqual(a, b) {
735
+ if (!(a instanceof Uint8Array)) throw new TypeError("First argument must be a buffer");
736
+ if (!(b instanceof Uint8Array)) throw new TypeError("Second argument must be a buffer");
737
+ const algorithm = {
738
+ name: "HMAC",
739
+ hash: "SHA-256"
740
+ };
741
+ const key = await getTimingSafeKey();
742
+ const aHmac = new Uint8Array(await crypto.subtle.sign(algorithm, key, a));
743
+ const bHmac = new Uint8Array(await crypto.subtle.sign(algorithm, key, b));
744
+ let out = 0;
745
+ let i = -1;
746
+ while (++i < 32) out |= aHmac[i] ^ bHmac[i];
747
+ return out === 0;
748
+ }
749
+ function writeUInt32BE$1(buf, value, offset) {
750
+ if (value < 0 || value >= 2 ** 32) throw new RangeError(`value must be >= 0 and <= ${2 ** 32 - 1}. Received ${value}`);
751
+ buf.set([
752
+ value >>> 24,
753
+ value >>> 16,
754
+ value >>> 8,
755
+ value & 255
756
+ ], offset);
757
+ }
758
+ function uint64be(value) {
759
+ const high = Math.floor(value / 2 ** 32);
760
+ const low = value % 2 ** 32;
761
+ const buf = new Uint8Array(8);
762
+ writeUInt32BE$1(buf, high, 0);
763
+ writeUInt32BE$1(buf, low, 4);
764
+ return buf;
763
765
  }
764
766
  //#endregion
765
- //#region src/core/jose/jwk_to_key.ts
767
+ //#region src/core/_crypto/_key-codec.ts
768
+ async function jwkTokey(jwk) {
769
+ if (!jwk.alg) throw new TypeError("\"alg\" argument is required when \"jwk.alg\" is not present");
770
+ const { algorithm, keyUsages } = subtleMapping(jwk);
771
+ return crypto.subtle.importKey("jwk", jwk, algorithm, jwk.ext ?? (jwk.d ? false : true), jwk.key_ops ?? keyUsages);
772
+ }
773
+ async function keyToJWK(key) {
774
+ if (key instanceof Uint8Array) return {
775
+ kty: "oct",
776
+ k: base64UrlEncode(key)
777
+ };
778
+ if (!isCryptoKey(key)) throw new TypeError("Key must be one of type: CryptoKey or Uint8Array");
779
+ if (!key.extractable) throw new TypeError("non-extractable CryptoKey cannot be exported as a JWK");
780
+ return await crypto.subtle.exportKey("jwk", key);
781
+ }
766
782
  function subtleMapping(jwk) {
767
783
  let algorithm;
768
784
  let keyUsages;
@@ -860,72 +876,47 @@ function subtleMapping(jwk) {
860
876
  keyUsages
861
877
  };
862
878
  }
863
- async function jwkTokey(jwk) {
864
- if (!jwk.alg) throw new TypeError("\"alg\" argument is required when \"jwk.alg\" is not present");
865
- const { algorithm, keyUsages } = subtleMapping(jwk);
866
- return crypto.subtle.importKey("jwk", jwk, algorithm, jwk.ext ?? (jwk.d ? false : true), jwk.key_ops ?? keyUsages);
867
- }
868
- //#endregion
869
- //#region src/core/jose/key_to_jwk.ts
870
- async function keyToJWK(key) {
871
- if (key instanceof Uint8Array) return {
872
- kty: "oct",
873
- k: base64UrlEncode(key)
874
- };
875
- if (!isCryptoKey(key)) throw new TypeError("Key must be one of type: CryptoKey or Uint8Array");
876
- if (!key.extractable) throw new TypeError("non-extractable CryptoKey cannot be exported as a JWK");
877
- return await crypto.subtle.exportKey("jwk", key);
878
- }
879
879
  //#endregion
880
- //#region src/core/jose/rsaes.ts
880
+ //#region src/core/_crypto/_rsa.ts
881
881
  async function encryptRSAES(alg, key, cek) {
882
- checkEncCryptoKey(key, alg, "encrypt");
883
- checkKeyLength(alg, key);
882
+ checkRSACryptoKey(key, alg, "encrypt");
883
+ checkRSAModulusLength(alg, key);
884
884
  return new Uint8Array(await crypto.subtle.encrypt("RSA-OAEP", key, cek));
885
885
  }
886
886
  async function decryptRSAES(alg, key, encryptedKey) {
887
- checkEncCryptoKey(key, alg, "decrypt");
888
- checkKeyLength(alg, key);
887
+ checkRSACryptoKey(key, alg, "decrypt");
888
+ checkRSAModulusLength(alg, key);
889
889
  return new Uint8Array(await crypto.subtle.decrypt("RSA-OAEP", key, encryptedKey));
890
890
  }
891
- //#endregion
892
- //#region src/core/jose/aesgcmkw.ts
893
- /**
894
- * This is a fork of Jose library's utility functions.
895
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/aesgcmkw.ts
896
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
897
- */
898
- async function encryptIV(alg, key, plaintext, iv) {
899
- const wrapped = await encrypt$1(alg.slice(0, 7), plaintext, key, iv, new Uint8Array(0));
900
- return {
901
- encryptedKey: wrapped.ciphertext,
902
- iv: base64UrlEncode(wrapped.iv),
903
- tag: base64UrlEncode(wrapped.tag)
904
- };
891
+ function checkRSACryptoKey(key, alg, usage) {
892
+ if (!isAlgorithm$1(key.algorithm, "RSA-OAEP")) throw unusable$2("RSA-OAEP");
893
+ const expected = Number.parseInt(alg.slice(9), 10) || 1;
894
+ if (getHashLength(key.algorithm.hash) !== expected) throw unusable$2(`SHA-${expected}`, "algorithm.hash");
895
+ checkUsage$2(key, usage);
905
896
  }
906
- async function decryptIV(alg, key, ciphertext, iv, tag) {
907
- return decrypt$1(alg.slice(0, 7), key, ciphertext, iv, tag, new Uint8Array(0));
897
+ function checkRSAModulusLength(alg, key) {
898
+ if (alg.startsWith("RS") || alg.startsWith("PS") || alg.startsWith("RSA")) {
899
+ const { modulusLength } = key.algorithm;
900
+ if (typeof modulusLength !== "number" || modulusLength < 2048) throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
901
+ }
908
902
  }
909
- //#endregion
910
- //#region src/core/jose/ecdhes.ts
911
- function lengthAndInput(input) {
912
- return concatUint8Arrays(uint32be(input.length), input);
903
+ function unusable$2(name, prop = "algorithm.name") {
904
+ return /* @__PURE__ */ new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
913
905
  }
914
- async function concatKdf(secret, bits, value) {
915
- const iterations = Math.ceil((bits >> 3) / 32);
916
- const res = new Uint8Array(iterations * 32);
917
- for (let iter = 0; iter < iterations; iter++) {
918
- const buf = new Uint8Array(4 + secret.length + value.length);
919
- buf.set(uint32be(iter + 1));
920
- buf.set(secret, 4);
921
- buf.set(value, 4 + secret.length);
922
- res.set(await digest("sha256", buf), iter * 32);
923
- }
924
- return res.slice(0, bits >> 3);
906
+ function isAlgorithm$1(algorithm, name) {
907
+ return algorithm.name === name;
908
+ }
909
+ function getHashLength(hash) {
910
+ return Number.parseInt(hash.name.slice(4), 10);
911
+ }
912
+ function checkUsage$2(key, usage) {
913
+ if (usage && !key.usages.includes(usage)) throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
925
914
  }
915
+ //#endregion
916
+ //#region src/core/_crypto/_ecdh.ts
926
917
  async function deriveECDHESKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) {
927
- checkEncCryptoKey(publicKey, "ECDH");
928
- checkEncCryptoKey(privateKey, "ECDH", "deriveBits");
918
+ checkECDHCryptoKey(publicKey, "ECDH");
919
+ checkECDHCryptoKey(privateKey, "ECDH", "deriveBits");
929
920
  const value = concatUint8Arrays(lengthAndInput(textEncoder.encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength));
930
921
  const length = publicKey.algorithm.name === "X25519" ? 256 : Math.ceil(Number.parseInt(publicKey.algorithm.namedCurve.slice(-3), 10) / 8) << 3;
931
922
  return concatKdf(new Uint8Array(await crypto.subtle.deriveBits({
@@ -933,7 +924,7 @@ async function deriveECDHESKey(publicKey, privateKey, algorithm, keyLength, apu
933
924
  public: publicKey
934
925
  }, privateKey, length)), keyLength, value);
935
926
  }
936
- function allowed(key) {
927
+ function isECDHKeyAllowed(key) {
937
928
  switch (key.algorithm.namedCurve) {
938
929
  case "P-256":
939
930
  case "P-384":
@@ -941,38 +932,65 @@ function allowed(key) {
941
932
  default: return key.algorithm.name === "X25519";
942
933
  }
943
934
  }
935
+ function checkECDHCryptoKey(key, alg, usage) {
936
+ switch (key.algorithm.name) {
937
+ case "ECDH":
938
+ case "X25519": break;
939
+ default: throw unusable$1("ECDH or X25519");
940
+ }
941
+ checkUsage$1(key, usage);
942
+ }
943
+ function unusable$1(name, prop = "algorithm.name") {
944
+ return /* @__PURE__ */ new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
945
+ }
946
+ function checkUsage$1(key, usage) {
947
+ if (usage && !key.usages.includes(usage)) throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
948
+ }
949
+ async function concatKdf(secret, bits, value) {
950
+ const iterations = Math.ceil((bits >> 3) / 32);
951
+ const res = new Uint8Array(iterations * 32);
952
+ for (let iter = 0; iter < iterations; iter++) {
953
+ const buf = new Uint8Array(4 + secret.length + value.length);
954
+ buf.set(uint32be(iter + 1));
955
+ buf.set(secret, 4);
956
+ buf.set(value, 4 + secret.length);
957
+ res.set(await digest("sha256", buf), iter * 32);
958
+ }
959
+ return res.slice(0, bits >> 3);
960
+ }
961
+ function lengthAndInput(input) {
962
+ return concatUint8Arrays(uint32be(input.length), input);
963
+ }
944
964
  async function digest(algorithm, data) {
945
- const subtleDigest = `SHA-${algorithm.slice(-3)}`;
946
- return new Uint8Array(await crypto.subtle.digest(subtleDigest, data));
965
+ return new Uint8Array(await crypto.subtle.digest(`SHA-${algorithm.slice(-3)}`, data));
966
+ }
967
+ function writeUInt32BE(buf, value, offset) {
968
+ if (value < 0 || value >= 2 ** 32) throw new RangeError(`value must be >= 0 and <= ${2 ** 32 - 1}. Received ${value}`);
969
+ buf.set([
970
+ value >>> 24,
971
+ value >>> 16,
972
+ value >>> 8,
973
+ value & 255
974
+ ], offset);
975
+ }
976
+ function uint32be(value) {
977
+ const buf = new Uint8Array(4);
978
+ writeUInt32BE(buf, value, 0);
979
+ return buf;
947
980
  }
948
981
  //#endregion
949
- //#region src/core/jose/pbes2kw.ts
950
- /**
951
- * This is a fork of Jose library's utility functions.
952
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/pbes2kw.ts
953
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
954
- */
955
- async function wrap(alg, key, cek, p2c, p2s) {
956
- if (p2c && p2s) {
957
- const derived = await deriveKey(p2s, alg, p2c, key);
958
- return {
959
- encryptedKey: await _wrap(alg.slice(-6), derived, cek),
960
- p2c,
961
- p2s: base64UrlEncode(p2s)
962
- };
963
- } else return { encryptedKey: await _wrap(alg, key, cek) };
964
- }
965
- async function unwrap(alg, key, encryptedKey, p2c, p2s) {
966
- if (p2c && p2s) {
967
- const derived = await deriveKey(p2s, alg, p2c, key);
968
- return _unwrap(alg.slice(-6), derived, encryptedKey);
969
- } else return _unwrap(alg, key, encryptedKey);
970
- }
971
- function getCryptoKey(key, alg, options = {}) {
972
- const { importAlg = "PBKDF2", extractable = false, usage = "deriveBits" } = options;
973
- if (key instanceof Uint8Array) return crypto.subtle.importKey("raw", key, importAlg, extractable, [usage]);
974
- checkEncCryptoKey(key, alg, usage);
975
- return key;
982
+ //#region src/core/_crypto/_pbes2.ts
983
+ async function pbes2Wrap(alg, key, cek, p2c, p2s) {
984
+ const derived = await deriveKey(p2s, alg, p2c, key);
985
+ return {
986
+ encryptedKey: await aesKwWrap(alg.slice(-6), derived, cek),
987
+ p2c,
988
+ p2s: base64UrlEncode(p2s)
989
+ };
990
+ }
991
+ async function pbes2Unwrap(alg, key, encryptedKey, p2c, p2s) {
992
+ const derived = await deriveKey(p2s, alg, p2c, key);
993
+ return aesKwUnwrap(alg.slice(-6), derived, encryptedKey);
976
994
  }
977
995
  async function deriveKey(p2s, alg, p2c, key) {
978
996
  if (!(p2s instanceof Uint8Array) || p2s.length < 8) throw new Error("PBES2 Salt Input must be 8 or more octets");
@@ -985,92 +1003,44 @@ async function deriveKey(p2s, alg, p2c, key) {
985
1003
  name: "PBKDF2",
986
1004
  salt
987
1005
  };
988
- const cryptoKey = await getCryptoKey(key, alg);
1006
+ const cryptoKey = await getPBKDF2CryptoKey(key, alg);
989
1007
  return new Uint8Array(await crypto.subtle.deriveBits(subtleAlg, cryptoKey, keylen));
990
1008
  }
991
- /**
992
- * Fork from: https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/aeskw.ts
993
- */
994
- function checkKeySize(key, alg) {
995
- if (key.algorithm.length !== Number.parseInt(alg.slice(1, 4), 10)) throw new TypeError(`Invalid key size for alg: ${alg}`);
996
- }
997
- async function _wrap(alg, key, cek) {
998
- const cryptoKey = await getCryptoKey(key, alg, {
999
- importAlg: "AES-KW",
1000
- extractable: true,
1001
- usage: "wrapKey"
1002
- });
1003
- checkKeySize(cryptoKey, alg);
1004
- const cryptoKeyCek = await crypto.subtle.importKey("raw", cek, {
1005
- hash: "SHA-256",
1006
- name: "HMAC"
1007
- }, true, ["sign"]);
1008
- return new Uint8Array(await crypto.subtle.wrapKey("raw", cryptoKeyCek, cryptoKey, "AES-KW"));
1009
- }
1010
- async function _unwrap(alg, key, encryptedKey) {
1011
- const cryptoKey = await getCryptoKey(key, alg, {
1012
- importAlg: "AES-KW",
1013
- extractable: true,
1014
- usage: "unwrapKey"
1015
- });
1016
- checkKeySize(cryptoKey, alg);
1017
- const cryptoKeyCek = await crypto.subtle.unwrapKey("raw", encryptedKey, cryptoKey, "AES-KW", {
1018
- hash: "SHA-256",
1019
- name: "HMAC"
1020
- }, true, ["sign"]);
1021
- return new Uint8Array(await crypto.subtle.exportKey("raw", cryptoKeyCek));
1009
+ function checkPBES2CryptoKey(key, alg, usage) {
1010
+ if (!isAlgorithm(key.algorithm, "PBKDF2")) throw unusable("PBKDF2");
1011
+ checkUsage(key, usage);
1022
1012
  }
1023
- //#endregion
1024
- //#region src/core/jose/encrypt_key.ts
1025
- const PUBLIC_EPK_FIELDS = new Set([
1026
- "kty",
1027
- "crv",
1028
- "x",
1029
- "y",
1030
- "kid",
1031
- "use",
1032
- "key_ops",
1033
- "alg",
1034
- "x5c",
1035
- "x5t",
1036
- "x5t#S256",
1037
- "x5u"
1038
- ]);
1039
- function toPublicEpkJwk(jwk) {
1040
- const filtered = {};
1041
- for (const [key, value] of Object.entries(jwk)) if (value !== void 0 && PUBLIC_EPK_FIELDS.has(key)) filtered[key] = value;
1042
- const { kty, crv, x, y } = filtered;
1043
- if (typeof kty !== "string") throw new TypeError("ECDH-ES ephemeral key JWK must include the \"kty\" parameter.");
1044
- if (typeof crv !== "string") throw new TypeError("ECDH-ES ephemeral key JWK must include the \"crv\" parameter.");
1045
- if (typeof x !== "string") throw new TypeError("ECDH-ES ephemeral key JWK must include the \"x\" coordinate.");
1046
- if (kty === "EC" && typeof y !== "string") throw new TypeError("ECDH-ES EC ephemeral key JWK must include both \"x\" and \"y\" coordinates.");
1047
- const publicJwk = {
1048
- ...filtered,
1049
- kty,
1050
- crv,
1051
- x
1052
- };
1053
- if (typeof y === "string") publicJwk.y = y;
1054
- else delete publicJwk.y;
1055
- return sanitizeObject(publicJwk);
1013
+ function unusable(name, prop = "algorithm.name") {
1014
+ return /* @__PURE__ */ new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
1056
1015
  }
1057
- async function ensurePrivateKey(alg, value) {
1058
- const normalized = await normalizeKey(value, alg);
1059
- if (!(normalized instanceof CryptoKey)) throw new TypeError("ECDH-ES ephemeral private key must be a CryptoKey or convertible JWK.");
1060
- if (normalized.type !== "private") throw new TypeError("ECDH-ES ephemeral private key must have type \"private\".");
1061
- return normalized;
1016
+ function isAlgorithm(algorithm, name) {
1017
+ return algorithm.name === name;
1062
1018
  }
1063
- async function exportPublicJwkFrom(alg, value) {
1064
- if (isCryptoKey(value)) try {
1065
- return toPublicEpkJwk(await keyToJWK(value));
1066
- } catch (error_) {
1067
- throw new TypeError("ECDH-ES ephemeral CryptoKey must be extractable to export as JWK.", { cause: error_ instanceof Error ? error_ : void 0 });
1068
- }
1069
- if (isJWK(value) && isAsymmetricJWK(value) && value.kty === "EC") return toPublicEpkJwk(value);
1070
- const normalized = await normalizeKey(value, alg);
1071
- if (!(normalized instanceof CryptoKey)) throw new TypeError("ECDH-ES ephemeral public key must be a CryptoKey or convertible JWK.");
1072
- return exportPublicJwkFrom(alg, normalized);
1019
+ function checkUsage(key, usage) {
1020
+ if (usage && !key.usages.includes(usage)) throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
1021
+ }
1022
+ async function getPBKDF2CryptoKey(key, alg, options = {}) {
1023
+ const { extractable = false, usage = "deriveBits" } = options;
1024
+ if (key instanceof Uint8Array) return crypto.subtle.importKey("raw", key, "PBKDF2", extractable, [usage]);
1025
+ checkPBES2CryptoKey(key, alg, usage);
1026
+ return key;
1073
1027
  }
1028
+ //#endregion
1029
+ //#region src/core/_crypto/_key-encryption.ts
1030
+ const PUBLIC_EPK_FIELDS = new Set([
1031
+ "kty",
1032
+ "crv",
1033
+ "x",
1034
+ "y",
1035
+ "kid",
1036
+ "use",
1037
+ "key_ops",
1038
+ "alg",
1039
+ "x5c",
1040
+ "x5t",
1041
+ "x5t#S256",
1042
+ "x5u"
1043
+ ]);
1074
1044
  async function encryptKey(alg, enc, key, providedCek, providedParameters = {}) {
1075
1045
  let encryptedKey;
1076
1046
  let parameters = {};
@@ -1084,7 +1054,7 @@ async function encryptKey(alg, enc, key, providedCek, providedParameters = {}) {
1084
1054
  case "ECDH-ES+A192KW":
1085
1055
  case "ECDH-ES+A256KW": {
1086
1056
  assertCryptoKey(key);
1087
- if (!allowed(key)) throw new Error("ECDH with the provided key is not allowed or not supported by your javascript runtime");
1057
+ if (!isECDHKeyAllowed(key)) throw new Error("ECDH with the provided key is not allowed or not supported by your javascript runtime");
1088
1058
  const { apu, apv, epk: providedEpk, epkPrivateKey } = providedParameters;
1089
1059
  if (providedEpk && !epkPrivateKey) throw new TypeError("ECDH-ES custom ephemeral key material must include the matching private key.");
1090
1060
  let ephemeralPrivateKey;
@@ -1107,7 +1077,7 @@ async function encryptKey(alg, enc, key, providedCek, providedParameters = {}) {
1107
1077
  break;
1108
1078
  }
1109
1079
  cek = providedCek || generateCEK(enc);
1110
- encryptedKey = (await wrap(alg.slice(-6), sharedSecret, cek)).encryptedKey;
1080
+ encryptedKey = await aesKwWrap(alg.slice(-6), sharedSecret, cek);
1111
1081
  break;
1112
1082
  }
1113
1083
  case "RSA-OAEP":
@@ -1123,21 +1093,21 @@ async function encryptKey(alg, enc, key, providedCek, providedParameters = {}) {
1123
1093
  case "PBES2-HS512+A256KW": {
1124
1094
  cek = providedCek || generateCEK(enc);
1125
1095
  const { p2c, p2s } = providedParameters;
1126
- ({encryptedKey, ...parameters} = await wrap(alg, key, cek, p2c, p2s));
1096
+ ({encryptedKey, ...parameters} = await pbes2Wrap(alg, key, cek, p2c, p2s));
1127
1097
  break;
1128
1098
  }
1129
1099
  case "A128KW":
1130
1100
  case "A192KW":
1131
1101
  case "A256KW":
1132
1102
  cek = providedCek || generateCEK(enc);
1133
- encryptedKey = (await wrap(alg, key, cek)).encryptedKey;
1103
+ encryptedKey = await aesKwWrap(alg, key, cek);
1134
1104
  break;
1135
1105
  case "A128GCMKW":
1136
1106
  case "A192GCMKW":
1137
1107
  case "A256GCMKW": {
1138
1108
  cek = providedCek || generateCEK(enc);
1139
1109
  const { iv } = providedParameters;
1140
- ({encryptedKey, ...parameters} = await encryptIV(alg, key, cek, iv));
1110
+ ({encryptedKey, ...parameters} = await gcmkwEncrypt(alg, key, cek, iv));
1141
1111
  break;
1142
1112
  }
1143
1113
  default: throw new Error("Invalid or unsupported \"alg\" (JWE Algorithm) header value");
@@ -1160,67 +1130,72 @@ async function normalizeKey(key, alg) {
1160
1130
  }
1161
1131
  throw new Error("unreachable");
1162
1132
  }
1133
+ function toPublicEpkJwk(jwk) {
1134
+ const filtered = {};
1135
+ for (const [key, value] of Object.entries(jwk)) if (value !== void 0 && PUBLIC_EPK_FIELDS.has(key)) filtered[key] = value;
1136
+ const { kty, crv, x, y } = filtered;
1137
+ if (typeof kty !== "string") throw new TypeError("ECDH-ES ephemeral key JWK must include the \"kty\" parameter.");
1138
+ if (typeof crv !== "string") throw new TypeError("ECDH-ES ephemeral key JWK must include the \"crv\" parameter.");
1139
+ if (typeof x !== "string") throw new TypeError("ECDH-ES ephemeral key JWK must include the \"x\" coordinate.");
1140
+ if (kty === "EC" && typeof y !== "string") throw new TypeError("ECDH-ES EC ephemeral key JWK must include both \"x\" and \"y\" coordinates.");
1141
+ const publicJwk = {
1142
+ ...filtered,
1143
+ kty,
1144
+ crv,
1145
+ x
1146
+ };
1147
+ if (typeof y === "string") publicJwk.y = y;
1148
+ else delete publicJwk.y;
1149
+ return sanitizeObject(publicJwk);
1150
+ }
1151
+ async function ensurePrivateKey(alg, value) {
1152
+ const normalized = await normalizeKey(value, alg);
1153
+ if (!(normalized instanceof CryptoKey)) throw new TypeError("ECDH-ES ephemeral private key must be a CryptoKey or convertible JWK.");
1154
+ if (normalized.type !== "private") throw new TypeError("ECDH-ES ephemeral private key must have type \"private\".");
1155
+ return normalized;
1156
+ }
1157
+ async function exportPublicJwkFrom(alg, value) {
1158
+ if (isCryptoKey(value)) try {
1159
+ return toPublicEpkJwk(await keyToJWK(value));
1160
+ } catch (error_) {
1161
+ throw new TypeError("ECDH-ES ephemeral CryptoKey must be extractable to export as JWK.", { cause: error_ instanceof Error ? error_ : void 0 });
1162
+ }
1163
+ if (isJWK(value) && isAsymmetricJWK(value) && value.kty === "EC") return toPublicEpkJwk(value);
1164
+ const normalized = await normalizeKey(value, alg);
1165
+ if (!(normalized instanceof CryptoKey)) throw new TypeError("ECDH-ES ephemeral public key must be a CryptoKey or convertible JWK.");
1166
+ return exportPublicJwkFrom(alg, normalized);
1167
+ }
1163
1168
  //#endregion
1164
- //#region src/core/jose/asn1.ts
1165
- /**
1166
- * This is a fork of Jose library's utility functions.
1167
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/asn1.ts
1168
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
1169
- */
1170
- const formatPEM = (b64, descriptor) => {
1171
- return `-----BEGIN ${descriptor}-----\n${(b64.match(/.{1,64}/g) || []).join("\n")}\n-----END ${descriptor}-----`;
1172
- };
1173
- const genericExport = async (keyType, keyFormat, key) => {
1174
- if (!isCryptoKey(key)) throw new TypeError(`Key must be ${key} of type CryptoKey`);
1175
- if (!key.extractable) throw new TypeError("CryptoKey is not extractable");
1176
- if (key.type !== keyType) throw new TypeError(`key is not a ${keyType} key`);
1177
- return formatPEM(base64Encode(new Uint8Array(await crypto.subtle.exportKey(keyFormat, key))), `${keyType.toUpperCase()} KEY`);
1178
- };
1169
+ //#region src/core/_crypto/_pem.ts
1179
1170
  const toSPKI = (key) => {
1180
1171
  return genericExport("public", "spki", key);
1181
1172
  };
1182
1173
  const toPKCS8 = (key) => {
1183
1174
  return genericExport("private", "pkcs8", key);
1184
1175
  };
1185
- const findOid = (keyData, oid, from = 0) => {
1186
- if (from === 0) {
1187
- oid.unshift(oid.length);
1188
- oid.unshift(6);
1189
- }
1190
- const i = keyData.indexOf(oid[0], from);
1191
- if (i === -1) return false;
1192
- const sub = keyData.subarray(i, i + oid.length);
1193
- if (sub.length !== oid.length) return false;
1194
- return sub.every((value, index) => value === oid[index]) || findOid(keyData, oid, i + 1);
1176
+ const fromPKCS8 = (pem, alg, options) => {
1177
+ return genericImport(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, "pkcs8", pem, alg, options);
1195
1178
  };
1196
- const getNamedCurve = (keyData) => {
1197
- switch (true) {
1198
- case findOid(keyData, [
1199
- 42,
1200
- 134,
1201
- 72,
1202
- 206,
1203
- 61,
1204
- 3,
1205
- 1,
1206
- 7
1207
- ]): return "P-256";
1208
- case findOid(keyData, [
1209
- 43,
1210
- 129,
1211
- 4,
1212
- 0,
1213
- 34
1214
- ]): return "P-384";
1215
- case findOid(keyData, [
1216
- 43,
1217
- 129,
1218
- 4,
1219
- 0,
1220
- 35
1221
- ]): return "P-521";
1222
- default: return;
1179
+ const fromSPKI = (pem, alg, options) => {
1180
+ return genericImport(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, "spki", pem, alg, options);
1181
+ };
1182
+ const fromX509 = (pem, alg, options) => {
1183
+ let spki;
1184
+ try {
1185
+ spki = getSPKI(pem);
1186
+ } catch (error_) {
1187
+ throw new TypeError("Failed to parse the X.509 certificate", { cause: error_ });
1223
1188
  }
1189
+ return fromSPKI(spki, alg, options);
1190
+ };
1191
+ const formatPEM = (b64, descriptor) => {
1192
+ return `-----BEGIN ${descriptor}-----\n${(b64.match(/.{1,64}/g) || []).join("\n")}\n-----END ${descriptor}-----`;
1193
+ };
1194
+ const genericExport = async (keyType, keyFormat, key) => {
1195
+ if (!isCryptoKey(key)) throw new TypeError(`Key must be ${key} of type CryptoKey`);
1196
+ if (!key.extractable) throw new TypeError("CryptoKey is not extractable");
1197
+ if (key.type !== keyType) throw new TypeError(`key is not a ${keyType} key`);
1198
+ return formatPEM(base64Encode(new Uint8Array(await crypto.subtle.exportKey(keyFormat, key))), `${keyType.toUpperCase()} KEY`);
1224
1199
  };
1225
1200
  const genericImport = async (replace, keyFormat, pem, alg, options) => {
1226
1201
  let algorithm;
@@ -1298,13 +1273,47 @@ const genericImport = async (replace, keyFormat, pem, alg, options) => {
1298
1273
  }
1299
1274
  return crypto.subtle.importKey(keyFormat, keyData, algorithm, options?.extractable ?? (isPublic ? true : false), keyUsages);
1300
1275
  };
1301
- const fromPKCS8 = (pem, alg, options) => {
1302
- return genericImport(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, "pkcs8", pem, alg, options);
1276
+ const findOid = (keyData, oid, from = 0) => {
1277
+ if (from === 0) {
1278
+ oid.unshift(oid.length);
1279
+ oid.unshift(6);
1280
+ }
1281
+ const i = keyData.indexOf(oid[0], from);
1282
+ if (i === -1) return false;
1283
+ const sub = keyData.subarray(i, i + oid.length);
1284
+ if (sub.length !== oid.length) return false;
1285
+ return sub.every((value, index) => value === oid[index]) || findOid(keyData, oid, i + 1);
1303
1286
  };
1304
- const fromSPKI = (pem, alg, options) => {
1305
- return genericImport(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, "spki", pem, alg, options);
1287
+ const getNamedCurve = (keyData) => {
1288
+ switch (true) {
1289
+ case findOid(keyData, [
1290
+ 42,
1291
+ 134,
1292
+ 72,
1293
+ 206,
1294
+ 61,
1295
+ 3,
1296
+ 1,
1297
+ 7
1298
+ ]): return "P-256";
1299
+ case findOid(keyData, [
1300
+ 43,
1301
+ 129,
1302
+ 4,
1303
+ 0,
1304
+ 34
1305
+ ]): return "P-384";
1306
+ case findOid(keyData, [
1307
+ 43,
1308
+ 129,
1309
+ 4,
1310
+ 0,
1311
+ 35
1312
+ ]): return "P-521";
1313
+ default: return;
1314
+ }
1306
1315
  };
1307
- function getElement(seq) {
1316
+ const getElement = (seq) => {
1308
1317
  const result = [];
1309
1318
  let next = 0;
1310
1319
  while (next < seq.length) {
@@ -1313,8 +1322,8 @@ function getElement(seq) {
1313
1322
  next += nextPart.byteLength;
1314
1323
  }
1315
1324
  return result;
1316
- }
1317
- function parseElement(bytes) {
1325
+ };
1326
+ const parseElement = (bytes) => {
1318
1327
  let position = 0;
1319
1328
  let tag = bytes[0] & 31;
1320
1329
  position++;
@@ -1358,13 +1367,13 @@ function parseElement(bytes) {
1358
1367
  contents: bytes.subarray(position, byteLength),
1359
1368
  raw: bytes.subarray(0, byteLength)
1360
1369
  };
1361
- }
1362
- function spkiFromX509(buf) {
1370
+ };
1371
+ const spkiFromX509 = (buf) => {
1363
1372
  const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
1364
1373
  return base64Encode(tbsCertificate[tbsCertificate[0].raw[0] === 160 ? 6 : 5].raw);
1365
- }
1374
+ };
1366
1375
  let createPublicKey;
1367
- function getSPKI(x509) {
1376
+ const getSPKI = (x509) => {
1368
1377
  try {
1369
1378
  createPublicKey ??= globalThis.process?.getBuiltinModule?.("node:crypto")?.createPublicKey;
1370
1379
  } catch {
@@ -1377,15 +1386,6 @@ function getSPKI(x509) {
1377
1386
  });
1378
1387
  } catch {}
1379
1388
  return formatPEM(spkiFromX509(base64Decode(x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, ""), false)), "PUBLIC KEY");
1380
- }
1381
- const fromX509 = (pem, alg, options) => {
1382
- let spki;
1383
- try {
1384
- spki = getSPKI(pem);
1385
- } catch (error_) {
1386
- throw new TypeError("Failed to parse the X.509 certificate", { cause: error_ });
1387
- }
1388
- return fromSPKI(spki, alg, options);
1389
1389
  };
1390
1390
  //#endregion
1391
1391
  //#region src/core/jwk.ts
@@ -1399,25 +1399,13 @@ async function generateKey(alg, options = {}) {
1399
1399
  }
1400
1400
  const { algorithm, keyUsages } = getGenerateKeyParams(alg, options);
1401
1401
  const key = await crypto.subtle.generateKey(algorithm, defaultExtractable, keyUsages);
1402
- if (exportToJWK) {
1403
- const { alg: _a, kty: _k, key_ops: _ko, ext: _e, ...additionalKeyParams } = typeof options.toJWK === "object" ? options.toJWK : {};
1404
- if (key instanceof CryptoKey) return exportKey(key, {
1405
- ...additionalKeyParams,
1406
- alg
1407
- });
1408
- else {
1409
- const [publicKey, privateKey] = await Promise.all([exportKey(key.publicKey, {
1410
- ...additionalKeyParams,
1411
- alg
1412
- }), exportKey(key.privateKey, {
1413
- ...additionalKeyParams,
1414
- alg
1415
- })]);
1416
- return {
1417
- privateKey,
1418
- publicKey
1419
- };
1420
- }
1402
+ if (exportToJWK) if (key instanceof CryptoKey) return exportKey(key, { alg });
1403
+ else {
1404
+ const [publicKey, privateKey] = await Promise.all([exportKey(key.publicKey, { alg }), exportKey(key.privateKey, { alg })]);
1405
+ return {
1406
+ privateKey,
1407
+ publicKey
1408
+ };
1421
1409
  }
1422
1410
  return key;
1423
1411
  }
@@ -1430,14 +1418,31 @@ async function generateKey(alg, options = {}) {
1430
1418
  * @returns A Promise resolving to the generated JWK or JWK pair representation.
1431
1419
  */
1432
1420
  async function generateJWK(alg, jwkParams, options = {}) {
1433
- const { toJWK: _, ...opts } = options;
1434
- return generateKey(alg, {
1435
- ...opts,
1436
- toJWK: {
1437
- kid: typeof jwkParams?.kid === "string" ? jwkParams.kid : crypto.randomUUID(),
1438
- ...jwkParams
1439
- }
1421
+ const extraParams = {
1422
+ kid: typeof jwkParams?.kid === "string" ? jwkParams.kid : crypto.randomUUID(),
1423
+ ...jwkParams
1424
+ };
1425
+ const result = await generateKey(alg, {
1426
+ ...options,
1427
+ toJWK: true
1440
1428
  });
1429
+ if (result && typeof result === "object" && "privateKey" in result && "publicKey" in result) {
1430
+ const pair = result;
1431
+ return {
1432
+ privateKey: {
1433
+ ...extraParams,
1434
+ ...pair.privateKey
1435
+ },
1436
+ publicKey: {
1437
+ ...extraParams,
1438
+ ...pair.publicKey
1439
+ }
1440
+ };
1441
+ }
1442
+ return {
1443
+ ...extraParams,
1444
+ ...result
1445
+ };
1441
1446
  }
1442
1447
  async function deriveKeyFromPassword(password, alg, options) {
1443
1448
  const { salt, iterations, extractable, keyUsage } = options;
@@ -1449,16 +1454,11 @@ async function deriveKeyFromPassword(password, alg, options) {
1449
1454
  const finalUsages = keyUsage ?? ["wrapKey", "unwrapKey"];
1450
1455
  const finalExtractable = exportToJWK ? true : extractable === true;
1451
1456
  const derivedKey = await crypto.subtle.importKey("raw", derivedBytes, { name: "AES-KW" }, finalExtractable, finalUsages);
1452
- if (exportToJWK) {
1453
- const jwk = await keyToJWK(derivedKey);
1454
- const { alg: _a, kty: _k, key_ops: _ko, ext: _e, ...additionalKeyParams } = typeof options.toJWK === "object" ? options.toJWK : {};
1455
- return {
1456
- ...additionalKeyParams,
1457
- ...jwk,
1458
- alg: wrappingAlg,
1459
- kty: "oct"
1460
- };
1461
- }
1457
+ if (exportToJWK) return {
1458
+ ...await keyToJWK(derivedKey),
1459
+ alg: wrappingAlg,
1460
+ kty: "oct"
1461
+ };
1462
1462
  return derivedKey;
1463
1463
  }
1464
1464
  /**
@@ -1471,36 +1471,67 @@ async function deriveKeyFromPassword(password, alg, options) {
1471
1471
  * @returns A Promise resolving to the derived JWK (JWK_oct).
1472
1472
  */
1473
1473
  async function deriveJWKFromPassword(password, alg, options, jwkParams) {
1474
- const { toJWK: _, ...opts } = options;
1475
- return deriveKeyFromPassword(password, alg, {
1476
- ...opts,
1477
- toJWK: jwkParams || true
1474
+ const result = await deriveKeyFromPassword(password, alg, {
1475
+ ...options,
1476
+ toJWK: true
1478
1477
  });
1478
+ return jwkParams ? {
1479
+ ...jwkParams,
1480
+ ...result
1481
+ } : result;
1479
1482
  }
1480
1483
  /**
1481
- * Cache for imported JWKs to avoid redundant CryptoKey creation.
1484
+ * Default JWK import cache backed by a WeakMap.
1485
+ *
1486
+ * The outer WeakMap is keyed by the JWK object reference — a cache hit only
1487
+ * occurs when the exact same object variable is passed to {@link importKey}.
1488
+ * Reconstructing a structurally identical JWK (e.g. `{ ...jwk }`) will miss
1489
+ * the cache.
1490
+ *
1491
+ * The inner `Record<string, CryptoKey>` uses a plain object rather than `Map`
1492
+ * because typical entries have 1–2 algorithm strings per key. V8 applies
1493
+ * hidden-class optimisation to small plain objects, making property access
1494
+ * faster than `Map.get()` at this cardinality.
1482
1495
  */
1483
- let _jwkCache;
1484
- async function importKey(key, alg) {
1496
+ var WeakMapJWKCache = class {
1497
+ _map = /* @__PURE__ */ new WeakMap();
1498
+ get(jwk, alg) {
1499
+ return this._map.get(jwk)?.[alg];
1500
+ }
1501
+ set(jwk, alg, key) {
1502
+ let entry = this._map.get(jwk);
1503
+ if (!entry) this._map.set(jwk, { [alg]: key });
1504
+ else entry[alg] = key;
1505
+ }
1506
+ };
1507
+ let _activeCache = new WeakMapJWKCache();
1508
+ async function importKey(key, algOrOptions) {
1509
+ const alg = typeof algOrOptions === "string" ? algOrOptions : void 0;
1485
1510
  if (typeof key === "string") key = textEncoder.encode(key);
1486
1511
  if (key instanceof Uint8Array) return key;
1487
1512
  if (isCryptoKey(key)) return key;
1488
- if (isJWK(key)) if ("k" in key && typeof key.k === "string") return base64UrlDecode(key.k, false);
1489
- else {
1513
+ if (isJWK(key)) if ("k" in key && typeof key.k === "string") {
1514
+ const rawBytes = base64UrlDecode(key.k, false);
1515
+ if (typeof algOrOptions === "object" && algOrOptions.asCryptoKey) {
1516
+ const { algorithm, usage, extractable = false } = algOrOptions;
1517
+ return crypto.subtle.importKey("raw", rawBytes, algorithm, extractable, usage);
1518
+ }
1519
+ return rawBytes;
1520
+ } else {
1490
1521
  if (!key.alg && !alg) throw new TypeError("Algorithm must be provided when importing non-oct JWK");
1491
1522
  const effectiveAlg = key.alg || alg;
1492
- _jwkCache ||= /* @__PURE__ */ new WeakMap();
1493
- const cached = _jwkCache.get(key);
1494
- if (cached?.[effectiveAlg]) return cached[effectiveAlg];
1523
+ if (_activeCache) {
1524
+ const cached = _activeCache.get(key, effectiveAlg);
1525
+ if (cached) return cached;
1526
+ }
1495
1527
  const cryptoKey = await jwkTokey(key.alg ? key : {
1496
1528
  ...key,
1497
1529
  alg
1498
1530
  });
1499
- if (!cached) _jwkCache.set(key, { [effectiveAlg]: cryptoKey });
1500
- else cached[effectiveAlg] = cryptoKey;
1531
+ if (_activeCache) _activeCache.set(key, effectiveAlg, cryptoKey);
1501
1532
  return cryptoKey;
1502
1533
  }
1503
- throw new Error("Invalid key type provided to importKey");
1534
+ throw new JWTError("Invalid key type provided to importKey", "ERR_JWK_INVALID");
1504
1535
  }
1505
1536
  /**
1506
1537
  * Exports a CryptoKey to a JWK (JSON Web Key) format.
@@ -1522,41 +1553,37 @@ async function resolveWrappingKey(alg, key) {
1522
1553
  if (typeof key === "string") throw new TypeError(`Key must be CryptoKey, JWK, or Uint8Array for ${alg}`);
1523
1554
  return importKey(key, alg);
1524
1555
  }
1525
- /**
1526
- * Unwraps a Content Encryption Key (CEK) using the specified algorithm and unwrapping key.
1527
- *
1528
- * @param alg The JWA key management algorithm (e.g., "A128KW", "RSA-OAEP", "PBES2-HS256+A128KW").
1529
- * @param wrappedKey The wrapped key (ciphertext) as Uint8Array.
1530
- * @param unwrappingKey The key used to unwrap the CEK (CryptoKey, JWK, or password string/Uint8Array for PBES2).
1531
- * @param options Additional options required by certain algorithms (e.g., iv, tag, p2s, p2c, epk).
1532
- * @returns A Promise resolving to the unwrapped key (CEK) as a CryptoKey or Uint8Array.
1533
- */
1534
1556
  async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
1535
- const { returnAs = true } = options;
1557
+ const returnRaw = options.format === "raw";
1536
1558
  const defaultExtractable = options.extractable !== false;
1537
1559
  const importedUnwrappingKey = isCryptoKey(unwrappingKey) ? unwrappingKey : await resolveWrappingKey(alg, unwrappingKey);
1538
1560
  let unwrappedCekBytes;
1539
1561
  switch (alg) {
1562
+ case "dir":
1563
+ if (importedUnwrappingKey instanceof Uint8Array) unwrappedCekBytes = importedUnwrappingKey;
1564
+ else {
1565
+ if (!returnRaw) return importedUnwrappingKey;
1566
+ unwrappedCekBytes = new Uint8Array(await crypto.subtle.exportKey("raw", importedUnwrappingKey));
1567
+ }
1568
+ break;
1540
1569
  case "A128KW":
1541
1570
  case "A192KW":
1542
1571
  case "A256KW":
1572
+ unwrappedCekBytes = await aesKwUnwrap(alg, importedUnwrappingKey, wrappedKey);
1573
+ break;
1543
1574
  case "PBES2-HS256+A128KW":
1544
1575
  case "PBES2-HS384+A192KW":
1545
1576
  case "PBES2-HS512+A256KW": {
1546
1577
  const { p2s, p2c } = options;
1547
- let p2sBytes;
1548
- if (alg.startsWith("PBES2")) {
1549
- if (!p2s || typeof p2c !== "number") throw new Error("PBES2 requires 'p2s' (salt) and 'p2c' (count) options");
1550
- p2sBytes = typeof p2s === "string" ? base64UrlDecode(p2s, false) : p2s;
1551
- }
1552
- unwrappedCekBytes = await unwrap(alg, importedUnwrappingKey, wrappedKey, p2c, p2sBytes);
1578
+ if (!p2s || typeof p2c !== "number") throw new Error("PBES2 requires 'p2s' (salt) and 'p2c' (count) options");
1579
+ unwrappedCekBytes = await pbes2Unwrap(alg, importedUnwrappingKey, wrappedKey, p2c, typeof p2s === "string" ? base64UrlDecode(p2s, false) : p2s);
1553
1580
  break;
1554
1581
  }
1555
1582
  case "A128GCMKW":
1556
1583
  case "A192GCMKW":
1557
1584
  case "A256GCMKW":
1558
1585
  if (!options.iv || !options.tag) throw new Error("AES-GCMKW requires 'iv' and 'tag' options for unwrapping");
1559
- unwrappedCekBytes = await decryptIV(alg, importedUnwrappingKey, wrappedKey, typeof options.iv === "string" ? base64UrlDecode(options.iv, false) : options.iv, typeof options.tag === "string" ? base64UrlDecode(options.tag, false) : options.tag);
1586
+ unwrappedCekBytes = await gcmkwDecrypt(alg, importedUnwrappingKey, wrappedKey, typeof options.iv === "string" ? base64UrlDecode(options.iv, false) : options.iv, typeof options.tag === "string" ? base64UrlDecode(options.tag, false) : options.tag);
1560
1587
  break;
1561
1588
  case "RSA-OAEP":
1562
1589
  case "RSA-OAEP-256":
@@ -1564,7 +1591,7 @@ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
1564
1591
  case "RSA-OAEP-512": {
1565
1592
  if (!(importedUnwrappingKey instanceof CryptoKey)) throw new TypeError("RSA-OAEP requires the unwrapping key to be provided as a CryptoKey");
1566
1593
  unwrappedCekBytes = await decryptRSAES(alg, importedUnwrappingKey, wrappedKey);
1567
- if (!returnAs) break;
1594
+ if (returnRaw) break;
1568
1595
  const inferredAlgorithm = options.unwrappedKeyAlgorithm || inferAesImportAlgorithm(options.enc, unwrappedCekBytes.length);
1569
1596
  if (!inferredAlgorithm) throw new Error("Unable to infer algorithm for RSA-OAEP unwrapped key. Provide \"unwrappedKeyAlgorithm\" in options.");
1570
1597
  const keyUsages = options.keyUsage || ["encrypt", "decrypt"];
@@ -1576,7 +1603,7 @@ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
1576
1603
  case "ECDH-ES+A256KW": {
1577
1604
  if (!options.epk) throw new Error("ECDH-ES requires 'epk' (Ephemeral Public Key) option");
1578
1605
  if (!(importedUnwrappingKey instanceof CryptoKey)) throw new TypeError("ECDH-ES requires the unwrapping key to be a CryptoKey");
1579
- if (!allowed(importedUnwrappingKey)) throw new Error("ECDH with the provided key is not allowed or not supported");
1606
+ if (!isECDHKeyAllowed(importedUnwrappingKey)) throw new Error("ECDH with the provided key is not allowed or not supported");
1580
1607
  const epkCandidate = options.epk instanceof CryptoKey ? options.epk : await normalizeKey(options.epk, alg);
1581
1608
  if (!(epkCandidate instanceof CryptoKey)) throw new TypeError("Failed to normalize ECDH ephemeral public key");
1582
1609
  const apuBytes = typeof options.apu === "string" ? base64UrlDecode(options.apu, false) : options.apu ?? new Uint8Array(0);
@@ -1588,13 +1615,13 @@ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
1588
1615
  else {
1589
1616
  const kwAlg = alg.slice(-6);
1590
1617
  if (!(wrappedKey instanceof Uint8Array) || wrappedKey.length === 0) throw new Error("ECDH-ES key agreement with key wrapping requires an encrypted key");
1591
- unwrappedCekBytes = await unwrap(kwAlg, sharedSecret, wrappedKey);
1618
+ unwrappedCekBytes = await aesKwUnwrap(kwAlg, sharedSecret, wrappedKey);
1592
1619
  }
1593
1620
  break;
1594
1621
  }
1595
1622
  default: throw new Error(`Unsupported key unwrapping algorithm: ${alg}`);
1596
1623
  }
1597
- if (!returnAs) return unwrappedCekBytes;
1624
+ if (returnRaw) return unwrappedCekBytes;
1598
1625
  return await crypto.subtle.importKey("raw", unwrappedCekBytes, options.unwrappedKeyAlgorithm || { name: "AES-GCM" }, defaultExtractable, options.keyUsage || ["encrypt", "decrypt"]);
1599
1626
  }
1600
1627
  /**
@@ -1608,35 +1635,25 @@ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
1608
1635
  * @param jwkExtras Additional properties to merge into the resulting JWK.
1609
1636
  * @returns A Promise resolving to the imported key as a JWK.
1610
1637
  */
1611
- async function importJWKFromPEM(pem, pemType, alg, importOptions, jwkExtras) {
1638
+ async function importFromPEM(pem, pemType, alg, options) {
1639
+ const extractable = options?.extractable !== false;
1612
1640
  let cryptoKey;
1613
- const defaultExtractable = importOptions?.extractable !== false;
1614
1641
  switch (pemType) {
1615
1642
  case "pkcs8":
1616
- cryptoKey = await fromPKCS8(pem, alg, {
1617
- ...importOptions,
1618
- extractable: defaultExtractable
1619
- });
1643
+ cryptoKey = await fromPKCS8(pem, alg, { extractable });
1620
1644
  break;
1621
1645
  case "spki":
1622
- cryptoKey = await fromSPKI(pem, alg, {
1623
- ...importOptions,
1624
- extractable: defaultExtractable
1625
- });
1646
+ cryptoKey = await fromSPKI(pem, alg, { extractable });
1626
1647
  break;
1627
1648
  case "x509":
1628
- cryptoKey = await fromX509(pem, alg, {
1629
- ...importOptions,
1630
- extractable: defaultExtractable
1631
- });
1649
+ cryptoKey = await fromX509(pem, alg, { extractable });
1632
1650
  break;
1633
1651
  default: throw new TypeError(`Unsupported PEM type: ${pemType}`);
1634
1652
  }
1635
- const finalJWKExtras = {
1653
+ return exportKey(cryptoKey, {
1636
1654
  alg,
1637
- ...jwkExtras
1638
- };
1639
- return exportKey(cryptoKey, finalJWKExtras);
1655
+ ...options?.jwkParams
1656
+ });
1640
1657
  }
1641
1658
  /**
1642
1659
  * Exports a JWK to a PEM-encoded string.
@@ -1647,7 +1664,7 @@ async function importJWKFromPEM(pem, pemType, alg, importOptions, jwkExtras) {
1647
1664
  * required to correctly convert it to a CryptoKey first.
1648
1665
  * @returns A Promise resolving to the PEM-encoded key string.
1649
1666
  */
1650
- async function exportJWKToPEM(jwk, pemFormat, algForCryptoKeyImport) {
1667
+ async function exportToPEM(jwk, pemFormat, algForCryptoKeyImport) {
1651
1668
  if (jwk.kty === "oct") throw new TypeError("Octet (symmetric) JWKs (kty: 'oct') cannot be exported to PKCS8 or SPKI PEM formats.");
1652
1669
  const effectiveAlg = jwk.alg || algForCryptoKeyImport;
1653
1670
  if (!effectiveAlg && (jwk.kty === "RSA" || jwk.kty === "EC" || jwk.kty === "OKP")) throw new TypeError("Algorithm (alg) must be provided in the JWK or as a parameter for converting this JWK type to a CryptoKey.");
@@ -1668,43 +1685,19 @@ async function exportJWKToPEM(jwk, pemFormat, algForCryptoKeyImport) {
1668
1685
  }
1669
1686
  }
1670
1687
  /**
1671
- * Retrieves a JWK (JSON Web Key) from a JWKSet based on a key ID (kid) or
1672
- * properties from a JOSE Header.
1688
+ * Returns all JWKs from a JWK Set, optionally narrowed by a predicate.
1689
+ *
1690
+ * Useful for multi-key verification retry, key rotation tooling, and
1691
+ * constructing multi-recipient JWE JSON Serialization structures.
1673
1692
  *
1674
- * @param jwkSet The JWKSet to search within.
1675
- * @param kidOrProtectedHeader Either a string representing the 'kid' (Key ID)
1676
- * or an object representing the JOSE Protected Header. If an object is
1677
- * provided, it must contain a 'kid' property. It can optionally contain
1678
- * 'alg' (Algorithm) and 'kty' (Key Type) to further refine the search.
1679
- * @returns The matching JWK from the set.
1680
- * @throws TypeError if `jwkSet` is invalid or `kidOrProtectedHeader` is not a
1681
- * string or a valid header object.
1682
- * @throws Error if no key is found matching the provided criteria, or if the
1683
- * header object is missing the 'kid' property.
1693
+ * @param jwkSet The JWK Set to search.
1694
+ * @param filter Optional predicate `(jwk: JWK) => boolean`. Returns all keys when omitted.
1695
+ * @returns An array of matching JWKs.
1684
1696
  */
1685
- function getJWKFromSet(jwkSet, kidOrProtectedHeader) {
1697
+ function getJWKsFromSet(jwkSet, filter) {
1686
1698
  if (!jwkSet || !isJWKSet(jwkSet)) throw new TypeError("Invalid JWK Set provided");
1687
- if (typeof kidOrProtectedHeader === "string") {
1688
- const kid = kidOrProtectedHeader;
1689
- const selectedKey = jwkSet.keys.find((k) => k.kid === kid);
1690
- if (!selectedKey) throw new Error(`No key found in JWK Set with kid "${kid}".`);
1691
- return selectedKey;
1692
- } else if (typeof kidOrProtectedHeader === "object") {
1693
- const { kid, alg, kty } = kidOrProtectedHeader;
1694
- if (!kid) throw new Error("JWS Protected Header is missing 'kid' (Key ID) and a JWK Set was provided. Cannot select key from JWK Set automatically.");
1695
- const selectedKey = jwkSet.keys.find((k) => {
1696
- return k.kid === kid && (!alg || k.alg === alg) && (!kty || k.kty === kty);
1697
- });
1698
- if (!selectedKey) {
1699
- let errorMessage = `No key found in JWK Set with kid "${kid}"`;
1700
- if (alg) errorMessage += ` and alg "${alg}"`;
1701
- if (kty) errorMessage += ` and kty "${kty}"`;
1702
- errorMessage += ".";
1703
- throw new Error(errorMessage);
1704
- }
1705
- return selectedKey;
1706
- }
1707
- throw new TypeError("`kidOrProtectedHeader` must be a string (kid) or an object (JOSE Protected Header).");
1699
+ if (!filter) return [...jwkSet.keys];
1700
+ return jwkSet.keys.filter(filter);
1708
1701
  }
1709
1702
  function getGenerateKeyParams(alg, options) {
1710
1703
  let algorithm;
@@ -1865,20 +1858,23 @@ function inferAesImportAlgorithm(enc, cekLengthBytes) {
1865
1858
  //#endregion
1866
1859
  //#region src/core/jwe.ts
1867
1860
  async function encrypt(payload, key, options = {}) {
1868
- const { protectedHeader: additionalProtectedHeader, cek: providedCek, contentEncryptionIV: providedContentIV, keyManagementIV, p2s, p2c, ecdh } = options;
1861
+ const { protectedHeader: userHeader, cek: providedCek, contentEncryptionIV: providedContentIV, keyManagementIV, p2s, p2c, ecdh } = options;
1869
1862
  let { alg, enc } = options;
1870
1863
  if (!alg) {
1871
1864
  if (typeof key === "string" || key instanceof Uint8Array) alg = "PBES2-HS256+A128KW";
1872
1865
  else if (isJWK(key)) alg = key.alg === "A128GCM" || key.alg === "A192GCM" || key.alg === "A256GCM" ? `${key.alg}KW` : key.alg;
1873
1866
  }
1874
1867
  if (!alg) throw new TypeError("JWE \"alg\" (Key Management Algorithm) must be provided in options or inferable from the key");
1875
- if (!enc) enc = isJWK(key) && "enc" in key ? key.enc : "A128GCM";
1868
+ if (!enc) if (alg === "dir") {
1869
+ enc = isJWK(key) && "enc" in key ? key.enc : void 0;
1870
+ if (!enc) throw new TypeError("JWE \"enc\" must be provided when alg is \"dir\"");
1871
+ } else enc = isJWK(key) && "enc" in key ? key.enc : "A128GCM";
1876
1872
  const jweKeyManagementParams = {};
1877
1873
  if (keyManagementIV) jweKeyManagementParams.iv = keyManagementIV;
1878
1874
  if (p2s) jweKeyManagementParams.p2s = p2s;
1879
1875
  else if (alg?.startsWith("PBES2")) jweKeyManagementParams.p2s = randomBytes(16);
1880
1876
  if (p2c) jweKeyManagementParams.p2c = p2c;
1881
- else if (alg?.startsWith("PBES2")) jweKeyManagementParams.p2c = 2048;
1877
+ else if (alg?.startsWith("PBES2")) jweKeyManagementParams.p2c = 6e5;
1882
1878
  if (ecdh?.partyUInfo) jweKeyManagementParams.apu = ecdh.partyUInfo;
1883
1879
  if (ecdh?.partyVInfo) jweKeyManagementParams.apv = ecdh.partyVInfo;
1884
1880
  if (ecdh?.ephemeralKey) {
@@ -1888,21 +1884,14 @@ async function encrypt(payload, key, options = {}) {
1888
1884
  }
1889
1885
  const wrappingKeyMaterial = await importKey(key, alg);
1890
1886
  const { cek: finalCek, encryptedKey: jweEncryptedKey, parameters: keyManagementHeaderParams } = await encryptKey(alg, enc, wrappingKeyMaterial, providedCek, jweKeyManagementParams);
1891
- const jweProtectedHeader = {
1892
- ...additionalProtectedHeader,
1893
- ...keyManagementHeaderParams,
1894
- alg,
1895
- enc
1896
- };
1897
- if (isJWK(key) && key.kid) jweProtectedHeader.kid = key.kid;
1898
- const protectedHeader = applyTypCtyDefaults(jweProtectedHeader, payload);
1887
+ const protectedHeader = _buildJWEHeader(alg, enc, key, userHeader, keyManagementHeaderParams, payload);
1899
1888
  const plaintextBytes = getPlaintextBytes(computeJwtTimeClaims(payload, protectedHeader.typ, options.expiresIn, options.currentDate) || payload);
1900
1889
  const protectedHeaderEncoded = base64UrlEncode(JSON.stringify(protectedHeader));
1901
1890
  const aadBytes = textEncoder.encode(protectedHeaderEncoded);
1902
1891
  const contentIVBytes = providedContentIV ?? generateIV(enc);
1903
1892
  const { ciphertext: contentCiphertext, tag: contentAuthTag, iv: actualContentIV } = await encrypt$1(enc, plaintextBytes, finalCek, contentIVBytes, aadBytes);
1904
- if (!actualContentIV) throw new Error("Content encryption IV was not generated or returned.");
1905
- if (!contentAuthTag) throw new Error("Content encryption auth tag was not generated or returned.");
1893
+ if (!actualContentIV) throw new JWTError("Content encryption IV was not generated or returned.", "ERR_JWE_INVALID");
1894
+ if (!contentAuthTag) throw new JWTError("Content encryption auth tag was not generated or returned.", "ERR_JWE_INVALID");
1906
1895
  return [
1907
1896
  protectedHeaderEncoded,
1908
1897
  jweEncryptedKey ? base64UrlEncode(jweEncryptedKey) : "",
@@ -1913,28 +1902,28 @@ async function encrypt(payload, key, options = {}) {
1913
1902
  }
1914
1903
  async function decrypt(jwe, key, options = {}) {
1915
1904
  const parts = jwe.split(".");
1916
- if (parts.length !== 5) throw new Error("Invalid JWE: Must contain five sections (RFC7516, section-3).");
1905
+ if (parts.length !== 5) throw new JWTError("Invalid JWE: Must contain five sections (RFC7516, section-3).", "ERR_JWE_INVALID");
1917
1906
  const [protectedHeaderEncoded, encryptedKeyEncoded, ivEncoded, ciphertextEncoded, authTagEncoded] = parts;
1918
1907
  let protectedHeader;
1919
1908
  try {
1920
1909
  const protectedHeaderJson = base64UrlDecode(protectedHeaderEncoded);
1921
1910
  protectedHeader = sanitizeObject(JSON.parse(protectedHeaderJson));
1922
1911
  } catch {
1923
- throw new Error("Invalid JWE: Protected header could not be decoded.");
1912
+ throw new JWTError("Invalid JWE: Protected header could not be decoded.", "ERR_JWE_INVALID");
1924
1913
  }
1925
- if (!protectedHeader || typeof protectedHeader !== "object" || !protectedHeader.alg || !protectedHeader.enc) throw new Error("Invalid JWE: Protected header must be an object with \"alg\" and \"enc\" properties.");
1914
+ if (!protectedHeader || typeof protectedHeader !== "object" || !protectedHeader.alg || !protectedHeader.enc) throw new JWTError("Invalid JWE: Protected header must be an object with \"alg\" and \"enc\" properties.", "ERR_JWE_INVALID");
1926
1915
  const alg = protectedHeader.alg;
1927
1916
  const enc = protectedHeader.enc;
1928
- if (options?.algorithms && !options.algorithms.includes(alg)) throw new Error(`Key management algorithm not allowed: ${alg}`);
1929
- if (options?.encryptionAlgorithms && !options.encryptionAlgorithms.includes(enc)) throw new Error(`Content encryption algorithm not allowed: ${enc}`);
1930
- const unwrappingKey = await importKey(typeof key === "function" ? await key(protectedHeader, jwe) : key, alg);
1917
+ if (options?.algorithms && !options.algorithms.includes(alg)) throw new JWTError(`Key management algorithm not allowed: ${alg}`, "ERR_JWE_ALG_NOT_ALLOWED");
1918
+ if (options?.encryptionAlgorithms && !options.encryptionAlgorithms.includes(enc)) throw new JWTError(`Content encryption algorithm not allowed: ${enc}`, "ERR_JWE_ALG_NOT_ALLOWED");
1919
+ const rawKeyMaterial = typeof key === "function" ? await key(protectedHeader, jwe) : key;
1931
1920
  const encryptedKeyBytes = base64UrlDecode(encryptedKeyEncoded, false);
1932
1921
  const contentIVBytes = base64UrlDecode(ivEncoded, false);
1933
1922
  const contentAuthTagBytes = base64UrlDecode(authTagEncoded, false);
1934
1923
  const ciphertextBytes = base64UrlDecode(ciphertextEncoded, false);
1935
1924
  const unwrapKeyOpts = {
1936
1925
  enc,
1937
- returnAs: enc.includes("GCM"),
1926
+ format: enc.includes("GCM") ? "cryptokey" : "raw",
1938
1927
  keyUsage: options?.keyUsage || ["decrypt"]
1939
1928
  };
1940
1929
  if (protectedHeader.iv) unwrapKeyOpts.iv = protectedHeader.iv;
@@ -1946,11 +1935,34 @@ async function decrypt(jwe, key, options = {}) {
1946
1935
  if (protectedHeader.apv) unwrapKeyOpts.apv = protectedHeader.apv;
1947
1936
  if (options?.unwrappedKeyAlgorithm) unwrapKeyOpts.unwrappedKeyAlgorithm = options.unwrappedKeyAlgorithm;
1948
1937
  if (options?.extractable) unwrapKeyOpts.extractable = options.extractable;
1949
- const cek = await unwrapKey(alg, encryptedKeyBytes, unwrappingKey, unwrapKeyOpts);
1950
1938
  const aadBytes = textEncoder.encode(protectedHeaderEncoded);
1951
- const payload = decodePayloadFromBytes(await decrypt$1(enc, cek, ciphertextBytes, contentIVBytes, contentAuthTagBytes, aadBytes), protectedHeader, options?.forceUint8Array);
1952
- if (protectedHeader.crit || options?.critical?.length || options?.requiredHeaders?.length) validateCriticalHeadersJWE(protectedHeader, [...options?.critical || [], ...options?.requiredHeaders || []]);
1953
- if (payload && typeof payload === "object" && options.validateJWT !== false && (options.validateJWT === true || protectedHeader.typ?.toLowerCase().includes("jwt")) && !options.forceUint8Array && !(payload instanceof Uint8Array)) validateJwtClaims(payload, options);
1939
+ let cek;
1940
+ let plaintextBytes;
1941
+ if (isJWKSet(rawKeyMaterial)) {
1942
+ const candidates = getJWKsFromSet(rawKeyMaterial, _buildJWESetFilter(protectedHeader));
1943
+ if (candidates.length === 0) throw new JWTError(`No key found in JWK Set${protectedHeader.kid ? ` with kid "${protectedHeader.kid}"` : ""}.`, "ERR_JWK_KEY_NOT_FOUND");
1944
+ let decrypted = false;
1945
+ for (const candidate of candidates) try {
1946
+ const candidateCek = await unwrapKey(alg, encryptedKeyBytes, await importKey(candidate, alg), unwrapKeyOpts);
1947
+ plaintextBytes = await decrypt$1(enc, candidateCek, ciphertextBytes, contentIVBytes, contentAuthTagBytes, aadBytes);
1948
+ cek = candidateCek;
1949
+ decrypted = true;
1950
+ break;
1951
+ } catch {}
1952
+ if (!decrypted) throw new JWTError("JWE decryption failed.", "ERR_JWE_DECRYPTION_FAILED");
1953
+ } else {
1954
+ const unwrappingKey = await importKey(rawKeyMaterial, alg);
1955
+ try {
1956
+ cek = await unwrapKey(alg, encryptedKeyBytes, unwrappingKey, unwrapKeyOpts);
1957
+ plaintextBytes = await decrypt$1(enc, cek, ciphertextBytes, contentIVBytes, contentAuthTagBytes, aadBytes);
1958
+ } catch (error_) {
1959
+ if (error_ instanceof JWTError) throw error_;
1960
+ throw new JWTError("JWE decryption failed.", "ERR_JWE_DECRYPTION_FAILED", error_);
1961
+ }
1962
+ }
1963
+ const payload = decodePayloadFromBytes(plaintextBytes, protectedHeader, options?.forceUint8Array);
1964
+ if (protectedHeader.crit || options?.recognizedHeaders?.length) validateCriticalHeadersJWE(protectedHeader, options?.recognizedHeaders);
1965
+ if (payload && typeof payload === "object" && options.validateClaims !== false && (options.validateClaims === true || protectedHeader.typ?.toLowerCase().includes("jwt")) && !options.forceUint8Array && !(payload instanceof Uint8Array)) validateJwtClaims(payload, options);
1954
1966
  const result = {
1955
1967
  payload,
1956
1968
  protectedHeader
@@ -1986,24 +1998,30 @@ function parseEphemeralKey(ephemeralKey) {
1986
1998
  epkPrivateKey
1987
1999
  };
1988
2000
  }
2001
+ function _buildJWESetFilter(header) {
2002
+ const { kid, alg } = header;
2003
+ return (k) => (!kid || k.kid === kid) && (!k.alg || k.alg === alg);
2004
+ }
2005
+ function _buildJWEHeader(alg, enc, key, userHeader, keyManagementParams, payload) {
2006
+ const safeHeader = sanitizeObject(userHeader);
2007
+ return applyTypCtyDefaults({
2008
+ ...isJWK(key) && key.kid ? { kid: key.kid } : {},
2009
+ ...safeHeader,
2010
+ ...keyManagementParams,
2011
+ alg,
2012
+ enc
2013
+ }, payload);
2014
+ }
1989
2015
  //#endregion
1990
2016
  //#region src/core/jws.ts
1991
2017
  async function sign(payload, key, options = {}) {
1992
- const { protectedHeader: additionalProtectedHeader } = options;
1993
- const safeAdditionalHeader = sanitizeObject(additionalProtectedHeader);
2018
+ const { protectedHeader: userHeader } = options;
1994
2019
  let { alg } = options;
1995
2020
  if (!alg) if (isJWK(key) && key.alg) alg = key.alg;
1996
2021
  else throw new TypeError("JWS \"alg\" (Algorithm) must be provided in options");
1997
- validateKeyLength(key, alg);
1998
- const signingKey = await importKey(key, alg);
1999
- const protectedHeader = {
2000
- ...safeAdditionalHeader,
2001
- ...isJWK(key) && key.kid ? { kid: key.kid } : {},
2002
- alg,
2003
- typ: safeAdditionalHeader?.typ
2004
- };
2005
- applyTypCtyDefaults(protectedHeader, payload);
2006
- if (protectedHeader.b64 === true) delete protectedHeader.b64;
2022
+ if (alg === "none") throw new JWTError("\"none\" is not a valid signing algorithm", "ERR_JWS_ALG_NOT_ALLOWED");
2023
+ const signingKey = await _resolveSigningKey(alg, await importKey(key, alg), "sign");
2024
+ const protectedHeader = _buildJWSHeader(alg, key, userHeader, payload);
2007
2025
  const computedPayload = computeJwtTimeClaims(payload, protectedHeader.typ, options.expiresIn, options.currentDate);
2008
2026
  const protectedHeaderEncoded = base64UrlEncode(JSON.stringify(protectedHeader));
2009
2027
  let payloadBytes;
@@ -2017,53 +2035,82 @@ async function sign(payload, key, options = {}) {
2017
2035
  }
2018
2036
  async function verify(jws, key, options = {}) {
2019
2037
  const parts = jws.split(".");
2020
- if (parts.length !== 3) throw new Error("Invalid JWS: Must contain three parts separated by dots.");
2038
+ if (parts.length !== 3) throw new JWTError("Invalid JWS: Must contain three parts separated by dots.", "ERR_JWS_INVALID");
2021
2039
  const [protectedHeaderEncoded, payloadEncoded, signatureEncoded] = parts;
2022
2040
  let protectedHeader;
2023
2041
  try {
2024
2042
  const protectedHeaderString = base64UrlDecode(protectedHeaderEncoded);
2025
2043
  protectedHeader = sanitizeObject(JSON.parse(protectedHeaderString));
2026
2044
  } catch {
2027
- throw new Error("Invalid JWS: Protected header could not be decoded.");
2045
+ throw new JWTError("Invalid JWS: Protected header could not be decoded.", "ERR_JWS_INVALID");
2028
2046
  }
2029
- if (!protectedHeader || typeof protectedHeader !== "object" || !protectedHeader.alg) throw new Error("Invalid JWS: Protected header must be an object with an \"alg\" property.");
2047
+ if (!protectedHeader || typeof protectedHeader !== "object" || !protectedHeader.alg) throw new JWTError("Invalid JWS: Protected header must be an object with an \"alg\" property.", "ERR_JWS_INVALID");
2030
2048
  const alg = protectedHeader.alg;
2031
- if (options.algorithms && !options.algorithms.includes(alg)) throw new Error(`Algorithm not allowed: ${alg}`);
2032
- if (options.typ && protectedHeader.typ !== options.typ) throw new Error(`Invalid JWS: "typ" (Type) Header Parameter mismatch. Expected "${options.typ}", got "${protectedHeader.typ}".`);
2049
+ if (options.algorithms && !options.algorithms.includes(alg)) throw new JWTError(`Algorithm not allowed: ${alg}`, "ERR_JWS_ALG_NOT_ALLOWED");
2050
+ if (options.typ && protectedHeader.typ !== options.typ) throw new JWTError(`Invalid JWS: "typ" (Type) Header Parameter mismatch. Expected "${options.typ}", got "${protectedHeader.typ}".`, "ERR_JWS_INVALID");
2033
2051
  let signatureBytes;
2034
2052
  try {
2035
2053
  signatureBytes = base64UrlDecode(signatureEncoded, false);
2036
2054
  } catch {
2037
- throw new Error("Invalid JWS: Signature could not be decoded.");
2055
+ throw new JWTError("Invalid JWS: Signature could not be decoded.", "ERR_JWS_INVALID");
2038
2056
  }
2039
2057
  const keyInput = typeof key === "function" ? await key(protectedHeader, jws) : key;
2040
- const verificationKey = await importKey(isJWKSet(keyInput) ? getJWKFromSet(keyInput, protectedHeader) : keyInput, alg);
2041
- const signingInputString = `${protectedHeaderEncoded}.${payloadEncoded}`;
2042
- const signingInputBytes = textEncoder.encode(signingInputString);
2043
- if (!await verify$1(alg, verificationKey, signatureBytes, signingInputBytes)) throw new Error("JWS signature verification failed.");
2058
+ const signingInputBytes = textEncoder.encode(`${protectedHeaderEncoded}.${payloadEncoded}`);
2059
+ if (isJWKSet(keyInput)) {
2060
+ const candidates = getJWKsFromSet(keyInput, _buildJWSSetFilter(protectedHeader));
2061
+ if (candidates.length === 0) throw new JWTError(`No key found in JWK Set${protectedHeader.kid ? ` with kid "${protectedHeader.kid}"` : ""}.`, "ERR_JWK_KEY_NOT_FOUND");
2062
+ let verified = false;
2063
+ for (const candidate of candidates) try {
2064
+ if (await verify$1(alg, await _resolveSigningKey(alg, await importKey(candidate, alg), "verify"), signatureBytes, signingInputBytes)) {
2065
+ verified = true;
2066
+ break;
2067
+ }
2068
+ } catch {}
2069
+ if (!verified) throw new JWTError("JWS signature verification failed.", "ERR_JWS_SIGNATURE_INVALID");
2070
+ } else if (!await verify$1(alg, await _resolveSigningKey(alg, await importKey(keyInput, alg), "verify"), signatureBytes, signingInputBytes)) throw new JWTError("JWS signature verification failed.", "ERR_JWS_SIGNATURE_INVALID");
2044
2071
  const useB64 = protectedHeader.b64 !== false;
2045
2072
  let payload;
2046
2073
  try {
2047
2074
  payload = useB64 ? decodePayloadFromB64UrlSegment(payloadEncoded, protectedHeader, options.forceUint8Array) : options.forceUint8Array ? textEncoder.encode(payloadEncoded) : payloadEncoded;
2048
2075
  } catch (error_) {
2049
- throw new Error(`Invalid JWS: Payload decoding failed (${error_ instanceof Error ? error_.message : error_})`);
2076
+ throw new JWTError(`Invalid JWS: Payload decoding failed (${error_ instanceof Error ? error_.message : error_})`, "ERR_JWS_INVALID");
2050
2077
  }
2051
- validateCriticalHeadersJWS(protectedHeader, [...options.critical || [], ...options.requiredHeaders || []]);
2052
- if (payload && typeof payload === "object" && options.validateJWT !== false && (options.validateJWT === true || protectedHeader.typ?.toLowerCase().includes("jwt")) && !options.forceUint8Array && !(payload instanceof Uint8Array)) validateJwtClaims(payload, options);
2078
+ validateCriticalHeadersJWS(protectedHeader, options.recognizedHeaders);
2079
+ if (payload && typeof payload === "object" && options.validateClaims !== false && (options.validateClaims === true || protectedHeader.typ?.toLowerCase().includes("jwt")) && !options.forceUint8Array && !(payload instanceof Uint8Array)) validateJwtClaims(payload, options);
2053
2080
  return {
2054
2081
  payload,
2055
2082
  protectedHeader
2056
2083
  };
2057
2084
  }
2058
- function validateKeyLength(key, alg) {
2059
- if (!alg || isJWK(key)) return;
2060
- if (alg.startsWith("HS") && key instanceof Uint8Array) {
2061
- const algLength = Number.parseInt(alg.slice(2)) / 8;
2062
- if (key.length < algLength) throw new TypeError(`${alg} requires key length to be ${algLength} bytes or larger`);
2063
- } else if ((alg.startsWith("RS") || alg.startsWith("PS")) && key instanceof CryptoKey) {
2085
+ async function _resolveSigningKey(alg, key, usage) {
2086
+ if (key instanceof Uint8Array) {
2087
+ const minBytes = Number.parseInt(alg.slice(2), 10) / 8;
2088
+ if (key.length < minBytes) throw new JWTError(`${alg} requires a key of at least ${minBytes} bytes`, "ERR_JWK_INVALID");
2089
+ return crypto.subtle.importKey("raw", key, {
2090
+ name: "HMAC",
2091
+ hash: `SHA-${alg.slice(-3)}`
2092
+ }, false, [usage]);
2093
+ }
2094
+ if (alg.startsWith("RS") || alg.startsWith("PS")) {
2064
2095
  const { modulusLength } = key.algorithm;
2065
- if (typeof modulusLength !== "number" || modulusLength < 2048) throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
2096
+ if (typeof modulusLength !== "number" || modulusLength < 2048) throw new JWTError(`${alg} requires a key modulusLength of at least 2048 bits`, "ERR_JWK_INVALID");
2066
2097
  }
2098
+ return key;
2099
+ }
2100
+ function _buildJWSSetFilter(header) {
2101
+ const { kid, alg } = header;
2102
+ return (k) => (!kid || k.kid === kid) && (!k.alg || k.alg === alg);
2103
+ }
2104
+ function _buildJWSHeader(alg, key, userHeader, payload) {
2105
+ const safeHeader = sanitizeObject(userHeader);
2106
+ const header = {
2107
+ ...isJWK(key) && key.kid ? { kid: key.kid } : {},
2108
+ ...safeHeader,
2109
+ alg
2110
+ };
2111
+ applyTypCtyDefaults(header, payload);
2112
+ if (header.b64 === true) delete header.b64;
2113
+ return header;
2067
2114
  }
2068
2115
  //#endregion
2069
- export { deriveJWKFromPassword as a, importJWKFromPEM as c, isSymmetricJWK as d, computeExpiresInSeconds as f, encrypt as i, isPrivateJWK as l, verify as n, exportJWKToPEM as o, decrypt as r, generateJWK as s, sign as t, isPublicJWK as u };
2116
+ export { deriveJWKFromPassword as a, importFromPEM as c, isSymmetricJWK as d, computeExpiresInSeconds as f, encrypt as i, isPrivateJWK as l, isJWTError as m, verify as n, exportToPEM as o, JWTError as p, decrypt as r, generateJWK as s, sign as t, isPublicJWK as u };