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
|
@@ -9,88 +9,94 @@ import { prepareKey } from './key.js';
|
|
|
9
9
|
import { jweAlgorithm, jweEncryption } from './jwe_algorithms.js';
|
|
10
10
|
import { decompress } from './deflate.js';
|
|
11
11
|
export function checkShared(jwe) {
|
|
12
|
+
const { ciphertext, protected: encodedProtected, unprotected } = jwe;
|
|
12
13
|
if (jwe.iv !== undefined && typeof jwe.iv !== 'string') {
|
|
13
14
|
throw new JWEInvalid('JWE Initialization Vector incorrect type');
|
|
14
15
|
}
|
|
15
|
-
if (typeof
|
|
16
|
+
if (typeof ciphertext !== 'string') {
|
|
16
17
|
throw new JWEInvalid('JWE Ciphertext missing or incorrect type');
|
|
17
18
|
}
|
|
18
19
|
if (jwe.tag !== undefined && typeof jwe.tag !== 'string') {
|
|
19
20
|
throw new JWEInvalid('JWE Authentication Tag incorrect type');
|
|
20
21
|
}
|
|
21
|
-
if (
|
|
22
|
+
if (encodedProtected !== undefined && typeof encodedProtected !== 'string') {
|
|
22
23
|
throw new JWEInvalid('JWE Protected Header incorrect type');
|
|
23
24
|
}
|
|
24
25
|
if (jwe.aad !== undefined && typeof jwe.aad !== 'string') {
|
|
25
26
|
throw new JWEInvalid('JWE AAD incorrect type');
|
|
26
27
|
}
|
|
27
|
-
if (
|
|
28
|
+
if (unprotected !== undefined && !isObject(unprotected)) {
|
|
28
29
|
throw new JWEInvalid('JWE Shared Unprotected Header incorrect type');
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
export function checkRecipient(jwe) {
|
|
32
|
-
|
|
33
|
+
const { encrypted_key: encryptedKey, header } = jwe;
|
|
34
|
+
if (encryptedKey !== undefined && typeof encryptedKey !== 'string') {
|
|
33
35
|
throw new JWEInvalid('JWE Encrypted Key incorrect type');
|
|
34
36
|
}
|
|
35
|
-
if (
|
|
37
|
+
if (header !== undefined && !isObject(header)) {
|
|
36
38
|
throw new JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type');
|
|
37
39
|
}
|
|
38
|
-
if (jwe.protected === undefined &&
|
|
40
|
+
if (jwe.protected === undefined && header === undefined && jwe.unprotected === undefined) {
|
|
39
41
|
throw new JWEInvalid('JOSE Header missing');
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
export function shareJWE(jwe) {
|
|
45
|
+
const { protected: encodedProtected, ciphertext, iv, tag, aad } = jwe;
|
|
43
46
|
let parsedProt;
|
|
44
|
-
if (
|
|
45
|
-
parsedProt = parseJoseHeader(
|
|
47
|
+
if (encodedProtected) {
|
|
48
|
+
parsedProt = parseJoseHeader(encodedProtected, JWEInvalid, 'JWE Protected Header is invalid');
|
|
46
49
|
}
|
|
47
|
-
const protectedHeader =
|
|
48
|
-
return
|
|
50
|
+
const protectedHeader = encodedProtected !== undefined ? encode(encodedProtected) : new Uint8Array();
|
|
51
|
+
return [
|
|
49
52
|
parsedProt,
|
|
50
|
-
|
|
51
|
-
iv
|
|
52
|
-
tag
|
|
53
|
-
|
|
54
|
-
? concat(protectedHeader, encode('.'), encodeBase64url(
|
|
53
|
+
decodeBase64url(ciphertext, 'ciphertext', JWEInvalid),
|
|
54
|
+
iv !== undefined ? decodeBase64url(iv, 'iv', JWEInvalid) : undefined,
|
|
55
|
+
tag !== undefined ? decodeBase64url(tag, 'tag', JWEInvalid) : undefined,
|
|
56
|
+
aad !== undefined
|
|
57
|
+
? concat(protectedHeader, encode('.'), encodeBase64url(aad, 'aad', JWEInvalid))
|
|
55
58
|
: protectedHeader,
|
|
56
|
-
|
|
59
|
+
];
|
|
57
60
|
}
|
|
58
61
|
export function decryptResult(jwe, decrypted) {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
const [plaintext, parsedProt, key, resolvedKey] = decrypted;
|
|
63
|
+
const { protected: encodedProtected, aad, unprotected, header } = jwe;
|
|
64
|
+
const result = { plaintext };
|
|
65
|
+
if (encodedProtected !== undefined) {
|
|
66
|
+
result.protectedHeader = parsedProt;
|
|
62
67
|
}
|
|
63
|
-
if (
|
|
64
|
-
result.additionalAuthenticatedData = decodeBase64url(
|
|
68
|
+
if (aad !== undefined) {
|
|
69
|
+
result.additionalAuthenticatedData = decodeBase64url(aad, 'aad', JWEInvalid);
|
|
65
70
|
}
|
|
66
|
-
if (
|
|
67
|
-
result.sharedUnprotectedHeader =
|
|
71
|
+
if (unprotected !== undefined) {
|
|
72
|
+
result.sharedUnprotectedHeader = unprotected;
|
|
68
73
|
}
|
|
69
|
-
if (
|
|
70
|
-
result.unprotectedHeader =
|
|
74
|
+
if (header !== undefined) {
|
|
75
|
+
result.unprotectedHeader = header;
|
|
71
76
|
}
|
|
72
|
-
if (
|
|
73
|
-
return { ...result, key
|
|
77
|
+
if (resolvedKey) {
|
|
78
|
+
return { ...result, key };
|
|
74
79
|
}
|
|
75
80
|
return result;
|
|
76
81
|
}
|
|
77
82
|
export function prepareDecrypt(options) {
|
|
78
|
-
return
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
return [
|
|
84
|
+
options && validateAlgorithms('keyManagementAlgorithms', options.keyManagementAlgorithms),
|
|
85
|
+
options &&
|
|
81
86
|
validateAlgorithms('contentEncryptionAlgorithms', options.contentEncryptionAlgorithms),
|
|
82
87
|
options,
|
|
83
|
-
|
|
88
|
+
];
|
|
84
89
|
}
|
|
85
90
|
export async function decryptRecipient(jwe, token, shared, key) {
|
|
86
|
-
const
|
|
87
|
-
const
|
|
91
|
+
const [keyManagementAlgorithms, contentEncryptionAlgorithms, options] = shared;
|
|
92
|
+
const [parsedProt, ciphertext, iv, tag, additionalData] = token;
|
|
93
|
+
const { encrypted_key: encodedKey, header, unprotected } = jwe;
|
|
88
94
|
let joseHeader;
|
|
89
|
-
if (
|
|
90
|
-
if (!isDisjoint(parsedProt,
|
|
95
|
+
if (header !== undefined || unprotected !== undefined) {
|
|
96
|
+
if (!isDisjoint(parsedProt, header, unprotected)) {
|
|
91
97
|
throw new JWEInvalid('JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint');
|
|
92
98
|
}
|
|
93
|
-
joseHeader = { ...parsedProt, ...
|
|
99
|
+
joseHeader = { ...parsedProt, ...header, ...unprotected };
|
|
94
100
|
}
|
|
95
101
|
else {
|
|
96
102
|
joseHeader = parsedProt ?? {};
|
|
@@ -109,7 +115,6 @@ export async function decryptRecipient(jwe, token, shared, key) {
|
|
|
109
115
|
if (typeof enc !== 'string' || !enc) {
|
|
110
116
|
throw new JWEInvalid('missing JWE Encryption Algorithm (enc) in JWE Header');
|
|
111
117
|
}
|
|
112
|
-
const { keyManagementAlgorithms, contentEncryptionAlgorithms } = shared;
|
|
113
118
|
if ((keyManagementAlgorithms && !keyManagementAlgorithms.has(alg)) ||
|
|
114
119
|
(!keyManagementAlgorithms && alg.startsWith('PBES2'))) {
|
|
115
120
|
throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
|
|
@@ -119,8 +124,8 @@ export async function decryptRecipient(jwe, token, shared, key) {
|
|
|
119
124
|
}
|
|
120
125
|
const encEntry = jweEncryption(enc);
|
|
121
126
|
let encryptedKey;
|
|
122
|
-
if (
|
|
123
|
-
encryptedKey = decodeBase64url(
|
|
127
|
+
if (encodedKey !== undefined) {
|
|
128
|
+
encryptedKey = decodeBase64url(encodedKey, 'encrypted_key', JWEInvalid);
|
|
124
129
|
}
|
|
125
130
|
let resolvedKey = false;
|
|
126
131
|
if (typeof key === 'function') {
|
|
@@ -139,7 +144,7 @@ export async function decryptRecipient(jwe, token, shared, key) {
|
|
|
139
144
|
}
|
|
140
145
|
cek = generateCek(encEntry);
|
|
141
146
|
}
|
|
142
|
-
let plaintext = await decrypt(encEntry, cek,
|
|
147
|
+
let plaintext = await decrypt(encEntry, cek, ciphertext, iv, tag, additionalData);
|
|
143
148
|
if (joseHeader.zip === 'DEF') {
|
|
144
149
|
const maxDecompressedLength = options?.maxDecompressedLength ?? 250_000;
|
|
145
150
|
if (maxDecompressedLength === 0) {
|
|
@@ -155,7 +160,7 @@ export async function decryptRecipient(jwe, token, shared, key) {
|
|
|
155
160
|
throw new JWEInvalid('Failed to decompress plaintext', { cause });
|
|
156
161
|
});
|
|
157
162
|
}
|
|
158
|
-
return
|
|
163
|
+
return [plaintext, parsedProt, k, resolvedKey];
|
|
159
164
|
}
|
|
160
165
|
export async function decryptJWE(jwe, shared, key) {
|
|
161
166
|
return decryptRecipient(jwe, shareJWE(jwe), shared, key);
|
|
@@ -9,7 +9,7 @@ import { prepareKey } from './key.js';
|
|
|
9
9
|
import { jweAlgorithm, jweEncryption } from './jwe_algorithms.js';
|
|
10
10
|
import { compress } from './deflate.js';
|
|
11
11
|
export function checkEncryptHeaders(input) {
|
|
12
|
-
const
|
|
12
|
+
const [, protectedHeader, unprotectedHeader, sharedUnprotectedHeader, , , , , crit] = input;
|
|
13
13
|
if (!isDisjoint(protectedHeader, unprotectedHeader, sharedUnprotectedHeader)) {
|
|
14
14
|
throw new JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint');
|
|
15
15
|
}
|
|
@@ -18,7 +18,7 @@ export function checkEncryptHeaders(input) {
|
|
|
18
18
|
...unprotectedHeader,
|
|
19
19
|
...sharedUnprotectedHeader,
|
|
20
20
|
};
|
|
21
|
-
validateCrit(JWEInvalid, JWE_RECOGNIZED,
|
|
21
|
+
validateCrit(JWEInvalid, JWE_RECOGNIZED, crit, protectedHeader, joseHeader);
|
|
22
22
|
if (joseHeader.zip !== undefined && joseHeader.zip !== 'DEF') {
|
|
23
23
|
throw new JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value.');
|
|
24
24
|
}
|
|
@@ -32,20 +32,21 @@ export function checkEncryptHeaders(input) {
|
|
|
32
32
|
if (typeof enc !== 'string' || !enc) {
|
|
33
33
|
throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid');
|
|
34
34
|
}
|
|
35
|
-
return
|
|
35
|
+
return [joseHeader, alg, enc, jweEncryption(enc)];
|
|
36
36
|
}
|
|
37
37
|
export async function encryptJWE(input, checked, key) {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
const [joseHeader, alg, , encEntry] = checked;
|
|
39
|
+
const [inputPlaintext, inputProtectedHeader, inputUnprotectedHeader, sharedUnprotectedHeader, aad, providedCek, inputIv, keyManagementParameters, , unprotectedParameters,] = input;
|
|
40
|
+
let protectedHeader = inputProtectedHeader;
|
|
41
|
+
let unprotectedHeader = inputUnprotectedHeader;
|
|
42
|
+
if (providedCek && (alg === 'dir' || alg === 'ECDH-ES')) {
|
|
42
43
|
throw new TypeError(`setContentEncryptionKey cannot be called with JWE "alg" (Algorithm) Header ${alg}`);
|
|
43
44
|
}
|
|
44
45
|
const algEntry = jweAlgorithm(alg);
|
|
45
46
|
const k = await prepareKey(alg === 'dir' ? encEntry : algEntry, key, 'encrypt');
|
|
46
|
-
const
|
|
47
|
+
const [cek, encryptedKey, parameters] = await encryptKeyManagement(alg, encEntry, k, providedCek, keyManagementParameters);
|
|
47
48
|
if (parameters) {
|
|
48
|
-
if (
|
|
49
|
+
if (unprotectedParameters) {
|
|
49
50
|
unprotectedHeader = unprotectedHeader ? { ...unprotectedHeader, ...parameters } : parameters;
|
|
50
51
|
}
|
|
51
52
|
else {
|
|
@@ -64,20 +65,20 @@ export async function encryptJWE(input, checked, key) {
|
|
|
64
65
|
}
|
|
65
66
|
let additionalData;
|
|
66
67
|
let aadMember;
|
|
67
|
-
if (
|
|
68
|
-
aadMember = b64u(
|
|
68
|
+
if (aad?.byteLength) {
|
|
69
|
+
aadMember = b64u(aad);
|
|
69
70
|
additionalData = concat(protectedHeaderB, encode('.'), encode(aadMember));
|
|
70
71
|
}
|
|
71
72
|
else {
|
|
72
73
|
additionalData = protectedHeaderB;
|
|
73
74
|
}
|
|
74
|
-
let plaintext =
|
|
75
|
+
let plaintext = inputPlaintext;
|
|
75
76
|
if (joseHeader.zip === 'DEF') {
|
|
76
77
|
plaintext = await compress(plaintext).catch((cause) => {
|
|
77
78
|
throw new JWEInvalid('Failed to compress plaintext', { cause });
|
|
78
79
|
});
|
|
79
80
|
}
|
|
80
|
-
const { ciphertext, tag, iv } = await encrypt(encEntry, plaintext, cek,
|
|
81
|
+
const { ciphertext, tag, iv } = await encrypt(encEntry, plaintext, cek, inputIv, additionalData);
|
|
81
82
|
const jwe = {
|
|
82
83
|
ciphertext: b64u(ciphertext),
|
|
83
84
|
};
|
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
import { JOSENotSupported } from '../util/errors.js';
|
|
2
|
-
const unsupportedAlg = 'Invalid or unsupported JWK "alg" (Algorithm) Parameter value';
|
|
3
|
-
function subtleParams(entry, jwk) {
|
|
4
|
-
if (!entry.kty.includes(jwk.kty)) {
|
|
5
|
-
throw new JOSENotSupported(unsupportedAlg);
|
|
6
|
-
}
|
|
7
|
-
return entry.subtleFor?.({ kty: jwk.kty, crv: jwk.crv }) ?? entry.subtle;
|
|
8
|
-
}
|
|
9
2
|
export async function jwkToKey(entry, jwk) {
|
|
10
3
|
if (jwk.kty === 'RSA' && 'oth' in jwk && jwk.oth !== undefined) {
|
|
11
4
|
throw new JOSENotSupported('RSA JWK "oth" (Other Primes Info) Parameter value is not supported');
|
|
12
5
|
}
|
|
13
|
-
|
|
6
|
+
if (!entry.kty.includes(jwk.kty)) {
|
|
7
|
+
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
|
|
8
|
+
}
|
|
9
|
+
const algorithm = entry.resolve?.({ kty: jwk.kty, crv: jwk.crv }) ?? entry.subtle;
|
|
14
10
|
const isPrivate = !!(jwk.d || jwk.priv);
|
|
15
|
-
const keyUsages = isPrivate ? entry.usages.private : entry.usages.public;
|
|
16
11
|
const keyData = { ...jwk };
|
|
17
12
|
if (keyData.kty !== 'AKP') {
|
|
18
13
|
delete keyData.alg;
|
|
19
14
|
}
|
|
20
15
|
delete keyData.use;
|
|
21
|
-
return crypto.subtle.importKey('jwk', keyData, algorithm, jwk.ext ??
|
|
16
|
+
return crypto.subtle.importKey('jwk', keyData, algorithm, jwk.ext ?? !isPrivate, jwk.key_ops ?? entry.usages[isPrivate ? 1 : 0]);
|
|
22
17
|
}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { JOSENotSupported } from '../util/errors.js';
|
|
2
2
|
import { table } from './key_descriptor.js';
|
|
3
|
-
const sig =
|
|
3
|
+
const sig = [['verify'], ['sign']];
|
|
4
4
|
function hmac(bits) {
|
|
5
5
|
const subtle = { name: 'HMAC', hash: `SHA-${bits}` };
|
|
6
|
-
return { kty: ['oct'],
|
|
6
|
+
return { kty: ['oct'], secret: true, subtle, signing: subtle, usages: sig };
|
|
7
7
|
}
|
|
8
|
-
function rsa(
|
|
8
|
+
function rsa(bits, saltLength) {
|
|
9
|
+
const name = saltLength ? 'RSA-PSS' : 'RSASSA-PKCS1-v1_5';
|
|
9
10
|
const subtle = { name, hash: `SHA-${bits}` };
|
|
10
11
|
return {
|
|
11
12
|
kty: ['RSA'],
|
|
12
13
|
subtle,
|
|
13
|
-
|
|
14
|
+
signing: saltLength ? { ...subtle, saltLength } : subtle,
|
|
14
15
|
usages: sig,
|
|
15
|
-
|
|
16
|
+
minRsaBits: 2048,
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
19
|
function ecdsa(crv, bits) {
|
|
@@ -20,7 +21,7 @@ function ecdsa(crv, bits) {
|
|
|
20
21
|
kty: ['EC'],
|
|
21
22
|
crv,
|
|
22
23
|
subtle: { name: 'ECDSA', namedCurve: crv },
|
|
23
|
-
|
|
24
|
+
signing: { name: 'ECDSA', hash: `SHA-${bits}` },
|
|
24
25
|
usages: sig,
|
|
25
26
|
};
|
|
26
27
|
}
|
|
@@ -30,45 +31,43 @@ function eddsa() {
|
|
|
30
31
|
kty: ['OKP'],
|
|
31
32
|
crv: 'Ed25519',
|
|
32
33
|
subtle,
|
|
33
|
-
|
|
34
|
+
signing: subtle,
|
|
34
35
|
usages: sig,
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
|
-
function mldsa(
|
|
38
|
+
function mldsa(bits) {
|
|
39
|
+
const name = `ML-DSA-${bits}`;
|
|
38
40
|
const subtle = { name };
|
|
39
41
|
return {
|
|
40
42
|
kty: ['AKP'],
|
|
41
43
|
subtle,
|
|
42
|
-
|
|
44
|
+
signing: subtle,
|
|
43
45
|
usages: sig,
|
|
44
46
|
};
|
|
45
47
|
}
|
|
46
|
-
const JWS = table({
|
|
48
|
+
export const JWS = table({
|
|
47
49
|
HS256: hmac(256),
|
|
48
50
|
HS384: hmac(384),
|
|
49
51
|
HS512: hmac(512),
|
|
50
|
-
RS256: rsa(
|
|
51
|
-
RS384: rsa(
|
|
52
|
-
RS512: rsa(
|
|
53
|
-
PS256: rsa(
|
|
54
|
-
PS384: rsa(
|
|
55
|
-
PS512: rsa(
|
|
52
|
+
RS256: rsa(256),
|
|
53
|
+
RS384: rsa(384),
|
|
54
|
+
RS512: rsa(512),
|
|
55
|
+
PS256: rsa(256, 32),
|
|
56
|
+
PS384: rsa(384, 48),
|
|
57
|
+
PS512: rsa(512, 64),
|
|
56
58
|
ES256: ecdsa('P-256', 256),
|
|
57
59
|
ES384: ecdsa('P-384', 384),
|
|
58
60
|
ES512: ecdsa('P-521', 512),
|
|
59
61
|
EdDSA: eddsa(),
|
|
60
62
|
Ed25519: eddsa(),
|
|
61
|
-
'ML-DSA-44': mldsa(
|
|
62
|
-
'ML-DSA-65': mldsa(
|
|
63
|
-
'ML-DSA-87': mldsa(
|
|
63
|
+
'ML-DSA-44': mldsa(44),
|
|
64
|
+
'ML-DSA-65': mldsa(65),
|
|
65
|
+
'ML-DSA-87': mldsa(87),
|
|
64
66
|
});
|
|
65
67
|
export function jwsAlgorithm(alg) {
|
|
66
|
-
const entry = JWS[alg];
|
|
68
|
+
const entry = typeof alg === 'string' ? JWS[alg] : undefined;
|
|
67
69
|
if (!entry) {
|
|
68
70
|
throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
|
|
69
71
|
}
|
|
70
72
|
return entry;
|
|
71
73
|
}
|
|
72
|
-
export function maybeJWSAlgorithm(alg) {
|
|
73
|
-
return JWS[alg];
|
|
74
|
-
}
|
|
@@ -13,6 +13,9 @@ export function unencodedPayload(protectedHeader) {
|
|
|
13
13
|
}
|
|
14
14
|
export async function createSignature(input, key) {
|
|
15
15
|
const { protectedHeader, unprotectedHeader } = input;
|
|
16
|
+
if (!protectedHeader && !unprotectedHeader) {
|
|
17
|
+
throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()');
|
|
18
|
+
}
|
|
16
19
|
if (!isDisjoint(protectedHeader, unprotectedHeader)) {
|
|
17
20
|
throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint');
|
|
18
21
|
}
|
|
@@ -20,7 +23,7 @@ export async function createSignature(input, key) {
|
|
|
20
23
|
validateCritDuplicates(JWSInvalid, protectedHeader);
|
|
21
24
|
const extensions = validateCrit(JWSInvalid, JWS_RECOGNIZED, input.crit, protectedHeader, joseHeader);
|
|
22
25
|
let b64 = true;
|
|
23
|
-
if (extensions.
|
|
26
|
+
if (extensions.includes('b64')) {
|
|
24
27
|
b64 = protectedHeader.b64;
|
|
25
28
|
if (typeof b64 !== 'boolean') {
|
|
26
29
|
throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
|
|
@@ -34,11 +37,11 @@ export async function createSignature(input, key) {
|
|
|
34
37
|
let payloadS;
|
|
35
38
|
let payloadB;
|
|
36
39
|
if (b64) {
|
|
37
|
-
const encoded = (input.encoded ??=
|
|
38
|
-
encoded
|
|
39
|
-
encoded
|
|
40
|
-
payloadS = encoded
|
|
41
|
-
payloadB = encoded
|
|
40
|
+
const encoded = (input.encoded ??= []);
|
|
41
|
+
encoded[0] ??= b64u(input.payload);
|
|
42
|
+
encoded[1] ??= encode(encoded[0]);
|
|
43
|
+
payloadS = encoded[0];
|
|
44
|
+
payloadB = encoded[1];
|
|
42
45
|
}
|
|
43
46
|
else {
|
|
44
47
|
payloadB = input.payload;
|
|
@@ -64,5 +67,8 @@ export async function createSignature(input, key) {
|
|
|
64
67
|
if (protectedHeader) {
|
|
65
68
|
jws.protected = protectedHeaderString;
|
|
66
69
|
}
|
|
70
|
+
if (unprotectedHeader) {
|
|
71
|
+
jws.header = unprotectedHeader;
|
|
72
|
+
}
|
|
67
73
|
return jws;
|
|
68
74
|
}
|
|
@@ -7,42 +7,41 @@ import { isDisjoint } from './type_checks.js';
|
|
|
7
7
|
import { validateCrit, validateAlgorithms, JWS_RECOGNIZED } from './options.js';
|
|
8
8
|
import { prepareKey } from './key.js';
|
|
9
9
|
export function verifyResult(jws, verified) {
|
|
10
|
-
const
|
|
10
|
+
const [payload, parsedProt, , key, resolvedKey] = verified;
|
|
11
|
+
const result = { payload };
|
|
11
12
|
if (jws.protected !== undefined) {
|
|
12
|
-
result.protectedHeader =
|
|
13
|
+
result.protectedHeader = parsedProt;
|
|
13
14
|
}
|
|
14
15
|
if (jws.header !== undefined) {
|
|
15
16
|
result.unprotectedHeader = jws.header;
|
|
16
17
|
}
|
|
17
|
-
if (
|
|
18
|
-
return { ...result, key
|
|
18
|
+
if (resolvedKey) {
|
|
19
|
+
return { ...result, key };
|
|
19
20
|
}
|
|
20
21
|
return result;
|
|
21
22
|
}
|
|
22
23
|
export function prepareVerify(options) {
|
|
23
|
-
return
|
|
24
|
-
algorithms: options && validateAlgorithms('algorithms', options.algorithms),
|
|
25
|
-
crit: options?.crit,
|
|
26
|
-
};
|
|
24
|
+
return [options && validateAlgorithms('algorithms', options.algorithms), options?.crit];
|
|
27
25
|
}
|
|
28
26
|
export async function verifySignature(jws, shared, key) {
|
|
27
|
+
const { protected: encodedProtected, header, payload: inputPayload } = jws;
|
|
29
28
|
let parsedProt = {};
|
|
30
|
-
if (
|
|
31
|
-
parsedProt = parseJoseHeader(
|
|
29
|
+
if (encodedProtected) {
|
|
30
|
+
parsedProt = parseJoseHeader(encodedProtected, JWSInvalid, 'JWS Protected Header is invalid');
|
|
32
31
|
}
|
|
33
32
|
let joseHeader;
|
|
34
|
-
if (
|
|
35
|
-
if (!isDisjoint(parsedProt,
|
|
33
|
+
if (header !== undefined) {
|
|
34
|
+
if (!isDisjoint(parsedProt, header)) {
|
|
36
35
|
throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint');
|
|
37
36
|
}
|
|
38
|
-
joseHeader = { ...parsedProt, ...
|
|
37
|
+
joseHeader = { ...parsedProt, ...header };
|
|
39
38
|
}
|
|
40
39
|
else {
|
|
41
40
|
joseHeader = parsedProt;
|
|
42
41
|
}
|
|
43
|
-
const extensions = validateCrit(JWSInvalid, JWS_RECOGNIZED, shared
|
|
42
|
+
const extensions = validateCrit(JWSInvalid, JWS_RECOGNIZED, shared[1], parsedProt, joseHeader);
|
|
44
43
|
let b64 = true;
|
|
45
|
-
if (extensions.
|
|
44
|
+
if (extensions.includes('b64')) {
|
|
46
45
|
b64 = parsedProt.b64;
|
|
47
46
|
if (typeof b64 !== 'boolean') {
|
|
48
47
|
throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
|
|
@@ -52,15 +51,15 @@ export async function verifySignature(jws, shared, key) {
|
|
|
52
51
|
if (typeof alg !== 'string' || !alg) {
|
|
53
52
|
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
54
53
|
}
|
|
55
|
-
if (shared
|
|
54
|
+
if (shared[0] && !shared[0].has(alg)) {
|
|
56
55
|
throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
|
|
57
56
|
}
|
|
58
57
|
if (b64) {
|
|
59
|
-
if (typeof
|
|
58
|
+
if (typeof inputPayload !== 'string') {
|
|
60
59
|
throw new JWSInvalid('JWS Payload must be a string');
|
|
61
60
|
}
|
|
62
61
|
}
|
|
63
|
-
else if (typeof
|
|
62
|
+
else if (typeof inputPayload !== 'string' && !(inputPayload instanceof Uint8Array)) {
|
|
64
63
|
throw new JWSInvalid('JWS Payload must be a string or an Uint8Array instance');
|
|
65
64
|
}
|
|
66
65
|
let resolvedKey = false;
|
|
@@ -69,12 +68,12 @@ export async function verifySignature(jws, shared, key) {
|
|
|
69
68
|
resolvedKey = true;
|
|
70
69
|
}
|
|
71
70
|
const entry = jwsAlgorithm(alg);
|
|
72
|
-
const data = concat(
|
|
71
|
+
const data = concat(encodedProtected !== undefined ? encode(encodedProtected) : new Uint8Array(), encode('.'), typeof inputPayload === 'string'
|
|
73
72
|
? b64
|
|
74
73
|
?
|
|
75
|
-
(shared
|
|
76
|
-
: encoder.encode(
|
|
77
|
-
:
|
|
74
|
+
(shared[2] ??= encodeBase64url(inputPayload, 'payload', JWSInvalid))
|
|
75
|
+
: encoder.encode(inputPayload)
|
|
76
|
+
: inputPayload);
|
|
78
77
|
const signature = decodeBase64url(jws.signature, 'signature', JWSInvalid);
|
|
79
78
|
const k = await prepareKey(entry, key, 'verify');
|
|
80
79
|
const verified = await verify(entry, k, signature, data);
|
|
@@ -83,15 +82,15 @@ export async function verifySignature(jws, shared, key) {
|
|
|
83
82
|
}
|
|
84
83
|
let payload;
|
|
85
84
|
if (b64) {
|
|
86
|
-
payload = decodeBase64url(
|
|
85
|
+
payload = decodeBase64url(inputPayload, 'payload', JWSInvalid);
|
|
87
86
|
}
|
|
88
|
-
else if (typeof
|
|
89
|
-
payload = encoder.encode(
|
|
87
|
+
else if (typeof inputPayload === 'string') {
|
|
88
|
+
payload = encoder.encode(inputPayload);
|
|
90
89
|
}
|
|
91
90
|
else {
|
|
92
|
-
payload =
|
|
91
|
+
payload = inputPayload;
|
|
93
92
|
}
|
|
94
|
-
return
|
|
93
|
+
return [payload, parsedProt, b64, k, resolvedKey];
|
|
95
94
|
}
|
|
96
95
|
export async function verifyCompact(jws, shared, key) {
|
|
97
96
|
if (jws instanceof Uint8Array) {
|