unjwt 0.6.1 → 0.7.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.
@@ -0,0 +1,84 @@
1
+ import { K as JOSEPayload, Q as ContentEncryptionAlgorithm, Ut as KeyManagementAlgorithm, _ as JWEMultiDecryptOptions, b as JWEMultiRecipient, c as JWEDecryptOptions, d as JWEEncryptOptions, f as JWEFlattenedSerialization, l as JWEDecryptResult, lt as JWKLookupFunction, m as JWEGeneralSerialization, pt as JWKSet, s as JWEDecryptJWK, u as JWEEncryptJWK, v as JWEMultiDecryptResult, y as JWEMultiEncryptOptions } from "./index.mjs";
2
+ /**
3
+ * Encrypt a payload to multiple recipients using JWE General JSON Serialization
4
+ * (RFC 7516 §7.2.1).
5
+ *
6
+ * One shared CEK encrypts the payload once; the CEK is then wrapped
7
+ * independently per recipient using each recipient's key-management algorithm.
8
+ * Per-recipient `alg` is read from `key.alg` and throws `ERR_JWE_RECIPIENT_ALG_INFERENCE`
9
+ * when absent.
10
+ *
11
+ * Throws `ERR_JWE_ALG_FORBIDDEN_IN_MULTI` when any recipient resolves to `dir`
12
+ * or bare `ECDH-ES` — those algorithms require exactly one recipient; use
13
+ * {@link encrypt} instead.
14
+ *
15
+ * @example
16
+ * const jwe = await encryptMulti({ sub: "u1" }, [
17
+ * { key: aliceRsaPublicJwk },
18
+ * { key: bobEcdhPublicJwk },
19
+ * ], { enc: "A256GCM", expiresIn: "1h" });
20
+ */
21
+ declare function encryptMulti(payload: JOSEPayload, recipients: JWEMultiRecipient[], options?: JWEMultiEncryptOptions): Promise<JWEGeneralSerialization>;
22
+ /**
23
+ * Decrypt a JWE in JSON Serialization object form (General RFC 7516 §7.2.1 or
24
+ * Flattened §7.2.2 — both accepted).
25
+ *
26
+ * Matches recipients by, in order: `kid` equality, `kty`/`crv`/length
27
+ * compatibility, then trial decryption. Set `strictRecipientMatch: true` to
28
+ * disable the trial fallback.
29
+ *
30
+ * For compact (string) tokens, use {@link decrypt}.
31
+ */
32
+ declare function decryptMulti<T extends JOSEPayload = JOSEPayload>(jwe: JWEGeneralSerialization | JWEFlattenedSerialization, keyOrLookup: CryptoKey | JWKSet | JWEDecryptJWK | string | Uint8Array<ArrayBuffer> | JWKLookupFunction, options?: JWEMultiDecryptOptions): Promise<JWEMultiDecryptResult<T>>;
33
+ /**
34
+ * Convert a {@link JWEGeneralSerialization} with exactly one recipient into
35
+ * the {@link JWEFlattenedSerialization} form (RFC 7516 §7.2.2). Throws
36
+ * `ERR_JWE_INVALID_SERIALIZATION` when the input has zero or multiple
37
+ * recipients — Flattened is strictly single-recipient.
38
+ *
39
+ * `encryptMulti` never emits Flattened itself (stable return shape across
40
+ * recipient counts); this is the canonical post-processing step when
41
+ * interoperating with a strict Flattened-only consumer.
42
+ *
43
+ * @example
44
+ * const general = await encryptMulti(payload, [recipient], opts);
45
+ * const flattened = generalToFlattened(general);
46
+ */
47
+ declare function generalToFlattened(jwe: JWEGeneralSerialization): JWEFlattenedSerialization;
48
+ /**
49
+ * Encrypts a payload to produce a JWE Compact Serialization string.
50
+ *
51
+ * @param payload The payload to encrypt. Can be a string, Uint8Array, or a JSON-serializable object.
52
+ * @param key The key encryption key (KEK). Can be a CryptoKey, JWK, or for PBES2, a password string/Uint8Array.
53
+ * @param options JWE encryption options, including `alg` (key management algorithm) and `enc` (content encryption algorithm).
54
+ * @returns A Promise resolving to the JWE Compact Serialization string.
55
+ */
56
+ declare function encrypt(payload: JOSEPayload, key: JWEEncryptJWK | string | Uint8Array<ArrayBuffer>, options?: JWEEncryptOptions): Promise<string>;
57
+ declare function encrypt(payload: JOSEPayload, key: CryptoKey | Extract<JWEEncryptJWK, {
58
+ kty: "oct";
59
+ }> | Uint8Array<ArrayBuffer>, options: JWEEncryptOptions & {
60
+ alg: "dir";
61
+ enc: ContentEncryptionAlgorithm;
62
+ }): Promise<string>;
63
+ declare function encrypt(payload: JOSEPayload, key: CryptoKey, options: JWEEncryptOptions & {
64
+ alg: KeyManagementAlgorithm;
65
+ enc: ContentEncryptionAlgorithm;
66
+ }): Promise<string>;
67
+ declare function encrypt(payload: JOSEPayload, key: CryptoKey | JWEEncryptJWK | string | Uint8Array<ArrayBuffer>, options: JWEEncryptOptions & {
68
+ alg: KeyManagementAlgorithm;
69
+ enc: ContentEncryptionAlgorithm;
70
+ }): Promise<string>;
71
+ /**
72
+ * Decrypts a JWE Compact Serialization string.
73
+ *
74
+ * @param jwe The JWE Compact Serialization string.
75
+ * @param key The key decryption key (KEK) or a function to look up the key.
76
+ * @param options JWE decryption options.
77
+ * @returns A Promise resolving to an object containing the decrypted plaintext, protected header, CEK, and AAD.
78
+ * @throws If JWE is invalid, decryption fails, or options are not met.
79
+ */
80
+ declare function decrypt<T extends JOSEPayload>(jwe: string, key: CryptoKey | JWKSet | JWEDecryptJWK | string | Uint8Array<ArrayBuffer> | JWKLookupFunction, options?: JWEDecryptOptions): Promise<JWEDecryptResult<T>>;
81
+ declare function decrypt(jwe: string, key: CryptoKey | JWKSet | JWEDecryptJWK | string | Uint8Array<ArrayBuffer> | JWKLookupFunction, options: JWEDecryptOptions & {
82
+ forceUint8Array: true;
83
+ }): Promise<JWEDecryptResult<Uint8Array<ArrayBuffer>>>;
84
+ export { generalToFlattened as a, encryptMulti as i, encrypt as n, decryptMulti as r, decrypt as t };
@@ -0,0 +1,407 @@
1
+ import { D as JWTError, S as validateCriticalHeadersJWE, b as getPlaintextBytes, f as applyTypCtyDefaults, h as computeJwtTimeClaims, i as isCryptoKey, o as isJWK, s as isJWKSet, y as decodePayloadFromBytes } from "./utils.mjs";
2
+ import { a as base64UrlEncode, i as base64UrlDecode, s as textEncoder, t as secureRandomBytes, u as sanitizeObjectCopy } from "./libs/unsecure.mjs";
3
+ import { C as generateIV, S as generateCEK, _ as unwrapKey, b as decrypt$1, h as importKey, p as getJWKsFromSet, x as encrypt$1, y as encryptKey } from "./jwk.mjs";
4
+ import { i as checkAlgAllowed, l as validateJwtClaimsIfJsonPayload, o as decodeProtectedHeader, r as buildJWKSetFilter, s as parseEphemeralKey, t as JWE_ALG_CTX } from "./_internal.mjs";
5
+ const DIRECT_ALGS = /* @__PURE__ */ new Set(["dir", "ECDH-ES"]);
6
+ async function encryptMulti(payload, recipients, options = {}) {
7
+ if (!Array.isArray(recipients) || recipients.length === 0) throw new JWTError("encryptMulti requires at least one recipient.", "ERR_JWE_INVALID");
8
+ const resolvedAlgs = recipients.map((r, i) => _resolveRecipientAlg(r.key, i));
9
+ const enc = options.enc ?? "A256GCM";
10
+ const userProtected = sanitizeObjectCopy(options.protectedHeader);
11
+ const sharedUnprotected = sanitizeObjectCopy(options.sharedUnprotectedHeader);
12
+ const protectedHeader = {};
13
+ if (userProtected) Object.assign(protectedHeader, userProtected);
14
+ protectedHeader.enc = enc;
15
+ applyTypCtyDefaults(protectedHeader, payload);
16
+ const plaintextBytes = getPlaintextBytes(computeJwtTimeClaims(payload, options) || payload);
17
+ const cek = options.cek ?? generateCEK(enc);
18
+ const wireRecipients = [];
19
+ for (let i = 0; i < recipients.length; i++) {
20
+ const recipient = recipients[i];
21
+ const alg = resolvedAlgs[i];
22
+ const keyMgmtParams = _buildRecipientKeyMgmtParams(recipient, alg);
23
+ const { encryptedKey, parameters: keyMgmtOut } = await encryptKey(alg, enc, await importKey(recipient.key, {
24
+ alg,
25
+ expect: "public"
26
+ }), cek, keyMgmtParams);
27
+ const perRecipientHeader = _buildPerRecipientHeader(recipient, alg, keyMgmtOut);
28
+ _assertDisjointHeaders(protectedHeader, sharedUnprotected, perRecipientHeader);
29
+ const wireRecipient = {};
30
+ if (Object.keys(perRecipientHeader).length > 0) wireRecipient.header = perRecipientHeader;
31
+ if (encryptedKey) wireRecipient.encrypted_key = base64UrlEncode(encryptedKey);
32
+ wireRecipients.push(wireRecipient);
33
+ }
34
+ const protectedHeaderEncoded = base64UrlEncode(JSON.stringify(protectedHeader));
35
+ let aadEncoded;
36
+ let aadForCipher = protectedHeaderEncoded;
37
+ if (options.aad !== void 0) {
38
+ aadEncoded = base64UrlEncode(typeof options.aad === "string" ? textEncoder.encode(options.aad) : options.aad);
39
+ aadForCipher = `${protectedHeaderEncoded}.${aadEncoded}`;
40
+ }
41
+ 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");
44
+ const output = {
45
+ protected: protectedHeaderEncoded,
46
+ recipients: wireRecipients,
47
+ iv: base64UrlEncode(actualContentIV),
48
+ ciphertext: base64UrlEncode(contentCiphertext),
49
+ tag: base64UrlEncode(contentAuthTag)
50
+ };
51
+ if (sharedUnprotected && Object.keys(sharedUnprotected).length > 0) output.unprotected = sharedUnprotected;
52
+ if (aadEncoded !== void 0) output.aad = aadEncoded;
53
+ return output;
54
+ }
55
+ async function decryptMulti(jwe, keyOrLookup, options = {}) {
56
+ const general = _normalizeSerialization(jwe);
57
+ 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");
58
+ const protectedHeaderEncoded = general.protected ?? "";
59
+ 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
+ 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);
68
+ let lastError;
69
+ let cryptoAttempted = false;
70
+ for (let i = 0; i < general.recipients.length; i++) {
71
+ const wireRecipient = general.recipients[i];
72
+ const recipientHeader = sanitizeObjectCopy(wireRecipient.header);
73
+ _assertDisjointHeaders(protectedHeader, sharedUnprotected, recipientHeader);
74
+ const effective = _mergeHeaders(protectedHeader, sharedUnprotected, recipientHeader);
75
+ const alg = effective.alg;
76
+ if (!alg) {
77
+ lastError = new JWTError(`Recipient[${i}] has no "alg" in the effective header.`, "ERR_JWE_INVALID");
78
+ continue;
79
+ }
80
+ const rawKeyMaterial = typeof keyOrLookup === "function" ? await keyOrLookup(effective, protectedHeaderEncoded) : keyOrLookup;
81
+ const algError = checkAlgAllowed(alg, rawKeyMaterial, options.algorithms, JWE_ALG_CTX);
82
+ if (algError) {
83
+ lastError = algError;
84
+ continue;
85
+ }
86
+ if (options.strictRecipientMatch && !_recipientKeyMatches(effective, rawKeyMaterial)) continue;
87
+ const encryptedKeyBytes = base64UrlDecode(wireRecipient.encrypted_key ?? "", { returnAs: "uint8array" });
88
+ const unwrapKeyOpts = _buildUnwrapKeyOptions(effective, enc, options);
89
+ cryptoAttempted = true;
90
+ 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;
111
+ } 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;
114
+ }
115
+ }
116
+ if (options.strictRecipientMatch && !cryptoAttempted) throw new JWTError("No recipient matched the provided key under strictRecipientMatch.", "ERR_JWE_NO_MATCHING_RECIPIENT");
117
+ throw lastError ?? new JWTError("JWE decryption failed.", "ERR_JWE_DECRYPTION_FAILED");
118
+ }
119
+ function generalToFlattened(jwe) {
120
+ if (jwe.recipients.length !== 1) throw new JWTError(`Flattened JWE Serialization requires exactly one recipient, got ${jwe.recipients.length}.`, "ERR_JWE_INVALID_SERIALIZATION");
121
+ const { recipients, ...rest } = jwe;
122
+ const [recipient] = recipients;
123
+ const flattened = { ...rest };
124
+ if (recipient.header !== void 0) flattened.header = recipient.header;
125
+ if (recipient.encrypted_key !== void 0) flattened.encrypted_key = recipient.encrypted_key;
126
+ return flattened;
127
+ }
128
+ function _resolveRecipientAlg(key, index) {
129
+ 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");
133
+ return alg;
134
+ }
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
+ function _buildPerRecipientHeader(recipient, alg, keyMgmtOut) {
152
+ const header = {};
153
+ if (recipient.key.kid) header.kid = recipient.key.kid;
154
+ const userHeader = sanitizeObjectCopy(recipient.header);
155
+ if (userHeader) Object.assign(header, userHeader);
156
+ if (keyMgmtOut) Object.assign(header, keyMgmtOut);
157
+ header.alg = alg;
158
+ return header;
159
+ }
160
+ function _assertDisjointHeaders(protectedHeader, sharedUnprotected, recipientHeader) {
161
+ const seen = /* @__PURE__ */ new Set();
162
+ const check = (obj) => {
163
+ if (!obj) return;
164
+ for (const k of Object.keys(obj)) {
165
+ if (seen.has(k)) throw new JWTError(`JWE header parameter "${k}" appears in multiple header tiers; protected, shared unprotected, and per-recipient headers must be disjoint (RFC 7516 §7.2.1).`, "ERR_JWE_HEADER_PARAMS_NOT_DISJOINT");
166
+ seen.add(k);
167
+ }
168
+ };
169
+ check(protectedHeader);
170
+ check(sharedUnprotected);
171
+ check(recipientHeader);
172
+ }
173
+ function _mergeHeaders(protectedHeader, sharedUnprotected, recipientHeader) {
174
+ const merged = { ...protectedHeader };
175
+ if (sharedUnprotected) Object.assign(merged, sharedUnprotected);
176
+ if (recipientHeader) Object.assign(merged, recipientHeader);
177
+ return merged;
178
+ }
179
+ 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;
207
+ }
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"]
215
+ };
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;
228
+ }
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
+ }
269
+ }
270
+ async function encrypt(payload, key, options = {}) {
271
+ const { protectedHeader: userHeader, cek: providedCek, contentEncryptionIV: providedContentIV, keyManagementIV, p2s, p2c, ecdh } = options;
272
+ let { alg, enc } = options;
273
+ if (!alg) {
274
+ if (typeof key === "string" || key instanceof Uint8Array) alg = "PBES2-HS256+A128KW";
275
+ else if (isJWK(key)) alg = key.alg;
276
+ }
277
+ 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;
294
+ }
295
+ const wrappingKeyMaterial = await importKey(key, {
296
+ alg,
297
+ expect: "public"
298
+ });
299
+ const { cek: finalCek, encryptedKey: jweEncryptedKey, parameters: keyManagementHeaderParams } = await encryptKey(alg, enc, wrappingKeyMaterial, providedCek, jweKeyManagementParams);
300
+ const protectedHeader = _buildJWEHeader(alg, enc, key, userHeader, keyManagementHeaderParams, payload);
301
+ const plaintextBytes = getPlaintextBytes(computeJwtTimeClaims(payload, options) || payload);
302
+ const protectedHeaderEncoded = base64UrlEncode(JSON.stringify(protectedHeader));
303
+ const aadBytes = textEncoder.encode(protectedHeaderEncoded);
304
+ const contentIVBytes = providedContentIV ?? generateIV(enc);
305
+ 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
+ return [
309
+ protectedHeaderEncoded,
310
+ jweEncryptedKey ? base64UrlEncode(jweEncryptedKey) : "",
311
+ base64UrlEncode(actualContentIV),
312
+ base64UrlEncode(contentCiphertext),
313
+ base64UrlEncode(contentAuthTag)
314
+ ].join(".");
315
+ }
316
+ async function decrypt(jwe, key, options = {}) {
317
+ const parts = jwe.split(".");
318
+ if (parts.length !== 5) throw new JWTError("Invalid JWE: Must contain five sections (RFC7516, section-3).", "ERR_JWE_INVALID");
319
+ const [protectedHeaderEncoded, encryptedKeyEncoded, ivEncoded, ciphertextEncoded, authTagEncoded] = parts;
320
+ 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);
327
+ 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;
348
+ 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);
387
+ const result = {
388
+ payload,
389
+ protectedHeader
390
+ };
391
+ if (options.returnCek) {
392
+ result.cek = isCryptoKey(cek) ? new Uint8Array(await crypto.subtle.exportKey("raw", cek)) : cek;
393
+ result.aad = aadBytes;
394
+ }
395
+ return result;
396
+ }
397
+ function _buildJWEHeader(alg, enc, key, userHeader, keyManagementParams, payload) {
398
+ const safeHeader = sanitizeObjectCopy(userHeader);
399
+ const header = {};
400
+ if (isJWK(key) && key.kid) header.kid = key.kid;
401
+ if (safeHeader) Object.assign(header, safeHeader);
402
+ if (keyManagementParams) Object.assign(header, keyManagementParams);
403
+ header.alg = alg;
404
+ header.enc = enc;
405
+ return applyTypCtyDefaults(header, payload);
406
+ }
407
+ export { generalToFlattened as a, encryptMulti as i, encrypt as n, decryptMulti as r, decrypt as t };