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.
- package/README.md +3 -9
- package/dist/_chunks/{_import-key.mjs → algorithms.mjs} +194 -399
- package/dist/_chunks/{_codec-jwe.d.mts → codec-jwe.d.mts} +40 -9
- package/dist/_chunks/{_codec-jws.mjs → codec-jws.mjs} +133 -80
- package/dist/_chunks/cookie.mjs +50 -0
- package/dist/_chunks/digest.mjs +10 -3
- package/dist/_chunks/error.d.mts +103 -0
- package/dist/_chunks/export.mjs +97 -0
- package/dist/_chunks/index.d.mts +5 -103
- package/dist/_chunks/index2.d.mts +206 -12
- package/dist/_chunks/index3.d.mts +12 -109
- package/dist/_chunks/{jwe.d.mts → index4.d.mts} +2 -1
- package/dist/_chunks/{jws.d.mts → index5.d.mts} +2 -1
- package/dist/_chunks/index6.d.mts +113 -0
- package/dist/_chunks/jwe.mjs +205 -72
- package/dist/{core → _chunks}/jwk.mjs +210 -495
- package/dist/_chunks/jws.mjs +280 -193
- package/dist/_chunks/jwt.d.mts +2 -1
- package/dist/_chunks/{_jwt-internal.mjs → jwt.mjs} +110 -54
- package/dist/_chunks/libs/unsecure.d.mts +35 -25
- package/dist/_chunks/libs/unsecure.mjs +101 -268
- package/dist/_chunks/{_internal.d.mts → limits.d.mts} +1 -1
- package/dist/_chunks/payload.mjs +23 -0
- package/dist/_chunks/wrap.mjs +209 -0
- package/dist/adapters/elysia/index.d.mts +4 -4
- package/dist/adapters/elysia/index.mjs +4 -3
- package/dist/adapters/h3/index.d.mts +13 -13
- package/dist/adapters/h3/index.mjs +8 -11
- package/dist/adapters/h3v1/index.d.mts +5 -5
- package/dist/adapters/h3v1/index.mjs +6 -9
- package/dist/core/internal.mjs +1 -1
- package/dist/core/{jwe.d.mts → jwe/index.d.mts} +5 -4
- package/dist/core/jwe/index.mjs +5 -0
- package/dist/core/{jwk.d.mts → jwk/index.d.mts} +5 -4
- package/dist/core/jwk/index.mjs +7 -0
- package/dist/core/{jws.d.mts → jws/index.d.mts} +5 -4
- package/dist/core/jws/index.mjs +5 -0
- package/dist/dpop/index.d.mts +73 -0
- package/dist/dpop/index.mjs +296 -0
- package/dist/error.d.mts +3 -0
- package/dist/error.mjs +20 -0
- package/dist/index.d.mts +10 -9
- package/dist/index.mjs +7 -4
- package/dist/session/index.d.mts +4 -4
- package/dist/session/index.mjs +6 -47
- package/dist/utils/index.d.mts +7 -0
- package/dist/{core/utils → utils}/index.mjs +4 -3
- package/package.json +21 -10
- package/dist/_chunks/jwk.d.mts +0 -205
- package/dist/core/jwe.mjs +0 -5
- package/dist/core/jws.mjs +0 -5
- package/dist/core/utils/index.d.mts +0 -6
- /package/dist/_chunks/{_cookie-attributes.d.mts → cookie-attributes.d.mts} +0 -0
|
@@ -1,377 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { JWTError
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
601
|
-
|
|
602
|
-
throw new JWTError("
|
|
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
|
-
|
|
605
|
-
if (kid === false) return jwk;
|
|
201
|
+
function withAlg(jwk, alg) {
|
|
606
202
|
return {
|
|
607
203
|
...jwk,
|
|
608
|
-
|
|
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
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
if (
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
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
|
-
|
|
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 };
|