unjwt 0.8.0 → 0.8.2
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 +4 -4
- package/dist/THIRD-PARTY-LICENSES.md.gz +0 -0
- package/dist/_chunks/_codec-jwe.d.mts +105 -22
- package/dist/_chunks/_codec-jws.mjs +308 -234
- package/dist/_chunks/_cookie-attributes.d.mts +12 -0
- package/dist/_chunks/_jwt-internal.mjs +1 -1
- package/dist/_chunks/digest.d.mts +8 -0
- package/dist/_chunks/{algorithms.mjs → digest.mjs} +78 -8
- package/dist/_chunks/index.d.mts +311 -268
- package/dist/_chunks/index2.d.mts +2 -1
- package/dist/_chunks/index3.d.mts +5 -4
- package/dist/_chunks/jwe.d.mts +5 -5
- package/dist/_chunks/jwe.mjs +21 -3
- package/dist/_chunks/jwk.mjs +220 -177
- package/dist/_chunks/jws.d.mts +10 -10
- package/dist/_chunks/jws.mjs +18 -6
- package/dist/_chunks/jwt.d.mts +2 -2
- package/dist/_chunks/libs/cookie-es.mjs +255 -0
- package/dist/adapters/elysia/index.d.mts +32 -15
- package/dist/adapters/elysia/index.mjs +46 -67
- package/dist/adapters/h3/index.d.mts +135 -0
- package/dist/adapters/h3/index.mjs +119 -0
- package/dist/adapters/h3v1/index.d.mts +100 -27
- package/dist/adapters/h3v1/index.mjs +59 -76
- package/dist/core/jwe.d.mts +2 -2
- package/dist/core/jwk.d.mts +22 -14
- package/dist/core/jwk.mjs +2 -2
- package/dist/core/jws.d.mts +2 -2
- package/dist/core/utils/index.d.mts +2 -1
- package/dist/core/utils/index.mjs +2 -2
- package/dist/index.d.mts +4 -3
- package/dist/index.mjs +3 -3
- package/dist/session/index.d.mts +44 -0
- package/dist/session/index.mjs +111 -0
- package/package.json +38 -18
- package/dist/adapters/h3v2/index.d.mts +0 -65
- package/dist/adapters/h3v2/index.mjs +0 -139
package/dist/_chunks/jwk.mjs
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import { safeJsonParse, sanitizeObjectCopy, secureRandomBytes, textEncoder } from "./libs/unsecure.mjs";
|
|
2
|
-
import { JWTError, assertClaimValidationOptions, assertCritShape, assertCryptoKey, assertPlainObject, assertStringList, base64Parse, base64Stringify, base64UrlParse, base64UrlStringify, concatUint8Arrays, isCryptoKey, isCryptoKeyPair, isJWK, isJWKSet, isPlainObject, parsePayloadBytes, parsePayloadSegment, uint32be, validateJwtClaims } from "./_jwt-internal.mjs";
|
|
3
|
-
import { ECDSA_CURVES, PBES2_ALGORITHMS, aesBitsOf, checkKeyUsage, cryptoKeyCurve, hashBitsOf, inferJWEAllowedAlgorithms, inferJWSAllowedAlgorithms, innerKeyWrapAlg, isAlgorithm, isContentEncryptionAlgorithm, isEd25519Signature, jwsHashOf, lengthOf, modulusLengthOf, namedCurveOf, rsaOaepHashOf, unusableKey } from "./
|
|
2
|
+
import { JWTError, assertClaimValidationOptions, assertCritShape, assertCryptoKey, assertPlainObject, assertStringList, base64Parse, base64Stringify, base64UrlParse, base64UrlStringify, concatUint8Arrays, isAsymmetricJWK, isCryptoKey, isCryptoKeyPair, isJWK, isJWKSet, isPlainObject, isPublicJWK, parsePayloadBytes, parsePayloadSegment, uint32be, validateJwtClaims } from "./_jwt-internal.mjs";
|
|
3
|
+
import { ECDSA_CURVES, PBES2_ALGORITHMS, aesBitsOf, checkKeyUsage, cryptoKeyCurve, digestUrl, hashBitsOf, inferJWEAllowedAlgorithms, inferJWSAllowedAlgorithms, innerKeyWrapAlg, isAlgorithm, isContentEncryptionAlgorithm, isECDHESAlgorithm, isEd25519Signature, isJWSAlgorithm, isKeyManagementAlgorithm, isPBES2Algorithm, jwsHashOf, lengthOf, modulusLengthOf, namedCurveOf, pbes2HashOf, rsaOaepHashOf, shaBitsOf, unusableKey } from "./digest.mjs";
|
|
4
4
|
const MAX_QUOTED_VALUE = 64;
|
|
5
5
|
function quoteTokenValue(value) {
|
|
6
6
|
const text = String(value);
|
|
7
7
|
return JSON.stringify(text.length > MAX_QUOTED_VALUE ? `${text.slice(0, MAX_QUOTED_VALUE)}…` : text);
|
|
8
8
|
}
|
|
9
|
-
const HASHED_SIGNATURE_ALGORITHMS = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
const HASHED_SIGNATURE_ALGORITHMS = /* @__PURE__ */ Object.freeze({
|
|
10
|
+
HS256: "HMAC",
|
|
11
|
+
HS384: "HMAC",
|
|
12
|
+
HS512: "HMAC",
|
|
13
|
+
RS256: "RSASSA-PKCS1-v1_5",
|
|
14
|
+
RS384: "RSASSA-PKCS1-v1_5",
|
|
15
|
+
RS512: "RSASSA-PKCS1-v1_5",
|
|
16
|
+
PS256: "RSA-PSS",
|
|
17
|
+
PS384: "RSA-PSS",
|
|
18
|
+
PS512: "RSA-PSS"
|
|
19
|
+
});
|
|
14
20
|
async function sign(alg, key, data) {
|
|
15
21
|
checkSigCryptoKey(key, alg, "sign");
|
|
16
22
|
const signature = await crypto.subtle.sign(subtleAlgorithm(alg, key.algorithm), key, data);
|
|
@@ -35,9 +41,9 @@ function checkSigCryptoKey(key, alg, usage) {
|
|
|
35
41
|
case "PS256":
|
|
36
42
|
case "PS384":
|
|
37
43
|
case "PS512": {
|
|
38
|
-
const name = HASHED_SIGNATURE_ALGORITHMS[alg
|
|
44
|
+
const name = HASHED_SIGNATURE_ALGORITHMS[alg];
|
|
39
45
|
if (!isAlgorithm(key.algorithm, name)) throw unusableKey(name);
|
|
40
|
-
const expected =
|
|
46
|
+
const expected = shaBitsOf(jwsHashOf(alg));
|
|
41
47
|
if (hashBitsOf(key.algorithm.hash) !== expected) throw unusableKey(`SHA-${expected}`, "algorithm.hash");
|
|
42
48
|
break;
|
|
43
49
|
}
|
|
@@ -53,45 +59,49 @@ function checkSigCryptoKey(key, alg, usage) {
|
|
|
53
59
|
if (key.algorithm.namedCurve !== expected) throw unusableKey(expected, "algorithm.namedCurve");
|
|
54
60
|
break;
|
|
55
61
|
}
|
|
56
|
-
default: throw new JWTError("CryptoKey does not support this operation", "ERR_JWK_INVALID");
|
|
57
62
|
}
|
|
58
63
|
checkKeyUsage(key, usage);
|
|
59
64
|
}
|
|
60
65
|
function subtleAlgorithm(alg, algorithm) {
|
|
61
|
-
const hash = jwsHashOf(alg);
|
|
62
66
|
switch (alg) {
|
|
63
67
|
case "HS256":
|
|
64
68
|
case "HS384":
|
|
65
69
|
case "HS512": return {
|
|
66
|
-
hash,
|
|
70
|
+
hash: jwsHashOf(alg),
|
|
67
71
|
name: "HMAC"
|
|
68
72
|
};
|
|
69
73
|
case "PS256":
|
|
70
74
|
case "PS384":
|
|
71
|
-
case "PS512":
|
|
72
|
-
hash
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
case "PS512": {
|
|
76
|
+
const hash = jwsHashOf(alg);
|
|
77
|
+
return {
|
|
78
|
+
hash,
|
|
79
|
+
name: "RSA-PSS",
|
|
80
|
+
saltLength: shaBitsOf(hash) >> 3
|
|
81
|
+
};
|
|
82
|
+
}
|
|
76
83
|
case "RS256":
|
|
77
84
|
case "RS384":
|
|
78
85
|
case "RS512": return {
|
|
79
|
-
hash,
|
|
86
|
+
hash: jwsHashOf(alg),
|
|
80
87
|
name: "RSASSA-PKCS1-v1_5"
|
|
81
88
|
};
|
|
82
89
|
case "ES256":
|
|
83
90
|
case "ES384":
|
|
84
91
|
case "ES512": return {
|
|
85
|
-
hash,
|
|
92
|
+
hash: jwsHashOf(alg),
|
|
86
93
|
name: "ECDSA",
|
|
87
94
|
namedCurve: namedCurveOf(algorithm)
|
|
88
95
|
};
|
|
89
96
|
case "Ed25519":
|
|
90
97
|
case "EdDSA": return { name: "Ed25519" };
|
|
91
|
-
default:
|
|
98
|
+
default: return unsupportedSignatureAlgorithm(alg);
|
|
92
99
|
}
|
|
93
100
|
}
|
|
94
|
-
|
|
101
|
+
function unsupportedSignatureAlgorithm(alg) {
|
|
102
|
+
throw new JWTError(`alg ${String(alg)} is not supported either by JOSE or your javascript runtime`, "ERR_JWS_ALG_NOT_ALLOWED");
|
|
103
|
+
}
|
|
104
|
+
const IV_BIT_LENGTHS = /* @__PURE__ */ Object.freeze({
|
|
95
105
|
"A128GCM": 96,
|
|
96
106
|
"A128GCMKW": 96,
|
|
97
107
|
"A192GCM": 96,
|
|
@@ -101,19 +111,17 @@ const IV_BIT_LENGTHS = {
|
|
|
101
111
|
"A128CBC-HS256": 128,
|
|
102
112
|
"A192CBC-HS384": 128,
|
|
103
113
|
"A256CBC-HS512": 128
|
|
104
|
-
};
|
|
105
|
-
const CEK_BIT_LENGTHS = {
|
|
114
|
+
});
|
|
115
|
+
const CEK_BIT_LENGTHS = /* @__PURE__ */ Object.freeze({
|
|
106
116
|
"A128GCM": 128,
|
|
107
117
|
"A192GCM": 192,
|
|
108
118
|
"A256GCM": 256,
|
|
109
119
|
"A128CBC-HS256": 256,
|
|
110
120
|
"A192CBC-HS384": 384,
|
|
111
121
|
"A256CBC-HS512": 512
|
|
112
|
-
};
|
|
122
|
+
});
|
|
113
123
|
function bitLengthIV(alg) {
|
|
114
|
-
|
|
115
|
-
if (length === void 0) throw new JWTError(`Unsupported JWE Algorithm: ${quoteTokenValue(alg)}`, "ERR_JWE_INVALID");
|
|
116
|
-
return length;
|
|
124
|
+
return IV_BIT_LENGTHS[alg];
|
|
117
125
|
}
|
|
118
126
|
function generateIV(alg) {
|
|
119
127
|
return crypto.getRandomValues(new Uint8Array(bitLengthIV(alg) >> 3));
|
|
@@ -121,7 +129,7 @@ function generateIV(alg) {
|
|
|
121
129
|
function checkIvLength(enc, iv) {
|
|
122
130
|
if (iv.length << 3 !== bitLengthIV(enc)) throw new JWTError("Invalid Initialization Vector length", "ERR_JWE_INVALID");
|
|
123
131
|
}
|
|
124
|
-
const TAG_BIT_LENGTHS = {
|
|
132
|
+
const TAG_BIT_LENGTHS = /* @__PURE__ */ Object.freeze({
|
|
125
133
|
"A128GCM": 128,
|
|
126
134
|
"A128GCMKW": 128,
|
|
127
135
|
"A192GCM": 128,
|
|
@@ -131,19 +139,15 @@ const TAG_BIT_LENGTHS = {
|
|
|
131
139
|
"A128CBC-HS256": 128,
|
|
132
140
|
"A192CBC-HS384": 192,
|
|
133
141
|
"A256CBC-HS512": 256
|
|
134
|
-
};
|
|
142
|
+
});
|
|
135
143
|
function bitLengthTag(alg) {
|
|
136
|
-
|
|
137
|
-
if (length === void 0) throw new JWTError(`Unsupported JWE Algorithm: ${quoteTokenValue(alg)}`, "ERR_JWE_INVALID");
|
|
138
|
-
return length;
|
|
144
|
+
return TAG_BIT_LENGTHS[alg];
|
|
139
145
|
}
|
|
140
146
|
function checkTagLength(enc, tag) {
|
|
141
147
|
if (tag.length << 3 !== bitLengthTag(enc)) throw new JWTError("Invalid Authentication Tag length", "ERR_JWE_INVALID");
|
|
142
148
|
}
|
|
143
|
-
function bitLengthCEK(
|
|
144
|
-
|
|
145
|
-
if (length === void 0) throw new JWTError(`Unsupported JWE Algorithm: ${quoteTokenValue(alg)}`, "ERR_JWE_INVALID");
|
|
146
|
-
return length;
|
|
149
|
+
function bitLengthCEK(enc) {
|
|
150
|
+
return CEK_BIT_LENGTHS[enc];
|
|
147
151
|
}
|
|
148
152
|
function generateCEK(alg) {
|
|
149
153
|
return crypto.getRandomValues(new Uint8Array(bitLengthCEK(alg) >> 3));
|
|
@@ -155,29 +159,18 @@ function checkCEKLength(cek, expected) {
|
|
|
155
159
|
function assertOctKeyLength(bytes, alg) {
|
|
156
160
|
if (!alg) return;
|
|
157
161
|
if (alg === "HS256" || alg === "HS384" || alg === "HS512") {
|
|
158
|
-
const minBytes =
|
|
162
|
+
const minBytes = shaBitsOf(jwsHashOf(alg)) >> 3;
|
|
159
163
|
if (bytes.length < minBytes) throw new JWTError(`${alg} requires a key of at least ${minBytes} bytes (got ${bytes.length})`, "ERR_JWK_INVALID");
|
|
160
164
|
return;
|
|
161
165
|
}
|
|
162
|
-
const exactBits =
|
|
166
|
+
const exactBits = isKeyWrapAlgorithm(alg) ? aesBitsOf(alg) : isContentEncryption(alg) ? CEK_BIT_LENGTHS[alg] : void 0;
|
|
163
167
|
if (exactBits !== void 0 && bytes.length << 3 !== exactBits) throw new JWTError(`${alg} requires a key of exactly ${exactBits >> 3} bytes (got ${bytes.length})`, "ERR_JWK_INVALID");
|
|
164
168
|
}
|
|
165
169
|
function checkAESCryptoKey(key, alg, usage) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
case "A128KW":
|
|
171
|
-
case "A192KW":
|
|
172
|
-
case "A256KW": {
|
|
173
|
-
const name = alg.endsWith("KW") ? "AES-KW" : "AES-GCM";
|
|
174
|
-
if (!isAlgorithm(key.algorithm, name)) throw unusableKey(name);
|
|
175
|
-
const expected = aesBitsOf(alg);
|
|
176
|
-
if (key.algorithm.length !== expected) throw unusableKey(expected, "algorithm.length");
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
default: throw new JWTError("CryptoKey does not support this operation", "ERR_JWK_INVALID");
|
|
180
|
-
}
|
|
170
|
+
const name = alg.endsWith("KW") ? "AES-KW" : "AES-GCM";
|
|
171
|
+
if (!isAlgorithm(key.algorithm, name)) throw unusableKey(name);
|
|
172
|
+
const expected = aesBitsOf(alg);
|
|
173
|
+
if (key.algorithm.length !== expected) throw unusableKey(expected, "algorithm.length");
|
|
181
174
|
checkKeyUsage(key, usage);
|
|
182
175
|
}
|
|
183
176
|
async function aesKwWrap(alg, key, cek) {
|
|
@@ -199,7 +192,7 @@ async function aesKwUnwrap(alg, key, encryptedKey) {
|
|
|
199
192
|
return new Uint8Array(await crypto.subtle.exportKey("raw", cryptoKeyCek));
|
|
200
193
|
}
|
|
201
194
|
async function gcmkwEncrypt(alg, key, plaintext, iv) {
|
|
202
|
-
const wrapped = await encrypt(alg
|
|
195
|
+
const wrapped = await encrypt(gcmOf(alg), plaintext, key, iv, /* @__PURE__ */ new Uint8Array(0));
|
|
203
196
|
return {
|
|
204
197
|
encryptedKey: wrapped.ciphertext,
|
|
205
198
|
iv: base64UrlStringify(wrapped.iv),
|
|
@@ -207,7 +200,7 @@ async function gcmkwEncrypt(alg, key, plaintext, iv) {
|
|
|
207
200
|
};
|
|
208
201
|
}
|
|
209
202
|
async function gcmkwDecrypt(alg, key, ciphertext, iv, tag) {
|
|
210
|
-
return decrypt(alg
|
|
203
|
+
return decrypt(gcmOf(alg), key, ciphertext, iv, tag, /* @__PURE__ */ new Uint8Array(0));
|
|
211
204
|
}
|
|
212
205
|
async function encrypt(enc, plaintext, cek, iv, aad) {
|
|
213
206
|
if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) throw new JWTError("Key must be one of type: CryptoKey or Uint8Array", "ERR_JWK_INVALID");
|
|
@@ -217,14 +210,14 @@ async function encrypt(enc, plaintext, cek, iv, aad) {
|
|
|
217
210
|
case "A128CBC-HS256":
|
|
218
211
|
case "A192CBC-HS384":
|
|
219
212
|
case "A256CBC-HS512":
|
|
220
|
-
if (cek instanceof Uint8Array) checkCEKLength(cek,
|
|
213
|
+
if (cek instanceof Uint8Array) checkCEKLength(cek, bitLengthCEK(enc));
|
|
221
214
|
return cbcEncrypt(enc, plaintext, cek, iv, aad);
|
|
222
215
|
case "A128GCM":
|
|
223
216
|
case "A192GCM":
|
|
224
217
|
case "A256GCM":
|
|
225
|
-
if (cek instanceof Uint8Array) checkCEKLength(cek,
|
|
218
|
+
if (cek instanceof Uint8Array) checkCEKLength(cek, bitLengthCEK(enc));
|
|
226
219
|
return gcmEncrypt(enc, plaintext, cek, iv, aad);
|
|
227
|
-
default:
|
|
220
|
+
default: return unsupportedContentEncryption(enc);
|
|
228
221
|
}
|
|
229
222
|
}
|
|
230
223
|
async function decrypt(enc, cek, ciphertext, iv, tag, aad) {
|
|
@@ -237,14 +230,14 @@ async function decrypt(enc, cek, ciphertext, iv, tag, aad) {
|
|
|
237
230
|
case "A128CBC-HS256":
|
|
238
231
|
case "A192CBC-HS384":
|
|
239
232
|
case "A256CBC-HS512":
|
|
240
|
-
if (cek instanceof Uint8Array) checkCEKLength(cek,
|
|
233
|
+
if (cek instanceof Uint8Array) checkCEKLength(cek, bitLengthCEK(enc));
|
|
241
234
|
return cbcDecrypt(enc, cek, ciphertext, iv, tag, aad);
|
|
242
235
|
case "A128GCM":
|
|
243
236
|
case "A192GCM":
|
|
244
237
|
case "A256GCM":
|
|
245
|
-
if (cek instanceof Uint8Array) checkCEKLength(cek,
|
|
238
|
+
if (cek instanceof Uint8Array) checkCEKLength(cek, bitLengthCEK(enc));
|
|
246
239
|
return gcmDecrypt(enc, cek, ciphertext, iv, tag, aad);
|
|
247
|
-
default:
|
|
240
|
+
default: return unsupportedContentEncryption(enc);
|
|
248
241
|
}
|
|
249
242
|
}
|
|
250
243
|
async function getAESKWCryptoKey(key, alg, options = {}) {
|
|
@@ -369,16 +362,33 @@ async function timingSafeEqual(a, b) {
|
|
|
369
362
|
function uint64be(value) {
|
|
370
363
|
return concatUint8Arrays(uint32be(Math.floor(value / 2 ** 32)), uint32be(value % 2 ** 32));
|
|
371
364
|
}
|
|
365
|
+
function gcmOf(alg) {
|
|
366
|
+
return alg.slice(0, 7);
|
|
367
|
+
}
|
|
368
|
+
function isKeyWrapAlgorithm(alg) {
|
|
369
|
+
return alg.startsWith("A") && alg.endsWith("KW");
|
|
370
|
+
}
|
|
371
|
+
function isContentEncryption(alg) {
|
|
372
|
+
return Object.hasOwn(CEK_BIT_LENGTHS, alg);
|
|
373
|
+
}
|
|
374
|
+
function unsupportedContentEncryption(enc) {
|
|
375
|
+
throw new JWTError(`Unsupported JWE Content Encryption Algorithm: ${quoteTokenValue(String(enc))}`, "ERR_JWE_INVALID");
|
|
376
|
+
}
|
|
372
377
|
async function jwkTokey(jwk, options = {}) {
|
|
373
|
-
|
|
374
|
-
|
|
378
|
+
const alg = jwk.alg ?? options.alg;
|
|
379
|
+
if (!alg) throw new JWTError("\"alg\" argument is required when \"jwk.alg\" is not present", "ERR_JWK_INVALID");
|
|
380
|
+
const { algorithm, keyUsages } = subtleMapping(jwk, alg);
|
|
381
|
+
const material = {
|
|
382
|
+
...jwk,
|
|
383
|
+
alg
|
|
384
|
+
};
|
|
375
385
|
const isPrivate = "d" in jwk && jwk.d !== void 0;
|
|
376
386
|
const extractable = jwk.ext === false ? false : options.extractable ?? !isPrivate;
|
|
377
387
|
try {
|
|
378
|
-
return await crypto.subtle.importKey("jwk",
|
|
388
|
+
return await crypto.subtle.importKey("jwk", material, algorithm, extractable, permittedUsages(jwk, keyUsages));
|
|
379
389
|
} catch (error) {
|
|
380
390
|
if (error instanceof JWTError) throw error;
|
|
381
|
-
throw new JWTError(`Key is not usable for ${quoteTokenValue(
|
|
391
|
+
throw new JWTError(`Key is not usable for ${quoteTokenValue(alg)}.`, "ERR_JWK_INVALID", error);
|
|
382
392
|
}
|
|
383
393
|
}
|
|
384
394
|
async function keyToJWK(key) {
|
|
@@ -390,7 +400,8 @@ async function keyToJWK(key) {
|
|
|
390
400
|
else {
|
|
391
401
|
if (!isCryptoKey(key)) throw new JWTError("Key must be one of type: CryptoKey or Uint8Array", "ERR_JWK_INVALID");
|
|
392
402
|
if (!key.extractable) throw new JWTError("non-extractable CryptoKey cannot be exported as a JWK", "ERR_JWK_INVALID");
|
|
393
|
-
|
|
403
|
+
const { ext: _ext, key_ops: _keyOps, ...exported } = await crypto.subtle.exportKey("jwk", key);
|
|
404
|
+
jwk = exported;
|
|
394
405
|
}
|
|
395
406
|
return jwk;
|
|
396
407
|
}
|
|
@@ -401,18 +412,18 @@ function permittedUsages(jwk, keyUsages) {
|
|
|
401
412
|
if (permitted.length === 0) throw new JWTError(`JWK "key_ops" does not permit ${keyUsages.join(", ")}.`, "ERR_JWK_INVALID");
|
|
402
413
|
return permitted;
|
|
403
414
|
}
|
|
404
|
-
function subtleMapping(jwk) {
|
|
415
|
+
function subtleMapping(jwk, alg) {
|
|
405
416
|
let algorithm;
|
|
406
417
|
let keyUsages;
|
|
407
418
|
switch (jwk.kty) {
|
|
408
419
|
case "RSA":
|
|
409
|
-
switch (
|
|
420
|
+
switch (alg) {
|
|
410
421
|
case "PS256":
|
|
411
422
|
case "PS384":
|
|
412
423
|
case "PS512":
|
|
413
424
|
algorithm = {
|
|
414
425
|
name: "RSA-PSS",
|
|
415
|
-
hash: jwsHashOf(
|
|
426
|
+
hash: jwsHashOf(alg)
|
|
416
427
|
};
|
|
417
428
|
keyUsages = hasPrivateMember(jwk) ? ["sign"] : ["verify"];
|
|
418
429
|
break;
|
|
@@ -421,7 +432,7 @@ function subtleMapping(jwk) {
|
|
|
421
432
|
case "RS512":
|
|
422
433
|
algorithm = {
|
|
423
434
|
name: "RSASSA-PKCS1-v1_5",
|
|
424
|
-
hash: jwsHashOf(
|
|
435
|
+
hash: jwsHashOf(alg)
|
|
425
436
|
};
|
|
426
437
|
keyUsages = hasPrivateMember(jwk) ? ["sign"] : ["verify"];
|
|
427
438
|
break;
|
|
@@ -431,21 +442,21 @@ function subtleMapping(jwk) {
|
|
|
431
442
|
case "RSA-OAEP-512":
|
|
432
443
|
algorithm = {
|
|
433
444
|
name: "RSA-OAEP",
|
|
434
|
-
hash: rsaOaepHashOf(
|
|
445
|
+
hash: rsaOaepHashOf(alg)
|
|
435
446
|
};
|
|
436
447
|
keyUsages = hasPrivateMember(jwk) ? ["decrypt", "unwrapKey"] : ["encrypt", "wrapKey"];
|
|
437
448
|
break;
|
|
438
|
-
default: throw new JWTError(`Invalid or unsupported JWK "alg" (Algorithm) Parameter value: ${quoteTokenValue(
|
|
449
|
+
default: throw new JWTError(`Invalid or unsupported JWK "alg" (Algorithm) Parameter value: ${quoteTokenValue(alg)}.`, "ERR_JWK_INVALID");
|
|
439
450
|
}
|
|
440
451
|
break;
|
|
441
452
|
case "EC":
|
|
442
|
-
switch (
|
|
453
|
+
switch (alg) {
|
|
443
454
|
case "ES256":
|
|
444
455
|
case "ES384":
|
|
445
456
|
case "ES512":
|
|
446
457
|
algorithm = {
|
|
447
458
|
name: "ECDSA",
|
|
448
|
-
namedCurve: ECDSA_CURVES[
|
|
459
|
+
namedCurve: ECDSA_CURVES[alg]
|
|
449
460
|
};
|
|
450
461
|
keyUsages = hasPrivateMember(jwk) ? ["sign"] : ["verify"];
|
|
451
462
|
break;
|
|
@@ -463,11 +474,11 @@ function subtleMapping(jwk) {
|
|
|
463
474
|
};
|
|
464
475
|
keyUsages = hasPrivateMember(jwk) ? ["deriveBits"] : [];
|
|
465
476
|
break;
|
|
466
|
-
default: throw new JWTError(`Invalid or unsupported JWK "alg" (Algorithm) Parameter value: ${quoteTokenValue(
|
|
477
|
+
default: throw new JWTError(`Invalid or unsupported JWK "alg" (Algorithm) Parameter value: ${quoteTokenValue(alg)}.`, "ERR_JWK_INVALID");
|
|
467
478
|
}
|
|
468
479
|
break;
|
|
469
480
|
case "OKP":
|
|
470
|
-
switch (
|
|
481
|
+
switch (alg) {
|
|
471
482
|
case "Ed25519":
|
|
472
483
|
case "EdDSA":
|
|
473
484
|
algorithm = { name: requireCurve(jwk, ["Ed25519"]) };
|
|
@@ -480,7 +491,7 @@ function subtleMapping(jwk) {
|
|
|
480
491
|
algorithm = { name: requireCurve(jwk, ["X25519"]) };
|
|
481
492
|
keyUsages = hasPrivateMember(jwk) ? ["deriveBits"] : [];
|
|
482
493
|
break;
|
|
483
|
-
default: throw new JWTError(`Invalid or unsupported JWK "alg" (Algorithm) Parameter value: ${quoteTokenValue(
|
|
494
|
+
default: throw new JWTError(`Invalid or unsupported JWK "alg" (Algorithm) Parameter value: ${quoteTokenValue(alg)}.`, "ERR_JWK_INVALID");
|
|
484
495
|
}
|
|
485
496
|
break;
|
|
486
497
|
default: throw new JWTError(`Invalid or unsupported JWK "kty" (Key Type) Parameter value: ${quoteTokenValue(jwk.kty)}.`, "ERR_JWK_INVALID");
|
|
@@ -510,19 +521,17 @@ async function decryptRSAES(alg, key, encryptedKey) {
|
|
|
510
521
|
}
|
|
511
522
|
function checkRSACryptoKey(key, alg, usage) {
|
|
512
523
|
if (!isAlgorithm(key.algorithm, "RSA-OAEP")) throw unusableKey("RSA-OAEP");
|
|
513
|
-
const expected =
|
|
524
|
+
const expected = shaBitsOf(rsaOaepHashOf(alg));
|
|
514
525
|
if (hashBitsOf(key.algorithm.hash) !== expected) throw unusableKey(`SHA-${expected}`, "algorithm.hash");
|
|
515
526
|
checkKeyUsage(key, usage);
|
|
516
527
|
}
|
|
517
528
|
function checkRSAModulusLength(alg, key) {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
if (typeof modulusLength !== "number" || modulusLength < 2048) throw new JWTError(`${alg} requires key modulusLength to be 2048 bits or larger`, "ERR_JWK_INVALID");
|
|
521
|
-
}
|
|
529
|
+
const modulusLength = modulusLengthOf(key.algorithm);
|
|
530
|
+
if (typeof modulusLength !== "number" || modulusLength < 2048) throw new JWTError(`${alg} requires key modulusLength to be 2048 bits or larger`, "ERR_JWK_INVALID");
|
|
522
531
|
}
|
|
523
532
|
async function deriveECDHESKey(publicKey, privateKey, algorithm, keyLength, apu = /* @__PURE__ */ new Uint8Array(0), apv = /* @__PURE__ */ new Uint8Array(0)) {
|
|
524
|
-
checkECDHCryptoKey(publicKey
|
|
525
|
-
checkECDHCryptoKey(privateKey, "
|
|
533
|
+
checkECDHCryptoKey(publicKey);
|
|
534
|
+
checkECDHCryptoKey(privateKey, "deriveBits");
|
|
526
535
|
const value = concatUint8Arrays(lengthAndInput(textEncoder.encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength));
|
|
527
536
|
const length = publicKey.algorithm.name === "X25519" ? 256 : Math.ceil(Number.parseInt((namedCurveOf(publicKey.algorithm) ?? "").slice(-3), 10) / 8) << 3;
|
|
528
537
|
return concatKdf(new Uint8Array(await crypto.subtle.deriveBits({
|
|
@@ -531,7 +540,9 @@ async function deriveECDHESKey(publicKey, privateKey, algorithm, keyLength, apu
|
|
|
531
540
|
}, privateKey, length)), keyLength, value);
|
|
532
541
|
}
|
|
533
542
|
function ecdhDerivedBits(alg, enc) {
|
|
534
|
-
|
|
543
|
+
if (alg !== "ECDH-ES") return aesBitsOf(innerKeyWrapAlg(alg));
|
|
544
|
+
if (enc) return bitLengthCEK(enc);
|
|
545
|
+
throw new JWTError("ECDH-ES requires \"enc\", the content encryption algorithm the key is agreed for.", "ERR_JWE_INVALID");
|
|
535
546
|
}
|
|
536
547
|
function generateEphemeralKeyPair(recipientKey) {
|
|
537
548
|
return crypto.subtle.generateKey(recipientKey.algorithm, true, ["deriveBits"]);
|
|
@@ -545,7 +556,7 @@ function assertECDHKeyAllowed(key) {
|
|
|
545
556
|
default: throw new JWTError("ECDH with the provided key is not allowed or not supported", "ERR_JWK_INVALID");
|
|
546
557
|
}
|
|
547
558
|
}
|
|
548
|
-
function checkECDHCryptoKey(key,
|
|
559
|
+
function checkECDHCryptoKey(key, usage) {
|
|
549
560
|
switch (key.algorithm.name) {
|
|
550
561
|
case "ECDH":
|
|
551
562
|
case "X25519": break;
|
|
@@ -614,9 +625,9 @@ async function deriveKey(p2s, alg, p2c, key) {
|
|
|
614
625
|
assertPBES2Salt(p2s, "\"p2s\"");
|
|
615
626
|
if (p2c === void 0 || p2c < 1) throw new JWTError("PBES2 Iteration Count Parameter must be a positive integer", "ERR_INVALID_OPTION");
|
|
616
627
|
const salt = concatUint8Arrays(textEncoder.encode(alg), new Uint8Array([0]), p2s);
|
|
617
|
-
const keylen =
|
|
628
|
+
const keylen = aesBitsOf(innerKeyWrapAlg(alg));
|
|
618
629
|
const subtleAlg = {
|
|
619
|
-
hash:
|
|
630
|
+
hash: pbes2HashOf(alg),
|
|
620
631
|
iterations: p2c,
|
|
621
632
|
name: "PBKDF2",
|
|
622
633
|
salt
|
|
@@ -710,7 +721,7 @@ async function ecdhesWrap(alg, options) {
|
|
|
710
721
|
epk = await exportPublicJwkFrom(generated.publicKey);
|
|
711
722
|
}
|
|
712
723
|
const kdfAlg = alg === "ECDH-ES" ? contentAlgorithm(enc) : alg;
|
|
713
|
-
const sharedSecret = await deriveECDHESKey(recipientKey, ephemeralPrivateKey, kdfAlg, ecdhDerivedBits(alg,
|
|
724
|
+
const sharedSecret = await deriveECDHESKey(recipientKey, ephemeralPrivateKey, kdfAlg, ecdhDerivedBits(alg, enc), apu, apv);
|
|
714
725
|
const parameters = { epk };
|
|
715
726
|
if (apu) parameters.apu = base64UrlStringify(apu);
|
|
716
727
|
if (apv) parameters.apv = base64UrlStringify(apv);
|
|
@@ -726,6 +737,7 @@ async function ecdhesWrap(alg, options) {
|
|
|
726
737
|
};
|
|
727
738
|
}
|
|
728
739
|
async function normalizeEpk(epk, options) {
|
|
740
|
+
const { alg } = options;
|
|
729
741
|
const recipientCurve = cryptoKeyCurve(options.recipientKey);
|
|
730
742
|
if (isCryptoKey(epk)) {
|
|
731
743
|
if (epk.type !== "public") throw invalidEpk("must not contain private key material.");
|
|
@@ -746,12 +758,12 @@ async function normalizeEpk(epk, options) {
|
|
|
746
758
|
crv,
|
|
747
759
|
x,
|
|
748
760
|
y,
|
|
749
|
-
alg
|
|
761
|
+
alg
|
|
750
762
|
} : {
|
|
751
763
|
kty: "OKP",
|
|
752
764
|
crv,
|
|
753
765
|
x,
|
|
754
|
-
alg
|
|
766
|
+
alg
|
|
755
767
|
});
|
|
756
768
|
}
|
|
757
769
|
const EC_ECDH_CURVES = /* @__PURE__ */ new Set([
|
|
@@ -800,10 +812,7 @@ function toPublicEpkJwk(jwk) {
|
|
|
800
812
|
};
|
|
801
813
|
}
|
|
802
814
|
async function ensurePrivateKey(alg, value) {
|
|
803
|
-
const normalized = isCryptoKey(value) ? value : await jwkTokey(value
|
|
804
|
-
...value,
|
|
805
|
-
alg
|
|
806
|
-
});
|
|
815
|
+
const normalized = isCryptoKey(value) ? value : await jwkTokey(value, { alg });
|
|
807
816
|
if (normalized.type !== "private") throw new JWTError("ECDH-ES ephemeral private key must have type \"private\".", "ERR_JWK_INVALID");
|
|
808
817
|
return normalized;
|
|
809
818
|
}
|
|
@@ -1121,8 +1130,7 @@ async function thumbprint(jwk, options = {}) {
|
|
|
1121
1130
|
if (typeof value !== "string" || value.length === 0) throw new JWTError(`JWK thumbprint requires the member "${name}" on an ${jwk.kty} key.`, "ERR_JWK_INVALID");
|
|
1122
1131
|
return `${JSON.stringify(name)}:${JSON.stringify(value)}`;
|
|
1123
1132
|
});
|
|
1124
|
-
|
|
1125
|
-
return base64UrlStringify(new Uint8Array(digest));
|
|
1133
|
+
return digestUrl(`{${canonical.join(",")}}`, hash);
|
|
1126
1134
|
}
|
|
1127
1135
|
const MAX_PARTY_INFO_BYTES = 1024;
|
|
1128
1136
|
const DEFAULT_JOSE_LIMITS = /* @__PURE__ */ Object.freeze({
|
|
@@ -1194,6 +1202,18 @@ function assertProtectedOnlyOption(header, options) {
|
|
|
1194
1202
|
throw new JWTError(`${family} "${param}" must be integrity protected; move it from the ${tier} header to the protected header.`, "ERR_INVALID_OPTION");
|
|
1195
1203
|
}
|
|
1196
1204
|
}
|
|
1205
|
+
function assertProducerKeyHeaders(header, options) {
|
|
1206
|
+
if (!header) return;
|
|
1207
|
+
const { tier, family } = options;
|
|
1208
|
+
if (Object.hasOwn(header, "jwk") && !isPublicJWK(Reflect.get(header, "jwk"))) throw new JWTError(`${family} "jwk" in the ${tier} header must be a public JWK; pass the pair's publicKey half.`, "ERR_INVALID_OPTION");
|
|
1209
|
+
if (Object.hasOwn(header, "epk")) throw new JWTError(`${family} "epk" is written by the ECDH-ES key agreement; pass an ephemeral key through "ecdh.ephemeralKey" instead of the ${tier} header.`, "ERR_INVALID_OPTION");
|
|
1210
|
+
}
|
|
1211
|
+
function assertProducerConfirmation(payload) {
|
|
1212
|
+
if (typeof payload !== "object" || payload === null) return;
|
|
1213
|
+
const cnf = Reflect.get(payload, "cnf");
|
|
1214
|
+
if (typeof cnf !== "object" || cnf === null || !Object.hasOwn(cnf, "jwk")) return;
|
|
1215
|
+
if (!isPublicJWK(Reflect.get(cnf, "jwk"))) throw new JWTError("Invalid JWT \"cnf\" (Confirmation) Claim: \"jwk\" must be a public JWK (RFC 7800 §3.2).", "ERR_JWT_CLAIM_INVALID");
|
|
1216
|
+
}
|
|
1197
1217
|
function assertOptionEntry(entry, options) {
|
|
1198
1218
|
const { label, objects } = options;
|
|
1199
1219
|
assertPlainObject(entry, label);
|
|
@@ -1300,14 +1320,14 @@ function buildECDHAgreementParams(ecdh) {
|
|
|
1300
1320
|
}
|
|
1301
1321
|
async function resolveSigningKey(alg, key, usage) {
|
|
1302
1322
|
if (key instanceof Uint8Array) {
|
|
1303
|
-
if (
|
|
1323
|
+
if (alg !== "HS256" && alg !== "HS384" && alg !== "HS512") throw new JWTError(`Raw key bytes are HMAC secrets; ${alg} needs an asymmetric key. Pass a JWK or a CryptoKey.`, "ERR_JWK_INVALID");
|
|
1304
1324
|
assertOctKeyLength(key, alg);
|
|
1305
1325
|
return crypto.subtle.importKey("raw", key, {
|
|
1306
1326
|
name: "HMAC",
|
|
1307
1327
|
hash: jwsHashOf(alg)
|
|
1308
1328
|
}, false, [usage]);
|
|
1309
1329
|
}
|
|
1310
|
-
if (
|
|
1330
|
+
if (RSA_SIGNATURE_ALGORITHMS.has(alg)) {
|
|
1311
1331
|
const modulusLength = modulusLengthOf(key.algorithm);
|
|
1312
1332
|
if (typeof modulusLength !== "number" || modulusLength < 2048) throw new JWTError(`${alg} requires a key modulusLength of at least 2048 bits`, "ERR_JWK_INVALID");
|
|
1313
1333
|
}
|
|
@@ -1341,7 +1361,7 @@ function assertNotStringKey(key) {
|
|
|
1341
1361
|
}
|
|
1342
1362
|
function resolvePasswordKey(alg, key) {
|
|
1343
1363
|
if (typeof key !== "string") return key;
|
|
1344
|
-
if (!alg
|
|
1364
|
+
if (!isPBES2Algorithm(alg)) throw stringKeyError();
|
|
1345
1365
|
return textEncoder.encode(key);
|
|
1346
1366
|
}
|
|
1347
1367
|
const JWS_ALG_CTX = /* @__PURE__ */ Object.freeze({
|
|
@@ -1378,32 +1398,17 @@ function keyMatchesHeader(header, keyMaterial, options) {
|
|
|
1378
1398
|
}));
|
|
1379
1399
|
return candidates.length === 0 || candidates.some((k) => keyServesHeaderAlg(header, k, ctx));
|
|
1380
1400
|
}
|
|
1381
|
-
if (isJWK(keyMaterial))
|
|
1382
|
-
if (header.kid && keyMaterial.kid && header.kid !== keyMaterial.kid) return false;
|
|
1383
|
-
return keyServesHeaderAlg(header, keyMaterial, ctx);
|
|
1384
|
-
}
|
|
1401
|
+
if (isJWK(keyMaterial)) return keyServesHeaderAlg(header, keyMaterial, ctx);
|
|
1385
1402
|
if (isCryptoKey(keyMaterial)) return keyServesHeaderAlg(header, keyMaterial, ctx);
|
|
1386
1403
|
return true;
|
|
1387
1404
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
RS256: true,
|
|
1393
|
-
RS384: true,
|
|
1394
|
-
RS512: true,
|
|
1395
|
-
PS256: true,
|
|
1396
|
-
PS384: true,
|
|
1397
|
-
PS512: true,
|
|
1398
|
-
ES256: true,
|
|
1399
|
-
ES384: true,
|
|
1400
|
-
ES512: true,
|
|
1401
|
-
Ed25519: true,
|
|
1402
|
-
EdDSA: true
|
|
1403
|
-
}));
|
|
1405
|
+
function assertJWSAlgSupported(alg) {
|
|
1406
|
+
const error = checkJWSAlgSupported(alg);
|
|
1407
|
+
if (error) throw error;
|
|
1408
|
+
}
|
|
1404
1409
|
function checkJWSAlgSupported(alg) {
|
|
1405
1410
|
return checkAlgSupported(alg, {
|
|
1406
|
-
supported:
|
|
1411
|
+
supported: isJWSAlgorithm,
|
|
1407
1412
|
code: "ERR_JWS_ALG_NOT_ALLOWED",
|
|
1408
1413
|
label: "Algorithm",
|
|
1409
1414
|
role: "signing algorithm"
|
|
@@ -1477,26 +1482,6 @@ function asKeyUsageError(error, alg) {
|
|
|
1477
1482
|
message: `Key is not usable for ${alg}.`
|
|
1478
1483
|
});
|
|
1479
1484
|
}
|
|
1480
|
-
const JWE_ALGORITHM_SET = /* @__PURE__ */ new Set(Object.keys({
|
|
1481
|
-
"dir": true,
|
|
1482
|
-
"A128KW": true,
|
|
1483
|
-
"A192KW": true,
|
|
1484
|
-
"A256KW": true,
|
|
1485
|
-
"A128GCMKW": true,
|
|
1486
|
-
"A192GCMKW": true,
|
|
1487
|
-
"A256GCMKW": true,
|
|
1488
|
-
"PBES2-HS256+A128KW": true,
|
|
1489
|
-
"PBES2-HS384+A192KW": true,
|
|
1490
|
-
"PBES2-HS512+A256KW": true,
|
|
1491
|
-
"RSA-OAEP": true,
|
|
1492
|
-
"RSA-OAEP-256": true,
|
|
1493
|
-
"RSA-OAEP-384": true,
|
|
1494
|
-
"RSA-OAEP-512": true,
|
|
1495
|
-
"ECDH-ES": true,
|
|
1496
|
-
"ECDH-ES+A128KW": true,
|
|
1497
|
-
"ECDH-ES+A192KW": true,
|
|
1498
|
-
"ECDH-ES+A256KW": true
|
|
1499
|
-
}));
|
|
1500
1485
|
const JWE_ENC_CEK_FORMAT = /* @__PURE__ */ Object.freeze({
|
|
1501
1486
|
"A128GCM": "cryptokey",
|
|
1502
1487
|
"A192GCM": "cryptokey",
|
|
@@ -1513,7 +1498,7 @@ function assertDirectKeyRole(key, enc) {
|
|
|
1513
1498
|
const JWE_DIRECT_ALGS = /* @__PURE__ */ new Set(["dir", "ECDH-ES"]);
|
|
1514
1499
|
function checkJWEAlgSupported(alg) {
|
|
1515
1500
|
return checkAlgSupported(alg, {
|
|
1516
|
-
supported:
|
|
1501
|
+
supported: isKeyManagementAlgorithm,
|
|
1517
1502
|
code: "ERR_JWE_ALG_NOT_ALLOWED",
|
|
1518
1503
|
label: "Key management algorithm",
|
|
1519
1504
|
role: "key management algorithm"
|
|
@@ -1524,7 +1509,7 @@ function checkJWEEncSupported(enc) {
|
|
|
1524
1509
|
return new JWTError(`Invalid JWE: unsupported "enc" ${quoteTokenValue(enc)}`, "ERR_JWE_INVALID");
|
|
1525
1510
|
}
|
|
1526
1511
|
function isJWEAlgSupported(alg) {
|
|
1527
|
-
return
|
|
1512
|
+
return isKeyManagementAlgorithm(alg);
|
|
1528
1513
|
}
|
|
1529
1514
|
function assertJWEAlgSupported(alg) {
|
|
1530
1515
|
const error = checkJWEAlgSupported(alg);
|
|
@@ -1649,18 +1634,26 @@ function decodeJWEPayload(plaintext, mode) {
|
|
|
1649
1634
|
function checkAlgSupported(alg, options) {
|
|
1650
1635
|
const { supported, code, label, role } = options;
|
|
1651
1636
|
if (alg === "none") return new JWTError(`"none" is not a valid ${role}`, code);
|
|
1652
|
-
if (!supported
|
|
1637
|
+
if (!supported(alg)) return new JWTError(`${label} not supported: ${quoteTokenValue(alg)}`, code);
|
|
1653
1638
|
}
|
|
1654
1639
|
function keyServesHeaderAlg(header, key, ctx) {
|
|
1655
1640
|
const { alg } = header;
|
|
1656
1641
|
if (!alg) return false;
|
|
1657
1642
|
const inferred = ctx.infer(key);
|
|
1658
1643
|
if (inferred && !inferred.includes(alg)) return false;
|
|
1659
|
-
if (!alg
|
|
1644
|
+
if (!isECDHESAlgorithm(alg)) return true;
|
|
1660
1645
|
const epkCurve = header.epk?.crv;
|
|
1661
1646
|
const keyCurve = isCryptoKey(key) ? cryptoKeyCurve(key) : "crv" in key ? key.crv : void 0;
|
|
1662
1647
|
return !epkCurve || !keyCurve || epkCurve === keyCurve;
|
|
1663
1648
|
}
|
|
1649
|
+
const RSA_SIGNATURE_ALGORITHMS = /* @__PURE__ */ new Set([
|
|
1650
|
+
"RS256",
|
|
1651
|
+
"RS384",
|
|
1652
|
+
"RS512",
|
|
1653
|
+
"PS256",
|
|
1654
|
+
"PS384",
|
|
1655
|
+
"PS512"
|
|
1656
|
+
]);
|
|
1664
1657
|
async function generateKey(alg, options = {}) {
|
|
1665
1658
|
const exportToJWK = options.toJWK !== void 0 && options.toJWK;
|
|
1666
1659
|
const defaultExtractable = exportToJWK || options.extractable !== false;
|
|
@@ -1668,41 +1661,37 @@ async function generateKey(alg, options = {}) {
|
|
|
1668
1661
|
if (alg === "A128CBC-HS256" || alg === "A192CBC-HS384" || alg === "A256CBC-HS512") {
|
|
1669
1662
|
const keyLength = bitLengthCEK(alg);
|
|
1670
1663
|
const keyBytes = secureRandomBytes(keyLength >> 3);
|
|
1671
|
-
if (exportToJWK)
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
ext: true,
|
|
1675
|
-
key_ops: resolveKeyUsage(options.keyUsage, {
|
|
1664
|
+
if (exportToJWK) {
|
|
1665
|
+
const jwk = await keyToJWK(keyBytes);
|
|
1666
|
+
const keyOps = options.keyUsage === void 0 ? void 0 : resolveKeyUsage(options.keyUsage, {
|
|
1676
1667
|
allowed: CONTENT_ENCRYPTION_USAGES,
|
|
1677
1668
|
fallback: ["encrypt", "decrypt"],
|
|
1678
1669
|
alg
|
|
1679
|
-
})
|
|
1680
|
-
|
|
1670
|
+
});
|
|
1671
|
+
return withGeneratedKid(kid, withKeyOps({
|
|
1672
|
+
...jwk,
|
|
1673
|
+
alg
|
|
1674
|
+
}, keyOps));
|
|
1675
|
+
}
|
|
1681
1676
|
return keyBytes;
|
|
1682
1677
|
}
|
|
1683
1678
|
const { algorithm, keyUsages } = getGenerateKeyParams(alg, options);
|
|
1684
1679
|
const key = await crypto.subtle.generateKey(algorithm, defaultExtractable, keyUsages);
|
|
1685
1680
|
if (exportToJWK) {
|
|
1686
|
-
if (key
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
else {
|
|
1691
|
-
const [pub, priv] = await Promise.all([exportKey(key.publicKey), exportKey(key.privateKey)]);
|
|
1692
|
-
const publicKey = await withGeneratedKid(kid, {
|
|
1693
|
-
...pub,
|
|
1694
|
-
alg
|
|
1695
|
-
});
|
|
1681
|
+
if (isCryptoKeyPair(key)) {
|
|
1682
|
+
const pairAlg = generatedPairAlgorithm(alg);
|
|
1683
|
+
const [pub, priv] = await Promise.all([exportJWK(key.publicKey), exportJWK(key.privateKey)]);
|
|
1684
|
+
const publicKey = await withGeneratedKid(kid, withKeyOps(withAlg(pub, pairAlg), requestedKeyOps(options, key.publicKey)));
|
|
1696
1685
|
const named = publicKey.kid === void 0 ? void 0 : { kid: publicKey.kid };
|
|
1697
1686
|
return {
|
|
1698
1687
|
privateKey: {
|
|
1699
|
-
...priv,
|
|
1700
|
-
alg,
|
|
1688
|
+
...withKeyOps(withAlg(priv, pairAlg), requestedKeyOps(options, key.privateKey)),
|
|
1701
1689
|
...named
|
|
1702
1690
|
},
|
|
1703
1691
|
publicKey
|
|
1704
1692
|
};
|
|
1705
1693
|
}
|
|
1694
|
+
return withGeneratedKid(kid, withKeyOps(withAlg(await exportJWK(key), generatedSecretAlgorithm(alg)), requestedKeyOps(options, key)));
|
|
1706
1695
|
}
|
|
1707
1696
|
return key;
|
|
1708
1697
|
}
|
|
@@ -1746,11 +1735,11 @@ async function deriveKeyFromPassword(password, options) {
|
|
|
1746
1735
|
const finalUsages = keyUsage ?? ["wrapKey", "unwrapKey"];
|
|
1747
1736
|
const finalExtractable = exportToJWK ? true : extractable === true;
|
|
1748
1737
|
const derivedKey = await crypto.subtle.importKey("raw", derivedBytes, { name: "AES-KW" }, finalExtractable, finalUsages);
|
|
1749
|
-
if (exportToJWK) return {
|
|
1738
|
+
if (exportToJWK) return withKeyOps({
|
|
1750
1739
|
...await keyToJWK(derivedKey),
|
|
1751
1740
|
alg: wrappingAlg,
|
|
1752
1741
|
kty: "oct"
|
|
1753
|
-
};
|
|
1742
|
+
}, requestedKeyOps(options, derivedKey));
|
|
1754
1743
|
return derivedKey;
|
|
1755
1744
|
}
|
|
1756
1745
|
async function deriveJWKFromPassword(password, options) {
|
|
@@ -1827,10 +1816,10 @@ async function importKey(key, algOrOptions) {
|
|
|
1827
1816
|
const cached = await cache.get(key, effectiveAlg);
|
|
1828
1817
|
if (cached) return cached;
|
|
1829
1818
|
}
|
|
1830
|
-
const cryptoKey = await jwkTokey(key
|
|
1831
|
-
|
|
1819
|
+
const cryptoKey = await jwkTokey(key, {
|
|
1820
|
+
extractable,
|
|
1832
1821
|
alg
|
|
1833
|
-
}
|
|
1822
|
+
});
|
|
1834
1823
|
if (cache) await cache.set(key, effectiveAlg, cryptoKey);
|
|
1835
1824
|
return cryptoKey;
|
|
1836
1825
|
}
|
|
@@ -1846,7 +1835,7 @@ function assertJWKAlgAuthority(jwk, alg) {
|
|
|
1846
1835
|
function assertIntentOnJWK(jwk, expect) {
|
|
1847
1836
|
if (expect === void 0 || jwk.kty === "oct") return;
|
|
1848
1837
|
const hasPrivateScalar = "d" in jwk && typeof jwk.d === "string";
|
|
1849
|
-
if (expect === "public" && hasPrivateScalar) throw new JWTError("Private JWK passed where a public key is expected.
|
|
1838
|
+
if (expect === "public" && hasPrivateScalar) throw new JWTError("Private JWK passed where a public key is expected.", "ERR_JWK_INVALID");
|
|
1850
1839
|
if (expect === "private" && !hasPrivateScalar) throw new JWTError("Public JWK passed where a private key is expected.", "ERR_JWK_INVALID");
|
|
1851
1840
|
}
|
|
1852
1841
|
function assertIntentOnCryptoKey(key, expect) {
|
|
@@ -1854,8 +1843,8 @@ function assertIntentOnCryptoKey(key, expect) {
|
|
|
1854
1843
|
if (expect === "public" && key.type === "private") throw new JWTError("Private CryptoKey passed where a public key is expected.", "ERR_JWK_INVALID");
|
|
1855
1844
|
if (expect === "private" && key.type === "public") throw new JWTError("Public CryptoKey passed where a private key is expected.", "ERR_JWK_INVALID");
|
|
1856
1845
|
}
|
|
1857
|
-
async function
|
|
1858
|
-
if (!isCryptoKey(key)) throw new JWTError(`
|
|
1846
|
+
async function exportJWK(key, jwkParams) {
|
|
1847
|
+
if (!isCryptoKey(key)) throw new JWTError(`exportJWK expects a CryptoKey, got ${key === null ? "null" : typeof key}.`, "ERR_INVALID_OPTION");
|
|
1859
1848
|
if (!key.extractable) throw new JWTError("A non-extractable CryptoKey cannot be exported as a JWK.", "ERR_JWK_INVALID");
|
|
1860
1849
|
const exportedJwk = await keyToJWK(key);
|
|
1861
1850
|
if (jwkParams) {
|
|
@@ -1868,6 +1857,19 @@ async function exportKey(key, jwkParams) {
|
|
|
1868
1857
|
}
|
|
1869
1858
|
return exportedJwk;
|
|
1870
1859
|
}
|
|
1860
|
+
async function exportKey(key, jwkParams) {
|
|
1861
|
+
return exportJWK(key, jwkParams);
|
|
1862
|
+
}
|
|
1863
|
+
function toPublicJWK(jwk) {
|
|
1864
|
+
if (!isAsymmetricJWK(jwk)) throw new JWTError(isJWK(jwk) ? "An oct JWK has no public half." : "toPublicJWK expects an asymmetric JWK.", "ERR_JWK_INVALID");
|
|
1865
|
+
const { d: _d, p: _p, q: _q, dp: _dp, dq: _dq, qi: _qi, oth: _oth, key_ops, ...publicMembers } = sanitizeObjectCopy({ ...jwk });
|
|
1866
|
+
const publicKey = key_ops === void 0 ? publicMembers : {
|
|
1867
|
+
...publicMembers,
|
|
1868
|
+
key_ops: publicKeyOps(key_ops)
|
|
1869
|
+
};
|
|
1870
|
+
if (!isPublicJWK(publicKey)) throw new JWTError(`${jwk.kty} JWK lacks the public members of its key type.`, "ERR_JWK_INVALID");
|
|
1871
|
+
return publicKey;
|
|
1872
|
+
}
|
|
1871
1873
|
async function wrapKey(keyToWrap, wrappingKey, options) {
|
|
1872
1874
|
assertPlainObject(options, "options");
|
|
1873
1875
|
assertKeyBytesArg(keyToWrap, "keyToWrap");
|
|
@@ -1916,6 +1918,7 @@ async function wrapKey(keyToWrap, wrappingKey, options) {
|
|
|
1916
1918
|
case "ECDH-ES+A256KW": {
|
|
1917
1919
|
assertCryptoKeyFor(alg, importedWrappingKey, "wrapping");
|
|
1918
1920
|
const enc = options.ecdh?.enc;
|
|
1921
|
+
if (enc !== void 0) assertJWEEncSupported(enc);
|
|
1919
1922
|
if (alg === "ECDH-ES" && !enc) throw new JWTError("ECDH-ES direct key agreement requires options.ecdh.enc (the target content encryption algorithm)", "ERR_JWK_INVALID");
|
|
1920
1923
|
const { encryptedKey, parameters } = await ecdhesWrap(alg, {
|
|
1921
1924
|
enc,
|
|
@@ -2007,9 +2010,10 @@ async function unwrapKey(wrappedKey, unwrappingKey, options) {
|
|
|
2007
2010
|
const apvBytes = segmentBytes(options.apv ?? /* @__PURE__ */ new Uint8Array(0), "apv");
|
|
2008
2011
|
assertPartyInfoLength("apu", apuBytes);
|
|
2009
2012
|
assertPartyInfoLength("apv", apvBytes);
|
|
2013
|
+
if (options.enc !== void 0) assertJWEEncSupported(options.enc);
|
|
2010
2014
|
const infoAlg = alg === "ECDH-ES" ? options.enc : alg;
|
|
2011
2015
|
if (!infoAlg) throw new JWTError("ECDH-ES requires content encryption algorithm ('enc') to derive the shared secret", "ERR_JWE_INVALID");
|
|
2012
|
-
const sharedSecret = await unwrapping(deriveECDHESKey(epkCandidate, importedUnwrappingKey, infoAlg, ecdhDerivedBits(alg,
|
|
2016
|
+
const sharedSecret = await unwrapping(deriveECDHESKey(epkCandidate, importedUnwrappingKey, infoAlg, ecdhDerivedBits(alg, options.enc), apuBytes, apvBytes));
|
|
2013
2017
|
if (alg === "ECDH-ES") unwrappedCekBytes = sharedSecret;
|
|
2014
2018
|
else {
|
|
2015
2019
|
const kwAlg = innerKeyWrapAlg(alg);
|
|
@@ -2043,7 +2047,7 @@ async function importPEM(pem, alg, options) {
|
|
|
2043
2047
|
default: throw new JWTError(`Unsupported PEM type: ${String(pemType)}`, "ERR_JWK_UNSUPPORTED");
|
|
2044
2048
|
}
|
|
2045
2049
|
return {
|
|
2046
|
-
...await
|
|
2050
|
+
...await exportJWK(cryptoKey, options?.jwkParams),
|
|
2047
2051
|
alg
|
|
2048
2052
|
};
|
|
2049
2053
|
}
|
|
@@ -2083,8 +2087,9 @@ function inferPEMType(pem) {
|
|
|
2083
2087
|
async function deriveSharedSecret(publicKey, privateKey, options) {
|
|
2084
2088
|
assertPlainObject(options, "options");
|
|
2085
2089
|
const { enc: alg, keyLength: requestedKeyLength, partyUInfo, partyVInfo } = options;
|
|
2090
|
+
if (!isECDHESAlgorithm(alg)) assertJWEEncSupported(alg);
|
|
2086
2091
|
if (requestedKeyLength !== void 0) assertDerivedKeyLength(requestedKeyLength);
|
|
2087
|
-
const importAlg = alg
|
|
2092
|
+
const importAlg = isECDHESAlgorithm(alg) ? alg : "ECDH-ES";
|
|
2088
2093
|
const pubCryptoKey = isCryptoKey(publicKey) ? publicKey : await importKey(publicKey, {
|
|
2089
2094
|
alg: importAlg,
|
|
2090
2095
|
expect: "public"
|
|
@@ -2096,7 +2101,7 @@ async function deriveSharedSecret(publicKey, privateKey, options) {
|
|
|
2096
2101
|
let keyLength;
|
|
2097
2102
|
if (requestedKeyLength !== void 0) keyLength = requestedKeyLength;
|
|
2098
2103
|
else if (alg === "ECDH-ES") throw new JWTError("deriveSharedSecret with enc \"ECDH-ES\" requires an explicit keyLength in options", "ERR_JWK_INVALID");
|
|
2099
|
-
else if (alg
|
|
2104
|
+
else if (isECDHESAlgorithm(alg)) keyLength = aesBitsOf(innerKeyWrapAlg(alg));
|
|
2100
2105
|
else keyLength = bitLengthCEK(alg);
|
|
2101
2106
|
return deriveECDHESKey(pubCryptoKey, privCryptoKey, alg, keyLength, partyUInfo ?? /* @__PURE__ */ new Uint8Array(0), partyVInfo ?? /* @__PURE__ */ new Uint8Array(0));
|
|
2102
2107
|
}
|
|
@@ -2405,4 +2410,42 @@ function segmentBytes(value, name) {
|
|
|
2405
2410
|
what: `"${name}"`
|
|
2406
2411
|
}) : value;
|
|
2407
2412
|
}
|
|
2408
|
-
|
|
2413
|
+
function withAlg(jwk, alg) {
|
|
2414
|
+
return {
|
|
2415
|
+
...jwk,
|
|
2416
|
+
alg
|
|
2417
|
+
};
|
|
2418
|
+
}
|
|
2419
|
+
function generatedPairAlgorithm(alg) {
|
|
2420
|
+
return alg;
|
|
2421
|
+
}
|
|
2422
|
+
function generatedSecretAlgorithm(alg) {
|
|
2423
|
+
return alg;
|
|
2424
|
+
}
|
|
2425
|
+
const PUBLIC_KEY_OPS = /* @__PURE__ */ new Map([
|
|
2426
|
+
["sign", "verify"],
|
|
2427
|
+
["verify", "verify"],
|
|
2428
|
+
["decrypt", "encrypt"],
|
|
2429
|
+
["encrypt", "encrypt"],
|
|
2430
|
+
["unwrapKey", "wrapKey"],
|
|
2431
|
+
["wrapKey", "wrapKey"]
|
|
2432
|
+
]);
|
|
2433
|
+
function publicKeyOps(keyOps) {
|
|
2434
|
+
if (!Array.isArray(keyOps)) return [];
|
|
2435
|
+
const ops = [];
|
|
2436
|
+
for (const op of keyOps) {
|
|
2437
|
+
const publicOp = typeof op === "string" ? PUBLIC_KEY_OPS.get(op) : void 0;
|
|
2438
|
+
if (publicOp !== void 0 && !ops.includes(publicOp)) ops.push(publicOp);
|
|
2439
|
+
}
|
|
2440
|
+
return ops;
|
|
2441
|
+
}
|
|
2442
|
+
function requestedKeyOps(options, key) {
|
|
2443
|
+
return options.keyUsage === void 0 ? void 0 : [...key.usages];
|
|
2444
|
+
}
|
|
2445
|
+
function withKeyOps(jwk, keyOps) {
|
|
2446
|
+
return keyOps === void 0 ? jwk : {
|
|
2447
|
+
...jwk,
|
|
2448
|
+
key_ops: keyOps
|
|
2449
|
+
};
|
|
2450
|
+
}
|
|
2451
|
+
export { DEFAULT_JOSE_LIMITS, DEFAULT_PBES2_ITERATIONS, JWE_ALG_CTX, JWE_DIRECT_ALGS, JWS_ALG_CTX, WeakMapJWKCache, applyB64Header, asDecryptionError, asJWTError, asKeyLookupError, asKeyUsageError, assertB64Option, assertCompactToken, assertConsumerOptions, assertDirectKeyRole, assertEntryLimit, assertJWEAlgSupported, assertJWEEncSupported, assertJWKSetCandidates, assertJWSAlgSupported, assertNoZipOption, assertNotStringKey, assertOptionEntry, assertPBES2Params, assertProducerCEK, assertProducerConfirmation, assertProducerCrit, assertProducerIV, assertProducerKeyHeaders, assertProtectedOnlyOption, assertSerializationObject, buildECDHAgreementParams, buildJWKSetFilter, buildUnwrapKeyOptions, checkAlgAllowed, checkJWEAlgSupported, checkJWEContentSegments, checkJWEDirectEncryptedKey, checkJWEEncAllowed, checkJWEEncSupported, checkJWEUnprotectedCrit, checkJWEZip, checkJWSAlgSupported, checkJWSB64, checkProtectedTyp, checkSerializationEntry, clearJWKCache, configureJWKCache, createPBES2Budget, decodeJWEPayload, decodeJWSPayload, decodeProtectedHeader, decodeSegment, decrypt, deriveJWKFromPassword, deriveKeyFromPassword, deriveSharedSecret, encrypt, encryptKey, exportJWK, exportKey, exportPEM, exportReturnedCek, generateCEK, generateIV, generateJWK, generateKey, getJWKsFromSet, importKey, importPEM, isJWEAlgSupported, keyMatchesHeader, quoteTokenValue, resolveKeyInput, resolveLimit, resolvePasswordKey, resolvePayloadMode, resolveSigningKey, serializeProtectedHeader, sign, stringKeyError, thumbprint, toPublicJWK, unwrapKey, validateClaimsIfJson, verify, wrapKey };
|