unjwt 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +4 -4
  2. package/dist/THIRD-PARTY-LICENSES.md.gz +0 -0
  3. package/dist/_chunks/_codec-jwe.d.mts +170 -0
  4. package/dist/_chunks/_codec-jws.mjs +878 -0
  5. package/dist/_chunks/_internal.d.mts +7 -0
  6. package/dist/_chunks/_jwt-internal.mjs +388 -0
  7. package/dist/_chunks/algorithms.mjs +186 -0
  8. package/dist/_chunks/index.d.mts +512 -818
  9. package/dist/_chunks/index2.d.mts +11 -0
  10. package/dist/_chunks/index3.d.mts +108 -0
  11. package/dist/_chunks/jwe.d.mts +43 -66
  12. package/dist/_chunks/jwe.mjs +384 -252
  13. package/dist/_chunks/jwk.mjs +1390 -622
  14. package/dist/_chunks/jws.d.mts +48 -103
  15. package/dist/_chunks/jws.mjs +372 -184
  16. package/dist/_chunks/jwt.d.mts +56 -0
  17. package/dist/_chunks/libs/unsecure.d.mts +164 -85
  18. package/dist/_chunks/libs/unsecure.mjs +390 -123
  19. package/dist/adapters/elysia/index.d.mts +33 -171
  20. package/dist/adapters/elysia/index.mjs +106 -575
  21. package/dist/adapters/h3v1/index.d.mts +59 -344
  22. package/dist/adapters/h3v1/index.mjs +114 -619
  23. package/dist/adapters/h3v2/index.d.mts +58 -326
  24. package/dist/adapters/h3v2/index.mjs +108 -611
  25. package/dist/core/internal.d.mts +2 -0
  26. package/dist/core/internal.mjs +1 -0
  27. package/dist/core/jwe.d.mts +4 -2
  28. package/dist/core/jwe.mjs +4 -2
  29. package/dist/core/jwk.d.mts +183 -371
  30. package/dist/core/jwk.mjs +4 -3
  31. package/dist/core/jws.d.mts +4 -2
  32. package/dist/core/jws.mjs +4 -2
  33. package/dist/core/utils/index.d.mts +5 -155
  34. package/dist/core/utils/index.mjs +12 -3
  35. package/dist/index.d.mts +8 -4
  36. package/dist/index.mjs +6 -3
  37. package/package.json +39 -36
  38. package/dist/_chunks/_internal.mjs +0 -88
  39. package/dist/_chunks/utils.mjs +0 -390
@@ -1,122 +1,318 @@
1
- import { JWTError, applyTypCtyDefaults, computeJwtTimeClaims, decodePayloadFromBytes, getPlaintextBytes, isCryptoKey, isJWK, isJWKSet, validateCriticalHeadersJWE } from "./utils.mjs";
2
- import { base64UrlDecode, base64UrlEncode, sanitizeObjectCopy, secureRandomBytes, textEncoder } from "./libs/unsecure.mjs";
3
- import { decrypt as decrypt$1, encrypt as encrypt$1, encryptKey, generateCEK, generateIV, getJWKsFromSet, importKey, unwrapKey } from "./jwk.mjs";
4
- import { JWE_ALG_CTX, buildJWKSetFilter, checkAlgAllowed, decodeProtectedHeader, parseEphemeralKey, validateJwtClaimsIfJsonPayload } from "./_internal.mjs";
5
- const DIRECT_ALGS = /* @__PURE__ */ new Set(["dir", "ECDH-ES"]);
1
+ import { sanitizeObjectCopy, secureRandomBytes, textEncoder } from "./libs/unsecure.mjs";
2
+ import { JWTError, applyTypDefault, base64UrlStringify, computeJwtTimeClaims, getPlaintextBytes, isJWK, isJWKSet, validateCriticalHeadersJWE } from "./_jwt-internal.mjs";
3
+ import { isContentEncryptionAlgorithm } from "./algorithms.mjs";
4
+ import { DEFAULT_PBES2_ITERATIONS, JWE_ALG_CTX, JWE_DIRECT_ALGS, asDecryptionError, asKeyLookupError, assertCompactToken, assertConsumerOptions, assertDirectKeyRole, assertEntryLimit, assertJWEAlgSupported, assertJWEEncSupported, assertJWKSetCandidates, assertNoZipOption, assertOptionEntry, assertPBES2Params, assertProducerCEK, assertProducerCrit, assertProducerIV, assertProtectedOnlyOption, assertSerializationObject, buildECDHAgreementParams, buildJWKSetFilter, buildUnwrapKeyOptions, checkAlgAllowed, checkJWEAlgSupported, checkJWEContentSegments, checkJWEDirectEncryptedKey, checkJWEEncAllowed, checkJWEEncSupported, checkJWEUnprotectedCrit, checkJWEZip, checkProtectedTyp, checkSerializationEntry, createPBES2Budget, decodeJWEPayload, decodeProtectedHeader, decodeSegment, decrypt as decrypt$1, encrypt as encrypt$1, encryptKey, exportReturnedCek, generateCEK, generateIV, getJWKsFromSet, importKey, isJWEAlgSupported, keyMatchesHeader, resolveKeyInput, resolveLimit, resolvePasswordKey, resolvePayloadMode, serializeProtectedHeader, unwrapKey, validateClaimsIfJson } from "./jwk.mjs";
5
+ function buildKeyManagementParams(alg, source) {
6
+ const { keyManagementIV, p2s, p2c, ecdh } = source;
7
+ const isPBES2 = alg.startsWith("PBES2");
8
+ const params = buildECDHAgreementParams(ecdh);
9
+ if (keyManagementIV) params.iv = keyManagementIV;
10
+ if (p2s !== void 0) params.p2s = p2s;
11
+ else if (isPBES2) params.p2s = secureRandomBytes(16);
12
+ if (p2c !== void 0) params.p2c = p2c;
13
+ else if (isPBES2) params.p2c = DEFAULT_PBES2_ITERATIONS;
14
+ return params;
15
+ }
16
+ async function unwrapAndDecrypt(alg, rawKeyMaterial, options) {
17
+ const { enc, encryptedKey, unwrapKeyOptions, ciphertext, iv, tag, aad, kid, allowSymmetric, keyCandidates, pbes2Budget } = options;
18
+ if (isJWKSet(rawKeyMaterial)) {
19
+ const candidates = getJWKsFromSet(rawKeyMaterial, buildJWKSetFilter({
20
+ kid,
21
+ alg
22
+ }, {
23
+ op: "enc",
24
+ allowSymmetric
25
+ }));
26
+ assertJWKSetCandidates(candidates, {
27
+ limit: keyCandidates,
28
+ kid
29
+ });
30
+ const attempts = [];
31
+ for (const candidate of candidates) try {
32
+ if (alg === "dir") assertDirectKeyRole(candidate, enc);
33
+ pbes2Budget.spend(unwrapKeyOptions);
34
+ const unwrappingKey = await importKey(candidate, {
35
+ alg,
36
+ expect: "private"
37
+ });
38
+ const cek = await unwrapKey(encryptedKey, unwrappingKey, unwrapKeyOptions);
39
+ return {
40
+ cek,
41
+ plaintextBytes: await decrypt$1(enc, cek, ciphertext, iv, tag, aad)
42
+ };
43
+ } catch (err) {
44
+ attempts.push(asDecryptionError(err));
45
+ }
46
+ throw new JWTError("JWE decryption failed.", "ERR_JWE_DECRYPTION_FAILED", attempts);
47
+ }
48
+ if (alg === "dir") assertDirectKeyRole(rawKeyMaterial, enc);
49
+ pbes2Budget.spend(unwrapKeyOptions);
50
+ try {
51
+ const unwrappingKey = await importKey(resolvePasswordKey(alg, rawKeyMaterial), {
52
+ alg,
53
+ expect: "private"
54
+ });
55
+ const cek = await unwrapKey(encryptedKey, unwrappingKey, unwrapKeyOptions);
56
+ return {
57
+ cek,
58
+ plaintextBytes: await decrypt$1(enc, cek, ciphertext, iv, tag, aad)
59
+ };
60
+ } catch (err) {
61
+ throw asDecryptionError(err);
62
+ }
63
+ }
6
64
  async function encryptMulti(payload, recipients, options = {}) {
7
65
  if (!Array.isArray(recipients) || recipients.length === 0) throw new JWTError("encryptMulti requires at least one recipient.", "ERR_JWE_INVALID");
66
+ assertOptionEntry(options, {
67
+ label: "options",
68
+ objects: ["protectedHeader", "sharedUnprotectedHeader"]
69
+ });
70
+ _assertAADOption(options.aad);
71
+ assertNoZipOption(options.protectedHeader, "protected");
72
+ assertNoZipOption(options.sharedUnprotectedHeader, "shared unprotected");
73
+ assertProtectedOnlyOption(options.sharedUnprotectedHeader, {
74
+ tier: "shared unprotected",
75
+ family: "JWE"
76
+ });
77
+ for (const [index, recipient] of recipients.entries()) {
78
+ assertOptionEntry(recipient, {
79
+ label: `recipients[${index}]`,
80
+ objects: ["header"]
81
+ });
82
+ assertNoZipOption(recipient.header, "per-recipient");
83
+ assertProtectedOnlyOption(recipient.header, {
84
+ tier: "per-recipient",
85
+ family: "JWE"
86
+ });
87
+ }
8
88
  const resolvedAlgs = recipients.map((r, i) => _resolveRecipientAlg(r.key, i));
9
89
  const enc = options.enc ?? "A256GCM";
90
+ const encError = checkJWEEncSupported(enc);
91
+ if (encError) throw encError;
92
+ assertProducerCEK(options.cek, enc);
93
+ assertProducerIV(options.contentEncryptionIV, enc);
94
+ for (const [index, recipient] of recipients.entries()) if (resolvedAlgs[index].startsWith("PBES2")) assertPBES2Params({
95
+ p2s: recipient.p2s,
96
+ p2c: recipient.p2c
97
+ });
10
98
  const userProtected = sanitizeObjectCopy(options.protectedHeader);
11
99
  const sharedUnprotected = sanitizeObjectCopy(options.sharedUnprotectedHeader);
12
100
  const protectedHeader = {};
13
101
  if (userProtected) Object.assign(protectedHeader, userProtected);
14
102
  protectedHeader.enc = enc;
15
- applyTypCtyDefaults(protectedHeader, payload);
16
- const plaintextBytes = getPlaintextBytes(computeJwtTimeClaims(payload, options) || payload);
103
+ applyTypDefault(protectedHeader, payload);
104
+ assertProducerCrit(protectedHeader, "JWE");
105
+ const computedPayload = computeJwtTimeClaims(payload, options);
106
+ const plaintextBytes = getPlaintextBytes(computedPayload || payload);
17
107
  const cek = options.cek ?? generateCEK(enc);
18
108
  const wireRecipients = [];
19
109
  for (let i = 0; i < recipients.length; i++) {
20
110
  const recipient = recipients[i];
21
111
  const alg = resolvedAlgs[i];
22
- const keyMgmtParams = _buildRecipientKeyMgmtParams(recipient, alg);
23
- const { encryptedKey, parameters: keyMgmtOut } = await encryptKey(alg, enc, await importKey(recipient.key, {
112
+ const keyMgmtParams = buildKeyManagementParams(alg, recipient);
113
+ const wrappingKey = await importKey(recipient.key, {
24
114
  alg,
25
115
  expect: "public"
26
- }), cek, keyMgmtParams);
116
+ });
117
+ const { encryptedKey, parameters: keyMgmtOut } = await encryptKey(alg, enc, wrappingKey, cek, keyMgmtParams);
27
118
  const perRecipientHeader = _buildPerRecipientHeader(recipient, alg, keyMgmtOut);
28
119
  _assertDisjointHeaders(protectedHeader, sharedUnprotected, perRecipientHeader);
29
120
  const wireRecipient = {};
30
121
  if (Object.keys(perRecipientHeader).length > 0) wireRecipient.header = perRecipientHeader;
31
- if (encryptedKey) wireRecipient.encrypted_key = base64UrlEncode(encryptedKey);
122
+ if (encryptedKey) wireRecipient.encrypted_key = base64UrlStringify(encryptedKey);
32
123
  wireRecipients.push(wireRecipient);
33
124
  }
34
- const protectedHeaderEncoded = base64UrlEncode(JSON.stringify(protectedHeader));
125
+ const protectedHeaderEncoded = base64UrlStringify(serializeProtectedHeader(protectedHeader));
35
126
  let aadEncoded;
36
127
  let aadForCipher = protectedHeaderEncoded;
37
- if (options.aad !== void 0) {
38
- aadEncoded = base64UrlEncode(typeof options.aad === "string" ? textEncoder.encode(options.aad) : options.aad);
128
+ const aadBytes = typeof options.aad === "string" ? textEncoder.encode(options.aad) : options.aad;
129
+ if (aadBytes !== void 0 && aadBytes.length > 0) {
130
+ aadEncoded = base64UrlStringify(aadBytes);
39
131
  aadForCipher = `${protectedHeaderEncoded}.${aadEncoded}`;
40
132
  }
41
133
  const contentAadBytes = textEncoder.encode(aadForCipher);
42
- const { ciphertext: contentCiphertext, tag: contentAuthTag, iv: actualContentIV } = await encrypt$1(enc, plaintextBytes, cek, options.contentEncryptionIV ?? generateIV(enc), contentAadBytes);
43
- if (!actualContentIV || !contentAuthTag) throw new JWTError("Content encryption did not return an IV / auth tag.", "ERR_JWE_INVALID");
134
+ const contentIVBytes = options.contentEncryptionIV ?? generateIV(enc);
135
+ const { ciphertext: contentCiphertext, tag: contentAuthTag, iv: actualContentIV } = await encrypt$1(enc, plaintextBytes, cek, contentIVBytes, contentAadBytes);
44
136
  const output = {
45
137
  protected: protectedHeaderEncoded,
46
138
  recipients: wireRecipients,
47
- iv: base64UrlEncode(actualContentIV),
48
- ciphertext: base64UrlEncode(contentCiphertext),
49
- tag: base64UrlEncode(contentAuthTag)
139
+ iv: base64UrlStringify(actualContentIV),
140
+ ciphertext: base64UrlStringify(contentCiphertext),
141
+ tag: base64UrlStringify(contentAuthTag)
50
142
  };
51
143
  if (sharedUnprotected && Object.keys(sharedUnprotected).length > 0) output.unprotected = sharedUnprotected;
52
144
  if (aadEncoded !== void 0) output.aad = aadEncoded;
53
145
  return output;
54
146
  }
55
147
  async function decryptMulti(jwe, keyOrLookup, options = {}) {
148
+ assertConsumerOptions(options);
149
+ const payloadMode = resolvePayloadMode(options);
150
+ const recipientLimit = resolveLimit(options.limits, "recipients");
151
+ const keyCandidates = resolveLimit(options.limits, "keyCandidates");
152
+ const pbes2Budget = createPBES2Budget(resolveLimit(options.limits, "pbes2Iterations"));
56
153
  const general = _normalizeSerialization(jwe);
57
154
  if (typeof general.iv !== "string" || typeof general.ciphertext !== "string" || typeof general.tag !== "string" || !Array.isArray(general.recipients) || general.recipients.length === 0) throw new JWTError("Invalid JWE JSON Serialization: missing iv/ciphertext/tag/recipients.", "ERR_JWE_INVALID_SERIALIZATION");
155
+ assertEntryLimit(general.recipients.length, recipientLimit, {
156
+ family: "JWE",
157
+ what: "recipients"
158
+ });
159
+ const envelopeError = checkSerializationEntry(general, {
160
+ family: "JWE",
161
+ label: "JSON Serialization",
162
+ objects: ["unprotected"],
163
+ strings: ["protected", "aad"]
164
+ });
165
+ if (envelopeError) throw envelopeError;
58
166
  const protectedHeaderEncoded = general.protected ?? "";
59
167
  const protectedHeader = decodeProtectedHeader(protectedHeaderEncoded || void 0, "JWE");
60
- const enc = protectedHeader.enc;
61
- if (!enc) throw new JWTError("Invalid JWE: Protected header is missing \"enc\".", "ERR_JWE_INVALID");
62
- if (options.encryptionAlgorithms && !options.encryptionAlgorithms.includes(enc)) throw new JWTError(`Content encryption algorithm not allowed: ${enc}`, "ERR_JWE_ALG_NOT_ALLOWED");
63
168
  const sharedUnprotected = sanitizeObjectCopy(general.unprotected);
64
- const ivBytes = base64UrlDecode(general.iv, { returnAs: "uint8array" });
65
- const ciphertextBytes = base64UrlDecode(general.ciphertext, { returnAs: "uint8array" });
66
- const tagBytes = base64UrlDecode(general.tag, { returnAs: "uint8array" });
67
- const contentAadBytes = textEncoder.encode(general.aad ? `${protectedHeaderEncoded}.${general.aad}` : protectedHeaderEncoded);
169
+ const sharedError = checkJWEZip(protectedHeader) ?? checkJWEUnprotectedCrit(sharedUnprotected) ?? checkJWEZip(sharedUnprotected);
170
+ if (sharedError) throw sharedError;
171
+ _assertDisjointHeaders(protectedHeader, sharedUnprotected, void 0);
172
+ const enc = protectedHeader.enc ?? sharedUnprotected?.enc;
173
+ if (!enc) throw new JWTError("Invalid JWE: \"enc\" is missing from the protected and shared unprotected headers.", "ERR_JWE_INVALID");
174
+ assertJWEEncSupported(enc);
175
+ const encError = checkJWEEncAllowed(enc, options.encryptionAlgorithms);
176
+ if (encError) throw encError;
177
+ validateCriticalHeadersJWE(protectedHeader, options.recognizedHeaders);
178
+ const typError = checkProtectedTyp(options.typ, protectedHeader, { family: "JWE" });
179
+ if (typError) throw typError;
180
+ const ivBytes = decodeSegment(general.iv, {
181
+ family: "JWE",
182
+ what: "Initialization vector"
183
+ });
184
+ const ciphertextBytes = decodeSegment(general.ciphertext, {
185
+ family: "JWE",
186
+ what: "Ciphertext"
187
+ });
188
+ const tagBytes = decodeSegment(general.tag, {
189
+ family: "JWE",
190
+ what: "Authentication tag"
191
+ });
192
+ const segmentError = checkJWEContentSegments(enc, ivBytes, tagBytes);
193
+ if (segmentError) throw segmentError;
194
+ const contentAadBytes = textEncoder.encode(general.aad === void 0 ? protectedHeaderEncoded : `${protectedHeaderEncoded}.${general.aad}`);
68
195
  let lastError;
69
196
  let cryptoAttempted = false;
70
197
  for (let i = 0; i < general.recipients.length; i++) {
198
+ const shapeError = checkSerializationEntry(general.recipients[i], {
199
+ family: "JWE",
200
+ label: `Recipient[${i}]`,
201
+ objects: ["header"],
202
+ strings: ["encrypted_key"]
203
+ });
204
+ if (shapeError) {
205
+ lastError = shapeError;
206
+ continue;
207
+ }
71
208
  const wireRecipient = general.recipients[i];
72
209
  const recipientHeader = sanitizeObjectCopy(wireRecipient.header);
73
- _assertDisjointHeaders(protectedHeader, sharedUnprotected, recipientHeader);
210
+ const recipientError = checkJWEUnprotectedCrit(recipientHeader) ?? checkJWEZip(recipientHeader);
211
+ if (recipientError) {
212
+ lastError = recipientError;
213
+ continue;
214
+ }
215
+ try {
216
+ _assertDisjointHeaders(protectedHeader, sharedUnprotected, recipientHeader);
217
+ } catch (err) {
218
+ lastError = asDecryptionError(err);
219
+ continue;
220
+ }
74
221
  const effective = _mergeHeaders(protectedHeader, sharedUnprotected, recipientHeader);
75
222
  const alg = effective.alg;
76
223
  if (!alg) {
77
224
  lastError = new JWTError(`Recipient[${i}] has no "alg" in the effective header.`, "ERR_JWE_INVALID");
78
225
  continue;
79
226
  }
80
- const rawKeyMaterial = typeof keyOrLookup === "function" ? await keyOrLookup(effective, protectedHeaderEncoded) : keyOrLookup;
81
- const algError = checkAlgAllowed(alg, rawKeyMaterial, options.algorithms, JWE_ALG_CTX);
227
+ if (!isJWEAlgSupported(alg)) {
228
+ lastError = checkJWEAlgSupported(alg);
229
+ continue;
230
+ }
231
+ const directKeyError = checkJWEDirectEncryptedKey(alg, wireRecipient.encrypted_key);
232
+ if (directKeyError) {
233
+ lastError = directKeyError;
234
+ continue;
235
+ }
236
+ let rawKeyMaterial;
237
+ try {
238
+ rawKeyMaterial = await resolveKeyInput(keyOrLookup, {
239
+ header: effective,
240
+ token: protectedHeaderEncoded,
241
+ what: `recipient[${i}]`
242
+ });
243
+ } catch (err) {
244
+ lastError = asKeyLookupError(err, `recipient[${i}]`);
245
+ continue;
246
+ }
247
+ const algError = checkAlgAllowed(alg, rawKeyMaterial, {
248
+ ctx: JWE_ALG_CTX,
249
+ algorithms: options.algorithms,
250
+ allowSymmetric: options.allowSymmetricJWKSet
251
+ });
82
252
  if (algError) {
83
253
  lastError = algError;
84
254
  continue;
85
255
  }
86
- if (options.strictRecipientMatch && !_recipientKeyMatches(effective, rawKeyMaterial)) continue;
87
- const encryptedKeyBytes = base64UrlDecode(wireRecipient.encrypted_key ?? "", { returnAs: "uint8array" });
88
- const unwrapKeyOpts = _buildUnwrapKeyOptions(effective, enc, options);
256
+ if (options.strictRecipientMatch && !keyMatchesHeader(effective, rawKeyMaterial, {
257
+ ctx: JWE_ALG_CTX,
258
+ allowSymmetric: options.allowSymmetricJWKSet
259
+ })) continue;
260
+ let encryptedKeyBytes;
261
+ try {
262
+ encryptedKeyBytes = decodeSegment(wireRecipient.encrypted_key ?? "", {
263
+ family: "JWE",
264
+ what: "Encrypted key"
265
+ });
266
+ } catch (err) {
267
+ lastError = asDecryptionError(err);
268
+ continue;
269
+ }
270
+ const unwrapKeyOpts = buildUnwrapKeyOptions(effective, {
271
+ alg,
272
+ enc
273
+ }, options);
89
274
  cryptoAttempted = true;
275
+ let cek;
276
+ let plaintextBytes;
90
277
  try {
91
- const { cek, plaintextBytes } = await _unwrapAndDecrypt(alg, enc, rawKeyMaterial, encryptedKeyBytes, unwrapKeyOpts, ciphertextBytes, ivBytes, tagBytes, contentAadBytes, effective.kid);
92
- const payload = decodePayloadFromBytes(plaintextBytes, options.forceUint8Array);
93
- if (protectedHeader.crit || options.recognizedHeaders?.length) validateCriticalHeadersJWE(protectedHeader, options.recognizedHeaders);
94
- validateJwtClaimsIfJsonPayload(payload, options);
95
- const result = {
96
- payload,
97
- protectedHeader: {
98
- ...protectedHeader,
99
- alg,
100
- enc
101
- },
102
- recipientIndex: i
103
- };
104
- if (sharedUnprotected) result.sharedUnprotectedHeader = sharedUnprotected;
105
- if (recipientHeader) result.recipientHeader = recipientHeader;
106
- if (options.returnCek) {
107
- result.cek = isCryptoKey(cek) ? new Uint8Array(await crypto.subtle.exportKey("raw", cek)) : cek;
108
- result.aad = contentAadBytes;
109
- }
110
- return result;
278
+ ({cek, plaintextBytes} = await unwrapAndDecrypt(alg, rawKeyMaterial, {
279
+ enc,
280
+ encryptedKey: encryptedKeyBytes,
281
+ unwrapKeyOptions: unwrapKeyOpts,
282
+ ciphertext: ciphertextBytes,
283
+ iv: ivBytes,
284
+ tag: tagBytes,
285
+ aad: contentAadBytes,
286
+ kid: effective.kid,
287
+ allowSymmetric: options.allowSymmetricJWKSet,
288
+ keyCandidates,
289
+ pbes2Budget
290
+ }));
111
291
  } catch (err) {
112
- if (err instanceof JWTError && err.code !== "ERR_JWE_DECRYPTION_FAILED" && err.code !== "ERR_JWK_KEY_NOT_FOUND") throw err;
113
- lastError = err instanceof JWTError ? err : void 0;
292
+ lastError = asDecryptionError(err);
293
+ continue;
114
294
  }
295
+ const payload = decodeJWEPayload(plaintextBytes, payloadMode);
296
+ validateClaimsIfJson(payload, payloadMode, options);
297
+ const result = {
298
+ payload,
299
+ protectedHeader,
300
+ recipientIndex: i
301
+ };
302
+ if (sharedUnprotected) result.sharedUnprotectedHeader = sharedUnprotected;
303
+ if (recipientHeader) result.recipientHeader = recipientHeader;
304
+ if (options.returnCek) {
305
+ result.cek = await exportReturnedCek(cek);
306
+ result.aad = contentAadBytes;
307
+ }
308
+ return result;
115
309
  }
116
310
  if (options.strictRecipientMatch && !cryptoAttempted) throw new JWTError("No recipient matched the provided key under strictRecipientMatch.", "ERR_JWE_NO_MATCHING_RECIPIENT");
117
311
  throw lastError ?? new JWTError("JWE decryption failed.", "ERR_JWE_DECRYPTION_FAILED");
118
312
  }
119
313
  function generalToFlattened(jwe) {
314
+ assertSerializationObject(jwe, "JWE");
315
+ if (!Array.isArray(jwe.recipients)) throw new JWTError("Invalid JWE JSON Serialization: missing \"recipients\".", "ERR_JWE_INVALID_SERIALIZATION");
120
316
  if (jwe.recipients.length !== 1) throw new JWTError(`Flattened JWE Serialization requires exactly one recipient, got ${jwe.recipients.length}.`, "ERR_JWE_INVALID_SERIALIZATION");
121
317
  const { recipients, ...rest } = jwe;
122
318
  const [recipient] = recipients;
@@ -127,27 +323,13 @@ function generalToFlattened(jwe) {
127
323
  }
128
324
  function _resolveRecipientAlg(key, index) {
129
325
  if (!isJWK(key)) throw new JWTError(`Recipient[${index}] key is not a JWK.`, "ERR_JWE_RECIPIENT_ALG_INFERENCE");
130
- const alg = key.alg;
131
- if (!alg) throw new JWTError(`Cannot infer "alg" for recipient[${index}]: the JWK has no "alg" property.`, "ERR_JWE_RECIPIENT_ALG_INFERENCE");
132
- if (DIRECT_ALGS.has(alg)) throw new JWTError(`Recipient[${index}] resolves to "${alg}", which requires exactly one recipient. Use encrypt() instead.`, "ERR_JWE_ALG_FORBIDDEN_IN_MULTI");
326
+ const declared = key.alg;
327
+ if (!declared) throw new JWTError(`Cannot infer "alg" for recipient[${index}]: the JWK has no "alg" property.`, "ERR_JWE_RECIPIENT_ALG_INFERENCE");
328
+ const alg = key.kty === "oct" && isContentEncryptionAlgorithm(declared) ? "dir" : declared;
329
+ if (JWE_DIRECT_ALGS.has(alg)) throw new JWTError(`Recipient[${index}] resolves to "${alg}", which requires exactly one recipient. Use encrypt() instead.`, "ERR_JWE_ALG_FORBIDDEN_IN_MULTI");
330
+ assertJWEAlgSupported(alg);
133
331
  return alg;
134
332
  }
135
- function _buildRecipientKeyMgmtParams(recipient, alg) {
136
- const params = {};
137
- if (recipient.keyManagementIV) params.iv = recipient.keyManagementIV;
138
- if (recipient.p2s) params.p2s = recipient.p2s;
139
- else if (alg.startsWith("PBES2")) params.p2s = secureRandomBytes(16);
140
- if (recipient.p2c) params.p2c = recipient.p2c;
141
- else if (alg.startsWith("PBES2")) params.p2c = 6e5;
142
- if (recipient.ecdh?.partyUInfo) params.apu = recipient.ecdh.partyUInfo;
143
- if (recipient.ecdh?.partyVInfo) params.apv = recipient.ecdh.partyVInfo;
144
- if (recipient.ecdh?.ephemeralKey) {
145
- const { epk, epkPrivateKey } = parseEphemeralKey(recipient.ecdh.ephemeralKey);
146
- params.epk = epk;
147
- params.epkPrivateKey = epkPrivateKey;
148
- }
149
- return params;
150
- }
151
333
  function _buildPerRecipientHeader(recipient, alg, keyMgmtOut) {
152
334
  const header = {};
153
335
  if (recipient.key.kid) header.kid = recipient.key.kid;
@@ -177,219 +359,162 @@ function _mergeHeaders(protectedHeader, sharedUnprotected, recipientHeader) {
177
359
  return merged;
178
360
  }
179
361
  function _normalizeSerialization(input) {
180
- if (!input || typeof input !== "object") throw new JWTError("Invalid JWE JSON Serialization: expected an object.", "ERR_JWE_INVALID_SERIALIZATION");
181
- const obj = input;
182
- if (Array.isArray(obj.recipients)) return obj;
183
- if ("encrypted_key" in obj || "header" in obj) {
184
- const flattened = obj;
185
- const general = {
186
- recipients: [{
187
- ...flattened.header !== void 0 ? { header: flattened.header } : {},
188
- ...flattened.encrypted_key !== void 0 ? { encrypted_key: flattened.encrypted_key } : {}
189
- }],
190
- iv: flattened.iv,
191
- ciphertext: flattened.ciphertext,
192
- tag: flattened.tag
193
- };
194
- if (flattened.protected !== void 0) general.protected = flattened.protected;
195
- if (flattened.unprotected !== void 0) general.unprotected = flattened.unprotected;
196
- if (flattened.aad !== void 0) general.aad = flattened.aad;
197
- return general;
198
- }
199
- throw new JWTError("Invalid JWE JSON Serialization: missing both recipients[] and flattened header/encrypted_key.", "ERR_JWE_INVALID_SERIALIZATION");
200
- }
201
- function _recipientKeyMatches(effectiveHeader, keyMaterial) {
202
- const headerKid = effectiveHeader.kid;
203
- if (isJWKSet(keyMaterial)) return keyMaterial.keys.some((k) => !headerKid || k.kid === headerKid);
204
- if (isJWK(keyMaterial)) {
205
- if (headerKid && keyMaterial.kid && headerKid !== keyMaterial.kid) return false;
206
- return true;
362
+ assertSerializationObject(input, "JWE");
363
+ if ("recipients" in input) {
364
+ if ("encrypted_key" in input || "header" in input) throw new JWTError("Invalid JWE JSON Serialization: \"recipients\" must not be combined with a top-level \"header\" or \"encrypted_key\".", "ERR_JWE_INVALID_SERIALIZATION");
365
+ return input;
207
366
  }
208
- return !headerKid;
209
- }
210
- function _buildUnwrapKeyOptions(effectiveHeader, enc, options) {
211
- const opts = {
212
- enc,
213
- format: enc.includes("GCM") ? "cryptokey" : "raw",
214
- keyUsage: options.keyUsage || ["decrypt"]
367
+ const flattened = input;
368
+ const recipient = {};
369
+ if (flattened.header !== void 0) recipient.header = flattened.header;
370
+ if (flattened.encrypted_key !== void 0) recipient.encrypted_key = flattened.encrypted_key;
371
+ const general = {
372
+ recipients: [recipient],
373
+ iv: flattened.iv,
374
+ ciphertext: flattened.ciphertext,
375
+ tag: flattened.tag
215
376
  };
216
- if (effectiveHeader.iv) opts.iv = effectiveHeader.iv;
217
- if (effectiveHeader.tag) opts.tag = effectiveHeader.tag;
218
- if (effectiveHeader.p2s) opts.p2s = effectiveHeader.p2s;
219
- if (effectiveHeader.p2c) opts.p2c = effectiveHeader.p2c;
220
- if (effectiveHeader.epk) opts.epk = effectiveHeader.epk;
221
- if (effectiveHeader.apu) opts.apu = effectiveHeader.apu;
222
- if (effectiveHeader.apv) opts.apv = effectiveHeader.apv;
223
- if (options.unwrappedKeyAlgorithm) opts.unwrappedKeyAlgorithm = options.unwrappedKeyAlgorithm;
224
- if (options.extractable) opts.extractable = options.extractable;
225
- if (options.minIterations !== void 0) opts.minIterations = options.minIterations;
226
- if (options.maxIterations !== void 0) opts.maxIterations = options.maxIterations;
227
- return opts;
377
+ if (flattened.protected !== void 0) general.protected = flattened.protected;
378
+ if (flattened.unprotected !== void 0) general.unprotected = flattened.unprotected;
379
+ if (flattened.aad !== void 0) general.aad = flattened.aad;
380
+ return general;
228
381
  }
229
- async function _unwrapAndDecrypt(alg, enc, rawKeyMaterial, encryptedKeyBytes, unwrapKeyOpts, ciphertextBytes, ivBytes, tagBytes, contentAadBytes, headerKid) {
230
- if (isJWKSet(rawKeyMaterial)) {
231
- const candidates = getJWKsFromSet(rawKeyMaterial, buildJWKSetFilter({
232
- kid: headerKid,
233
- alg
234
- }));
235
- if (candidates.length === 0) throw new JWTError("No key found in JWK Set.", "ERR_JWK_KEY_NOT_FOUND");
236
- let lastErr;
237
- for (const candidate of candidates) {
238
- const unwrappingKey = await importKey(candidate, {
239
- alg,
240
- expect: "private"
241
- });
242
- try {
243
- const cek = await unwrapKey(alg, encryptedKeyBytes, unwrappingKey, unwrapKeyOpts);
244
- return {
245
- cek,
246
- plaintextBytes: await decrypt$1(enc, cek, ciphertextBytes, ivBytes, tagBytes, contentAadBytes)
247
- };
248
- } catch (err) {
249
- if (err instanceof JWTError && err.code !== "ERR_JWE_DECRYPTION_FAILED") throw err;
250
- lastErr = err;
251
- }
252
- }
253
- throw lastErr ?? new JWTError("JWE decryption failed.", "ERR_JWE_DECRYPTION_FAILED");
254
- }
255
- const unwrappingKey = await importKey(rawKeyMaterial, {
256
- alg,
257
- expect: "private"
258
- });
259
- try {
260
- const cek = await unwrapKey(alg, encryptedKeyBytes, unwrappingKey, unwrapKeyOpts);
261
- return {
262
- cek,
263
- plaintextBytes: await decrypt$1(enc, cek, ciphertextBytes, ivBytes, tagBytes, contentAadBytes)
264
- };
265
- } catch (err) {
266
- if (err instanceof JWTError) throw err;
267
- throw new JWTError("JWE decryption failed.", "ERR_JWE_DECRYPTION_FAILED", err);
268
- }
382
+ function _assertAADOption(aad) {
383
+ if (aad === void 0 || typeof aad === "string" || aad instanceof Uint8Array) return;
384
+ throw new JWTError("\"aad\" must be a string or a Uint8Array.", "ERR_INVALID_OPTION");
269
385
  }
270
386
  async function encrypt(payload, key, options = {}) {
387
+ assertOptionEntry(options, {
388
+ label: "options",
389
+ objects: ["protectedHeader"]
390
+ });
271
391
  const { protectedHeader: userHeader, cek: providedCek, contentEncryptionIV: providedContentIV, keyManagementIV, p2s, p2c, ecdh } = options;
272
- let { alg, enc } = options;
392
+ let alg = options.alg;
393
+ let enc = options.enc;
394
+ assertNoZipOption(userHeader, "protected");
395
+ const cekRole = isJWK(key) && key.kty === "oct" && typeof key.alg === "string" ? isContentEncryptionAlgorithm(key.alg) ? key.alg : void 0 : void 0;
273
396
  if (!alg) {
274
- if (typeof key === "string" || key instanceof Uint8Array) alg = "PBES2-HS256+A128KW";
397
+ if (typeof key === "string") alg = "PBES2-HS256+A128KW";
398
+ else if (cekRole) alg = "dir";
275
399
  else if (isJWK(key)) alg = key.alg;
276
400
  }
277
401
  if (!alg) throw new JWTError("JWE \"alg\" (Key Management Algorithm) must be provided in options or inferable from the key", "ERR_JWE_ALG_MISSING");
278
- if (!enc) if (alg === "dir") {
279
- enc = isJWK(key) && "enc" in key ? key.enc : void 0;
280
- if (!enc) throw new JWTError("JWE \"enc\" must be provided when alg is \"dir\"", "ERR_JWE_ENC_MISSING");
281
- } else enc = isJWK(key) && "enc" in key ? key.enc : "A128GCM";
282
- const jweKeyManagementParams = {};
283
- if (keyManagementIV) jweKeyManagementParams.iv = keyManagementIV;
284
- if (p2s) jweKeyManagementParams.p2s = p2s;
285
- else if (alg?.startsWith("PBES2")) jweKeyManagementParams.p2s = secureRandomBytes(16);
286
- if (p2c) jweKeyManagementParams.p2c = p2c;
287
- else if (alg?.startsWith("PBES2")) jweKeyManagementParams.p2c = 6e5;
288
- if (ecdh?.partyUInfo) jweKeyManagementParams.apu = ecdh.partyUInfo;
289
- if (ecdh?.partyVInfo) jweKeyManagementParams.apv = ecdh.partyVInfo;
290
- if (ecdh?.ephemeralKey) {
291
- const parsed = parseEphemeralKey(ecdh.ephemeralKey);
292
- jweKeyManagementParams.epk = parsed.epk;
293
- jweKeyManagementParams.epkPrivateKey = parsed.epkPrivateKey;
402
+ if (!enc) {
403
+ if (alg === "dir") {
404
+ enc = cekRole ?? (isJWK(key) ? key.enc : void 0);
405
+ if (!enc) throw new JWTError("JWE \"enc\" must be provided when alg is \"dir\"", "ERR_JWE_ENC_MISSING");
406
+ } else enc = isJWK(key) && key.enc !== void 0 ? key.enc : "A256GCM";
294
407
  }
295
- const wrappingKeyMaterial = await importKey(key, {
408
+ assertJWEAlgSupported(alg);
409
+ assertJWEEncSupported(enc);
410
+ assertProducerCEK(providedCek, enc);
411
+ assertProducerIV(providedContentIV, enc);
412
+ if (alg.startsWith("PBES2")) assertPBES2Params({
413
+ p2s,
414
+ p2c
415
+ });
416
+ const jweKeyManagementParams = buildKeyManagementParams(alg, {
417
+ keyManagementIV,
418
+ p2s,
419
+ p2c,
420
+ ecdh
421
+ });
422
+ const wrappingKeyMaterial = await importKey(resolvePasswordKey(alg, key), {
296
423
  alg,
297
424
  expect: "public"
298
425
  });
426
+ if (alg === "dir") assertDirectKeyRole(key, enc);
299
427
  const { cek: finalCek, encryptedKey: jweEncryptedKey, parameters: keyManagementHeaderParams } = await encryptKey(alg, enc, wrappingKeyMaterial, providedCek, jweKeyManagementParams);
300
428
  const protectedHeader = _buildJWEHeader(alg, enc, key, userHeader, keyManagementHeaderParams, payload);
301
- const plaintextBytes = getPlaintextBytes(computeJwtTimeClaims(payload, options) || payload);
302
- const protectedHeaderEncoded = base64UrlEncode(JSON.stringify(protectedHeader));
429
+ const computedPayload = computeJwtTimeClaims(payload, options);
430
+ const plaintextBytes = getPlaintextBytes(computedPayload || payload);
431
+ const protectedHeaderSerialized = serializeProtectedHeader(protectedHeader);
432
+ const protectedHeaderEncoded = base64UrlStringify(protectedHeaderSerialized);
303
433
  const aadBytes = textEncoder.encode(protectedHeaderEncoded);
304
434
  const contentIVBytes = providedContentIV ?? generateIV(enc);
305
435
  const { ciphertext: contentCiphertext, tag: contentAuthTag, iv: actualContentIV } = await encrypt$1(enc, plaintextBytes, finalCek, contentIVBytes, aadBytes);
306
- if (!actualContentIV) throw new JWTError("Content encryption IV was not generated or returned.", "ERR_JWE_INVALID");
307
- if (!contentAuthTag) throw new JWTError("Content encryption auth tag was not generated or returned.", "ERR_JWE_INVALID");
308
436
  return [
309
437
  protectedHeaderEncoded,
310
- jweEncryptedKey ? base64UrlEncode(jweEncryptedKey) : "",
311
- base64UrlEncode(actualContentIV),
312
- base64UrlEncode(contentCiphertext),
313
- base64UrlEncode(contentAuthTag)
438
+ jweEncryptedKey ? base64UrlStringify(jweEncryptedKey) : "",
439
+ base64UrlStringify(actualContentIV),
440
+ base64UrlStringify(contentCiphertext),
441
+ base64UrlStringify(contentAuthTag)
314
442
  ].join(".");
315
443
  }
316
444
  async function decrypt(jwe, key, options = {}) {
445
+ assertCompactToken(jwe, "JWE");
446
+ assertConsumerOptions(options);
447
+ const payloadMode = resolvePayloadMode(options);
448
+ const keyCandidateLimit = resolveLimit(options.limits, "keyCandidates");
449
+ const pbes2Budget = createPBES2Budget(resolveLimit(options.limits, "pbes2Iterations"));
317
450
  const parts = jwe.split(".");
318
451
  if (parts.length !== 5) throw new JWTError("Invalid JWE: Must contain five sections (RFC7516, section-3).", "ERR_JWE_INVALID");
319
452
  const [protectedHeaderEncoded, encryptedKeyEncoded, ivEncoded, ciphertextEncoded, authTagEncoded] = parts;
320
453
  const protectedHeader = decodeProtectedHeader(protectedHeaderEncoded, "JWE");
321
- if (!protectedHeader.alg || !protectedHeader.enc) throw new JWTError("Invalid JWE: Protected header must be an object with \"alg\" and \"enc\" properties.", "ERR_JWE_INVALID");
322
- const alg = protectedHeader.alg;
323
- const enc = protectedHeader.enc;
324
- if (options?.encryptionAlgorithms && !options.encryptionAlgorithms.includes(enc)) throw new JWTError(`Content encryption algorithm not allowed: ${enc}`, "ERR_JWE_ALG_NOT_ALLOWED");
325
- const rawKeyMaterial = typeof key === "function" ? await key(protectedHeader, jwe) : key;
326
- const algError = checkAlgAllowed(alg, rawKeyMaterial, options?.algorithms, JWE_ALG_CTX);
454
+ assertHeaderAlgorithms(protectedHeader);
455
+ const { alg, enc } = protectedHeader;
456
+ const headerError = checkJWEZip(protectedHeader) ?? checkJWEEncAllowed(enc, options.encryptionAlgorithms);
457
+ if (headerError) throw headerError;
458
+ validateCriticalHeadersJWE(protectedHeader, options.recognizedHeaders);
459
+ const typError = checkProtectedTyp(options.typ, protectedHeader, { family: "JWE" });
460
+ if (typError) throw typError;
461
+ const directKeyError = checkJWEDirectEncryptedKey(alg, encryptedKeyEncoded);
462
+ if (directKeyError) throw directKeyError;
463
+ const encryptedKeyBytes = decodeSegment(encryptedKeyEncoded, {
464
+ family: "JWE",
465
+ what: "Encrypted key"
466
+ });
467
+ const contentIVBytes = decodeSegment(ivEncoded, {
468
+ family: "JWE",
469
+ what: "Initialization vector"
470
+ });
471
+ const contentAuthTagBytes = decodeSegment(authTagEncoded, {
472
+ family: "JWE",
473
+ what: "Authentication tag"
474
+ });
475
+ const ciphertextBytes = decodeSegment(ciphertextEncoded, {
476
+ family: "JWE",
477
+ what: "Ciphertext"
478
+ });
479
+ const segmentError = checkJWEContentSegments(enc, contentIVBytes, contentAuthTagBytes);
480
+ if (segmentError) throw segmentError;
481
+ const rawKeyMaterial = await resolveKeyInput(key, {
482
+ header: protectedHeader,
483
+ token: jwe,
484
+ what: "the JWE"
485
+ });
486
+ const algError = checkAlgAllowed(alg, rawKeyMaterial, {
487
+ ctx: JWE_ALG_CTX,
488
+ algorithms: options.algorithms,
489
+ allowSymmetric: options.allowSymmetricJWKSet
490
+ });
327
491
  if (algError) throw algError;
328
- const encryptedKeyBytes = base64UrlDecode(encryptedKeyEncoded, { returnAs: "uint8array" });
329
- const contentIVBytes = base64UrlDecode(ivEncoded, { returnAs: "uint8array" });
330
- const contentAuthTagBytes = base64UrlDecode(authTagEncoded, { returnAs: "uint8array" });
331
- const ciphertextBytes = base64UrlDecode(ciphertextEncoded, { returnAs: "uint8array" });
332
- const unwrapKeyOpts = {
333
- enc,
334
- format: enc.includes("GCM") ? "cryptokey" : "raw",
335
- keyUsage: options?.keyUsage || ["decrypt"]
336
- };
337
- if (protectedHeader.iv) unwrapKeyOpts.iv = protectedHeader.iv;
338
- if (protectedHeader.tag) unwrapKeyOpts.tag = protectedHeader.tag;
339
- if (protectedHeader.p2s) unwrapKeyOpts.p2s = protectedHeader.p2s;
340
- if (protectedHeader.p2c) unwrapKeyOpts.p2c = protectedHeader.p2c;
341
- if (protectedHeader.epk) unwrapKeyOpts.epk = protectedHeader.epk;
342
- if (protectedHeader.apu) unwrapKeyOpts.apu = protectedHeader.apu;
343
- if (protectedHeader.apv) unwrapKeyOpts.apv = protectedHeader.apv;
344
- if (options?.unwrappedKeyAlgorithm) unwrapKeyOpts.unwrappedKeyAlgorithm = options.unwrappedKeyAlgorithm;
345
- if (options?.extractable) unwrapKeyOpts.extractable = options.extractable;
346
- if (options?.minIterations !== void 0) unwrapKeyOpts.minIterations = options.minIterations;
347
- if (options?.maxIterations !== void 0) unwrapKeyOpts.maxIterations = options.maxIterations;
492
+ const unwrapKeyOpts = buildUnwrapKeyOptions(protectedHeader, {
493
+ alg,
494
+ enc
495
+ }, options);
348
496
  const aadBytes = textEncoder.encode(protectedHeaderEncoded);
349
- let cek;
350
- let plaintextBytes;
351
- if (isJWKSet(rawKeyMaterial)) {
352
- const candidates = getJWKsFromSet(rawKeyMaterial, buildJWKSetFilter(protectedHeader));
353
- if (candidates.length === 0) throw new JWTError(`No key found in JWK Set${protectedHeader.kid ? ` with kid "${protectedHeader.kid}"` : ""}.`, "ERR_JWK_KEY_NOT_FOUND");
354
- let decrypted = false;
355
- for (const candidate of candidates) {
356
- const unwrappingKey = await importKey(candidate, {
357
- alg,
358
- expect: "private"
359
- });
360
- try {
361
- const candidateCek = await unwrapKey(alg, encryptedKeyBytes, unwrappingKey, unwrapKeyOpts);
362
- plaintextBytes = await decrypt$1(enc, candidateCek, ciphertextBytes, contentIVBytes, contentAuthTagBytes, aadBytes);
363
- cek = candidateCek;
364
- decrypted = true;
365
- break;
366
- } catch (err) {
367
- if (err instanceof JWTError && err.code !== "ERR_JWE_DECRYPTION_FAILED") throw err;
368
- }
369
- }
370
- if (!decrypted) throw new JWTError("JWE decryption failed.", "ERR_JWE_DECRYPTION_FAILED");
371
- } else {
372
- const unwrappingKey = await importKey(rawKeyMaterial, {
373
- alg,
374
- expect: "private"
375
- });
376
- try {
377
- cek = await unwrapKey(alg, encryptedKeyBytes, unwrappingKey, unwrapKeyOpts);
378
- plaintextBytes = await decrypt$1(enc, cek, ciphertextBytes, contentIVBytes, contentAuthTagBytes, aadBytes);
379
- } catch (error_) {
380
- if (error_ instanceof JWTError) throw error_;
381
- throw new JWTError("JWE decryption failed.", "ERR_JWE_DECRYPTION_FAILED", error_);
382
- }
383
- }
384
- const payload = decodePayloadFromBytes(plaintextBytes, options?.forceUint8Array);
385
- if (protectedHeader.crit || options?.recognizedHeaders?.length) validateCriticalHeadersJWE(protectedHeader, options?.recognizedHeaders);
386
- validateJwtClaimsIfJsonPayload(payload, options);
497
+ const { cek, plaintextBytes } = await unwrapAndDecrypt(alg, rawKeyMaterial, {
498
+ enc,
499
+ encryptedKey: encryptedKeyBytes,
500
+ unwrapKeyOptions: unwrapKeyOpts,
501
+ ciphertext: ciphertextBytes,
502
+ iv: contentIVBytes,
503
+ tag: contentAuthTagBytes,
504
+ aad: aadBytes,
505
+ kid: protectedHeader.kid,
506
+ allowSymmetric: options.allowSymmetricJWKSet,
507
+ keyCandidates: keyCandidateLimit,
508
+ pbes2Budget
509
+ });
510
+ const payload = decodeJWEPayload(plaintextBytes, payloadMode);
511
+ validateClaimsIfJson(payload, payloadMode, options);
387
512
  const result = {
388
513
  payload,
389
514
  protectedHeader
390
515
  };
391
516
  if (options.returnCek) {
392
- result.cek = isCryptoKey(cek) ? new Uint8Array(await crypto.subtle.exportKey("raw", cek)) : cek;
517
+ result.cek = await exportReturnedCek(cek);
393
518
  result.aad = aadBytes;
394
519
  }
395
520
  return result;
@@ -402,6 +527,13 @@ function _buildJWEHeader(alg, enc, key, userHeader, keyManagementParams, payload
402
527
  if (keyManagementParams) Object.assign(header, keyManagementParams);
403
528
  header.alg = alg;
404
529
  header.enc = enc;
405
- return applyTypCtyDefaults(header, payload);
530
+ applyTypDefault(header, payload);
531
+ assertProducerCrit(header, "JWE");
532
+ return header;
533
+ }
534
+ function assertHeaderAlgorithms(header) {
535
+ if (!header.alg || !header.enc) throw new JWTError("Invalid JWE: Protected header must be an object with \"alg\" and \"enc\" properties.", "ERR_JWE_INVALID");
536
+ assertJWEAlgSupported(header.alg);
537
+ assertJWEEncSupported(header.enc);
406
538
  }
407
539
  export { decrypt, decryptMulti, encrypt, encryptMulti, generalToFlattened };