unjwt 0.5.40 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +147 -6
  2. package/dist/_chunks/_crypto.mjs +1014 -0
  3. package/dist/_chunks/error.d.mts +67 -0
  4. package/dist/_chunks/index.d.mts +149 -45
  5. package/dist/_chunks/jwk.d.mts +154 -57
  6. package/dist/_chunks/jws.mjs +729 -683
  7. package/dist/_chunks/utils.mjs +74 -70
  8. package/dist/{_chunks/jws.d.mts → adapters/h3v1/index.d.mts} +98 -58
  9. package/dist/{_chunks/jws2.mjs → adapters/h3v1/index.mjs} +198 -85
  10. package/dist/adapters/h3v2/index.d.mts +213 -0
  11. package/dist/adapters/{h3v2.mjs → h3v2/index.mjs} +195 -89
  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 +48 -40
  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/adapters/h3.d.mts +0 -3
  29. package/dist/adapters/h3.mjs +0 -3
  30. package/dist/adapters/h3v1.d.mts +0 -3
  31. package/dist/adapters/h3v1.mjs +0 -3
  32. package/dist/adapters/h3v2.d.mts +0 -183
  33. package/dist/jwe.d.mts +0 -3
  34. package/dist/jwe.mjs +0 -2
  35. package/dist/jwk.d.mts +0 -3
  36. package/dist/jwk.mjs +0 -2
  37. package/dist/jws.d.mts +0 -3
  38. package/dist/jws.mjs +0 -2
  39. package/dist/utils.d.mts +0 -3
  40. 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,41 +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
- const seen = /* @__PURE__ */ new WeakSet();
191
- seen.add(obj);
192
- sanitizeKeys(obj, seen);
193
- return obj;
194
- }
195
- function sanitizeKeys(current, seen) {
196
- if (Object.prototype.hasOwnProperty.call(current, "__proto__")) delete current["__proto__"];
197
- if (Object.prototype.hasOwnProperty.call(current, "prototype")) delete current["prototype"];
198
- if (Object.prototype.hasOwnProperty.call(current, "constructor")) delete current["constructor"];
199
- const items = Array.isArray(current) ? current : Object.values(current);
200
- for (const v of items) if (v && typeof v === "object") {
201
- if (!seen.has(v)) {
202
- seen.add(v);
203
- sanitizeKeys(v, seen);
204
- }
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;
205
242
  }
243
+ return result;
206
244
  }
207
245
  //#endregion
208
246
  //#region src/core/utils/index.ts
@@ -338,157 +376,61 @@ function isPublicJWK(key) {
338
376
  return false;
339
377
  }
340
378
  //#endregion
341
- //#region src/core/jose/crypto_key.ts
342
- /**
343
- * This is a fork of Jose library's utility functions.
344
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/crypto_key.ts
345
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
346
- */
347
- function unusable(name, prop = "algorithm.name") {
348
- return /* @__PURE__ */ new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
349
- }
350
- function isAlgorithm(algorithm, name) {
351
- return algorithm.name === name;
352
- }
353
- function getHashLength(hash) {
354
- 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);
355
384
  }
356
- function getNamedCurve$1(alg) {
357
- switch (alg) {
358
- case "ES256": return "P-256";
359
- case "ES384": return "P-384";
360
- case "ES512": return "P-521";
361
- 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;
362
391
  }
363
392
  }
364
- function checkUsage(key, usage) {
365
- if (usage && !key.usages.includes(usage)) throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
366
- }
367
393
  function checkSigCryptoKey(key, alg, usage) {
368
394
  switch (alg) {
369
395
  case "HS256":
370
396
  case "HS384":
371
397
  case "HS512": {
372
- if (!isAlgorithm(key.algorithm, "HMAC")) throw unusable("HMAC");
398
+ if (!isAlgorithm$3(key.algorithm, "HMAC")) throw unusable$4("HMAC");
373
399
  const expected = Number.parseInt(alg.slice(2), 10);
374
- 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");
375
401
  break;
376
402
  }
377
403
  case "RS256":
378
404
  case "RS384":
379
405
  case "RS512": {
380
- 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");
381
407
  const expected = Number.parseInt(alg.slice(2), 10);
382
- 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");
383
409
  break;
384
410
  }
385
411
  case "PS256":
386
412
  case "PS384":
387
413
  case "PS512": {
388
- if (!isAlgorithm(key.algorithm, "RSA-PSS")) throw unusable("RSA-PSS");
414
+ if (!isAlgorithm$3(key.algorithm, "RSA-PSS")) throw unusable$4("RSA-PSS");
389
415
  const expected = Number.parseInt(alg.slice(2), 10);
390
- 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");
391
417
  break;
392
418
  }
393
419
  case "Ed25519":
394
420
  case "EdDSA":
395
- if (!isAlgorithm(key.algorithm, "Ed25519")) throw unusable("Ed25519");
421
+ if (!isAlgorithm$3(key.algorithm, "Ed25519")) throw unusable$4("Ed25519");
396
422
  break;
397
423
  case "ES256":
398
424
  case "ES384":
399
425
  case "ES512": {
400
- if (!isAlgorithm(key.algorithm, "ECDSA")) throw unusable("ECDSA");
426
+ if (!isAlgorithm$3(key.algorithm, "ECDSA")) throw unusable$4("ECDSA");
401
427
  const expected = getNamedCurve$1(alg);
402
- if (key.algorithm.namedCurve !== expected) throw unusable(expected, "algorithm.namedCurve");
403
- break;
404
- }
405
- default: throw new TypeError("CryptoKey does not support this operation");
406
- }
407
- checkUsage(key, usage);
408
- }
409
- function checkEncCryptoKey(key, alg, usage) {
410
- switch (alg) {
411
- case "A128GCM":
412
- case "A192GCM":
413
- case "A256GCM": {
414
- if (!isAlgorithm(key.algorithm, "AES-GCM")) throw unusable("AES-GCM");
415
- const expected = Number.parseInt(alg.slice(1, 4), 10);
416
- if (key.algorithm.length !== expected) throw unusable(expected, "algorithm.length");
417
- break;
418
- }
419
- case "A128KW":
420
- case "A192KW":
421
- case "A256KW": {
422
- if (!isAlgorithm(key.algorithm, "AES-KW")) throw unusable("AES-KW");
423
- const expected = Number.parseInt(alg.slice(1, 4), 10);
424
- if (key.algorithm.length !== expected) throw unusable(expected, "algorithm.length");
425
- break;
426
- }
427
- case "ECDH":
428
- switch (key.algorithm.name) {
429
- case "ECDH":
430
- case "X25519": break;
431
- default: throw unusable("ECDH or X25519");
432
- }
433
- break;
434
- case "PBES2-HS256+A128KW":
435
- case "PBES2-HS384+A192KW":
436
- case "PBES2-HS512+A256KW":
437
- if (!isAlgorithm(key.algorithm, "PBKDF2")) throw unusable("PBKDF2");
438
- break;
439
- case "RSA-OAEP":
440
- case "RSA-OAEP-256":
441
- case "RSA-OAEP-384":
442
- case "RSA-OAEP-512": {
443
- if (!isAlgorithm(key.algorithm, "RSA-OAEP")) throw unusable("RSA-OAEP");
444
- const expected = Number.parseInt(alg.slice(9), 10) || 1;
445
- if (getHashLength(key.algorithm.hash) !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash");
428
+ if (key.algorithm.namedCurve !== expected) throw unusable$4(expected, "algorithm.namedCurve");
446
429
  break;
447
430
  }
448
431
  default: throw new TypeError("CryptoKey does not support this operation");
449
432
  }
450
- checkUsage(key, usage);
451
- }
452
- //#endregion
453
- //#region src/core/jose/sign-verify.ts
454
- /**
455
- * This is a fork of Jose library's utility functions.
456
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/sign.ts
457
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/verify.ts
458
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
459
- */
460
- async function sign$1(alg, key, data) {
461
- const cryptoKey = await getSignVerifyKey(alg, key, "sign");
462
- checkKeyLength(alg, cryptoKey);
463
- const signature = await crypto.subtle.sign(subtleAlgorithm(alg, cryptoKey.algorithm), cryptoKey, data);
464
- return new Uint8Array(signature);
465
- }
466
- async function verify$1(alg, key, signature, data) {
467
- const cryptoKey = await getSignVerifyKey(alg, key, "verify");
468
- checkKeyLength(alg, cryptoKey);
469
- const algorithm = subtleAlgorithm(alg, cryptoKey.algorithm);
470
- try {
471
- return await crypto.subtle.verify(algorithm, cryptoKey, signature, data);
472
- } catch {
473
- return false;
474
- }
475
- }
476
- function checkKeyLength(alg, key) {
477
- if (alg.startsWith("RS") || alg.startsWith("PS")) {
478
- const { modulusLength } = key.algorithm;
479
- if (typeof modulusLength !== "number" || modulusLength < 2048) throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
480
- }
481
- }
482
- async function getSignVerifyKey(alg, key, usage) {
483
- if (key instanceof Uint8Array) {
484
- if (!alg.startsWith("HS")) throw new TypeError("Key must be of type CryptoKey or JSON Web Key for non-HMAC algorithms");
485
- return crypto.subtle.importKey("raw", key, {
486
- hash: `SHA-${alg.slice(-3)}`,
487
- name: "HMAC"
488
- }, false, [usage]);
489
- }
490
- checkSigCryptoKey(key, alg, usage);
491
- return key;
433
+ checkUsage$4(key, usage);
492
434
  }
493
435
  function subtleAlgorithm(alg, algorithm) {
494
436
  const hash = `SHA-${alg.slice(-3)}`;
@@ -524,44 +466,28 @@ function subtleAlgorithm(alg, algorithm) {
524
466
  default: throw new Error(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
525
467
  }
526
468
  }
527
- //#endregion
528
- //#region src/core/jose/buffer_utils.ts
529
- /**
530
- * This is a fork of Jose library's utility functions.
531
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/buffer_utils.ts
532
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
533
- */
534
- const MAX_INT32 = 2 ** 32;
535
- function writeUInt32BE(buf, value, offset) {
536
- if (value < 0 || value >= MAX_INT32) throw new RangeError(`value must be >= 0 and <= ${MAX_INT32 - 1}. Received ${value}`);
537
- buf.set([
538
- value >>> 24,
539
- value >>> 16,
540
- value >>> 8,
541
- value & 255
542
- ], 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}`);
543
471
  }
544
- function uint64be(value) {
545
- const high = Math.floor(value / MAX_INT32);
546
- const low = value % MAX_INT32;
547
- const buf = new Uint8Array(8);
548
- writeUInt32BE(buf, high, 0);
549
- writeUInt32BE(buf, low, 4);
550
- return buf;
472
+ function isAlgorithm$3(algorithm, name) {
473
+ return algorithm.name === name;
551
474
  }
552
- function uint32be(value) {
553
- const buf = new Uint8Array(4);
554
- writeUInt32BE(buf, value);
555
- 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}.`);
556
488
  }
557
489
  //#endregion
558
- //#region src/core/jose/cek-iv.ts
559
- /**
560
- * This is a fork of Jose library's utility functions.
561
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/cek.ts
562
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/iv.ts
563
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
564
- */
490
+ //#region src/core/_crypto/_aes.ts
565
491
  const IV_BIT_LENGTHS = {
566
492
  A128GCM: 96,
567
493
  A128GCMKW: 96,
@@ -604,14 +530,112 @@ function checkCEKLength(cek, expected) {
604
530
  const actual = cek.byteLength << 3;
605
531
  if (actual !== expected) throw new Error(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`);
606
532
  }
607
- //#endregion
608
- //#region src/core/jose/encrypt-decrypt.ts
609
- /**
610
- * This is a fork of Jose library's utility functions.
611
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/encrypt.ts
612
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/decrypt.ts
613
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
614
- */
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
+ }
615
639
  async function importCBCKeys(enc, cek, usage) {
616
640
  const keySize = Number.parseInt(enc.slice(1, 4), 10);
617
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), {
@@ -645,7 +669,7 @@ async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
645
669
  let encKey;
646
670
  if (cek instanceof Uint8Array) encKey = await crypto.subtle.importKey("raw", cek, "AES-GCM", false, ["encrypt"]);
647
671
  else {
648
- checkEncCryptoKey(cek, enc, "encrypt");
672
+ checkAESCryptoKey(cek, enc, "encrypt");
649
673
  encKey = cek;
650
674
  }
651
675
  const encrypted = new Uint8Array(await crypto.subtle.encrypt({
@@ -661,50 +685,6 @@ async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
661
685
  iv
662
686
  };
663
687
  }
664
- async function encrypt$1(enc, plaintext, cek, iv, aad) {
665
- if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) throw new TypeError("Key must be one of type: CryptoKey or Uint8Array");
666
- if (iv) checkIvLength(enc, iv);
667
- else iv = generateIV(enc);
668
- switch (enc) {
669
- case "A128CBC-HS256":
670
- case "A192CBC-HS384":
671
- case "A256CBC-HS512":
672
- if (cek instanceof Uint8Array) checkCEKLength(cek, Number.parseInt(enc.slice(-3), 10));
673
- return cbcEncrypt(enc, plaintext, cek, iv, aad);
674
- case "A128GCM":
675
- case "A192GCM":
676
- case "A256GCM":
677
- if (cek instanceof Uint8Array) checkCEKLength(cek, Number.parseInt(enc.slice(1, 4), 10));
678
- return gcmEncrypt(enc, plaintext, cek, iv, aad);
679
- default: throw new Error("Unsupported JWE Content Encryption Algorithm");
680
- }
681
- }
682
- /**
683
- * Decrypt fork
684
- */
685
- let _timingSafeKey;
686
- function getTimingSafeKey() {
687
- if (!_timingSafeKey) _timingSafeKey = crypto.subtle.generateKey({
688
- name: "HMAC",
689
- hash: "SHA-256"
690
- }, false, ["sign"]);
691
- return _timingSafeKey;
692
- }
693
- async function timingSafeEqual(a, b) {
694
- if (!(a instanceof Uint8Array)) throw new TypeError("First argument must be a buffer");
695
- if (!(b instanceof Uint8Array)) throw new TypeError("Second argument must be a buffer");
696
- const algorithm = {
697
- name: "HMAC",
698
- hash: "SHA-256"
699
- };
700
- const key = await getTimingSafeKey();
701
- const aHmac = new Uint8Array(await crypto.subtle.sign(algorithm, key, a));
702
- const bHmac = new Uint8Array(await crypto.subtle.sign(algorithm, key, b));
703
- let out = 0;
704
- let i = -1;
705
- while (++i < 32) out |= aHmac[i] ^ bHmac[i];
706
- return out === 0;
707
- }
708
688
  async function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) {
709
689
  if (!(cek instanceof Uint8Array)) throw new TypeError("CBC key must be of type Uint8Array");
710
690
  const { encKey, macKey, keySize } = await importCBCKeys(enc, cek, "decrypt");
@@ -729,7 +709,7 @@ async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) {
729
709
  let encKey;
730
710
  if (cek instanceof Uint8Array) encKey = await crypto.subtle.importKey("raw", cek, "AES-GCM", false, ["decrypt"]);
731
711
  else {
732
- checkEncCryptoKey(cek, enc, "decrypt");
712
+ checkAESCryptoKey(cek, enc, "decrypt");
733
713
  encKey = cek;
734
714
  }
735
715
  try {
@@ -743,27 +723,62 @@ async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) {
743
723
  throw new Error("JWE Decryption Failed");
744
724
  }
745
725
  }
746
- async function decrypt$1(enc, cek, ciphertext, iv, tag, aad) {
747
- if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) throw new TypeError("Key must be one of type: CryptoKey or Uint8Array");
748
- if (!iv) throw new Error("JWE Initialization Vector missing");
749
- if (!tag) throw new Error("JWE Authentication Tag missing");
750
- checkIvLength(enc, iv);
751
- switch (enc) {
752
- case "A128CBC-HS256":
753
- case "A192CBC-HS384":
754
- case "A256CBC-HS512":
755
- if (cek instanceof Uint8Array) checkCEKLength(cek, Number.parseInt(enc.slice(-3), 10));
756
- return cbcDecrypt(enc, cek, ciphertext, iv, tag, aad);
757
- case "A128GCM":
758
- case "A192GCM":
759
- case "A256GCM":
760
- if (cek instanceof Uint8Array) checkCEKLength(cek, Number.parseInt(enc.slice(1, 4), 10));
761
- return gcmDecrypt(enc, cek, ciphertext, iv, tag, aad);
762
- default: throw new Error("Unsupported JWE Content Encryption Algorithm");
763
- }
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;
764
765
  }
765
766
  //#endregion
766
- //#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
+ }
767
782
  function subtleMapping(jwk) {
768
783
  let algorithm;
769
784
  let keyUsages;
@@ -861,72 +876,47 @@ function subtleMapping(jwk) {
861
876
  keyUsages
862
877
  };
863
878
  }
864
- async function jwkTokey(jwk) {
865
- if (!jwk.alg) throw new TypeError("\"alg\" argument is required when \"jwk.alg\" is not present");
866
- const { algorithm, keyUsages } = subtleMapping(jwk);
867
- return crypto.subtle.importKey("jwk", jwk, algorithm, jwk.ext ?? (jwk.d ? false : true), jwk.key_ops ?? keyUsages);
868
- }
869
- //#endregion
870
- //#region src/core/jose/key_to_jwk.ts
871
- async function keyToJWK(key) {
872
- if (key instanceof Uint8Array) return {
873
- kty: "oct",
874
- k: base64UrlEncode(key)
875
- };
876
- if (!isCryptoKey(key)) throw new TypeError("Key must be one of type: CryptoKey or Uint8Array");
877
- if (!key.extractable) throw new TypeError("non-extractable CryptoKey cannot be exported as a JWK");
878
- return await crypto.subtle.exportKey("jwk", key);
879
- }
880
879
  //#endregion
881
- //#region src/core/jose/rsaes.ts
880
+ //#region src/core/_crypto/_rsa.ts
882
881
  async function encryptRSAES(alg, key, cek) {
883
- checkEncCryptoKey(key, alg, "encrypt");
884
- checkKeyLength(alg, key);
882
+ checkRSACryptoKey(key, alg, "encrypt");
883
+ checkRSAModulusLength(alg, key);
885
884
  return new Uint8Array(await crypto.subtle.encrypt("RSA-OAEP", key, cek));
886
885
  }
887
886
  async function decryptRSAES(alg, key, encryptedKey) {
888
- checkEncCryptoKey(key, alg, "decrypt");
889
- checkKeyLength(alg, key);
887
+ checkRSACryptoKey(key, alg, "decrypt");
888
+ checkRSAModulusLength(alg, key);
890
889
  return new Uint8Array(await crypto.subtle.decrypt("RSA-OAEP", key, encryptedKey));
891
890
  }
892
- //#endregion
893
- //#region src/core/jose/aesgcmkw.ts
894
- /**
895
- * This is a fork of Jose library's utility functions.
896
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/aesgcmkw.ts
897
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
898
- */
899
- async function encryptIV(alg, key, plaintext, iv) {
900
- const wrapped = await encrypt$1(alg.slice(0, 7), plaintext, key, iv, new Uint8Array(0));
901
- return {
902
- encryptedKey: wrapped.ciphertext,
903
- iv: base64UrlEncode(wrapped.iv),
904
- tag: base64UrlEncode(wrapped.tag)
905
- };
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);
906
896
  }
907
- async function decryptIV(alg, key, ciphertext, iv, tag) {
908
- 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
+ }
909
902
  }
910
- //#endregion
911
- //#region src/core/jose/ecdhes.ts
912
- function lengthAndInput(input) {
913
- 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}`);
914
905
  }
915
- async function concatKdf(secret, bits, value) {
916
- const iterations = Math.ceil((bits >> 3) / 32);
917
- const res = new Uint8Array(iterations * 32);
918
- for (let iter = 0; iter < iterations; iter++) {
919
- const buf = new Uint8Array(4 + secret.length + value.length);
920
- buf.set(uint32be(iter + 1));
921
- buf.set(secret, 4);
922
- buf.set(value, 4 + secret.length);
923
- res.set(await digest("sha256", buf), iter * 32);
924
- }
925
- 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}.`);
926
914
  }
915
+ //#endregion
916
+ //#region src/core/_crypto/_ecdh.ts
927
917
  async function deriveECDHESKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) {
928
- checkEncCryptoKey(publicKey, "ECDH");
929
- checkEncCryptoKey(privateKey, "ECDH", "deriveBits");
918
+ checkECDHCryptoKey(publicKey, "ECDH");
919
+ checkECDHCryptoKey(privateKey, "ECDH", "deriveBits");
930
920
  const value = concatUint8Arrays(lengthAndInput(textEncoder.encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength));
931
921
  const length = publicKey.algorithm.name === "X25519" ? 256 : Math.ceil(Number.parseInt(publicKey.algorithm.namedCurve.slice(-3), 10) / 8) << 3;
932
922
  return concatKdf(new Uint8Array(await crypto.subtle.deriveBits({
@@ -934,7 +924,7 @@ async function deriveECDHESKey(publicKey, privateKey, algorithm, keyLength, apu
934
924
  public: publicKey
935
925
  }, privateKey, length)), keyLength, value);
936
926
  }
937
- function allowed(key) {
927
+ function isECDHKeyAllowed(key) {
938
928
  switch (key.algorithm.namedCurve) {
939
929
  case "P-256":
940
930
  case "P-384":
@@ -942,38 +932,65 @@ function allowed(key) {
942
932
  default: return key.algorithm.name === "X25519";
943
933
  }
944
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
+ }
945
964
  async function digest(algorithm, data) {
946
- const subtleDigest = `SHA-${algorithm.slice(-3)}`;
947
- 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;
948
980
  }
949
981
  //#endregion
950
- //#region src/core/jose/pbes2kw.ts
951
- /**
952
- * This is a fork of Jose library's utility functions.
953
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/pbes2kw.ts
954
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
955
- */
956
- async function wrap(alg, key, cek, p2c, p2s) {
957
- if (p2c && p2s) {
958
- const derived = await deriveKey(p2s, alg, p2c, key);
959
- return {
960
- encryptedKey: await _wrap(alg.slice(-6), derived, cek),
961
- p2c,
962
- p2s: base64UrlEncode(p2s)
963
- };
964
- } else return { encryptedKey: await _wrap(alg, key, cek) };
965
- }
966
- async function unwrap(alg, key, encryptedKey, p2c, p2s) {
967
- if (p2c && p2s) {
968
- const derived = await deriveKey(p2s, alg, p2c, key);
969
- return _unwrap(alg.slice(-6), derived, encryptedKey);
970
- } else return _unwrap(alg, key, encryptedKey);
971
- }
972
- function getCryptoKey(key, alg, options = {}) {
973
- const { importAlg = "PBKDF2", extractable = false, usage = "deriveBits" } = options;
974
- if (key instanceof Uint8Array) return crypto.subtle.importKey("raw", key, importAlg, extractable, [usage]);
975
- checkEncCryptoKey(key, alg, usage);
976
- 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);
977
994
  }
978
995
  async function deriveKey(p2s, alg, p2c, key) {
979
996
  if (!(p2s instanceof Uint8Array) || p2s.length < 8) throw new Error("PBES2 Salt Input must be 8 or more octets");
@@ -986,92 +1003,44 @@ async function deriveKey(p2s, alg, p2c, key) {
986
1003
  name: "PBKDF2",
987
1004
  salt
988
1005
  };
989
- const cryptoKey = await getCryptoKey(key, alg);
1006
+ const cryptoKey = await getPBKDF2CryptoKey(key, alg);
990
1007
  return new Uint8Array(await crypto.subtle.deriveBits(subtleAlg, cryptoKey, keylen));
991
1008
  }
992
- /**
993
- * Fork from: https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/aeskw.ts
994
- */
995
- function checkKeySize(key, alg) {
996
- if (key.algorithm.length !== Number.parseInt(alg.slice(1, 4), 10)) throw new TypeError(`Invalid key size for alg: ${alg}`);
997
- }
998
- async function _wrap(alg, key, cek) {
999
- const cryptoKey = await getCryptoKey(key, alg, {
1000
- importAlg: "AES-KW",
1001
- extractable: true,
1002
- usage: "wrapKey"
1003
- });
1004
- checkKeySize(cryptoKey, alg);
1005
- const cryptoKeyCek = await crypto.subtle.importKey("raw", cek, {
1006
- hash: "SHA-256",
1007
- name: "HMAC"
1008
- }, true, ["sign"]);
1009
- return new Uint8Array(await crypto.subtle.wrapKey("raw", cryptoKeyCek, cryptoKey, "AES-KW"));
1010
- }
1011
- async function _unwrap(alg, key, encryptedKey) {
1012
- const cryptoKey = await getCryptoKey(key, alg, {
1013
- importAlg: "AES-KW",
1014
- extractable: true,
1015
- usage: "unwrapKey"
1016
- });
1017
- checkKeySize(cryptoKey, alg);
1018
- const cryptoKeyCek = await crypto.subtle.unwrapKey("raw", encryptedKey, cryptoKey, "AES-KW", {
1019
- hash: "SHA-256",
1020
- name: "HMAC"
1021
- }, true, ["sign"]);
1022
- 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);
1023
1012
  }
1024
- //#endregion
1025
- //#region src/core/jose/encrypt_key.ts
1026
- const PUBLIC_EPK_FIELDS = new Set([
1027
- "kty",
1028
- "crv",
1029
- "x",
1030
- "y",
1031
- "kid",
1032
- "use",
1033
- "key_ops",
1034
- "alg",
1035
- "x5c",
1036
- "x5t",
1037
- "x5t#S256",
1038
- "x5u"
1039
- ]);
1040
- function toPublicEpkJwk(jwk) {
1041
- const filtered = {};
1042
- for (const [key, value] of Object.entries(jwk)) if (value !== void 0 && PUBLIC_EPK_FIELDS.has(key)) filtered[key] = value;
1043
- const { kty, crv, x, y } = filtered;
1044
- if (typeof kty !== "string") throw new TypeError("ECDH-ES ephemeral key JWK must include the \"kty\" parameter.");
1045
- if (typeof crv !== "string") throw new TypeError("ECDH-ES ephemeral key JWK must include the \"crv\" parameter.");
1046
- if (typeof x !== "string") throw new TypeError("ECDH-ES ephemeral key JWK must include the \"x\" coordinate.");
1047
- if (kty === "EC" && typeof y !== "string") throw new TypeError("ECDH-ES EC ephemeral key JWK must include both \"x\" and \"y\" coordinates.");
1048
- const publicJwk = {
1049
- ...filtered,
1050
- kty,
1051
- crv,
1052
- x
1053
- };
1054
- if (typeof y === "string") publicJwk.y = y;
1055
- else delete publicJwk.y;
1056
- 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}`);
1057
1015
  }
1058
- async function ensurePrivateKey(alg, value) {
1059
- const normalized = await normalizeKey(value, alg);
1060
- if (!(normalized instanceof CryptoKey)) throw new TypeError("ECDH-ES ephemeral private key must be a CryptoKey or convertible JWK.");
1061
- if (normalized.type !== "private") throw new TypeError("ECDH-ES ephemeral private key must have type \"private\".");
1062
- return normalized;
1016
+ function isAlgorithm(algorithm, name) {
1017
+ return algorithm.name === name;
1063
1018
  }
1064
- async function exportPublicJwkFrom(alg, value) {
1065
- if (isCryptoKey(value)) try {
1066
- return toPublicEpkJwk(await keyToJWK(value));
1067
- } catch (error_) {
1068
- throw new TypeError("ECDH-ES ephemeral CryptoKey must be extractable to export as JWK.", { cause: error_ instanceof Error ? error_ : void 0 });
1069
- }
1070
- if (isJWK(value) && isAsymmetricJWK(value) && value.kty === "EC") return toPublicEpkJwk(value);
1071
- const normalized = await normalizeKey(value, alg);
1072
- if (!(normalized instanceof CryptoKey)) throw new TypeError("ECDH-ES ephemeral public key must be a CryptoKey or convertible JWK.");
1073
- 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;
1074
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
+ ]);
1075
1044
  async function encryptKey(alg, enc, key, providedCek, providedParameters = {}) {
1076
1045
  let encryptedKey;
1077
1046
  let parameters = {};
@@ -1085,7 +1054,7 @@ async function encryptKey(alg, enc, key, providedCek, providedParameters = {}) {
1085
1054
  case "ECDH-ES+A192KW":
1086
1055
  case "ECDH-ES+A256KW": {
1087
1056
  assertCryptoKey(key);
1088
- 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");
1089
1058
  const { apu, apv, epk: providedEpk, epkPrivateKey } = providedParameters;
1090
1059
  if (providedEpk && !epkPrivateKey) throw new TypeError("ECDH-ES custom ephemeral key material must include the matching private key.");
1091
1060
  let ephemeralPrivateKey;
@@ -1108,7 +1077,7 @@ async function encryptKey(alg, enc, key, providedCek, providedParameters = {}) {
1108
1077
  break;
1109
1078
  }
1110
1079
  cek = providedCek || generateCEK(enc);
1111
- encryptedKey = (await wrap(alg.slice(-6), sharedSecret, cek)).encryptedKey;
1080
+ encryptedKey = await aesKwWrap(alg.slice(-6), sharedSecret, cek);
1112
1081
  break;
1113
1082
  }
1114
1083
  case "RSA-OAEP":
@@ -1124,21 +1093,21 @@ async function encryptKey(alg, enc, key, providedCek, providedParameters = {}) {
1124
1093
  case "PBES2-HS512+A256KW": {
1125
1094
  cek = providedCek || generateCEK(enc);
1126
1095
  const { p2c, p2s } = providedParameters;
1127
- ({encryptedKey, ...parameters} = await wrap(alg, key, cek, p2c, p2s));
1096
+ ({encryptedKey, ...parameters} = await pbes2Wrap(alg, key, cek, p2c, p2s));
1128
1097
  break;
1129
1098
  }
1130
1099
  case "A128KW":
1131
1100
  case "A192KW":
1132
1101
  case "A256KW":
1133
1102
  cek = providedCek || generateCEK(enc);
1134
- encryptedKey = (await wrap(alg, key, cek)).encryptedKey;
1103
+ encryptedKey = await aesKwWrap(alg, key, cek);
1135
1104
  break;
1136
1105
  case "A128GCMKW":
1137
1106
  case "A192GCMKW":
1138
1107
  case "A256GCMKW": {
1139
1108
  cek = providedCek || generateCEK(enc);
1140
1109
  const { iv } = providedParameters;
1141
- ({encryptedKey, ...parameters} = await encryptIV(alg, key, cek, iv));
1110
+ ({encryptedKey, ...parameters} = await gcmkwEncrypt(alg, key, cek, iv));
1142
1111
  break;
1143
1112
  }
1144
1113
  default: throw new Error("Invalid or unsupported \"alg\" (JWE Algorithm) header value");
@@ -1161,67 +1130,72 @@ async function normalizeKey(key, alg) {
1161
1130
  }
1162
1131
  throw new Error("unreachable");
1163
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
+ }
1164
1168
  //#endregion
1165
- //#region src/core/jose/asn1.ts
1166
- /**
1167
- * This is a fork of Jose library's utility functions.
1168
- * @source https://github.com/panva/jose/tree/69b7960c67e05be55fa2ec31c74b987696c20c60/src/lib/asn1.ts
1169
- * @license MIT https://github.com/panva/jose/blob/69b7960c67e05be55fa2ec31c74b987696c20c60/LICENSE.md
1170
- */
1171
- const formatPEM = (b64, descriptor) => {
1172
- return `-----BEGIN ${descriptor}-----\n${(b64.match(/.{1,64}/g) || []).join("\n")}\n-----END ${descriptor}-----`;
1173
- };
1174
- const genericExport = async (keyType, keyFormat, key) => {
1175
- if (!isCryptoKey(key)) throw new TypeError(`Key must be ${key} of type CryptoKey`);
1176
- if (!key.extractable) throw new TypeError("CryptoKey is not extractable");
1177
- if (key.type !== keyType) throw new TypeError(`key is not a ${keyType} key`);
1178
- return formatPEM(base64Encode(new Uint8Array(await crypto.subtle.exportKey(keyFormat, key))), `${keyType.toUpperCase()} KEY`);
1179
- };
1169
+ //#region src/core/_crypto/_pem.ts
1180
1170
  const toSPKI = (key) => {
1181
1171
  return genericExport("public", "spki", key);
1182
1172
  };
1183
1173
  const toPKCS8 = (key) => {
1184
1174
  return genericExport("private", "pkcs8", key);
1185
1175
  };
1186
- const findOid = (keyData, oid, from = 0) => {
1187
- if (from === 0) {
1188
- oid.unshift(oid.length);
1189
- oid.unshift(6);
1190
- }
1191
- const i = keyData.indexOf(oid[0], from);
1192
- if (i === -1) return false;
1193
- const sub = keyData.subarray(i, i + oid.length);
1194
- if (sub.length !== oid.length) return false;
1195
- 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);
1196
1178
  };
1197
- const getNamedCurve = (keyData) => {
1198
- switch (true) {
1199
- case findOid(keyData, [
1200
- 42,
1201
- 134,
1202
- 72,
1203
- 206,
1204
- 61,
1205
- 3,
1206
- 1,
1207
- 7
1208
- ]): return "P-256";
1209
- case findOid(keyData, [
1210
- 43,
1211
- 129,
1212
- 4,
1213
- 0,
1214
- 34
1215
- ]): return "P-384";
1216
- case findOid(keyData, [
1217
- 43,
1218
- 129,
1219
- 4,
1220
- 0,
1221
- 35
1222
- ]): return "P-521";
1223
- 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_ });
1224
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`);
1225
1199
  };
1226
1200
  const genericImport = async (replace, keyFormat, pem, alg, options) => {
1227
1201
  let algorithm;
@@ -1299,13 +1273,47 @@ const genericImport = async (replace, keyFormat, pem, alg, options) => {
1299
1273
  }
1300
1274
  return crypto.subtle.importKey(keyFormat, keyData, algorithm, options?.extractable ?? (isPublic ? true : false), keyUsages);
1301
1275
  };
1302
- const fromPKCS8 = (pem, alg, options) => {
1303
- 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);
1304
1286
  };
1305
- const fromSPKI = (pem, alg, options) => {
1306
- 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
+ }
1307
1315
  };
1308
- function getElement(seq) {
1316
+ const getElement = (seq) => {
1309
1317
  const result = [];
1310
1318
  let next = 0;
1311
1319
  while (next < seq.length) {
@@ -1314,8 +1322,8 @@ function getElement(seq) {
1314
1322
  next += nextPart.byteLength;
1315
1323
  }
1316
1324
  return result;
1317
- }
1318
- function parseElement(bytes) {
1325
+ };
1326
+ const parseElement = (bytes) => {
1319
1327
  let position = 0;
1320
1328
  let tag = bytes[0] & 31;
1321
1329
  position++;
@@ -1359,13 +1367,13 @@ function parseElement(bytes) {
1359
1367
  contents: bytes.subarray(position, byteLength),
1360
1368
  raw: bytes.subarray(0, byteLength)
1361
1369
  };
1362
- }
1363
- function spkiFromX509(buf) {
1370
+ };
1371
+ const spkiFromX509 = (buf) => {
1364
1372
  const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
1365
1373
  return base64Encode(tbsCertificate[tbsCertificate[0].raw[0] === 160 ? 6 : 5].raw);
1366
- }
1374
+ };
1367
1375
  let createPublicKey;
1368
- function getSPKI(x509) {
1376
+ const getSPKI = (x509) => {
1369
1377
  try {
1370
1378
  createPublicKey ??= globalThis.process?.getBuiltinModule?.("node:crypto")?.createPublicKey;
1371
1379
  } catch {
@@ -1378,15 +1386,6 @@ function getSPKI(x509) {
1378
1386
  });
1379
1387
  } catch {}
1380
1388
  return formatPEM(spkiFromX509(base64Decode(x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, ""), false)), "PUBLIC KEY");
1381
- }
1382
- const fromX509 = (pem, alg, options) => {
1383
- let spki;
1384
- try {
1385
- spki = getSPKI(pem);
1386
- } catch (error_) {
1387
- throw new TypeError("Failed to parse the X.509 certificate", { cause: error_ });
1388
- }
1389
- return fromSPKI(spki, alg, options);
1390
1389
  };
1391
1390
  //#endregion
1392
1391
  //#region src/core/jwk.ts
@@ -1400,25 +1399,13 @@ async function generateKey(alg, options = {}) {
1400
1399
  }
1401
1400
  const { algorithm, keyUsages } = getGenerateKeyParams(alg, options);
1402
1401
  const key = await crypto.subtle.generateKey(algorithm, defaultExtractable, keyUsages);
1403
- if (exportToJWK) {
1404
- const { alg: _a, kty: _k, key_ops: _ko, ext: _e, ...additionalKeyParams } = typeof options.toJWK === "object" ? options.toJWK : {};
1405
- if (key instanceof CryptoKey) return exportKey(key, {
1406
- ...additionalKeyParams,
1407
- alg
1408
- });
1409
- else {
1410
- const [publicKey, privateKey] = await Promise.all([exportKey(key.publicKey, {
1411
- ...additionalKeyParams,
1412
- alg
1413
- }), exportKey(key.privateKey, {
1414
- ...additionalKeyParams,
1415
- alg
1416
- })]);
1417
- return {
1418
- privateKey,
1419
- publicKey
1420
- };
1421
- }
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
+ };
1422
1409
  }
1423
1410
  return key;
1424
1411
  }
@@ -1431,14 +1418,31 @@ async function generateKey(alg, options = {}) {
1431
1418
  * @returns A Promise resolving to the generated JWK or JWK pair representation.
1432
1419
  */
1433
1420
  async function generateJWK(alg, jwkParams, options = {}) {
1434
- const { toJWK: _, ...opts } = options;
1435
- return generateKey(alg, {
1436
- ...opts,
1437
- toJWK: {
1438
- kid: typeof jwkParams?.kid === "string" ? jwkParams.kid : crypto.randomUUID(),
1439
- ...jwkParams
1440
- }
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
1441
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
+ };
1442
1446
  }
1443
1447
  async function deriveKeyFromPassword(password, alg, options) {
1444
1448
  const { salt, iterations, extractable, keyUsage } = options;
@@ -1450,16 +1454,11 @@ async function deriveKeyFromPassword(password, alg, options) {
1450
1454
  const finalUsages = keyUsage ?? ["wrapKey", "unwrapKey"];
1451
1455
  const finalExtractable = exportToJWK ? true : extractable === true;
1452
1456
  const derivedKey = await crypto.subtle.importKey("raw", derivedBytes, { name: "AES-KW" }, finalExtractable, finalUsages);
1453
- if (exportToJWK) {
1454
- const jwk = await keyToJWK(derivedKey);
1455
- const { alg: _a, kty: _k, key_ops: _ko, ext: _e, ...additionalKeyParams } = typeof options.toJWK === "object" ? options.toJWK : {};
1456
- return {
1457
- ...additionalKeyParams,
1458
- ...jwk,
1459
- alg: wrappingAlg,
1460
- kty: "oct"
1461
- };
1462
- }
1457
+ if (exportToJWK) return {
1458
+ ...await keyToJWK(derivedKey),
1459
+ alg: wrappingAlg,
1460
+ kty: "oct"
1461
+ };
1463
1462
  return derivedKey;
1464
1463
  }
1465
1464
  /**
@@ -1472,36 +1471,67 @@ async function deriveKeyFromPassword(password, alg, options) {
1472
1471
  * @returns A Promise resolving to the derived JWK (JWK_oct).
1473
1472
  */
1474
1473
  async function deriveJWKFromPassword(password, alg, options, jwkParams) {
1475
- const { toJWK: _, ...opts } = options;
1476
- return deriveKeyFromPassword(password, alg, {
1477
- ...opts,
1478
- toJWK: jwkParams || true
1474
+ const result = await deriveKeyFromPassword(password, alg, {
1475
+ ...options,
1476
+ toJWK: true
1479
1477
  });
1478
+ return jwkParams ? {
1479
+ ...jwkParams,
1480
+ ...result
1481
+ } : result;
1480
1482
  }
1481
1483
  /**
1482
- * 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.
1483
1495
  */
1484
- let _jwkCache;
1485
- 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;
1486
1510
  if (typeof key === "string") key = textEncoder.encode(key);
1487
1511
  if (key instanceof Uint8Array) return key;
1488
1512
  if (isCryptoKey(key)) return key;
1489
- if (isJWK(key)) if ("k" in key && typeof key.k === "string") return base64UrlDecode(key.k, false);
1490
- 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 {
1491
1521
  if (!key.alg && !alg) throw new TypeError("Algorithm must be provided when importing non-oct JWK");
1492
1522
  const effectiveAlg = key.alg || alg;
1493
- _jwkCache ||= /* @__PURE__ */ new WeakMap();
1494
- const cached = _jwkCache.get(key);
1495
- if (cached?.[effectiveAlg]) return cached[effectiveAlg];
1523
+ if (_activeCache) {
1524
+ const cached = _activeCache.get(key, effectiveAlg);
1525
+ if (cached) return cached;
1526
+ }
1496
1527
  const cryptoKey = await jwkTokey(key.alg ? key : {
1497
1528
  ...key,
1498
1529
  alg
1499
1530
  });
1500
- if (!cached) _jwkCache.set(key, { [effectiveAlg]: cryptoKey });
1501
- else cached[effectiveAlg] = cryptoKey;
1531
+ if (_activeCache) _activeCache.set(key, effectiveAlg, cryptoKey);
1502
1532
  return cryptoKey;
1503
1533
  }
1504
- throw new Error("Invalid key type provided to importKey");
1534
+ throw new JWTError("Invalid key type provided to importKey", "ERR_JWK_INVALID");
1505
1535
  }
1506
1536
  /**
1507
1537
  * Exports a CryptoKey to a JWK (JSON Web Key) format.
@@ -1523,41 +1553,37 @@ async function resolveWrappingKey(alg, key) {
1523
1553
  if (typeof key === "string") throw new TypeError(`Key must be CryptoKey, JWK, or Uint8Array for ${alg}`);
1524
1554
  return importKey(key, alg);
1525
1555
  }
1526
- /**
1527
- * Unwraps a Content Encryption Key (CEK) using the specified algorithm and unwrapping key.
1528
- *
1529
- * @param alg The JWA key management algorithm (e.g., "A128KW", "RSA-OAEP", "PBES2-HS256+A128KW").
1530
- * @param wrappedKey The wrapped key (ciphertext) as Uint8Array.
1531
- * @param unwrappingKey The key used to unwrap the CEK (CryptoKey, JWK, or password string/Uint8Array for PBES2).
1532
- * @param options Additional options required by certain algorithms (e.g., iv, tag, p2s, p2c, epk).
1533
- * @returns A Promise resolving to the unwrapped key (CEK) as a CryptoKey or Uint8Array.
1534
- */
1535
1556
  async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
1536
- const { returnAs = true } = options;
1557
+ const returnRaw = options.format === "raw";
1537
1558
  const defaultExtractable = options.extractable !== false;
1538
1559
  const importedUnwrappingKey = isCryptoKey(unwrappingKey) ? unwrappingKey : await resolveWrappingKey(alg, unwrappingKey);
1539
1560
  let unwrappedCekBytes;
1540
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;
1541
1569
  case "A128KW":
1542
1570
  case "A192KW":
1543
1571
  case "A256KW":
1572
+ unwrappedCekBytes = await aesKwUnwrap(alg, importedUnwrappingKey, wrappedKey);
1573
+ break;
1544
1574
  case "PBES2-HS256+A128KW":
1545
1575
  case "PBES2-HS384+A192KW":
1546
1576
  case "PBES2-HS512+A256KW": {
1547
1577
  const { p2s, p2c } = options;
1548
- let p2sBytes;
1549
- if (alg.startsWith("PBES2")) {
1550
- if (!p2s || typeof p2c !== "number") throw new Error("PBES2 requires 'p2s' (salt) and 'p2c' (count) options");
1551
- p2sBytes = typeof p2s === "string" ? base64UrlDecode(p2s, false) : p2s;
1552
- }
1553
- 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);
1554
1580
  break;
1555
1581
  }
1556
1582
  case "A128GCMKW":
1557
1583
  case "A192GCMKW":
1558
1584
  case "A256GCMKW":
1559
1585
  if (!options.iv || !options.tag) throw new Error("AES-GCMKW requires 'iv' and 'tag' options for unwrapping");
1560
- 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);
1561
1587
  break;
1562
1588
  case "RSA-OAEP":
1563
1589
  case "RSA-OAEP-256":
@@ -1565,7 +1591,7 @@ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
1565
1591
  case "RSA-OAEP-512": {
1566
1592
  if (!(importedUnwrappingKey instanceof CryptoKey)) throw new TypeError("RSA-OAEP requires the unwrapping key to be provided as a CryptoKey");
1567
1593
  unwrappedCekBytes = await decryptRSAES(alg, importedUnwrappingKey, wrappedKey);
1568
- if (!returnAs) break;
1594
+ if (returnRaw) break;
1569
1595
  const inferredAlgorithm = options.unwrappedKeyAlgorithm || inferAesImportAlgorithm(options.enc, unwrappedCekBytes.length);
1570
1596
  if (!inferredAlgorithm) throw new Error("Unable to infer algorithm for RSA-OAEP unwrapped key. Provide \"unwrappedKeyAlgorithm\" in options.");
1571
1597
  const keyUsages = options.keyUsage || ["encrypt", "decrypt"];
@@ -1577,7 +1603,7 @@ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
1577
1603
  case "ECDH-ES+A256KW": {
1578
1604
  if (!options.epk) throw new Error("ECDH-ES requires 'epk' (Ephemeral Public Key) option");
1579
1605
  if (!(importedUnwrappingKey instanceof CryptoKey)) throw new TypeError("ECDH-ES requires the unwrapping key to be a CryptoKey");
1580
- 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");
1581
1607
  const epkCandidate = options.epk instanceof CryptoKey ? options.epk : await normalizeKey(options.epk, alg);
1582
1608
  if (!(epkCandidate instanceof CryptoKey)) throw new TypeError("Failed to normalize ECDH ephemeral public key");
1583
1609
  const apuBytes = typeof options.apu === "string" ? base64UrlDecode(options.apu, false) : options.apu ?? new Uint8Array(0);
@@ -1589,13 +1615,13 @@ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
1589
1615
  else {
1590
1616
  const kwAlg = alg.slice(-6);
1591
1617
  if (!(wrappedKey instanceof Uint8Array) || wrappedKey.length === 0) throw new Error("ECDH-ES key agreement with key wrapping requires an encrypted key");
1592
- unwrappedCekBytes = await unwrap(kwAlg, sharedSecret, wrappedKey);
1618
+ unwrappedCekBytes = await aesKwUnwrap(kwAlg, sharedSecret, wrappedKey);
1593
1619
  }
1594
1620
  break;
1595
1621
  }
1596
1622
  default: throw new Error(`Unsupported key unwrapping algorithm: ${alg}`);
1597
1623
  }
1598
- if (!returnAs) return unwrappedCekBytes;
1624
+ if (returnRaw) return unwrappedCekBytes;
1599
1625
  return await crypto.subtle.importKey("raw", unwrappedCekBytes, options.unwrappedKeyAlgorithm || { name: "AES-GCM" }, defaultExtractable, options.keyUsage || ["encrypt", "decrypt"]);
1600
1626
  }
1601
1627
  /**
@@ -1609,35 +1635,25 @@ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
1609
1635
  * @param jwkExtras Additional properties to merge into the resulting JWK.
1610
1636
  * @returns A Promise resolving to the imported key as a JWK.
1611
1637
  */
1612
- async function importJWKFromPEM(pem, pemType, alg, importOptions, jwkExtras) {
1638
+ async function importFromPEM(pem, pemType, alg, options) {
1639
+ const extractable = options?.extractable !== false;
1613
1640
  let cryptoKey;
1614
- const defaultExtractable = importOptions?.extractable !== false;
1615
1641
  switch (pemType) {
1616
1642
  case "pkcs8":
1617
- cryptoKey = await fromPKCS8(pem, alg, {
1618
- ...importOptions,
1619
- extractable: defaultExtractable
1620
- });
1643
+ cryptoKey = await fromPKCS8(pem, alg, { extractable });
1621
1644
  break;
1622
1645
  case "spki":
1623
- cryptoKey = await fromSPKI(pem, alg, {
1624
- ...importOptions,
1625
- extractable: defaultExtractable
1626
- });
1646
+ cryptoKey = await fromSPKI(pem, alg, { extractable });
1627
1647
  break;
1628
1648
  case "x509":
1629
- cryptoKey = await fromX509(pem, alg, {
1630
- ...importOptions,
1631
- extractable: defaultExtractable
1632
- });
1649
+ cryptoKey = await fromX509(pem, alg, { extractable });
1633
1650
  break;
1634
1651
  default: throw new TypeError(`Unsupported PEM type: ${pemType}`);
1635
1652
  }
1636
- const finalJWKExtras = {
1653
+ return exportKey(cryptoKey, {
1637
1654
  alg,
1638
- ...jwkExtras
1639
- };
1640
- return exportKey(cryptoKey, finalJWKExtras);
1655
+ ...options?.jwkParams
1656
+ });
1641
1657
  }
1642
1658
  /**
1643
1659
  * Exports a JWK to a PEM-encoded string.
@@ -1648,7 +1664,7 @@ async function importJWKFromPEM(pem, pemType, alg, importOptions, jwkExtras) {
1648
1664
  * required to correctly convert it to a CryptoKey first.
1649
1665
  * @returns A Promise resolving to the PEM-encoded key string.
1650
1666
  */
1651
- async function exportJWKToPEM(jwk, pemFormat, algForCryptoKeyImport) {
1667
+ async function exportToPEM(jwk, pemFormat, algForCryptoKeyImport) {
1652
1668
  if (jwk.kty === "oct") throw new TypeError("Octet (symmetric) JWKs (kty: 'oct') cannot be exported to PKCS8 or SPKI PEM formats.");
1653
1669
  const effectiveAlg = jwk.alg || algForCryptoKeyImport;
1654
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.");
@@ -1669,43 +1685,19 @@ async function exportJWKToPEM(jwk, pemFormat, algForCryptoKeyImport) {
1669
1685
  }
1670
1686
  }
1671
1687
  /**
1672
- * Retrieves a JWK (JSON Web Key) from a JWKSet based on a key ID (kid) or
1673
- * 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.
1674
1692
  *
1675
- * @param jwkSet The JWKSet to search within.
1676
- * @param kidOrProtectedHeader Either a string representing the 'kid' (Key ID)
1677
- * or an object representing the JOSE Protected Header. If an object is
1678
- * provided, it must contain a 'kid' property. It can optionally contain
1679
- * 'alg' (Algorithm) and 'kty' (Key Type) to further refine the search.
1680
- * @returns The matching JWK from the set.
1681
- * @throws TypeError if `jwkSet` is invalid or `kidOrProtectedHeader` is not a
1682
- * string or a valid header object.
1683
- * @throws Error if no key is found matching the provided criteria, or if the
1684
- * 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.
1685
1696
  */
1686
- function getJWKFromSet(jwkSet, kidOrProtectedHeader) {
1697
+ function getJWKsFromSet(jwkSet, filter) {
1687
1698
  if (!jwkSet || !isJWKSet(jwkSet)) throw new TypeError("Invalid JWK Set provided");
1688
- if (typeof kidOrProtectedHeader === "string") {
1689
- const kid = kidOrProtectedHeader;
1690
- const selectedKey = jwkSet.keys.find((k) => k.kid === kid);
1691
- if (!selectedKey) throw new Error(`No key found in JWK Set with kid "${kid}".`);
1692
- return selectedKey;
1693
- } else if (typeof kidOrProtectedHeader === "object") {
1694
- const { kid, alg, kty } = kidOrProtectedHeader;
1695
- 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.");
1696
- const selectedKey = jwkSet.keys.find((k) => {
1697
- return k.kid === kid && (!alg || k.alg === alg) && (!kty || k.kty === kty);
1698
- });
1699
- if (!selectedKey) {
1700
- let errorMessage = `No key found in JWK Set with kid "${kid}"`;
1701
- if (alg) errorMessage += ` and alg "${alg}"`;
1702
- if (kty) errorMessage += ` and kty "${kty}"`;
1703
- errorMessage += ".";
1704
- throw new Error(errorMessage);
1705
- }
1706
- return selectedKey;
1707
- }
1708
- 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);
1709
1701
  }
1710
1702
  function getGenerateKeyParams(alg, options) {
1711
1703
  let algorithm;
@@ -1866,20 +1858,23 @@ function inferAesImportAlgorithm(enc, cekLengthBytes) {
1866
1858
  //#endregion
1867
1859
  //#region src/core/jwe.ts
1868
1860
  async function encrypt(payload, key, options = {}) {
1869
- 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;
1870
1862
  let { alg, enc } = options;
1871
1863
  if (!alg) {
1872
1864
  if (typeof key === "string" || key instanceof Uint8Array) alg = "PBES2-HS256+A128KW";
1873
1865
  else if (isJWK(key)) alg = key.alg === "A128GCM" || key.alg === "A192GCM" || key.alg === "A256GCM" ? `${key.alg}KW` : key.alg;
1874
1866
  }
1875
1867
  if (!alg) throw new TypeError("JWE \"alg\" (Key Management Algorithm) must be provided in options or inferable from the key");
1876
- 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";
1877
1872
  const jweKeyManagementParams = {};
1878
1873
  if (keyManagementIV) jweKeyManagementParams.iv = keyManagementIV;
1879
1874
  if (p2s) jweKeyManagementParams.p2s = p2s;
1880
1875
  else if (alg?.startsWith("PBES2")) jweKeyManagementParams.p2s = randomBytes(16);
1881
1876
  if (p2c) jweKeyManagementParams.p2c = p2c;
1882
- else if (alg?.startsWith("PBES2")) jweKeyManagementParams.p2c = 2048;
1877
+ else if (alg?.startsWith("PBES2")) jweKeyManagementParams.p2c = 6e5;
1883
1878
  if (ecdh?.partyUInfo) jweKeyManagementParams.apu = ecdh.partyUInfo;
1884
1879
  if (ecdh?.partyVInfo) jweKeyManagementParams.apv = ecdh.partyVInfo;
1885
1880
  if (ecdh?.ephemeralKey) {
@@ -1889,21 +1884,14 @@ async function encrypt(payload, key, options = {}) {
1889
1884
  }
1890
1885
  const wrappingKeyMaterial = await importKey(key, alg);
1891
1886
  const { cek: finalCek, encryptedKey: jweEncryptedKey, parameters: keyManagementHeaderParams } = await encryptKey(alg, enc, wrappingKeyMaterial, providedCek, jweKeyManagementParams);
1892
- const jweProtectedHeader = {
1893
- ...additionalProtectedHeader,
1894
- ...keyManagementHeaderParams,
1895
- alg,
1896
- enc
1897
- };
1898
- if (isJWK(key) && key.kid) jweProtectedHeader.kid = key.kid;
1899
- const protectedHeader = applyTypCtyDefaults(jweProtectedHeader, payload);
1887
+ const protectedHeader = _buildJWEHeader(alg, enc, key, userHeader, keyManagementHeaderParams, payload);
1900
1888
  const plaintextBytes = getPlaintextBytes(computeJwtTimeClaims(payload, protectedHeader.typ, options.expiresIn, options.currentDate) || payload);
1901
1889
  const protectedHeaderEncoded = base64UrlEncode(JSON.stringify(protectedHeader));
1902
1890
  const aadBytes = textEncoder.encode(protectedHeaderEncoded);
1903
1891
  const contentIVBytes = providedContentIV ?? generateIV(enc);
1904
1892
  const { ciphertext: contentCiphertext, tag: contentAuthTag, iv: actualContentIV } = await encrypt$1(enc, plaintextBytes, finalCek, contentIVBytes, aadBytes);
1905
- if (!actualContentIV) throw new Error("Content encryption IV was not generated or returned.");
1906
- 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");
1907
1895
  return [
1908
1896
  protectedHeaderEncoded,
1909
1897
  jweEncryptedKey ? base64UrlEncode(jweEncryptedKey) : "",
@@ -1914,28 +1902,28 @@ async function encrypt(payload, key, options = {}) {
1914
1902
  }
1915
1903
  async function decrypt(jwe, key, options = {}) {
1916
1904
  const parts = jwe.split(".");
1917
- 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");
1918
1906
  const [protectedHeaderEncoded, encryptedKeyEncoded, ivEncoded, ciphertextEncoded, authTagEncoded] = parts;
1919
1907
  let protectedHeader;
1920
1908
  try {
1921
1909
  const protectedHeaderJson = base64UrlDecode(protectedHeaderEncoded);
1922
1910
  protectedHeader = sanitizeObject(JSON.parse(protectedHeaderJson));
1923
1911
  } catch {
1924
- 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");
1925
1913
  }
1926
- 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");
1927
1915
  const alg = protectedHeader.alg;
1928
1916
  const enc = protectedHeader.enc;
1929
- if (options?.algorithms && !options.algorithms.includes(alg)) throw new Error(`Key management algorithm not allowed: ${alg}`);
1930
- if (options?.encryptionAlgorithms && !options.encryptionAlgorithms.includes(enc)) throw new Error(`Content encryption algorithm not allowed: ${enc}`);
1931
- 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;
1932
1920
  const encryptedKeyBytes = base64UrlDecode(encryptedKeyEncoded, false);
1933
1921
  const contentIVBytes = base64UrlDecode(ivEncoded, false);
1934
1922
  const contentAuthTagBytes = base64UrlDecode(authTagEncoded, false);
1935
1923
  const ciphertextBytes = base64UrlDecode(ciphertextEncoded, false);
1936
1924
  const unwrapKeyOpts = {
1937
1925
  enc,
1938
- returnAs: enc.includes("GCM"),
1926
+ format: enc.includes("GCM") ? "cryptokey" : "raw",
1939
1927
  keyUsage: options?.keyUsage || ["decrypt"]
1940
1928
  };
1941
1929
  if (protectedHeader.iv) unwrapKeyOpts.iv = protectedHeader.iv;
@@ -1947,11 +1935,34 @@ async function decrypt(jwe, key, options = {}) {
1947
1935
  if (protectedHeader.apv) unwrapKeyOpts.apv = protectedHeader.apv;
1948
1936
  if (options?.unwrappedKeyAlgorithm) unwrapKeyOpts.unwrappedKeyAlgorithm = options.unwrappedKeyAlgorithm;
1949
1937
  if (options?.extractable) unwrapKeyOpts.extractable = options.extractable;
1950
- const cek = await unwrapKey(alg, encryptedKeyBytes, unwrappingKey, unwrapKeyOpts);
1951
1938
  const aadBytes = textEncoder.encode(protectedHeaderEncoded);
1952
- const payload = decodePayloadFromBytes(await decrypt$1(enc, cek, ciphertextBytes, contentIVBytes, contentAuthTagBytes, aadBytes), protectedHeader, options?.forceUint8Array);
1953
- if (protectedHeader.crit || options?.critical?.length || options?.requiredHeaders?.length) validateCriticalHeadersJWE(protectedHeader, [...options?.critical || [], ...options?.requiredHeaders || []]);
1954
- 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);
1955
1966
  const result = {
1956
1967
  payload,
1957
1968
  protectedHeader
@@ -1987,24 +1998,30 @@ function parseEphemeralKey(ephemeralKey) {
1987
1998
  epkPrivateKey
1988
1999
  };
1989
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
+ }
1990
2015
  //#endregion
1991
2016
  //#region src/core/jws.ts
1992
2017
  async function sign(payload, key, options = {}) {
1993
- const { protectedHeader: additionalProtectedHeader } = options;
1994
- const safeAdditionalHeader = sanitizeObject(additionalProtectedHeader);
2018
+ const { protectedHeader: userHeader } = options;
1995
2019
  let { alg } = options;
1996
2020
  if (!alg) if (isJWK(key) && key.alg) alg = key.alg;
1997
2021
  else throw new TypeError("JWS \"alg\" (Algorithm) must be provided in options");
1998
- validateKeyLength(key, alg);
1999
- const signingKey = await importKey(key, alg);
2000
- const protectedHeader = {
2001
- ...safeAdditionalHeader,
2002
- ...isJWK(key) && key.kid ? { kid: key.kid } : {},
2003
- alg,
2004
- typ: safeAdditionalHeader?.typ
2005
- };
2006
- applyTypCtyDefaults(protectedHeader, payload);
2007
- 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);
2008
2025
  const computedPayload = computeJwtTimeClaims(payload, protectedHeader.typ, options.expiresIn, options.currentDate);
2009
2026
  const protectedHeaderEncoded = base64UrlEncode(JSON.stringify(protectedHeader));
2010
2027
  let payloadBytes;
@@ -2018,53 +2035,82 @@ async function sign(payload, key, options = {}) {
2018
2035
  }
2019
2036
  async function verify(jws, key, options = {}) {
2020
2037
  const parts = jws.split(".");
2021
- 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");
2022
2039
  const [protectedHeaderEncoded, payloadEncoded, signatureEncoded] = parts;
2023
2040
  let protectedHeader;
2024
2041
  try {
2025
2042
  const protectedHeaderString = base64UrlDecode(protectedHeaderEncoded);
2026
2043
  protectedHeader = sanitizeObject(JSON.parse(protectedHeaderString));
2027
2044
  } catch {
2028
- 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");
2029
2046
  }
2030
- 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");
2031
2048
  const alg = protectedHeader.alg;
2032
- if (options.algorithms && !options.algorithms.includes(alg)) throw new Error(`Algorithm not allowed: ${alg}`);
2033
- 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");
2034
2051
  let signatureBytes;
2035
2052
  try {
2036
2053
  signatureBytes = base64UrlDecode(signatureEncoded, false);
2037
2054
  } catch {
2038
- throw new Error("Invalid JWS: Signature could not be decoded.");
2055
+ throw new JWTError("Invalid JWS: Signature could not be decoded.", "ERR_JWS_INVALID");
2039
2056
  }
2040
2057
  const keyInput = typeof key === "function" ? await key(protectedHeader, jws) : key;
2041
- const verificationKey = await importKey(isJWKSet(keyInput) ? getJWKFromSet(keyInput, protectedHeader) : keyInput, alg);
2042
- const signingInputString = `${protectedHeaderEncoded}.${payloadEncoded}`;
2043
- const signingInputBytes = textEncoder.encode(signingInputString);
2044
- 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");
2045
2071
  const useB64 = protectedHeader.b64 !== false;
2046
2072
  let payload;
2047
2073
  try {
2048
2074
  payload = useB64 ? decodePayloadFromB64UrlSegment(payloadEncoded, protectedHeader, options.forceUint8Array) : options.forceUint8Array ? textEncoder.encode(payloadEncoded) : payloadEncoded;
2049
2075
  } catch (error_) {
2050
- 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");
2051
2077
  }
2052
- validateCriticalHeadersJWS(protectedHeader, [...options.critical || [], ...options.requiredHeaders || []]);
2053
- 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);
2054
2080
  return {
2055
2081
  payload,
2056
2082
  protectedHeader
2057
2083
  };
2058
2084
  }
2059
- function validateKeyLength(key, alg) {
2060
- if (!alg || isJWK(key)) return;
2061
- if (alg.startsWith("HS") && key instanceof Uint8Array) {
2062
- const algLength = Number.parseInt(alg.slice(2)) / 8;
2063
- if (key.length < algLength) throw new TypeError(`${alg} requires key length to be ${algLength} bytes or larger`);
2064
- } 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")) {
2065
2095
  const { modulusLength } = key.algorithm;
2066
- 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");
2067
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;
2068
2114
  }
2069
2115
  //#endregion
2070
- 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 };