unjwt 0.8.4 → 0.8.5

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 (53) hide show
  1. package/README.md +3 -9
  2. package/dist/_chunks/{_import-key.mjs → algorithms.mjs} +194 -399
  3. package/dist/_chunks/{_codec-jwe.d.mts → codec-jwe.d.mts} +40 -9
  4. package/dist/_chunks/{_codec-jws.mjs → codec-jws.mjs} +133 -80
  5. package/dist/_chunks/cookie.mjs +50 -0
  6. package/dist/_chunks/digest.mjs +10 -3
  7. package/dist/_chunks/error.d.mts +103 -0
  8. package/dist/_chunks/export.mjs +97 -0
  9. package/dist/_chunks/index.d.mts +5 -103
  10. package/dist/_chunks/index2.d.mts +206 -12
  11. package/dist/_chunks/index3.d.mts +12 -109
  12. package/dist/_chunks/{jwe.d.mts → index4.d.mts} +2 -1
  13. package/dist/_chunks/{jws.d.mts → index5.d.mts} +2 -1
  14. package/dist/_chunks/index6.d.mts +113 -0
  15. package/dist/_chunks/jwe.mjs +205 -72
  16. package/dist/{core → _chunks}/jwk.mjs +210 -495
  17. package/dist/_chunks/jws.mjs +280 -193
  18. package/dist/_chunks/jwt.d.mts +2 -1
  19. package/dist/_chunks/{_jwt-internal.mjs → jwt.mjs} +110 -54
  20. package/dist/_chunks/libs/unsecure.d.mts +35 -25
  21. package/dist/_chunks/libs/unsecure.mjs +101 -268
  22. package/dist/_chunks/{_internal.d.mts → limits.d.mts} +1 -1
  23. package/dist/_chunks/payload.mjs +23 -0
  24. package/dist/_chunks/wrap.mjs +209 -0
  25. package/dist/adapters/elysia/index.d.mts +4 -4
  26. package/dist/adapters/elysia/index.mjs +4 -3
  27. package/dist/adapters/h3/index.d.mts +13 -13
  28. package/dist/adapters/h3/index.mjs +8 -11
  29. package/dist/adapters/h3v1/index.d.mts +5 -5
  30. package/dist/adapters/h3v1/index.mjs +6 -9
  31. package/dist/core/internal.mjs +1 -1
  32. package/dist/core/{jwe.d.mts → jwe/index.d.mts} +5 -4
  33. package/dist/core/jwe/index.mjs +5 -0
  34. package/dist/core/{jwk.d.mts → jwk/index.d.mts} +5 -4
  35. package/dist/core/jwk/index.mjs +7 -0
  36. package/dist/core/{jws.d.mts → jws/index.d.mts} +5 -4
  37. package/dist/core/jws/index.mjs +5 -0
  38. package/dist/dpop/index.d.mts +73 -0
  39. package/dist/dpop/index.mjs +296 -0
  40. package/dist/error.d.mts +3 -0
  41. package/dist/error.mjs +20 -0
  42. package/dist/index.d.mts +10 -9
  43. package/dist/index.mjs +7 -4
  44. package/dist/session/index.d.mts +4 -4
  45. package/dist/session/index.mjs +6 -47
  46. package/dist/utils/index.d.mts +7 -0
  47. package/dist/{core/utils → utils}/index.mjs +4 -3
  48. package/package.json +21 -10
  49. package/dist/_chunks/jwk.d.mts +0 -205
  50. package/dist/core/jwe.mjs +0 -5
  51. package/dist/core/jws.mjs +0 -5
  52. package/dist/core/utils/index.d.mts +0 -6
  53. /package/dist/_chunks/{_cookie-attributes.d.mts → cookie-attributes.d.mts} +0 -0
@@ -1,377 +1,10 @@
1
- import { UnsecureError, sanitizeObjectCopy, secureRandomBytes, textEncoder } from "../_chunks/libs/unsecure.mjs";
2
- import { JWTError, assertPlainObject, isAsymmetricJWK, isCryptoKey, isCryptoKeyPair, isJWK, isJWTError, isPublicJWK, isUnsecureError } from "../_chunks/_jwt-internal.mjs";
3
- import { ECDSA_CURVES, PBES2_ALGORITHMS, aesBitsOf, innerKeyWrapAlg, isECDHESAlgorithm, jwsHashOf, rsaOaepHashOf } from "../_chunks/digest.mjs";
4
- import { MAX_PARTY_INFO_BYTES, WeakMapJWKCache, aesKwUnwrap, aesKwWrap, asDecryptionError, assertDirectKeyRole, assertECDHKeyAllowed, assertJWEEncSupported, assertOctKeyLength, assertPBES2Count, assertPBES2Params, assertPBES2Salt, bitLengthCEK, bitLengthIV, bitLengthTag, buildECDHAgreementParams, clearJWKCache, configureJWKCache, decodeSegment, decryptRSAES, deriveECDHESKey, deriveKey, ecdhDerivedBits, ecdhesWrap, encryptRSAES, fromPKCS8, fromSPKI, fromX509, gcmkwDecrypt, gcmkwEncrypt, getJWKsFromSet, importKey, keyToJWK, normalizeEpk, pbes2Unwrap, pbes2Wrap, resolvePasswordKey, thumbprint, toPKCS8, toSPKI } from "../_chunks/_import-key.mjs";
5
- async function generateKey(alg, options = {}) {
6
- const exportToJWK = options.toJWK !== void 0 && options.toJWK;
7
- const defaultExtractable = exportToJWK || options.extractable !== false;
8
- const kid = exportToJWK ? assertKidOption(options.kid) : void 0;
9
- if (alg === "A128CBC-HS256" || alg === "A192CBC-HS384" || alg === "A256CBC-HS512") {
10
- const keyLength = bitLengthCEK(alg);
11
- const keyBytes = secureRandomBytes(keyLength >> 3);
12
- if (exportToJWK) {
13
- const jwk = await keyToJWK(keyBytes);
14
- const keyOps = options.keyUsage === void 0 ? void 0 : resolveKeyUsage(options.keyUsage, {
15
- allowed: CONTENT_ENCRYPTION_USAGES,
16
- fallback: ["encrypt", "decrypt"],
17
- alg
18
- });
19
- return withGeneratedKid(kid, withKeyOps({
20
- ...jwk,
21
- alg
22
- }, keyOps));
23
- }
24
- return keyBytes;
25
- }
26
- const { algorithm, keyUsages } = getGenerateKeyParams(alg, options);
27
- const key = await crypto.subtle.generateKey(algorithm, defaultExtractable, keyUsages);
28
- if (exportToJWK) {
29
- if (isCryptoKeyPair(key)) {
30
- const pairAlg = generatedPairAlgorithm(alg);
31
- const [pub, priv] = await Promise.all([exportJWK(key.publicKey), exportJWK(key.privateKey)]);
32
- const publicKey = await withGeneratedKid(kid, withKeyOps(withAlg(pub, pairAlg), requestedKeyOps(options, key.publicKey)));
33
- const named = publicKey.kid === void 0 ? void 0 : { kid: publicKey.kid };
34
- return {
35
- privateKey: {
36
- ...withKeyOps(withAlg(priv, pairAlg), requestedKeyOps(options, key.privateKey)),
37
- ...named
38
- },
39
- publicKey
40
- };
41
- }
42
- return withGeneratedKid(kid, withKeyOps(withAlg(await exportJWK(key), generatedSecretAlgorithm(alg)), requestedKeyOps(options, key)));
43
- }
44
- return key;
45
- }
46
- async function generateJWK(alg, options) {
47
- const { keyUsage, modulusLength, publicExponent, namedCurve, kid, extractable: _extractable, toJWK: _toJWK, ...jwkParams } = options ?? {};
48
- assertNoReservedJWKParams(jwkParams);
49
- const result = await generateKey(alg, {
50
- keyUsage,
51
- modulusLength,
52
- publicExponent,
53
- namedCurve,
54
- kid,
55
- toJWK: true
56
- });
57
- if (result && typeof result === "object" && "privateKey" in result && "publicKey" in result) return {
58
- privateKey: {
59
- ...jwkParams,
60
- ...result.privateKey
61
- },
62
- publicKey: {
63
- ...jwkParams,
64
- ...result.publicKey
65
- }
66
- };
67
- return {
68
- ...jwkParams,
69
- ...result
70
- };
71
- }
72
- async function deriveKeyFromPassword(password, options) {
73
- assertPlainObject(options, "options");
74
- if (typeof password !== "string" && !(password instanceof Uint8Array)) throw new JWTError("\"password\" must be a string or a Uint8Array.", "ERR_INVALID_OPTION");
75
- const { alg, salt, iterations, extractable, keyUsage } = options;
76
- assertPBES2Algorithm(alg);
77
- const exportToJWK = options.toJWK !== void 0 && options.toJWK;
78
- const saltBytes = typeof salt === "string" ? textEncoder.encode(salt) : salt;
79
- assertPBES2Salt(saltBytes, "\"salt\"");
80
- assertPBES2Count(iterations, "\"iterations\"");
81
- const passwordBytes = typeof password === "string" ? textEncoder.encode(password) : password;
82
- const derivedBytes = await deriveKey(saltBytes, alg, iterations, passwordBytes);
83
- const wrappingAlg = innerKeyWrapAlg(alg);
84
- const finalUsages = keyUsage ?? ["wrapKey", "unwrapKey"];
85
- const finalExtractable = exportToJWK ? true : extractable === true;
86
- const derivedKey = await crypto.subtle.importKey("raw", derivedBytes, { name: "AES-KW" }, finalExtractable, finalUsages);
87
- if (exportToJWK) return withKeyOps({
88
- ...await keyToJWK(derivedKey),
89
- alg: wrappingAlg,
90
- kty: "oct"
91
- }, requestedKeyOps(options, derivedKey));
92
- return derivedKey;
93
- }
94
- async function deriveJWKFromPassword(password, options) {
95
- assertPlainObject(options, "options");
96
- const { alg, salt, iterations, keyUsage, kid: kidOption, extractable: _extractable, toJWK: _toJWK, ...jwkParams } = options;
97
- assertNoReservedJWKParams(jwkParams);
98
- const kid = assertKidOption(kidOption);
99
- const result = await deriveKeyFromPassword(password, {
100
- alg,
101
- salt,
102
- iterations,
103
- keyUsage,
104
- toJWK: true
105
- });
106
- return {
107
- ...jwkParams,
108
- ...await withGeneratedKid(kid, result)
109
- };
110
- }
111
- async function exportJWK(key, jwkParams) {
112
- if (!isCryptoKey(key)) throw new JWTError("exportJWK expects a CryptoKey.", "ERR_INVALID_OPTION");
113
- if (!key.extractable) throw new JWTError("A non-extractable CryptoKey cannot be exported as a JWK.", "ERR_JWK_INVALID");
114
- const exportedJwk = await keyToJWK(key);
115
- if (jwkParams) {
116
- const extraParams = sanitizeObjectCopy(jwkParams);
117
- assertNoReservedJWKParams(extraParams);
118
- return {
119
- ...exportedJwk,
120
- ...extraParams
121
- };
122
- }
123
- return exportedJwk;
124
- }
125
- async function exportKey(key, jwkParams) {
126
- return exportJWK(key, jwkParams);
127
- }
128
- function toPublicJWK(jwk) {
129
- if (!isAsymmetricJWK(jwk)) throw new JWTError(isJWK(jwk) ? "An oct JWK has no public half." : "toPublicJWK expects an asymmetric JWK.", "ERR_JWK_INVALID");
130
- const { d: _d, p: _p, q: _q, dp: _dp, dq: _dq, qi: _qi, oth: _oth, key_ops, ...publicMembers } = sanitizeObjectCopy({ ...jwk });
131
- const publicKey = key_ops === void 0 ? publicMembers : {
132
- ...publicMembers,
133
- key_ops: publicKeyOps(key_ops)
134
- };
135
- if (!isPublicJWK(publicKey)) throw new JWTError(`${jwk.kty} JWK lacks the public members of its key type.`, "ERR_JWK_INVALID");
136
- return publicKey;
137
- }
138
- async function wrapKey(keyToWrap, wrappingKey, options) {
139
- assertPlainObject(options, "options");
140
- assertKeyBytesArg(keyToWrap, "keyToWrap");
141
- const { alg } = options;
142
- const cekBytes = keyToWrap instanceof Uint8Array ? keyToWrap : new Uint8Array(await crypto.subtle.exportKey("raw", keyToWrap));
143
- if (alg.endsWith("KW")) assertWrappableCEK(cekBytes, alg);
144
- if (alg.startsWith("PBES2")) assertPBES2Params({
145
- p2s: options.p2s,
146
- p2c: options.p2c
147
- });
148
- const importedWrappingKey = await importKey(resolvePasswordKey(alg, wrappingKey), {
149
- alg,
150
- expect: "public"
151
- });
152
- switch (alg) {
153
- case "dir": return { encryptedKey: /* @__PURE__ */ new Uint8Array(0) };
154
- case "A128KW":
155
- case "A192KW":
156
- case "A256KW": return { encryptedKey: await aesKwWrap(alg, importedWrappingKey, cekBytes) };
157
- case "PBES2-HS256+A128KW":
158
- case "PBES2-HS384+A192KW":
159
- case "PBES2-HS512+A256KW": {
160
- const { p2s, p2c } = options;
161
- if (!p2s || typeof p2c !== "number") throw new JWTError("PBES2 requires 'p2s' (salt) and 'p2c' (count) options", "ERR_JWE_INVALID");
162
- return pbes2Wrap(alg, importedWrappingKey, cekBytes, p2c, p2s);
163
- }
164
- case "A128GCMKW":
165
- case "A192GCMKW":
166
- case "A256GCMKW": {
167
- const { encryptedKey, iv, tag } = await gcmkwEncrypt(alg, importedWrappingKey, cekBytes, options.iv);
168
- return {
169
- encryptedKey,
170
- iv,
171
- tag
172
- };
173
- }
174
- case "RSA-OAEP":
175
- case "RSA-OAEP-256":
176
- case "RSA-OAEP-384":
177
- case "RSA-OAEP-512":
178
- assertCryptoKeyFor(alg, importedWrappingKey, "wrapping");
179
- return { encryptedKey: await encryptRSAES(alg, importedWrappingKey, cekBytes) };
180
- case "ECDH-ES":
181
- case "ECDH-ES+A128KW":
182
- case "ECDH-ES+A192KW":
183
- case "ECDH-ES+A256KW": {
184
- assertCryptoKeyFor(alg, importedWrappingKey, "wrapping");
185
- const enc = options.ecdh?.enc;
186
- if (enc !== void 0) assertJWEEncSupported(enc);
187
- if (alg === "ECDH-ES" && !enc) throw new JWTError("ECDH-ES direct key agreement requires options.ecdh.enc", "ERR_JWK_INVALID");
188
- const { encryptedKey, parameters } = await ecdhesWrap(alg, {
189
- enc,
190
- recipientKey: importedWrappingKey,
191
- cek: cekBytes,
192
- parameters: buildECDHAgreementParams(options.ecdh)
193
- });
194
- const wrapResult = {
195
- encryptedKey: encryptedKey ?? /* @__PURE__ */ new Uint8Array(0),
196
- epk: parameters.epk
197
- };
198
- if (parameters.apu) wrapResult.apu = parameters.apu;
199
- if (parameters.apv) wrapResult.apv = parameters.apv;
200
- return wrapResult;
201
- }
202
- default: throw new JWTError(`Unsupported key wrapping algorithm: ${alg}`, "ERR_JWK_UNSUPPORTED");
203
- }
204
- }
205
- async function unwrapKey(wrappedKey, unwrappingKey, options) {
206
- assertPlainObject(options, "options");
207
- if (!(wrappedKey instanceof Uint8Array)) throw new JWTError("\"wrappedKey\" must be a Uint8Array.", "ERR_INVALID_OPTION");
208
- const { alg } = options;
209
- const returnRaw = options.format === "raw";
210
- const defaultExtractable = options.extractable !== false;
211
- const importedUnwrappingKey = await importKey(resolvePasswordKey(alg, unwrappingKey), {
212
- alg,
213
- expect: "private"
214
- });
215
- let unwrappedCekBytes;
216
- switch (alg) {
217
- case "dir":
218
- assertDirectKeyRole(unwrappingKey, options.enc);
219
- if (importedUnwrappingKey instanceof Uint8Array) {
220
- assertOctKeyLength(importedUnwrappingKey, options.enc);
221
- unwrappedCekBytes = importedUnwrappingKey;
222
- } else {
223
- if (!returnRaw) return importedUnwrappingKey;
224
- unwrappedCekBytes = new Uint8Array(await crypto.subtle.exportKey("raw", importedUnwrappingKey));
225
- }
226
- break;
227
- case "A128KW":
228
- case "A192KW":
229
- case "A256KW":
230
- unwrappedCekBytes = await unwrapping(aesKwUnwrap(alg, importedUnwrappingKey, wrappedKey));
231
- break;
232
- case "PBES2-HS256+A128KW":
233
- case "PBES2-HS384+A192KW":
234
- case "PBES2-HS512+A256KW": {
235
- const { p2s, p2c, minIterations, maxIterations } = options;
236
- if (!p2s || typeof p2c !== "number") throw new JWTError("PBES2 requires 'p2s' (salt) and 'p2c' (count) options", "ERR_JWE_INVALID");
237
- const p2sBytes = segmentBytes(p2s, "p2s");
238
- unwrappedCekBytes = await unwrapping(pbes2Unwrap(alg, importedUnwrappingKey, wrappedKey, p2c, p2sBytes, minIterations, maxIterations));
239
- break;
240
- }
241
- case "A128GCMKW":
242
- case "A192GCMKW":
243
- case "A256GCMKW": {
244
- if (!options.iv || !options.tag) throw new JWTError("AES-GCMKW requires 'iv' and 'tag' options for unwrapping", "ERR_JWE_INVALID");
245
- const ivBytes = segmentBytes(options.iv, "iv");
246
- const tagBytes = segmentBytes(options.tag, "tag");
247
- assertKeyWrapSegmentLength("iv", ivBytes, bitLengthIV(alg) >> 3, alg);
248
- assertKeyWrapSegmentLength("tag", tagBytes, bitLengthTag(alg) >> 3, alg);
249
- unwrappedCekBytes = await unwrapping(gcmkwDecrypt(alg, importedUnwrappingKey, wrappedKey, ivBytes, tagBytes));
250
- break;
251
- }
252
- case "RSA-OAEP":
253
- case "RSA-OAEP-256":
254
- case "RSA-OAEP-384":
255
- case "RSA-OAEP-512": {
256
- assertCryptoKeyFor(alg, importedUnwrappingKey, "unwrapping");
257
- unwrappedCekBytes = await unwrapping(decryptRSAES(alg, importedUnwrappingKey, wrappedKey));
258
- if (returnRaw) break;
259
- const inferredAlgorithm = options.unwrappedKeyAlgorithm || inferAesImportAlgorithm(options.enc, unwrappedCekBytes.length);
260
- if (!inferredAlgorithm) throw new JWTError("Cannot infer the RSA-OAEP unwrapped key algorithm, pass \"unwrappedKeyAlgorithm\".", "ERR_JWE_INVALID");
261
- const keyUsages = options.keyUsage || ["encrypt", "decrypt"];
262
- return await crypto.subtle.importKey("raw", unwrappedCekBytes, inferredAlgorithm, defaultExtractable, keyUsages);
263
- }
264
- case "ECDH-ES":
265
- case "ECDH-ES+A128KW":
266
- case "ECDH-ES+A192KW":
267
- case "ECDH-ES+A256KW": {
268
- if (!options.epk) throw new JWTError("ECDH-ES requires 'epk' option", "ERR_JWE_INVALID");
269
- assertCryptoKeyFor(alg, importedUnwrappingKey, "unwrapping");
270
- assertECDHKeyAllowed(importedUnwrappingKey);
271
- const epkCandidate = await normalizeEpk(options.epk, {
272
- alg,
273
- recipientKey: importedUnwrappingKey
274
- });
275
- const apuBytes = segmentBytes(options.apu ?? /* @__PURE__ */ new Uint8Array(0), "apu");
276
- const apvBytes = segmentBytes(options.apv ?? /* @__PURE__ */ new Uint8Array(0), "apv");
277
- assertPartyInfoLength("apu", apuBytes);
278
- assertPartyInfoLength("apv", apvBytes);
279
- if (options.enc !== void 0) assertJWEEncSupported(options.enc);
280
- const infoAlg = alg === "ECDH-ES" ? options.enc : alg;
281
- if (!infoAlg) throw new JWTError("ECDH-ES requires 'enc' to derive the shared secret", "ERR_JWE_INVALID");
282
- const keyLength = ecdhDerivedBits(alg, options.enc);
283
- const sharedSecret = await unwrapping(deriveECDHESKey(epkCandidate, importedUnwrappingKey, infoAlg, keyLength, apuBytes, apvBytes));
284
- if (alg === "ECDH-ES") unwrappedCekBytes = sharedSecret;
285
- else {
286
- const kwAlg = innerKeyWrapAlg(alg);
287
- if (!(wrappedKey instanceof Uint8Array) || wrappedKey.length === 0) throw new JWTError("ECDH-ES key agreement with key wrapping requires an encrypted key", "ERR_JWE_INVALID");
288
- unwrappedCekBytes = await unwrapping(aesKwUnwrap(kwAlg, sharedSecret, wrappedKey));
289
- }
290
- break;
291
- }
292
- default: throw new JWTError(`Unsupported key unwrapping algorithm: ${alg}`, "ERR_JWK_UNSUPPORTED");
293
- }
294
- if (returnRaw) return unwrappedCekBytes;
295
- return await crypto.subtle.importKey("raw", unwrappedCekBytes, options.unwrappedKeyAlgorithm || { name: "AES-GCM" }, defaultExtractable, options.keyUsage || ["encrypt", "decrypt"]);
296
- }
297
- async function importPEM(pem, alg, options) {
298
- if (typeof pem !== "string") throw new JWTError("\"pem\" must be a PEM-encoded string.", "ERR_INVALID_OPTION");
299
- if (options !== void 0) assertPlainObject(options, "options");
300
- const extractable = true;
301
- const pemType = options?.pemType ?? inferPEMType(pem);
302
- if (!pemType) throw new JWTError("Cannot infer PEM type from the input, pass \"pemType\".", "ERR_JWK_INVALID");
303
- let cryptoKey;
304
- switch (pemType) {
305
- case "pkcs8":
306
- cryptoKey = await fromPKCS8(pem, alg, { extractable });
307
- break;
308
- case "spki":
309
- cryptoKey = await fromSPKI(pem, alg, { extractable });
310
- break;
311
- case "x509":
312
- cryptoKey = await fromX509(pem, alg, { extractable });
313
- break;
314
- default: throw new JWTError(`Unsupported PEM type: ${String(pemType)}`, "ERR_JWK_UNSUPPORTED");
315
- }
316
- return {
317
- ...await exportJWK(cryptoKey, options?.jwkParams),
318
- alg
319
- };
320
- }
321
- async function exportPEM(jwk, options) {
322
- assertPlainObject(jwk, "jwk");
323
- if (options !== void 0) assertPlainObject(options, "options");
324
- if (jwk.kty === "oct") throw new JWTError("An oct JWK cannot be exported to PKCS8 or SPKI PEM.", "ERR_JWK_UNSUPPORTED");
325
- const effectiveAlg = jwk.alg || options?.alg;
326
- if (!effectiveAlg && (jwk.kty === "RSA" || jwk.kty === "EC" || jwk.kty === "OKP")) throw new JWTError("\"alg\" is required, set it on the JWK or pass it in options.", "ERR_JWK_INVALID");
327
- if (effectiveAlg !== void 0 && !PEM_ALGORITHMS.has(effectiveAlg)) throw new JWTError(`Unsupported PEM algorithm: ${effectiveAlg}`, "ERR_JWK_UNSUPPORTED");
328
- const pemFormat = options?.pemFormat ?? ("d" in jwk ? "pkcs8" : "spki");
329
- const cryptoKeyCandidate = await importKey({
330
- ...jwk,
331
- ext: true
332
- }, {
333
- alg: effectiveAlg,
334
- expect: pemFormat === "pkcs8" ? "private" : "public",
335
- extractable: true
336
- });
337
- if (!isCryptoKey(cryptoKeyCandidate)) throw new JWTError("Failed to convert JWK to a CryptoKey instance suitable for PEM export.", "ERR_JWK_INVALID");
338
- const cryptoKey = cryptoKeyCandidate;
339
- switch (pemFormat) {
340
- case "pkcs8":
341
- if (cryptoKey.type !== "private") throw new JWTError(`Only a private CryptoKey can be exported to PKCS8 PEM (got ${cryptoKey.type}).`, "ERR_JWK_INVALID");
342
- return toPKCS8(cryptoKey);
343
- case "spki":
344
- if (cryptoKey.type !== "public") throw new JWTError(`Only a public CryptoKey can be exported to SPKI PEM (got ${cryptoKey.type}).`, "ERR_JWK_INVALID");
345
- return toSPKI(cryptoKey);
346
- default: throw new JWTError(`Unsupported PEM format: ${String(pemFormat)}`, "ERR_JWK_UNSUPPORTED");
347
- }
348
- }
349
- function inferPEMType(pem) {
350
- if (pem.includes("-----BEGIN PRIVATE KEY-----")) return "pkcs8";
351
- if (pem.includes("-----BEGIN PUBLIC KEY-----")) return "spki";
352
- if (pem.includes("-----BEGIN CERTIFICATE-----")) return "x509";
353
- }
354
- async function deriveSharedSecret(publicKey, privateKey, options) {
355
- assertPlainObject(options, "options");
356
- const { enc: alg, keyLength: requestedKeyLength, partyUInfo, partyVInfo } = options;
357
- if (!isECDHESAlgorithm(alg)) assertJWEEncSupported(alg);
358
- if (requestedKeyLength !== void 0) assertDerivedKeyLength(requestedKeyLength);
359
- const importAlg = isECDHESAlgorithm(alg) ? alg : "ECDH-ES";
360
- const pubCryptoKey = isCryptoKey(publicKey) ? publicKey : await importKey(publicKey, {
361
- alg: importAlg,
362
- expect: "public"
363
- });
364
- const privCryptoKey = isCryptoKey(privateKey) ? privateKey : await importKey(privateKey, {
365
- alg: importAlg,
366
- expect: "private"
367
- });
368
- let keyLength;
369
- if (requestedKeyLength !== void 0) keyLength = requestedKeyLength;
370
- else if (alg === "ECDH-ES") throw new JWTError("deriveSharedSecret with enc \"ECDH-ES\" requires an explicit keyLength in options", "ERR_JWK_INVALID");
371
- else if (isECDHESAlgorithm(alg)) keyLength = aesBitsOf(innerKeyWrapAlg(alg));
372
- else keyLength = bitLengthCEK(alg);
373
- return deriveECDHESKey(pubCryptoKey, privCryptoKey, alg, keyLength, partyUInfo ?? /* @__PURE__ */ new Uint8Array(0), partyVInfo ?? /* @__PURE__ */ new Uint8Array(0));
374
- }
1
+ import { secureRandomBytes, textEncoder } from "./libs/unsecure.mjs";
2
+ import { JWTError } from "../error.mjs";
3
+ import { assertPlainObject, isCryptoKey, isCryptoKeyPair } from "./jwt.mjs";
4
+ import { assertJWEEncSupported, assertPBES2Count, assertPBES2Salt, bitLengthCEK, deriveECDHESKey, deriveKey, fromPKCS8, fromSPKI, fromX509, importKey, keyToJWK, toPKCS8, toSPKI } from "./algorithms.mjs";
5
+ import { ECDSA_CURVES, PBES2_ALGORITHMS, aesBitsOf, innerKeyWrapAlg, isECDHESAlgorithm, jwsHashOf, rsaOaepHashOf } from "./digest.mjs";
6
+ import "./wrap.mjs";
7
+ import { assertKidOption, assertNoReservedJWKParams, exportJWK, requestedKeyOps, withGeneratedKid, withKeyOps } from "./export.mjs";
375
8
  function getGenerateKeyParams(alg, options) {
376
9
  let algorithm;
377
10
  let keyUsages;
@@ -508,43 +141,6 @@ function getGenerateKeyParams(alg, options) {
508
141
  keyUsages
509
142
  };
510
143
  }
511
- function inferAesImportAlgorithm(enc, cekLengthBytes) {
512
- const bitLength = cekLengthBytes << 3;
513
- if (enc) {
514
- if (enc.includes("GCM")) return {
515
- name: "AES-GCM",
516
- length: aesBitsOf(enc)
517
- };
518
- if (enc.includes("CBC-HS")) return;
519
- }
520
- if (bitLength === 128 || bitLength === 192 || bitLength === 256) return {
521
- name: "AES-GCM",
522
- length: bitLength
523
- };
524
- }
525
- const RESERVED_JWK_PARAMS = /* @__PURE__ */ new Set([
526
- "kty",
527
- "alg",
528
- "key_ops",
529
- "ext",
530
- "k",
531
- "d",
532
- "n",
533
- "e",
534
- "p",
535
- "q",
536
- "dp",
537
- "dq",
538
- "qi",
539
- "oth",
540
- "x",
541
- "y",
542
- "crv"
543
- ]);
544
- function assertNoReservedJWKParams(jwkParams) {
545
- const reserved = Object.keys(jwkParams).filter((name) => RESERVED_JWK_PARAMS.has(name));
546
- if (reserved.length > 0) throw new JWTError(`jwkParams must not set reserved JWK members: ${reserved.join(", ")}`, "ERR_INVALID_OPTION");
547
- }
548
144
  function assertNamedCurve(requested, expected, alg) {
549
145
  if (requested === void 0 || requested === expected) return;
550
146
  throw new JWTError(`"namedCurve" ${requested} contradicts ${alg}, which is defined over ${expected}.`, "ERR_INVALID_OPTION");
@@ -597,17 +193,193 @@ function assertPairUsages(usages, options) {
597
193
  }
598
194
  return usages;
599
195
  }
600
- function assertKidOption(kid) {
601
- if (kid === void 0 || kid === false || typeof kid === "string") return kid;
602
- throw new JWTError("\"kid\" must be a string or false.", "ERR_INVALID_OPTION");
196
+ function assertModulusLength(modulusLength) {
197
+ const length = modulusLength ?? 2048;
198
+ if (!Number.isInteger(length) || length < 2048) throw new JWTError("modulusLength must be at least 2048 bits", "ERR_INVALID_OPTION");
199
+ return length;
603
200
  }
604
- async function withGeneratedKid(kid, jwk) {
605
- if (kid === false) return jwk;
201
+ function withAlg(jwk, alg) {
606
202
  return {
607
203
  ...jwk,
608
- kid: kid ?? await thumbprint(jwk)
204
+ alg
205
+ };
206
+ }
207
+ function generatedPairAlgorithm(alg) {
208
+ return alg;
209
+ }
210
+ function generatedSecretAlgorithm(alg) {
211
+ return alg;
212
+ }
213
+ async function generateKey(alg, options = {}) {
214
+ const exportToJWK = options.toJWK !== void 0 && options.toJWK;
215
+ const defaultExtractable = exportToJWK || options.extractable !== false;
216
+ const kid = exportToJWK ? assertKidOption(options.kid) : void 0;
217
+ if (alg === "A128CBC-HS256" || alg === "A192CBC-HS384" || alg === "A256CBC-HS512") {
218
+ const keyLength = bitLengthCEK(alg);
219
+ const keyBytes = secureRandomBytes(keyLength >> 3);
220
+ if (exportToJWK) {
221
+ const jwk = await keyToJWK(keyBytes);
222
+ const keyOps = options.keyUsage === void 0 ? void 0 : resolveKeyUsage(options.keyUsage, {
223
+ allowed: CONTENT_ENCRYPTION_USAGES,
224
+ fallback: ["encrypt", "decrypt"],
225
+ alg
226
+ });
227
+ return withGeneratedKid(kid, withKeyOps({
228
+ ...jwk,
229
+ alg
230
+ }, keyOps));
231
+ }
232
+ return keyBytes;
233
+ }
234
+ const { algorithm, keyUsages } = getGenerateKeyParams(alg, options);
235
+ const key = await crypto.subtle.generateKey(algorithm, defaultExtractable, keyUsages);
236
+ if (exportToJWK) {
237
+ if (isCryptoKeyPair(key)) {
238
+ const pairAlg = generatedPairAlgorithm(alg);
239
+ const [pub, priv] = await Promise.all([exportJWK(key.publicKey), exportJWK(key.privateKey)]);
240
+ const publicKey = await withGeneratedKid(kid, withKeyOps(withAlg(pub, pairAlg), requestedKeyOps(options, key.publicKey)));
241
+ const named = publicKey.kid === void 0 ? void 0 : { kid: publicKey.kid };
242
+ return {
243
+ privateKey: {
244
+ ...withKeyOps(withAlg(priv, pairAlg), requestedKeyOps(options, key.privateKey)),
245
+ ...named
246
+ },
247
+ publicKey
248
+ };
249
+ }
250
+ return withGeneratedKid(kid, withKeyOps(withAlg(await exportJWK(key), generatedSecretAlgorithm(alg)), requestedKeyOps(options, key)));
251
+ }
252
+ return key;
253
+ }
254
+ async function generateJWK(alg, options) {
255
+ const { keyUsage, modulusLength, publicExponent, namedCurve, kid, extractable: _extractable, toJWK: _toJWK, ...jwkParams } = options ?? {};
256
+ assertNoReservedJWKParams(jwkParams);
257
+ const result = await generateKey(alg, {
258
+ keyUsage,
259
+ modulusLength,
260
+ publicExponent,
261
+ namedCurve,
262
+ kid,
263
+ toJWK: true
264
+ });
265
+ if (result && typeof result === "object" && "privateKey" in result && "publicKey" in result) return {
266
+ privateKey: {
267
+ ...jwkParams,
268
+ ...result.privateKey
269
+ },
270
+ publicKey: {
271
+ ...jwkParams,
272
+ ...result.publicKey
273
+ }
274
+ };
275
+ return {
276
+ ...jwkParams,
277
+ ...result
278
+ };
279
+ }
280
+ async function deriveKeyFromPassword(password, options) {
281
+ assertPlainObject(options, "options");
282
+ if (typeof password !== "string" && !(password instanceof Uint8Array)) throw new JWTError("\"password\" must be a string or a Uint8Array.", "ERR_INVALID_OPTION");
283
+ const { alg, salt, iterations, extractable, keyUsage } = options;
284
+ assertPBES2Algorithm(alg);
285
+ const exportToJWK = options.toJWK !== void 0 && options.toJWK;
286
+ const saltBytes = typeof salt === "string" ? textEncoder.encode(salt) : salt;
287
+ assertPBES2Salt(saltBytes, "\"salt\"");
288
+ assertPBES2Count(iterations, "\"iterations\"");
289
+ const passwordBytes = typeof password === "string" ? textEncoder.encode(password) : password;
290
+ const derivedBytes = await deriveKey(saltBytes, alg, iterations, passwordBytes);
291
+ const wrappingAlg = innerKeyWrapAlg(alg);
292
+ const finalUsages = keyUsage ?? ["wrapKey", "unwrapKey"];
293
+ const finalExtractable = exportToJWK ? true : extractable === true;
294
+ const derivedKey = await crypto.subtle.importKey("raw", derivedBytes, { name: "AES-KW" }, finalExtractable, finalUsages);
295
+ if (exportToJWK) {
296
+ const jwk = await keyToJWK(derivedKey);
297
+ return withKeyOps({
298
+ ...jwk,
299
+ alg: wrappingAlg,
300
+ kty: "oct"
301
+ }, requestedKeyOps(options, derivedKey));
302
+ }
303
+ return derivedKey;
304
+ }
305
+ async function deriveJWKFromPassword(password, options) {
306
+ assertPlainObject(options, "options");
307
+ const { alg, salt, iterations, keyUsage, kid: kidOption, extractable: _extractable, toJWK: _toJWK, ...jwkParams } = options;
308
+ assertNoReservedJWKParams(jwkParams);
309
+ const kid = assertKidOption(kidOption);
310
+ const result = await deriveKeyFromPassword(password, {
311
+ alg,
312
+ salt,
313
+ iterations,
314
+ keyUsage,
315
+ toJWK: true
316
+ });
317
+ return {
318
+ ...jwkParams,
319
+ ...await withGeneratedKid(kid, result)
320
+ };
321
+ }
322
+ function assertPBES2Algorithm(alg) {
323
+ if (typeof alg === "string" && PBES2_ALGORITHMS.includes(alg)) return;
324
+ throw new JWTError(`Password derivation is defined for ${PBES2_ALGORITHMS.join(", ")}, not ${String(alg)}.`, "ERR_JWK_UNSUPPORTED");
325
+ }
326
+ async function importPEM(pem, alg, options) {
327
+ if (typeof pem !== "string") throw new JWTError("\"pem\" must be a PEM-encoded string.", "ERR_INVALID_OPTION");
328
+ if (options !== void 0) assertPlainObject(options, "options");
329
+ const extractable = true;
330
+ const pemType = options?.pemType ?? inferPEMType(pem);
331
+ if (!pemType) throw new JWTError("Cannot infer PEM type from the input, pass \"pemType\".", "ERR_JWK_INVALID");
332
+ let cryptoKey;
333
+ switch (pemType) {
334
+ case "pkcs8":
335
+ cryptoKey = await fromPKCS8(pem, alg, { extractable });
336
+ break;
337
+ case "spki":
338
+ cryptoKey = await fromSPKI(pem, alg, { extractable });
339
+ break;
340
+ case "x509":
341
+ cryptoKey = await fromX509(pem, alg, { extractable });
342
+ break;
343
+ default: throw new JWTError(`Unsupported PEM type: ${String(pemType)}`, "ERR_JWK_UNSUPPORTED");
344
+ }
345
+ return {
346
+ ...await exportJWK(cryptoKey, options?.jwkParams),
347
+ alg
609
348
  };
610
349
  }
350
+ async function exportPEM(jwk, options) {
351
+ assertPlainObject(jwk, "jwk");
352
+ if (options !== void 0) assertPlainObject(options, "options");
353
+ if (jwk.kty === "oct") throw new JWTError("An oct JWK cannot be exported to PKCS8 or SPKI PEM.", "ERR_JWK_UNSUPPORTED");
354
+ const effectiveAlg = jwk.alg || options?.alg;
355
+ if (!effectiveAlg && (jwk.kty === "RSA" || jwk.kty === "EC" || jwk.kty === "OKP")) throw new JWTError("\"alg\" is required, set it on the JWK or pass it in options.", "ERR_JWK_INVALID");
356
+ if (effectiveAlg !== void 0 && !PEM_ALGORITHMS.has(effectiveAlg)) throw new JWTError(`Unsupported PEM algorithm: ${effectiveAlg}`, "ERR_JWK_UNSUPPORTED");
357
+ const pemFormat = options?.pemFormat ?? ("d" in jwk ? "pkcs8" : "spki");
358
+ const cryptoKeyCandidate = await importKey({
359
+ ...jwk,
360
+ ext: true
361
+ }, {
362
+ alg: effectiveAlg,
363
+ expect: pemFormat === "pkcs8" ? "private" : "public",
364
+ extractable: true
365
+ });
366
+ if (!isCryptoKey(cryptoKeyCandidate)) throw new JWTError("Failed to convert JWK to a CryptoKey instance suitable for PEM export.", "ERR_JWK_INVALID");
367
+ const cryptoKey = cryptoKeyCandidate;
368
+ switch (pemFormat) {
369
+ case "pkcs8":
370
+ if (cryptoKey.type !== "private") throw new JWTError(`Only a private CryptoKey can be exported to PKCS8 PEM (got ${cryptoKey.type}).`, "ERR_JWK_INVALID");
371
+ return toPKCS8(cryptoKey);
372
+ case "spki":
373
+ if (cryptoKey.type !== "public") throw new JWTError(`Only a public CryptoKey can be exported to SPKI PEM (got ${cryptoKey.type}).`, "ERR_JWK_INVALID");
374
+ return toSPKI(cryptoKey);
375
+ default: throw new JWTError(`Unsupported PEM format: ${String(pemFormat)}`, "ERR_JWK_UNSUPPORTED");
376
+ }
377
+ }
378
+ function inferPEMType(pem) {
379
+ if (pem.includes("-----BEGIN PRIVATE KEY-----")) return "pkcs8";
380
+ if (pem.includes("-----BEGIN PUBLIC KEY-----")) return "spki";
381
+ if (pem.includes("-----BEGIN CERTIFICATE-----")) return "x509";
382
+ }
611
383
  const PEM_ALGORITHMS = /* @__PURE__ */ new Set(Object.keys({
612
384
  "PS256": true,
613
385
  "PS384": true,
@@ -629,85 +401,28 @@ const PEM_ALGORITHMS = /* @__PURE__ */ new Set(Object.keys({
629
401
  "Ed25519": true,
630
402
  "EdDSA": true
631
403
  }));
632
- function assertWrappableCEK(cek, alg) {
633
- if (cek.length >= 16 && cek.length % 8 === 0) return;
634
- throw new JWTError(`${alg} wraps a key of 16 or more bytes in whole 8-byte blocks (got ${cek.length}).`, "ERR_INVALID_OPTION");
635
- }
636
- async function unwrapping(operation) {
637
- try {
638
- return await operation;
639
- } catch (error) {
640
- throw asDecryptionError(error);
641
- }
642
- }
643
- function assertPBES2Algorithm(alg) {
644
- if (typeof alg === "string" && PBES2_ALGORITHMS.includes(alg)) return;
645
- throw new JWTError(`Password derivation is defined for ${PBES2_ALGORITHMS.join(", ")}, not ${String(alg)}.`, "ERR_JWK_UNSUPPORTED");
646
- }
647
- function assertKeyBytesArg(value, name) {
648
- if (value instanceof Uint8Array || isCryptoKey(value)) return;
649
- throw new JWTError(`"${name}" must be a Uint8Array or a CryptoKey.`, "ERR_INVALID_OPTION");
650
- }
651
- function assertModulusLength(modulusLength) {
652
- const length = modulusLength ?? 2048;
653
- if (!Number.isInteger(length) || length < 2048) throw new JWTError("modulusLength must be at least 2048 bits", "ERR_INVALID_OPTION");
654
- return length;
655
- }
656
- function assertPartyInfoLength(name, bytes) {
657
- if (bytes.length > 1024) throw new JWTError(`Invalid JWE: "${name}" must not exceed ${MAX_PARTY_INFO_BYTES} bytes (got ${bytes.length}).`, "ERR_JWE_INVALID");
658
- }
659
- function assertKeyWrapSegmentLength(name, bytes, expected, alg) {
660
- if (bytes.length !== expected) throw new JWTError(`Invalid JWE: "${name}" must be ${expected} bytes for ${alg} (got ${bytes.length}).`, "ERR_JWE_INVALID");
404
+ async function deriveSharedSecret(publicKey, privateKey, options) {
405
+ assertPlainObject(options, "options");
406
+ const { enc: alg, keyLength: requestedKeyLength, partyUInfo, partyVInfo } = options;
407
+ if (!isECDHESAlgorithm(alg)) assertJWEEncSupported(alg);
408
+ if (requestedKeyLength !== void 0) assertDerivedKeyLength(requestedKeyLength);
409
+ const importAlg = isECDHESAlgorithm(alg) ? alg : "ECDH-ES";
410
+ const pubCryptoKey = isCryptoKey(publicKey) ? publicKey : await importKey(publicKey, {
411
+ alg: importAlg,
412
+ expect: "public"
413
+ });
414
+ const privCryptoKey = isCryptoKey(privateKey) ? privateKey : await importKey(privateKey, {
415
+ alg: importAlg,
416
+ expect: "private"
417
+ });
418
+ let keyLength;
419
+ if (requestedKeyLength !== void 0) keyLength = requestedKeyLength;
420
+ else if (alg === "ECDH-ES") throw new JWTError("deriveSharedSecret with enc \"ECDH-ES\" requires an explicit keyLength in options", "ERR_JWK_INVALID");
421
+ else if (isECDHESAlgorithm(alg)) keyLength = aesBitsOf(innerKeyWrapAlg(alg));
422
+ else keyLength = bitLengthCEK(alg);
423
+ return deriveECDHESKey(pubCryptoKey, privCryptoKey, alg, keyLength, partyUInfo ?? /* @__PURE__ */ new Uint8Array(0), partyVInfo ?? /* @__PURE__ */ new Uint8Array(0));
661
424
  }
662
425
  function assertDerivedKeyLength(keyLength) {
663
426
  if (!Number.isInteger(keyLength) || keyLength <= 0 || keyLength % 8 !== 0 || keyLength > 8192) throw new JWTError(`keyLength must be a positive multiple of 8 no greater than 8192 bits, got ${keyLength}`, "ERR_INVALID_OPTION");
664
427
  }
665
- function assertCryptoKeyFor(alg, key, role) {
666
- if (isCryptoKey(key)) return;
667
- throw new JWTError(`${alg} requires the ${role} key to be a CryptoKey`, "ERR_JWK_INVALID");
668
- }
669
- function segmentBytes(value, name) {
670
- return typeof value === "string" ? decodeSegment(value, {
671
- family: "JWE",
672
- what: `"${name}"`
673
- }) : value;
674
- }
675
- function withAlg(jwk, alg) {
676
- return {
677
- ...jwk,
678
- alg
679
- };
680
- }
681
- function generatedPairAlgorithm(alg) {
682
- return alg;
683
- }
684
- function generatedSecretAlgorithm(alg) {
685
- return alg;
686
- }
687
- const PUBLIC_KEY_OPS = /* @__PURE__ */ new Map([
688
- ["sign", "verify"],
689
- ["verify", "verify"],
690
- ["decrypt", "encrypt"],
691
- ["encrypt", "encrypt"],
692
- ["unwrapKey", "wrapKey"],
693
- ["wrapKey", "wrapKey"]
694
- ]);
695
- function publicKeyOps(keyOps) {
696
- if (!Array.isArray(keyOps)) return [];
697
- const ops = [];
698
- for (const op of keyOps) {
699
- const publicOp = typeof op === "string" ? PUBLIC_KEY_OPS.get(op) : void 0;
700
- if (publicOp !== void 0 && !ops.includes(publicOp)) ops.push(publicOp);
701
- }
702
- return ops;
703
- }
704
- function requestedKeyOps(options, key) {
705
- return options.keyUsage === void 0 ? void 0 : [...key.usages];
706
- }
707
- function withKeyOps(jwk, keyOps) {
708
- return keyOps === void 0 ? jwk : {
709
- ...jwk,
710
- key_ops: keyOps
711
- };
712
- }
713
- export { JWTError, UnsecureError, WeakMapJWKCache, clearJWKCache, configureJWKCache, deriveJWKFromPassword, deriveKeyFromPassword, deriveSharedSecret, exportJWK, exportKey, exportPEM, generateJWK, generateKey, getJWKsFromSet, importKey, importPEM, isJWTError, isUnsecureError, thumbprint, toPublicJWK, unwrapKey, wrapKey };
428
+ export { deriveJWKFromPassword, deriveKeyFromPassword, deriveSharedSecret, exportPEM, generateJWK, generateKey, importPEM };