jose 4.1.2 → 4.2.0

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 (42) hide show
  1. package/README.md +4 -1
  2. package/dist/browser/index.js +1 -0
  3. package/dist/browser/jwe/flattened/encrypt.js +15 -4
  4. package/dist/browser/jwe/general/decrypt.js +3 -0
  5. package/dist/browser/jwe/general/encrypt.js +161 -0
  6. package/dist/browser/key/import.js +2 -1
  7. package/dist/browser/lib/crypto_key.js +1 -1
  8. package/dist/browser/runtime/asn1.js +2 -3
  9. package/dist/browser/runtime/base64url.js +2 -4
  10. package/dist/browser/runtime/check_key_length.js +0 -7
  11. package/dist/browser/runtime/env.js +11 -0
  12. package/dist/browser/runtime/fetch_jwks.js +3 -5
  13. package/dist/browser/runtime/generate.js +1 -1
  14. package/dist/browser/runtime/jwk_to_key.js +1 -1
  15. package/dist/browser/runtime/subtle_dsa.js +1 -1
  16. package/dist/browser/runtime/webcrypto.js +8 -4
  17. package/dist/node/cjs/index.js +9 -7
  18. package/dist/node/cjs/jwe/flattened/encrypt.js +16 -5
  19. package/dist/node/cjs/jwe/general/decrypt.js +3 -0
  20. package/dist/node/cjs/jwe/general/encrypt.js +165 -0
  21. package/dist/node/cjs/key/import.js +2 -1
  22. package/dist/node/cjs/lib/crypto_key.js +3 -3
  23. package/dist/node/cjs/runtime/decrypt.js +8 -6
  24. package/dist/node/cjs/runtime/encrypt.js +2 -1
  25. package/dist/node/cjs/runtime/{global.js → env.js} +0 -0
  26. package/dist/node/cjs/runtime/sign.js +0 -4
  27. package/dist/node/esm/index.js +1 -0
  28. package/dist/node/esm/jwe/flattened/encrypt.js +15 -4
  29. package/dist/node/esm/jwe/general/decrypt.js +3 -0
  30. package/dist/node/esm/jwe/general/encrypt.js +161 -0
  31. package/dist/node/esm/key/import.js +2 -1
  32. package/dist/node/esm/lib/crypto_key.js +1 -1
  33. package/dist/node/esm/runtime/decrypt.js +8 -6
  34. package/dist/node/esm/runtime/encrypt.js +2 -1
  35. package/dist/node/esm/runtime/{global.js → env.js} +0 -0
  36. package/dist/node/esm/runtime/sign.js +0 -4
  37. package/dist/types/index.d.ts +3 -1
  38. package/dist/types/jwe/flattened/encrypt.d.ts +1 -0
  39. package/dist/types/jwe/general/encrypt.d.ts +17 -0
  40. package/dist/types/types.d.ts +2 -1
  41. package/package.json +1 -1
  42. package/dist/browser/runtime/global.js +0 -27
package/README.md CHANGED
@@ -37,7 +37,7 @@ If you or your business use `jose`, please consider becoming a [sponsor][support
37
37
  - [Public Key Import (X.509 Certificate)](docs/functions/key_import.importX509.md#readme)
38
38
  - [Private Key Import (PKCS #8)](docs/functions/key_import.importPKCS8.md#readme)
39
39
  - JSON Web Encryption (JWE)
40
- - Encryption - [Compact](docs/classes/jwe_compact_encrypt.CompactEncrypt.md#readme), [Flattened](docs/classes/jwe_flattened_encrypt.FlattenedEncrypt.md#readme)
40
+ - Encryption - [Compact](docs/classes/jwe_compact_encrypt.CompactEncrypt.md#readme), [Flattened](docs/classes/jwe_flattened_encrypt.FlattenedEncrypt.md#readme), [General](docs/classes/jwe_general_encrypt.GeneralEncrypt.md#readme)
41
41
  - Decryption - [Compact](docs/functions/jwe_compact_decrypt.compactDecrypt.md#readme), [Flattened](docs/functions/jwe_flattened_decrypt.flattenedDecrypt.md#readme), [General](docs/functions/jwe_general_decrypt.generalDecrypt.md#readme)
42
42
  - JSON Web Signature (JWS)
43
43
  - Signing - [Compact](docs/classes/jws_compact_sign.CompactSign.md#readme), [Flattened](docs/classes/jws_flattened_sign.FlattenedSign.md#readme), [General](docs/classes/jws_general_sign.GeneralSign.md#readme)
@@ -65,6 +65,7 @@ If you or your business use `jose`, please consider becoming a [sponsor][support
65
65
  - [Cloudflare Workers Support][]
66
66
  - [Deno Support][]
67
67
  - [Electron Support][]
68
+ - [Next.js Middleware / Vercel Edge Functions Support][]
68
69
  - [Node.js Support][]
69
70
 
70
71
  ## FAQ
@@ -133,8 +134,10 @@ install size should not be a cause for concern.
133
134
  [webcrypto]: https://www.w3.org/TR/WebCryptoAPI/
134
135
  [nodewebcrypto]: https://nodejs.org/docs/latest-v15.x/api/webcrypto.html
135
136
  [deno.land/x/jose]: https://deno.land/x/jose
137
+
136
138
  [Browser Support]: https://github.com/panva/jose/issues/263
137
139
  [Cloudflare Workers Support]: https://github.com/panva/jose/issues/265
138
140
  [Deno Support]: https://github.com/panva/jose/issues/266
139
141
  [Electron Support]: https://github.com/panva/jose/issues/264
142
+ [Next.js Middleware / Vercel Edge Functions Support]: https://github.com/panva/jose/issues/301
140
143
  [Node.js Support]: https://github.com/panva/jose/issues/262
@@ -1,6 +1,7 @@
1
1
  export { compactDecrypt } from './jwe/compact/decrypt.js';
2
2
  export { flattenedDecrypt } from './jwe/flattened/decrypt.js';
3
3
  export { generalDecrypt } from './jwe/general/decrypt.js';
4
+ export { GeneralEncrypt } from './jwe/general/encrypt.js';
4
5
  export { compactVerify } from './jws/compact/verify.js';
5
6
  export { flattenedVerify } from './jws/flattened/verify.js';
6
7
  export { generalVerify } from './jws/general/verify.js';
@@ -7,6 +7,7 @@ import { JOSENotSupported, JWEInvalid } from '../../util/errors.js';
7
7
  import isDisjoint from '../../lib/is_disjoint.js';
8
8
  import { encoder, decoder, concat } from '../../lib/buffer_utils.js';
9
9
  import validateCrit from '../../lib/validate_crit.js';
10
+ export const unprotected = Symbol();
10
11
  export class FlattenedEncrypt {
11
12
  constructor(plaintext) {
12
13
  if (!(plaintext instanceof Uint8Array)) {
@@ -65,7 +66,7 @@ export class FlattenedEncrypt {
65
66
  throw new JWEInvalid('either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()');
66
67
  }
67
68
  if (!isDisjoint(this._protectedHeader, this._unprotectedHeader, this._sharedUnprotectedHeader)) {
68
- throw new JWEInvalid('JWE Shared Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint');
69
+ throw new JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint');
69
70
  }
70
71
  const joseHeader = {
71
72
  ...this._protectedHeader,
@@ -104,11 +105,21 @@ export class FlattenedEncrypt {
104
105
  let parameters;
105
106
  ({ cek, encryptedKey, parameters } = await encryptKeyManagement(alg, enc, key, this._cek, this._keyManagementParameters));
106
107
  if (parameters) {
107
- if (!this._protectedHeader) {
108
- this.setProtectedHeader(parameters);
108
+ if (options && unprotected in options) {
109
+ if (!this._unprotectedHeader) {
110
+ this.setUnprotectedHeader(parameters);
111
+ }
112
+ else {
113
+ this._unprotectedHeader = { ...this._unprotectedHeader, ...parameters };
114
+ }
109
115
  }
110
116
  else {
111
- this._protectedHeader = { ...this._protectedHeader, ...parameters };
117
+ if (!this._protectedHeader) {
118
+ this.setProtectedHeader(parameters);
119
+ }
120
+ else {
121
+ this._protectedHeader = { ...this._protectedHeader, ...parameters };
122
+ }
112
123
  }
113
124
  }
114
125
  }
@@ -8,6 +8,9 @@ export async function generalDecrypt(jwe, key, options) {
8
8
  if (!Array.isArray(jwe.recipients) || !jwe.recipients.every(isObject)) {
9
9
  throw new JWEInvalid('JWE Recipients missing or incorrect type');
10
10
  }
11
+ if (!jwe.recipients.length) {
12
+ throw new JWEInvalid('JWE Recipients has no members');
13
+ }
11
14
  for (const recipient of jwe.recipients) {
12
15
  try {
13
16
  return await flattenedDecrypt({
@@ -0,0 +1,161 @@
1
+ import { FlattenedEncrypt, unprotected } from '../flattened/encrypt.js';
2
+ import { JWEInvalid } from '../../util/errors.js';
3
+ import generateCek from '../../lib/cek.js';
4
+ import isDisjoint from '../../lib/is_disjoint.js';
5
+ import encryptKeyManagement from '../../lib/encrypt_key_management.js';
6
+ import { encode as base64url } from '../../runtime/base64url.js';
7
+ import validateCrit from '../../lib/validate_crit.js';
8
+ const recipientRef = new WeakMap();
9
+ class IndividualRecipient {
10
+ setUnprotectedHeader(unprotectedHeader) {
11
+ if (this._unprotectedHeader) {
12
+ throw new TypeError('setUnprotectedHeader can only be called once');
13
+ }
14
+ this._unprotectedHeader = unprotectedHeader;
15
+ return this;
16
+ }
17
+ set _unprotectedHeader(value) {
18
+ recipientRef.get(this).unprotectedHeader = value;
19
+ }
20
+ get _unprotectedHeader() {
21
+ return recipientRef.get(this).unprotectedHeader;
22
+ }
23
+ }
24
+ export class GeneralEncrypt {
25
+ constructor(plaintext) {
26
+ this._recipients = [];
27
+ this._plaintext = plaintext;
28
+ }
29
+ addRecipient(key, options) {
30
+ const recipient = new IndividualRecipient();
31
+ recipientRef.set(recipient, { key, options: { crit: options === null || options === void 0 ? void 0 : options.crit } });
32
+ this._recipients.push(recipient);
33
+ return recipient;
34
+ }
35
+ setProtectedHeader(protectedHeader) {
36
+ if (this._protectedHeader) {
37
+ throw new TypeError('setProtectedHeader can only be called once');
38
+ }
39
+ this._protectedHeader = protectedHeader;
40
+ return this;
41
+ }
42
+ setSharedUnprotectedHeader(sharedUnprotectedHeader) {
43
+ if (this._unprotectedHeader) {
44
+ throw new TypeError('setSharedUnprotectedHeader can only be called once');
45
+ }
46
+ this._unprotectedHeader = sharedUnprotectedHeader;
47
+ return this;
48
+ }
49
+ setAdditionalAuthenticatedData(aad) {
50
+ this._aad = aad;
51
+ return this;
52
+ }
53
+ async encrypt(options) {
54
+ var _a, _b;
55
+ if (!this._recipients.length) {
56
+ throw new JWEInvalid('at least one recipient must be added');
57
+ }
58
+ options = { deflateRaw: options === null || options === void 0 ? void 0 : options.deflateRaw };
59
+ if (this._recipients.length === 1) {
60
+ const { unprotectedHeader, options: recipientOpts, key, } = recipientRef.get(this._recipients[0]);
61
+ const flattened = await new FlattenedEncrypt(this._plaintext)
62
+ .setAdditionalAuthenticatedData(this._aad)
63
+ .setProtectedHeader(this._protectedHeader)
64
+ .setSharedUnprotectedHeader(this._unprotectedHeader)
65
+ .setUnprotectedHeader(unprotectedHeader)
66
+ .encrypt(key, { ...recipientOpts, ...options });
67
+ let jwe = {
68
+ ciphertext: flattened.ciphertext,
69
+ iv: flattened.iv,
70
+ recipients: [{}],
71
+ tag: flattened.tag,
72
+ };
73
+ if (flattened.aad)
74
+ jwe.aad = flattened.aad;
75
+ if (flattened.protected)
76
+ jwe.protected = flattened.protected;
77
+ if (flattened.unprotected)
78
+ jwe.unprotected = flattened.unprotected;
79
+ if (flattened.encrypted_key)
80
+ jwe.recipients[0].encrypted_key = flattened.encrypted_key;
81
+ if (flattened.header)
82
+ jwe.recipients[0].header = flattened.header;
83
+ return jwe;
84
+ }
85
+ let enc;
86
+ for (let i = 0; i < this._recipients.length; i++) {
87
+ const recipient = this._recipients[i];
88
+ const { unprotectedHeader, options: recipientOpts } = recipientRef.get(recipient);
89
+ if (!isDisjoint(this._protectedHeader, this._unprotectedHeader, unprotectedHeader)) {
90
+ throw new JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint');
91
+ }
92
+ const joseHeader = {
93
+ ...this._protectedHeader,
94
+ ...this._unprotectedHeader,
95
+ ...unprotectedHeader,
96
+ };
97
+ const { alg } = joseHeader;
98
+ if (typeof alg !== 'string' || !alg) {
99
+ throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid');
100
+ }
101
+ if (alg === 'dir' || alg === 'ECDH-ES') {
102
+ throw new JWEInvalid('"dir" and "ECDH-ES" alg may only be used with a single recipient');
103
+ }
104
+ if (typeof joseHeader.enc !== 'string' || !joseHeader.enc) {
105
+ throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid');
106
+ }
107
+ if (!enc) {
108
+ enc = joseHeader.enc;
109
+ }
110
+ else if (enc !== joseHeader.enc) {
111
+ throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients');
112
+ }
113
+ validateCrit(JWEInvalid, new Map(), recipientOpts === null || recipientOpts === void 0 ? void 0 : recipientOpts.crit, this._protectedHeader, joseHeader);
114
+ if (joseHeader.zip !== undefined) {
115
+ if (!this._protectedHeader || !this._protectedHeader.zip) {
116
+ throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected');
117
+ }
118
+ }
119
+ }
120
+ const cek = generateCek(enc);
121
+ let jwe = {
122
+ ciphertext: '',
123
+ iv: '',
124
+ recipients: [],
125
+ tag: '',
126
+ };
127
+ for (let i = 0; i < this._recipients.length; i++) {
128
+ const recipient = this._recipients[i];
129
+ const target = {};
130
+ jwe.recipients.push(target);
131
+ const { unprotectedHeader, options: recipientOpts, key } = recipientRef.get(recipient);
132
+ if (i === 0) {
133
+ const flattened = await new FlattenedEncrypt(this._plaintext)
134
+ .setAdditionalAuthenticatedData(this._aad)
135
+ .setContentEncryptionKey(cek)
136
+ .setProtectedHeader(this._protectedHeader)
137
+ .setSharedUnprotectedHeader(this._unprotectedHeader)
138
+ .setUnprotectedHeader(unprotectedHeader)
139
+ .encrypt(key, { ...recipientOpts, ...options, [unprotected]: true });
140
+ jwe.ciphertext = flattened.ciphertext;
141
+ jwe.iv = flattened.iv;
142
+ jwe.tag = flattened.tag;
143
+ if (flattened.aad)
144
+ jwe.aad = flattened.aad;
145
+ if (flattened.protected)
146
+ jwe.protected = flattened.protected;
147
+ if (flattened.unprotected)
148
+ jwe.unprotected = flattened.unprotected;
149
+ target.encrypted_key = flattened.encrypted_key;
150
+ if (flattened.header)
151
+ target.header = flattened.header;
152
+ continue;
153
+ }
154
+ const { encryptedKey, parameters } = await encryptKeyManagement((unprotectedHeader === null || unprotectedHeader === void 0 ? void 0 : unprotectedHeader.alg) || ((_a = this._protectedHeader) === null || _a === void 0 ? void 0 : _a.alg) || ((_b = this._unprotectedHeader) === null || _b === void 0 ? void 0 : _b.alg), enc, key, cek);
155
+ target.encrypted_key = base64url(encryptedKey);
156
+ if (unprotectedHeader || parameters)
157
+ target.header = { ...unprotectedHeader, ...parameters };
158
+ }
159
+ return jwe;
160
+ }
161
+ }
@@ -62,7 +62,8 @@ function parseElement(bytes) {
62
62
  };
63
63
  }
64
64
  function spkiFromX509(buf) {
65
- return encodeBase64(getElement(getElement(parseElement(buf).contents)[0].contents)[6].raw);
65
+ const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
66
+ return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw);
66
67
  }
67
68
  function getSPKI(x509) {
68
69
  const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '');
@@ -1,4 +1,4 @@
1
- import { isCloudflareWorkers, isNodeJs } from '../runtime/global.js';
1
+ import { isCloudflareWorkers, isNodeJs } from '../runtime/env.js';
2
2
  function unusable(name, prop = 'algorithm.name') {
3
3
  return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
4
4
  }
@@ -1,4 +1,4 @@
1
- import globalThis, { isCloudflareWorkers, isNodeJs } from './global.js';
1
+ import { isCloudflareWorkers, isNodeJs } from './env.js';
2
2
  import crypto, { isCryptoKey } from './webcrypto.js';
3
3
  import invalidKeyInput from '../lib/invalid_key_input.js';
4
4
  import { encodeBase64 } from './base64url.js';
@@ -54,8 +54,7 @@ const genericImport = async (replace, keyFormat, pem, alg, options) => {
54
54
  var _a;
55
55
  let algorithm;
56
56
  let keyUsages;
57
- const keyData = new Uint8Array(globalThis
58
- .atob(pem.replace(replace, ''))
57
+ const keyData = new Uint8Array(atob(pem.replace(replace, ''))
59
58
  .split('')
60
59
  .map((c) => c.charCodeAt(0)));
61
60
  const isPublic = keyFormat === 'spki';
@@ -1,5 +1,4 @@
1
1
  import { encoder, decoder } from '../lib/buffer_utils.js';
2
- import globalThis from './global.js';
3
2
  export const encodeBase64 = (input) => {
4
3
  let unencoded = input;
5
4
  if (typeof unencoded === 'string') {
@@ -10,14 +9,13 @@ export const encodeBase64 = (input) => {
10
9
  for (let i = 0; i < unencoded.length; i += CHUNK_SIZE) {
11
10
  arr.push(String.fromCharCode.apply(null, unencoded.subarray(i, i + CHUNK_SIZE)));
12
11
  }
13
- return globalThis.btoa(arr.join(''));
12
+ return btoa(arr.join(''));
14
13
  };
15
14
  export const encode = (input) => {
16
15
  return encodeBase64(input).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
17
16
  };
18
17
  export const decodeBase64 = (encoded) => {
19
- return new Uint8Array(globalThis
20
- .atob(encoded)
18
+ return new Uint8Array(atob(encoded)
21
19
  .split('')
22
20
  .map((c) => c.charCodeAt(0)));
23
21
  };
@@ -1,11 +1,4 @@
1
1
  export default (alg, key) => {
2
- if (alg.startsWith('HS')) {
3
- const bitlen = parseInt(alg.substr(-3), 10);
4
- const { length } = key.algorithm;
5
- if (typeof length !== 'number' || length < bitlen) {
6
- throw new TypeError(`${alg} requires symmetric keys to be ${bitlen} bits or larger`);
7
- }
8
- }
9
2
  if (alg.startsWith('RS') || alg.startsWith('PS')) {
10
3
  const { modulusLength } = key.algorithm;
11
4
  if (typeof modulusLength !== 'number' || modulusLength < 2048) {
@@ -0,0 +1,11 @@
1
+ export function isCloudflareWorkers() {
2
+ return typeof WebSocketPair === 'function';
3
+ }
4
+ export function isNodeJs() {
5
+ try {
6
+ return process.versions.node !== undefined;
7
+ }
8
+ catch (_a) {
9
+ return false;
10
+ }
11
+ }
@@ -1,5 +1,5 @@
1
1
  import { JOSEError, JWKSTimeout } from '../util/errors.js';
2
- import globalThis, { isCloudflareWorkers } from './global.js';
2
+ import { isCloudflareWorkers } from './env.js';
3
3
  const fetchJwks = async (url, timeout) => {
4
4
  let controller;
5
5
  let id;
@@ -11,8 +11,7 @@ const fetchJwks = async (url, timeout) => {
11
11
  controller.abort();
12
12
  }, timeout);
13
13
  }
14
- const response = await globalThis
15
- .fetch(url.href, {
14
+ const response = await fetch(url.href, {
16
15
  signal: controller ? controller.signal : undefined,
17
16
  redirect: 'manual',
18
17
  method: 'GET',
@@ -23,8 +22,7 @@ const fetchJwks = async (url, timeout) => {
23
22
  mode: 'cors',
24
23
  }
25
24
  : undefined),
26
- })
27
- .catch((err) => {
25
+ }).catch((err) => {
28
26
  if (timedOut)
29
27
  throw new JWKSTimeout();
30
28
  throw err;
@@ -1,4 +1,4 @@
1
- import { isCloudflareWorkers, isNodeJs } from './global.js';
1
+ import { isCloudflareWorkers, isNodeJs } from './env.js';
2
2
  import crypto from './webcrypto.js';
3
3
  import { JOSENotSupported } from '../util/errors.js';
4
4
  import random from './random.js';
@@ -1,4 +1,4 @@
1
- import { isCloudflareWorkers, isNodeJs } from './global.js';
1
+ import { isCloudflareWorkers, isNodeJs } from './env.js';
2
2
  import crypto from './webcrypto.js';
3
3
  import { JOSENotSupported } from '../util/errors.js';
4
4
  import { decode as base64url } from './base64url.js';
@@ -1,4 +1,4 @@
1
- import { isCloudflareWorkers, isNodeJs } from './global.js';
1
+ import { isCloudflareWorkers, isNodeJs } from './env.js';
2
2
  import { JOSENotSupported } from '../util/errors.js';
3
3
  export default function subtleDsa(alg, namedCurve) {
4
4
  const length = parseInt(alg.substr(-3), 10);
@@ -1,8 +1,12 @@
1
- import globalThis from './global.js';
2
- export default globalThis.crypto;
1
+ export default crypto;
3
2
  export function isCryptoKey(key) {
4
- if (typeof globalThis.CryptoKey === 'undefined') {
3
+ try {
4
+ return (key != null &&
5
+ typeof key.extractable === 'boolean' &&
6
+ typeof key.algorithm.name === 'string' &&
7
+ typeof key.type === 'string');
8
+ }
9
+ catch (_a) {
5
10
  return false;
6
11
  }
7
- return key != null && key instanceof globalThis.CryptoKey;
8
12
  }
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.base64url = exports.generateSecret = exports.generateKeyPair = exports.errors = exports.decodeProtectedHeader = exports.importJWK = exports.importX509 = exports.importPKCS8 = exports.importSPKI = exports.exportJWK = exports.exportSPKI = exports.exportPKCS8 = exports.UnsecuredJWT = exports.createRemoteJWKSet = exports.EmbeddedJWK = exports.calculateJwkThumbprint = exports.EncryptJWT = exports.SignJWT = exports.GeneralSign = exports.FlattenedSign = exports.CompactSign = exports.FlattenedEncrypt = exports.CompactEncrypt = exports.jwtDecrypt = exports.jwtVerify = exports.generalVerify = exports.flattenedVerify = exports.compactVerify = exports.generalDecrypt = exports.flattenedDecrypt = exports.compactDecrypt = void 0;
3
+ exports.base64url = exports.generateSecret = exports.generateKeyPair = exports.errors = exports.decodeProtectedHeader = exports.importJWK = exports.importX509 = exports.importPKCS8 = exports.importSPKI = exports.exportJWK = exports.exportSPKI = exports.exportPKCS8 = exports.UnsecuredJWT = exports.createRemoteJWKSet = exports.EmbeddedJWK = exports.calculateJwkThumbprint = exports.EncryptJWT = exports.SignJWT = exports.GeneralSign = exports.FlattenedSign = exports.CompactSign = exports.FlattenedEncrypt = exports.CompactEncrypt = exports.jwtDecrypt = exports.jwtVerify = exports.generalVerify = exports.flattenedVerify = exports.compactVerify = exports.GeneralEncrypt = exports.generalDecrypt = exports.flattenedDecrypt = exports.compactDecrypt = void 0;
4
4
  var decrypt_js_1 = require("./jwe/compact/decrypt.js");
5
5
  Object.defineProperty(exports, "compactDecrypt", { enumerable: true, get: function () { return decrypt_js_1.compactDecrypt; } });
6
6
  var decrypt_js_2 = require("./jwe/flattened/decrypt.js");
7
7
  Object.defineProperty(exports, "flattenedDecrypt", { enumerable: true, get: function () { return decrypt_js_2.flattenedDecrypt; } });
8
8
  var decrypt_js_3 = require("./jwe/general/decrypt.js");
9
9
  Object.defineProperty(exports, "generalDecrypt", { enumerable: true, get: function () { return decrypt_js_3.generalDecrypt; } });
10
+ var encrypt_js_1 = require("./jwe/general/encrypt.js");
11
+ Object.defineProperty(exports, "GeneralEncrypt", { enumerable: true, get: function () { return encrypt_js_1.GeneralEncrypt; } });
10
12
  var verify_js_1 = require("./jws/compact/verify.js");
11
13
  Object.defineProperty(exports, "compactVerify", { enumerable: true, get: function () { return verify_js_1.compactVerify; } });
12
14
  var verify_js_2 = require("./jws/flattened/verify.js");
@@ -17,10 +19,10 @@ var verify_js_4 = require("./jwt/verify.js");
17
19
  Object.defineProperty(exports, "jwtVerify", { enumerable: true, get: function () { return verify_js_4.jwtVerify; } });
18
20
  var decrypt_js_4 = require("./jwt/decrypt.js");
19
21
  Object.defineProperty(exports, "jwtDecrypt", { enumerable: true, get: function () { return decrypt_js_4.jwtDecrypt; } });
20
- var encrypt_js_1 = require("./jwe/compact/encrypt.js");
21
- Object.defineProperty(exports, "CompactEncrypt", { enumerable: true, get: function () { return encrypt_js_1.CompactEncrypt; } });
22
- var encrypt_js_2 = require("./jwe/flattened/encrypt.js");
23
- Object.defineProperty(exports, "FlattenedEncrypt", { enumerable: true, get: function () { return encrypt_js_2.FlattenedEncrypt; } });
22
+ var encrypt_js_2 = require("./jwe/compact/encrypt.js");
23
+ Object.defineProperty(exports, "CompactEncrypt", { enumerable: true, get: function () { return encrypt_js_2.CompactEncrypt; } });
24
+ var encrypt_js_3 = require("./jwe/flattened/encrypt.js");
25
+ Object.defineProperty(exports, "FlattenedEncrypt", { enumerable: true, get: function () { return encrypt_js_3.FlattenedEncrypt; } });
24
26
  var sign_js_1 = require("./jws/compact/sign.js");
25
27
  Object.defineProperty(exports, "CompactSign", { enumerable: true, get: function () { return sign_js_1.CompactSign; } });
26
28
  var sign_js_2 = require("./jws/flattened/sign.js");
@@ -29,8 +31,8 @@ var sign_js_3 = require("./jws/general/sign.js");
29
31
  Object.defineProperty(exports, "GeneralSign", { enumerable: true, get: function () { return sign_js_3.GeneralSign; } });
30
32
  var sign_js_4 = require("./jwt/sign.js");
31
33
  Object.defineProperty(exports, "SignJWT", { enumerable: true, get: function () { return sign_js_4.SignJWT; } });
32
- var encrypt_js_3 = require("./jwt/encrypt.js");
33
- Object.defineProperty(exports, "EncryptJWT", { enumerable: true, get: function () { return encrypt_js_3.EncryptJWT; } });
34
+ var encrypt_js_4 = require("./jwt/encrypt.js");
35
+ Object.defineProperty(exports, "EncryptJWT", { enumerable: true, get: function () { return encrypt_js_4.EncryptJWT; } });
34
36
  var thumbprint_js_1 = require("./jwk/thumbprint.js");
35
37
  Object.defineProperty(exports, "calculateJwkThumbprint", { enumerable: true, get: function () { return thumbprint_js_1.calculateJwkThumbprint; } });
36
38
  var embedded_js_1 = require("./jwk/embedded.js");
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FlattenedEncrypt = void 0;
3
+ exports.FlattenedEncrypt = exports.unprotected = void 0;
4
4
  const base64url_js_1 = require("../../runtime/base64url.js");
5
5
  const encrypt_js_1 = require("../../runtime/encrypt.js");
6
6
  const zlib_js_1 = require("../../runtime/zlib.js");
@@ -10,6 +10,7 @@ const errors_js_1 = require("../../util/errors.js");
10
10
  const is_disjoint_js_1 = require("../../lib/is_disjoint.js");
11
11
  const buffer_utils_js_1 = require("../../lib/buffer_utils.js");
12
12
  const validate_crit_js_1 = require("../../lib/validate_crit.js");
13
+ exports.unprotected = Symbol();
13
14
  class FlattenedEncrypt {
14
15
  constructor(plaintext) {
15
16
  if (!(plaintext instanceof Uint8Array)) {
@@ -68,7 +69,7 @@ class FlattenedEncrypt {
68
69
  throw new errors_js_1.JWEInvalid('either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()');
69
70
  }
70
71
  if (!(0, is_disjoint_js_1.default)(this._protectedHeader, this._unprotectedHeader, this._sharedUnprotectedHeader)) {
71
- throw new errors_js_1.JWEInvalid('JWE Shared Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint');
72
+ throw new errors_js_1.JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint');
72
73
  }
73
74
  const joseHeader = {
74
75
  ...this._protectedHeader,
@@ -107,11 +108,21 @@ class FlattenedEncrypt {
107
108
  let parameters;
108
109
  ({ cek, encryptedKey, parameters } = await (0, encrypt_key_management_js_1.default)(alg, enc, key, this._cek, this._keyManagementParameters));
109
110
  if (parameters) {
110
- if (!this._protectedHeader) {
111
- this.setProtectedHeader(parameters);
111
+ if (options && exports.unprotected in options) {
112
+ if (!this._unprotectedHeader) {
113
+ this.setUnprotectedHeader(parameters);
114
+ }
115
+ else {
116
+ this._unprotectedHeader = { ...this._unprotectedHeader, ...parameters };
117
+ }
112
118
  }
113
119
  else {
114
- this._protectedHeader = { ...this._protectedHeader, ...parameters };
120
+ if (!this._protectedHeader) {
121
+ this.setProtectedHeader(parameters);
122
+ }
123
+ else {
124
+ this._protectedHeader = { ...this._protectedHeader, ...parameters };
125
+ }
115
126
  }
116
127
  }
117
128
  }
@@ -11,6 +11,9 @@ async function generalDecrypt(jwe, key, options) {
11
11
  if (!Array.isArray(jwe.recipients) || !jwe.recipients.every(is_object_js_1.default)) {
12
12
  throw new errors_js_1.JWEInvalid('JWE Recipients missing or incorrect type');
13
13
  }
14
+ if (!jwe.recipients.length) {
15
+ throw new errors_js_1.JWEInvalid('JWE Recipients has no members');
16
+ }
14
17
  for (const recipient of jwe.recipients) {
15
18
  try {
16
19
  return await (0, decrypt_js_1.flattenedDecrypt)({
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GeneralEncrypt = void 0;
4
+ const encrypt_js_1 = require("../flattened/encrypt.js");
5
+ const errors_js_1 = require("../../util/errors.js");
6
+ const cek_js_1 = require("../../lib/cek.js");
7
+ const is_disjoint_js_1 = require("../../lib/is_disjoint.js");
8
+ const encrypt_key_management_js_1 = require("../../lib/encrypt_key_management.js");
9
+ const base64url_js_1 = require("../../runtime/base64url.js");
10
+ const validate_crit_js_1 = require("../../lib/validate_crit.js");
11
+ const recipientRef = new WeakMap();
12
+ class IndividualRecipient {
13
+ setUnprotectedHeader(unprotectedHeader) {
14
+ if (this._unprotectedHeader) {
15
+ throw new TypeError('setUnprotectedHeader can only be called once');
16
+ }
17
+ this._unprotectedHeader = unprotectedHeader;
18
+ return this;
19
+ }
20
+ set _unprotectedHeader(value) {
21
+ recipientRef.get(this).unprotectedHeader = value;
22
+ }
23
+ get _unprotectedHeader() {
24
+ return recipientRef.get(this).unprotectedHeader;
25
+ }
26
+ }
27
+ class GeneralEncrypt {
28
+ constructor(plaintext) {
29
+ this._recipients = [];
30
+ this._plaintext = plaintext;
31
+ }
32
+ addRecipient(key, options) {
33
+ const recipient = new IndividualRecipient();
34
+ recipientRef.set(recipient, { key, options: { crit: options === null || options === void 0 ? void 0 : options.crit } });
35
+ this._recipients.push(recipient);
36
+ return recipient;
37
+ }
38
+ setProtectedHeader(protectedHeader) {
39
+ if (this._protectedHeader) {
40
+ throw new TypeError('setProtectedHeader can only be called once');
41
+ }
42
+ this._protectedHeader = protectedHeader;
43
+ return this;
44
+ }
45
+ setSharedUnprotectedHeader(sharedUnprotectedHeader) {
46
+ if (this._unprotectedHeader) {
47
+ throw new TypeError('setSharedUnprotectedHeader can only be called once');
48
+ }
49
+ this._unprotectedHeader = sharedUnprotectedHeader;
50
+ return this;
51
+ }
52
+ setAdditionalAuthenticatedData(aad) {
53
+ this._aad = aad;
54
+ return this;
55
+ }
56
+ async encrypt(options) {
57
+ var _a, _b;
58
+ if (!this._recipients.length) {
59
+ throw new errors_js_1.JWEInvalid('at least one recipient must be added');
60
+ }
61
+ options = { deflateRaw: options === null || options === void 0 ? void 0 : options.deflateRaw };
62
+ if (this._recipients.length === 1) {
63
+ const { unprotectedHeader, options: recipientOpts, key, } = recipientRef.get(this._recipients[0]);
64
+ const flattened = await new encrypt_js_1.FlattenedEncrypt(this._plaintext)
65
+ .setAdditionalAuthenticatedData(this._aad)
66
+ .setProtectedHeader(this._protectedHeader)
67
+ .setSharedUnprotectedHeader(this._unprotectedHeader)
68
+ .setUnprotectedHeader(unprotectedHeader)
69
+ .encrypt(key, { ...recipientOpts, ...options });
70
+ let jwe = {
71
+ ciphertext: flattened.ciphertext,
72
+ iv: flattened.iv,
73
+ recipients: [{}],
74
+ tag: flattened.tag,
75
+ };
76
+ if (flattened.aad)
77
+ jwe.aad = flattened.aad;
78
+ if (flattened.protected)
79
+ jwe.protected = flattened.protected;
80
+ if (flattened.unprotected)
81
+ jwe.unprotected = flattened.unprotected;
82
+ if (flattened.encrypted_key)
83
+ jwe.recipients[0].encrypted_key = flattened.encrypted_key;
84
+ if (flattened.header)
85
+ jwe.recipients[0].header = flattened.header;
86
+ return jwe;
87
+ }
88
+ let enc;
89
+ for (let i = 0; i < this._recipients.length; i++) {
90
+ const recipient = this._recipients[i];
91
+ const { unprotectedHeader, options: recipientOpts } = recipientRef.get(recipient);
92
+ if (!(0, is_disjoint_js_1.default)(this._protectedHeader, this._unprotectedHeader, unprotectedHeader)) {
93
+ throw new errors_js_1.JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint');
94
+ }
95
+ const joseHeader = {
96
+ ...this._protectedHeader,
97
+ ...this._unprotectedHeader,
98
+ ...unprotectedHeader,
99
+ };
100
+ const { alg } = joseHeader;
101
+ if (typeof alg !== 'string' || !alg) {
102
+ throw new errors_js_1.JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid');
103
+ }
104
+ if (alg === 'dir' || alg === 'ECDH-ES') {
105
+ throw new errors_js_1.JWEInvalid('"dir" and "ECDH-ES" alg may only be used with a single recipient');
106
+ }
107
+ if (typeof joseHeader.enc !== 'string' || !joseHeader.enc) {
108
+ throw new errors_js_1.JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid');
109
+ }
110
+ if (!enc) {
111
+ enc = joseHeader.enc;
112
+ }
113
+ else if (enc !== joseHeader.enc) {
114
+ throw new errors_js_1.JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients');
115
+ }
116
+ (0, validate_crit_js_1.default)(errors_js_1.JWEInvalid, new Map(), recipientOpts === null || recipientOpts === void 0 ? void 0 : recipientOpts.crit, this._protectedHeader, joseHeader);
117
+ if (joseHeader.zip !== undefined) {
118
+ if (!this._protectedHeader || !this._protectedHeader.zip) {
119
+ throw new errors_js_1.JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected');
120
+ }
121
+ }
122
+ }
123
+ const cek = (0, cek_js_1.default)(enc);
124
+ let jwe = {
125
+ ciphertext: '',
126
+ iv: '',
127
+ recipients: [],
128
+ tag: '',
129
+ };
130
+ for (let i = 0; i < this._recipients.length; i++) {
131
+ const recipient = this._recipients[i];
132
+ const target = {};
133
+ jwe.recipients.push(target);
134
+ const { unprotectedHeader, options: recipientOpts, key } = recipientRef.get(recipient);
135
+ if (i === 0) {
136
+ const flattened = await new encrypt_js_1.FlattenedEncrypt(this._plaintext)
137
+ .setAdditionalAuthenticatedData(this._aad)
138
+ .setContentEncryptionKey(cek)
139
+ .setProtectedHeader(this._protectedHeader)
140
+ .setSharedUnprotectedHeader(this._unprotectedHeader)
141
+ .setUnprotectedHeader(unprotectedHeader)
142
+ .encrypt(key, { ...recipientOpts, ...options, [encrypt_js_1.unprotected]: true });
143
+ jwe.ciphertext = flattened.ciphertext;
144
+ jwe.iv = flattened.iv;
145
+ jwe.tag = flattened.tag;
146
+ if (flattened.aad)
147
+ jwe.aad = flattened.aad;
148
+ if (flattened.protected)
149
+ jwe.protected = flattened.protected;
150
+ if (flattened.unprotected)
151
+ jwe.unprotected = flattened.unprotected;
152
+ target.encrypted_key = flattened.encrypted_key;
153
+ if (flattened.header)
154
+ target.header = flattened.header;
155
+ continue;
156
+ }
157
+ const { encryptedKey, parameters } = await (0, encrypt_key_management_js_1.default)((unprotectedHeader === null || unprotectedHeader === void 0 ? void 0 : unprotectedHeader.alg) || ((_a = this._protectedHeader) === null || _a === void 0 ? void 0 : _a.alg) || ((_b = this._unprotectedHeader) === null || _b === void 0 ? void 0 : _b.alg), enc, key, cek);
158
+ target.encrypted_key = (0, base64url_js_1.encode)(encryptedKey);
159
+ if (unprotectedHeader || parameters)
160
+ target.header = { ...unprotectedHeader, ...parameters };
161
+ }
162
+ return jwe;
163
+ }
164
+ }
165
+ exports.GeneralEncrypt = GeneralEncrypt;
@@ -65,7 +65,8 @@ function parseElement(bytes) {
65
65
  };
66
66
  }
67
67
  function spkiFromX509(buf) {
68
- return (0, base64url_js_1.encodeBase64)(getElement(getElement(parseElement(buf).contents)[0].contents)[6].raw);
68
+ const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
69
+ return (0, base64url_js_1.encodeBase64)(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw);
69
70
  }
70
71
  function getSPKI(x509) {
71
72
  const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '');
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.checkEncCryptoKey = exports.checkSigCryptoKey = void 0;
4
- const global_js_1 = require("../runtime/global.js");
4
+ const env_js_1 = require("../runtime/env.js");
5
5
  function unusable(name, prop = 'algorithm.name') {
6
6
  return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
7
7
  }
@@ -74,12 +74,12 @@ function checkSigCryptoKey(key, alg, ...usages) {
74
74
  throw unusable(`SHA-${expected}`, 'algorithm.hash');
75
75
  break;
76
76
  }
77
- case (0, global_js_1.isNodeJs)() && 'EdDSA': {
77
+ case (0, env_js_1.isNodeJs)() && 'EdDSA': {
78
78
  if (key.algorithm.name !== 'NODE-ED25519' && key.algorithm.name !== 'NODE-ED448')
79
79
  throw unusable('NODE-ED25519 or NODE-ED448');
80
80
  break;
81
81
  }
82
- case (0, global_js_1.isCloudflareWorkers)() && 'EdDSA': {
82
+ case (0, env_js_1.isCloudflareWorkers)() && 'EdDSA': {
83
83
  if (!isAlgorithm(key.algorithm, 'NODE-ED25519'))
84
84
  throw unusable('NODE-ED25519');
85
85
  break;
@@ -36,8 +36,8 @@ async function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) {
36
36
  }
37
37
  let plaintext;
38
38
  try {
39
- const cipher = (0, crypto_1.createDecipheriv)(algorithm, encKey, iv);
40
- plaintext = (0, buffer_utils_js_1.concat)(cipher.update(ciphertext), cipher.final());
39
+ const decipher = (0, crypto_1.createDecipheriv)(algorithm, encKey, iv);
40
+ plaintext = (0, buffer_utils_js_1.concat)(decipher.update(ciphertext), decipher.final());
41
41
  }
42
42
  catch {
43
43
  }
@@ -53,12 +53,14 @@ async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) {
53
53
  throw new errors_js_1.JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`);
54
54
  }
55
55
  try {
56
- const cipher = (0, crypto_1.createDecipheriv)(algorithm, cek, iv, { authTagLength: 16 });
57
- cipher.setAuthTag(tag);
56
+ const decipher = (0, crypto_1.createDecipheriv)(algorithm, cek, iv, { authTagLength: 16 });
57
+ decipher.setAuthTag(tag);
58
58
  if (aad.byteLength) {
59
- cipher.setAAD(aad, { plaintextLength: ciphertext.length });
59
+ decipher.setAAD(aad, { plaintextLength: ciphertext.length });
60
60
  }
61
- return (0, buffer_utils_js_1.concat)(cipher.update(ciphertext), cipher.final());
61
+ const plaintext = decipher.update(ciphertext);
62
+ decipher.final();
63
+ return plaintext;
62
64
  }
63
65
  catch {
64
66
  throw new errors_js_1.JWEDecryptionFailed();
@@ -38,7 +38,8 @@ async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
38
38
  if (aad.byteLength) {
39
39
  cipher.setAAD(aad, { plaintextLength: plaintext.length });
40
40
  }
41
- const ciphertext = (0, buffer_utils_js_1.concat)(cipher.update(plaintext), cipher.final());
41
+ const ciphertext = cipher.update(plaintext);
42
+ cipher.final();
42
43
  const tag = cipher.getAuthTag();
43
44
  return { ciphertext, tag };
44
45
  }
File without changes
@@ -16,10 +16,6 @@ else {
16
16
  const sign = async (alg, key, data) => {
17
17
  const keyObject = (0, get_sign_verify_key_js_1.default)(alg, key, 'sign');
18
18
  if (alg.startsWith('HS')) {
19
- const bitlen = parseInt(alg.substr(-3), 10);
20
- if (!keyObject.symmetricKeySize || keyObject.symmetricKeySize << 3 < bitlen) {
21
- throw new TypeError(`${alg} requires symmetric keys to be ${bitlen} bits or larger`);
22
- }
23
19
  const hmac = crypto.createHmac((0, hmac_digest_js_1.default)(alg), keyObject);
24
20
  hmac.update(data);
25
21
  return hmac.digest();
@@ -1,6 +1,7 @@
1
1
  export { compactDecrypt } from './jwe/compact/decrypt.js';
2
2
  export { flattenedDecrypt } from './jwe/flattened/decrypt.js';
3
3
  export { generalDecrypt } from './jwe/general/decrypt.js';
4
+ export { GeneralEncrypt } from './jwe/general/encrypt.js';
4
5
  export { compactVerify } from './jws/compact/verify.js';
5
6
  export { flattenedVerify } from './jws/flattened/verify.js';
6
7
  export { generalVerify } from './jws/general/verify.js';
@@ -7,6 +7,7 @@ import { JOSENotSupported, JWEInvalid } from '../../util/errors.js';
7
7
  import isDisjoint from '../../lib/is_disjoint.js';
8
8
  import { encoder, decoder, concat } from '../../lib/buffer_utils.js';
9
9
  import validateCrit from '../../lib/validate_crit.js';
10
+ export const unprotected = Symbol();
10
11
  export class FlattenedEncrypt {
11
12
  constructor(plaintext) {
12
13
  if (!(plaintext instanceof Uint8Array)) {
@@ -65,7 +66,7 @@ export class FlattenedEncrypt {
65
66
  throw new JWEInvalid('either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()');
66
67
  }
67
68
  if (!isDisjoint(this._protectedHeader, this._unprotectedHeader, this._sharedUnprotectedHeader)) {
68
- throw new JWEInvalid('JWE Shared Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint');
69
+ throw new JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint');
69
70
  }
70
71
  const joseHeader = {
71
72
  ...this._protectedHeader,
@@ -104,11 +105,21 @@ export class FlattenedEncrypt {
104
105
  let parameters;
105
106
  ({ cek, encryptedKey, parameters } = await encryptKeyManagement(alg, enc, key, this._cek, this._keyManagementParameters));
106
107
  if (parameters) {
107
- if (!this._protectedHeader) {
108
- this.setProtectedHeader(parameters);
108
+ if (options && unprotected in options) {
109
+ if (!this._unprotectedHeader) {
110
+ this.setUnprotectedHeader(parameters);
111
+ }
112
+ else {
113
+ this._unprotectedHeader = { ...this._unprotectedHeader, ...parameters };
114
+ }
109
115
  }
110
116
  else {
111
- this._protectedHeader = { ...this._protectedHeader, ...parameters };
117
+ if (!this._protectedHeader) {
118
+ this.setProtectedHeader(parameters);
119
+ }
120
+ else {
121
+ this._protectedHeader = { ...this._protectedHeader, ...parameters };
122
+ }
112
123
  }
113
124
  }
114
125
  }
@@ -8,6 +8,9 @@ export async function generalDecrypt(jwe, key, options) {
8
8
  if (!Array.isArray(jwe.recipients) || !jwe.recipients.every(isObject)) {
9
9
  throw new JWEInvalid('JWE Recipients missing or incorrect type');
10
10
  }
11
+ if (!jwe.recipients.length) {
12
+ throw new JWEInvalid('JWE Recipients has no members');
13
+ }
11
14
  for (const recipient of jwe.recipients) {
12
15
  try {
13
16
  return await flattenedDecrypt({
@@ -0,0 +1,161 @@
1
+ import { FlattenedEncrypt, unprotected } from '../flattened/encrypt.js';
2
+ import { JWEInvalid } from '../../util/errors.js';
3
+ import generateCek from '../../lib/cek.js';
4
+ import isDisjoint from '../../lib/is_disjoint.js';
5
+ import encryptKeyManagement from '../../lib/encrypt_key_management.js';
6
+ import { encode as base64url } from '../../runtime/base64url.js';
7
+ import validateCrit from '../../lib/validate_crit.js';
8
+ const recipientRef = new WeakMap();
9
+ class IndividualRecipient {
10
+ setUnprotectedHeader(unprotectedHeader) {
11
+ if (this._unprotectedHeader) {
12
+ throw new TypeError('setUnprotectedHeader can only be called once');
13
+ }
14
+ this._unprotectedHeader = unprotectedHeader;
15
+ return this;
16
+ }
17
+ set _unprotectedHeader(value) {
18
+ recipientRef.get(this).unprotectedHeader = value;
19
+ }
20
+ get _unprotectedHeader() {
21
+ return recipientRef.get(this).unprotectedHeader;
22
+ }
23
+ }
24
+ export class GeneralEncrypt {
25
+ constructor(plaintext) {
26
+ this._recipients = [];
27
+ this._plaintext = plaintext;
28
+ }
29
+ addRecipient(key, options) {
30
+ const recipient = new IndividualRecipient();
31
+ recipientRef.set(recipient, { key, options: { crit: options === null || options === void 0 ? void 0 : options.crit } });
32
+ this._recipients.push(recipient);
33
+ return recipient;
34
+ }
35
+ setProtectedHeader(protectedHeader) {
36
+ if (this._protectedHeader) {
37
+ throw new TypeError('setProtectedHeader can only be called once');
38
+ }
39
+ this._protectedHeader = protectedHeader;
40
+ return this;
41
+ }
42
+ setSharedUnprotectedHeader(sharedUnprotectedHeader) {
43
+ if (this._unprotectedHeader) {
44
+ throw new TypeError('setSharedUnprotectedHeader can only be called once');
45
+ }
46
+ this._unprotectedHeader = sharedUnprotectedHeader;
47
+ return this;
48
+ }
49
+ setAdditionalAuthenticatedData(aad) {
50
+ this._aad = aad;
51
+ return this;
52
+ }
53
+ async encrypt(options) {
54
+ var _a, _b;
55
+ if (!this._recipients.length) {
56
+ throw new JWEInvalid('at least one recipient must be added');
57
+ }
58
+ options = { deflateRaw: options === null || options === void 0 ? void 0 : options.deflateRaw };
59
+ if (this._recipients.length === 1) {
60
+ const { unprotectedHeader, options: recipientOpts, key, } = recipientRef.get(this._recipients[0]);
61
+ const flattened = await new FlattenedEncrypt(this._plaintext)
62
+ .setAdditionalAuthenticatedData(this._aad)
63
+ .setProtectedHeader(this._protectedHeader)
64
+ .setSharedUnprotectedHeader(this._unprotectedHeader)
65
+ .setUnprotectedHeader(unprotectedHeader)
66
+ .encrypt(key, { ...recipientOpts, ...options });
67
+ let jwe = {
68
+ ciphertext: flattened.ciphertext,
69
+ iv: flattened.iv,
70
+ recipients: [{}],
71
+ tag: flattened.tag,
72
+ };
73
+ if (flattened.aad)
74
+ jwe.aad = flattened.aad;
75
+ if (flattened.protected)
76
+ jwe.protected = flattened.protected;
77
+ if (flattened.unprotected)
78
+ jwe.unprotected = flattened.unprotected;
79
+ if (flattened.encrypted_key)
80
+ jwe.recipients[0].encrypted_key = flattened.encrypted_key;
81
+ if (flattened.header)
82
+ jwe.recipients[0].header = flattened.header;
83
+ return jwe;
84
+ }
85
+ let enc;
86
+ for (let i = 0; i < this._recipients.length; i++) {
87
+ const recipient = this._recipients[i];
88
+ const { unprotectedHeader, options: recipientOpts } = recipientRef.get(recipient);
89
+ if (!isDisjoint(this._protectedHeader, this._unprotectedHeader, unprotectedHeader)) {
90
+ throw new JWEInvalid('JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint');
91
+ }
92
+ const joseHeader = {
93
+ ...this._protectedHeader,
94
+ ...this._unprotectedHeader,
95
+ ...unprotectedHeader,
96
+ };
97
+ const { alg } = joseHeader;
98
+ if (typeof alg !== 'string' || !alg) {
99
+ throw new JWEInvalid('JWE "alg" (Algorithm) Header Parameter missing or invalid');
100
+ }
101
+ if (alg === 'dir' || alg === 'ECDH-ES') {
102
+ throw new JWEInvalid('"dir" and "ECDH-ES" alg may only be used with a single recipient');
103
+ }
104
+ if (typeof joseHeader.enc !== 'string' || !joseHeader.enc) {
105
+ throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid');
106
+ }
107
+ if (!enc) {
108
+ enc = joseHeader.enc;
109
+ }
110
+ else if (enc !== joseHeader.enc) {
111
+ throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter must be the same for all recipients');
112
+ }
113
+ validateCrit(JWEInvalid, new Map(), recipientOpts === null || recipientOpts === void 0 ? void 0 : recipientOpts.crit, this._protectedHeader, joseHeader);
114
+ if (joseHeader.zip !== undefined) {
115
+ if (!this._protectedHeader || !this._protectedHeader.zip) {
116
+ throw new JWEInvalid('JWE "zip" (Compression Algorithm) Header MUST be integrity protected');
117
+ }
118
+ }
119
+ }
120
+ const cek = generateCek(enc);
121
+ let jwe = {
122
+ ciphertext: '',
123
+ iv: '',
124
+ recipients: [],
125
+ tag: '',
126
+ };
127
+ for (let i = 0; i < this._recipients.length; i++) {
128
+ const recipient = this._recipients[i];
129
+ const target = {};
130
+ jwe.recipients.push(target);
131
+ const { unprotectedHeader, options: recipientOpts, key } = recipientRef.get(recipient);
132
+ if (i === 0) {
133
+ const flattened = await new FlattenedEncrypt(this._plaintext)
134
+ .setAdditionalAuthenticatedData(this._aad)
135
+ .setContentEncryptionKey(cek)
136
+ .setProtectedHeader(this._protectedHeader)
137
+ .setSharedUnprotectedHeader(this._unprotectedHeader)
138
+ .setUnprotectedHeader(unprotectedHeader)
139
+ .encrypt(key, { ...recipientOpts, ...options, [unprotected]: true });
140
+ jwe.ciphertext = flattened.ciphertext;
141
+ jwe.iv = flattened.iv;
142
+ jwe.tag = flattened.tag;
143
+ if (flattened.aad)
144
+ jwe.aad = flattened.aad;
145
+ if (flattened.protected)
146
+ jwe.protected = flattened.protected;
147
+ if (flattened.unprotected)
148
+ jwe.unprotected = flattened.unprotected;
149
+ target.encrypted_key = flattened.encrypted_key;
150
+ if (flattened.header)
151
+ target.header = flattened.header;
152
+ continue;
153
+ }
154
+ const { encryptedKey, parameters } = await encryptKeyManagement((unprotectedHeader === null || unprotectedHeader === void 0 ? void 0 : unprotectedHeader.alg) || ((_a = this._protectedHeader) === null || _a === void 0 ? void 0 : _a.alg) || ((_b = this._unprotectedHeader) === null || _b === void 0 ? void 0 : _b.alg), enc, key, cek);
155
+ target.encrypted_key = base64url(encryptedKey);
156
+ if (unprotectedHeader || parameters)
157
+ target.header = { ...unprotectedHeader, ...parameters };
158
+ }
159
+ return jwe;
160
+ }
161
+ }
@@ -62,7 +62,8 @@ function parseElement(bytes) {
62
62
  };
63
63
  }
64
64
  function spkiFromX509(buf) {
65
- return encodeBase64(getElement(getElement(parseElement(buf).contents)[0].contents)[6].raw);
65
+ const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
66
+ return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw);
66
67
  }
67
68
  function getSPKI(x509) {
68
69
  const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '');
@@ -1,4 +1,4 @@
1
- import { isCloudflareWorkers, isNodeJs } from '../runtime/global.js';
1
+ import { isCloudflareWorkers, isNodeJs } from '../runtime/env.js';
2
2
  function unusable(name, prop = 'algorithm.name') {
3
3
  return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
4
4
  }
@@ -34,8 +34,8 @@ async function cbcDecrypt(enc, cek, ciphertext, iv, tag, aad) {
34
34
  }
35
35
  let plaintext;
36
36
  try {
37
- const cipher = createDecipheriv(algorithm, encKey, iv);
38
- plaintext = concat(cipher.update(ciphertext), cipher.final());
37
+ const decipher = createDecipheriv(algorithm, encKey, iv);
38
+ plaintext = concat(decipher.update(ciphertext), decipher.final());
39
39
  }
40
40
  catch {
41
41
  }
@@ -51,12 +51,14 @@ async function gcmDecrypt(enc, cek, ciphertext, iv, tag, aad) {
51
51
  throw new JOSENotSupported(`alg ${enc} is not supported by your javascript runtime`);
52
52
  }
53
53
  try {
54
- const cipher = createDecipheriv(algorithm, cek, iv, { authTagLength: 16 });
55
- cipher.setAuthTag(tag);
54
+ const decipher = createDecipheriv(algorithm, cek, iv, { authTagLength: 16 });
55
+ decipher.setAuthTag(tag);
56
56
  if (aad.byteLength) {
57
- cipher.setAAD(aad, { plaintextLength: ciphertext.length });
57
+ decipher.setAAD(aad, { plaintextLength: ciphertext.length });
58
58
  }
59
- return concat(cipher.update(ciphertext), cipher.final());
59
+ const plaintext = decipher.update(ciphertext);
60
+ decipher.final();
61
+ return plaintext;
60
62
  }
61
63
  catch {
62
64
  throw new JWEDecryptionFailed();
@@ -36,7 +36,8 @@ async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
36
36
  if (aad.byteLength) {
37
37
  cipher.setAAD(aad, { plaintextLength: plaintext.length });
38
38
  }
39
- const ciphertext = concat(cipher.update(plaintext), cipher.final());
39
+ const ciphertext = cipher.update(plaintext);
40
+ cipher.final();
40
41
  const tag = cipher.getAuthTag();
41
42
  return { ciphertext, tag };
42
43
  }
File without changes
@@ -14,10 +14,6 @@ else {
14
14
  const sign = async (alg, key, data) => {
15
15
  const keyObject = getSignKey(alg, key, 'sign');
16
16
  if (alg.startsWith('HS')) {
17
- const bitlen = parseInt(alg.substr(-3), 10);
18
- if (!keyObject.symmetricKeySize || keyObject.symmetricKeySize << 3 < bitlen) {
19
- throw new TypeError(`${alg} requires symmetric keys to be ${bitlen} bits or larger`);
20
- }
21
17
  const hmac = crypto.createHmac(hmacDigest(alg), keyObject);
22
18
  hmac.update(data);
23
19
  return hmac.digest();
@@ -4,6 +4,8 @@ export { flattenedDecrypt } from './jwe/flattened/decrypt';
4
4
  export type { FlattenedDecryptGetKey } from './jwe/flattened/decrypt';
5
5
  export { generalDecrypt } from './jwe/general/decrypt';
6
6
  export type { GeneralDecryptGetKey } from './jwe/general/decrypt';
7
+ export { GeneralEncrypt } from './jwe/general/encrypt';
8
+ export type { Recipient } from './jwe/general/encrypt';
7
9
  export { compactVerify } from './jws/compact/verify';
8
10
  export type { CompactVerifyGetKey } from './jws/compact/verify';
9
11
  export { flattenedVerify } from './jws/flattened/verify';
@@ -40,4 +42,4 @@ export type { GenerateKeyPairResult, GenerateKeyPairOptions } from './key/genera
40
42
  export { generateSecret } from './key/generate_secret';
41
43
  export type { GenerateSecretOptions } from './key/generate_secret';
42
44
  export * as base64url from './util/base64url';
43
- export type { KeyLike, JWK, FlattenedJWSInput, GeneralJWSInput, FlattenedJWS, GeneralJWS, JoseHeaderParameters, JWSHeaderParameters, JWEKeyManagementHeaderParameters, FlattenedJWE, GeneralJWE, JWEHeaderParameters, CritOption, DecryptOptions, EncryptOptions, JWTClaimVerificationOptions, VerifyOptions, SignOptions, JWTPayload, DeflateFunction, InflateFunction, FlattenedDecryptResult, GeneralDecryptResult, CompactDecryptResult, FlattenedVerifyResult, GeneralVerifyResult, CompactVerifyResult, JWTVerifyResult, JWTDecryptResult, ResolvedKey, } from './types';
45
+ export type { KeyLike, JWK, FlattenedJWSInput, GeneralJWSInput, FlattenedJWS, GeneralJWS, JoseHeaderParameters, JWSHeaderParameters, JWEKeyManagementHeaderParameters, FlattenedJWE, GeneralJWE, JWEHeaderParameters, CritOption, DeflateOption, DecryptOptions, EncryptOptions, JWTClaimVerificationOptions, VerifyOptions, SignOptions, JWTPayload, DeflateFunction, InflateFunction, FlattenedDecryptResult, GeneralDecryptResult, CompactDecryptResult, FlattenedVerifyResult, GeneralVerifyResult, CompactVerifyResult, JWTVerifyResult, JWTDecryptResult, ResolvedKey, } from './types';
@@ -1,4 +1,5 @@
1
1
  import type { KeyLike, FlattenedJWE, JWEHeaderParameters, JWEKeyManagementHeaderParameters, EncryptOptions } from '../../types';
2
+ export declare const unprotected: unique symbol;
2
3
  export declare class FlattenedEncrypt {
3
4
  private _plaintext;
4
5
  private _protectedHeader;
@@ -0,0 +1,17 @@
1
+ import type { KeyLike, GeneralJWE, JWEHeaderParameters, CritOption, DeflateOption } from '../../types';
2
+ export interface Recipient {
3
+ setUnprotectedHeader(unprotectedHeader: JWEHeaderParameters): Recipient;
4
+ }
5
+ export declare class GeneralEncrypt {
6
+ private _plaintext;
7
+ private _recipients;
8
+ private _protectedHeader;
9
+ private _unprotectedHeader;
10
+ private _aad;
11
+ constructor(plaintext: Uint8Array);
12
+ addRecipient(key: KeyLike | Uint8Array, options?: CritOption): Recipient;
13
+ setProtectedHeader(protectedHeader: JWEHeaderParameters): this;
14
+ setSharedUnprotectedHeader(sharedUnprotectedHeader: JWEHeaderParameters): this;
15
+ setAdditionalAuthenticatedData(aad: Uint8Array): this;
16
+ encrypt(options?: DeflateOption): Promise<GeneralJWE>;
17
+ }
@@ -200,9 +200,10 @@ export interface DecryptOptions extends CritOption {
200
200
  contentEncryptionAlgorithms?: string[]
201
201
  inflateRaw?: InflateFunction
202
202
  }
203
- export interface EncryptOptions extends CritOption {
203
+ export interface DeflateOption {
204
204
  deflateRaw?: DeflateFunction
205
205
  }
206
+ export interface EncryptOptions extends CritOption, DeflateOption {}
206
207
  export interface JWTClaimVerificationOptions {
207
208
  audience?: string | string[]
208
209
  clockTolerance?: string | number
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jose",
3
- "version": "4.1.2",
3
+ "version": "4.2.0",
4
4
  "description": "'JSON Web Almost Everything' - JWA, JWS, JWE, JWT, JWK, JWKS with no dependencies using runtime's native crypto",
5
5
  "keywords": [
6
6
  "browser",
@@ -1,27 +0,0 @@
1
- function getGlobal() {
2
- if (typeof globalThis !== 'undefined')
3
- return globalThis;
4
- if (typeof self !== 'undefined')
5
- return self;
6
- if (typeof window !== 'undefined')
7
- return window;
8
- throw new Error('unable to locate global object');
9
- }
10
- export default getGlobal();
11
- export function isCloudflareWorkers() {
12
- try {
13
- return getGlobal().WebSocketPair !== undefined;
14
- }
15
- catch (_a) {
16
- return false;
17
- }
18
- }
19
- export function isNodeJs() {
20
- var _a, _b;
21
- try {
22
- return ((_b = (_a = getGlobal().process) === null || _a === void 0 ? void 0 : _a.versions) === null || _b === void 0 ? void 0 : _b.node) !== undefined;
23
- }
24
- catch (_c) {
25
- return false;
26
- }
27
- }