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.
Files changed (62) hide show
  1. package/node_modules/fast-uri/index.js +201 -48
  2. package/node_modules/fast-uri/lib/schemes.js +9 -4
  3. package/node_modules/fast-uri/lib/utils.js +388 -91
  4. package/node_modules/fast-uri/package.json +1 -1
  5. package/node_modules/fast-uri/test/component-safe-serialization.test.js +105 -0
  6. package/node_modules/fast-uri/test/equal.test.js +31 -3
  7. package/node_modules/fast-uri/test/fixtures/uri-js-parse.json +2 -0
  8. package/node_modules/fast-uri/test/ipv6-canonical.test.js +34 -0
  9. package/node_modules/fast-uri/test/ipv6-validation.test.js +90 -0
  10. package/node_modules/fast-uri/test/malformed-percent.test.js +77 -0
  11. package/node_modules/fast-uri/test/malformed-urn.test.js +61 -0
  12. package/node_modules/fast-uri/test/parse.test.js +7 -3
  13. package/node_modules/fast-uri/test/query-fragment-normalization.test.js +33 -0
  14. package/node_modules/fast-uri/test/reserved-path-normalization.test.js +109 -0
  15. package/node_modules/fast-uri/test/scheme-validation.test.js +124 -0
  16. package/node_modules/fast-uri/test/security-normalization.test.js +101 -0
  17. package/node_modules/fast-uri/test/security.test.js +75 -3
  18. package/node_modules/fast-uri/test/urn-full-input.test.js +29 -0
  19. package/node_modules/fast-uri/test/websocket-query-preservation.test.js +24 -0
  20. package/node_modules/jose/dist/types/jwks/remote.d.ts +4 -4
  21. package/node_modules/jose/dist/types/jws/general/verify.d.ts +3 -3
  22. package/node_modules/jose/dist/types/jwt/encrypt.d.ts +8 -14
  23. package/node_modules/jose/dist/types/jwt/sign.d.ts +8 -14
  24. package/node_modules/jose/dist/types/jwt/unsecured.d.ts +9 -15
  25. package/node_modules/jose/dist/webapi/jwe/compact/encrypt.js +30 -8
  26. package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +8 -4
  27. package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +6 -13
  28. package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +19 -19
  29. package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +27 -20
  30. package/node_modules/jose/dist/webapi/jwk/embedded.js +15 -1
  31. package/node_modules/jose/dist/webapi/jwk/thumbprint.js +11 -5
  32. package/node_modules/jose/dist/webapi/jwks/local.js +45 -53
  33. package/node_modules/jose/dist/webapi/jwks/remote.js +82 -103
  34. package/node_modules/jose/dist/webapi/jws/compact/sign.js +10 -9
  35. package/node_modules/jose/dist/webapi/jws/flattened/sign.js +2 -1
  36. package/node_modules/jose/dist/webapi/jws/flattened/verify.js +8 -7
  37. package/node_modules/jose/dist/webapi/jws/general/sign.js +5 -3
  38. package/node_modules/jose/dist/webapi/jws/general/verify.js +51 -21
  39. package/node_modules/jose/dist/webapi/jwt/decrypt.js +7 -9
  40. package/node_modules/jose/dist/webapi/jwt/encrypt.js +21 -50
  41. package/node_modules/jose/dist/webapi/jwt/sign.js +8 -41
  42. package/node_modules/jose/dist/webapi/jwt/unsecured.js +21 -42
  43. package/node_modules/jose/dist/webapi/key/generate_key_pair.js +6 -2
  44. package/node_modules/jose/dist/webapi/key/generate_secret.js +7 -6
  45. package/node_modules/jose/dist/webapi/key/import.js +16 -12
  46. package/node_modules/jose/dist/webapi/lib/asn1.js +7 -2
  47. package/node_modules/jose/dist/webapi/lib/content_encryption.js +7 -15
  48. package/node_modules/jose/dist/webapi/lib/deflate.js +8 -0
  49. package/node_modules/jose/dist/webapi/lib/helpers.js +1 -1
  50. package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +85 -28
  51. package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +40 -12
  52. package/node_modules/jose/dist/webapi/lib/jwk_metadata.js +20 -0
  53. package/node_modules/jose/dist/webapi/lib/jws_sign.js +47 -36
  54. package/node_modules/jose/dist/webapi/lib/jws_verify.js +77 -45
  55. package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +82 -47
  56. package/node_modules/jose/dist/webapi/lib/key.js +24 -9
  57. package/node_modules/jose/dist/webapi/lib/key_management.js +10 -4
  58. package/node_modules/jose/dist/webapi/lib/key_options.js +6 -0
  59. package/node_modules/jose/dist/webapi/lib/options.js +26 -0
  60. package/node_modules/jose/dist/webapi/lib/type_checks.js +11 -13
  61. package/node_modules/jose/package.json +1 -1
  62. 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 checkShared(jwe) {
12
- const { ciphertext, protected: encodedProtected, unprotected } = jwe;
13
- if (jwe.iv !== undefined && typeof jwe.iv !== 'string') {
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 (jwe.tag !== undefined && typeof jwe.tag !== 'string') {
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 (jwe.aad !== undefined && typeof jwe.aad !== 'string') {
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 && !isObject(header)) {
38
- throw new JWEInvalid('JWE Per-Recipient Unprotected Header incorrect type');
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 [keyManagementAlgorithms, contentEncryptionAlgorithms, options] = shared;
92
- const [parsedProt, ciphertext, iv, tag, additionalData] = token;
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
- validateCrit(JWEInvalid, JWE_RECOGNIZED, options?.crit, parsedProt, joseHeader);
105
- if (joseHeader.zip !== undefined && joseHeader.zip !== 'DEF') {
106
- throw new JOSENotSupported('Unsupported JWE "zip" (Compression Algorithm) Header Parameter value.');
107
- }
108
- if (joseHeader.zip !== undefined && !parsedProt?.zip) {
109
- throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header Parameter MUST be in a protected header.');
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, options);
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 maxDecompressedLength = options?.maxDecompressedLength ?? 250_000;
150
- if (maxDecompressedLength === 0) {
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 (maxDecompressedLength !== Infinity &&
154
- (!Number.isSafeInteger(maxDecompressedLength) || maxDecompressedLength < 1)) {
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, maxDecompressedLength).catch((cause) => {
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
- return decryptJWE({
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
- }, shared, key);
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 { JOSENotSupported, JWEInvalid } from '../util/errors.js';
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
- const [, protectedHeader, unprotectedHeader, sharedUnprotectedHeader, , , , , crit] = input;
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
- if (joseHeader.zip !== undefined && joseHeader.zip !== 'DEF') {
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
- export function unencodedPayload(protectedHeader) {
10
- return (protectedHeader?.b64 === false &&
11
- Array.isArray(protectedHeader.crit) &&
12
- protectedHeader.crit.includes('b64'));
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
- export async function createSignature(input, key) {
15
- const { protectedHeader, unprotectedHeader } = input;
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
- validateCritDuplicates(JWSInvalid, protectedHeader);
24
- const extensions = validateCrit(JWSInvalid, JWS_RECOGNIZED, input.crit, protectedHeader, joseHeader);
25
- let b64 = true;
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: b64u(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 async function verifySignature(jws, shared, key) {
27
- const { protected: encodedProtected, header, payload: inputPayload } = jws;
28
- let parsedProt = {};
29
- if (encodedProtected) {
30
- parsedProt = parseJoseHeader(encodedProtected, JWSInvalid, 'JWS Protected Header is invalid');
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
- const extensions = validateCrit(JWSInvalid, JWS_RECOGNIZED, shared[1], parsedProt, joseHeader);
43
- let b64 = true;
44
- if (extensions.includes('b64')) {
45
- b64 = parsedProt.b64;
46
- if (typeof b64 !== 'boolean') {
47
- throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
48
- }
49
- }
50
- const { alg } = joseHeader;
51
- if (typeof alg !== 'string' || !alg) {
52
- throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
53
- }
54
- if (shared[0] && !shared[0].has(alg)) {
55
- throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
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
- else if (typeof inputPayload !== 'string' && !(inputPayload instanceof Uint8Array)) {
63
- throw new JWSInvalid('JWS Payload must be a string or an Uint8Array instance');
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('.'), typeof inputPayload === 'string'
72
- ? b64
73
- ?
74
- (shared[2] ??= encodeBase64url(inputPayload, 'payload', JWSInvalid))
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
- const verified = await verify(entry, k, signature, data);
80
- if (!verified) {
103
+ if (!(await verify(entry, k, signature, data))) {
81
104
  throw new JWSSignatureVerificationFailed();
82
105
  }
83
- let payload;
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
- payload = decodeBase64url(inputPayload, 'payload', JWSInvalid);
86
- }
87
- else if (typeof inputPayload === 'string') {
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
- payload = inputPayload;
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
- return [payload, parsedProt, b64, k, resolvedKey];
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
- return verifySignature({ payload, protected: protectedHeader, signature }, shared, key);
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
  }