jose 6.2.6 → 6.2.8
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/dist/webapi/jwe/compact/decrypt.js +2 -2
- package/dist/webapi/jwe/flattened/encrypt.js +12 -12
- package/dist/webapi/jwe/general/decrypt.js +8 -0
- package/dist/webapi/jwe/general/encrypt.js +51 -52
- package/dist/webapi/jwks/local.js +13 -34
- package/dist/webapi/jwks/remote.js +17 -26
- package/dist/webapi/jws/compact/verify.js +2 -2
- package/dist/webapi/jws/flattened/sign.js +1 -9
- package/dist/webapi/jws/general/sign.js +12 -21
- package/dist/webapi/jws/general/verify.js +13 -11
- package/dist/webapi/jwt/decrypt.js +3 -3
- package/dist/webapi/jwt/verify.js +4 -4
- package/dist/webapi/key/export.js +3 -3
- package/dist/webapi/key/generate_key_pair.js +10 -9
- package/dist/webapi/key/generate_secret.js +2 -2
- package/dist/webapi/lib/asn1.js +32 -65
- package/dist/webapi/lib/content_encryption.js +16 -34
- package/dist/webapi/lib/crypto_key.js +6 -0
- package/dist/webapi/lib/invalid_key_input.js +0 -1
- package/dist/webapi/lib/jwe_algorithms.js +48 -84
- package/dist/webapi/lib/jwe_decrypt.js +46 -41
- package/dist/webapi/lib/jwe_encrypt.js +14 -13
- package/dist/webapi/lib/jwk_to_key.js +5 -10
- package/dist/webapi/lib/jws_algorithms.js +22 -23
- package/dist/webapi/lib/jws_sign.js +12 -6
- package/dist/webapi/lib/jws_verify.js +26 -27
- package/dist/webapi/lib/jwt_claims_set.js +60 -102
- package/dist/webapi/lib/key.js +61 -99
- package/dist/webapi/lib/key_algorithm.js +7 -13
- package/dist/webapi/lib/key_descriptor.js +1 -1
- package/dist/webapi/lib/key_management.js +74 -161
- package/dist/webapi/lib/options.js +11 -14
- package/dist/webapi/lib/signing.js +6 -16
- package/dist/webapi/lib/type_checks.js +13 -18
- package/dist/webapi/util/base64url.js +4 -3
- package/package.json +1 -1
|
@@ -2,56 +2,23 @@ import { JWTClaimValidationFailed, JWTExpired, JWTInvalid } from '../util/errors
|
|
|
2
2
|
import { encoder, strictDecoder } from './buffer_utils.js';
|
|
3
3
|
import { isObject } from './type_checks.js';
|
|
4
4
|
const epoch = (date) => Math.floor(date.getTime() / 1000);
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const multipliers = {
|
|
6
|
+
s: 1,
|
|
7
|
+
m: 60,
|
|
8
|
+
h: 3600,
|
|
9
|
+
d: 86400,
|
|
10
|
+
w: 604800,
|
|
11
|
+
y: 31557600,
|
|
12
|
+
};
|
|
10
13
|
const REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;
|
|
14
|
+
const checkFailed = 'check_failed';
|
|
11
15
|
export function secs(str) {
|
|
12
16
|
const matched = REGEX.exec(str);
|
|
13
17
|
if (!matched || (matched[4] && matched[1])) {
|
|
14
18
|
throw new TypeError('Invalid time period format');
|
|
15
19
|
}
|
|
16
20
|
const value = parseFloat(matched[2]);
|
|
17
|
-
const
|
|
18
|
-
let numericDate;
|
|
19
|
-
switch (unit) {
|
|
20
|
-
case 'sec':
|
|
21
|
-
case 'secs':
|
|
22
|
-
case 'second':
|
|
23
|
-
case 'seconds':
|
|
24
|
-
case 's':
|
|
25
|
-
numericDate = Math.round(value);
|
|
26
|
-
break;
|
|
27
|
-
case 'minute':
|
|
28
|
-
case 'minutes':
|
|
29
|
-
case 'min':
|
|
30
|
-
case 'mins':
|
|
31
|
-
case 'm':
|
|
32
|
-
numericDate = Math.round(value * minute);
|
|
33
|
-
break;
|
|
34
|
-
case 'hour':
|
|
35
|
-
case 'hours':
|
|
36
|
-
case 'hr':
|
|
37
|
-
case 'hrs':
|
|
38
|
-
case 'h':
|
|
39
|
-
numericDate = Math.round(value * hour);
|
|
40
|
-
break;
|
|
41
|
-
case 'day':
|
|
42
|
-
case 'days':
|
|
43
|
-
case 'd':
|
|
44
|
-
numericDate = Math.round(value * day);
|
|
45
|
-
break;
|
|
46
|
-
case 'week':
|
|
47
|
-
case 'weeks':
|
|
48
|
-
case 'w':
|
|
49
|
-
numericDate = Math.round(value * week);
|
|
50
|
-
break;
|
|
51
|
-
default:
|
|
52
|
-
numericDate = Math.round(value * year);
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
21
|
+
const numericDate = Math.round(value * multipliers[matched[3][0].toLowerCase()]);
|
|
55
22
|
if (matched[1] === '-' || matched[4] === 'ago') {
|
|
56
23
|
return -numericDate;
|
|
57
24
|
}
|
|
@@ -63,6 +30,13 @@ function validateInput(label, input) {
|
|
|
63
30
|
}
|
|
64
31
|
return input;
|
|
65
32
|
}
|
|
33
|
+
function numericDate(value, label) {
|
|
34
|
+
if (typeof value === 'number')
|
|
35
|
+
return validateInput(label, value);
|
|
36
|
+
if (value instanceof Date)
|
|
37
|
+
return validateInput(label, epoch(value));
|
|
38
|
+
return epoch(new Date()) + secs(value);
|
|
39
|
+
}
|
|
66
40
|
const normalizeTyp = (value) => {
|
|
67
41
|
if (value.includes('/')) {
|
|
68
42
|
return value.toLowerCase();
|
|
@@ -74,10 +48,22 @@ const checkAudiencePresence = (audPayload, audOption) => {
|
|
|
74
48
|
return audOption.includes(audPayload);
|
|
75
49
|
}
|
|
76
50
|
if (Array.isArray(audPayload)) {
|
|
77
|
-
return audOption.some(
|
|
51
|
+
return audOption.some((aud) => audPayload.includes(aud));
|
|
78
52
|
}
|
|
79
53
|
return false;
|
|
80
54
|
};
|
|
55
|
+
function validateNumericDate(payload, claim, required = false) {
|
|
56
|
+
const value = payload[claim];
|
|
57
|
+
if (value === undefined && !required)
|
|
58
|
+
return undefined;
|
|
59
|
+
if (typeof value !== 'number') {
|
|
60
|
+
throw new JWTClaimValidationFailed(`"${claim}" claim must be a number`, payload, claim, 'invalid');
|
|
61
|
+
}
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
function unexpectedClaim(payload, claim) {
|
|
65
|
+
throw new JWTClaimValidationFailed(`unexpected "${claim}" claim value`, payload, claim, checkFailed);
|
|
66
|
+
}
|
|
81
67
|
export function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {
|
|
82
68
|
let payload;
|
|
83
69
|
try {
|
|
@@ -92,7 +78,7 @@ export function validateClaimsSet(protectedHeader, encodedPayload, options = {})
|
|
|
92
78
|
if (typ &&
|
|
93
79
|
(typeof protectedHeader.typ !== 'string' ||
|
|
94
80
|
normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ))) {
|
|
95
|
-
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, 'typ',
|
|
81
|
+
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, 'typ', checkFailed);
|
|
96
82
|
}
|
|
97
83
|
const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options;
|
|
98
84
|
const presenceCheck = [...requiredClaims];
|
|
@@ -105,65 +91,56 @@ export function validateClaimsSet(protectedHeader, encodedPayload, options = {})
|
|
|
105
91
|
if (issuer !== undefined)
|
|
106
92
|
presenceCheck.push('iss');
|
|
107
93
|
for (const claim of new Set(presenceCheck.reverse())) {
|
|
108
|
-
if (!(claim
|
|
94
|
+
if (!Object.hasOwn(payload, claim)) {
|
|
109
95
|
throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, payload, claim, 'missing');
|
|
110
96
|
}
|
|
111
97
|
}
|
|
112
98
|
if (issuer !== undefined &&
|
|
113
99
|
!(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss)) {
|
|
114
|
-
|
|
100
|
+
unexpectedClaim(payload, 'iss');
|
|
115
101
|
}
|
|
116
102
|
if (subject !== undefined && payload.sub !== subject) {
|
|
117
|
-
|
|
103
|
+
unexpectedClaim(payload, 'sub');
|
|
118
104
|
}
|
|
119
105
|
if (audience !== undefined &&
|
|
120
106
|
!checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) {
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
break;
|
|
131
|
-
case 'undefined':
|
|
132
|
-
tolerance = 0;
|
|
133
|
-
break;
|
|
134
|
-
default:
|
|
107
|
+
unexpectedClaim(payload, 'aud');
|
|
108
|
+
}
|
|
109
|
+
const { clockTolerance } = options;
|
|
110
|
+
let tolerance = 0;
|
|
111
|
+
if (typeof clockTolerance === 'string') {
|
|
112
|
+
tolerance = secs(clockTolerance);
|
|
113
|
+
}
|
|
114
|
+
else if (clockTolerance !== undefined) {
|
|
115
|
+
if (typeof clockTolerance !== 'number') {
|
|
135
116
|
throw new TypeError('Invalid clockTolerance option type');
|
|
117
|
+
}
|
|
118
|
+
tolerance = clockTolerance;
|
|
136
119
|
}
|
|
137
120
|
validateInput('clockTolerance option', tolerance);
|
|
138
121
|
const { currentDate } = options;
|
|
139
122
|
const now = validateInput('currentDate option', epoch(currentDate || new Date()));
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
throw new JWTClaimValidationFailed('"nbf" claim must be a number', payload, 'nbf', 'invalid');
|
|
146
|
-
}
|
|
147
|
-
if (payload.nbf > now + tolerance) {
|
|
148
|
-
throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, 'nbf', 'check_failed');
|
|
123
|
+
const iat = validateNumericDate(payload, 'iat', maxTokenAge !== undefined);
|
|
124
|
+
const nbf = validateNumericDate(payload, 'nbf');
|
|
125
|
+
if (nbf !== undefined) {
|
|
126
|
+
if (nbf > now + tolerance) {
|
|
127
|
+
throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, 'nbf', checkFailed);
|
|
149
128
|
}
|
|
150
129
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (payload.exp <= now - tolerance) {
|
|
156
|
-
throw new JWTExpired('"exp" claim timestamp check failed', payload, 'exp', 'check_failed');
|
|
130
|
+
const exp = validateNumericDate(payload, 'exp');
|
|
131
|
+
if (exp !== undefined) {
|
|
132
|
+
if (exp <= now - tolerance) {
|
|
133
|
+
throw new JWTExpired('"exp" claim timestamp check failed', payload, 'exp', checkFailed);
|
|
157
134
|
}
|
|
158
135
|
}
|
|
159
136
|
if (maxTokenAge !== undefined) {
|
|
160
|
-
const age = now -
|
|
137
|
+
const age = now - iat;
|
|
161
138
|
const max = typeof maxTokenAge === 'number' ? maxTokenAge : secs(maxTokenAge);
|
|
162
139
|
if (age - tolerance > max) {
|
|
163
|
-
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, 'iat',
|
|
140
|
+
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, 'iat', checkFailed);
|
|
164
141
|
}
|
|
165
142
|
if (age < 0 - tolerance) {
|
|
166
|
-
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, 'iat',
|
|
143
|
+
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, 'iat', checkFailed);
|
|
167
144
|
}
|
|
168
145
|
}
|
|
169
146
|
return payload;
|
|
@@ -201,39 +178,20 @@ export class JWTClaimsBuilder {
|
|
|
201
178
|
this.#payload.jti = value;
|
|
202
179
|
}
|
|
203
180
|
set nbf(value) {
|
|
204
|
-
|
|
205
|
-
this.#payload.nbf = validateInput('setNotBefore', value);
|
|
206
|
-
}
|
|
207
|
-
else if (value instanceof Date) {
|
|
208
|
-
this.#payload.nbf = validateInput('setNotBefore', epoch(value));
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
this.#payload.nbf = epoch(new Date()) + secs(value);
|
|
212
|
-
}
|
|
181
|
+
this.#payload.nbf = numericDate(value, 'setNotBefore');
|
|
213
182
|
}
|
|
214
183
|
set exp(value) {
|
|
215
|
-
|
|
216
|
-
this.#payload.exp = validateInput('setExpirationTime', value);
|
|
217
|
-
}
|
|
218
|
-
else if (value instanceof Date) {
|
|
219
|
-
this.#payload.exp = validateInput('setExpirationTime', epoch(value));
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
this.#payload.exp = epoch(new Date()) + secs(value);
|
|
223
|
-
}
|
|
184
|
+
this.#payload.exp = numericDate(value, 'setExpirationTime');
|
|
224
185
|
}
|
|
225
186
|
set iat(value) {
|
|
226
187
|
if (value === undefined) {
|
|
227
188
|
this.#payload.iat = epoch(new Date());
|
|
228
189
|
}
|
|
229
|
-
else if (value instanceof Date) {
|
|
230
|
-
this.#payload.iat = validateInput('setIssuedAt', epoch(value));
|
|
231
|
-
}
|
|
232
190
|
else if (typeof value === 'string') {
|
|
233
191
|
this.#payload.iat = validateInput('setIssuedAt', epoch(new Date()) + secs(value));
|
|
234
192
|
}
|
|
235
193
|
else {
|
|
236
|
-
this.#payload.iat =
|
|
194
|
+
this.#payload.iat = numericDate(value, 'setIssuedAt');
|
|
237
195
|
}
|
|
238
196
|
}
|
|
239
197
|
}
|
package/dist/webapi/lib/key.js
CHANGED
|
@@ -7,17 +7,7 @@ const tag = (key) => key[Symbol.toStringTag];
|
|
|
7
7
|
const jwkMatchesOp = (entry, key, usage) => {
|
|
8
8
|
const { alg } = entry;
|
|
9
9
|
if (key.use !== undefined) {
|
|
10
|
-
|
|
11
|
-
switch (usage) {
|
|
12
|
-
case 'sign':
|
|
13
|
-
case 'verify':
|
|
14
|
-
expected = 'sig';
|
|
15
|
-
break;
|
|
16
|
-
case 'encrypt':
|
|
17
|
-
case 'decrypt':
|
|
18
|
-
expected = 'enc';
|
|
19
|
-
break;
|
|
20
|
-
}
|
|
10
|
+
const expected = usage === 'sign' || usage === 'verify' ? 'sig' : 'enc';
|
|
21
11
|
if (key.use !== expected) {
|
|
22
12
|
throw new TypeError(`Invalid key for this operation, its "use" must be "${expected}" when present`);
|
|
23
13
|
}
|
|
@@ -26,79 +16,56 @@ const jwkMatchesOp = (entry, key, usage) => {
|
|
|
26
16
|
throw new TypeError(`Invalid key for this operation, its "alg" must be "${alg}" when present`);
|
|
27
17
|
}
|
|
28
18
|
if (Array.isArray(key.key_ops)) {
|
|
29
|
-
const expectedKeyOp = usage === 'encrypt' || usage === 'decrypt' ? entry.
|
|
30
|
-
if (expectedKeyOp && key.key_ops
|
|
19
|
+
const expectedKeyOp = usage === 'encrypt' || usage === 'decrypt' ? entry.ops?.[usage === 'encrypt' ? 0 : 1] : usage;
|
|
20
|
+
if (expectedKeyOp && !key.key_ops.includes(expectedKeyOp)) {
|
|
31
21
|
throw new TypeError(`Invalid key for this operation, its "key_ops" must include "${expectedKeyOp}" when present`);
|
|
32
22
|
}
|
|
33
23
|
}
|
|
34
|
-
return true;
|
|
35
|
-
};
|
|
36
|
-
const symmetricTypeCheck = (entry, key, usage) => {
|
|
37
|
-
const { alg } = entry;
|
|
38
|
-
if (key instanceof Uint8Array)
|
|
39
|
-
return { kind: BYTES, key };
|
|
40
|
-
if (jwk.isJWK(key)) {
|
|
41
|
-
if (jwk.isSecretJWK(key) && jwkMatchesOp(entry, key, usage))
|
|
42
|
-
return { kind: JWK, key };
|
|
43
|
-
throw new TypeError(`JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present`);
|
|
44
|
-
}
|
|
45
|
-
if (!isKeyLike(key)) {
|
|
46
|
-
throw new TypeError(invalidKeyInput(alg, key, 'CryptoKey', 'KeyObject', 'JSON Web Key', 'Uint8Array'));
|
|
47
|
-
}
|
|
48
|
-
if (key.type !== 'secret') {
|
|
49
|
-
throw new TypeError(`${tag(key)} instances for symmetric algorithms must be of type "secret"`);
|
|
50
|
-
}
|
|
51
|
-
return isCryptoKey(key) ? { kind: CRYPTO, key } : { kind: KEYOBJECT, key };
|
|
52
24
|
};
|
|
53
|
-
|
|
54
|
-
const { alg } = entry;
|
|
25
|
+
export function checkKeyType(entry, key, usage) {
|
|
26
|
+
const { alg, secret } = entry;
|
|
27
|
+
const privateKey = usage === 'decrypt' || usage === 'sign';
|
|
28
|
+
if (secret && key instanceof Uint8Array)
|
|
29
|
+
return [BYTES, key];
|
|
55
30
|
if (jwk.isJWK(key)) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return { kind: JWK, key };
|
|
61
|
-
throw new TypeError(`JSON Web Key for this operation must be a private JWK`);
|
|
62
|
-
case 'encrypt':
|
|
63
|
-
case 'verify':
|
|
64
|
-
if (jwk.isPublicJWK(key) && jwkMatchesOp(entry, key, usage))
|
|
65
|
-
return { kind: JWK, key };
|
|
66
|
-
throw new TypeError(`JSON Web Key for this operation must be a public JWK`);
|
|
31
|
+
if (secret ? !jwk.isSecretJWK(key) : !(privateKey ? jwk.isPrivateJWK(key) : jwk.isPublicJWK(key))) {
|
|
32
|
+
throw new TypeError(secret
|
|
33
|
+
? `JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present`
|
|
34
|
+
: `JSON Web Key for this operation must be a ${privateKey ? 'private' : 'public'} JWK`);
|
|
67
35
|
}
|
|
36
|
+
jwkMatchesOp(entry, key, usage);
|
|
37
|
+
return [JWK, key];
|
|
68
38
|
}
|
|
69
39
|
if (!isKeyLike(key)) {
|
|
70
|
-
throw new TypeError(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
throw new TypeError(`${tag(key)} instances for asymmetric algorithms must not be of type "secret"`);
|
|
40
|
+
throw new TypeError(secret
|
|
41
|
+
? invalidKeyInput(alg, key, 'CryptoKey', 'KeyObject', 'JSON Web Key', 'Uint8Array')
|
|
42
|
+
: invalidKeyInput(alg, key, 'CryptoKey', 'KeyObject', 'JSON Web Key'));
|
|
74
43
|
}
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
throw new TypeError(`${tag(key)} instances for asymmetric algorithm signing must be of type "private"`);
|
|
79
|
-
case 'decrypt':
|
|
80
|
-
throw new TypeError(`${tag(key)} instances for asymmetric algorithm decryption must be of type "private"`);
|
|
44
|
+
if (secret) {
|
|
45
|
+
if (key.type !== 'secret') {
|
|
46
|
+
throw new TypeError(`${tag(key)} instances for symmetric algorithms must be of type "secret"`);
|
|
81
47
|
}
|
|
82
48
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
49
|
+
else {
|
|
50
|
+
if (key.type === 'secret') {
|
|
51
|
+
throw new TypeError(`${tag(key)} instances for asymmetric algorithms must not be of type "secret"`);
|
|
52
|
+
}
|
|
53
|
+
const expectedType = privateKey ? 'private' : 'public';
|
|
54
|
+
if ((key.type === 'public' || key.type === 'private') && key.type !== expectedType) {
|
|
55
|
+
const operation = usage === 'sign'
|
|
56
|
+
? 'signing'
|
|
57
|
+
: usage === 'verify'
|
|
58
|
+
? 'verifying'
|
|
59
|
+
: `${usage.slice(0, -1)}tion`;
|
|
60
|
+
throw new TypeError(`${tag(key)} instances for asymmetric algorithm ${operation} must be of type "${expectedType}"`);
|
|
89
61
|
}
|
|
90
62
|
}
|
|
91
|
-
return isCryptoKey(key) ?
|
|
92
|
-
};
|
|
93
|
-
const BYTES = Symbol();
|
|
94
|
-
const CRYPTO = Symbol();
|
|
95
|
-
const KEYOBJECT = Symbol();
|
|
96
|
-
const JWK = Symbol();
|
|
97
|
-
export function checkKeyType(entry, key, usage) {
|
|
98
|
-
return entry.symmetric
|
|
99
|
-
? symmetricTypeCheck(entry, key, usage)
|
|
100
|
-
: asymmetricTypeCheck(entry, key, usage);
|
|
63
|
+
return isCryptoKey(key) ? [CRYPTO, key] : [KEYOBJECT, key];
|
|
101
64
|
}
|
|
65
|
+
const BYTES = 0;
|
|
66
|
+
const CRYPTO = 1;
|
|
67
|
+
const KEYOBJECT = 2;
|
|
68
|
+
const JWK = 3;
|
|
102
69
|
let cache;
|
|
103
70
|
const nist = {
|
|
104
71
|
__proto__: null,
|
|
@@ -106,58 +73,53 @@ const nist = {
|
|
|
106
73
|
secp384r1: 'P-384',
|
|
107
74
|
secp521r1: 'P-521',
|
|
108
75
|
};
|
|
109
|
-
function cached(key, alg) {
|
|
76
|
+
function cached(key, alg, value) {
|
|
110
77
|
cache ||= new WeakMap();
|
|
111
|
-
return cache.get(key)?.[alg];
|
|
112
|
-
}
|
|
113
|
-
function store(key, alg, cryptoKey) {
|
|
114
78
|
const entry = cache.get(key);
|
|
115
|
-
if (
|
|
116
|
-
entry
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
79
|
+
if (value) {
|
|
80
|
+
if (entry) {
|
|
81
|
+
entry[alg] = value;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
cache.set(key, { __proto__: null, [alg]: value });
|
|
85
|
+
}
|
|
120
86
|
}
|
|
121
|
-
return
|
|
87
|
+
return value ?? entry?.[alg];
|
|
122
88
|
}
|
|
123
|
-
const handleJWK = async (key, jwk, entry) =>
|
|
124
|
-
|
|
125
|
-
if (hit)
|
|
126
|
-
return hit;
|
|
127
|
-
const cryptoKey = await jwkToKey(entry, { ...jwk, alg: entry.alg });
|
|
128
|
-
return store(key, entry.alg, cryptoKey);
|
|
129
|
-
};
|
|
89
|
+
const handleJWK = async (key, jwk, entry) => cached(key, entry.alg) ??
|
|
90
|
+
cached(key, entry.alg, await jwkToKey(entry, { ...jwk, alg: entry.alg }));
|
|
130
91
|
const handleKeyObject = (keyObject, entry) => {
|
|
131
92
|
const hit = cached(keyObject, entry.alg);
|
|
132
93
|
if (hit)
|
|
133
94
|
return hit;
|
|
134
95
|
const isPublic = keyObject.type === 'public';
|
|
135
|
-
const usages = isPublic ?
|
|
96
|
+
const usages = entry.usages[isPublic ? 0 : 1];
|
|
136
97
|
const { asymmetricKeyType } = keyObject;
|
|
137
98
|
const crv = nist[keyObject.asymmetricKeyDetails?.namedCurve];
|
|
138
|
-
const params = entry.
|
|
139
|
-
return
|
|
99
|
+
const params = entry.resolve?.({ crv, asymmetricKeyType }) ?? entry.subtle;
|
|
100
|
+
return cached(keyObject, entry.alg, keyObject.toCryptoKey(params, isPublic, usages));
|
|
140
101
|
};
|
|
141
102
|
export async function prepareKey(entry, key, usage) {
|
|
142
103
|
const tagged = checkKeyType(entry, key, usage);
|
|
143
|
-
switch (tagged
|
|
104
|
+
switch (tagged[0]) {
|
|
144
105
|
case BYTES:
|
|
145
106
|
case CRYPTO:
|
|
146
|
-
return tagged
|
|
107
|
+
return tagged[1];
|
|
147
108
|
case JWK: {
|
|
148
|
-
|
|
149
|
-
|
|
109
|
+
const key = tagged[1];
|
|
110
|
+
if (key.k) {
|
|
111
|
+
return decode(key.k);
|
|
150
112
|
}
|
|
151
|
-
if (!Object.isFrozen(
|
|
152
|
-
const { key_ops } =
|
|
113
|
+
if (!Object.isFrozen(key)) {
|
|
114
|
+
const { key_ops } = key;
|
|
153
115
|
if (Array.isArray(key_ops))
|
|
154
116
|
Object.freeze(key_ops);
|
|
155
|
-
Object.freeze(
|
|
117
|
+
Object.freeze(key);
|
|
156
118
|
}
|
|
157
|
-
return handleJWK(
|
|
119
|
+
return handleJWK(key, key, entry);
|
|
158
120
|
}
|
|
159
121
|
case KEYOBJECT: {
|
|
160
|
-
const keyObject = tagged
|
|
122
|
+
const keyObject = tagged[1];
|
|
161
123
|
if (keyObject.type === 'secret') {
|
|
162
124
|
return keyObject.export();
|
|
163
125
|
}
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import { JOSENotSupported } from '../util/errors.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { JWS } from './jws_algorithms.js';
|
|
3
|
+
import { JWE } from './jwe_algorithms.js';
|
|
4
|
+
export const algArgument = '"alg" (Algorithm)';
|
|
5
|
+
export function unsupportedAlg(source = 'JWK "alg" (Algorithm) Parameter') {
|
|
6
|
+
throw new JOSENotSupported(`Invalid or unsupported ${source} value`);
|
|
6
7
|
}
|
|
7
|
-
export function keyAlgorithm(alg) {
|
|
8
|
-
|
|
9
|
-
throw unsupportedAlgorithm();
|
|
10
|
-
}
|
|
11
|
-
const entry = maybeJWSAlgorithm(alg) ?? maybeJWEAlgorithm(alg);
|
|
12
|
-
if (!entry) {
|
|
13
|
-
throw unsupportedAlgorithm();
|
|
14
|
-
}
|
|
15
|
-
return entry;
|
|
8
|
+
export function keyAlgorithm(alg, source) {
|
|
9
|
+
return (typeof alg === 'string' ? (JWS[alg] ?? JWE[alg]) : undefined) ?? unsupportedAlg(source);
|
|
16
10
|
}
|