jose 6.2.6 → 6.2.8
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/webapi/jwe/compact/decrypt.js +2 -2
- package/dist/webapi/jwe/flattened/encrypt.js +12 -12
- package/dist/webapi/jwe/general/decrypt.js +8 -0
- package/dist/webapi/jwe/general/encrypt.js +51 -52
- package/dist/webapi/jwks/local.js +13 -34
- package/dist/webapi/jwks/remote.js +17 -26
- package/dist/webapi/jws/compact/verify.js +2 -2
- package/dist/webapi/jws/flattened/sign.js +1 -9
- package/dist/webapi/jws/general/sign.js +12 -21
- package/dist/webapi/jws/general/verify.js +13 -11
- package/dist/webapi/jwt/decrypt.js +3 -3
- package/dist/webapi/jwt/verify.js +4 -4
- package/dist/webapi/key/export.js +3 -3
- package/dist/webapi/key/generate_key_pair.js +10 -9
- package/dist/webapi/key/generate_secret.js +2 -2
- package/dist/webapi/lib/asn1.js +32 -65
- package/dist/webapi/lib/content_encryption.js +16 -34
- package/dist/webapi/lib/crypto_key.js +6 -0
- package/dist/webapi/lib/invalid_key_input.js +0 -1
- package/dist/webapi/lib/jwe_algorithms.js +48 -84
- package/dist/webapi/lib/jwe_decrypt.js +46 -41
- package/dist/webapi/lib/jwe_encrypt.js +14 -13
- package/dist/webapi/lib/jwk_to_key.js +5 -10
- package/dist/webapi/lib/jws_algorithms.js +22 -23
- package/dist/webapi/lib/jws_sign.js +12 -6
- package/dist/webapi/lib/jws_verify.js +26 -27
- package/dist/webapi/lib/jwt_claims_set.js +60 -102
- package/dist/webapi/lib/key.js +61 -99
- package/dist/webapi/lib/key_algorithm.js +7 -13
- package/dist/webapi/lib/key_descriptor.js +1 -1
- package/dist/webapi/lib/key_management.js +74 -161
- package/dist/webapi/lib/options.js +11 -14
- package/dist/webapi/lib/signing.js +6 -16
- package/dist/webapi/lib/type_checks.js +13 -18
- package/dist/webapi/util/base64url.js +4 -3
- package/package.json +1 -1
|
@@ -6,75 +6,38 @@ import { JOSENotSupported, JWEInvalid } from '../util/errors.js';
|
|
|
6
6
|
import { decodeBase64url, digest } from './helpers.js';
|
|
7
7
|
import { generateCek, encrypt, decrypt } from './content_encryption.js';
|
|
8
8
|
import { isObject } from './type_checks.js';
|
|
9
|
-
import { checkCryptoKey, checkUsage } from './crypto_key.js';
|
|
10
|
-
import { checkModulusLength } from './signing.js';
|
|
9
|
+
import { checkCryptoKey, checkModulusLength, checkUsage } from './crypto_key.js';
|
|
11
10
|
import { concat, encode, uint32be } from './buffer_utils.js';
|
|
12
11
|
import { assertCryptoKey } from './is_key_like.js';
|
|
13
12
|
function checkEcdhCryptoKey(key, usage) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
case 'X25519':
|
|
17
|
-
break;
|
|
18
|
-
default:
|
|
19
|
-
throw new TypeError('CryptoKey does not support this operation, its algorithm.name must be ECDH or X25519');
|
|
13
|
+
if (key.algorithm.name !== 'ECDH' && key.algorithm.name !== 'X25519') {
|
|
14
|
+
throw new TypeError('CryptoKey does not support this operation, its algorithm.name must be ECDH or X25519');
|
|
20
15
|
}
|
|
21
16
|
checkUsage(key, usage);
|
|
22
17
|
}
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
checkCryptoKey(key, jweAlgorithm(alg).subtle, usage);
|
|
33
|
-
return key;
|
|
18
|
+
async function aeskwCryptoKey(key, alg, usage) {
|
|
19
|
+
const expected = jweAlgorithm(alg).subtle;
|
|
20
|
+
const cryptoKey = key instanceof Uint8Array
|
|
21
|
+
? await crypto.subtle.importKey('raw', key, 'AES-KW', true, [
|
|
22
|
+
usage,
|
|
23
|
+
])
|
|
24
|
+
: key;
|
|
25
|
+
checkCryptoKey(cryptoKey, expected, usage);
|
|
26
|
+
return cryptoKey;
|
|
34
27
|
}
|
|
35
28
|
async function aeskwWrap(alg, key, cek) {
|
|
36
29
|
const cryptoKey = await aeskwCryptoKey(key, alg, 'wrapKey');
|
|
37
|
-
checkKeySize(cryptoKey, alg);
|
|
38
30
|
const cryptoKeyCek = await crypto.subtle.importKey('raw', cek, { hash: 'SHA-256', name: 'HMAC' }, true, ['sign']);
|
|
39
31
|
return new Uint8Array(await crypto.subtle.wrapKey('raw', cryptoKeyCek, cryptoKey, 'AES-KW'));
|
|
40
32
|
}
|
|
41
33
|
async function aeskwUnwrap(alg, key, encryptedKey) {
|
|
42
34
|
const cryptoKey = await aeskwCryptoKey(key, alg, 'unwrapKey');
|
|
43
|
-
checkKeySize(cryptoKey, alg);
|
|
44
35
|
const cryptoKeyCek = await crypto.subtle.unwrapKey('raw', encryptedKey, cryptoKey, 'AES-KW', { hash: 'SHA-256', name: 'HMAC' }, true, ['sign']);
|
|
45
36
|
return new Uint8Array(await crypto.subtle.exportKey('raw', cryptoKeyCek));
|
|
46
37
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
encryptedKey: wrapped.ciphertext,
|
|
51
|
-
iv: b64u(wrapped.iv),
|
|
52
|
-
tag: b64u(wrapped.tag),
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
async function aesGcmKwUnwrap(gcm, key, encryptedKey, iv, tag) {
|
|
56
|
-
return decrypt(gcm, key, encryptedKey, iv, tag, new Uint8Array());
|
|
57
|
-
}
|
|
58
|
-
const subtleAlgorithm = (alg) => {
|
|
59
|
-
switch (alg) {
|
|
60
|
-
case 'RSA-OAEP':
|
|
61
|
-
case 'RSA-OAEP-256':
|
|
62
|
-
case 'RSA-OAEP-384':
|
|
63
|
-
case 'RSA-OAEP-512':
|
|
64
|
-
return 'RSA-OAEP';
|
|
65
|
-
default:
|
|
66
|
-
throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
async function rsaesEncrypt(alg, key, cek) {
|
|
70
|
-
checkCryptoKey(key, jweAlgorithm(alg).subtle, 'encrypt');
|
|
71
|
-
checkModulusLength(alg, key);
|
|
72
|
-
return new Uint8Array(await crypto.subtle.encrypt(subtleAlgorithm(alg), key, cek));
|
|
73
|
-
}
|
|
74
|
-
async function rsaesDecrypt(alg, key, encryptedKey) {
|
|
75
|
-
checkCryptoKey(key, jweAlgorithm(alg).subtle, 'decrypt');
|
|
38
|
+
function checkRsaKey(alg, key, usage) {
|
|
39
|
+
checkCryptoKey(key, jweAlgorithm(alg).subtle, usage);
|
|
76
40
|
checkModulusLength(alg, key);
|
|
77
|
-
return new Uint8Array(await crypto.subtle.decrypt(subtleAlgorithm(alg), key, encryptedKey));
|
|
78
41
|
}
|
|
79
42
|
function pbes2CryptoKey(key, alg) {
|
|
80
43
|
if (key instanceof Uint8Array) {
|
|
@@ -85,7 +48,6 @@ function pbes2CryptoKey(key, alg) {
|
|
|
85
48
|
checkCryptoKey(key, jweAlgorithm(alg).subtle, 'deriveBits');
|
|
86
49
|
return key;
|
|
87
50
|
}
|
|
88
|
-
const concatSalt = (alg, p2sInput) => concat(encode(alg), Uint8Array.of(0x00), p2sInput);
|
|
89
51
|
async function deriveKey(p2s, alg, p2c, key) {
|
|
90
52
|
if (!(p2s instanceof Uint8Array) || p2s.length < 8) {
|
|
91
53
|
throw new JWEInvalid('PBES2 Salt Input must be 8 or more octets');
|
|
@@ -93,7 +55,7 @@ async function deriveKey(p2s, alg, p2c, key) {
|
|
|
93
55
|
if (!Number.isSafeInteger(p2c) || Math.sign(p2c) !== 1) {
|
|
94
56
|
throw new JWEInvalid('PBES2 Count Input must be a positive integer');
|
|
95
57
|
}
|
|
96
|
-
const salt =
|
|
58
|
+
const salt = concat(encode(alg), Uint8Array.of(0), p2s);
|
|
97
59
|
const keylen = parseInt(alg.slice(13, 16), 10);
|
|
98
60
|
const subtleAlg = {
|
|
99
61
|
hash: `SHA-${alg.slice(8, 11)}`,
|
|
@@ -104,15 +66,6 @@ async function deriveKey(p2s, alg, p2c, key) {
|
|
|
104
66
|
const cryptoKey = await pbes2CryptoKey(key, alg);
|
|
105
67
|
return new Uint8Array(await crypto.subtle.deriveBits(subtleAlg, cryptoKey, keylen));
|
|
106
68
|
}
|
|
107
|
-
async function pbes2kwWrap(alg, key, cek, p2c = 2048, p2s = crypto.getRandomValues(new Uint8Array(16))) {
|
|
108
|
-
const derived = await deriveKey(p2s, alg, p2c, key);
|
|
109
|
-
const encryptedKey = await aeskwWrap(alg.slice(-6), derived, cek);
|
|
110
|
-
return { encryptedKey, p2c, p2s: b64u(p2s) };
|
|
111
|
-
}
|
|
112
|
-
async function pbes2kwUnwrap(alg, key, encryptedKey, p2c, p2s) {
|
|
113
|
-
const derived = await deriveKey(p2s, alg, p2c, key);
|
|
114
|
-
return aeskwUnwrap(alg.slice(-6), derived, encryptedKey);
|
|
115
|
-
}
|
|
116
69
|
function lengthAndInput(input) {
|
|
117
70
|
return concat(uint32be(input.length), input);
|
|
118
71
|
}
|
|
@@ -122,11 +75,7 @@ async function concatKdf(Z, L, OtherInfo) {
|
|
|
122
75
|
const reps = Math.ceil(dkLen / hashLen);
|
|
123
76
|
const dk = new Uint8Array(reps * hashLen);
|
|
124
77
|
for (let i = 1; i <= reps; i++) {
|
|
125
|
-
const
|
|
126
|
-
hashInput.set(uint32be(i), 0);
|
|
127
|
-
hashInput.set(Z, 4);
|
|
128
|
-
hashInput.set(OtherInfo, 4 + Z.length);
|
|
129
|
-
const hashResult = await digest('sha256', hashInput);
|
|
78
|
+
const hashResult = await digest('sha256', concat(uint32be(i), Z, OtherInfo));
|
|
130
79
|
dk.set(hashResult, (i - 1) * hashLen);
|
|
131
80
|
}
|
|
132
81
|
return dk.slice(0, dkLen);
|
|
@@ -134,58 +83,47 @@ async function concatKdf(Z, L, OtherInfo) {
|
|
|
134
83
|
async function ecdhesDeriveKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(), apv = new Uint8Array()) {
|
|
135
84
|
checkEcdhCryptoKey(publicKey);
|
|
136
85
|
checkEcdhCryptoKey(privateKey, 'deriveBits');
|
|
137
|
-
const
|
|
138
|
-
const partyUInfo = lengthAndInput(apu);
|
|
139
|
-
const partyVInfo = lengthAndInput(apv);
|
|
140
|
-
const suppPubInfo = uint32be(keyLength);
|
|
141
|
-
const suppPrivInfo = new Uint8Array();
|
|
142
|
-
const otherInfo = concat(algorithmID, partyUInfo, partyVInfo, suppPubInfo, suppPrivInfo);
|
|
86
|
+
const otherInfo = concat(lengthAndInput(encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength));
|
|
143
87
|
const Z = new Uint8Array(await crypto.subtle.deriveBits({
|
|
144
88
|
name: publicKey.algorithm.name,
|
|
145
89
|
public: publicKey,
|
|
146
|
-
}, privateKey,
|
|
90
|
+
}, privateKey, publicKey.algorithm.name === 'X25519'
|
|
91
|
+
? 256
|
|
92
|
+
: Math.ceil(parseInt(publicKey.algorithm.namedCurve.slice(-3), 10) / 8) << 3));
|
|
147
93
|
return concatKdf(Z, keyLength, otherInfo);
|
|
148
94
|
}
|
|
149
|
-
function
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
case 'P-256':
|
|
158
|
-
case 'P-384':
|
|
159
|
-
case 'P-521':
|
|
160
|
-
return true;
|
|
161
|
-
default:
|
|
162
|
-
return key.algorithm.name === 'X25519';
|
|
95
|
+
function assertEcdhKey(key) {
|
|
96
|
+
assertCryptoKey(key);
|
|
97
|
+
const curve = key.algorithm.namedCurve;
|
|
98
|
+
if (curve !== 'P-256' &&
|
|
99
|
+
curve !== 'P-384' &&
|
|
100
|
+
curve !== 'P-521' &&
|
|
101
|
+
key.algorithm.name !== 'X25519') {
|
|
102
|
+
throw new JOSENotSupported('ECDH with the provided key is not allowed or not supported by your javascript runtime');
|
|
163
103
|
}
|
|
164
104
|
}
|
|
165
|
-
const unsupportedAlgHeader = 'Invalid or unsupported "alg" (JWE Algorithm) header value';
|
|
166
105
|
function assertEncryptedKey(encryptedKey) {
|
|
167
106
|
if (encryptedKey === undefined)
|
|
168
107
|
throw new JWEInvalid('JWE Encrypted Key missing');
|
|
169
108
|
}
|
|
109
|
+
function assertNoEncryptedKey(encryptedKey) {
|
|
110
|
+
if (encryptedKey !== undefined)
|
|
111
|
+
throw new JWEInvalid('Encountered unexpected JWE Encrypted Key');
|
|
112
|
+
}
|
|
170
113
|
export async function decryptKeyManagement(alg, enc, key, encryptedKey, joseHeader, options) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
case 'ECDH
|
|
178
|
-
if (
|
|
179
|
-
|
|
180
|
-
case 'ECDH-ES+A128KW':
|
|
181
|
-
case 'ECDH-ES+A192KW':
|
|
182
|
-
case 'ECDH-ES+A256KW': {
|
|
114
|
+
const entry = jweAlgorithm(alg);
|
|
115
|
+
if (alg === 'dir') {
|
|
116
|
+
assertNoEncryptedKey(encryptedKey);
|
|
117
|
+
return key;
|
|
118
|
+
}
|
|
119
|
+
switch (entry.subtle.name) {
|
|
120
|
+
case 'ECDH': {
|
|
121
|
+
if (alg === 'ECDH-ES')
|
|
122
|
+
assertNoEncryptedKey(encryptedKey);
|
|
183
123
|
if (!isObject(joseHeader.epk))
|
|
184
124
|
throw new JWEInvalid(`JOSE Header "epk" (Ephemeral Public Key) missing or invalid`);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
throw new JOSENotSupported('ECDH with the provided key is not allowed or not supported by your javascript runtime');
|
|
188
|
-
const epk = await jwkToKey(jweAlgorithm(alg), joseHeader.epk);
|
|
125
|
+
assertEcdhKey(key);
|
|
126
|
+
const epk = await jwkToKey(entry, joseHeader.epk);
|
|
189
127
|
let partyUInfo;
|
|
190
128
|
let partyVInfo;
|
|
191
129
|
if (joseHeader.apu !== undefined) {
|
|
@@ -204,17 +142,13 @@ export async function decryptKeyManagement(alg, enc, key, encryptedKey, joseHead
|
|
|
204
142
|
assertEncryptedKey(encryptedKey);
|
|
205
143
|
return aeskwUnwrap(alg.slice(-6), sharedSecret, encryptedKey);
|
|
206
144
|
}
|
|
207
|
-
case 'RSA-OAEP':
|
|
208
|
-
case 'RSA-OAEP-256':
|
|
209
|
-
case 'RSA-OAEP-384':
|
|
210
|
-
case 'RSA-OAEP-512': {
|
|
145
|
+
case 'RSA-OAEP': {
|
|
211
146
|
assertEncryptedKey(encryptedKey);
|
|
212
147
|
assertCryptoKey(key);
|
|
213
|
-
|
|
148
|
+
checkRsaKey(alg, key, 'decrypt');
|
|
149
|
+
return new Uint8Array(await crypto.subtle.decrypt('RSA-OAEP', key, encryptedKey));
|
|
214
150
|
}
|
|
215
|
-
case '
|
|
216
|
-
case 'PBES2-HS384+A192KW':
|
|
217
|
-
case 'PBES2-HS512+A256KW': {
|
|
151
|
+
case 'PBKDF2': {
|
|
218
152
|
assertEncryptedKey(encryptedKey);
|
|
219
153
|
if (typeof joseHeader.p2c !== 'number')
|
|
220
154
|
throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) missing or invalid`);
|
|
@@ -223,19 +157,15 @@ export async function decryptKeyManagement(alg, enc, key, encryptedKey, joseHead
|
|
|
223
157
|
throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds`);
|
|
224
158
|
if (typeof joseHeader.p2s !== 'string')
|
|
225
159
|
throw new JWEInvalid(`JOSE Header "p2s" (PBES2 Salt) missing or invalid`);
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
return
|
|
160
|
+
const p2s = decodeBase64url(joseHeader.p2s, 'p2s', JWEInvalid);
|
|
161
|
+
const derived = await deriveKey(p2s, alg, joseHeader.p2c, key);
|
|
162
|
+
return aeskwUnwrap(alg.slice(-6), derived, encryptedKey);
|
|
229
163
|
}
|
|
230
|
-
case '
|
|
231
|
-
case 'A192KW':
|
|
232
|
-
case 'A256KW': {
|
|
164
|
+
case 'AES-KW': {
|
|
233
165
|
assertEncryptedKey(encryptedKey);
|
|
234
166
|
return aeskwUnwrap(alg, key, encryptedKey);
|
|
235
167
|
}
|
|
236
|
-
case '
|
|
237
|
-
case 'A192GCMKW':
|
|
238
|
-
case 'A256GCMKW': {
|
|
168
|
+
case 'AES-GCM': {
|
|
239
169
|
assertEncryptedKey(encryptedKey);
|
|
240
170
|
if (typeof joseHeader.iv !== 'string')
|
|
241
171
|
throw new JWEInvalid(`JOSE Header "iv" (Initialization Vector) missing or invalid`);
|
|
@@ -245,10 +175,7 @@ export async function decryptKeyManagement(alg, enc, key, encryptedKey, joseHead
|
|
|
245
175
|
iv = decodeBase64url(joseHeader.iv, 'iv', JWEInvalid);
|
|
246
176
|
let tag;
|
|
247
177
|
tag = decodeBase64url(joseHeader.tag, 'tag', JWEInvalid);
|
|
248
|
-
return
|
|
249
|
-
}
|
|
250
|
-
default: {
|
|
251
|
-
throw new JOSENotSupported(unsupportedAlgHeader);
|
|
178
|
+
return decrypt(jweEncryption(alg.slice(0, -2)), key, encryptedKey, iv, tag, new Uint8Array());
|
|
252
179
|
}
|
|
253
180
|
}
|
|
254
181
|
}
|
|
@@ -256,23 +183,16 @@ export async function encryptKeyManagement(alg, enc, key, providedCek, providedP
|
|
|
256
183
|
let encryptedKey;
|
|
257
184
|
let parameters;
|
|
258
185
|
let cek;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
case 'ECDH-ES+A128KW':
|
|
266
|
-
case 'ECDH-ES+A192KW':
|
|
267
|
-
case 'ECDH-ES+A256KW': {
|
|
268
|
-
assertCryptoKey(key);
|
|
269
|
-
if (!ecdhesAllowed(key)) {
|
|
270
|
-
throw new JOSENotSupported('ECDH with the provided key is not allowed or not supported by your javascript runtime');
|
|
271
|
-
}
|
|
186
|
+
const entry = jweAlgorithm(alg);
|
|
187
|
+
if (alg === 'dir')
|
|
188
|
+
return [key, undefined, undefined];
|
|
189
|
+
switch (entry.subtle.name) {
|
|
190
|
+
case 'ECDH': {
|
|
191
|
+
assertEcdhKey(key);
|
|
272
192
|
const { apu, apv } = providedParameters;
|
|
273
193
|
let ephemeralKey;
|
|
274
194
|
if (providedParameters.epk) {
|
|
275
|
-
ephemeralKey = (await prepareKey(
|
|
195
|
+
ephemeralKey = (await prepareKey(entry, providedParameters.epk, 'decrypt'));
|
|
276
196
|
}
|
|
277
197
|
else {
|
|
278
198
|
ephemeralKey = (await crypto.subtle.generateKey(key.algorithm, true, ['deriveBits'])).privateKey;
|
|
@@ -303,41 +223,34 @@ export async function encryptKeyManagement(alg, enc, key, providedCek, providedP
|
|
|
303
223
|
encryptedKey = await aeskwWrap(kwAlg, sharedSecret, cek);
|
|
304
224
|
break;
|
|
305
225
|
}
|
|
306
|
-
case 'RSA-OAEP':
|
|
307
|
-
case 'RSA-OAEP-256':
|
|
308
|
-
case 'RSA-OAEP-384':
|
|
309
|
-
case 'RSA-OAEP-512': {
|
|
226
|
+
case 'RSA-OAEP': {
|
|
310
227
|
cek = providedCek || generateCek(enc);
|
|
311
228
|
assertCryptoKey(key);
|
|
312
|
-
|
|
229
|
+
checkRsaKey(alg, key, 'encrypt');
|
|
230
|
+
encryptedKey = new Uint8Array(await crypto.subtle.encrypt('RSA-OAEP', key, cek));
|
|
313
231
|
break;
|
|
314
232
|
}
|
|
315
|
-
case '
|
|
316
|
-
case 'PBES2-HS384+A192KW':
|
|
317
|
-
case 'PBES2-HS512+A256KW': {
|
|
233
|
+
case 'PBKDF2': {
|
|
318
234
|
cek = providedCek || generateCek(enc);
|
|
319
|
-
const { p2c, p2s } = providedParameters;
|
|
320
|
-
|
|
235
|
+
const { p2c = 2048, p2s = crypto.getRandomValues(new Uint8Array(16)) } = providedParameters;
|
|
236
|
+
const derived = await deriveKey(p2s, alg, p2c, key);
|
|
237
|
+
encryptedKey = await aeskwWrap(alg.slice(-6), derived, cek);
|
|
238
|
+
parameters = { p2c, p2s: b64u(p2s) };
|
|
321
239
|
break;
|
|
322
240
|
}
|
|
323
|
-
case '
|
|
324
|
-
case 'A192KW':
|
|
325
|
-
case 'A256KW': {
|
|
241
|
+
case 'AES-KW': {
|
|
326
242
|
cek = providedCek || generateCek(enc);
|
|
327
243
|
encryptedKey = await aeskwWrap(alg, key, cek);
|
|
328
244
|
break;
|
|
329
245
|
}
|
|
330
|
-
case '
|
|
331
|
-
case 'A192GCMKW':
|
|
332
|
-
case 'A256GCMKW': {
|
|
246
|
+
case 'AES-GCM': {
|
|
333
247
|
cek = providedCek || generateCek(enc);
|
|
334
248
|
const { iv } = providedParameters;
|
|
335
|
-
|
|
249
|
+
const wrapped = await encrypt(jweEncryption(alg.slice(0, -2)), cek, key, iv, new Uint8Array());
|
|
250
|
+
encryptedKey = wrapped.ciphertext;
|
|
251
|
+
parameters = { iv: b64u(wrapped.iv), tag: b64u(wrapped.tag) };
|
|
336
252
|
break;
|
|
337
253
|
}
|
|
338
|
-
default: {
|
|
339
|
-
throw new JOSENotSupported(unsupportedAlgHeader);
|
|
340
|
-
}
|
|
341
254
|
}
|
|
342
|
-
return
|
|
255
|
+
return [cek, encryptedKey, parameters];
|
|
343
256
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JOSENotSupported, JWEInvalid, JWSInvalid } from '../util/errors.js';
|
|
2
|
-
export const JWS_RECOGNIZED =
|
|
3
|
-
export const JWE_RECOGNIZED =
|
|
2
|
+
export const JWS_RECOGNIZED = { __proto__: null, b64: true };
|
|
3
|
+
export const JWE_RECOGNIZED = { __proto__: null };
|
|
4
4
|
export function validateAlgorithms(option, algorithms) {
|
|
5
5
|
if (algorithms !== undefined &&
|
|
6
6
|
(!Array.isArray(algorithms) || algorithms.some((s) => typeof s !== 'string'))) {
|
|
@@ -22,30 +22,27 @@ export function validateCrit(Err, recognizedDefault, recognizedOption, protected
|
|
|
22
22
|
throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected');
|
|
23
23
|
}
|
|
24
24
|
if (!protectedHeader || protectedHeader.crit === undefined) {
|
|
25
|
-
return
|
|
25
|
+
return [];
|
|
26
26
|
}
|
|
27
27
|
if (!Array.isArray(protectedHeader.crit) ||
|
|
28
28
|
protectedHeader.crit.length === 0 ||
|
|
29
29
|
protectedHeader.crit.some((input) => typeof input !== 'string' || input.length === 0)) {
|
|
30
30
|
throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
recognized = recognizedDefault;
|
|
38
|
-
}
|
|
32
|
+
const recognized = recognizedOption === undefined
|
|
33
|
+
? recognizedDefault
|
|
34
|
+
: { __proto__: null, ...recognizedOption, ...recognizedDefault };
|
|
39
35
|
for (const parameter of protectedHeader.crit) {
|
|
40
|
-
if (!
|
|
36
|
+
if (!(parameter in recognized)) {
|
|
41
37
|
throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`);
|
|
42
38
|
}
|
|
43
|
-
if (joseHeader[parameter] === undefined) {
|
|
39
|
+
if (!Object.hasOwn(joseHeader, parameter) || joseHeader[parameter] === undefined) {
|
|
44
40
|
throw new Err(`Extension Header Parameter "${parameter}" is missing`);
|
|
45
41
|
}
|
|
46
|
-
if (recognized
|
|
42
|
+
if (recognized[parameter] &&
|
|
43
|
+
(!Object.hasOwn(protectedHeader, parameter) || protectedHeader[parameter] === undefined)) {
|
|
47
44
|
throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`);
|
|
48
45
|
}
|
|
49
46
|
}
|
|
50
|
-
return
|
|
47
|
+
return protectedHeader.crit;
|
|
51
48
|
}
|
|
@@ -1,34 +1,24 @@
|
|
|
1
|
-
import { checkCryptoKey } from './crypto_key.js';
|
|
2
|
-
export function checkModulusLength(alg, key) {
|
|
3
|
-
const { modulusLength } = key.algorithm;
|
|
4
|
-
if (typeof modulusLength !== 'number' || modulusLength < 2048) {
|
|
5
|
-
throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
function checkSigCryptoKey(entry, key, usage) {
|
|
9
|
-
checkCryptoKey(key, entry.subtle, usage);
|
|
10
|
-
if (entry.minModulusLength) {
|
|
11
|
-
checkModulusLength(entry.alg, key);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
1
|
+
import { checkCryptoKey, checkModulusLength } from './crypto_key.js';
|
|
14
2
|
async function getSigKey(entry, key, usage) {
|
|
15
3
|
if (key instanceof Uint8Array) {
|
|
16
4
|
return crypto.subtle.importKey('raw', key, entry.subtle, false, [
|
|
17
5
|
usage,
|
|
18
6
|
]);
|
|
19
7
|
}
|
|
20
|
-
|
|
8
|
+
checkCryptoKey(key, entry.subtle, usage);
|
|
9
|
+
if (entry.minRsaBits)
|
|
10
|
+
checkModulusLength(entry.alg, key);
|
|
21
11
|
return key;
|
|
22
12
|
}
|
|
23
13
|
export async function sign(entry, key, data) {
|
|
24
14
|
const cryptoKey = await getSigKey(entry, key, 'sign');
|
|
25
|
-
const signature = await crypto.subtle.sign(entry.
|
|
15
|
+
const signature = await crypto.subtle.sign(entry.signing, cryptoKey, data);
|
|
26
16
|
return new Uint8Array(signature);
|
|
27
17
|
}
|
|
28
18
|
export async function verify(entry, key, signature, data) {
|
|
29
19
|
const cryptoKey = await getSigKey(entry, key, 'verify');
|
|
30
20
|
try {
|
|
31
|
-
return await crypto.subtle.verify(entry.
|
|
21
|
+
return await crypto.subtle.verify(entry.signing, cryptoKey, signature, data);
|
|
32
22
|
}
|
|
33
23
|
catch {
|
|
34
24
|
return false;
|
|
@@ -1,34 +1,29 @@
|
|
|
1
|
-
const isObjectLike = (value) => typeof value === 'object' && value !== null;
|
|
2
1
|
export function isObject(input) {
|
|
3
|
-
if (
|
|
2
|
+
if (typeof input !== 'object' ||
|
|
3
|
+
input === null ||
|
|
4
|
+
Object.prototype.toString.call(input) !== '[object Object]') {
|
|
4
5
|
return false;
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
+
const prototype = Object.getPrototypeOf(input);
|
|
8
|
+
if (prototype === null) {
|
|
7
9
|
return true;
|
|
8
10
|
}
|
|
9
|
-
let proto =
|
|
11
|
+
let proto = prototype;
|
|
10
12
|
while (Object.getPrototypeOf(proto) !== null) {
|
|
11
13
|
proto = Object.getPrototypeOf(proto);
|
|
12
14
|
}
|
|
13
|
-
return
|
|
15
|
+
return prototype === proto;
|
|
14
16
|
}
|
|
15
17
|
export function isDisjoint(...headers) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
let acc;
|
|
21
|
-
for (const header of sources) {
|
|
22
|
-
const parameters = Object.keys(header);
|
|
23
|
-
if (!acc || acc.size === 0) {
|
|
24
|
-
acc = new Set(parameters);
|
|
18
|
+
const parameters = new Set();
|
|
19
|
+
for (const header of headers) {
|
|
20
|
+
if (!header)
|
|
25
21
|
continue;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (acc.has(parameter)) {
|
|
22
|
+
for (const parameter of Object.keys(header)) {
|
|
23
|
+
if (parameters.has(parameter)) {
|
|
29
24
|
return false;
|
|
30
25
|
}
|
|
31
|
-
|
|
26
|
+
parameters.add(parameter);
|
|
32
27
|
}
|
|
33
28
|
}
|
|
34
29
|
return true;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { encoder, decoder } from '../lib/buffer_utils.js';
|
|
2
2
|
import { encodeBase64, decodeBase64 } from '../lib/base64.js';
|
|
3
|
+
const invalid = 'The input to be decoded is not correctly encoded.';
|
|
3
4
|
export function decode(input) {
|
|
4
5
|
if (Uint8Array.fromBase64) {
|
|
5
6
|
try {
|
|
@@ -8,7 +9,7 @@ export function decode(input) {
|
|
|
8
9
|
});
|
|
9
10
|
}
|
|
10
11
|
catch (cause) {
|
|
11
|
-
throw new TypeError(
|
|
12
|
+
throw new TypeError(invalid, { cause });
|
|
12
13
|
}
|
|
13
14
|
}
|
|
14
15
|
let encoded = input;
|
|
@@ -16,14 +17,14 @@ export function decode(input) {
|
|
|
16
17
|
encoded = decoder.decode(encoded);
|
|
17
18
|
}
|
|
18
19
|
if (encoded.includes('+') || encoded.includes('/')) {
|
|
19
|
-
throw new TypeError(
|
|
20
|
+
throw new TypeError(invalid);
|
|
20
21
|
}
|
|
21
22
|
encoded = encoded.replace(/-/g, '+').replace(/_/g, '/');
|
|
22
23
|
try {
|
|
23
24
|
return decodeBase64(encoded);
|
|
24
25
|
}
|
|
25
26
|
catch {
|
|
26
|
-
throw new TypeError(
|
|
27
|
+
throw new TypeError(invalid);
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
export function encode(input) {
|