unjwt 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- import { a as base64UrlEncode, c as isCryptoKey, i as base64UrlDecode, n as base64Decode, o as concatUint8Arrays, r as base64Encode, s as isAsymmetricJWK, t as assertCryptoKey, u as isJWK, v as textEncoder, y as sanitizeObject } from "./utils.mjs";
2
- //#region src/core/_crypto/_sign-verify.ts
1
+ import { D as JWTError, a as isCryptoKeyPair, i as isCryptoKey, n as concatUint8Arrays, o as isJWK, r as isAsymmetricJWK, s as isJWKSet, t as assertCryptoKey } from "./utils.mjs";
2
+ import { a as base64UrlEncode, i as base64UrlDecode, n as base64Decode, r as base64Encode, s as textEncoder, t as secureRandomBytes, u as sanitizeObjectCopy } from "./libs/unsecure.mjs";
3
3
  async function sign(alg, key, data) {
4
4
  checkSigCryptoKey(key, alg, "sign");
5
5
  const signature = await crypto.subtle.sign(subtleAlgorithm(alg, key.algorithm), key, data);
@@ -109,8 +109,6 @@ function getNamedCurve$1(alg) {
109
109
  function checkUsage$4(key, usage) {
110
110
  if (usage && !key.usages.includes(usage)) throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
111
111
  }
112
- //#endregion
113
- //#region src/core/_crypto/_aes.ts
114
112
  const IV_BIT_LENGTHS = {
115
113
  A128GCM: 96,
116
114
  A128GCMKW: 96,
@@ -386,8 +384,6 @@ function uint64be(value) {
386
384
  writeUInt32BE$1(buf, low, 4);
387
385
  return buf;
388
386
  }
389
- //#endregion
390
- //#region src/core/_crypto/_key-codec.ts
391
387
  async function jwkTokey(jwk) {
392
388
  if (!jwk.alg) throw new TypeError("\"alg\" argument is required when \"jwk.alg\" is not present");
393
389
  const { algorithm, keyUsages } = subtleMapping(jwk);
@@ -499,8 +495,6 @@ function subtleMapping(jwk) {
499
495
  keyUsages
500
496
  };
501
497
  }
502
- //#endregion
503
- //#region src/core/_crypto/_rsa.ts
504
498
  async function encryptRSAES(alg, key, cek) {
505
499
  checkRSACryptoKey(key, alg, "encrypt");
506
500
  checkRSAModulusLength(alg, key);
@@ -535,8 +529,6 @@ function getHashLength(hash) {
535
529
  function checkUsage$2(key, usage) {
536
530
  if (usage && !key.usages.includes(usage)) throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
537
531
  }
538
- //#endregion
539
- //#region src/core/_crypto/_ecdh.ts
540
532
  async function deriveECDHESKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) {
541
533
  checkECDHCryptoKey(publicKey, "ECDH");
542
534
  checkECDHCryptoKey(privateKey, "ECDH", "deriveBits");
@@ -601,8 +593,8 @@ function uint32be(value) {
601
593
  writeUInt32BE(buf, value, 0);
602
594
  return buf;
603
595
  }
604
- //#endregion
605
- //#region src/core/_crypto/_pbes2.ts
596
+ const DEFAULT_PBES2_MIN_ITERATIONS = 1e3;
597
+ const DEFAULT_PBES2_MAX_ITERATIONS = 1e6;
606
598
  async function pbes2Wrap(alg, key, cek, p2c, p2s) {
607
599
  const derived = await deriveKey(p2s, alg, p2c, key);
608
600
  return {
@@ -611,7 +603,9 @@ async function pbes2Wrap(alg, key, cek, p2c, p2s) {
611
603
  p2s: base64UrlEncode(p2s)
612
604
  };
613
605
  }
614
- async function pbes2Unwrap(alg, key, encryptedKey, p2c, p2s) {
606
+ async function pbes2Unwrap(alg, key, encryptedKey, p2c, p2s, minIterations = DEFAULT_PBES2_MIN_ITERATIONS, maxIterations = DEFAULT_PBES2_MAX_ITERATIONS) {
607
+ if (p2c < minIterations) throw new JWTError(`PBES2 "p2c" below the minimum of ${minIterations} iterations (got ${p2c}).`, "ERR_JWE_INVALID");
608
+ if (p2c > maxIterations) throw new JWTError(`PBES2 "p2c" above the maximum of ${maxIterations} iterations (got ${p2c}).`, "ERR_JWE_INVALID");
615
609
  const derived = await deriveKey(p2s, alg, p2c, key);
616
610
  return aesKwUnwrap(alg.slice(-6), derived, encryptedKey);
617
611
  }
@@ -648,8 +642,6 @@ async function getPBKDF2CryptoKey(key, alg, options = {}) {
648
642
  checkPBES2CryptoKey(key, alg, usage);
649
643
  return key;
650
644
  }
651
- //#endregion
652
- //#region src/core/_crypto/_key-encryption.ts
653
645
  const PUBLIC_EPK_FIELDS = new Set([
654
646
  "kty",
655
647
  "crv",
@@ -745,7 +737,7 @@ async function normalizeKey(key, alg) {
745
737
  if (key instanceof Uint8Array) return key;
746
738
  if (isCryptoKey(key)) return key;
747
739
  if (isJWK(key)) {
748
- if ("k" in key && key.k) return base64UrlDecode(key.k, false);
740
+ if ("k" in key && key.k) return base64UrlDecode(key.k, { returnAs: "uint8array" });
749
741
  return jwkTokey(key.alg ? key : {
750
742
  ...key,
751
743
  alg
@@ -769,7 +761,7 @@ function toPublicEpkJwk(jwk) {
769
761
  };
770
762
  if (typeof y === "string") publicJwk.y = y;
771
763
  else delete publicJwk.y;
772
- return sanitizeObject(publicJwk);
764
+ return sanitizeObjectCopy(publicJwk);
773
765
  }
774
766
  async function ensurePrivateKey(alg, value) {
775
767
  const normalized = await normalizeKey(value, alg);
@@ -788,8 +780,6 @@ async function exportPublicJwkFrom(alg, value) {
788
780
  if (!(normalized instanceof CryptoKey)) throw new TypeError("ECDH-ES ephemeral public key must be a CryptoKey or convertible JWK.");
789
781
  return exportPublicJwkFrom(alg, normalized);
790
782
  }
791
- //#endregion
792
- //#region src/core/_crypto/_pem.ts
793
783
  const toSPKI = (key) => {
794
784
  return genericExport("public", "spki", key);
795
785
  };
@@ -797,10 +787,10 @@ const toPKCS8 = (key) => {
797
787
  return genericExport("private", "pkcs8", key);
798
788
  };
799
789
  const fromPKCS8 = (pem, alg, options) => {
800
- return genericImport(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, "pkcs8", pem, alg, options);
790
+ return genericImport(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, "PRIVATE KEY", "pkcs8", pem, alg, options);
801
791
  };
802
792
  const fromSPKI = (pem, alg, options) => {
803
- return genericImport(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, "spki", pem, alg, options);
793
+ return genericImport(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, "PUBLIC KEY", "spki", pem, alg, options);
804
794
  };
805
795
  const fromX509 = (pem, alg, options) => {
806
796
  let spki;
@@ -814,16 +804,25 @@ const fromX509 = (pem, alg, options) => {
814
804
  const formatPEM = (b64, descriptor) => {
815
805
  return `-----BEGIN ${descriptor}-----\n${(b64.match(/.{1,64}/g) || []).join("\n")}\n-----END ${descriptor}-----`;
816
806
  };
807
+ const assertPEMLabel = (pem, descriptor) => {
808
+ const begin = `-----BEGIN ${descriptor}-----`;
809
+ const end = `-----END ${descriptor}-----`;
810
+ if (!pem.includes(begin) || !pem.includes(end)) {
811
+ const found = pem.match(/-----BEGIN [^-]+-----/)?.[0];
812
+ throw new JWTError(found ? `PEM label mismatch: expected "${begin}", got "${found}".` : `PEM label missing: expected "${begin}".`, "ERR_JWK_INVALID");
813
+ }
814
+ };
817
815
  const genericExport = async (keyType, keyFormat, key) => {
818
816
  if (!isCryptoKey(key)) throw new TypeError(`Key must be ${key} of type CryptoKey`);
819
817
  if (!key.extractable) throw new TypeError("CryptoKey is not extractable");
820
818
  if (key.type !== keyType) throw new TypeError(`key is not a ${keyType} key`);
821
819
  return formatPEM(base64Encode(new Uint8Array(await crypto.subtle.exportKey(keyFormat, key))), `${keyType.toUpperCase()} KEY`);
822
820
  };
823
- const genericImport = async (replace, keyFormat, pem, alg, options) => {
821
+ const genericImport = async (replace, descriptor, keyFormat, pem, alg, options) => {
824
822
  let algorithm;
825
823
  let keyUsages;
826
- const keyData = base64Decode(pem.replace(replace, ""), false);
824
+ assertPEMLabel(pem, descriptor);
825
+ const keyData = base64Decode(pem.replace(replace, ""), { returnAs: "uint8array" });
827
826
  const isPublic = keyFormat === "spki";
828
827
  switch (alg) {
829
828
  case "PS256":
@@ -1008,7 +1007,619 @@ const getSPKI = (x509) => {
1008
1007
  type: "spki"
1009
1008
  });
1010
1009
  } catch {}
1011
- return formatPEM(spkiFromX509(base64Decode(x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, ""), false)), "PUBLIC KEY");
1010
+ return formatPEM(spkiFromX509(base64Decode(x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, ""), { returnAs: "uint8array" })), "PUBLIC KEY");
1011
+ };
1012
+ async function generateKey(alg, options = {}) {
1013
+ const exportToJWK = options.toJWK !== void 0 && options.toJWK !== false;
1014
+ const defaultExtractable = options.extractable !== false;
1015
+ if (alg === "A128CBC-HS256" || alg === "A192CBC-HS384" || alg === "A256CBC-HS512") {
1016
+ const keyBytes = secureRandomBytes(bitLengthCEK(alg) >> 3);
1017
+ if (exportToJWK) return keyToJWK(keyBytes);
1018
+ return keyBytes;
1019
+ }
1020
+ const { algorithm, keyUsages } = getGenerateKeyParams(alg, options);
1021
+ const key = await crypto.subtle.generateKey(algorithm, defaultExtractable, keyUsages);
1022
+ if (exportToJWK) if (key instanceof CryptoKey) return {
1023
+ ...await exportKey(key),
1024
+ alg
1025
+ };
1026
+ else {
1027
+ const [pub, priv] = await Promise.all([exportKey(key.publicKey), exportKey(key.privateKey)]);
1028
+ return {
1029
+ privateKey: {
1030
+ ...priv,
1031
+ alg
1032
+ },
1033
+ publicKey: {
1034
+ ...pub,
1035
+ alg
1036
+ }
1037
+ };
1038
+ }
1039
+ return key;
1040
+ }
1041
+ async function generateJWK(alg, options) {
1042
+ const { keyUsage, extractable, modulusLength, publicExponent, namedCurve, ...jwkParams } = options ?? {};
1043
+ const kid = typeof jwkParams.kid === "string" ? jwkParams.kid : crypto.randomUUID();
1044
+ const extraParams = {
1045
+ ...jwkParams,
1046
+ kid
1047
+ };
1048
+ const result = await generateKey(alg, {
1049
+ keyUsage,
1050
+ extractable,
1051
+ modulusLength,
1052
+ publicExponent,
1053
+ namedCurve,
1054
+ toJWK: true
1055
+ });
1056
+ if (result && typeof result === "object" && "privateKey" in result && "publicKey" in result) {
1057
+ const pair = result;
1058
+ return {
1059
+ privateKey: {
1060
+ ...extraParams,
1061
+ ...pair.privateKey
1062
+ },
1063
+ publicKey: {
1064
+ ...extraParams,
1065
+ ...pair.publicKey
1066
+ }
1067
+ };
1068
+ }
1069
+ return {
1070
+ ...extraParams,
1071
+ ...result
1072
+ };
1073
+ }
1074
+ async function deriveKeyFromPassword(password, alg, options) {
1075
+ const { salt, iterations, extractable, keyUsage } = options;
1076
+ const exportToJWK = options.toJWK !== void 0 && options.toJWK !== false;
1077
+ if (!(salt instanceof Uint8Array) || salt.length < 8) throw new JWTError("PBES2 Salt Input (salt) must be 8 or more octets", "ERR_JWE_INVALID");
1078
+ if (typeof iterations !== "number" || iterations < 1) throw new JWTError("PBES2 Iteration Count (iterations) must be a positive integer", "ERR_JWE_INVALID");
1079
+ const derivedBytes = await deriveKey(salt, alg, iterations, typeof password === "string" ? textEncoder.encode(password) : password);
1080
+ const wrappingAlg = alg.slice(-6);
1081
+ const finalUsages = keyUsage ?? ["wrapKey", "unwrapKey"];
1082
+ const finalExtractable = exportToJWK ? true : extractable === true;
1083
+ const derivedKey = await crypto.subtle.importKey("raw", derivedBytes, { name: "AES-KW" }, finalExtractable, finalUsages);
1084
+ if (exportToJWK) return {
1085
+ ...await keyToJWK(derivedKey),
1086
+ alg: wrappingAlg,
1087
+ kty: "oct"
1088
+ };
1089
+ return derivedKey;
1090
+ }
1091
+ async function deriveJWKFromPassword(password, alg, options) {
1092
+ const { salt, iterations, keyUsage, extractable, ...jwkParams } = options;
1093
+ const result = await deriveKeyFromPassword(password, alg, {
1094
+ salt,
1095
+ iterations,
1096
+ keyUsage,
1097
+ extractable,
1098
+ toJWK: true
1099
+ });
1100
+ return Object.keys(jwkParams).length > 0 ? {
1101
+ ...jwkParams,
1102
+ ...result
1103
+ } : result;
1104
+ }
1105
+ var WeakMapJWKCache = class {
1106
+ _map = /* @__PURE__ */ new WeakMap();
1107
+ get(jwk, alg) {
1108
+ return this._map.get(jwk)?.[alg];
1109
+ }
1110
+ set(jwk, alg, key) {
1111
+ let entry = this._map.get(jwk);
1112
+ if (!entry) this._map.set(jwk, { [alg]: key });
1113
+ else entry[alg] = key;
1114
+ }
1012
1115
  };
1013
- //#endregion
1014
- export { gcmkwEncrypt as C, verify as D, sign as E, gcmkwDecrypt as S, generateIV as T, aesKwUnwrap as _, toSPKI as a, decrypt as b, deriveKey as c, deriveECDHESKey as d, isECDHKeyAllowed as f, keyToJWK as g, jwkTokey as h, toPKCS8 as i, pbes2Unwrap as l, encryptRSAES as m, fromSPKI as n, encryptKey as o, decryptRSAES as p, fromX509 as r, normalizeKey as s, fromPKCS8 as t, pbes2Wrap as u, aesKwWrap as v, generateCEK as w, encrypt as x, bitLengthCEK as y };
1116
+ let _activeCache = new WeakMapJWKCache();
1117
+ function configureJWKCache(cache) {
1118
+ _activeCache = cache;
1119
+ }
1120
+ function clearJWKCache() {
1121
+ _activeCache = new WeakMapJWKCache();
1122
+ }
1123
+ async function importKey(key, algOrOptions) {
1124
+ const alg = typeof algOrOptions === "string" ? algOrOptions : algOrOptions?.alg ?? void 0;
1125
+ const expect = typeof algOrOptions === "object" ? algOrOptions.expect ?? void 0 : void 0;
1126
+ if (typeof key === "string") key = textEncoder.encode(key);
1127
+ if (key instanceof Uint8Array) return key;
1128
+ if (isCryptoKey(key)) {
1129
+ assertIntentOnCryptoKey(key, expect);
1130
+ return key;
1131
+ }
1132
+ if (isJWK(key)) if ("k" in key && typeof key.k === "string") {
1133
+ const rawBytes = base64UrlDecode(key.k, { returnAs: "uint8array" });
1134
+ if (typeof algOrOptions === "object" && algOrOptions.asCryptoKey) {
1135
+ const { algorithm, usage, extractable = false } = algOrOptions;
1136
+ if (!algorithm || !usage) throw new JWTError("asCryptoKey option requires `algorithm` and `usage` to be provided.", "ERR_JWK_INVALID");
1137
+ return crypto.subtle.importKey("raw", rawBytes, algorithm, extractable, usage);
1138
+ }
1139
+ return rawBytes;
1140
+ } else {
1141
+ if (!key.alg && !alg) throw new JWTError("Algorithm must be provided when importing non-oct JWK", "ERR_JWK_INVALID");
1142
+ assertIntentOnJWK(key, expect);
1143
+ const effectiveAlg = key.alg || alg;
1144
+ if (_activeCache) {
1145
+ const cached = _activeCache.get(key, effectiveAlg);
1146
+ if (cached) return cached;
1147
+ }
1148
+ const cryptoKey = await jwkTokey(key.alg ? key : {
1149
+ ...key,
1150
+ alg
1151
+ });
1152
+ if (_activeCache) _activeCache.set(key, effectiveAlg, cryptoKey);
1153
+ return cryptoKey;
1154
+ }
1155
+ throw new JWTError("Invalid key type provided to importKey", "ERR_JWK_INVALID");
1156
+ }
1157
+ function assertIntentOnJWK(jwk, expect) {
1158
+ if (expect === void 0 || jwk.kty === "oct") return;
1159
+ const hasPrivateScalar = "d" in jwk && typeof jwk.d === "string";
1160
+ if (expect === "public" && hasPrivateScalar) throw new JWTError("Private JWK passed where a public key is expected. Strip `d` (and CRT fields on RSA) before import.", "ERR_JWK_INVALID");
1161
+ if (expect === "private" && !hasPrivateScalar) throw new JWTError("Public JWK passed where a private key is expected.", "ERR_JWK_INVALID");
1162
+ }
1163
+ function assertIntentOnCryptoKey(key, expect) {
1164
+ if (expect === void 0 || key.type === "secret") return;
1165
+ if (expect === "public" && key.type === "private") throw new JWTError("Private CryptoKey passed where a public key is expected.", "ERR_JWK_INVALID");
1166
+ if (expect === "private" && key.type === "public") throw new JWTError("Public CryptoKey passed where a private key is expected.", "ERR_JWK_INVALID");
1167
+ }
1168
+ async function exportKey(key, jwkParams) {
1169
+ const exportedJwk = await keyToJWK(key);
1170
+ if (jwkParams) return {
1171
+ ...exportedJwk,
1172
+ ...sanitizeObjectCopy(jwkParams)
1173
+ };
1174
+ return exportedJwk;
1175
+ }
1176
+ async function resolveWrappingKey(alg, key, expect) {
1177
+ if (alg.startsWith("PBES2") && (typeof key === "string" || key instanceof Uint8Array)) return typeof key === "string" ? textEncoder.encode(key) : key;
1178
+ if (typeof key === "string") throw new TypeError(`Key must be CryptoKey, JWK, or Uint8Array for ${alg}`);
1179
+ if (expect !== void 0) return importKey(key, {
1180
+ alg,
1181
+ expect
1182
+ });
1183
+ return importKey(key, alg);
1184
+ }
1185
+ async function wrapKey(alg, keyToWrap, wrappingKey, options = {}) {
1186
+ const cekBytes = keyToWrap instanceof Uint8Array ? keyToWrap : new Uint8Array(await crypto.subtle.exportKey("raw", keyToWrap));
1187
+ const importedWrappingKey = await resolveWrappingKey(alg, wrappingKey, "public");
1188
+ switch (alg) {
1189
+ case "dir": return { encryptedKey: new Uint8Array(0) };
1190
+ case "A128KW":
1191
+ case "A192KW":
1192
+ case "A256KW": return { encryptedKey: await aesKwWrap(alg, importedWrappingKey, cekBytes) };
1193
+ case "PBES2-HS256+A128KW":
1194
+ case "PBES2-HS384+A192KW":
1195
+ case "PBES2-HS512+A256KW": {
1196
+ const { p2s, p2c } = options;
1197
+ if (!p2s || typeof p2c !== "number") throw new JWTError("PBES2 requires 'p2s' (salt) and 'p2c' (count) options", "ERR_JWE_INVALID");
1198
+ return pbes2Wrap(alg, importedWrappingKey, cekBytes, p2c, p2s);
1199
+ }
1200
+ case "A128GCMKW":
1201
+ case "A192GCMKW":
1202
+ case "A256GCMKW": {
1203
+ const { encryptedKey, iv, tag } = await gcmkwEncrypt(alg, importedWrappingKey, cekBytes, options.iv);
1204
+ return {
1205
+ encryptedKey,
1206
+ iv,
1207
+ tag
1208
+ };
1209
+ }
1210
+ case "RSA-OAEP":
1211
+ case "RSA-OAEP-256":
1212
+ case "RSA-OAEP-384":
1213
+ case "RSA-OAEP-512": return { encryptedKey: await encryptRSAES(alg, importedWrappingKey, cekBytes) };
1214
+ case "ECDH-ES":
1215
+ case "ECDH-ES+A128KW":
1216
+ case "ECDH-ES+A192KW":
1217
+ case "ECDH-ES+A256KW": {
1218
+ if (!(importedWrappingKey instanceof CryptoKey)) throw new TypeError("ECDH-ES requires the wrapping key to be a CryptoKey (recipient's public key)");
1219
+ if (!isECDHKeyAllowed(importedWrappingKey)) throw new JWTError("ECDH with the provided key is not allowed or not supported", "ERR_JWK_INVALID");
1220
+ const apu = options.ecdh?.partyUInfo ?? new Uint8Array(0);
1221
+ const apv = options.ecdh?.partyVInfo ?? new Uint8Array(0);
1222
+ const { ephemeralPrivateKey, epkJwk } = await resolveECDHEphemeralPair(importedWrappingKey, options.ecdh?.ephemeralKey);
1223
+ const infoAlg = alg === "ECDH-ES" ? (() => {
1224
+ if (!options.ecdh?.enc) throw new JWTError("ECDH-ES direct key agreement requires options.ecdh.enc (the target content encryption algorithm)", "ERR_JWK_INVALID");
1225
+ return options.ecdh.enc;
1226
+ })() : alg;
1227
+ const sharedSecret = await deriveECDHESKey(importedWrappingKey, ephemeralPrivateKey, infoAlg, alg === "ECDH-ES" ? bitLengthCEK(infoAlg) : Number.parseInt(alg.slice(-5, -2), 10), apu, apv);
1228
+ const wrapResult = {
1229
+ encryptedKey: new Uint8Array(0),
1230
+ epk: epkJwk
1231
+ };
1232
+ if (apu.length > 0) wrapResult.apu = base64UrlEncode(apu);
1233
+ if (apv.length > 0) wrapResult.apv = base64UrlEncode(apv);
1234
+ if (alg === "ECDH-ES") return wrapResult;
1235
+ wrapResult.encryptedKey = await aesKwWrap(alg.slice(-6), sharedSecret, cekBytes);
1236
+ return wrapResult;
1237
+ }
1238
+ default: throw new JWTError(`Unsupported key wrapping algorithm: ${alg}`, "ERR_JWK_UNSUPPORTED");
1239
+ }
1240
+ }
1241
+ async function unwrapKey(alg, wrappedKey, unwrappingKey, options = {}) {
1242
+ const returnRaw = options.format === "raw";
1243
+ const defaultExtractable = options.extractable !== false;
1244
+ const importedUnwrappingKey = await resolveWrappingKey(alg, unwrappingKey, "private");
1245
+ let unwrappedCekBytes;
1246
+ switch (alg) {
1247
+ case "dir":
1248
+ if (importedUnwrappingKey instanceof Uint8Array) unwrappedCekBytes = importedUnwrappingKey;
1249
+ else {
1250
+ if (!returnRaw) return importedUnwrappingKey;
1251
+ unwrappedCekBytes = new Uint8Array(await crypto.subtle.exportKey("raw", importedUnwrappingKey));
1252
+ }
1253
+ break;
1254
+ case "A128KW":
1255
+ case "A192KW":
1256
+ case "A256KW":
1257
+ unwrappedCekBytes = await aesKwUnwrap(alg, importedUnwrappingKey, wrappedKey);
1258
+ break;
1259
+ case "PBES2-HS256+A128KW":
1260
+ case "PBES2-HS384+A192KW":
1261
+ case "PBES2-HS512+A256KW": {
1262
+ const { p2s, p2c, minIterations, maxIterations } = options;
1263
+ if (!p2s || typeof p2c !== "number") throw new JWTError("PBES2 requires 'p2s' (salt) and 'p2c' (count) options", "ERR_JWE_INVALID");
1264
+ unwrappedCekBytes = await pbes2Unwrap(alg, importedUnwrappingKey, wrappedKey, p2c, typeof p2s === "string" ? base64UrlDecode(p2s, { returnAs: "uint8array" }) : p2s, minIterations, maxIterations);
1265
+ break;
1266
+ }
1267
+ case "A128GCMKW":
1268
+ case "A192GCMKW":
1269
+ case "A256GCMKW":
1270
+ if (!options.iv || !options.tag) throw new JWTError("AES-GCMKW requires 'iv' and 'tag' options for unwrapping", "ERR_JWE_INVALID");
1271
+ unwrappedCekBytes = await gcmkwDecrypt(alg, importedUnwrappingKey, wrappedKey, typeof options.iv === "string" ? base64UrlDecode(options.iv, { returnAs: "uint8array" }) : options.iv, typeof options.tag === "string" ? base64UrlDecode(options.tag, { returnAs: "uint8array" }) : options.tag);
1272
+ break;
1273
+ case "RSA-OAEP":
1274
+ case "RSA-OAEP-256":
1275
+ case "RSA-OAEP-384":
1276
+ case "RSA-OAEP-512": {
1277
+ if (!(importedUnwrappingKey instanceof CryptoKey)) throw new TypeError("RSA-OAEP requires the unwrapping key to be provided as a CryptoKey");
1278
+ unwrappedCekBytes = await decryptRSAES(alg, importedUnwrappingKey, wrappedKey);
1279
+ if (returnRaw) break;
1280
+ const inferredAlgorithm = options.unwrappedKeyAlgorithm || inferAesImportAlgorithm(options.enc, unwrappedCekBytes.length);
1281
+ if (!inferredAlgorithm) throw new JWTError("Unable to infer algorithm for RSA-OAEP unwrapped key. Provide \"unwrappedKeyAlgorithm\" in options.", "ERR_JWE_INVALID");
1282
+ const keyUsages = options.keyUsage || ["encrypt", "decrypt"];
1283
+ return await crypto.subtle.importKey("raw", unwrappedCekBytes, inferredAlgorithm, defaultExtractable, keyUsages);
1284
+ }
1285
+ case "ECDH-ES":
1286
+ case "ECDH-ES+A128KW":
1287
+ case "ECDH-ES+A192KW":
1288
+ case "ECDH-ES+A256KW": {
1289
+ if (!options.epk) throw new JWTError("ECDH-ES requires 'epk' (Ephemeral Public Key) option", "ERR_JWE_INVALID");
1290
+ if (!(importedUnwrappingKey instanceof CryptoKey)) throw new TypeError("ECDH-ES requires the unwrapping key to be a CryptoKey");
1291
+ if (!isECDHKeyAllowed(importedUnwrappingKey)) throw new JWTError("ECDH with the provided key is not allowed or not supported", "ERR_JWK_INVALID");
1292
+ const epkCandidate = options.epk instanceof CryptoKey ? options.epk : await normalizeKey(options.epk, alg);
1293
+ if (!(epkCandidate instanceof CryptoKey)) throw new JWTError("Failed to normalize ECDH ephemeral public key", "ERR_JWK_INVALID");
1294
+ const apuBytes = typeof options.apu === "string" ? base64UrlDecode(options.apu, { returnAs: "uint8array" }) : options.apu ?? new Uint8Array(0);
1295
+ const apvBytes = typeof options.apv === "string" ? base64UrlDecode(options.apv, { returnAs: "uint8array" }) : options.apv ?? new Uint8Array(0);
1296
+ const infoAlg = alg === "ECDH-ES" ? options.enc : alg;
1297
+ if (!infoAlg) throw new JWTError("ECDH-ES requires content encryption algorithm ('enc') to derive the shared secret", "ERR_JWE_INVALID");
1298
+ const sharedSecret = await deriveECDHESKey(epkCandidate, importedUnwrappingKey, infoAlg, alg === "ECDH-ES" ? bitLengthCEK(infoAlg) : Number.parseInt(alg.slice(-5, -2), 10), apuBytes, apvBytes);
1299
+ if (alg === "ECDH-ES") unwrappedCekBytes = sharedSecret;
1300
+ else {
1301
+ const kwAlg = alg.slice(-6);
1302
+ if (!(wrappedKey instanceof Uint8Array) || wrappedKey.length === 0) throw new JWTError("ECDH-ES key agreement with key wrapping requires an encrypted key", "ERR_JWE_INVALID");
1303
+ unwrappedCekBytes = await aesKwUnwrap(kwAlg, sharedSecret, wrappedKey);
1304
+ }
1305
+ break;
1306
+ }
1307
+ default: throw new JWTError(`Unsupported key unwrapping algorithm: ${alg}`, "ERR_JWK_UNSUPPORTED");
1308
+ }
1309
+ if (returnRaw) return unwrappedCekBytes;
1310
+ return await crypto.subtle.importKey("raw", unwrappedCekBytes, options.unwrappedKeyAlgorithm || { name: "AES-GCM" }, defaultExtractable, options.keyUsage || ["encrypt", "decrypt"]);
1311
+ }
1312
+ async function importPEM(pem, alg, options) {
1313
+ const extractable = options?.extractable !== false;
1314
+ const pemType = options?.pemType ?? inferPEMType(pem);
1315
+ if (!pemType) throw new JWTError("Cannot infer PEM type from the input; pass \"options.pemType\" explicitly.", "ERR_JWK_INVALID");
1316
+ let cryptoKey;
1317
+ switch (pemType) {
1318
+ case "pkcs8":
1319
+ cryptoKey = await fromPKCS8(pem, alg, { extractable });
1320
+ break;
1321
+ case "spki":
1322
+ cryptoKey = await fromSPKI(pem, alg, { extractable });
1323
+ break;
1324
+ case "x509":
1325
+ cryptoKey = await fromX509(pem, alg, { extractable });
1326
+ break;
1327
+ default: throw new JWTError(`Unsupported PEM type: ${pemType}`, "ERR_JWK_UNSUPPORTED");
1328
+ }
1329
+ return {
1330
+ ...await exportKey(cryptoKey, options?.jwkParams),
1331
+ alg
1332
+ };
1333
+ }
1334
+ async function importFromPEM(pem, pemType, alg, options) {
1335
+ return importPEM(pem, alg, {
1336
+ pemType,
1337
+ ...options
1338
+ });
1339
+ }
1340
+ async function exportPEM(jwk, options) {
1341
+ if (jwk.kty === "oct") throw new JWTError("Octet (symmetric) JWKs (kty: 'oct') cannot be exported to PKCS8 or SPKI PEM formats.", "ERR_JWK_UNSUPPORTED");
1342
+ const effectiveAlg = jwk.alg || options?.alg;
1343
+ if (!effectiveAlg && (jwk.kty === "RSA" || jwk.kty === "EC" || jwk.kty === "OKP")) throw new JWTError("Algorithm (alg) must be provided in the JWK or via options for converting this JWK type to a CryptoKey.", "ERR_JWK_INVALID");
1344
+ const pemFormat = options?.pemFormat ?? ("d" in jwk ? "pkcs8" : "spki");
1345
+ const cryptoKeyCandidate = await importKey({
1346
+ ...jwk,
1347
+ ext: true
1348
+ }, {
1349
+ alg: effectiveAlg,
1350
+ expect: pemFormat === "pkcs8" ? "private" : "public"
1351
+ });
1352
+ if (!isCryptoKey(cryptoKeyCandidate)) throw new JWTError("Failed to convert JWK to a CryptoKey instance suitable for PEM export.", "ERR_JWK_INVALID");
1353
+ const cryptoKey = cryptoKeyCandidate;
1354
+ switch (pemFormat) {
1355
+ case "pkcs8":
1356
+ if (cryptoKey.type !== "private") throw new JWTError(`Only 'private' type CryptoKeys can be exported to PKCS8 PEM format. Key type is '${cryptoKey.type}'.`, "ERR_JWK_INVALID");
1357
+ return toPKCS8(cryptoKey);
1358
+ case "spki":
1359
+ if (cryptoKey.type !== "public") throw new JWTError(`Only 'public' type CryptoKeys can be exported to SPKI PEM format. Key type is '${cryptoKey.type}'.`, "ERR_JWK_INVALID");
1360
+ return toSPKI(cryptoKey);
1361
+ default: throw new JWTError(`Unsupported PEM format: ${pemFormat}`, "ERR_JWK_UNSUPPORTED");
1362
+ }
1363
+ }
1364
+ async function exportToPEM(jwk, pemFormat, algForCryptoKeyImport) {
1365
+ return exportPEM(jwk, {
1366
+ pemFormat,
1367
+ alg: algForCryptoKeyImport
1368
+ });
1369
+ }
1370
+ function inferPEMType(pem) {
1371
+ if (pem.includes("-----BEGIN PRIVATE KEY-----")) return "pkcs8";
1372
+ if (pem.includes("-----BEGIN PUBLIC KEY-----")) return "spki";
1373
+ if (pem.includes("-----BEGIN CERTIFICATE-----")) return "x509";
1374
+ }
1375
+ async function deriveSharedSecret(publicKey, privateKey, alg, options) {
1376
+ const pubCryptoKey = isCryptoKey(publicKey) ? publicKey : await importKey(publicKey, {
1377
+ alg: "ECDH-ES",
1378
+ expect: "public"
1379
+ });
1380
+ const privCryptoKey = isCryptoKey(privateKey) ? privateKey : await importKey(privateKey, {
1381
+ alg: "ECDH-ES",
1382
+ expect: "private"
1383
+ });
1384
+ let keyLength;
1385
+ if (options?.keyLength !== void 0) keyLength = options.keyLength;
1386
+ else if (alg === "ECDH-ES") throw new JWTError("deriveSharedSecret with alg \"ECDH-ES\" requires an explicit keyLength in options", "ERR_JWK_INVALID");
1387
+ else if (alg.startsWith("ECDH-ES+")) keyLength = Number.parseInt(alg.slice(-5, -2), 10);
1388
+ else keyLength = bitLengthCEK(alg);
1389
+ return deriveECDHESKey(pubCryptoKey, privCryptoKey, alg, keyLength, options?.partyUInfo ?? new Uint8Array(0), options?.partyVInfo ?? new Uint8Array(0));
1390
+ }
1391
+ function getJWKFromSet(jwkSet, kidOrProtectedHeader) {
1392
+ if (!jwkSet || !isJWKSet(jwkSet)) throw new JWTError("Invalid JWK Set provided", "ERR_JWK_INVALID");
1393
+ if (typeof kidOrProtectedHeader === "string") {
1394
+ const kid = kidOrProtectedHeader;
1395
+ const selectedKey = jwkSet.keys.find((k) => k.kid === kid);
1396
+ if (!selectedKey) throw new JWTError(`No key found in JWK Set with kid "${kid}".`, "ERR_JWK_KEY_NOT_FOUND");
1397
+ return selectedKey;
1398
+ } else if (typeof kidOrProtectedHeader === "object") {
1399
+ const { kid, alg, kty } = kidOrProtectedHeader;
1400
+ if (!kid) {
1401
+ if (jwkSet.keys.length === 1) return jwkSet.keys[0];
1402
+ throw new JWTError("JWS Protected Header is missing 'kid' (Key ID) and the JWK Set contains multiple keys. Add a 'kid' parameter to the token header and to the matching JWK to enable automatic selection.", "ERR_JWK_KEY_NOT_FOUND");
1403
+ }
1404
+ const selectedKey = jwkSet.keys.find((k) => {
1405
+ return k.kid === kid && (!alg || k.alg === alg) && (!kty || k.kty === kty);
1406
+ });
1407
+ if (!selectedKey) {
1408
+ let errorMessage = `No key found in JWK Set with kid "${kid}"`;
1409
+ if (alg) errorMessage += ` and alg "${alg}"`;
1410
+ if (kty) errorMessage += ` and kty "${kty}"`;
1411
+ errorMessage += ".";
1412
+ throw new JWTError(errorMessage, "ERR_JWK_KEY_NOT_FOUND");
1413
+ }
1414
+ return selectedKey;
1415
+ }
1416
+ throw new TypeError("`kidOrProtectedHeader` must be a string (kid) or an object (JOSE Protected Header).");
1417
+ }
1418
+ function getJWKsFromSet(jwkSet, filter) {
1419
+ if (!jwkSet || !isJWKSet(jwkSet)) throw new JWTError("Invalid JWK Set provided", "ERR_JWK_INVALID");
1420
+ if (!filter) return [...jwkSet.keys];
1421
+ return jwkSet.keys.filter(filter);
1422
+ }
1423
+ function getGenerateKeyParams(alg, options) {
1424
+ let algorithm;
1425
+ let keyUsages;
1426
+ const defaultKeyUsage = options?.keyUsage;
1427
+ switch (alg) {
1428
+ case "HS256":
1429
+ case "HS384":
1430
+ case "HS512":
1431
+ algorithm = {
1432
+ name: "HMAC",
1433
+ hash: `SHA-${alg.slice(2)}`
1434
+ };
1435
+ keyUsages = defaultKeyUsage ?? ["sign", "verify"];
1436
+ break;
1437
+ case "RS256":
1438
+ case "RS384":
1439
+ case "RS512":
1440
+ algorithm = {
1441
+ name: "RSASSA-PKCS1-v1_5",
1442
+ modulusLength: options?.modulusLength ?? 2048,
1443
+ publicExponent: options?.publicExponent ?? new Uint8Array([
1444
+ 1,
1445
+ 0,
1446
+ 1
1447
+ ]),
1448
+ hash: `SHA-${alg.slice(2)}`
1449
+ };
1450
+ keyUsages = defaultKeyUsage ?? ["sign", "verify"];
1451
+ break;
1452
+ case "PS256":
1453
+ case "PS384":
1454
+ case "PS512":
1455
+ algorithm = {
1456
+ name: "RSA-PSS",
1457
+ modulusLength: options?.modulusLength ?? 2048,
1458
+ publicExponent: options?.publicExponent ?? new Uint8Array([
1459
+ 1,
1460
+ 0,
1461
+ 1
1462
+ ]),
1463
+ hash: `SHA-${alg.slice(2)}`
1464
+ };
1465
+ keyUsages = defaultKeyUsage ?? ["sign", "verify"];
1466
+ break;
1467
+ case "RSA-OAEP":
1468
+ case "RSA-OAEP-256":
1469
+ case "RSA-OAEP-384":
1470
+ case "RSA-OAEP-512":
1471
+ algorithm = {
1472
+ name: "RSA-OAEP",
1473
+ modulusLength: options?.modulusLength ?? 2048,
1474
+ publicExponent: options?.publicExponent ?? new Uint8Array([
1475
+ 1,
1476
+ 0,
1477
+ 1
1478
+ ]),
1479
+ hash: `SHA-${Number.parseInt(alg.slice(9), 10) || 1}`
1480
+ };
1481
+ keyUsages = defaultKeyUsage ?? [
1482
+ "encrypt",
1483
+ "decrypt",
1484
+ "wrapKey",
1485
+ "unwrapKey"
1486
+ ];
1487
+ break;
1488
+ case "A128KW":
1489
+ case "A192KW":
1490
+ case "A256KW":
1491
+ algorithm = {
1492
+ name: "AES-KW",
1493
+ length: Number.parseInt(alg.slice(1, 4), 10)
1494
+ };
1495
+ keyUsages = defaultKeyUsage ?? ["wrapKey", "unwrapKey"];
1496
+ break;
1497
+ case "A128GCM":
1498
+ case "A192GCM":
1499
+ case "A256GCM":
1500
+ case "A128GCMKW":
1501
+ case "A192GCMKW":
1502
+ case "A256GCMKW": {
1503
+ const baseAlg = alg.replace("KW", "");
1504
+ algorithm = {
1505
+ name: "AES-GCM",
1506
+ length: Number.parseInt(baseAlg.slice(1, 4), 10)
1507
+ };
1508
+ keyUsages = defaultKeyUsage ?? (alg.endsWith("KW") ? ["wrapKey", "unwrapKey"] : ["encrypt", "decrypt"]);
1509
+ break;
1510
+ }
1511
+ case "ES256":
1512
+ case "ES384":
1513
+ case "ES512":
1514
+ algorithm = {
1515
+ name: "ECDSA",
1516
+ namedCurve: `P-${alg.slice(2)}`
1517
+ };
1518
+ keyUsages = defaultKeyUsage ?? ["sign", "verify"];
1519
+ break;
1520
+ case "Ed25519":
1521
+ keyUsages = defaultKeyUsage ?? ["sign", "verify"];
1522
+ algorithm = { name: "Ed25519" };
1523
+ break;
1524
+ case "EdDSA": {
1525
+ keyUsages = defaultKeyUsage ?? ["sign", "verify"];
1526
+ const namedCurve = options?.namedCurve ?? "Ed25519";
1527
+ switch (namedCurve) {
1528
+ case "Ed25519":
1529
+ case "Ed448":
1530
+ algorithm = { name: namedCurve };
1531
+ break;
1532
+ default: throw new JWTError("Unsupported namedCurve provided. Supported values are: Ed25519 and Ed448", "ERR_JWK_UNSUPPORTED");
1533
+ }
1534
+ break;
1535
+ }
1536
+ case "ECDH-ES":
1537
+ case "ECDH-ES+A128KW":
1538
+ case "ECDH-ES+A192KW":
1539
+ case "ECDH-ES+A256KW": {
1540
+ keyUsages = ["deriveBits"];
1541
+ const namedCurve = options?.namedCurve ?? "P-256";
1542
+ switch (namedCurve) {
1543
+ case "P-256":
1544
+ case "P-384":
1545
+ case "P-521":
1546
+ algorithm = {
1547
+ name: "ECDH",
1548
+ namedCurve
1549
+ };
1550
+ break;
1551
+ case "X25519":
1552
+ algorithm = { name: "X25519" };
1553
+ break;
1554
+ default: throw new JWTError("Unsupported namedCurve provided. Supported values are: P-256, P-384, P-521 and X25519", "ERR_JWK_UNSUPPORTED");
1555
+ }
1556
+ break;
1557
+ }
1558
+ default: throw new JWTError(`Unsupported or invalid algorithm for key generation: ${alg}`, "ERR_JWK_UNSUPPORTED");
1559
+ }
1560
+ return {
1561
+ algorithm,
1562
+ keyUsages
1563
+ };
1564
+ }
1565
+ async function resolveECDHEphemeralPair(recipientPublicKey, ephemeralKeyInput) {
1566
+ if (!ephemeralKeyInput) {
1567
+ const generated = await crypto.subtle.generateKey(recipientPublicKey.algorithm, true, ["deriveBits"]);
1568
+ const rawJwk = await keyToJWK(generated.publicKey);
1569
+ return {
1570
+ ephemeralPrivateKey: generated.privateKey,
1571
+ epkJwk: stripPrivateJwkFields(rawJwk)
1572
+ };
1573
+ }
1574
+ if (isCryptoKeyPair(ephemeralKeyInput)) {
1575
+ const rawJwk = await keyToJWK(ephemeralKeyInput.publicKey);
1576
+ return {
1577
+ ephemeralPrivateKey: ephemeralKeyInput.privateKey,
1578
+ epkJwk: stripPrivateJwkFields(rawJwk)
1579
+ };
1580
+ }
1581
+ if (typeof ephemeralKeyInput === "object" && "publicKey" in ephemeralKeyInput && "privateKey" in ephemeralKeyInput) {
1582
+ const { publicKey, privateKey } = ephemeralKeyInput;
1583
+ return {
1584
+ ephemeralPrivateKey: isCryptoKey(privateKey) ? privateKey : await importKey(privateKey, {
1585
+ alg: "ECDH-ES",
1586
+ expect: "private"
1587
+ }),
1588
+ epkJwk: isCryptoKey(publicKey) ? stripPrivateJwkFields(await keyToJWK(publicKey)) : stripPrivateJwkFields(publicKey)
1589
+ };
1590
+ }
1591
+ if (isCryptoKey(ephemeralKeyInput)) {
1592
+ if (ephemeralKeyInput.type !== "private") throw new JWTError("ECDH-ES ephemeral CryptoKey must be a private key", "ERR_JWK_INVALID");
1593
+ return {
1594
+ ephemeralPrivateKey: ephemeralKeyInput,
1595
+ epkJwk: stripPrivateJwkFields(await keyToJWK(ephemeralKeyInput))
1596
+ };
1597
+ }
1598
+ if (isJWK(ephemeralKeyInput) && "d" in ephemeralKeyInput) return {
1599
+ ephemeralPrivateKey: await importKey(ephemeralKeyInput, {
1600
+ alg: "ECDH-ES",
1601
+ expect: "private"
1602
+ }),
1603
+ epkJwk: stripPrivateJwkFields(ephemeralKeyInput)
1604
+ };
1605
+ throw new JWTError("Unsupported ECDH-ES ephemeral key material", "ERR_JWK_INVALID");
1606
+ }
1607
+ function stripPrivateJwkFields(jwk) {
1608
+ const { d: _d, ...pub } = jwk;
1609
+ return pub;
1610
+ }
1611
+ function inferAesImportAlgorithm(enc, cekLengthBytes) {
1612
+ const bitLength = cekLengthBytes << 3;
1613
+ if (enc) {
1614
+ if (enc.includes("GCM")) return {
1615
+ name: "AES-GCM",
1616
+ length: Number.parseInt(enc.slice(1, 4), 10)
1617
+ };
1618
+ if (enc.includes("CBC-HS")) return;
1619
+ }
1620
+ if (bitLength === 128 || bitLength === 192 || bitLength === 256) return {
1621
+ name: "AES-GCM",
1622
+ length: bitLength
1623
+ };
1624
+ }
1625
+ export { generateIV as C, generateCEK as S, verify as T, unwrapKey as _, deriveKeyFromPassword as a, decrypt as b, exportPEM as c, generateKey as d, getJWKFromSet as f, importPEM as g, importKey as h, deriveJWKFromPassword as i, exportToPEM as l, importFromPEM as m, clearJWKCache as n, deriveSharedSecret as o, getJWKsFromSet as p, configureJWKCache as r, exportKey as s, WeakMapJWKCache as t, generateJWK as u, wrapKey as v, sign as w, encrypt as x, encryptKey as y };