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.
Files changed (36) hide show
  1. package/dist/webapi/jwe/compact/decrypt.js +2 -2
  2. package/dist/webapi/jwe/flattened/encrypt.js +12 -12
  3. package/dist/webapi/jwe/general/decrypt.js +8 -0
  4. package/dist/webapi/jwe/general/encrypt.js +51 -52
  5. package/dist/webapi/jwks/local.js +13 -34
  6. package/dist/webapi/jwks/remote.js +17 -26
  7. package/dist/webapi/jws/compact/verify.js +2 -2
  8. package/dist/webapi/jws/flattened/sign.js +1 -9
  9. package/dist/webapi/jws/general/sign.js +12 -21
  10. package/dist/webapi/jws/general/verify.js +13 -11
  11. package/dist/webapi/jwt/decrypt.js +3 -3
  12. package/dist/webapi/jwt/verify.js +4 -4
  13. package/dist/webapi/key/export.js +3 -3
  14. package/dist/webapi/key/generate_key_pair.js +10 -9
  15. package/dist/webapi/key/generate_secret.js +2 -2
  16. package/dist/webapi/lib/asn1.js +32 -65
  17. package/dist/webapi/lib/content_encryption.js +16 -34
  18. package/dist/webapi/lib/crypto_key.js +6 -0
  19. package/dist/webapi/lib/invalid_key_input.js +0 -1
  20. package/dist/webapi/lib/jwe_algorithms.js +48 -84
  21. package/dist/webapi/lib/jwe_decrypt.js +46 -41
  22. package/dist/webapi/lib/jwe_encrypt.js +14 -13
  23. package/dist/webapi/lib/jwk_to_key.js +5 -10
  24. package/dist/webapi/lib/jws_algorithms.js +22 -23
  25. package/dist/webapi/lib/jws_sign.js +12 -6
  26. package/dist/webapi/lib/jws_verify.js +26 -27
  27. package/dist/webapi/lib/jwt_claims_set.js +60 -102
  28. package/dist/webapi/lib/key.js +61 -99
  29. package/dist/webapi/lib/key_algorithm.js +7 -13
  30. package/dist/webapi/lib/key_descriptor.js +1 -1
  31. package/dist/webapi/lib/key_management.js +74 -161
  32. package/dist/webapi/lib/options.js +11 -14
  33. package/dist/webapi/lib/signing.js +6 -16
  34. package/dist/webapi/lib/type_checks.js +13 -18
  35. package/dist/webapi/util/base64url.js +4 -3
  36. 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 minute = 60;
6
- const hour = minute * 60;
7
- const day = hour * 24;
8
- const week = day * 7;
9
- const year = day * 365.25;
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 unit = matched[3].toLowerCase();
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(Set.prototype.has.bind(new Set(audPayload)));
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', 'check_failed');
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 in payload)) {
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
- throw new JWTClaimValidationFailed('unexpected "iss" claim value', payload, 'iss', 'check_failed');
100
+ unexpectedClaim(payload, 'iss');
115
101
  }
116
102
  if (subject !== undefined && payload.sub !== subject) {
117
- throw new JWTClaimValidationFailed('unexpected "sub" claim value', payload, 'sub', 'check_failed');
103
+ unexpectedClaim(payload, 'sub');
118
104
  }
119
105
  if (audience !== undefined &&
120
106
  !checkAudiencePresence(payload.aud, typeof audience === 'string' ? [audience] : audience)) {
121
- throw new JWTClaimValidationFailed('unexpected "aud" claim value', payload, 'aud', 'check_failed');
122
- }
123
- let tolerance;
124
- switch (typeof options.clockTolerance) {
125
- case 'string':
126
- tolerance = secs(options.clockTolerance);
127
- break;
128
- case 'number':
129
- tolerance = options.clockTolerance;
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
- if ((payload.iat !== undefined || maxTokenAge !== undefined) && typeof payload.iat !== 'number') {
141
- throw new JWTClaimValidationFailed('"iat" claim must be a number', payload, 'iat', 'invalid');
142
- }
143
- if (payload.nbf !== undefined) {
144
- if (typeof payload.nbf !== 'number') {
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
- if (payload.exp !== undefined) {
152
- if (typeof payload.exp !== 'number') {
153
- throw new JWTClaimValidationFailed('"exp" claim must be a number', payload, 'exp', 'invalid');
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 - payload.iat;
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', 'check_failed');
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', 'check_failed');
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
- if (typeof value === 'number') {
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
- if (typeof value === 'number') {
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 = validateInput('setIssuedAt', value);
194
+ this.#payload.iat = numericDate(value, 'setIssuedAt');
237
195
  }
238
196
  }
239
197
  }
@@ -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
- let expected;
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.keyOps?.[usage] : usage;
30
- if (expectedKeyOp && key.key_ops?.includes?.(expectedKeyOp) === false) {
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
- const asymmetricTypeCheck = (entry, key, usage) => {
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
- switch (usage) {
57
- case 'decrypt':
58
- case 'sign':
59
- if (jwk.isPrivateJWK(key) && jwkMatchesOp(entry, key, usage))
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(invalidKeyInput(alg, key, 'CryptoKey', 'KeyObject', 'JSON Web Key'));
71
- }
72
- if (key.type === 'secret') {
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 (key.type === 'public') {
76
- switch (usage) {
77
- case 'sign':
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
- if (key.type === 'private') {
84
- switch (usage) {
85
- case 'verify':
86
- throw new TypeError(`${tag(key)} instances for asymmetric algorithm verifying must be of type "public"`);
87
- case 'encrypt':
88
- throw new TypeError(`${tag(key)} instances for asymmetric algorithm encryption must be of type "public"`);
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) ? { kind: CRYPTO, key } : { kind: KEYOBJECT, 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 (entry) {
116
- entry[alg] = cryptoKey;
117
- }
118
- else {
119
- cache.set(key, { [alg]: cryptoKey });
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 cryptoKey;
87
+ return value ?? entry?.[alg];
122
88
  }
123
- const handleJWK = async (key, jwk, entry) => {
124
- const hit = cached(key, entry.alg);
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 ? entry.usages.public : entry.usages.private;
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.subtleFor?.({ crv, asymmetricKeyType }) ?? entry.subtle;
139
- return store(keyObject, entry.alg, keyObject.toCryptoKey(params, isPublic, usages));
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.kind) {
104
+ switch (tagged[0]) {
144
105
  case BYTES:
145
106
  case CRYPTO:
146
- return tagged.key;
107
+ return tagged[1];
147
108
  case JWK: {
148
- if (tagged.key.k) {
149
- return decode(tagged.key.k);
109
+ const key = tagged[1];
110
+ if (key.k) {
111
+ return decode(key.k);
150
112
  }
151
- if (!Object.isFrozen(tagged.key)) {
152
- const { key_ops } = tagged.key;
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(tagged.key);
117
+ Object.freeze(key);
156
118
  }
157
- return handleJWK(tagged.key, tagged.key, entry);
119
+ return handleJWK(key, key, entry);
158
120
  }
159
121
  case KEYOBJECT: {
160
- const keyObject = tagged.key;
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 { maybeJWSAlgorithm } from './jws_algorithms.js';
3
- import { maybeJWEAlgorithm } from './jwe_algorithms.js';
4
- function unsupportedAlgorithm() {
5
- return new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
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
- if (typeof alg !== 'string') {
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
  }
@@ -1,6 +1,6 @@
1
1
  export function table(entries) {
2
2
  const out = { __proto__: null };
3
- for (const alg of Object.keys(entries)) {
3
+ for (const alg in entries) {
4
4
  out[alg] = { ...entries[alg], alg };
5
5
  }
6
6
  return out;