jose 4.1.1 → 4.1.5
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 +3 -0
- package/dist/browser/key/import.js +2 -1
- package/dist/browser/lib/crypto_key.js +1 -1
- package/dist/browser/runtime/asn1.js +2 -3
- package/dist/browser/runtime/base64url.js +2 -4
- package/dist/browser/runtime/check_key_length.js +0 -7
- package/dist/browser/runtime/env.js +11 -0
- package/dist/browser/runtime/fetch_jwks.js +3 -5
- package/dist/browser/runtime/generate.js +1 -1
- package/dist/browser/runtime/jwk_to_key.js +1 -1
- package/dist/browser/runtime/subtle_dsa.js +1 -1
- package/dist/browser/runtime/webcrypto.js +8 -4
- package/dist/node/cjs/key/import.js +2 -1
- package/dist/node/cjs/lib/crypto_key.js +3 -3
- package/dist/node/cjs/runtime/decrypt.js +8 -6
- package/dist/node/cjs/runtime/encrypt.js +2 -1
- package/dist/node/cjs/runtime/{global.js → env.js} +0 -0
- package/dist/node/cjs/runtime/sign.js +0 -4
- package/dist/node/esm/key/import.js +2 -1
- package/dist/node/esm/lib/crypto_key.js +1 -1
- package/dist/node/esm/runtime/decrypt.js +8 -6
- package/dist/node/esm/runtime/encrypt.js +2 -1
- package/dist/node/esm/runtime/{global.js → env.js} +0 -0
- package/dist/node/esm/runtime/sign.js +0 -4
- package/package.json +1 -1
- package/dist/browser/runtime/global.js +0 -27
package/README.md
CHANGED
|
@@ -65,6 +65,7 @@ If you or your business use `jose`, please consider becoming a [sponsor][support
|
|
|
65
65
|
- [Cloudflare Workers Support][]
|
|
66
66
|
- [Deno Support][]
|
|
67
67
|
- [Electron Support][]
|
|
68
|
+
- [Next.js Middleware / Vercel Edge Functions Support][]
|
|
68
69
|
- [Node.js Support][]
|
|
69
70
|
|
|
70
71
|
## FAQ
|
|
@@ -133,8 +134,10 @@ install size should not be a cause for concern.
|
|
|
133
134
|
[webcrypto]: https://www.w3.org/TR/WebCryptoAPI/
|
|
134
135
|
[nodewebcrypto]: https://nodejs.org/docs/latest-v15.x/api/webcrypto.html
|
|
135
136
|
[deno.land/x/jose]: https://deno.land/x/jose
|
|
137
|
+
|
|
136
138
|
[Browser Support]: https://github.com/panva/jose/issues/263
|
|
137
139
|
[Cloudflare Workers Support]: https://github.com/panva/jose/issues/265
|
|
138
140
|
[Deno Support]: https://github.com/panva/jose/issues/266
|
|
139
141
|
[Electron Support]: https://github.com/panva/jose/issues/264
|
|
142
|
+
[Next.js Middleware / Vercel Edge Functions Support]: https://github.com/panva/jose/issues/301
|
|
140
143
|
[Node.js Support]: https://github.com/panva/jose/issues/262
|
|
@@ -62,7 +62,8 @@ function parseElement(bytes) {
|
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
function spkiFromX509(buf) {
|
|
65
|
-
|
|
65
|
+
const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
|
|
66
|
+
return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw);
|
|
66
67
|
}
|
|
67
68
|
function getSPKI(x509) {
|
|
68
69
|
const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isCloudflareWorkers, isNodeJs } from '../runtime/
|
|
1
|
+
import { isCloudflareWorkers, isNodeJs } from '../runtime/env.js';
|
|
2
2
|
function unusable(name, prop = 'algorithm.name') {
|
|
3
3
|
return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
|
|
4
4
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { isCloudflareWorkers, isNodeJs } from './env.js';
|
|
2
2
|
import crypto, { isCryptoKey } from './webcrypto.js';
|
|
3
3
|
import invalidKeyInput from '../lib/invalid_key_input.js';
|
|
4
4
|
import { encodeBase64 } from './base64url.js';
|
|
@@ -54,8 +54,7 @@ const genericImport = async (replace, keyFormat, pem, alg, options) => {
|
|
|
54
54
|
var _a;
|
|
55
55
|
let algorithm;
|
|
56
56
|
let keyUsages;
|
|
57
|
-
const keyData = new Uint8Array(
|
|
58
|
-
.atob(pem.replace(replace, ''))
|
|
57
|
+
const keyData = new Uint8Array(atob(pem.replace(replace, ''))
|
|
59
58
|
.split('')
|
|
60
59
|
.map((c) => c.charCodeAt(0)));
|
|
61
60
|
const isPublic = keyFormat === 'spki';
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { encoder, decoder } from '../lib/buffer_utils.js';
|
|
2
|
-
import globalThis from './global.js';
|
|
3
2
|
export const encodeBase64 = (input) => {
|
|
4
3
|
let unencoded = input;
|
|
5
4
|
if (typeof unencoded === 'string') {
|
|
@@ -10,14 +9,13 @@ export const encodeBase64 = (input) => {
|
|
|
10
9
|
for (let i = 0; i < unencoded.length; i += CHUNK_SIZE) {
|
|
11
10
|
arr.push(String.fromCharCode.apply(null, unencoded.subarray(i, i + CHUNK_SIZE)));
|
|
12
11
|
}
|
|
13
|
-
return
|
|
12
|
+
return btoa(arr.join(''));
|
|
14
13
|
};
|
|
15
14
|
export const encode = (input) => {
|
|
16
15
|
return encodeBase64(input).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
|
17
16
|
};
|
|
18
17
|
export const decodeBase64 = (encoded) => {
|
|
19
|
-
return new Uint8Array(
|
|
20
|
-
.atob(encoded)
|
|
18
|
+
return new Uint8Array(atob(encoded)
|
|
21
19
|
.split('')
|
|
22
20
|
.map((c) => c.charCodeAt(0)));
|
|
23
21
|
};
|
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
export default (alg, key) => {
|
|
2
|
-
if (alg.startsWith('HS')) {
|
|
3
|
-
const bitlen = parseInt(alg.substr(-3), 10);
|
|
4
|
-
const { length } = key.algorithm;
|
|
5
|
-
if (typeof length !== 'number' || length < bitlen) {
|
|
6
|
-
throw new TypeError(`${alg} requires symmetric keys to be ${bitlen} bits or larger`);
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
2
|
if (alg.startsWith('RS') || alg.startsWith('PS')) {
|
|
10
3
|
const { modulusLength } = key.algorithm;
|
|
11
4
|
if (typeof modulusLength !== 'number' || modulusLength < 2048) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JOSEError, JWKSTimeout } from '../util/errors.js';
|
|
2
|
-
import
|
|
2
|
+
import { isCloudflareWorkers } from './env.js';
|
|
3
3
|
const fetchJwks = async (url, timeout) => {
|
|
4
4
|
let controller;
|
|
5
5
|
let id;
|
|
@@ -11,8 +11,7 @@ const fetchJwks = async (url, timeout) => {
|
|
|
11
11
|
controller.abort();
|
|
12
12
|
}, timeout);
|
|
13
13
|
}
|
|
14
|
-
const response = await
|
|
15
|
-
.fetch(url.href, {
|
|
14
|
+
const response = await fetch(url.href, {
|
|
16
15
|
signal: controller ? controller.signal : undefined,
|
|
17
16
|
redirect: 'manual',
|
|
18
17
|
method: 'GET',
|
|
@@ -23,8 +22,7 @@ const fetchJwks = async (url, timeout) => {
|
|
|
23
22
|
mode: 'cors',
|
|
24
23
|
}
|
|
25
24
|
: undefined),
|
|
26
|
-
})
|
|
27
|
-
.catch((err) => {
|
|
25
|
+
}).catch((err) => {
|
|
28
26
|
if (timedOut)
|
|
29
27
|
throw new JWKSTimeout();
|
|
30
28
|
throw err;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isCloudflareWorkers, isNodeJs } from './
|
|
1
|
+
import { isCloudflareWorkers, isNodeJs } from './env.js';
|
|
2
2
|
import { JOSENotSupported } from '../util/errors.js';
|
|
3
3
|
export default function subtleDsa(alg, namedCurve) {
|
|
4
4
|
const length = parseInt(alg.substr(-3), 10);
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
export default globalThis.crypto;
|
|
1
|
+
export default crypto;
|
|
3
2
|
export function isCryptoKey(key) {
|
|
4
|
-
|
|
3
|
+
try {
|
|
4
|
+
return (key != null &&
|
|
5
|
+
typeof key.extractable === 'boolean' &&
|
|
6
|
+
typeof key.algorithm.name === 'string' &&
|
|
7
|
+
typeof key.type === 'string');
|
|
8
|
+
}
|
|
9
|
+
catch (_a) {
|
|
5
10
|
return false;
|
|
6
11
|
}
|
|
7
|
-
return key != null && key instanceof globalThis.CryptoKey;
|
|
8
12
|
}
|
|
@@ -65,7 +65,8 @@ function parseElement(bytes) {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
function spkiFromX509(buf) {
|
|
68
|
-
|
|
68
|
+
const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
|
|
69
|
+
return (0, base64url_js_1.encodeBase64)(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw);
|
|
69
70
|
}
|
|
70
71
|
function getSPKI(x509) {
|
|
71
72
|
const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.checkEncCryptoKey = exports.checkSigCryptoKey = void 0;
|
|
4
|
-
const
|
|
4
|
+
const env_js_1 = require("../runtime/env.js");
|
|
5
5
|
function unusable(name, prop = 'algorithm.name') {
|
|
6
6
|
return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
|
|
7
7
|
}
|
|
@@ -74,12 +74,12 @@ function checkSigCryptoKey(key, alg, ...usages) {
|
|
|
74
74
|
throw unusable(`SHA-${expected}`, 'algorithm.hash');
|
|
75
75
|
break;
|
|
76
76
|
}
|
|
77
|
-
case (0,
|
|
77
|
+
case (0, env_js_1.isNodeJs)() && 'EdDSA': {
|
|
78
78
|
if (key.algorithm.name !== 'NODE-ED25519' && key.algorithm.name !== 'NODE-ED448')
|
|
79
79
|
throw unusable('NODE-ED25519 or NODE-ED448');
|
|
80
80
|
break;
|
|
81
81
|
}
|
|
82
|
-
case (0,
|
|
82
|
+
case (0, env_js_1.isCloudflareWorkers)() && 'EdDSA': {
|
|
83
83
|
if (!isAlgorithm(key.algorithm, 'NODE-ED25519'))
|
|
84
84
|
throw unusable('NODE-ED25519');
|
|
85
85
|
break;
|
|
@@ -36,8 +36,8 @@ async function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) {
|
|
|
36
36
|
}
|
|
37
37
|
let plaintext;
|
|
38
38
|
try {
|
|
39
|
-
const
|
|
40
|
-
plaintext = (0, buffer_utils_js_1.concat)(
|
|
39
|
+
const decipher = (0, crypto_1.createDecipheriv)(algorithm, encKey, iv);
|
|
40
|
+
plaintext = (0, buffer_utils_js_1.concat)(decipher.update(ciphertext), decipher.final());
|
|
41
41
|
}
|
|
42
42
|
catch {
|
|
43
43
|
}
|
|
@@ -53,12 +53,14 @@ async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) {
|
|
|
53
53
|
throw new errors_js_1.JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`);
|
|
54
54
|
}
|
|
55
55
|
try {
|
|
56
|
-
const
|
|
57
|
-
|
|
56
|
+
const decipher = (0, crypto_1.createDecipheriv)(algorithm, cek, iv, { authTagLength: 16 });
|
|
57
|
+
decipher.setAuthTag(tag);
|
|
58
58
|
if (aad.byteLength) {
|
|
59
|
-
|
|
59
|
+
decipher.setAAD(aad, { plaintextLength: ciphertext.length });
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
const plaintext = decipher.update(ciphertext);
|
|
62
|
+
decipher.final();
|
|
63
|
+
return plaintext;
|
|
62
64
|
}
|
|
63
65
|
catch {
|
|
64
66
|
throw new errors_js_1.JWEDecryptionFailed();
|
|
@@ -38,7 +38,8 @@ async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
|
|
|
38
38
|
if (aad.byteLength) {
|
|
39
39
|
cipher.setAAD(aad, { plaintextLength: plaintext.length });
|
|
40
40
|
}
|
|
41
|
-
const ciphertext =
|
|
41
|
+
const ciphertext = cipher.update(plaintext);
|
|
42
|
+
cipher.final();
|
|
42
43
|
const tag = cipher.getAuthTag();
|
|
43
44
|
return { ciphertext, tag };
|
|
44
45
|
}
|
|
File without changes
|
|
@@ -16,10 +16,6 @@ else {
|
|
|
16
16
|
const sign = async (alg, key, data) => {
|
|
17
17
|
const keyObject = (0, get_sign_verify_key_js_1.default)(alg, key, 'sign');
|
|
18
18
|
if (alg.startsWith('HS')) {
|
|
19
|
-
const bitlen = parseInt(alg.substr(-3), 10);
|
|
20
|
-
if (!keyObject.symmetricKeySize || keyObject.symmetricKeySize << 3 < bitlen) {
|
|
21
|
-
throw new TypeError(`${alg} requires symmetric keys to be ${bitlen} bits or larger`);
|
|
22
|
-
}
|
|
23
19
|
const hmac = crypto.createHmac((0, hmac_digest_js_1.default)(alg), keyObject);
|
|
24
20
|
hmac.update(data);
|
|
25
21
|
return hmac.digest();
|
|
@@ -62,7 +62,8 @@ function parseElement(bytes) {
|
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
function spkiFromX509(buf) {
|
|
65
|
-
|
|
65
|
+
const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
|
|
66
|
+
return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw);
|
|
66
67
|
}
|
|
67
68
|
function getSPKI(x509) {
|
|
68
69
|
const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isCloudflareWorkers, isNodeJs } from '../runtime/
|
|
1
|
+
import { isCloudflareWorkers, isNodeJs } from '../runtime/env.js';
|
|
2
2
|
function unusable(name, prop = 'algorithm.name') {
|
|
3
3
|
return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
|
|
4
4
|
}
|
|
@@ -34,8 +34,8 @@ async function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) {
|
|
|
34
34
|
}
|
|
35
35
|
let plaintext;
|
|
36
36
|
try {
|
|
37
|
-
const
|
|
38
|
-
plaintext = concat(
|
|
37
|
+
const decipher = createDecipheriv(algorithm, encKey, iv);
|
|
38
|
+
plaintext = concat(decipher.update(ciphertext), decipher.final());
|
|
39
39
|
}
|
|
40
40
|
catch {
|
|
41
41
|
}
|
|
@@ -51,12 +51,14 @@ async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) {
|
|
|
51
51
|
throw new JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`);
|
|
52
52
|
}
|
|
53
53
|
try {
|
|
54
|
-
const
|
|
55
|
-
|
|
54
|
+
const decipher = createDecipheriv(algorithm, cek, iv, { authTagLength: 16 });
|
|
55
|
+
decipher.setAuthTag(tag);
|
|
56
56
|
if (aad.byteLength) {
|
|
57
|
-
|
|
57
|
+
decipher.setAAD(aad, { plaintextLength: ciphertext.length });
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
const plaintext = decipher.update(ciphertext);
|
|
60
|
+
decipher.final();
|
|
61
|
+
return plaintext;
|
|
60
62
|
}
|
|
61
63
|
catch {
|
|
62
64
|
throw new JWEDecryptionFailed();
|
|
@@ -36,7 +36,8 @@ async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
|
|
|
36
36
|
if (aad.byteLength) {
|
|
37
37
|
cipher.setAAD(aad, { plaintextLength: plaintext.length });
|
|
38
38
|
}
|
|
39
|
-
const ciphertext =
|
|
39
|
+
const ciphertext = cipher.update(plaintext);
|
|
40
|
+
cipher.final();
|
|
40
41
|
const tag = cipher.getAuthTag();
|
|
41
42
|
return { ciphertext, tag };
|
|
42
43
|
}
|
|
File without changes
|
|
@@ -14,10 +14,6 @@ else {
|
|
|
14
14
|
const sign = async (alg, key, data) => {
|
|
15
15
|
const keyObject = getSignKey(alg, key, 'sign');
|
|
16
16
|
if (alg.startsWith('HS')) {
|
|
17
|
-
const bitlen = parseInt(alg.substr(-3), 10);
|
|
18
|
-
if (!keyObject.symmetricKeySize || keyObject.symmetricKeySize << 3 < bitlen) {
|
|
19
|
-
throw new TypeError(`${alg} requires symmetric keys to be ${bitlen} bits or larger`);
|
|
20
|
-
}
|
|
21
17
|
const hmac = crypto.createHmac(hmacDigest(alg), keyObject);
|
|
22
18
|
hmac.update(data);
|
|
23
19
|
return hmac.digest();
|
package/package.json
CHANGED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
function getGlobal() {
|
|
2
|
-
if (typeof globalThis !== 'undefined')
|
|
3
|
-
return globalThis;
|
|
4
|
-
if (typeof self !== 'undefined')
|
|
5
|
-
return self;
|
|
6
|
-
if (typeof window !== 'undefined')
|
|
7
|
-
return window;
|
|
8
|
-
throw new Error('unable to locate global object');
|
|
9
|
-
}
|
|
10
|
-
export default getGlobal();
|
|
11
|
-
export function isCloudflareWorkers() {
|
|
12
|
-
try {
|
|
13
|
-
return getGlobal().WebSocketPair !== undefined;
|
|
14
|
-
}
|
|
15
|
-
catch (_a) {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
export function isNodeJs() {
|
|
20
|
-
var _a, _b;
|
|
21
|
-
try {
|
|
22
|
-
return ((_b = (_a = getGlobal().process) === null || _a === void 0 ? void 0 : _a.versions) === null || _b === void 0 ? void 0 : _b.node) !== undefined;
|
|
23
|
-
}
|
|
24
|
-
catch (_c) {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
}
|