jose 6.2.9 → 6.2.11
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/dist/types/jwe/compact/decrypt.d.ts +7 -16
- package/dist/types/jwe/compact/encrypt.d.ts +6 -17
- package/dist/types/jwe/flattened/decrypt.d.ts +7 -16
- package/dist/types/jwe/flattened/encrypt.d.ts +11 -34
- package/dist/types/jwe/general/decrypt.d.ts +7 -16
- package/dist/types/jwe/general/encrypt.d.ts +15 -35
- package/dist/types/jwk/embedded.d.ts +3 -8
- package/dist/types/jwk/thumbprint.d.ts +5 -5
- package/dist/types/jwks/local.d.ts +6 -10
- package/dist/types/jwks/remote.d.ts +17 -28
- package/dist/types/jws/compact/sign.d.ts +5 -10
- package/dist/types/jws/compact/verify.d.ts +9 -18
- package/dist/types/jws/flattened/sign.d.ts +7 -16
- package/dist/types/jws/flattened/verify.d.ts +9 -18
- package/dist/types/jws/general/sign.d.ts +11 -21
- package/dist/types/jws/general/verify.d.ts +12 -21
- package/dist/types/jwt/decrypt.d.ts +10 -20
- package/dist/types/jwt/encrypt.d.ts +13 -26
- package/dist/types/jwt/sign.d.ts +12 -19
- package/dist/types/jwt/unsecured.d.ts +17 -28
- package/dist/types/jwt/verify.d.ts +9 -19
- package/dist/types/key/export.d.ts +4 -4
- package/dist/types/key/generate_key_pair.d.ts +6 -8
- package/dist/types/key/generate_secret.d.ts +4 -11
- package/dist/types/key/import.d.ts +10 -17
- package/dist/types/types.d.ts +44 -84
- package/dist/types/util/base64url.d.ts +2 -2
- package/dist/types/util/decode_jwt.d.ts +3 -10
- package/dist/types/util/decode_protected_header.d.ts +4 -4
- package/dist/types/util/errors.d.ts +26 -53
- package/dist/webapi/index.js +70 -32
- package/dist/webapi/jwe/compact/decrypt.js +7 -8
- package/dist/webapi/jwe/compact/encrypt.js +42 -26
- package/dist/webapi/jwe/flattened/decrypt.js +14 -10
- package/dist/webapi/jwe/flattened/encrypt.js +53 -71
- package/dist/webapi/jwe/general/decrypt.js +38 -49
- package/dist/webapi/jwe/general/encrypt.js +121 -162
- package/dist/webapi/jwk/embedded.js +30 -18
- package/dist/webapi/jwk/thumbprint.js +54 -64
- package/dist/webapi/jwks/local.js +49 -74
- package/dist/webapi/jwks/remote.js +91 -160
- package/dist/webapi/jws/compact/sign.js +21 -20
- package/dist/webapi/jws/compact/verify.js +7 -8
- package/dist/webapi/jws/flattened/sign.js +29 -30
- package/dist/webapi/jws/flattened/verify.js +21 -23
- package/dist/webapi/jws/general/sign.js +61 -70
- package/dist/webapi/jws/general/verify.js +47 -41
- package/dist/webapi/jwt/decrypt.js +13 -22
- package/dist/webapi/jwt/encrypt.js +58 -100
- package/dist/webapi/jwt/sign.js +18 -50
- package/dist/webapi/jwt/unsecured.js +34 -63
- package/dist/webapi/jwt/verify.js +12 -14
- package/dist/webapi/key/export.js +32 -37
- package/dist/webapi/key/generate_key_pair.js +40 -44
- package/dist/webapi/key/generate_secret.js +35 -39
- package/dist/webapi/key/import.js +52 -52
- package/dist/webapi/lib/asn1.js +130 -164
- package/dist/webapi/lib/base64.js +18 -20
- package/dist/webapi/lib/buffer_utils.js +34 -38
- package/dist/webapi/lib/content_encryption.js +79 -123
- package/dist/webapi/lib/crypto_key.js +24 -25
- package/dist/webapi/lib/deflate.js +46 -40
- package/dist/webapi/lib/helpers.js +40 -37
- package/dist/webapi/lib/invalid_key_input.js +10 -24
- package/dist/webapi/lib/is_key_like.js +17 -15
- package/dist/webapi/lib/jwe_algorithms.js +106 -93
- package/dist/webapi/lib/jwe_decrypt.js +210 -176
- package/dist/webapi/lib/jwe_encrypt.js +104 -110
- package/dist/webapi/lib/jwk_metadata.js +19 -0
- package/dist/webapi/lib/jwk_to_key.js +9 -16
- package/dist/webapi/lib/jws_algorithms.js +62 -61
- package/dist/webapi/lib/jws_sign.js +58 -72
- package/dist/webapi/lib/jws_verify.js +90 -103
- package/dist/webapi/lib/jwt_claims_set.js +134 -185
- package/dist/webapi/lib/key.js +84 -121
- package/dist/webapi/lib/key_algorithm.js +13 -8
- package/dist/webapi/lib/key_descriptor.js +8 -6
- package/dist/webapi/lib/key_management.js +200 -225
- package/dist/webapi/lib/key_options.js +8 -0
- package/dist/webapi/lib/options.js +57 -44
- package/dist/webapi/lib/signing.js +18 -22
- package/dist/webapi/lib/type_checks.js +28 -33
- package/dist/webapi/util/base64url.js +26 -35
- package/dist/webapi/util/decode_jwt.js +32 -31
- package/dist/webapi/util/decode_protected_header.js +18 -23
- package/dist/webapi/util/errors.js +94 -84
- package/package.json +17 -81
|
@@ -1,170 +1,129 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { checkDisjoint, checkEncryptHeaders, encryptJWE } from '../../lib/jwe_encrypt.js';
|
|
9
|
-
import { prepareKey } from '../../lib/key.js';
|
|
10
|
-
import { jweAlgorithm } from '../../lib/jwe_algorithms.js';
|
|
1
|
+
import { assertNotSet } from "../../lib/helpers.js";
|
|
2
|
+
import { JWEInvalid } from "../../util/errors.js";
|
|
3
|
+
import { generateCek } from "../../lib/content_encryption.js";
|
|
4
|
+
import { encode as b64u } from "../../util/base64url.js";
|
|
5
|
+
import { checkDisjoint, checkEncryptHeaders, createJWE, encryptJWE, transportCek } from "../../lib/jwe_encrypt.js";
|
|
6
|
+
import { JWE, isJWECEKTransport, jweAlgorithm } from "../../lib/jwe_algorithms.js";
|
|
7
|
+
import { assertUint8Array } from "../../lib/type_checks.js";
|
|
11
8
|
class IndividualRecipient {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
setUnprotectedHeader
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return this.#parent.encrypt(...args);
|
|
33
|
-
}
|
|
34
|
-
done() {
|
|
35
|
-
return this.#parent;
|
|
36
|
-
}
|
|
9
|
+
#parent;
|
|
10
|
+
state;
|
|
11
|
+
constructor(enc, key, crit) {
|
|
12
|
+
this.#parent = enc, this.state = [void 0, void 0, key, crit];
|
|
13
|
+
}
|
|
14
|
+
setUnprotectedHeader(unprotectedHeader) {
|
|
15
|
+
return assertNotSet(this.state[0], "setUnprotectedHeader"), this.state[0] = unprotectedHeader, this;
|
|
16
|
+
}
|
|
17
|
+
setKeyManagementParameters(parameters) {
|
|
18
|
+
return assertNotSet(this.state[1], "setKeyManagementParameters"), this.state[1] = parameters, this;
|
|
19
|
+
}
|
|
20
|
+
addRecipient(...args) {
|
|
21
|
+
return this.#parent.addRecipient(...args);
|
|
22
|
+
}
|
|
23
|
+
encrypt(...args) {
|
|
24
|
+
return this.#parent.encrypt(...args);
|
|
25
|
+
}
|
|
26
|
+
done() {
|
|
27
|
+
return this.#parent;
|
|
28
|
+
}
|
|
37
29
|
}
|
|
38
30
|
function copyOptionalMembers(flattened, jwe, recipient) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
jwe.aad = aad;
|
|
42
|
-
if (protectedHeader)
|
|
43
|
-
jwe.protected = protectedHeader;
|
|
44
|
-
if (unprotected)
|
|
45
|
-
jwe.unprotected = unprotected;
|
|
46
|
-
if (header)
|
|
47
|
-
recipient.header = header;
|
|
31
|
+
const { aad, protected: protectedHeader, unprotected, header } = flattened;
|
|
32
|
+
aad && (jwe.aad = aad), protectedHeader && (jwe.protected = protectedHeader), unprotected && (jwe.unprotected = unprotected), header && (recipient.header = header);
|
|
48
33
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
34
|
+
class GeneralEncrypt {
|
|
35
|
+
#plaintext;
|
|
36
|
+
#recipients = [];
|
|
37
|
+
#protectedHeader;
|
|
38
|
+
#unprotectedHeader;
|
|
39
|
+
#aad;
|
|
40
|
+
constructor(plaintext) {
|
|
41
|
+
this.#plaintext = plaintext;
|
|
42
|
+
}
|
|
43
|
+
addRecipient(key, options) {
|
|
44
|
+
const recipient = new IndividualRecipient(this, key, options?.crit);
|
|
45
|
+
return this.#recipients.push(recipient), recipient;
|
|
46
|
+
}
|
|
47
|
+
setProtectedHeader(protectedHeader) {
|
|
48
|
+
return assertNotSet(this.#protectedHeader, "setProtectedHeader"), this.#protectedHeader = protectedHeader, this;
|
|
49
|
+
}
|
|
50
|
+
setSharedUnprotectedHeader(sharedUnprotectedHeader) {
|
|
51
|
+
return assertNotSet(this.#unprotectedHeader, "setSharedUnprotectedHeader"), this.#unprotectedHeader = sharedUnprotectedHeader, this;
|
|
52
|
+
}
|
|
53
|
+
setAdditionalAuthenticatedData(aad) {
|
|
54
|
+
return this.#aad = aad, this;
|
|
55
|
+
}
|
|
56
|
+
async encrypt() {
|
|
57
|
+
if (!this.#recipients.length)
|
|
58
|
+
throw new JWEInvalid("at least one recipient must be added");
|
|
59
|
+
if (assertUint8Array(this.#plaintext, "plaintext"), this.#recipients.length === 1) {
|
|
60
|
+
const [unprotectedHeader, keyManagementParameters, key, crit] = this.#recipients[0].state, flattened = await createJWE([
|
|
61
|
+
this.#plaintext,
|
|
62
|
+
this.#protectedHeader,
|
|
63
|
+
unprotectedHeader,
|
|
64
|
+
this.#unprotectedHeader,
|
|
65
|
+
this.#aad,
|
|
66
|
+
void 0,
|
|
67
|
+
void 0,
|
|
68
|
+
keyManagementParameters,
|
|
69
|
+
crit,
|
|
70
|
+
!1
|
|
71
|
+
], key), jwe2 = {
|
|
72
|
+
ciphertext: flattened.ciphertext,
|
|
73
|
+
recipients: [{}]
|
|
74
|
+
};
|
|
75
|
+
return flattened.iv && (jwe2.iv = flattened.iv), flattened.tag && (jwe2.tag = flattened.tag), flattened.encrypted_key && (jwe2.recipients[0].encrypted_key = flattened.encrypted_key), copyOptionalMembers(flattened, jwe2, jwe2.recipients[0]), jwe2;
|
|
72
76
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
let enc, protectedHeader = this.#protectedHeader, sharedUnprotectedHeader = this.#unprotectedHeader;
|
|
78
|
+
const inputs = [], checked = [];
|
|
79
|
+
for (let i = 0; i < this.#recipients.length; i++) {
|
|
80
|
+
const recipient = this.#recipients[i], [unprotectedHeader, keyManagementParameters, , crit] = recipient.state, input = [
|
|
81
|
+
this.#plaintext,
|
|
82
|
+
protectedHeader,
|
|
83
|
+
unprotectedHeader,
|
|
84
|
+
sharedUnprotectedHeader,
|
|
85
|
+
this.#aad,
|
|
86
|
+
void 0,
|
|
87
|
+
void 0,
|
|
88
|
+
keyManagementParameters,
|
|
89
|
+
crit,
|
|
90
|
+
!0
|
|
91
|
+
], headers = checkEncryptHeaders(input);
|
|
92
|
+
inputs.push(input), checked.push(headers), i === 0 && (protectedHeader = input[1], sharedUnprotectedHeader = input[3]);
|
|
93
|
+
const algEntry = JWE[headers[1]];
|
|
94
|
+
if (algEntry && !isJWECEKTransport(algEntry))
|
|
95
|
+
throw new JWEInvalid(`"${headers[1]}" alg may only have a single recipient`);
|
|
96
|
+
if (!enc)
|
|
97
|
+
enc = headers[2];
|
|
98
|
+
else if (enc !== headers[2])
|
|
99
|
+
throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients');
|
|
76
100
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
};
|
|
100
|
-
if (flattened.encrypted_key)
|
|
101
|
-
jwe.recipients[0].encrypted_key = flattened.encrypted_key;
|
|
102
|
-
copyOptionalMembers(flattened, jwe, jwe.recipients[0]);
|
|
103
|
-
return jwe;
|
|
104
|
-
}
|
|
105
|
-
validateCritDuplicates(JWEInvalid, this.#protectedHeader);
|
|
106
|
-
let enc;
|
|
107
|
-
const inputs = [];
|
|
108
|
-
const checked = [];
|
|
109
|
-
for (let i = 0; i < this.#recipients.length; i++) {
|
|
110
|
-
const recipient = this.#recipients[i];
|
|
111
|
-
const [unprotectedHeader, keyManagementParameters, , crit] = recipient.state;
|
|
112
|
-
const input = [
|
|
113
|
-
this.#plaintext,
|
|
114
|
-
this.#protectedHeader,
|
|
115
|
-
unprotectedHeader,
|
|
116
|
-
this.#unprotectedHeader,
|
|
117
|
-
this.#aad,
|
|
118
|
-
undefined,
|
|
119
|
-
undefined,
|
|
120
|
-
keyManagementParameters,
|
|
121
|
-
crit,
|
|
122
|
-
true,
|
|
123
|
-
];
|
|
124
|
-
const headers = checkEncryptHeaders(input);
|
|
125
|
-
inputs.push(input);
|
|
126
|
-
checked.push(headers);
|
|
127
|
-
if (headers[1] === 'dir' || headers[1] === 'ECDH-ES') {
|
|
128
|
-
throw new JWEInvalid(`"${headers[1]}" alg may only have a single recipient`);
|
|
129
|
-
}
|
|
130
|
-
if (!enc) {
|
|
131
|
-
enc = headers[2];
|
|
132
|
-
}
|
|
133
|
-
else if (enc !== headers[2]) {
|
|
134
|
-
throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients');
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
const cek = generateCek(checked[0][3]);
|
|
138
|
-
const jwe = {
|
|
139
|
-
ciphertext: '',
|
|
140
|
-
recipients: [],
|
|
141
|
-
};
|
|
142
|
-
for (let i = 0; i < this.#recipients.length; i++) {
|
|
143
|
-
const recipient = this.#recipients[i];
|
|
144
|
-
const [unprotectedHeader, keyManagementParameters, key] = recipient.state;
|
|
145
|
-
const target = {};
|
|
146
|
-
jwe.recipients.push(target);
|
|
147
|
-
if (i === 0) {
|
|
148
|
-
inputs[0][5] = cek;
|
|
149
|
-
const flattened = await encryptJWE(inputs[0], checked[0], key);
|
|
150
|
-
jwe.ciphertext = flattened.ciphertext;
|
|
151
|
-
jwe.iv = flattened.iv;
|
|
152
|
-
jwe.tag = flattened.tag;
|
|
153
|
-
target.encrypted_key = flattened.encrypted_key;
|
|
154
|
-
copyOptionalMembers(flattened, jwe, target);
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
const [, alg, , encEntry] = checked[i];
|
|
158
|
-
const k = await prepareKey(jweAlgorithm(alg), key, 'encrypt');
|
|
159
|
-
const [, encryptedKey, parameters] = await encryptKeyManagement(alg, encEntry, k, cek, keyManagementParameters);
|
|
160
|
-
target.encrypted_key = b64u(encryptedKey);
|
|
161
|
-
if (unprotectedHeader || parameters) {
|
|
162
|
-
const header = { ...unprotectedHeader, ...parameters };
|
|
163
|
-
if (parameters)
|
|
164
|
-
checkDisjoint(this.#protectedHeader, header, this.#unprotectedHeader);
|
|
165
|
-
target.header = header;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return jwe;
|
|
101
|
+
const algEntries = checked.map(([, alg]) => {
|
|
102
|
+
const algEntry = jweAlgorithm(alg);
|
|
103
|
+
if (!isJWECEKTransport(algEntry))
|
|
104
|
+
throw new JWEInvalid(`"${alg}" alg may only have a single recipient`);
|
|
105
|
+
return algEntry;
|
|
106
|
+
}), cek = generateCek(checked[0][3]), jwe = {
|
|
107
|
+
ciphertext: "",
|
|
108
|
+
recipients: []
|
|
109
|
+
};
|
|
110
|
+
for (let i = 0; i < this.#recipients.length; i++) {
|
|
111
|
+
const recipient = this.#recipients[i], [, keyManagementParameters, key] = recipient.state, target = {};
|
|
112
|
+
if (jwe.recipients.push(target), i === 0) {
|
|
113
|
+
inputs[0][5] = cek;
|
|
114
|
+
const flattened = await encryptJWE(inputs[0], checked[0], key, algEntries[0]);
|
|
115
|
+
jwe.ciphertext = flattened.ciphertext, flattened.iv && (jwe.iv = flattened.iv), flattened.tag && (jwe.tag = flattened.tag), flattened.encrypted_key && (target.encrypted_key = flattened.encrypted_key), copyOptionalMembers(flattened, jwe, target);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const [joseHeader, , , encEntry] = checked[i], unprotectedHeader = inputs[i][2], [, encryptedKey, parameters] = await transportCek(algEntries[i], encEntry, key, cek, joseHeader, keyManagementParameters);
|
|
119
|
+
if (target.encrypted_key = b64u(encryptedKey), unprotectedHeader || parameters) {
|
|
120
|
+
const header = { ...unprotectedHeader, ...parameters };
|
|
121
|
+
parameters && checkDisjoint(inputs[i][1], header, inputs[i][3]), target.header = header;
|
|
122
|
+
}
|
|
169
123
|
}
|
|
124
|
+
return jwe;
|
|
125
|
+
}
|
|
170
126
|
}
|
|
127
|
+
export {
|
|
128
|
+
GeneralEncrypt
|
|
129
|
+
};
|
|
@@ -1,19 +1,31 @@
|
|
|
1
|
-
import { jwkToKey } from
|
|
2
|
-
import { jwsAlgorithm } from
|
|
3
|
-
import { isObject } from
|
|
4
|
-
import { JWSInvalid } from
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
1
|
+
import { jwkToKey } from "../lib/jwk_to_key.js";
|
|
2
|
+
import { jwsAlgorithm } from "../lib/jws_algorithms.js";
|
|
3
|
+
import { isObject } from "../lib/type_checks.js";
|
|
4
|
+
import { JWSInvalid } from "../util/errors.js";
|
|
5
|
+
import { normalizeJwk } from "../lib/jwk_metadata.js";
|
|
6
|
+
async function EmbeddedJWK(protectedHeader, token) {
|
|
7
|
+
const joseHeader = {
|
|
8
|
+
...protectedHeader,
|
|
9
|
+
...token?.header
|
|
10
|
+
};
|
|
11
|
+
if (!isObject(joseHeader.jwk))
|
|
12
|
+
throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a JSON object');
|
|
13
|
+
let jwk;
|
|
14
|
+
try {
|
|
15
|
+
jwk = normalizeJwk(joseHeader.jwk);
|
|
16
|
+
} catch (cause) {
|
|
17
|
+
throw new JWSInvalid("Invalid Embedded JWK", { cause });
|
|
18
|
+
}
|
|
19
|
+
const entry = jwsAlgorithm(joseHeader.alg);
|
|
20
|
+
if (jwk.use !== void 0 && jwk.use !== "sig")
|
|
21
|
+
throw new JWSInvalid('Invalid Embedded JWK, its "use" must be "sig" when present');
|
|
22
|
+
if (jwk.alg !== void 0 && jwk.alg !== entry.alg)
|
|
23
|
+
throw new JWSInvalid(`Invalid Embedded JWK, its "alg" must be "${entry.alg}" when present`);
|
|
24
|
+
const key = await jwkToKey(entry, { ...jwk, ext: !0 });
|
|
25
|
+
if (key.type !== "public")
|
|
26
|
+
throw new JWSInvalid('"jwk" (JSON Web Key) Header Parameter must be a public key');
|
|
27
|
+
return key;
|
|
19
28
|
}
|
|
29
|
+
export {
|
|
30
|
+
EmbeddedJWK
|
|
31
|
+
};
|
|
@@ -1,68 +1,58 @@
|
|
|
1
|
-
import { digest } from
|
|
2
|
-
import { encode as b64u } from
|
|
3
|
-
import { JOSENotSupported, JWKInvalid } from
|
|
4
|
-
import { encode } from
|
|
5
|
-
import { isKeyLike } from
|
|
6
|
-
import {
|
|
7
|
-
import { exportJWK } from
|
|
8
|
-
import { invalidKeyInput } from
|
|
1
|
+
import { digest } from "../lib/helpers.js";
|
|
2
|
+
import { encode as b64u } from "../util/base64url.js";
|
|
3
|
+
import { JOSENotSupported, JWKInvalid } from "../util/errors.js";
|
|
4
|
+
import { encode } from "../lib/buffer_utils.js";
|
|
5
|
+
import { isKeyLike } from "../lib/is_key_like.js";
|
|
6
|
+
import { isObject } from "../lib/type_checks.js";
|
|
7
|
+
import { exportJWK } from "../key/export.js";
|
|
8
|
+
import { invalidKeyInput } from "../lib/invalid_key_input.js";
|
|
9
|
+
import { snapshotJwk } from "../lib/jwk_metadata.js";
|
|
9
10
|
const check = (value, description) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
11
|
+
if (typeof value != "string" || !value)
|
|
12
|
+
throw new JWKInvalid(`${description} missing or invalid`);
|
|
13
13
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
case 'RSA':
|
|
50
|
-
check(jwk.e, '"e" (Exponent) Parameter');
|
|
51
|
-
check(jwk.n, '"n" (Modulus) Parameter');
|
|
52
|
-
components = { e: jwk.e, kty: jwk.kty, n: jwk.n };
|
|
53
|
-
break;
|
|
54
|
-
case 'oct':
|
|
55
|
-
check(jwk.k, '"k" (Key Value) Parameter');
|
|
56
|
-
components = { k: jwk.k, kty: jwk.kty };
|
|
57
|
-
break;
|
|
58
|
-
default:
|
|
59
|
-
throw new JOSENotSupported('"kty" (Key Type) Parameter missing or unsupported');
|
|
60
|
-
}
|
|
61
|
-
const data = encode(JSON.stringify(components));
|
|
62
|
-
return b64u(await digest(digestAlgorithm, data));
|
|
14
|
+
async function calculateJwkThumbprint(key, digestAlgorithm) {
|
|
15
|
+
let jwk;
|
|
16
|
+
if (isObject(key)) {
|
|
17
|
+
if (jwk = snapshotJwk(key), typeof jwk.kty != "string")
|
|
18
|
+
throw new TypeError(invalidKeyInput(key, "CryptoKey", "KeyObject", "JSON Web Key"));
|
|
19
|
+
} else if (isKeyLike(key))
|
|
20
|
+
jwk = snapshotJwk(await exportJWK(key));
|
|
21
|
+
else
|
|
22
|
+
throw new TypeError(invalidKeyInput(key, "CryptoKey", "KeyObject", "JSON Web Key"));
|
|
23
|
+
if (digestAlgorithm ??= "sha256", digestAlgorithm !== "sha256" && digestAlgorithm !== "sha384" && digestAlgorithm !== "sha512")
|
|
24
|
+
throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');
|
|
25
|
+
let components;
|
|
26
|
+
switch (jwk.kty) {
|
|
27
|
+
case "AKP":
|
|
28
|
+
check(jwk.alg, '"alg" (Algorithm) Parameter'), check(jwk.pub, '"pub" (Public key) Parameter'), components = { alg: jwk.alg, kty: jwk.kty, pub: jwk.pub };
|
|
29
|
+
break;
|
|
30
|
+
case "EC":
|
|
31
|
+
check(jwk.crv, '"crv" (Curve) Parameter'), check(jwk.x, '"x" (X Coordinate) Parameter'), check(jwk.y, '"y" (Y Coordinate) Parameter'), components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y };
|
|
32
|
+
break;
|
|
33
|
+
case "OKP":
|
|
34
|
+
check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter'), check(jwk.x, '"x" (Public Key) Parameter'), components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x };
|
|
35
|
+
break;
|
|
36
|
+
case "RSA":
|
|
37
|
+
check(jwk.e, '"e" (Exponent) Parameter'), check(jwk.n, '"n" (Modulus) Parameter'), components = { e: jwk.e, kty: jwk.kty, n: jwk.n };
|
|
38
|
+
break;
|
|
39
|
+
case "oct":
|
|
40
|
+
if (typeof jwk.k != "string")
|
|
41
|
+
throw new JWKInvalid('"k" (Key Value) Parameter missing or invalid');
|
|
42
|
+
components = { k: jwk.k, kty: jwk.kty };
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
throw new JOSENotSupported('"kty" (Key Type) Parameter missing or unsupported');
|
|
46
|
+
}
|
|
47
|
+
const data = encode(JSON.stringify(components));
|
|
48
|
+
return b64u(await digest(digestAlgorithm, data));
|
|
63
49
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
50
|
+
async function calculateJwkThumbprintUri(key, digestAlgorithm) {
|
|
51
|
+
digestAlgorithm ??= "sha256";
|
|
52
|
+
const thumbprint = await calculateJwkThumbprint(key, digestAlgorithm);
|
|
53
|
+
return `urn:ietf:params:oauth:jwk-thumbprint:sha-${digestAlgorithm.slice(-3)}:${thumbprint}`;
|
|
68
54
|
}
|
|
55
|
+
export {
|
|
56
|
+
calculateJwkThumbprint,
|
|
57
|
+
calculateJwkThumbprintUri
|
|
58
|
+
};
|
|
@@ -1,78 +1,53 @@
|
|
|
1
|
-
import { jwkToKey } from
|
|
2
|
-
import { JWS } from
|
|
3
|
-
import { JWKSInvalid, JOSENotSupported, JWKSNoMatchingKey, JWKSMultipleMatchingKeys
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
return entry;
|
|
11
|
-
}
|
|
12
|
-
function isJWKSLike(jwks) {
|
|
13
|
-
if (!jwks || typeof jwks !== 'object') {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
const { keys } = jwks;
|
|
17
|
-
return Array.isArray(keys) && keys.every((isObject));
|
|
18
|
-
}
|
|
19
|
-
class LocalJWKSetImpl {
|
|
20
|
-
#jwks;
|
|
21
|
-
#cached = new WeakMap();
|
|
22
|
-
constructor(jwks) {
|
|
23
|
-
if (!isJWKSLike(jwks)) {
|
|
24
|
-
throw new JWKSInvalid('JSON Web Key Set malformed');
|
|
25
|
-
}
|
|
26
|
-
this.#jwks = structuredClone(jwks);
|
|
27
|
-
}
|
|
28
|
-
jwks() {
|
|
29
|
-
return this.#jwks;
|
|
30
|
-
}
|
|
31
|
-
async getKey(protectedHeader, token) {
|
|
32
|
-
const { alg, kid } = { ...protectedHeader, ...token?.header };
|
|
33
|
-
const entry = signatureAlgorithm(alg);
|
|
34
|
-
const candidates = this.#jwks.keys.filter((jwk) => entry.kty.includes(jwk.kty) &&
|
|
35
|
-
(typeof kid !== 'string' || kid === jwk.kid) &&
|
|
36
|
-
(!(typeof jwk.alg === 'string' || jwk.kty === 'AKP') || alg === jwk.alg) &&
|
|
37
|
-
(typeof jwk.use !== 'string' || jwk.use === 'sig') &&
|
|
38
|
-
(!Array.isArray(jwk.key_ops) || jwk.key_ops.includes('verify')) &&
|
|
39
|
-
(!entry.crv || jwk.crv === entry.crv));
|
|
40
|
-
const { 0: jwk, length } = candidates;
|
|
41
|
-
if (length === 0) {
|
|
42
|
-
throw new JWKSNoMatchingKey();
|
|
43
|
-
}
|
|
44
|
-
if (length !== 1) {
|
|
45
|
-
const error = new JWKSMultipleMatchingKeys();
|
|
46
|
-
const _cached = this.#cached;
|
|
47
|
-
error[Symbol.asyncIterator] = async function* () {
|
|
48
|
-
for (const jwk of candidates) {
|
|
49
|
-
try {
|
|
50
|
-
yield await importWithAlgCache(_cached, jwk, entry);
|
|
51
|
-
}
|
|
52
|
-
catch { }
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
throw error;
|
|
56
|
-
}
|
|
57
|
-
return importWithAlgCache(this.#cached, jwk, entry);
|
|
58
|
-
}
|
|
1
|
+
import { jwkToKey } from "../lib/jwk_to_key.js";
|
|
2
|
+
import { JWS } from "../lib/jws_algorithms.js";
|
|
3
|
+
import { JWKSInvalid, JOSENotSupported, JWKSNoMatchingKey, JWKSMultipleMatchingKeys } from "../util/errors.js";
|
|
4
|
+
import { isJwkSet } from "../lib/type_checks.js";
|
|
5
|
+
import { snapshotJwk } from "../lib/jwk_metadata.js";
|
|
6
|
+
function isUsableJWK(jwk, entry, alg, kid) {
|
|
7
|
+
const { kty, key_ops, ext, kid: jwkKid, alg: jwkAlg, use, crv } = snapshotJwk(jwk), keyOps = Array.isArray(key_ops) ? [...key_ops] : key_ops;
|
|
8
|
+
return (ext === void 0 || typeof ext == "boolean") && (keyOps === void 0 || Array.isArray(keyOps) && keyOps.every((operation, index) => typeof operation == "string" && keyOps.indexOf(operation) === index) && keyOps.includes("verify")) && entry.kty.includes(kty) && (kid === void 0 || typeof kid == "string" && kid === jwkKid) && (jwkAlg === void 0 ? kty !== "AKP" : alg === jwkAlg) && (use === void 0 || use === "sig") && (!entry.crv || crv === entry.crv);
|
|
59
9
|
}
|
|
60
10
|
async function importWithAlgCache(cache, jwk, entry) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return cached[entry.alg];
|
|
11
|
+
const cached = cache.get(jwk) || cache.set(jwk, {}).get(jwk), { alg } = entry;
|
|
12
|
+
if (cached[alg] === void 0) {
|
|
13
|
+
const key = await jwkToKey(entry, { ...jwk, alg, ext: !0 });
|
|
14
|
+
if (key.type !== "public")
|
|
15
|
+
throw new JWKSInvalid("JSON Web Key Set members must be public keys");
|
|
16
|
+
cached[alg] = key;
|
|
17
|
+
}
|
|
18
|
+
return cached[alg];
|
|
70
19
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
20
|
+
function createLocalJWKSet(jwks) {
|
|
21
|
+
let snapshot;
|
|
22
|
+
try {
|
|
23
|
+
snapshot = structuredClone(jwks);
|
|
24
|
+
} catch {
|
|
25
|
+
}
|
|
26
|
+
if (!isJwkSet(snapshot))
|
|
27
|
+
throw new JWKSInvalid("JSON Web Key Set malformed");
|
|
28
|
+
const cached = /* @__PURE__ */ new WeakMap();
|
|
29
|
+
return Object.defineProperty(async (protectedHeader, token) => {
|
|
30
|
+
const { alg, kid } = { ...protectedHeader, ...token?.header }, entry = typeof alg == "string" ? JWS[alg] : void 0;
|
|
31
|
+
if (!entry || entry.secret)
|
|
32
|
+
throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
|
|
33
|
+
const candidates = snapshot.keys.filter((jwk2) => isUsableJWK(jwk2, entry, alg, kid)), { 0: jwk, length } = candidates;
|
|
34
|
+
if (!length)
|
|
35
|
+
throw new JWKSNoMatchingKey();
|
|
36
|
+
if (length !== 1) {
|
|
37
|
+
const error = new JWKSMultipleMatchingKeys();
|
|
38
|
+
throw error[Symbol.asyncIterator] = async function* () {
|
|
39
|
+
for (const jwk2 of candidates)
|
|
40
|
+
try {
|
|
41
|
+
yield await importWithAlgCache(cached, jwk2, entry);
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}, error;
|
|
45
|
+
}
|
|
46
|
+
return importWithAlgCache(cached, jwk, entry);
|
|
47
|
+
}, "jwks", {
|
|
48
|
+
value: () => structuredClone(snapshot)
|
|
49
|
+
});
|
|
78
50
|
}
|
|
51
|
+
export {
|
|
52
|
+
createLocalJWKSet
|
|
53
|
+
};
|