jose 5.7.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/jwe/flattened/encrypt.js +1 -1
- package/dist/browser/jwe/general/encrypt.js +2 -1
- package/dist/browser/jwks/local.js +1 -1
- 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/lib/private_symbols.js +1 -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/jwe/flattened/encrypt.js +3 -3
- package/dist/node/cjs/jwe/general/encrypt.js +2 -1
- package/dist/node/cjs/jwks/local.js +1 -2
- 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/lib/private_symbols.js +4 -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/jwe/flattened/encrypt.js +1 -1
- package/dist/node/esm/jwe/general/encrypt.js +2 -1
- package/dist/node/esm/jwks/local.js +1 -1
- 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/lib/private_symbols.js +1 -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/jwe/compact/decrypt.d.ts +3 -0
- package/dist/types/jwe/compact/encrypt.d.ts +3 -0
- package/dist/types/jwe/flattened/decrypt.d.ts +3 -0
- package/dist/types/jwe/flattened/encrypt.d.ts +3 -2
- package/dist/types/jwe/general/decrypt.d.ts +3 -0
- package/dist/types/jwe/general/encrypt.d.ts +3 -0
- package/dist/types/jwk/embedded.d.ts +3 -0
- package/dist/types/jwk/thumbprint.d.ts +6 -0
- package/dist/types/jwks/local.d.ts +3 -7
- package/dist/types/jwks/remote.d.ts +3 -0
- package/dist/types/jws/compact/sign.d.ts +5 -2
- package/dist/types/jws/compact/verify.d.ts +6 -3
- package/dist/types/jws/flattened/sign.d.ts +5 -2
- package/dist/types/jws/flattened/verify.d.ts +6 -3
- package/dist/types/jws/general/sign.d.ts +5 -2
- package/dist/types/jws/general/verify.d.ts +6 -3
- package/dist/types/jwt/decrypt.d.ts +3 -0
- package/dist/types/jwt/encrypt.d.ts +3 -0
- package/dist/types/jwt/sign.d.ts +5 -2
- package/dist/types/jwt/unsecured.d.ts +3 -0
- package/dist/types/jwt/verify.d.ts +6 -3
- package/dist/types/key/export.d.ts +9 -0
- package/dist/types/key/generate_key_pair.d.ts +3 -0
- package/dist/types/key/generate_secret.d.ts +3 -0
- package/dist/types/key/import.d.ts +12 -0
- package/dist/types/types.d.ts +129 -29
- package/dist/types/util/decode_jwt.d.ts +3 -0
- package/dist/types/util/decode_protected_header.d.ts +3 -0
- package/package.json +271 -1
|
@@ -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;
|
|
@@ -8,9 +8,9 @@ const sign_js_1 = require("./sign.js");
|
|
|
8
8
|
const get_sign_verify_key_js_1 = require("./get_sign_verify_key.js");
|
|
9
9
|
const oneShotVerify = (0, node_util_1.promisify)(crypto.verify);
|
|
10
10
|
const verify = async (alg, key, signature, data) => {
|
|
11
|
-
const
|
|
11
|
+
const k = (0, get_sign_verify_key_js_1.default)(alg, key, 'verify');
|
|
12
12
|
if (alg.startsWith('HS')) {
|
|
13
|
-
const expected = await (0, sign_js_1.default)(alg,
|
|
13
|
+
const expected = await (0, sign_js_1.default)(alg, k, data);
|
|
14
14
|
const actual = signature;
|
|
15
15
|
try {
|
|
16
16
|
return crypto.timingSafeEqual(actual, expected);
|
|
@@ -20,7 +20,7 @@ const verify = async (alg, key, signature, data) => {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
const algorithm = (0, dsa_digest_js_1.default)(alg);
|
|
23
|
-
const keyInput = (0, node_key_js_1.default)(alg,
|
|
23
|
+
const keyInput = (0, node_key_js_1.default)(alg, k);
|
|
24
24
|
try {
|
|
25
25
|
return await oneShotVerify(algorithm, data, keyInput, signature);
|
|
26
26
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { encode as base64url } from '../../runtime/base64url.js';
|
|
2
|
+
import { unprotected } from '../../lib/private_symbols.js';
|
|
2
3
|
import encrypt from '../../runtime/encrypt.js';
|
|
3
4
|
import encryptKeyManagement from '../../lib/encrypt_key_management.js';
|
|
4
5
|
import { JOSENotSupported, JWEInvalid } from '../../util/errors.js';
|
|
5
6
|
import isDisjoint from '../../lib/is_disjoint.js';
|
|
6
7
|
import { encoder, decoder, concat } from '../../lib/buffer_utils.js';
|
|
7
8
|
import validateCrit from '../../lib/validate_crit.js';
|
|
8
|
-
export const unprotected = Symbol();
|
|
9
9
|
export class FlattenedEncrypt {
|
|
10
10
|
_plaintext;
|
|
11
11
|
_protectedHeader;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { FlattenedEncrypt
|
|
1
|
+
import { FlattenedEncrypt } from '../flattened/encrypt.js';
|
|
2
|
+
import { unprotected } from '../../lib/private_symbols.js';
|
|
2
3
|
import { JOSENotSupported, JWEInvalid } from '../../util/errors.js';
|
|
3
4
|
import generateCek from '../../lib/cek.js';
|
|
4
5
|
import isDisjoint from '../../lib/is_disjoint.js';
|
|
@@ -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
|
_payload;
|
|
@@ -52,7 +52,7 @@ export class FlattenedSign {
|
|
|
52
52
|
if (typeof alg !== 'string' || !alg) {
|
|
53
53
|
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
checkKeyTypeWithJwk(alg, key, 'sign');
|
|
56
56
|
let payload = this._payload;
|
|
57
57
|
if (b64) {
|
|
58
58
|
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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const unprotected = Symbol();
|
|
@@ -11,4 +11,4 @@ const encode = (input) => Buffer.from(input).toString('base64url');
|
|
|
11
11
|
export const decodeBase64 = (input) => new Uint8Array(Buffer.from(input, 'base64'));
|
|
12
12
|
export const encodeBase64 = (input) => Buffer.from(input).toString('base64');
|
|
13
13
|
export { encode };
|
|
14
|
-
export const decode = (input) => new Uint8Array(Buffer.from(normalize(input), '
|
|
14
|
+
export const decode = (input) => new Uint8Array(Buffer.from(normalize(input), 'base64url'));
|
|
@@ -1,5 +1,15 @@
|
|
|
1
|
+
import { KeyObject } from 'node:crypto';
|
|
1
2
|
export default (key, alg) => {
|
|
2
|
-
|
|
3
|
+
let modulusLength;
|
|
4
|
+
try {
|
|
5
|
+
if (key instanceof KeyObject) {
|
|
6
|
+
modulusLength = key.asymmetricKeyDetails?.modulusLength;
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
modulusLength = Buffer.from(key.n, 'base64url').byteLength << 3;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
catch { }
|
|
3
13
|
if (typeof modulusLength !== 'number' || modulusLength < 2048) {
|
|
4
14
|
throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
|
|
5
15
|
}
|
|
@@ -4,6 +4,7 @@ import { isCryptoKey } from './webcrypto.js';
|
|
|
4
4
|
import isKeyObject from './is_key_object.js';
|
|
5
5
|
import invalidKeyInput from '../lib/invalid_key_input.js';
|
|
6
6
|
import { types } from './is_key_like.js';
|
|
7
|
+
import { isJWK } from '../lib/is_jwk.js';
|
|
7
8
|
export const weakMap = new WeakMap();
|
|
8
9
|
const namedCurveToJOSE = (namedCurve) => {
|
|
9
10
|
switch (namedCurve) {
|
|
@@ -27,6 +28,9 @@ const getNamedCurve = (kee, raw) => {
|
|
|
27
28
|
else if (isKeyObject(kee)) {
|
|
28
29
|
key = kee;
|
|
29
30
|
}
|
|
31
|
+
else if (isJWK(kee)) {
|
|
32
|
+
return kee.crv;
|
|
33
|
+
}
|
|
30
34
|
else {
|
|
31
35
|
throw new TypeError(invalidKeyInput(kee, ...types));
|
|
32
36
|
}
|
|
@@ -3,6 +3,7 @@ import { isCryptoKey } from './webcrypto.js';
|
|
|
3
3
|
import { checkSigCryptoKey } from '../lib/crypto_key.js';
|
|
4
4
|
import invalidKeyInput from '../lib/invalid_key_input.js';
|
|
5
5
|
import { types } from './is_key_like.js';
|
|
6
|
+
import * as jwk from '../lib/is_jwk.js';
|
|
6
7
|
export default function getSignVerifyKey(alg, key, usage) {
|
|
7
8
|
if (key instanceof Uint8Array) {
|
|
8
9
|
if (!alg.startsWith('HS')) {
|
|
@@ -17,5 +18,11 @@ export default function getSignVerifyKey(alg, key, usage) {
|
|
|
17
18
|
checkSigCryptoKey(key, alg, usage);
|
|
18
19
|
return KeyObject.from(key);
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
+
if (jwk.isJWK(key)) {
|
|
22
|
+
if (alg.startsWith('HS')) {
|
|
23
|
+
return createSecretKey(Buffer.from(key.k, 'base64url'));
|
|
24
|
+
}
|
|
25
|
+
return key;
|
|
26
|
+
}
|
|
27
|
+
throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array', 'JSON Web Key'));
|
|
21
28
|
}
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { constants } from 'node:crypto';
|
|
1
|
+
import { constants, KeyObject } from 'node:crypto';
|
|
2
2
|
import getNamedCurve from './get_named_curve.js';
|
|
3
3
|
import { JOSENotSupported } from '../util/errors.js';
|
|
4
4
|
import checkKeyLength from './check_key_length.js';
|
|
5
|
-
const PSS = {
|
|
6
|
-
padding: constants.RSA_PKCS1_PSS_PADDING,
|
|
7
|
-
saltLength: constants.RSA_PSS_SALTLEN_DIGEST,
|
|
8
|
-
};
|
|
9
5
|
const ecCurveAlgMap = new Map([
|
|
10
6
|
['ES256', 'P-256'],
|
|
11
7
|
['ES256K', 'secp256k1'],
|
|
@@ -13,25 +9,57 @@ const ecCurveAlgMap = new Map([
|
|
|
13
9
|
['ES512', 'P-521'],
|
|
14
10
|
]);
|
|
15
11
|
export default function keyForCrypto(alg, key) {
|
|
12
|
+
let asymmetricKeyType;
|
|
13
|
+
let asymmetricKeyDetails;
|
|
14
|
+
let isJWK;
|
|
15
|
+
if (key instanceof KeyObject) {
|
|
16
|
+
asymmetricKeyType = key.asymmetricKeyType;
|
|
17
|
+
asymmetricKeyDetails = key.asymmetricKeyDetails;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
isJWK = true;
|
|
21
|
+
switch (key.kty) {
|
|
22
|
+
case 'RSA':
|
|
23
|
+
asymmetricKeyType = 'rsa';
|
|
24
|
+
break;
|
|
25
|
+
case 'EC':
|
|
26
|
+
asymmetricKeyType = 'ec';
|
|
27
|
+
break;
|
|
28
|
+
case 'OKP': {
|
|
29
|
+
if (key.crv === 'Ed25519') {
|
|
30
|
+
asymmetricKeyType = 'ed25519';
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
if (key.crv === 'Ed448') {
|
|
34
|
+
asymmetricKeyType = 'ed448';
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
throw new TypeError('Invalid key for this operation, its crv must be Ed25519 or Ed448');
|
|
38
|
+
}
|
|
39
|
+
default:
|
|
40
|
+
throw new TypeError('Invalid key for this operation, its kty must be RSA, OKP, or EC');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
let options;
|
|
16
44
|
switch (alg) {
|
|
17
45
|
case 'EdDSA':
|
|
18
|
-
if (!['ed25519', 'ed448'].includes(
|
|
46
|
+
if (!['ed25519', 'ed448'].includes(asymmetricKeyType)) {
|
|
19
47
|
throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be ed25519 or ed448');
|
|
20
48
|
}
|
|
21
|
-
|
|
49
|
+
break;
|
|
22
50
|
case 'RS256':
|
|
23
51
|
case 'RS384':
|
|
24
52
|
case 'RS512':
|
|
25
|
-
if (
|
|
53
|
+
if (asymmetricKeyType !== 'rsa') {
|
|
26
54
|
throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa');
|
|
27
55
|
}
|
|
28
56
|
checkKeyLength(key, alg);
|
|
29
|
-
|
|
57
|
+
break;
|
|
30
58
|
case 'PS256':
|
|
31
59
|
case 'PS384':
|
|
32
60
|
case 'PS512':
|
|
33
|
-
if (
|
|
34
|
-
const { hashAlgorithm, mgf1HashAlgorithm, saltLength } =
|
|
61
|
+
if (asymmetricKeyType === 'rsa-pss') {
|
|
62
|
+
const { hashAlgorithm, mgf1HashAlgorithm, saltLength } = asymmetricKeyDetails;
|
|
35
63
|
const length = parseInt(alg.slice(-3), 10);
|
|
36
64
|
if (hashAlgorithm !== undefined &&
|
|
37
65
|
(hashAlgorithm !== `sha${length}` || mgf1HashAlgorithm !== hashAlgorithm)) {
|
|
@@ -41,16 +69,20 @@ export default function keyForCrypto(alg, key) {
|
|
|
41
69
|
throw new TypeError(`Invalid key for this operation, its RSA-PSS parameter saltLength does not meet the requirements of "alg" ${alg}`);
|
|
42
70
|
}
|
|
43
71
|
}
|
|
44
|
-
else if (
|
|
72
|
+
else if (asymmetricKeyType !== 'rsa') {
|
|
45
73
|
throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be rsa or rsa-pss');
|
|
46
74
|
}
|
|
47
75
|
checkKeyLength(key, alg);
|
|
48
|
-
|
|
76
|
+
options = {
|
|
77
|
+
padding: constants.RSA_PKCS1_PSS_PADDING,
|
|
78
|
+
saltLength: constants.RSA_PSS_SALTLEN_DIGEST,
|
|
79
|
+
};
|
|
80
|
+
break;
|
|
49
81
|
case 'ES256':
|
|
50
82
|
case 'ES256K':
|
|
51
83
|
case 'ES384':
|
|
52
84
|
case 'ES512': {
|
|
53
|
-
if (
|
|
85
|
+
if (asymmetricKeyType !== 'ec') {
|
|
54
86
|
throw new TypeError('Invalid key for this operation, its asymmetricKeyType must be ec');
|
|
55
87
|
}
|
|
56
88
|
const actual = getNamedCurve(key);
|
|
@@ -58,9 +90,14 @@ export default function keyForCrypto(alg, key) {
|
|
|
58
90
|
if (actual !== expected) {
|
|
59
91
|
throw new TypeError(`Invalid key curve for the algorithm, its curve must be ${expected}, got ${actual}`);
|
|
60
92
|
}
|
|
61
|
-
|
|
93
|
+
options = { dsaEncoding: 'ieee-p1363' };
|
|
94
|
+
break;
|
|
62
95
|
}
|
|
63
96
|
default:
|
|
64
97
|
throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
|
|
65
98
|
}
|
|
99
|
+
if (isJWK) {
|
|
100
|
+
return { format: 'jwk', key, ...options };
|
|
101
|
+
}
|
|
102
|
+
return options ? { ...options, key } : key;
|
|
66
103
|
}
|
|
@@ -6,12 +6,12 @@ import nodeKey from './node_key.js';
|
|
|
6
6
|
import getSignKey from './get_sign_verify_key.js';
|
|
7
7
|
const oneShotSign = promisify(crypto.sign);
|
|
8
8
|
const sign = async (alg, key, data) => {
|
|
9
|
-
const
|
|
9
|
+
const k = getSignKey(alg, key, 'sign');
|
|
10
10
|
if (alg.startsWith('HS')) {
|
|
11
|
-
const hmac = crypto.createHmac(hmacDigest(alg),
|
|
11
|
+
const hmac = crypto.createHmac(hmacDigest(alg), k);
|
|
12
12
|
hmac.update(data);
|
|
13
13
|
return hmac.digest();
|
|
14
14
|
}
|
|
15
|
-
return oneShotSign(nodeDigest(alg), data, nodeKey(alg,
|
|
15
|
+
return oneShotSign(nodeDigest(alg), data, nodeKey(alg, k));
|
|
16
16
|
};
|
|
17
17
|
export default sign;
|
|
@@ -6,9 +6,9 @@ import sign from './sign.js';
|
|
|
6
6
|
import getVerifyKey from './get_sign_verify_key.js';
|
|
7
7
|
const oneShotVerify = promisify(crypto.verify);
|
|
8
8
|
const verify = async (alg, key, signature, data) => {
|
|
9
|
-
const
|
|
9
|
+
const k = getVerifyKey(alg, key, 'verify');
|
|
10
10
|
if (alg.startsWith('HS')) {
|
|
11
|
-
const expected = await sign(alg,
|
|
11
|
+
const expected = await sign(alg, k, data);
|
|
12
12
|
const actual = signature;
|
|
13
13
|
try {
|
|
14
14
|
return crypto.timingSafeEqual(actual, expected);
|
|
@@ -18,7 +18,7 @@ const verify = async (alg, key, signature, data) => {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
const algorithm = nodeDigest(alg);
|
|
21
|
-
const keyInput = nodeKey(alg,
|
|
21
|
+
const keyInput = nodeKey(alg, k);
|
|
22
22
|
try {
|
|
23
23
|
return await oneShotVerify(algorithm, data, keyInput, signature);
|
|
24
24
|
}
|
|
@@ -8,6 +8,9 @@ export interface CompactDecryptGetKey extends GetKeyFunction<CompactJWEHeaderPar
|
|
|
8
8
|
/**
|
|
9
9
|
* Decrypts a Compact JWE.
|
|
10
10
|
*
|
|
11
|
+
* This function is exported (as a named export) from the main `'jose'` module entry point as well
|
|
12
|
+
* as from its subpath export `'jose/jwe/compact/decrypt'`.
|
|
13
|
+
*
|
|
11
14
|
* @param jwe Compact JWE.
|
|
12
15
|
* @param key Private Key or Secret to decrypt the JWE with. See
|
|
13
16
|
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
|
|
@@ -2,6 +2,9 @@ import type { KeyLike, JWEKeyManagementHeaderParameters, CompactJWEHeaderParamet
|
|
|
2
2
|
/**
|
|
3
3
|
* The CompactEncrypt class is used to build and encrypt Compact JWE strings.
|
|
4
4
|
*
|
|
5
|
+
* This class is exported (as a named export) from the main `'jose'` module entry point as well as
|
|
6
|
+
* from its subpath export `'jose/jwe/compact/encrypt'`.
|
|
7
|
+
*
|
|
5
8
|
*/
|
|
6
9
|
export declare class CompactEncrypt {
|
|
7
10
|
private _flattened;
|
|
@@ -8,6 +8,9 @@ export interface FlattenedDecryptGetKey extends GetKeyFunction<JWEHeaderParamete
|
|
|
8
8
|
/**
|
|
9
9
|
* Decrypts a Flattened JWE.
|
|
10
10
|
*
|
|
11
|
+
* This function is exported (as a named export) from the main `'jose'` module entry point as well
|
|
12
|
+
* as from its subpath export `'jose/jwe/flattened/decrypt'`.
|
|
13
|
+
*
|
|
11
14
|
* @param jwe Flattened JWE.
|
|
12
15
|
* @param key Private Key or Secret to decrypt the JWE with. See
|
|
13
16
|
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { KeyLike, FlattenedJWE, JWEHeaderParameters, JWEKeyManagementHeaderParameters, EncryptOptions } from '../../types';
|
|
2
|
-
/** @private */
|
|
3
|
-
export declare const unprotected: unique symbol;
|
|
4
2
|
/**
|
|
5
3
|
* The FlattenedEncrypt class is used to build and encrypt Flattened JWE objects.
|
|
6
4
|
*
|
|
5
|
+
* This class is exported (as a named export) from the main `'jose'` module entry point as well as
|
|
6
|
+
* from its subpath export `'jose/jwe/flattened/encrypt'`.
|
|
7
|
+
*
|
|
7
8
|
*/
|
|
8
9
|
export declare class FlattenedEncrypt {
|
|
9
10
|
private _plaintext;
|
|
@@ -8,6 +8,9 @@ export interface GeneralDecryptGetKey extends GetKeyFunction<JWEHeaderParameters
|
|
|
8
8
|
/**
|
|
9
9
|
* Decrypts a General JWE.
|
|
10
10
|
*
|
|
11
|
+
* This function is exported (as a named export) from the main `'jose'` module entry point as well
|
|
12
|
+
* as from its subpath export `'jose/jwe/general/decrypt'`.
|
|
13
|
+
*
|
|
11
14
|
* @param jwe General JWE.
|
|
12
15
|
* @param key Private Key or Secret to decrypt the JWE with. See
|
|
13
16
|
* {@link https://github.com/panva/jose/issues/210#jwe-alg Algorithm Key Requirements}.
|
|
@@ -16,6 +16,9 @@ export interface Recipient {
|
|
|
16
16
|
/**
|
|
17
17
|
* The GeneralEncrypt class is used to build and encrypt General JWE objects.
|
|
18
18
|
*
|
|
19
|
+
* This class is exported (as a named export) from the main `'jose'` module entry point as well as
|
|
20
|
+
* from its subpath export `'jose/jwe/general/encrypt'`.
|
|
21
|
+
*
|
|
19
22
|
*/
|
|
20
23
|
export declare class GeneralEncrypt {
|
|
21
24
|
private _plaintext;
|
|
@@ -5,5 +5,8 @@ import type { KeyLike, FlattenedJWSInput, JWSHeaderParameters } from '../types';
|
|
|
5
5
|
* token's "jwk" (JSON Web Key) Header Parameter. It is recommended to combine this with the verify
|
|
6
6
|
* function's `algorithms` option to define accepted JWS "alg" (Algorithm) Header Parameter values.
|
|
7
7
|
*
|
|
8
|
+
* This function is exported (as a named export) from the main `'jose'` module entry point as well
|
|
9
|
+
* as from its subpath export `'jose/jwk/embedded'`.
|
|
10
|
+
*
|
|
8
11
|
*/
|
|
9
12
|
export declare function EmbeddedJWK<KeyLikeType extends KeyLike = KeyLike>(protectedHeader?: JWSHeaderParameters, token?: FlattenedJWSInput): Promise<KeyLikeType>;
|