jose 4.9.2 → 4.10.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 +4 -2
- package/dist/browser/key/import.js +20 -11
- package/dist/browser/lib/check_key_type.js +7 -7
- package/dist/browser/lib/crypto_key.js +16 -3
- package/dist/browser/lib/invalid_key_input.js +7 -2
- package/dist/browser/runtime/asn1.js +17 -4
- package/dist/browser/runtime/check_cek_length.js +3 -2
- package/dist/browser/runtime/ecdhes.js +16 -3
- package/dist/browser/runtime/env.js +3 -1
- package/dist/browser/runtime/generate.js +31 -4
- package/dist/browser/runtime/jwk_to_key.js +19 -1
- package/dist/browser/runtime/subtle_dsa.js +2 -0
- package/dist/node/cjs/key/import.js +20 -11
- package/dist/node/cjs/lib/check_key_type.js +6 -6
- package/dist/node/cjs/lib/crypto_key.js +16 -3
- package/dist/node/cjs/lib/invalid_key_input.js +9 -2
- package/dist/node/cjs/runtime/check_cek_length.js +6 -4
- package/dist/node/esm/key/import.js +20 -11
- package/dist/node/esm/lib/check_key_type.js +7 -7
- package/dist/node/esm/lib/crypto_key.js +16 -3
- package/dist/node/esm/lib/invalid_key_input.js +7 -2
- package/dist/node/esm/runtime/check_cek_length.js +6 -4
- package/dist/types/types.d.ts +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# jose
|
|
2
2
|
|
|
3
|
-
> "JSON Web Almost Everything" - JWA, JWS, JWE, JWT, JWK, JWKS
|
|
3
|
+
> "JSON Web Almost Everything" - JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, and other Web-interoperable runtimes.
|
|
4
4
|
|
|
5
5
|
## Implemented specs & features
|
|
6
6
|
|
|
@@ -22,6 +22,8 @@ implementation is correct.
|
|
|
22
22
|
|
|
23
23
|
## [💗 Help the project](https://github.com/sponsors/panva)
|
|
24
24
|
|
|
25
|
+
## Dependencies: 0
|
|
26
|
+
|
|
25
27
|
## Documentation
|
|
26
28
|
|
|
27
29
|
**`example`** ESM import
|
|
@@ -100,7 +102,7 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
|
|
|
100
102
|
|
|
101
103
|
#### How is it different from [`jws`](https://github.com/brianloveswords/node-jws), [`jwa`](https://github.com/brianloveswords/node-jwa) or [`jsonwebtoken`](https://github.com/auth0/node-jsonwebtoken)?
|
|
102
104
|
|
|
103
|
-
- it supports Browser, Cloudflare Workers, and
|
|
105
|
+
- it supports Browser, Deno, Cloudflare Workers, and other Web-interoperable runtimes
|
|
104
106
|
- it supports encrypted JWTs (i.e. in JWE format)
|
|
105
107
|
- supports secp256k1, Ed25519, Ed448, X25519, and X448
|
|
106
108
|
- it supports JWK Key Format for all four key types (oct, RSA, EC and OKP)
|
|
@@ -33,18 +33,12 @@ function parseElement(bytes) {
|
|
|
33
33
|
length = bytes[position];
|
|
34
34
|
position++;
|
|
35
35
|
}
|
|
36
|
-
else {
|
|
37
|
-
let numberOfDigits = bytes[position] & 0x7f;
|
|
38
|
-
position++;
|
|
39
|
-
length = 0;
|
|
40
|
-
for (let i = 0; i < numberOfDigits; i++) {
|
|
41
|
-
length = length * 256 + bytes[position];
|
|
42
|
-
position++;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
if (length === 0x80) {
|
|
36
|
+
else if (length === 0x80) {
|
|
46
37
|
length = 0;
|
|
47
38
|
while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) {
|
|
39
|
+
if (length > bytes.byteLength) {
|
|
40
|
+
throw new TypeError('invalid indefinite form length');
|
|
41
|
+
}
|
|
48
42
|
length++;
|
|
49
43
|
}
|
|
50
44
|
const byteLength = position + length + 2;
|
|
@@ -54,6 +48,15 @@ function parseElement(bytes) {
|
|
|
54
48
|
raw: bytes.subarray(0, byteLength),
|
|
55
49
|
};
|
|
56
50
|
}
|
|
51
|
+
else {
|
|
52
|
+
let numberOfDigits = bytes[position] & 0x7f;
|
|
53
|
+
position++;
|
|
54
|
+
length = 0;
|
|
55
|
+
for (let i = 0; i < numberOfDigits; i++) {
|
|
56
|
+
length = length * 256 + bytes[position];
|
|
57
|
+
position++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
57
60
|
const byteLength = position + length;
|
|
58
61
|
return {
|
|
59
62
|
byteLength,
|
|
@@ -80,7 +83,13 @@ export async function importX509(x509, alg, options) {
|
|
|
80
83
|
if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) {
|
|
81
84
|
throw new TypeError('"x509" must be X.509 formatted string');
|
|
82
85
|
}
|
|
83
|
-
|
|
86
|
+
let spki;
|
|
87
|
+
try {
|
|
88
|
+
spki = getSPKI(x509);
|
|
89
|
+
}
|
|
90
|
+
catch (cause) {
|
|
91
|
+
throw new TypeError('failed to parse the X.509 certificate', { cause });
|
|
92
|
+
}
|
|
84
93
|
return importPublic(spki, alg, options);
|
|
85
94
|
}
|
|
86
95
|
export async function importPKCS8(pkcs8, alg, options) {
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import invalidKeyInput from './invalid_key_input.js';
|
|
1
|
+
import { withAlg as invalidKeyInput } from './invalid_key_input.js';
|
|
2
2
|
import isKeyLike, { types } from '../runtime/is_key_like.js';
|
|
3
|
-
const symmetricTypeCheck = (key) => {
|
|
3
|
+
const symmetricTypeCheck = (alg, key) => {
|
|
4
4
|
if (key instanceof Uint8Array)
|
|
5
5
|
return;
|
|
6
6
|
if (!isKeyLike(key)) {
|
|
7
|
-
throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array'));
|
|
7
|
+
throw new TypeError(invalidKeyInput(alg, key, ...types, 'Uint8Array'));
|
|
8
8
|
}
|
|
9
9
|
if (key.type !== 'secret') {
|
|
10
10
|
throw new TypeError(`${types.join(' or ')} instances for symmetric algorithms must be of type "secret"`);
|
|
11
11
|
}
|
|
12
12
|
};
|
|
13
|
-
const asymmetricTypeCheck = (key, usage) => {
|
|
13
|
+
const asymmetricTypeCheck = (alg, key, usage) => {
|
|
14
14
|
if (!isKeyLike(key)) {
|
|
15
|
-
throw new TypeError(invalidKeyInput(key, ...types));
|
|
15
|
+
throw new TypeError(invalidKeyInput(alg, key, ...types));
|
|
16
16
|
}
|
|
17
17
|
if (key.type === 'secret') {
|
|
18
18
|
throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithms must not be of type "secret"`);
|
|
@@ -36,10 +36,10 @@ const checkKeyType = (alg, key, usage) => {
|
|
|
36
36
|
alg.startsWith('PBES2') ||
|
|
37
37
|
/^A\d{3}(?:GCM)?KW$/.test(alg);
|
|
38
38
|
if (symmetric) {
|
|
39
|
-
symmetricTypeCheck(key);
|
|
39
|
+
symmetricTypeCheck(alg, key);
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
|
-
asymmetricTypeCheck(key, usage);
|
|
42
|
+
asymmetricTypeCheck(alg, key, usage);
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
export default checkKeyType;
|
|
@@ -76,6 +76,12 @@ export function checkSigCryptoKey(key, alg, ...usages) {
|
|
|
76
76
|
throw unusable('NODE-ED25519');
|
|
77
77
|
break;
|
|
78
78
|
}
|
|
79
|
+
case 'EdDSA': {
|
|
80
|
+
if (key.algorithm.name !== 'Ed25519' && key.algorithm.name !== 'Ed448') {
|
|
81
|
+
throw unusable('Ed25519 or Ed448');
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
79
85
|
case 'ES256':
|
|
80
86
|
case 'ES384':
|
|
81
87
|
case 'ES512': {
|
|
@@ -116,10 +122,17 @@ export function checkEncCryptoKey(key, alg, ...usages) {
|
|
|
116
122
|
throw unusable(expected, 'algorithm.length');
|
|
117
123
|
break;
|
|
118
124
|
}
|
|
119
|
-
case 'ECDH':
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
case 'ECDH': {
|
|
126
|
+
switch (key.algorithm.name) {
|
|
127
|
+
case 'ECDH':
|
|
128
|
+
case 'X25519':
|
|
129
|
+
case 'X448':
|
|
130
|
+
break;
|
|
131
|
+
default:
|
|
132
|
+
throw unusable('ECDH, X25519, or X448');
|
|
133
|
+
}
|
|
122
134
|
break;
|
|
135
|
+
}
|
|
123
136
|
case 'PBES2-HS256+A128KW':
|
|
124
137
|
case 'PBES2-HS384+A192KW':
|
|
125
138
|
case 'PBES2-HS512+A256KW':
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
let msg = 'Key must be ';
|
|
1
|
+
function message(msg, actual, ...types) {
|
|
3
2
|
if (types.length > 2) {
|
|
4
3
|
const last = types.pop();
|
|
5
4
|
msg += `one of type ${types.join(', ')}, or ${last}.`;
|
|
@@ -22,4 +21,10 @@ export default (actual, ...types) => {
|
|
|
22
21
|
}
|
|
23
22
|
}
|
|
24
23
|
return msg;
|
|
24
|
+
}
|
|
25
|
+
export default (actual, ...types) => {
|
|
26
|
+
return message('Key must be ', actual, ...types);
|
|
25
27
|
};
|
|
28
|
+
export function withAlg(alg, actual, ...types) {
|
|
29
|
+
return message(`Key for the ${alg} algorithm must be `, actual, ...types);
|
|
30
|
+
}
|
|
@@ -44,8 +44,14 @@ const getNamedCurve = (keyData) => {
|
|
|
44
44
|
return 'P-384';
|
|
45
45
|
case findOid(keyData, [0x2b, 0x81, 0x04, 0x00, 0x23]):
|
|
46
46
|
return 'P-521';
|
|
47
|
-
case
|
|
47
|
+
case findOid(keyData, [0x2b, 0x65, 0x6e]):
|
|
48
|
+
return 'X25519';
|
|
49
|
+
case findOid(keyData, [0x2b, 0x65, 0x6f]):
|
|
50
|
+
return 'X448';
|
|
51
|
+
case findOid(keyData, [0x2b, 0x65, 0x70]):
|
|
48
52
|
return 'Ed25519';
|
|
53
|
+
case findOid(keyData, [0x2b, 0x65, 0x71]):
|
|
54
|
+
return 'Ed448';
|
|
49
55
|
default:
|
|
50
56
|
throw new JOSENotSupported('Invalid or unsupported EC Key Curve or OKP Key Sub Type');
|
|
51
57
|
}
|
|
@@ -96,15 +102,22 @@ const genericImport = async (replace, keyFormat, pem, alg, options) => {
|
|
|
96
102
|
case 'ECDH-ES':
|
|
97
103
|
case 'ECDH-ES+A128KW':
|
|
98
104
|
case 'ECDH-ES+A192KW':
|
|
99
|
-
case 'ECDH-ES+A256KW':
|
|
100
|
-
|
|
105
|
+
case 'ECDH-ES+A256KW': {
|
|
106
|
+
const namedCurve = getNamedCurve(keyData);
|
|
107
|
+
algorithm = namedCurve.startsWith('P-') ? { name: 'ECDH', namedCurve } : { name: namedCurve };
|
|
101
108
|
keyUsages = isPublic ? [] : ['deriveBits'];
|
|
102
109
|
break;
|
|
103
|
-
|
|
110
|
+
}
|
|
111
|
+
case isCloudflareWorkers() && 'EdDSA': {
|
|
104
112
|
const namedCurve = getNamedCurve(keyData).toUpperCase();
|
|
105
113
|
algorithm = { name: `NODE-${namedCurve}`, namedCurve: `NODE-${namedCurve}` };
|
|
106
114
|
keyUsages = isPublic ? ['verify'] : ['sign'];
|
|
107
115
|
break;
|
|
116
|
+
}
|
|
117
|
+
case 'EdDSA':
|
|
118
|
+
algorithm = { name: getNamedCurve(keyData) };
|
|
119
|
+
keyUsages = isPublic ? ['verify'] : ['sign'];
|
|
120
|
+
break;
|
|
108
121
|
default:
|
|
109
122
|
throw new JOSENotSupported('Invalid or unsupported "alg" (Algorithm) value');
|
|
110
123
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { JWEInvalid } from '../util/errors.js';
|
|
2
2
|
const checkCekLength = (cek, expected) => {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const actual = cek.byteLength << 3;
|
|
4
|
+
if (actual !== expected) {
|
|
5
|
+
throw new JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`);
|
|
5
6
|
}
|
|
6
7
|
};
|
|
7
8
|
export default checkCekLength;
|
|
@@ -13,10 +13,21 @@ export async function deriveKey(publicKey, privateKey, algorithm, keyLength, apu
|
|
|
13
13
|
}
|
|
14
14
|
checkEncCryptoKey(privateKey, 'ECDH', 'deriveBits');
|
|
15
15
|
const value = concat(lengthAndInput(encoder.encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength));
|
|
16
|
+
let length;
|
|
17
|
+
if (publicKey.algorithm.name === 'X25519') {
|
|
18
|
+
length = 256;
|
|
19
|
+
}
|
|
20
|
+
else if (publicKey.algorithm.name === 'X448') {
|
|
21
|
+
length = 448;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
length =
|
|
25
|
+
Math.ceil(parseInt(publicKey.algorithm.namedCurve.substr(-3), 10) / 8) << 3;
|
|
26
|
+
}
|
|
16
27
|
const sharedSecret = new Uint8Array(await crypto.subtle.deriveBits({
|
|
17
|
-
name:
|
|
28
|
+
name: publicKey.algorithm.name,
|
|
18
29
|
public: publicKey,
|
|
19
|
-
}, privateKey,
|
|
30
|
+
}, privateKey, length));
|
|
20
31
|
return concatKdf(sharedSecret, keyLength, value);
|
|
21
32
|
}
|
|
22
33
|
export async function generateEpk(key) {
|
|
@@ -29,5 +40,7 @@ export function ecdhAllowed(key) {
|
|
|
29
40
|
if (!isCryptoKey(key)) {
|
|
30
41
|
throw new TypeError(invalidKeyInput(key, ...types));
|
|
31
42
|
}
|
|
32
|
-
return ['P-256', 'P-384', 'P-521'].includes(key.algorithm.namedCurve)
|
|
43
|
+
return (['P-256', 'P-384', 'P-521'].includes(key.algorithm.namedCurve) ||
|
|
44
|
+
key.algorithm.name === 'X25519' ||
|
|
45
|
+
key.algorithm.name === 'X448');
|
|
33
46
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export function isCloudflareWorkers() {
|
|
2
|
-
return typeof WebSocketPair
|
|
2
|
+
return (typeof WebSocketPair !== 'undefined' ||
|
|
3
|
+
(typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') ||
|
|
4
|
+
(typeof EdgeRuntime !== 'undefined' && EdgeRuntime === 'vercel'));
|
|
3
5
|
}
|
|
@@ -51,7 +51,7 @@ function getModulusLengthOption(options) {
|
|
|
51
51
|
return modulusLength;
|
|
52
52
|
}
|
|
53
53
|
export async function generateKeyPair(alg, options) {
|
|
54
|
-
var _a, _b;
|
|
54
|
+
var _a, _b, _c;
|
|
55
55
|
let algorithm;
|
|
56
56
|
let keyUsages;
|
|
57
57
|
switch (alg) {
|
|
@@ -112,15 +112,42 @@ export async function generateKeyPair(alg, options) {
|
|
|
112
112
|
throw new JOSENotSupported('Invalid or unsupported crv option provided');
|
|
113
113
|
}
|
|
114
114
|
break;
|
|
115
|
+
case 'EdDSA':
|
|
116
|
+
keyUsages = ['sign', 'verify'];
|
|
117
|
+
const crv = (_a = options === null || options === void 0 ? void 0 : options.crv) !== null && _a !== void 0 ? _a : 'Ed25519';
|
|
118
|
+
switch (crv) {
|
|
119
|
+
case 'Ed25519':
|
|
120
|
+
case 'Ed448':
|
|
121
|
+
algorithm = { name: crv };
|
|
122
|
+
break;
|
|
123
|
+
default:
|
|
124
|
+
throw new JOSENotSupported('Invalid or unsupported crv option provided');
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
115
127
|
case 'ECDH-ES':
|
|
116
128
|
case 'ECDH-ES+A128KW':
|
|
117
129
|
case 'ECDH-ES+A192KW':
|
|
118
|
-
case 'ECDH-ES+A256KW':
|
|
119
|
-
algorithm = { name: 'ECDH', namedCurve: (_a = options === null || options === void 0 ? void 0 : options.crv) !== null && _a !== void 0 ? _a : 'P-256' };
|
|
130
|
+
case 'ECDH-ES+A256KW': {
|
|
120
131
|
keyUsages = ['deriveKey', 'deriveBits'];
|
|
132
|
+
const crv = (_b = options === null || options === void 0 ? void 0 : options.crv) !== null && _b !== void 0 ? _b : 'P-256';
|
|
133
|
+
switch (crv) {
|
|
134
|
+
case 'P-256':
|
|
135
|
+
case 'P-384':
|
|
136
|
+
case 'P-521': {
|
|
137
|
+
algorithm = { name: 'ECDH', namedCurve: crv };
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
case 'X25519':
|
|
141
|
+
case 'X448':
|
|
142
|
+
algorithm = { name: crv };
|
|
143
|
+
break;
|
|
144
|
+
default:
|
|
145
|
+
throw new JOSENotSupported('Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448');
|
|
146
|
+
}
|
|
121
147
|
break;
|
|
148
|
+
}
|
|
122
149
|
default:
|
|
123
150
|
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
|
|
124
151
|
}
|
|
125
|
-
return (crypto.subtle.generateKey(algorithm, (
|
|
152
|
+
return (crypto.subtle.generateKey(algorithm, (_c = options === null || options === void 0 ? void 0 : options.extractable) !== null && _c !== void 0 ? _c : false, keyUsages));
|
|
126
153
|
}
|
|
@@ -109,9 +109,27 @@ function subtleMapping(jwk) {
|
|
|
109
109
|
keyUsages = jwk.d ? ['sign'] : ['verify'];
|
|
110
110
|
break;
|
|
111
111
|
default:
|
|
112
|
-
throw new JOSENotSupported('Invalid or unsupported JWK "
|
|
112
|
+
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
|
|
113
113
|
}
|
|
114
114
|
break;
|
|
115
|
+
case 'OKP': {
|
|
116
|
+
switch (jwk.alg) {
|
|
117
|
+
case 'EdDSA':
|
|
118
|
+
algorithm = { name: jwk.crv };
|
|
119
|
+
keyUsages = jwk.d ? ['sign'] : ['verify'];
|
|
120
|
+
break;
|
|
121
|
+
case 'ECDH-ES':
|
|
122
|
+
case 'ECDH-ES+A128KW':
|
|
123
|
+
case 'ECDH-ES+A192KW':
|
|
124
|
+
case 'ECDH-ES+A256KW':
|
|
125
|
+
algorithm = { name: jwk.crv };
|
|
126
|
+
keyUsages = jwk.d ? ['deriveBits'] : [];
|
|
127
|
+
break;
|
|
128
|
+
default:
|
|
129
|
+
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
115
133
|
default:
|
|
116
134
|
throw new JOSENotSupported('Invalid or unsupported JWK "kty" (Key Type) Parameter value');
|
|
117
135
|
}
|
|
@@ -22,6 +22,8 @@ export default function subtleDsa(alg, algorithm) {
|
|
|
22
22
|
case isCloudflareWorkers() && 'EdDSA':
|
|
23
23
|
const { namedCurve } = algorithm;
|
|
24
24
|
return { name: namedCurve, namedCurve };
|
|
25
|
+
case 'EdDSA':
|
|
26
|
+
return { name: algorithm.name };
|
|
25
27
|
default:
|
|
26
28
|
throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
|
|
27
29
|
}
|
|
@@ -36,18 +36,12 @@ function parseElement(bytes) {
|
|
|
36
36
|
length = bytes[position];
|
|
37
37
|
position++;
|
|
38
38
|
}
|
|
39
|
-
else {
|
|
40
|
-
let numberOfDigits = bytes[position] & 0x7f;
|
|
41
|
-
position++;
|
|
42
|
-
length = 0;
|
|
43
|
-
for (let i = 0; i < numberOfDigits; i++) {
|
|
44
|
-
length = length * 256 + bytes[position];
|
|
45
|
-
position++;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (length === 0x80) {
|
|
39
|
+
else if (length === 0x80) {
|
|
49
40
|
length = 0;
|
|
50
41
|
while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) {
|
|
42
|
+
if (length > bytes.byteLength) {
|
|
43
|
+
throw new TypeError('invalid indefinite form length');
|
|
44
|
+
}
|
|
51
45
|
length++;
|
|
52
46
|
}
|
|
53
47
|
const byteLength = position + length + 2;
|
|
@@ -57,6 +51,15 @@ function parseElement(bytes) {
|
|
|
57
51
|
raw: bytes.subarray(0, byteLength),
|
|
58
52
|
};
|
|
59
53
|
}
|
|
54
|
+
else {
|
|
55
|
+
let numberOfDigits = bytes[position] & 0x7f;
|
|
56
|
+
position++;
|
|
57
|
+
length = 0;
|
|
58
|
+
for (let i = 0; i < numberOfDigits; i++) {
|
|
59
|
+
length = length * 256 + bytes[position];
|
|
60
|
+
position++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
60
63
|
const byteLength = position + length;
|
|
61
64
|
return {
|
|
62
65
|
byteLength,
|
|
@@ -84,7 +87,13 @@ async function importX509(x509, alg, options) {
|
|
|
84
87
|
if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) {
|
|
85
88
|
throw new TypeError('"x509" must be X.509 formatted string');
|
|
86
89
|
}
|
|
87
|
-
|
|
90
|
+
let spki;
|
|
91
|
+
try {
|
|
92
|
+
spki = getSPKI(x509);
|
|
93
|
+
}
|
|
94
|
+
catch (cause) {
|
|
95
|
+
throw new TypeError('failed to parse the X.509 certificate', { cause });
|
|
96
|
+
}
|
|
88
97
|
return (0, asn1_js_1.fromSPKI)(spki, alg, options);
|
|
89
98
|
}
|
|
90
99
|
exports.importX509 = importX509;
|
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const invalid_key_input_js_1 = require("./invalid_key_input.js");
|
|
4
4
|
const is_key_like_js_1 = require("../runtime/is_key_like.js");
|
|
5
|
-
const symmetricTypeCheck = (key) => {
|
|
5
|
+
const symmetricTypeCheck = (alg, key) => {
|
|
6
6
|
if (key instanceof Uint8Array)
|
|
7
7
|
return;
|
|
8
8
|
if (!(0, is_key_like_js_1.default)(key)) {
|
|
9
|
-
throw new TypeError((0, invalid_key_input_js_1.
|
|
9
|
+
throw new TypeError((0, invalid_key_input_js_1.withAlg)(alg, key, ...is_key_like_js_1.types, 'Uint8Array'));
|
|
10
10
|
}
|
|
11
11
|
if (key.type !== 'secret') {
|
|
12
12
|
throw new TypeError(`${is_key_like_js_1.types.join(' or ')} instances for symmetric algorithms must be of type "secret"`);
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
|
-
const asymmetricTypeCheck = (key, usage) => {
|
|
15
|
+
const asymmetricTypeCheck = (alg, key, usage) => {
|
|
16
16
|
if (!(0, is_key_like_js_1.default)(key)) {
|
|
17
|
-
throw new TypeError((0, invalid_key_input_js_1.
|
|
17
|
+
throw new TypeError((0, invalid_key_input_js_1.withAlg)(alg, key, ...is_key_like_js_1.types));
|
|
18
18
|
}
|
|
19
19
|
if (key.type === 'secret') {
|
|
20
20
|
throw new TypeError(`${is_key_like_js_1.types.join(' or ')} instances for asymmetric algorithms must not be of type "secret"`);
|
|
@@ -38,10 +38,10 @@ const checkKeyType = (alg, key, usage) => {
|
|
|
38
38
|
alg.startsWith('PBES2') ||
|
|
39
39
|
/^A\d{3}(?:GCM)?KW$/.test(alg);
|
|
40
40
|
if (symmetric) {
|
|
41
|
-
symmetricTypeCheck(key);
|
|
41
|
+
symmetricTypeCheck(alg, key);
|
|
42
42
|
}
|
|
43
43
|
else {
|
|
44
|
-
asymmetricTypeCheck(key, usage);
|
|
44
|
+
asymmetricTypeCheck(alg, key, usage);
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
exports.default = checkKeyType;
|
|
@@ -79,6 +79,12 @@ function checkSigCryptoKey(key, alg, ...usages) {
|
|
|
79
79
|
throw unusable('NODE-ED25519');
|
|
80
80
|
break;
|
|
81
81
|
}
|
|
82
|
+
case 'EdDSA': {
|
|
83
|
+
if (key.algorithm.name !== 'Ed25519' && key.algorithm.name !== 'Ed448') {
|
|
84
|
+
throw unusable('Ed25519 or Ed448');
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
82
88
|
case 'ES256':
|
|
83
89
|
case 'ES384':
|
|
84
90
|
case 'ES512': {
|
|
@@ -120,10 +126,17 @@ function checkEncCryptoKey(key, alg, ...usages) {
|
|
|
120
126
|
throw unusable(expected, 'algorithm.length');
|
|
121
127
|
break;
|
|
122
128
|
}
|
|
123
|
-
case 'ECDH':
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
case 'ECDH': {
|
|
130
|
+
switch (key.algorithm.name) {
|
|
131
|
+
case 'ECDH':
|
|
132
|
+
case 'X25519':
|
|
133
|
+
case 'X448':
|
|
134
|
+
break;
|
|
135
|
+
default:
|
|
136
|
+
throw unusable('ECDH, X25519, or X448');
|
|
137
|
+
}
|
|
126
138
|
break;
|
|
139
|
+
}
|
|
127
140
|
case 'PBES2-HS256+A128KW':
|
|
128
141
|
case 'PBES2-HS384+A192KW':
|
|
129
142
|
case 'PBES2-HS512+A256KW':
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
3
|
+
exports.withAlg = void 0;
|
|
4
|
+
function message(msg, actual, ...types) {
|
|
5
5
|
if (types.length > 2) {
|
|
6
6
|
const last = types.pop();
|
|
7
7
|
msg += `one of type ${types.join(', ')}, or ${last}.`;
|
|
@@ -24,4 +24,11 @@ exports.default = (actual, ...types) => {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
return msg;
|
|
27
|
+
}
|
|
28
|
+
exports.default = (actual, ...types) => {
|
|
29
|
+
return message('Key must be ', actual, ...types);
|
|
27
30
|
};
|
|
31
|
+
function withAlg(alg, actual, ...types) {
|
|
32
|
+
return message(`Key for the ${alg} algorithm must be `, actual, ...types);
|
|
33
|
+
}
|
|
34
|
+
exports.withAlg = withAlg;
|
|
@@ -19,14 +19,16 @@ const checkCekLength = (enc, cek) => {
|
|
|
19
19
|
throw new errors_js_1.JOSENotSupported(`Content Encryption Algorithm ${enc} is not supported either by JOSE or your javascript runtime`);
|
|
20
20
|
}
|
|
21
21
|
if (cek instanceof Uint8Array) {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const actual = cek.byteLength << 3;
|
|
23
|
+
if (actual !== expected) {
|
|
24
|
+
throw new errors_js_1.JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`);
|
|
24
25
|
}
|
|
25
26
|
return;
|
|
26
27
|
}
|
|
27
28
|
if ((0, is_key_object_js_1.default)(cek) && cek.type === 'secret') {
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
const actual = cek.symmetricKeySize << 3;
|
|
30
|
+
if (actual !== expected) {
|
|
31
|
+
throw new errors_js_1.JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`);
|
|
30
32
|
}
|
|
31
33
|
return;
|
|
32
34
|
}
|
|
@@ -33,18 +33,12 @@ function parseElement(bytes) {
|
|
|
33
33
|
length = bytes[position];
|
|
34
34
|
position++;
|
|
35
35
|
}
|
|
36
|
-
else {
|
|
37
|
-
let numberOfDigits = bytes[position] & 0x7f;
|
|
38
|
-
position++;
|
|
39
|
-
length = 0;
|
|
40
|
-
for (let i = 0; i < numberOfDigits; i++) {
|
|
41
|
-
length = length * 256 + bytes[position];
|
|
42
|
-
position++;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
if (length === 0x80) {
|
|
36
|
+
else if (length === 0x80) {
|
|
46
37
|
length = 0;
|
|
47
38
|
while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) {
|
|
39
|
+
if (length > bytes.byteLength) {
|
|
40
|
+
throw new TypeError('invalid indefinite form length');
|
|
41
|
+
}
|
|
48
42
|
length++;
|
|
49
43
|
}
|
|
50
44
|
const byteLength = position + length + 2;
|
|
@@ -54,6 +48,15 @@ function parseElement(bytes) {
|
|
|
54
48
|
raw: bytes.subarray(0, byteLength),
|
|
55
49
|
};
|
|
56
50
|
}
|
|
51
|
+
else {
|
|
52
|
+
let numberOfDigits = bytes[position] & 0x7f;
|
|
53
|
+
position++;
|
|
54
|
+
length = 0;
|
|
55
|
+
for (let i = 0; i < numberOfDigits; i++) {
|
|
56
|
+
length = length * 256 + bytes[position];
|
|
57
|
+
position++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
57
60
|
const byteLength = position + length;
|
|
58
61
|
return {
|
|
59
62
|
byteLength,
|
|
@@ -80,7 +83,13 @@ export async function importX509(x509, alg, options) {
|
|
|
80
83
|
if (typeof x509 !== 'string' || x509.indexOf('-----BEGIN CERTIFICATE-----') !== 0) {
|
|
81
84
|
throw new TypeError('"x509" must be X.509 formatted string');
|
|
82
85
|
}
|
|
83
|
-
|
|
86
|
+
let spki;
|
|
87
|
+
try {
|
|
88
|
+
spki = getSPKI(x509);
|
|
89
|
+
}
|
|
90
|
+
catch (cause) {
|
|
91
|
+
throw new TypeError('failed to parse the X.509 certificate', { cause });
|
|
92
|
+
}
|
|
84
93
|
return importPublic(spki, alg, options);
|
|
85
94
|
}
|
|
86
95
|
export async function importPKCS8(pkcs8, alg, options) {
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import invalidKeyInput from './invalid_key_input.js';
|
|
1
|
+
import { withAlg as invalidKeyInput } from './invalid_key_input.js';
|
|
2
2
|
import isKeyLike, { types } from '../runtime/is_key_like.js';
|
|
3
|
-
const symmetricTypeCheck = (key) => {
|
|
3
|
+
const symmetricTypeCheck = (alg, key) => {
|
|
4
4
|
if (key instanceof Uint8Array)
|
|
5
5
|
return;
|
|
6
6
|
if (!isKeyLike(key)) {
|
|
7
|
-
throw new TypeError(invalidKeyInput(key, ...types, 'Uint8Array'));
|
|
7
|
+
throw new TypeError(invalidKeyInput(alg, key, ...types, 'Uint8Array'));
|
|
8
8
|
}
|
|
9
9
|
if (key.type !== 'secret') {
|
|
10
10
|
throw new TypeError(`${types.join(' or ')} instances for symmetric algorithms must be of type "secret"`);
|
|
11
11
|
}
|
|
12
12
|
};
|
|
13
|
-
const asymmetricTypeCheck = (key, usage) => {
|
|
13
|
+
const asymmetricTypeCheck = (alg, key, usage) => {
|
|
14
14
|
if (!isKeyLike(key)) {
|
|
15
|
-
throw new TypeError(invalidKeyInput(key, ...types));
|
|
15
|
+
throw new TypeError(invalidKeyInput(alg, key, ...types));
|
|
16
16
|
}
|
|
17
17
|
if (key.type === 'secret') {
|
|
18
18
|
throw new TypeError(`${types.join(' or ')} instances for asymmetric algorithms must not be of type "secret"`);
|
|
@@ -36,10 +36,10 @@ const checkKeyType = (alg, key, usage) => {
|
|
|
36
36
|
alg.startsWith('PBES2') ||
|
|
37
37
|
/^A\d{3}(?:GCM)?KW$/.test(alg);
|
|
38
38
|
if (symmetric) {
|
|
39
|
-
symmetricTypeCheck(key);
|
|
39
|
+
symmetricTypeCheck(alg, key);
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
|
-
asymmetricTypeCheck(key, usage);
|
|
42
|
+
asymmetricTypeCheck(alg, key, usage);
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
export default checkKeyType;
|
|
@@ -76,6 +76,12 @@ export function checkSigCryptoKey(key, alg, ...usages) {
|
|
|
76
76
|
throw unusable('NODE-ED25519');
|
|
77
77
|
break;
|
|
78
78
|
}
|
|
79
|
+
case 'EdDSA': {
|
|
80
|
+
if (key.algorithm.name !== 'Ed25519' && key.algorithm.name !== 'Ed448') {
|
|
81
|
+
throw unusable('Ed25519 or Ed448');
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
79
85
|
case 'ES256':
|
|
80
86
|
case 'ES384':
|
|
81
87
|
case 'ES512': {
|
|
@@ -116,10 +122,17 @@ export function checkEncCryptoKey(key, alg, ...usages) {
|
|
|
116
122
|
throw unusable(expected, 'algorithm.length');
|
|
117
123
|
break;
|
|
118
124
|
}
|
|
119
|
-
case 'ECDH':
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
case 'ECDH': {
|
|
126
|
+
switch (key.algorithm.name) {
|
|
127
|
+
case 'ECDH':
|
|
128
|
+
case 'X25519':
|
|
129
|
+
case 'X448':
|
|
130
|
+
break;
|
|
131
|
+
default:
|
|
132
|
+
throw unusable('ECDH, X25519, or X448');
|
|
133
|
+
}
|
|
122
134
|
break;
|
|
135
|
+
}
|
|
123
136
|
case 'PBES2-HS256+A128KW':
|
|
124
137
|
case 'PBES2-HS384+A192KW':
|
|
125
138
|
case 'PBES2-HS512+A256KW':
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
let msg = 'Key must be ';
|
|
1
|
+
function message(msg, actual, ...types) {
|
|
3
2
|
if (types.length > 2) {
|
|
4
3
|
const last = types.pop();
|
|
5
4
|
msg += `one of type ${types.join(', ')}, or ${last}.`;
|
|
@@ -22,4 +21,10 @@ export default (actual, ...types) => {
|
|
|
22
21
|
}
|
|
23
22
|
}
|
|
24
23
|
return msg;
|
|
24
|
+
}
|
|
25
|
+
export default (actual, ...types) => {
|
|
26
|
+
return message('Key must be ', actual, ...types);
|
|
25
27
|
};
|
|
28
|
+
export function withAlg(alg, actual, ...types) {
|
|
29
|
+
return message(`Key for the ${alg} algorithm must be `, actual, ...types);
|
|
30
|
+
}
|
|
@@ -17,14 +17,16 @@ const checkCekLength = (enc, cek) => {
|
|
|
17
17
|
throw new JOSENotSupported(`Content Encryption Algorithm ${enc} is not supported either by JOSE or your javascript runtime`);
|
|
18
18
|
}
|
|
19
19
|
if (cek instanceof Uint8Array) {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const actual = cek.byteLength << 3;
|
|
21
|
+
if (actual !== expected) {
|
|
22
|
+
throw new JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`);
|
|
22
23
|
}
|
|
23
24
|
return;
|
|
24
25
|
}
|
|
25
26
|
if (isKeyObject(cek) && cek.type === 'secret') {
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
const actual = cek.symmetricKeySize << 3;
|
|
28
|
+
if (actual !== expected) {
|
|
29
|
+
throw new JWEInvalid(`Invalid Content Encryption Key length. Expected ${expected} bits, got ${actual} bits`);
|
|
28
30
|
}
|
|
29
31
|
return;
|
|
30
32
|
}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
* obtain a KeyObject from your existing key material.
|
|
21
21
|
*
|
|
22
22
|
* [CryptoKey](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey) is a representation of a
|
|
23
|
-
* key/secret available in the Browser and
|
|
24
|
-
* this library you may use the
|
|
23
|
+
* key/secret available in the Browser and Web-interoperable runtimes. In addition to the import
|
|
24
|
+
* functions of this library you may use the
|
|
25
25
|
* [SubtleCrypto.importKey](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey)
|
|
26
26
|
* API to obtain a CryptoKey from your existing key material.
|
|
27
27
|
*
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jose",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "'JSON Web Almost Everything' - JWA, JWS, JWE, JWT, JWK, JWKS
|
|
3
|
+
"version": "4.10.0",
|
|
4
|
+
"description": "'JSON Web Almost Everything' - JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, and other Web-interoperable runtimes",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"browser",
|
|
7
7
|
"cloudflare",
|