jose 5.8.0 → 5.9.0
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 +21 -17
- package/dist/browser/jwks/remote.js +1 -1
- package/dist/browser/jws/flattened/sign.js +2 -2
- package/dist/browser/jws/flattened/verify.js +10 -2
- package/dist/browser/lib/check_key_type.js +40 -9
- package/dist/browser/lib/invalid_key_input.js +1 -0
- package/dist/browser/lib/is_jwk.js +13 -0
- package/dist/browser/runtime/get_sign_verify_key.js +1 -1
- package/dist/browser/runtime/normalize_key.js +21 -4
- package/dist/node/cjs/jwks/remote.js +1 -1
- package/dist/node/cjs/jws/flattened/sign.js +1 -1
- package/dist/node/cjs/jws/flattened/verify.js +9 -1
- package/dist/node/cjs/lib/check_key_type.js +41 -9
- package/dist/node/cjs/lib/invalid_key_input.js +1 -0
- package/dist/node/cjs/lib/is_jwk.js +20 -0
- package/dist/node/cjs/runtime/base64url.js +1 -1
- package/dist/node/cjs/runtime/check_key_length.js +11 -1
- package/dist/node/cjs/runtime/get_named_curve.js +4 -0
- package/dist/node/cjs/runtime/get_sign_verify_key.js +8 -1
- package/dist/node/cjs/runtime/node_key.js +51 -14
- package/dist/node/cjs/runtime/sign.js +3 -3
- package/dist/node/cjs/runtime/verify.js +3 -3
- package/dist/node/esm/jwks/remote.js +1 -1
- package/dist/node/esm/jws/flattened/sign.js +2 -2
- package/dist/node/esm/jws/flattened/verify.js +10 -2
- package/dist/node/esm/lib/check_key_type.js +40 -9
- package/dist/node/esm/lib/invalid_key_input.js +1 -0
- package/dist/node/esm/lib/is_jwk.js +13 -0
- package/dist/node/esm/runtime/base64url.js +1 -1
- package/dist/node/esm/runtime/check_key_length.js +11 -1
- package/dist/node/esm/runtime/get_named_curve.js +4 -0
- package/dist/node/esm/runtime/get_sign_verify_key.js +8 -1
- package/dist/node/esm/runtime/node_key.js +52 -15
- package/dist/node/esm/runtime/sign.js +3 -3
- package/dist/node/esm/runtime/verify.js +3 -3
- package/dist/types/jws/compact/sign.d.ts +2 -2
- package/dist/types/jws/compact/verify.d.ts +3 -3
- package/dist/types/jws/flattened/sign.d.ts +2 -2
- package/dist/types/jws/flattened/verify.d.ts +3 -3
- package/dist/types/jws/general/sign.d.ts +2 -2
- package/dist/types/jws/general/verify.d.ts +3 -3
- package/dist/types/jwt/sign.d.ts +2 -2
- package/dist/types/jwt/verify.d.ts +3 -3
- package/dist/types/types.d.ts +129 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -92,23 +92,6 @@ The following are additional features and utilities provided by the `jose` modul
|
|
|
92
92
|
- [Unsecured JWT](docs/classes/jwt_unsecured.UnsecuredJWT.md)
|
|
93
93
|
- [JOSE Errors](docs/modules/util_errors.md)
|
|
94
94
|
|
|
95
|
-
## Implemented specifications
|
|
96
|
-
|
|
97
|
-
The `jose` module implements the following specifications:
|
|
98
|
-
|
|
99
|
-
- JSON Web Signature (JWS) - [RFC7515](https://www.rfc-editor.org/rfc/rfc7515)
|
|
100
|
-
- JSON Web Encryption (JWE) - [RFC7516](https://www.rfc-editor.org/rfc/rfc7516)
|
|
101
|
-
- JSON Web Key (JWK) - [RFC7517](https://www.rfc-editor.org/rfc/rfc7517)
|
|
102
|
-
- JSON Web Algorithms (JWA) - [RFC7518](https://www.rfc-editor.org/rfc/rfc7518)
|
|
103
|
-
- JSON Web Token (JWT) - [RFC7519](https://www.rfc-editor.org/rfc/rfc7519)
|
|
104
|
-
- JSON Web Key Thumbprint - [RFC7638](https://www.rfc-editor.org/rfc/rfc7638)
|
|
105
|
-
- JSON Web Key Thumbprint URI - [RFC9278](https://www.rfc-editor.org/rfc/rfc9278)
|
|
106
|
-
- JWS Unencoded Payload Option - [RFC7797](https://www.rfc-editor.org/rfc/rfc7797)
|
|
107
|
-
- CFRG Elliptic Curve ECDH and Signatures - [RFC8037](https://www.rfc-editor.org/rfc/rfc8037)
|
|
108
|
-
- secp256k1 EC Key curve support - [RFC8812](https://www.rfc-editor.org/rfc/rfc8812)
|
|
109
|
-
|
|
110
|
-
The algorithm implementations have been tested using test vectors from their respective specifications as well as [RFC7520](https://www.rfc-editor.org/rfc/rfc7520).
|
|
111
|
-
|
|
112
95
|
## Supported Runtimes
|
|
113
96
|
|
|
114
97
|
The `jose` module is compatible with JavaScript runtimes that support the utilized Web API globals and standard built-in objects or are Node.js.
|
|
@@ -131,3 +114,24 @@ Please note that certain algorithms may not be available depending on the runtim
|
|
|
131
114
|
| [v5.x](https://github.com/panva/jose/tree/v5.x) | ✅ | ✅ | ✅ |
|
|
132
115
|
| [v4.x](https://github.com/panva/jose/tree/v4.x) | ✅ | ❌ | ❌ |
|
|
133
116
|
| [v2.x](https://github.com/panva/jose/tree/v2.x) | ✅ | ❌ | ❌ |
|
|
117
|
+
|
|
118
|
+
## Specifications
|
|
119
|
+
|
|
120
|
+
<details>
|
|
121
|
+
<summary>Details</summary>
|
|
122
|
+
|
|
123
|
+
- JSON Web Signature (JWS) - [RFC7515](https://www.rfc-editor.org/rfc/rfc7515)
|
|
124
|
+
- JSON Web Encryption (JWE) - [RFC7516](https://www.rfc-editor.org/rfc/rfc7516)
|
|
125
|
+
- JSON Web Key (JWK) - [RFC7517](https://www.rfc-editor.org/rfc/rfc7517)
|
|
126
|
+
- JSON Web Algorithms (JWA) - [RFC7518](https://www.rfc-editor.org/rfc/rfc7518)
|
|
127
|
+
- JSON Web Token (JWT) - [RFC7519](https://www.rfc-editor.org/rfc/rfc7519)
|
|
128
|
+
- JSON Web Key Thumbprint - [RFC7638](https://www.rfc-editor.org/rfc/rfc7638)
|
|
129
|
+
- JSON Web Key Thumbprint URI - [RFC9278](https://www.rfc-editor.org/rfc/rfc9278)
|
|
130
|
+
- JWS Unencoded Payload Option - [RFC7797](https://www.rfc-editor.org/rfc/rfc7797)
|
|
131
|
+
- CFRG Elliptic Curve ECDH and Signatures - [RFC8037](https://www.rfc-editor.org/rfc/rfc8037)
|
|
132
|
+
- secp256k1 EC Key curve support - [RFC8812](https://www.rfc-editor.org/rfc/rfc8812)
|
|
133
|
+
|
|
134
|
+
The algorithm implementations in `jose` have been tested using test vectors from their respective specifications as well as [RFC7520](https://www.rfc-editor.org/rfc/rfc7520).
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
</details>
|
|
@@ -10,7 +10,7 @@ function isCloudflareWorkers() {
|
|
|
10
10
|
let USER_AGENT;
|
|
11
11
|
if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
|
|
12
12
|
const NAME = 'jose';
|
|
13
|
-
const VERSION = 'v5.
|
|
13
|
+
const VERSION = 'v5.9.0';
|
|
14
14
|
USER_AGENT = `${NAME}/${VERSION}`;
|
|
15
15
|
}
|
|
16
16
|
export const jwksCache = Symbol();
|
|
@@ -3,7 +3,7 @@ import sign from '../../runtime/sign.js';
|
|
|
3
3
|
import isDisjoint from '../../lib/is_disjoint.js';
|
|
4
4
|
import { JWSInvalid } from '../../util/errors.js';
|
|
5
5
|
import { encoder, decoder, concat } from '../../lib/buffer_utils.js';
|
|
6
|
-
import
|
|
6
|
+
import { checkKeyTypeWithJwk } from '../../lib/check_key_type.js';
|
|
7
7
|
import validateCrit from '../../lib/validate_crit.js';
|
|
8
8
|
export class FlattenedSign {
|
|
9
9
|
constructor(payload) {
|
|
@@ -49,7 +49,7 @@ export class FlattenedSign {
|
|
|
49
49
|
if (typeof alg !== 'string' || !alg) {
|
|
50
50
|
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
checkKeyTypeWithJwk(alg, key, 'sign');
|
|
53
53
|
let payload = this._payload;
|
|
54
54
|
if (b64) {
|
|
55
55
|
payload = encoder.encode(base64url(payload));
|
|
@@ -4,9 +4,11 @@ import { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '.
|
|
|
4
4
|
import { concat, encoder, decoder } from '../../lib/buffer_utils.js';
|
|
5
5
|
import isDisjoint from '../../lib/is_disjoint.js';
|
|
6
6
|
import isObject from '../../lib/is_object.js';
|
|
7
|
-
import
|
|
7
|
+
import { checkKeyTypeWithJwk } from '../../lib/check_key_type.js';
|
|
8
8
|
import validateCrit from '../../lib/validate_crit.js';
|
|
9
9
|
import validateAlgorithms from '../../lib/validate_algorithms.js';
|
|
10
|
+
import { isJWK } from '../../lib/is_jwk.js';
|
|
11
|
+
import { importJWK } from '../../key/import.js';
|
|
10
12
|
export async function flattenedVerify(jws, key, options) {
|
|
11
13
|
if (!isObject(jws)) {
|
|
12
14
|
throw new JWSInvalid('Flattened JWS must be an object');
|
|
@@ -71,8 +73,14 @@ export async function flattenedVerify(jws, key, options) {
|
|
|
71
73
|
if (typeof key === 'function') {
|
|
72
74
|
key = await key(parsedProt, jws);
|
|
73
75
|
resolvedKey = true;
|
|
76
|
+
checkKeyTypeWithJwk(alg, key, 'verify');
|
|
77
|
+
if (isJWK(key)) {
|
|
78
|
+
key = await importJWK(key, alg);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
checkKeyTypeWithJwk(alg, key, 'verify');
|
|
74
83
|
}
|
|
75
|
-
checkKeyType(alg, key, 'verify');
|
|
76
84
|
const data = concat(encoder.encode(jws.protected ?? ''), encoder.encode('.'), typeof jws.payload === 'string' ? encoder.encode(jws.payload) : jws.payload);
|
|
77
85
|
let signature;
|
|
78
86
|
try {
|
|
@@ -1,19 +1,49 @@
|
|
|
1
1
|
import { withAlg as invalidKeyInput } from './invalid_key_input.js';
|
|
2
2
|
import isKeyLike, { types } from '../runtime/is_key_like.js';
|
|
3
|
+
import * as jwk from './is_jwk.js';
|
|
3
4
|
const tag = (key) => key?.[Symbol.toStringTag];
|
|
4
|
-
const
|
|
5
|
+
const jwkMatchesOp = (alg, key, usage) => {
|
|
6
|
+
if (key.use !== undefined && key.use !== 'sig') {
|
|
7
|
+
throw new TypeError('Invalid key for this operation, when present its use must be sig');
|
|
8
|
+
}
|
|
9
|
+
if (key.key_ops !== undefined && key.key_ops.includes?.(usage) !== true) {
|
|
10
|
+
throw new TypeError(`Invalid key for this operation, when present its key_ops must include ${usage}`);
|
|
11
|
+
}
|
|
12
|
+
if (key.alg !== undefined && key.alg !== alg) {
|
|
13
|
+
throw new TypeError(`Invalid key for this operation, when present its alg must be ${alg}`);
|
|
14
|
+
}
|
|
15
|
+
return true;
|
|
16
|
+
};
|
|
17
|
+
const symmetricTypeCheck = (alg, key, usage, allowJwk) => {
|
|
5
18
|
if (key instanceof Uint8Array)
|
|
6
19
|
return;
|
|
20
|
+
if (allowJwk && jwk.isJWK(key)) {
|
|
21
|
+
if (jwk.isSecretJWK(key) && jwkMatchesOp(alg, key, usage))
|
|
22
|
+
return;
|
|
23
|
+
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`);
|
|
24
|
+
}
|
|
7
25
|
if (!isKeyLike(key)) {
|
|
8
|
-
throw new TypeError(invalidKeyInput(alg, key, ...types, 'Uint8Array'));
|
|
26
|
+
throw new TypeError(invalidKeyInput(alg, key, ...types, 'Uint8Array', allowJwk ? 'JSON Web Key' : null));
|
|
9
27
|
}
|
|
10
28
|
if (key.type !== 'secret') {
|
|
11
29
|
throw new TypeError(`${tag(key)} instances for symmetric algorithms must be of type "secret"`);
|
|
12
30
|
}
|
|
13
31
|
};
|
|
14
|
-
const asymmetricTypeCheck = (alg, key, usage) => {
|
|
32
|
+
const asymmetricTypeCheck = (alg, key, usage, allowJwk) => {
|
|
33
|
+
if (allowJwk && jwk.isJWK(key)) {
|
|
34
|
+
switch (usage) {
|
|
35
|
+
case 'sign':
|
|
36
|
+
if (jwk.isPrivateJWK(key) && jwkMatchesOp(alg, key, usage))
|
|
37
|
+
return;
|
|
38
|
+
throw new TypeError(`JSON Web Key for this operation be a private JWK`);
|
|
39
|
+
case 'verify':
|
|
40
|
+
if (jwk.isPublicJWK(key) && jwkMatchesOp(alg, key, usage))
|
|
41
|
+
return;
|
|
42
|
+
throw new TypeError(`JSON Web Key for this operation be a public JWK`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
15
45
|
if (!isKeyLike(key)) {
|
|
16
|
-
throw new TypeError(invalidKeyInput(alg, key, ...types));
|
|
46
|
+
throw new TypeError(invalidKeyInput(alg, key, ...types, allowJwk ? 'JSON Web Key' : null));
|
|
17
47
|
}
|
|
18
48
|
if (key.type === 'secret') {
|
|
19
49
|
throw new TypeError(`${tag(key)} instances for asymmetric algorithms must not be of type "secret"`);
|
|
@@ -31,16 +61,17 @@ const asymmetricTypeCheck = (alg, key, usage) => {
|
|
|
31
61
|
throw new TypeError(`${tag(key)} instances for asymmetric algorithm encryption must be of type "public"`);
|
|
32
62
|
}
|
|
33
63
|
};
|
|
34
|
-
|
|
64
|
+
function checkKeyType(allowJwk, alg, key, usage) {
|
|
35
65
|
const symmetric = alg.startsWith('HS') ||
|
|
36
66
|
alg === 'dir' ||
|
|
37
67
|
alg.startsWith('PBES2') ||
|
|
38
68
|
/^A\d{3}(?:GCM)?KW$/.test(alg);
|
|
39
69
|
if (symmetric) {
|
|
40
|
-
symmetricTypeCheck(alg, key);
|
|
70
|
+
symmetricTypeCheck(alg, key, usage, allowJwk);
|
|
41
71
|
}
|
|
42
72
|
else {
|
|
43
|
-
asymmetricTypeCheck(alg, key, usage);
|
|
73
|
+
asymmetricTypeCheck(alg, key, usage, allowJwk);
|
|
44
74
|
}
|
|
45
|
-
}
|
|
46
|
-
export default checkKeyType;
|
|
75
|
+
}
|
|
76
|
+
export default checkKeyType.bind(undefined, false);
|
|
77
|
+
export const checkKeyTypeWithJwk = checkKeyType.bind(undefined, true);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import isObject from './is_object.js';
|
|
2
|
+
export function isJWK(key) {
|
|
3
|
+
return isObject(key) && typeof key.kty === 'string';
|
|
4
|
+
}
|
|
5
|
+
export function isPrivateJWK(key) {
|
|
6
|
+
return key.kty !== 'oct' && typeof key.d === 'string';
|
|
7
|
+
}
|
|
8
|
+
export function isPublicJWK(key) {
|
|
9
|
+
return key.kty !== 'oct' && typeof key.d === 'undefined';
|
|
10
|
+
}
|
|
11
|
+
export function isSecretJWK(key) {
|
|
12
|
+
return isJWK(key) && key.kty === 'oct' && typeof key.k === 'string';
|
|
13
|
+
}
|
|
@@ -20,5 +20,5 @@ export default async function getCryptoKey(alg, key, usage) {
|
|
|
20
20
|
}
|
|
21
21
|
return crypto.subtle.importKey('raw', key, { hash: `SHA-${alg.slice(-3)}`, name: 'HMAC' }, false, [usage]);
|
|
22
22
|
}
|
|
23
|
-
throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array'));
|
|
23
|
+
throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array', 'JSON Web Key'));
|
|
24
24
|
}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
+
import { isJWK } from '../lib/is_jwk.js';
|
|
1
2
|
import { decode } from './base64url.js';
|
|
2
3
|
import importJWK from './jwk_to_key.js';
|
|
3
|
-
const
|
|
4
|
+
const exportKeyValue = (k) => decode(k);
|
|
4
5
|
let privCache;
|
|
5
6
|
let pubCache;
|
|
6
7
|
const isKeyObject = (key) => {
|
|
7
8
|
return key?.[Symbol.toStringTag] === 'KeyObject';
|
|
8
9
|
};
|
|
9
|
-
const importAndCache = async (cache, key, jwk, alg) => {
|
|
10
|
+
const importAndCache = async (cache, key, jwk, alg, freeze = false) => {
|
|
10
11
|
let cached = cache.get(key);
|
|
11
12
|
if (cached?.[alg]) {
|
|
12
13
|
return cached[alg];
|
|
13
14
|
}
|
|
14
15
|
const cryptoKey = await importJWK({ ...jwk, alg });
|
|
16
|
+
if (freeze)
|
|
17
|
+
Object.freeze(key);
|
|
15
18
|
if (!cached) {
|
|
16
19
|
cache.set(key, { [alg]: cryptoKey });
|
|
17
20
|
}
|
|
@@ -30,22 +33,36 @@ const normalizePublicKey = (key, alg) => {
|
|
|
30
33
|
delete jwk.q;
|
|
31
34
|
delete jwk.qi;
|
|
32
35
|
if (jwk.k) {
|
|
33
|
-
return
|
|
36
|
+
return exportKeyValue(jwk.k);
|
|
34
37
|
}
|
|
35
38
|
pubCache || (pubCache = new WeakMap());
|
|
36
39
|
return importAndCache(pubCache, key, jwk, alg);
|
|
37
40
|
}
|
|
41
|
+
if (isJWK(key)) {
|
|
42
|
+
if (key.k)
|
|
43
|
+
return decode(key.k);
|
|
44
|
+
pubCache || (pubCache = new WeakMap());
|
|
45
|
+
const cryptoKey = importAndCache(pubCache, key, key, alg, true);
|
|
46
|
+
return cryptoKey;
|
|
47
|
+
}
|
|
38
48
|
return key;
|
|
39
49
|
};
|
|
40
50
|
const normalizePrivateKey = (key, alg) => {
|
|
41
51
|
if (isKeyObject(key)) {
|
|
42
52
|
let jwk = key.export({ format: 'jwk' });
|
|
43
53
|
if (jwk.k) {
|
|
44
|
-
return
|
|
54
|
+
return exportKeyValue(jwk.k);
|
|
45
55
|
}
|
|
46
56
|
privCache || (privCache = new WeakMap());
|
|
47
57
|
return importAndCache(privCache, key, jwk, alg);
|
|
48
58
|
}
|
|
59
|
+
if (isJWK(key)) {
|
|
60
|
+
if (key.k)
|
|
61
|
+
return decode(key.k);
|
|
62
|
+
privCache || (privCache = new WeakMap());
|
|
63
|
+
const cryptoKey = importAndCache(privCache, key, key, alg, true);
|
|
64
|
+
return cryptoKey;
|
|
65
|
+
}
|
|
49
66
|
return key;
|
|
50
67
|
};
|
|
51
68
|
export default { normalizePublicKey, normalizePrivateKey };
|
|
@@ -13,7 +13,7 @@ function isCloudflareWorkers() {
|
|
|
13
13
|
let USER_AGENT;
|
|
14
14
|
if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
|
|
15
15
|
const NAME = 'jose';
|
|
16
|
-
const VERSION = 'v5.
|
|
16
|
+
const VERSION = 'v5.9.0';
|
|
17
17
|
USER_AGENT = `${NAME}/${VERSION}`;
|
|
18
18
|
}
|
|
19
19
|
exports.jwksCache = Symbol();
|
|
@@ -55,7 +55,7 @@ class FlattenedSign {
|
|
|
55
55
|
if (typeof alg !== 'string' || !alg) {
|
|
56
56
|
throw new errors_js_1.JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
57
57
|
}
|
|
58
|
-
(0, check_key_type_js_1.
|
|
58
|
+
(0, check_key_type_js_1.checkKeyTypeWithJwk)(alg, key, 'sign');
|
|
59
59
|
let payload = this._payload;
|
|
60
60
|
if (b64) {
|
|
61
61
|
payload = buffer_utils_js_1.encoder.encode((0, base64url_js_1.encode)(payload));
|
|
@@ -10,6 +10,8 @@ const is_object_js_1 = require("../../lib/is_object.js");
|
|
|
10
10
|
const check_key_type_js_1 = require("../../lib/check_key_type.js");
|
|
11
11
|
const validate_crit_js_1 = require("../../lib/validate_crit.js");
|
|
12
12
|
const validate_algorithms_js_1 = require("../../lib/validate_algorithms.js");
|
|
13
|
+
const is_jwk_js_1 = require("../../lib/is_jwk.js");
|
|
14
|
+
const import_js_1 = require("../../key/import.js");
|
|
13
15
|
async function flattenedVerify(jws, key, options) {
|
|
14
16
|
if (!(0, is_object_js_1.default)(jws)) {
|
|
15
17
|
throw new errors_js_1.JWSInvalid('Flattened JWS must be an object');
|
|
@@ -74,8 +76,14 @@ async function flattenedVerify(jws, key, options) {
|
|
|
74
76
|
if (typeof key === 'function') {
|
|
75
77
|
key = await key(parsedProt, jws);
|
|
76
78
|
resolvedKey = true;
|
|
79
|
+
(0, check_key_type_js_1.checkKeyTypeWithJwk)(alg, key, 'verify');
|
|
80
|
+
if ((0, is_jwk_js_1.isJWK)(key)) {
|
|
81
|
+
key = await (0, import_js_1.importJWK)(key, alg);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
(0, check_key_type_js_1.checkKeyTypeWithJwk)(alg, key, 'verify');
|
|
77
86
|
}
|
|
78
|
-
(0, check_key_type_js_1.default)(alg, key, 'verify');
|
|
79
87
|
const data = (0, buffer_utils_js_1.concat)(buffer_utils_js_1.encoder.encode(jws.protected ?? ''), buffer_utils_js_1.encoder.encode('.'), typeof jws.payload === 'string' ? buffer_utils_js_1.encoder.encode(jws.payload) : jws.payload);
|
|
80
88
|
let signature;
|
|
81
89
|
try {
|
|
@@ -1,21 +1,52 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkKeyTypeWithJwk = void 0;
|
|
3
4
|
const invalid_key_input_js_1 = require("./invalid_key_input.js");
|
|
4
5
|
const is_key_like_js_1 = require("../runtime/is_key_like.js");
|
|
6
|
+
const jwk = require("./is_jwk.js");
|
|
5
7
|
const tag = (key) => key?.[Symbol.toStringTag];
|
|
6
|
-
const
|
|
8
|
+
const jwkMatchesOp = (alg, key, usage) => {
|
|
9
|
+
if (key.use !== undefined && key.use !== 'sig') {
|
|
10
|
+
throw new TypeError('Invalid key for this operation, when present its use must be sig');
|
|
11
|
+
}
|
|
12
|
+
if (key.key_ops !== undefined && key.key_ops.includes?.(usage) !== true) {
|
|
13
|
+
throw new TypeError(`Invalid key for this operation, when present its key_ops must include ${usage}`);
|
|
14
|
+
}
|
|
15
|
+
if (key.alg !== undefined && key.alg !== alg) {
|
|
16
|
+
throw new TypeError(`Invalid key for this operation, when present its alg must be ${alg}`);
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
};
|
|
20
|
+
const symmetricTypeCheck = (alg, key, usage, allowJwk) => {
|
|
7
21
|
if (key instanceof Uint8Array)
|
|
8
22
|
return;
|
|
23
|
+
if (allowJwk && jwk.isJWK(key)) {
|
|
24
|
+
if (jwk.isSecretJWK(key) && jwkMatchesOp(alg, key, usage))
|
|
25
|
+
return;
|
|
26
|
+
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`);
|
|
27
|
+
}
|
|
9
28
|
if (!(0, is_key_like_js_1.default)(key)) {
|
|
10
|
-
throw new TypeError((0, invalid_key_input_js_1.withAlg)(alg, key, ...is_key_like_js_1.types, 'Uint8Array'));
|
|
29
|
+
throw new TypeError((0, invalid_key_input_js_1.withAlg)(alg, key, ...is_key_like_js_1.types, 'Uint8Array', allowJwk ? 'JSON Web Key' : null));
|
|
11
30
|
}
|
|
12
31
|
if (key.type !== 'secret') {
|
|
13
32
|
throw new TypeError(`${tag(key)} instances for symmetric algorithms must be of type "secret"`);
|
|
14
33
|
}
|
|
15
34
|
};
|
|
16
|
-
const asymmetricTypeCheck = (alg, key, usage) => {
|
|
35
|
+
const asymmetricTypeCheck = (alg, key, usage, allowJwk) => {
|
|
36
|
+
if (allowJwk && jwk.isJWK(key)) {
|
|
37
|
+
switch (usage) {
|
|
38
|
+
case 'sign':
|
|
39
|
+
if (jwk.isPrivateJWK(key) && jwkMatchesOp(alg, key, usage))
|
|
40
|
+
return;
|
|
41
|
+
throw new TypeError(`JSON Web Key for this operation be a private JWK`);
|
|
42
|
+
case 'verify':
|
|
43
|
+
if (jwk.isPublicJWK(key) && jwkMatchesOp(alg, key, usage))
|
|
44
|
+
return;
|
|
45
|
+
throw new TypeError(`JSON Web Key for this operation be a public JWK`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
17
48
|
if (!(0, is_key_like_js_1.default)(key)) {
|
|
18
|
-
throw new TypeError((0, invalid_key_input_js_1.withAlg)(alg, key, ...is_key_like_js_1.types));
|
|
49
|
+
throw new TypeError((0, invalid_key_input_js_1.withAlg)(alg, key, ...is_key_like_js_1.types, allowJwk ? 'JSON Web Key' : null));
|
|
19
50
|
}
|
|
20
51
|
if (key.type === 'secret') {
|
|
21
52
|
throw new TypeError(`${tag(key)} instances for asymmetric algorithms must not be of type "secret"`);
|
|
@@ -33,16 +64,17 @@ const asymmetricTypeCheck = (alg, key, usage) => {
|
|
|
33
64
|
throw new TypeError(`${tag(key)} instances for asymmetric algorithm encryption must be of type "public"`);
|
|
34
65
|
}
|
|
35
66
|
};
|
|
36
|
-
|
|
67
|
+
function checkKeyType(allowJwk, alg, key, usage) {
|
|
37
68
|
const symmetric = alg.startsWith('HS') ||
|
|
38
69
|
alg === 'dir' ||
|
|
39
70
|
alg.startsWith('PBES2') ||
|
|
40
71
|
/^A\d{3}(?:GCM)?KW$/.test(alg);
|
|
41
72
|
if (symmetric) {
|
|
42
|
-
symmetricTypeCheck(alg, key);
|
|
73
|
+
symmetricTypeCheck(alg, key, usage, allowJwk);
|
|
43
74
|
}
|
|
44
75
|
else {
|
|
45
|
-
asymmetricTypeCheck(alg, key, usage);
|
|
76
|
+
asymmetricTypeCheck(alg, key, usage, allowJwk);
|
|
46
77
|
}
|
|
47
|
-
}
|
|
48
|
-
exports.default = checkKeyType;
|
|
78
|
+
}
|
|
79
|
+
exports.default = checkKeyType.bind(undefined, false);
|
|
80
|
+
exports.checkKeyTypeWithJwk = checkKeyType.bind(undefined, true);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.withAlg = void 0;
|
|
4
4
|
function message(msg, actual, ...types) {
|
|
5
|
+
types = types.filter(Boolean);
|
|
5
6
|
if (types.length > 2) {
|
|
6
7
|
const last = types.pop();
|
|
7
8
|
msg += `one of type ${types.join(', ')}, or ${last}.`;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isSecretJWK = exports.isPublicJWK = exports.isPrivateJWK = exports.isJWK = void 0;
|
|
4
|
+
const is_object_js_1 = require("./is_object.js");
|
|
5
|
+
function isJWK(key) {
|
|
6
|
+
return (0, is_object_js_1.default)(key) && typeof key.kty === 'string';
|
|
7
|
+
}
|
|
8
|
+
exports.isJWK = isJWK;
|
|
9
|
+
function isPrivateJWK(key) {
|
|
10
|
+
return key.kty !== 'oct' && typeof key.d === 'string';
|
|
11
|
+
}
|
|
12
|
+
exports.isPrivateJWK = isPrivateJWK;
|
|
13
|
+
function isPublicJWK(key) {
|
|
14
|
+
return key.kty !== 'oct' && typeof key.d === 'undefined';
|
|
15
|
+
}
|
|
16
|
+
exports.isPublicJWK = isPublicJWK;
|
|
17
|
+
function isSecretJWK(key) {
|
|
18
|
+
return isJWK(key) && key.kty === 'oct' && typeof key.k === 'string';
|
|
19
|
+
}
|
|
20
|
+
exports.isSecretJWK = isSecretJWK;
|
|
@@ -16,5 +16,5 @@ const decodeBase64 = (input) => new Uint8Array(node_buffer_1.Buffer.from(input,
|
|
|
16
16
|
exports.decodeBase64 = decodeBase64;
|
|
17
17
|
const encodeBase64 = (input) => node_buffer_1.Buffer.from(input).toString('base64');
|
|
18
18
|
exports.encodeBase64 = encodeBase64;
|
|
19
|
-
const decode = (input) => new Uint8Array(node_buffer_1.Buffer.from(normalize(input), '
|
|
19
|
+
const decode = (input) => new Uint8Array(node_buffer_1.Buffer.from(normalize(input), 'base64url'));
|
|
20
20
|
exports.decode = decode;
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const node_crypto_1 = require("node:crypto");
|
|
3
4
|
exports.default = (key, alg) => {
|
|
4
|
-
|
|
5
|
+
let modulusLength;
|
|
6
|
+
try {
|
|
7
|
+
if (key instanceof node_crypto_1.KeyObject) {
|
|
8
|
+
modulusLength = key.asymmetricKeyDetails?.modulusLength;
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
modulusLength = Buffer.from(key.n, 'base64url').byteLength << 3;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
catch { }
|
|
5
15
|
if (typeof modulusLength !== 'number' || modulusLength < 2048) {
|
|
6
16
|
throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
|
|
7
17
|
}
|
|
@@ -7,6 +7,7 @@ const webcrypto_js_1 = require("./webcrypto.js");
|
|
|
7
7
|
const is_key_object_js_1 = require("./is_key_object.js");
|
|
8
8
|
const invalid_key_input_js_1 = require("../lib/invalid_key_input.js");
|
|
9
9
|
const is_key_like_js_1 = require("./is_key_like.js");
|
|
10
|
+
const is_jwk_js_1 = require("../lib/is_jwk.js");
|
|
10
11
|
exports.weakMap = new WeakMap();
|
|
11
12
|
const namedCurveToJOSE = (namedCurve) => {
|
|
12
13
|
switch (namedCurve) {
|
|
@@ -30,6 +31,9 @@ const getNamedCurve = (kee, raw) => {
|
|
|
30
31
|
else if ((0, is_key_object_js_1.default)(kee)) {
|
|
31
32
|
key = kee;
|
|
32
33
|
}
|
|
34
|
+
else if ((0, is_jwk_js_1.isJWK)(kee)) {
|
|
35
|
+
return kee.crv;
|
|
36
|
+
}
|
|
33
37
|
else {
|
|
34
38
|
throw new TypeError((0, invalid_key_input_js_1.default)(kee, ...is_key_like_js_1.types));
|
|
35
39
|
}
|
|
@@ -5,6 +5,7 @@ const webcrypto_js_1 = require("./webcrypto.js");
|
|
|
5
5
|
const crypto_key_js_1 = require("../lib/crypto_key.js");
|
|
6
6
|
const invalid_key_input_js_1 = require("../lib/invalid_key_input.js");
|
|
7
7
|
const is_key_like_js_1 = require("./is_key_like.js");
|
|
8
|
+
const jwk = require("../lib/is_jwk.js");
|
|
8
9
|
function getSignVerifyKey(alg, key, usage) {
|
|
9
10
|
if (key instanceof Uint8Array) {
|
|
10
11
|
if (!alg.startsWith('HS')) {
|
|
@@ -19,6 +20,12 @@ function getSignVerifyKey(alg, key, usage) {
|
|
|
19
20
|
(0, crypto_key_js_1.checkSigCryptoKey)(key, alg, usage);
|
|
20
21
|
return node_crypto_1.KeyObject.from(key);
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
+
if (jwk.isJWK(key)) {
|
|
24
|
+
if (alg.startsWith('HS')) {
|
|
25
|
+
return (0, node_crypto_1.createSecretKey)(Buffer.from(key.k, 'base64url'));
|
|
26
|
+
}
|
|
27
|
+
return key;
|
|
28
|
+
}
|
|
29
|
+
throw new TypeError((0, invalid_key_input_js_1.default)(key, ...is_key_like_js_1.types, 'Uint8Array', 'JSON Web Key'));
|
|
23
30
|
}
|
|
24
31
|
exports.default = getSignVerifyKey;
|
|
@@ -4,10 +4,6 @@ const node_crypto_1 = require("node:crypto");
|
|
|
4
4
|
const get_named_curve_js_1 = require("./get_named_curve.js");
|
|
5
5
|
const errors_js_1 = require("../util/errors.js");
|
|
6
6
|
const check_key_length_js_1 = require("./check_key_length.js");
|
|
7
|
-
const PSS = {
|
|
8
|
-
padding: node_crypto_1.constants.RSA_PKCS1_PSS_PADDING,
|
|
9
|
-
saltLength: node_crypto_1.constants.RSA_PSS_SALTLEN_DIGEST,
|
|
10
|
-
};
|
|
11
7
|
const ecCurveAlgMap = new Map([
|
|
12
8
|
['ES256', 'P-256'],
|
|
13
9
|
['ES256K', 'secp256k1'],
|
|
@@ -15,25 +11,57 @@ const ecCurveAlgMap = new Map([
|
|
|
15
11
|
['ES512', 'P-521'],
|
|
16
12
|
]);
|
|
17
13
|
function keyForCrypto(alg, key) {
|
|
14
|
+
let asymmetricKeyType;
|
|
15
|
+
let asymmetricKeyDetails;
|
|
16
|
+
let isJWK;
|
|
17
|
+
if (key instanceof node_crypto_1.KeyObject) {
|
|
18
|
+
asymmetricKeyType = key.asymmetricKeyType;
|
|
19
|
+
asymmetricKeyDetails = key.asymmetricKeyDetails;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
isJWK = true;
|
|
23
|
+
switch (key.kty) {
|
|
24
|
+
case 'RSA':
|
|
25
|
+
asymmetricKeyType = 'rsa';
|
|
26
|
+
break;
|
|
27
|
+
case 'EC':
|
|
28
|
+
asymmetricKeyType = 'ec';
|
|
29
|
+
break;
|
|
30
|
+
case 'OKP': {
|
|
31
|
+
if (key.crv === 'Ed25519') {
|
|
32
|
+
asymmetricKeyType = 'ed25519';
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
if (key.crv === 'Ed448') {
|
|
36
|
+
asymmetricKeyType = 'ed448';
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
throw new TypeError('Invalid key for this operation, its crv must be Ed25519 or Ed448');
|
|
40
|
+
}
|
|
41
|
+
default:
|
|
42
|
+
throw new TypeError('Invalid key for this operation, its kty must be RSA, OKP, or EC');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
let options;
|
|
18
46
|
switch (alg) {
|
|
19
47
|
case 'EdDSA':
|
|
20
|
-
if (!['ed25519', 'ed448'].includes(
|
|
48
|
+
if (!['ed25519', 'ed448'].includes(asymmetricKeyType)) {
|
|
21
49
|
throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be ed25519 or ed448');
|
|
22
50
|
}
|
|
23
|
-
|
|
51
|
+
break;
|
|
24
52
|
case 'RS256':
|
|
25
53
|
case 'RS384':
|
|
26
54
|
case 'RS512':
|
|
27
|
-
if (
|
|
55
|
+
if (asymmetricKeyType !== 'rsa') {
|
|
28
56
|
throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa');
|
|
29
57
|
}
|
|
30
58
|
(0, check_key_length_js_1.default)(key, alg);
|
|
31
|
-
|
|
59
|
+
break;
|
|
32
60
|
case 'PS256':
|
|
33
61
|
case 'PS384':
|
|
34
62
|
case 'PS512':
|
|
35
|
-
if (
|
|
36
|
-
const { hashAlgorithm, mgf1HashAlgorithm, saltLength } =
|
|
63
|
+
if (asymmetricKeyType === 'rsa-pss') {
|
|
64
|
+
const { hashAlgorithm, mgf1HashAlgorithm, saltLength } = asymmetricKeyDetails;
|
|
37
65
|
const length = parseInt(alg.slice(-3), 10);
|
|
38
66
|
if (hashAlgorithm !== undefined &&
|
|
39
67
|
(hashAlgorithm !== `sha${length}` || mgf1HashAlgorithm !== hashAlgorithm)) {
|
|
@@ -43,16 +71,20 @@ function keyForCrypto(alg, key) {
|
|
|
43
71
|
throw new TypeError(`Invalid key for this operation, its RSA-PSS parameter saltLength does not meet the requirements of "alg" ${alg}`);
|
|
44
72
|
}
|
|
45
73
|
}
|
|
46
|
-
else if (
|
|
74
|
+
else if (asymmetricKeyType !== 'rsa') {
|
|
47
75
|
throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa or rsa-pss');
|
|
48
76
|
}
|
|
49
77
|
(0, check_key_length_js_1.default)(key, alg);
|
|
50
|
-
|
|
78
|
+
options = {
|
|
79
|
+
padding: node_crypto_1.constants.RSA_PKCS1_PSS_PADDING,
|
|
80
|
+
saltLength: node_crypto_1.constants.RSA_PSS_SALTLEN_DIGEST,
|
|
81
|
+
};
|
|
82
|
+
break;
|
|
51
83
|
case 'ES256':
|
|
52
84
|
case 'ES256K':
|
|
53
85
|
case 'ES384':
|
|
54
86
|
case 'ES512': {
|
|
55
|
-
if (
|
|
87
|
+
if (asymmetricKeyType !== 'ec') {
|
|
56
88
|
throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be ec');
|
|
57
89
|
}
|
|
58
90
|
const actual = (0, get_named_curve_js_1.default)(key);
|
|
@@ -60,10 +92,15 @@ function keyForCrypto(alg, key) {
|
|
|
60
92
|
if (actual !== expected) {
|
|
61
93
|
throw new TypeError(`Invalid key curve for the algorithm, its curve must be ${expected}, got ${actual}`);
|
|
62
94
|
}
|
|
63
|
-
|
|
95
|
+
options = { dsaEncoding: 'ieee-p1363' };
|
|
96
|
+
break;
|
|
64
97
|
}
|
|
65
98
|
default:
|
|
66
99
|
throw new errors_js_1.JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
|
|
67
100
|
}
|
|
101
|
+
if (isJWK) {
|
|
102
|
+
return { format: 'jwk', key, ...options };
|
|
103
|
+
}
|
|
104
|
+
return options ? { ...options, key } : key;
|
|
68
105
|
}
|
|
69
106
|
exports.default = keyForCrypto;
|
|
@@ -8,12 +8,12 @@ const node_key_js_1 = require("./node_key.js");
|
|
|
8
8
|
const get_sign_verify_key_js_1 = require("./get_sign_verify_key.js");
|
|
9
9
|
const oneShotSign = (0, node_util_1.promisify)(crypto.sign);
|
|
10
10
|
const sign = async (alg, key, data) => {
|
|
11
|
-
const
|
|
11
|
+
const k = (0, get_sign_verify_key_js_1.default)(alg, key, 'sign');
|
|
12
12
|
if (alg.startsWith('HS')) {
|
|
13
|
-
const hmac = crypto.createHmac((0, hmac_digest_js_1.default)(alg),
|
|
13
|
+
const hmac = crypto.createHmac((0, hmac_digest_js_1.default)(alg), k);
|
|
14
14
|
hmac.update(data);
|
|
15
15
|
return hmac.digest();
|
|
16
16
|
}
|
|
17
|
-
return oneShotSign((0, dsa_digest_js_1.default)(alg), data, (0, node_key_js_1.default)(alg,
|
|
17
|
+
return oneShotSign((0, dsa_digest_js_1.default)(alg), data, (0, node_key_js_1.default)(alg, k));
|
|
18
18
|
};
|
|
19
19
|
exports.default = sign;
|