repocairn 0.1.6 → 0.1.7
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/node_modules/fast-uri/index.js +201 -48
- package/node_modules/fast-uri/lib/schemes.js +9 -4
- package/node_modules/fast-uri/lib/utils.js +388 -91
- package/node_modules/fast-uri/package.json +1 -1
- package/node_modules/fast-uri/test/component-safe-serialization.test.js +105 -0
- package/node_modules/fast-uri/test/equal.test.js +31 -3
- package/node_modules/fast-uri/test/fixtures/uri-js-parse.json +2 -0
- package/node_modules/fast-uri/test/ipv6-canonical.test.js +34 -0
- package/node_modules/fast-uri/test/ipv6-validation.test.js +90 -0
- package/node_modules/fast-uri/test/malformed-percent.test.js +77 -0
- package/node_modules/fast-uri/test/malformed-urn.test.js +61 -0
- package/node_modules/fast-uri/test/parse.test.js +7 -3
- package/node_modules/fast-uri/test/query-fragment-normalization.test.js +33 -0
- package/node_modules/fast-uri/test/reserved-path-normalization.test.js +109 -0
- package/node_modules/fast-uri/test/scheme-validation.test.js +124 -0
- package/node_modules/fast-uri/test/security-normalization.test.js +101 -0
- package/node_modules/fast-uri/test/security.test.js +75 -3
- package/node_modules/fast-uri/test/urn-full-input.test.js +29 -0
- package/node_modules/fast-uri/test/websocket-query-preservation.test.js +24 -0
- package/node_modules/jose/dist/types/jwks/remote.d.ts +4 -4
- package/node_modules/jose/dist/types/jws/general/verify.d.ts +3 -3
- package/node_modules/jose/dist/types/jwt/encrypt.d.ts +8 -14
- package/node_modules/jose/dist/types/jwt/sign.d.ts +8 -14
- package/node_modules/jose/dist/types/jwt/unsecured.d.ts +9 -15
- package/node_modules/jose/dist/webapi/jwe/compact/encrypt.js +30 -8
- package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +8 -4
- package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +6 -13
- package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +19 -19
- package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +27 -20
- package/node_modules/jose/dist/webapi/jwk/embedded.js +15 -1
- package/node_modules/jose/dist/webapi/jwk/thumbprint.js +11 -5
- package/node_modules/jose/dist/webapi/jwks/local.js +45 -53
- package/node_modules/jose/dist/webapi/jwks/remote.js +82 -103
- package/node_modules/jose/dist/webapi/jws/compact/sign.js +10 -9
- package/node_modules/jose/dist/webapi/jws/flattened/sign.js +2 -1
- package/node_modules/jose/dist/webapi/jws/flattened/verify.js +8 -7
- package/node_modules/jose/dist/webapi/jws/general/sign.js +5 -3
- package/node_modules/jose/dist/webapi/jws/general/verify.js +51 -21
- package/node_modules/jose/dist/webapi/jwt/decrypt.js +7 -9
- package/node_modules/jose/dist/webapi/jwt/encrypt.js +21 -50
- package/node_modules/jose/dist/webapi/jwt/sign.js +8 -41
- package/node_modules/jose/dist/webapi/jwt/unsecured.js +21 -42
- package/node_modules/jose/dist/webapi/key/generate_key_pair.js +6 -2
- package/node_modules/jose/dist/webapi/key/generate_secret.js +7 -6
- package/node_modules/jose/dist/webapi/key/import.js +16 -12
- package/node_modules/jose/dist/webapi/lib/asn1.js +7 -2
- package/node_modules/jose/dist/webapi/lib/content_encryption.js +7 -15
- package/node_modules/jose/dist/webapi/lib/deflate.js +8 -0
- package/node_modules/jose/dist/webapi/lib/helpers.js +1 -1
- package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +85 -28
- package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +40 -12
- package/node_modules/jose/dist/webapi/lib/jwk_metadata.js +20 -0
- package/node_modules/jose/dist/webapi/lib/jws_sign.js +47 -36
- package/node_modules/jose/dist/webapi/lib/jws_verify.js +77 -45
- package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +82 -47
- package/node_modules/jose/dist/webapi/lib/key.js +24 -9
- package/node_modules/jose/dist/webapi/lib/key_management.js +10 -4
- package/node_modules/jose/dist/webapi/lib/key_options.js +6 -0
- package/node_modules/jose/dist/webapi/lib/options.js +26 -0
- package/node_modules/jose/dist/webapi/lib/type_checks.js +11 -13
- package/node_modules/jose/package.json +1 -1
- package/package.json +1 -1
|
@@ -7,35 +7,75 @@ import { concat, decoder, encode } from './buffer_utils.js';
|
|
|
7
7
|
import { validateCrit, validateAlgorithms, JWE_RECOGNIZED } from './options.js';
|
|
8
8
|
import { prepareKey } from './key.js';
|
|
9
9
|
import { jweAlgorithm, jweEncryption } from './jwe_algorithms.js';
|
|
10
|
-
import { decompress } from './deflate.js';
|
|
11
|
-
export function
|
|
12
|
-
const { ciphertext, protected: encodedProtected, unprotected } = jwe;
|
|
13
|
-
if (
|
|
10
|
+
import { decompress, validateZip } from './deflate.js';
|
|
11
|
+
export function snapshotSharedJWE(jwe) {
|
|
12
|
+
const { aad, ciphertext, iv, protected: encodedProtected, tag, unprotected } = jwe;
|
|
13
|
+
if (iv !== undefined && typeof iv !== 'string') {
|
|
14
14
|
throw new JWEInvalid('JWE Initialization Vector incorrect type');
|
|
15
15
|
}
|
|
16
16
|
if (typeof ciphertext !== 'string') {
|
|
17
17
|
throw new JWEInvalid('JWE Ciphertext missing or incorrect type');
|
|
18
18
|
}
|
|
19
|
-
if (
|
|
19
|
+
if (tag !== undefined && typeof tag !== 'string') {
|
|
20
20
|
throw new JWEInvalid('JWE Authentication Tag incorrect type');
|
|
21
21
|
}
|
|
22
22
|
if (encodedProtected !== undefined && typeof encodedProtected !== 'string') {
|
|
23
23
|
throw new JWEInvalid('JWE Protected Header incorrect type');
|
|
24
24
|
}
|
|
25
|
-
if (
|
|
25
|
+
if (aad !== undefined && (typeof aad !== 'string' || !aad)) {
|
|
26
26
|
throw new JWEInvalid('JWE AAD incorrect type');
|
|
27
27
|
}
|
|
28
28
|
if (unprotected !== undefined && !isObject(unprotected)) {
|
|
29
29
|
throw new JWEInvalid('JWE Shared Unprotected Header incorrect type');
|
|
30
30
|
}
|
|
31
|
+
return {
|
|
32
|
+
aad,
|
|
33
|
+
ciphertext,
|
|
34
|
+
iv,
|
|
35
|
+
protected: encodedProtected,
|
|
36
|
+
tag,
|
|
37
|
+
unprotected: unprotected === undefined ? undefined : { ...unprotected },
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function snapshotRecipientJWE(recipient) {
|
|
41
|
+
let header;
|
|
42
|
+
let headerAlg;
|
|
43
|
+
try {
|
|
44
|
+
const { header: inputHeader } = recipient;
|
|
45
|
+
if (isObject(inputHeader)) {
|
|
46
|
+
headerAlg = inputHeader.alg;
|
|
47
|
+
const parameters = Object.keys(inputHeader);
|
|
48
|
+
if (!parameters.includes('alg'))
|
|
49
|
+
headerAlg = undefined;
|
|
50
|
+
header = Object.fromEntries(parameters.map((parameter) => [
|
|
51
|
+
parameter,
|
|
52
|
+
parameter === 'alg' ? headerAlg : inputHeader[parameter],
|
|
53
|
+
]));
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
header = inputHeader;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
return [undefined, headerAlg, error];
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const { encrypted_key: encryptedKey } = recipient;
|
|
64
|
+
return [{ encrypted_key: encryptedKey, header }, headerAlg];
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
return [undefined, headerAlg, error];
|
|
68
|
+
}
|
|
31
69
|
}
|
|
32
70
|
export function checkRecipient(jwe) {
|
|
33
71
|
const { encrypted_key: encryptedKey, header } = jwe;
|
|
34
72
|
if (encryptedKey !== undefined && typeof encryptedKey !== 'string') {
|
|
35
73
|
throw new JWEInvalid('JWE Encrypted Key incorrect type');
|
|
36
74
|
}
|
|
37
|
-
if (header !== undefined
|
|
38
|
-
|
|
75
|
+
if (header !== undefined) {
|
|
76
|
+
if (!isObject(header)) {
|
|
77
|
+
throw new JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type');
|
|
78
|
+
}
|
|
39
79
|
}
|
|
40
80
|
if (jwe.protected === undefined && header === undefined && jwe.unprotected === undefined) {
|
|
41
81
|
throw new JWEInvalid('JOSE Header missing');
|
|
@@ -44,7 +84,7 @@ export function checkRecipient(jwe) {
|
|
|
44
84
|
export function shareJWE(jwe) {
|
|
45
85
|
const { protected: encodedProtected, ciphertext, iv, tag, aad } = jwe;
|
|
46
86
|
let parsedProt;
|
|
47
|
-
if (encodedProtected) {
|
|
87
|
+
if (encodedProtected !== undefined) {
|
|
48
88
|
parsedProt = parseJoseHeader(encodedProtected, JWEInvalid, 'JWE Protected Header is invalid');
|
|
49
89
|
}
|
|
50
90
|
const protectedHeader = encodedProtected !== undefined ? encode(encodedProtected) : new Uint8Array();
|
|
@@ -84,13 +124,14 @@ export function prepareDecrypt(options) {
|
|
|
84
124
|
options && validateAlgorithms('keyManagementAlgorithms', options.keyManagementAlgorithms),
|
|
85
125
|
options &&
|
|
86
126
|
validateAlgorithms('contentEncryptionAlgorithms', options.contentEncryptionAlgorithms),
|
|
87
|
-
options,
|
|
127
|
+
options?.crit,
|
|
128
|
+
options?.maxPBES2Count,
|
|
129
|
+
options?.maxDecompressedLength,
|
|
88
130
|
];
|
|
89
131
|
}
|
|
90
132
|
export async function decryptRecipient(jwe, token, shared, key) {
|
|
91
|
-
const [
|
|
92
|
-
const
|
|
93
|
-
const { encrypted_key: encodedKey, header, unprotected } = jwe;
|
|
133
|
+
const [parsedProt] = token;
|
|
134
|
+
const { header, unprotected } = jwe;
|
|
94
135
|
let joseHeader;
|
|
95
136
|
if (header !== undefined || unprotected !== undefined) {
|
|
96
137
|
if (!isDisjoint(parsedProt, header, unprotected)) {
|
|
@@ -101,13 +142,14 @@ export async function decryptRecipient(jwe, token, shared, key) {
|
|
|
101
142
|
else {
|
|
102
143
|
joseHeader = parsedProt ?? {};
|
|
103
144
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
145
|
+
return decryptRecipientCore(jwe, token, shared, key, joseHeader);
|
|
146
|
+
}
|
|
147
|
+
async function decryptRecipientCore(jwe, token, shared, key, joseHeader) {
|
|
148
|
+
const [keyManagementAlgorithms, contentEncryptionAlgorithms, crit, maxPBES2Count, maxDecompressedLength,] = shared;
|
|
149
|
+
const [parsedProt, ciphertext, iv, tag, additionalData] = token;
|
|
150
|
+
const { encrypted_key: encodedKey } = jwe;
|
|
151
|
+
validateCrit(JWEInvalid, JWE_RECOGNIZED, crit, parsedProt, joseHeader);
|
|
152
|
+
validateZip(joseHeader, parsedProt);
|
|
111
153
|
const { alg, enc } = joseHeader;
|
|
112
154
|
if (typeof alg !== 'string' || !alg) {
|
|
113
155
|
throw new JWEInvalid('missing JWE Algorithm (alg) in JWE Header');
|
|
@@ -136,7 +178,12 @@ export async function decryptRecipient(jwe, token, shared, key) {
|
|
|
136
178
|
const k = await prepareKey(alg === 'dir' ? encEntry : algEntry, key, 'decrypt');
|
|
137
179
|
let cek;
|
|
138
180
|
try {
|
|
139
|
-
cek = await decryptKeyManagement(alg, encEntry, k, encryptedKey, joseHeader,
|
|
181
|
+
cek = await decryptKeyManagement(alg, encEntry, k, encryptedKey, joseHeader, maxPBES2Count);
|
|
182
|
+
if (encodedKey !== undefined &&
|
|
183
|
+
cek instanceof Uint8Array &&
|
|
184
|
+
cek.byteLength << 3 !== encEntry.cekBits) {
|
|
185
|
+
cek = generateCek(encEntry);
|
|
186
|
+
}
|
|
140
187
|
}
|
|
141
188
|
catch (err) {
|
|
142
189
|
if (err instanceof TypeError || err instanceof JWEInvalid || err instanceof JOSENotSupported) {
|
|
@@ -146,15 +193,15 @@ export async function decryptRecipient(jwe, token, shared, key) {
|
|
|
146
193
|
}
|
|
147
194
|
let plaintext = await decrypt(encEntry, cek, ciphertext, iv, tag, additionalData);
|
|
148
195
|
if (joseHeader.zip === 'DEF') {
|
|
149
|
-
const
|
|
150
|
-
if (
|
|
196
|
+
const decompressionLimit = maxDecompressedLength ?? 250_000;
|
|
197
|
+
if (decompressionLimit === 0) {
|
|
151
198
|
throw new JOSENotSupported('JWE "zip" (Compression Algorithm) Header Parameter is not supported.');
|
|
152
199
|
}
|
|
153
|
-
if (
|
|
154
|
-
(!Number.isSafeInteger(
|
|
200
|
+
if (decompressionLimit !== Infinity &&
|
|
201
|
+
(!Number.isSafeInteger(decompressionLimit) || decompressionLimit < 1)) {
|
|
155
202
|
throw new TypeError('maxDecompressedLength must be 0, a positive safe integer, or Infinity');
|
|
156
203
|
}
|
|
157
|
-
plaintext = await decompress(plaintext,
|
|
204
|
+
plaintext = await decompress(plaintext, decompressionLimit).catch((cause) => {
|
|
158
205
|
if (cause instanceof JWEInvalid)
|
|
159
206
|
throw cause;
|
|
160
207
|
throw new JWEInvalid('Failed to decompress plaintext', { cause });
|
|
@@ -176,11 +223,21 @@ export async function decryptCompact(jwe, shared, key) {
|
|
|
176
223
|
if (length !== 5) {
|
|
177
224
|
throw new JWEInvalid('Invalid Compact JWE');
|
|
178
225
|
}
|
|
179
|
-
|
|
226
|
+
const flattened = {
|
|
180
227
|
ciphertext,
|
|
181
228
|
iv: iv || undefined,
|
|
182
229
|
protected: protectedHeader,
|
|
183
230
|
tag: tag || undefined,
|
|
184
231
|
encrypted_key: encryptedKey || undefined,
|
|
185
|
-
}
|
|
232
|
+
};
|
|
233
|
+
const parsedProt = parseJoseHeader(protectedHeader, JWEInvalid, 'JWE Protected Header is invalid');
|
|
234
|
+
const protectedBytes = encode(protectedHeader);
|
|
235
|
+
const token = [
|
|
236
|
+
parsedProt,
|
|
237
|
+
decodeBase64url(ciphertext, 'ciphertext', JWEInvalid),
|
|
238
|
+
iv ? decodeBase64url(iv, 'iv', JWEInvalid) : undefined,
|
|
239
|
+
tag ? decodeBase64url(tag, 'tag', JWEInvalid) : undefined,
|
|
240
|
+
protectedBytes,
|
|
241
|
+
];
|
|
242
|
+
return decryptRecipientCore(flattened, token, shared, key, parsedProt);
|
|
186
243
|
}
|
|
@@ -1,33 +1,54 @@
|
|
|
1
1
|
import { encode as b64u } from '../util/base64url.js';
|
|
2
2
|
import { encrypt } from './content_encryption.js';
|
|
3
3
|
import { encryptKeyManagement } from './key_management.js';
|
|
4
|
-
import {
|
|
5
|
-
import { isDisjoint } from './type_checks.js';
|
|
4
|
+
import { JWEInvalid } from '../util/errors.js';
|
|
5
|
+
import { assertUint8Array, isDisjoint, isObject } from './type_checks.js';
|
|
6
6
|
import { concat, encode } from './buffer_utils.js';
|
|
7
|
-
import { validateCrit, JWE_RECOGNIZED } from './options.js';
|
|
7
|
+
import { serializeJoseHeader, validateCrit, validateCritDuplicates, JWE_RECOGNIZED, } from './options.js';
|
|
8
8
|
import { prepareKey } from './key.js';
|
|
9
9
|
import { jweAlgorithm, jweEncryption } from './jwe_algorithms.js';
|
|
10
|
-
import { compress } from './deflate.js';
|
|
10
|
+
import { compress, validateZip } from './deflate.js';
|
|
11
|
+
import { unprotected } from './helpers.js';
|
|
11
12
|
export function checkDisjoint(protectedHeader, unprotectedHeader, sharedUnprotectedHeader) {
|
|
12
13
|
if (!isDisjoint(protectedHeader, unprotectedHeader, sharedUnprotectedHeader)) {
|
|
13
14
|
throw new JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint');
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
export function checkEncryptHeaders(input) {
|
|
17
|
-
|
|
18
|
+
let [, protectedHeader, unprotectedHeader, sharedUnprotectedHeader, aad, cek, iv, keyManagementParameters, crit,] = input;
|
|
19
|
+
if (aad !== undefined) {
|
|
20
|
+
assertUint8Array(aad, 'JWE Additional Authenticated Data');
|
|
21
|
+
}
|
|
22
|
+
if (cek !== undefined) {
|
|
23
|
+
assertUint8Array(cek, 'JWE Content Encryption Key');
|
|
24
|
+
}
|
|
25
|
+
if (iv !== undefined) {
|
|
26
|
+
assertUint8Array(iv, 'JWE Initialization Vector');
|
|
27
|
+
}
|
|
28
|
+
if (protectedHeader !== undefined) {
|
|
29
|
+
protectedHeader = serializeJoseHeader(JWEInvalid, protectedHeader)[0];
|
|
30
|
+
input[1] = protectedHeader;
|
|
31
|
+
}
|
|
32
|
+
if (unprotectedHeader !== undefined) {
|
|
33
|
+
unprotectedHeader = serializeJoseHeader(JWEInvalid, unprotectedHeader)[0];
|
|
34
|
+
input[2] = unprotectedHeader;
|
|
35
|
+
}
|
|
36
|
+
if (sharedUnprotectedHeader !== undefined) {
|
|
37
|
+
sharedUnprotectedHeader = serializeJoseHeader(JWEInvalid, sharedUnprotectedHeader)[0];
|
|
38
|
+
input[3] = sharedUnprotectedHeader;
|
|
39
|
+
}
|
|
40
|
+
if (keyManagementParameters !== undefined && !isObject(keyManagementParameters)) {
|
|
41
|
+
throw new TypeError('JWE Key Management Parameters must be an object');
|
|
42
|
+
}
|
|
18
43
|
checkDisjoint(protectedHeader, unprotectedHeader, sharedUnprotectedHeader);
|
|
19
44
|
const joseHeader = {
|
|
20
45
|
...protectedHeader,
|
|
21
46
|
...unprotectedHeader,
|
|
22
47
|
...sharedUnprotectedHeader,
|
|
23
48
|
};
|
|
49
|
+
validateCritDuplicates(JWEInvalid, protectedHeader);
|
|
24
50
|
validateCrit(JWEInvalid, JWE_RECOGNIZED, crit, protectedHeader, joseHeader);
|
|
25
|
-
|
|
26
|
-
throw new JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value.');
|
|
27
|
-
}
|
|
28
|
-
if (joseHeader.zip !== undefined && !protectedHeader?.zip) {
|
|
29
|
-
throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header Parameter MUST be in a protected header.');
|
|
30
|
-
}
|
|
51
|
+
validateZip(joseHeader, protectedHeader);
|
|
31
52
|
const { alg, enc } = joseHeader;
|
|
32
53
|
if (typeof alg !== 'string' || !alg) {
|
|
33
54
|
throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid');
|
|
@@ -109,6 +130,13 @@ export async function encryptJWE(input, checked, key) {
|
|
|
109
130
|
}
|
|
110
131
|
return jwe;
|
|
111
132
|
}
|
|
112
|
-
export async function createJWE(input, key) {
|
|
133
|
+
export async function createJWE(input, key, options) {
|
|
134
|
+
if (!input[1] && !input[2] && !input[3]) {
|
|
135
|
+
throw new JWEInvalid('either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()');
|
|
136
|
+
}
|
|
137
|
+
if (options !== undefined) {
|
|
138
|
+
input[8] = options?.crit;
|
|
139
|
+
input[9] = options ? unprotected in options : false;
|
|
140
|
+
}
|
|
113
141
|
return encryptJWE(input, checkEncryptHeaders(input), key);
|
|
114
142
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function snapshotJwk(jwk) {
|
|
2
|
+
return { __proto__: null, ...jwk };
|
|
3
|
+
}
|
|
4
|
+
export function normalizeJwk(jwk) {
|
|
5
|
+
const normalized = snapshotJwk(jwk);
|
|
6
|
+
if (normalized.ext !== undefined && typeof normalized.ext !== 'boolean') {
|
|
7
|
+
throw new TypeError('"ext" (Extractable) Parameter must be a boolean');
|
|
8
|
+
}
|
|
9
|
+
if (normalized.key_ops !== undefined) {
|
|
10
|
+
const value = normalized.key_ops;
|
|
11
|
+
const keyOps = Array.isArray(value) ? [...value] : undefined;
|
|
12
|
+
if (!keyOps ||
|
|
13
|
+
keyOps.some((operation) => typeof operation !== 'string') ||
|
|
14
|
+
new Set(keyOps).size !== keyOps.length) {
|
|
15
|
+
throw new TypeError('"key_ops" (Key Operations) Parameter must be an array of unique strings');
|
|
16
|
+
}
|
|
17
|
+
normalized.key_ops = keyOps;
|
|
18
|
+
}
|
|
19
|
+
return normalized;
|
|
20
|
+
}
|
|
@@ -4,15 +4,37 @@ import { jwsAlgorithm } from './jws_algorithms.js';
|
|
|
4
4
|
import { isDisjoint } from './type_checks.js';
|
|
5
5
|
import { JWSInvalid } from '../util/errors.js';
|
|
6
6
|
import { concat, encode } from './buffer_utils.js';
|
|
7
|
-
import { validateCrit, validateCritDuplicates, JWS_RECOGNIZED } from './options.js';
|
|
7
|
+
import { serializeJoseHeader, validateB64, validateCrit, validateCritDuplicates, JWS_RECOGNIZED, } from './options.js';
|
|
8
8
|
import { prepareKey } from './key.js';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
function serializeProtectedHeader(protectedHeader) {
|
|
10
|
+
if (protectedHeader === undefined)
|
|
11
|
+
return [undefined, ''];
|
|
12
|
+
const normalized = serializeJoseHeader(JWSInvalid, protectedHeader);
|
|
13
|
+
return [normalized[0], b64u(normalized[1])];
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
function validateSignatureHeader(protectedHeader, joseHeader, crit) {
|
|
16
|
+
validateCritDuplicates(JWSInvalid, protectedHeader);
|
|
17
|
+
return validateB64(protectedHeader, validateCrit(JWSInvalid, JWS_RECOGNIZED, crit, protectedHeader, joseHeader));
|
|
18
|
+
}
|
|
19
|
+
function signatureAlgorithm(joseHeader) {
|
|
20
|
+
const alg = joseHeader.alg;
|
|
21
|
+
if (typeof alg !== 'string' || !alg) {
|
|
22
|
+
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
23
|
+
}
|
|
24
|
+
return jwsAlgorithm(alg);
|
|
25
|
+
}
|
|
26
|
+
async function signSignature(protectedHeader, payload, entry, key) {
|
|
27
|
+
const data = concat(encode(protectedHeader), encode('.'), payload);
|
|
28
|
+
const k = await prepareKey(entry, key, 'sign');
|
|
29
|
+
return b64u(await sign(entry, k, data));
|
|
30
|
+
}
|
|
31
|
+
export async function createSignature(input, key, assertB64) {
|
|
32
|
+
let { protectedHeader, unprotectedHeader } = input;
|
|
33
|
+
let protectedHeaderString;
|
|
34
|
+
[protectedHeader, protectedHeaderString] = serializeProtectedHeader(protectedHeader);
|
|
35
|
+
if (unprotectedHeader !== undefined) {
|
|
36
|
+
unprotectedHeader = serializeJoseHeader(JWSInvalid, unprotectedHeader)[0];
|
|
37
|
+
}
|
|
16
38
|
if (!protectedHeader && !unprotectedHeader) {
|
|
17
39
|
throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()');
|
|
18
40
|
}
|
|
@@ -20,20 +42,9 @@ export async function createSignature(input, key) {
|
|
|
20
42
|
throw new JWSInvalid('JWS Protected and JWS Unprotected Header Parameter names must be disjoint');
|
|
21
43
|
}
|
|
22
44
|
const joseHeader = { ...protectedHeader, ...unprotectedHeader };
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (extensions.includes('b64')) {
|
|
27
|
-
b64 = protectedHeader.b64;
|
|
28
|
-
if (typeof b64 !== 'boolean') {
|
|
29
|
-
throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
const { alg } = joseHeader;
|
|
33
|
-
if (typeof alg !== 'string' || !alg) {
|
|
34
|
-
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
35
|
-
}
|
|
36
|
-
const entry = jwsAlgorithm(alg);
|
|
45
|
+
const b64 = validateSignatureHeader(protectedHeader, joseHeader, input.crit);
|
|
46
|
+
assertB64?.(b64);
|
|
47
|
+
const entry = signatureAlgorithm(joseHeader);
|
|
37
48
|
let payloadS;
|
|
38
49
|
let payloadB;
|
|
39
50
|
if (b64) {
|
|
@@ -47,21 +58,8 @@ export async function createSignature(input, key) {
|
|
|
47
58
|
payloadB = input.payload;
|
|
48
59
|
payloadS = '';
|
|
49
60
|
}
|
|
50
|
-
let protectedHeaderString;
|
|
51
|
-
let protectedHeaderBytes;
|
|
52
|
-
if (protectedHeader) {
|
|
53
|
-
protectedHeaderString = b64u(JSON.stringify(protectedHeader));
|
|
54
|
-
protectedHeaderBytes = encode(protectedHeaderString);
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
protectedHeaderString = '';
|
|
58
|
-
protectedHeaderBytes = new Uint8Array();
|
|
59
|
-
}
|
|
60
|
-
const data = concat(protectedHeaderBytes, encode('.'), payloadB);
|
|
61
|
-
const k = await prepareKey(entry, key, 'sign');
|
|
62
|
-
const signature = await sign(entry, k, data);
|
|
63
61
|
const jws = {
|
|
64
|
-
signature:
|
|
62
|
+
signature: await signSignature(protectedHeaderString, payloadB, entry, key),
|
|
65
63
|
payload: payloadS,
|
|
66
64
|
};
|
|
67
65
|
if (protectedHeader) {
|
|
@@ -70,5 +68,18 @@ export async function createSignature(input, key) {
|
|
|
70
68
|
if (unprotectedHeader) {
|
|
71
69
|
jws.header = unprotectedHeader;
|
|
72
70
|
}
|
|
73
|
-
return jws;
|
|
71
|
+
return [jws, b64];
|
|
72
|
+
}
|
|
73
|
+
export async function createCompactSignature(payload, inputProtectedHeader, inputCrit, key, rejectUnencoded) {
|
|
74
|
+
const [protectedHeader, protectedHeaderString] = serializeProtectedHeader(inputProtectedHeader);
|
|
75
|
+
if (!protectedHeader) {
|
|
76
|
+
throw new JWSInvalid('either setProtectedHeader or setUnprotectedHeader must be called before #sign()');
|
|
77
|
+
}
|
|
78
|
+
const b64 = validateSignatureHeader(protectedHeader, protectedHeader, inputCrit);
|
|
79
|
+
if (!b64)
|
|
80
|
+
rejectUnencoded();
|
|
81
|
+
const entry = signatureAlgorithm(protectedHeader);
|
|
82
|
+
const encodedPayload = b64u(payload);
|
|
83
|
+
const signature = await signSignature(protectedHeaderString, encode(encodedPayload), entry, key);
|
|
84
|
+
return `${protectedHeaderString}.${encodedPayload}.${signature}`;
|
|
74
85
|
}
|
|
@@ -3,9 +3,25 @@ import { jwsAlgorithm } from './jws_algorithms.js';
|
|
|
3
3
|
import { JOSEAlgNotAllowed, JWSInvalid, JWSSignatureVerificationFailed } from '../util/errors.js';
|
|
4
4
|
import { concat, decoder, encoder, encode } from './buffer_utils.js';
|
|
5
5
|
import { decodeBase64url, encodeBase64url, parseJoseHeader } from './helpers.js';
|
|
6
|
-
import { isDisjoint } from './type_checks.js';
|
|
7
|
-
import { validateCrit, validateAlgorithms, JWS_RECOGNIZED } from './options.js';
|
|
6
|
+
import { isDisjoint, isObject } from './type_checks.js';
|
|
7
|
+
import { validateB64, validateCrit, validateAlgorithms, JWS_RECOGNIZED } from './options.js';
|
|
8
8
|
import { prepareKey } from './key.js';
|
|
9
|
+
export function snapshotJws(jws, sharedPayload) {
|
|
10
|
+
const encodedProtected = jws.protected;
|
|
11
|
+
const inputHeader = jws.header;
|
|
12
|
+
const header = isObject(inputHeader) ? { ...inputHeader } : inputHeader;
|
|
13
|
+
let payload = sharedPayload ? sharedPayload[0] : jws.payload;
|
|
14
|
+
if (!sharedPayload && payload instanceof Uint8Array) {
|
|
15
|
+
payload = new Uint8Array(payload);
|
|
16
|
+
}
|
|
17
|
+
const signature = jws.signature;
|
|
18
|
+
const snapshot = { payload, signature };
|
|
19
|
+
if (encodedProtected !== undefined)
|
|
20
|
+
snapshot.protected = encodedProtected;
|
|
21
|
+
if (inputHeader !== undefined)
|
|
22
|
+
snapshot.header = header;
|
|
23
|
+
return snapshot;
|
|
24
|
+
}
|
|
9
25
|
export function verifyResult(jws, verified) {
|
|
10
26
|
const [payload, parsedProt, , key, resolvedKey] = verified;
|
|
11
27
|
const result = { payload };
|
|
@@ -23,12 +39,24 @@ export function verifyResult(jws, verified) {
|
|
|
23
39
|
export function prepareVerify(options) {
|
|
24
40
|
return [options && validateAlgorithms('algorithms', options.algorithms), options?.crit];
|
|
25
41
|
}
|
|
26
|
-
export
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
42
|
+
export function parseProtectedHeader(encodedProtected, parsedProtected = encodedProtected === undefined
|
|
43
|
+
? {}
|
|
44
|
+
: parseJoseHeader(encodedProtected, JWSInvalid, 'JWS Protected Header is invalid')) {
|
|
45
|
+
return parsedProtected;
|
|
46
|
+
}
|
|
47
|
+
function validateJwsHeaders(parsedProt, joseHeader, shared) {
|
|
48
|
+
const b64 = validateB64(parsedProt, validateCrit(JWSInvalid, JWS_RECOGNIZED, shared[1], parsedProt, joseHeader));
|
|
49
|
+
const alg = joseHeader.alg;
|
|
50
|
+
if (typeof alg !== 'string' || !alg) {
|
|
51
|
+
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
31
52
|
}
|
|
53
|
+
if (shared[0] && !shared[0].has(alg)) {
|
|
54
|
+
throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
|
|
55
|
+
}
|
|
56
|
+
return [b64, alg];
|
|
57
|
+
}
|
|
58
|
+
export function parseJwsHeaders(encodedProtected, header, shared, parsedProtected) {
|
|
59
|
+
const parsedProt = parseProtectedHeader(encodedProtected, parsedProtected);
|
|
32
60
|
let joseHeader;
|
|
33
61
|
if (header !== undefined) {
|
|
34
62
|
if (!isDisjoint(parsedProt, header)) {
|
|
@@ -39,58 +67,58 @@ export async function verifySignature(jws, shared, key) {
|
|
|
39
67
|
else {
|
|
40
68
|
joseHeader = parsedProt;
|
|
41
69
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
if (b64) {
|
|
58
|
-
if (typeof inputPayload !== 'string') {
|
|
59
|
-
throw new JWSInvalid('JWS Payload must be a string');
|
|
60
|
-
}
|
|
70
|
+
return [parsedProt, joseHeader, ...validateJwsHeaders(parsedProt, joseHeader, shared)];
|
|
71
|
+
}
|
|
72
|
+
export function encodeJsonUnencodedPayload(payload) {
|
|
73
|
+
const invalid = /[\p{Cs}\p{Cn}]/u.exec(payload)?.[0];
|
|
74
|
+
if (invalid !== undefined) {
|
|
75
|
+
throw new JWSInvalid(/\p{Cs}/u.test(invalid)
|
|
76
|
+
? 'JWS Payload must be a well-formed Unicode string'
|
|
77
|
+
: 'JWS Payload must not contain unassigned Unicode code points');
|
|
78
|
+
}
|
|
79
|
+
return encoder.encode(payload);
|
|
80
|
+
}
|
|
81
|
+
function encodeCompactUnencodedPayload(payload) {
|
|
82
|
+
try {
|
|
83
|
+
return encode(payload);
|
|
61
84
|
}
|
|
62
|
-
|
|
63
|
-
throw new JWSInvalid('JWS
|
|
85
|
+
catch {
|
|
86
|
+
throw new JWSInvalid('JWS Compact Serialization payload must use only ASCII characters');
|
|
64
87
|
}
|
|
88
|
+
}
|
|
89
|
+
async function verifyPrepared(jws, shared, key, encodedProtected, parsedProt, alg, signingPayload) {
|
|
65
90
|
let resolvedKey = false;
|
|
66
91
|
if (typeof key === 'function') {
|
|
67
92
|
key = await key(parsedProt, jws);
|
|
68
93
|
resolvedKey = true;
|
|
69
94
|
}
|
|
95
|
+
const b64 = typeof signingPayload === 'string';
|
|
70
96
|
const entry = jwsAlgorithm(alg);
|
|
71
|
-
const data = concat(encodedProtected !== undefined ? encode(encodedProtected) : new Uint8Array(), encode('.'),
|
|
72
|
-
?
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
: encoder.encode(inputPayload)
|
|
76
|
-
: inputPayload);
|
|
97
|
+
const data = concat(encodedProtected !== undefined ? encode(encodedProtected) : new Uint8Array(), encode('.'), b64
|
|
98
|
+
?
|
|
99
|
+
(shared[2] ??= encodeBase64url(signingPayload, 'payload', JWSInvalid))
|
|
100
|
+
: signingPayload);
|
|
77
101
|
const signature = decodeBase64url(jws.signature, 'signature', JWSInvalid);
|
|
78
102
|
const k = await prepareKey(entry, key, 'verify');
|
|
79
|
-
|
|
80
|
-
if (!verified) {
|
|
103
|
+
if (!(await verify(entry, k, signature, data))) {
|
|
81
104
|
throw new JWSSignatureVerificationFailed();
|
|
82
105
|
}
|
|
83
|
-
|
|
106
|
+
const payload = b64 ? decodeBase64url(signingPayload, 'payload', JWSInvalid) : signingPayload;
|
|
107
|
+
return [payload, parsedProt, b64, k, resolvedKey];
|
|
108
|
+
}
|
|
109
|
+
export async function verifySignature(jws, shared, key, encodeUnencodedPayload, parsedProtected) {
|
|
110
|
+
const { protected: encodedProtected, header, payload: inputPayload } = jws;
|
|
111
|
+
const [parsedProt, , b64, alg] = parseJwsHeaders(encodedProtected, header, shared, parsedProtected);
|
|
84
112
|
if (b64) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
payload = encoder.encode(inputPayload);
|
|
113
|
+
if (typeof inputPayload !== 'string') {
|
|
114
|
+
throw new JWSInvalid('JWS Payload must be a string');
|
|
115
|
+
}
|
|
89
116
|
}
|
|
90
|
-
else {
|
|
91
|
-
|
|
117
|
+
else if (typeof inputPayload !== 'string' && !(inputPayload instanceof Uint8Array)) {
|
|
118
|
+
throw new JWSInvalid('JWS Payload must be a string or an Uint8Array instance');
|
|
92
119
|
}
|
|
93
|
-
|
|
120
|
+
const signingPayload = b64 || typeof inputPayload !== 'string' ? inputPayload : encodeUnencodedPayload(inputPayload);
|
|
121
|
+
return verifyPrepared(jws, shared, key, encodedProtected, parsedProt, alg, signingPayload);
|
|
94
122
|
}
|
|
95
123
|
export async function verifyCompact(jws, shared, key) {
|
|
96
124
|
if (jws instanceof Uint8Array) {
|
|
@@ -103,5 +131,9 @@ export async function verifyCompact(jws, shared, key) {
|
|
|
103
131
|
if (length !== 3) {
|
|
104
132
|
throw new JWSInvalid('Invalid Compact JWS');
|
|
105
133
|
}
|
|
106
|
-
|
|
134
|
+
const compactJws = { payload, protected: protectedHeader, signature };
|
|
135
|
+
const parsedProt = parseProtectedHeader(protectedHeader);
|
|
136
|
+
const [b64, alg] = validateJwsHeaders(parsedProt, parsedProt, shared);
|
|
137
|
+
const signingPayload = b64 ? payload : encodeCompactUnencodedPayload(payload);
|
|
138
|
+
return verifyPrepared(compactJws, shared, key, protectedHeader, parsedProt, alg, signingPayload);
|
|
107
139
|
}
|