jose 5.2.2 → 5.2.4

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.
@@ -1,6 +1,5 @@
1
1
  import { encode as base64url } from '../../runtime/base64url.js';
2
2
  import encrypt from '../../runtime/encrypt.js';
3
- import generateIv from '../../lib/iv.js';
4
3
  import encryptKeyManagement from '../../lib/encrypt_key_management.js';
5
4
  import { JOSENotSupported, JWEInvalid } from '../../util/errors.js';
6
5
  import isDisjoint from '../../lib/is_disjoint.js';
@@ -84,15 +83,8 @@ export class FlattenedEncrypt {
84
83
  throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid');
85
84
  }
86
85
  let encryptedKey;
87
- if (alg === 'dir') {
88
- if (this._cek) {
89
- throw new TypeError('setContentEncryptionKey cannot be called when using Direct Encryption');
90
- }
91
- }
92
- else if (alg === 'ECDH-ES') {
93
- if (this._cek) {
94
- throw new TypeError('setContentEncryptionKey cannot be called when using Direct Key Agreement');
95
- }
86
+ if (this._cek && (alg === 'dir' || alg === 'ECDH-ES')) {
87
+ throw new TypeError(`setContentEncryptionKey cannot be called with JWE "alg" (Algorithm) Header ${alg}`);
96
88
  }
97
89
  let cek;
98
90
  {
@@ -117,7 +109,6 @@ export class FlattenedEncrypt {
117
109
  }
118
110
  }
119
111
  }
120
- this._iv || (this._iv = generateIv(enc));
121
112
  let additionalData;
122
113
  let protectedHeader;
123
114
  let aadMember;
@@ -134,12 +125,16 @@ export class FlattenedEncrypt {
134
125
  else {
135
126
  additionalData = protectedHeader;
136
127
  }
137
- const { ciphertext, tag } = await encrypt(enc, this._plaintext, cek, this._iv, additionalData);
128
+ const { ciphertext, tag, iv } = await encrypt(enc, this._plaintext, cek, this._iv, additionalData);
138
129
  const jwe = {
139
130
  ciphertext: base64url(ciphertext),
140
- iv: base64url(this._iv),
141
- tag: base64url(tag),
142
131
  };
132
+ if (iv) {
133
+ jwe.iv = base64url(iv);
134
+ }
135
+ if (tag) {
136
+ jwe.tag = base64url(tag);
137
+ }
143
138
  if (encryptedKey) {
144
139
  jwe.encrypted_key = base64url(encryptedKey);
145
140
  }
@@ -14,7 +14,7 @@ function getKtyFromAlg(alg) {
14
14
  throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
15
15
  }
16
16
  }
17
- export function isJWKSLike(jwks) {
17
+ function isJWKSLike(jwks) {
18
18
  return (jwks &&
19
19
  typeof jwks === 'object' &&
20
20
  Array.isArray(jwks.keys) &&
@@ -1,6 +1,6 @@
1
1
  import fetchJwks from '../runtime/fetch_jwks.js';
2
- import { JWKSInvalid, JWKSNoMatchingKey } from '../util/errors.js';
3
- import { isJWKSLike, LocalJWKSet } from './local.js';
2
+ import { JWKSNoMatchingKey } from '../util/errors.js';
3
+ import { createLocalJWKSet } from './local.js';
4
4
  function isCloudflareWorkers() {
5
5
  return (typeof WebSocketPair !== 'undefined' ||
6
6
  (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') ||
@@ -9,13 +9,11 @@ function isCloudflareWorkers() {
9
9
  let USER_AGENT;
10
10
  if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
11
11
  const NAME = 'jose';
12
- const VERSION = 'v5.2.2';
12
+ const VERSION = 'v5.2.4';
13
13
  USER_AGENT = `${NAME}/${VERSION}`;
14
14
  }
15
- class RemoteJWKSet extends LocalJWKSet {
15
+ class RemoteJWKSet {
16
16
  constructor(url, options) {
17
- super({ keys: [] });
18
- this._jwks = undefined;
19
17
  if (!(url instanceof URL)) {
20
18
  throw new TypeError('url must be an instance of URL');
21
19
  }
@@ -38,17 +36,17 @@ class RemoteJWKSet extends LocalJWKSet {
38
36
  : false;
39
37
  }
40
38
  async getKey(protectedHeader, token) {
41
- if (!this._jwks || !this.fresh()) {
39
+ if (!this._local || !this.fresh()) {
42
40
  await this.reload();
43
41
  }
44
42
  try {
45
- return await super.getKey(protectedHeader, token);
43
+ return await this._local(protectedHeader, token);
46
44
  }
47
45
  catch (err) {
48
46
  if (err instanceof JWKSNoMatchingKey) {
49
47
  if (this.coolingDown() === false) {
50
48
  await this.reload();
51
- return super.getKey(protectedHeader, token);
49
+ return this._local(protectedHeader, token);
52
50
  }
53
51
  }
54
52
  throw err;
@@ -65,10 +63,7 @@ class RemoteJWKSet extends LocalJWKSet {
65
63
  }
66
64
  this._pendingFetch || (this._pendingFetch = fetchJwks(this._url, this._timeoutDuration, this._options)
67
65
  .then((json) => {
68
- if (!isJWKSLike(json)) {
69
- throw new JWKSInvalid('JSON Web Key Set malformed');
70
- }
71
- this._jwks = { keys: json.keys };
66
+ this._local = createLocalJWKSet(json);
72
67
  this._jwksTimestamp = Date.now();
73
68
  this._pendingFetch = undefined;
74
69
  })
@@ -1,12 +1,14 @@
1
1
  import encrypt from '../runtime/encrypt.js';
2
2
  import decrypt from '../runtime/decrypt.js';
3
- import generateIv from './iv.js';
4
3
  import { encode as base64url } from '../runtime/base64url.js';
5
4
  export async function wrap(alg, key, cek, iv) {
6
5
  const jweAlgorithm = alg.slice(0, 7);
7
- iv || (iv = generateIv(jweAlgorithm));
8
- const { ciphertext: encryptedKey, tag } = await encrypt(jweAlgorithm, cek, key, iv, new Uint8Array(0));
9
- return { encryptedKey, iv: base64url(iv), tag: base64url(tag) };
6
+ const wrapped = await encrypt(jweAlgorithm, cek, key, iv, new Uint8Array(0));
7
+ return {
8
+ encryptedKey: wrapped.ciphertext,
9
+ iv: base64url(wrapped.iv),
10
+ tag: base64url(wrapped.tag),
11
+ };
10
12
  }
11
13
  export async function unwrap(alg, key, encryptedKey, iv, tag) {
12
14
  const jweAlgorithm = alg.slice(0, 7);
@@ -1,6 +1,6 @@
1
1
  import { JOSENotSupported } from '../util/errors.js';
2
2
  function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {
3
- if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) {
3
+ if (joseHeader.crit !== undefined && protectedHeader?.crit === undefined) {
4
4
  throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected');
5
5
  }
6
6
  if (!protectedHeader || protectedHeader.crit === undefined) {
@@ -4,6 +4,7 @@ import checkCekLength from './check_cek_length.js';
4
4
  import crypto, { isCryptoKey } from './webcrypto.js';
5
5
  import { checkEncCryptoKey } from '../lib/crypto_key.js';
6
6
  import invalidKeyInput from '../lib/invalid_key_input.js';
7
+ import generateIv from '../lib/iv.js';
7
8
  import { JOSENotSupported } from '../util/errors.js';
8
9
  import { types } from './is_key_like.js';
9
10
  async function cbcEncrypt(enc, plaintext, cek, iv, aad) {
@@ -22,7 +23,7 @@ async function cbcEncrypt(enc, plaintext, cek, iv, aad) {
22
23
  }, encKey, plaintext));
23
24
  const macData = concat(aad, iv, ciphertext, uint64be(aad.length << 3));
24
25
  const tag = new Uint8Array((await crypto.subtle.sign('HMAC', macKey, macData)).slice(0, keySize >> 3));
25
- return { ciphertext, tag };
26
+ return { ciphertext, tag, iv };
26
27
  }
27
28
  async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
28
29
  let encKey;
@@ -41,25 +42,32 @@ async function gcmEncrypt(enc, plaintext, cek, iv, aad) {
41
42
  }, encKey, plaintext));
42
43
  const tag = encrypted.slice(-16);
43
44
  const ciphertext = encrypted.slice(0, -16);
44
- return { ciphertext, tag };
45
+ return { ciphertext, tag, iv };
45
46
  }
46
47
  const encrypt = async (enc, plaintext, cek, iv, aad) => {
47
48
  if (!isCryptoKey(cek) && !(cek instanceof Uint8Array)) {
48
49
  throw new TypeError(invalidKeyInput(cek, ...types, 'Uint8Array'));
49
50
  }
50
- checkIvLength(enc, iv);
51
+ if (iv) {
52
+ checkIvLength(enc, iv);
53
+ }
54
+ else {
55
+ iv = generateIv(enc);
56
+ }
51
57
  switch (enc) {
52
58
  case 'A128CBC-HS256':
53
59
  case 'A192CBC-HS384':
54
60
  case 'A256CBC-HS512':
55
- if (cek instanceof Uint8Array)
61
+ if (cek instanceof Uint8Array) {
56
62
  checkCekLength(cek, parseInt(enc.slice(-3), 10));
63
+ }
57
64
  return cbcEncrypt(enc, plaintext, cek, iv, aad);
58
65
  case 'A128GCM':
59
66
  case 'A192GCM':
60
67
  case 'A256GCM':
61
- if (cek instanceof Uint8Array)
68
+ if (cek instanceof Uint8Array) {
62
69
  checkCekLength(cek, parseInt(enc.slice(1, 4), 10));
70
+ }
63
71
  return gcmEncrypt(enc, plaintext, cek, iv, aad);
64
72
  default:
65
73
  throw new JOSENotSupported('Unsupported JWE Content Encryption Algorithm');
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
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
- const iv_js_1 = require("../../lib/iv.js");
7
6
  const encrypt_key_management_js_1 = require("../../lib/encrypt_key_management.js");
8
7
  const errors_js_1 = require("../../util/errors.js");
9
8
  const is_disjoint_js_1 = require("../../lib/is_disjoint.js");
@@ -95,15 +94,8 @@ class FlattenedEncrypt {
95
94
  throw new errors_js_1.JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid');
96
95
  }
97
96
  let encryptedKey;
98
- if (alg === 'dir') {
99
- if (this._cek) {
100
- throw new TypeError('setContentEncryptionKey cannot be called when using Direct Encryption');
101
- }
102
- }
103
- else if (alg === 'ECDH-ES') {
104
- if (this._cek) {
105
- throw new TypeError('setContentEncryptionKey cannot be called when using Direct Key Agreement');
106
- }
97
+ if (this._cek && (alg === 'dir' || alg === 'ECDH-ES')) {
98
+ throw new TypeError(`setContentEncryptionKey cannot be called with JWE "alg" (Algorithm) Header ${alg}`);
107
99
  }
108
100
  let cek;
109
101
  {
@@ -128,7 +120,6 @@ class FlattenedEncrypt {
128
120
  }
129
121
  }
130
122
  }
131
- this._iv ||= (0, iv_js_1.default)(enc);
132
123
  let additionalData;
133
124
  let protectedHeader;
134
125
  let aadMember;
@@ -145,12 +136,16 @@ class FlattenedEncrypt {
145
136
  else {
146
137
  additionalData = protectedHeader;
147
138
  }
148
- const { ciphertext, tag } = await (0, encrypt_js_1.default)(enc, this._plaintext, cek, this._iv, additionalData);
139
+ const { ciphertext, tag, iv } = await (0, encrypt_js_1.default)(enc, this._plaintext, cek, this._iv, additionalData);
149
140
  const jwe = {
150
141
  ciphertext: (0, base64url_js_1.encode)(ciphertext),
151
- iv: (0, base64url_js_1.encode)(this._iv),
152
- tag: (0, base64url_js_1.encode)(tag),
153
142
  };
143
+ if (iv) {
144
+ jwe.iv = (0, base64url_js_1.encode)(iv);
145
+ }
146
+ if (tag) {
147
+ jwe.tag = (0, base64url_js_1.encode)(tag);
148
+ }
154
149
  if (encryptedKey) {
155
150
  jwe.encrypted_key = (0, base64url_js_1.encode)(encryptedKey);
156
151
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createLocalJWKSet = exports.LocalJWKSet = exports.isJWKSLike = void 0;
3
+ exports.createLocalJWKSet = exports.LocalJWKSet = void 0;
4
4
  const import_js_1 = require("../key/import.js");
5
5
  const errors_js_1 = require("../util/errors.js");
6
6
  const is_object_js_1 = require("../lib/is_object.js");
@@ -23,7 +23,6 @@ function isJWKSLike(jwks) {
23
23
  Array.isArray(jwks.keys) &&
24
24
  jwks.keys.every(isJWKLike));
25
25
  }
26
- exports.isJWKSLike = isJWKSLike;
27
26
  function isJWKLike(key) {
28
27
  return (0, is_object_js_1.default)(key);
29
28
  }
@@ -12,10 +12,10 @@ function isCloudflareWorkers() {
12
12
  let USER_AGENT;
13
13
  if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
14
14
  const NAME = 'jose';
15
- const VERSION = 'v5.2.2';
15
+ const VERSION = 'v5.2.4';
16
16
  USER_AGENT = `${NAME}/${VERSION}`;
17
17
  }
18
- class RemoteJWKSet extends local_js_1.LocalJWKSet {
18
+ class RemoteJWKSet {
19
19
  _url;
20
20
  _timeoutDuration;
21
21
  _cooldownDuration;
@@ -23,9 +23,8 @@ class RemoteJWKSet extends local_js_1.LocalJWKSet {
23
23
  _jwksTimestamp;
24
24
  _pendingFetch;
25
25
  _options;
26
+ _local;
26
27
  constructor(url, options) {
27
- super({ keys: [] });
28
- this._jwks = undefined;
29
28
  if (!(url instanceof URL)) {
30
29
  throw new TypeError('url must be an instance of URL');
31
30
  }
@@ -48,17 +47,17 @@ class RemoteJWKSet extends local_js_1.LocalJWKSet {
48
47
  : false;
49
48
  }
50
49
  async getKey(protectedHeader, token) {
51
- if (!this._jwks || !this.fresh()) {
50
+ if (!this._local || !this.fresh()) {
52
51
  await this.reload();
53
52
  }
54
53
  try {
55
- return await super.getKey(protectedHeader, token);
54
+ return await this._local(protectedHeader, token);
56
55
  }
57
56
  catch (err) {
58
57
  if (err instanceof errors_js_1.JWKSNoMatchingKey) {
59
58
  if (this.coolingDown() === false) {
60
59
  await this.reload();
61
- return super.getKey(protectedHeader, token);
60
+ return this._local(protectedHeader, token);
62
61
  }
63
62
  }
64
63
  throw err;
@@ -75,10 +74,7 @@ class RemoteJWKSet extends local_js_1.LocalJWKSet {
75
74
  }
76
75
  this._pendingFetch ||= (0, fetch_jwks_js_1.default)(this._url, this._timeoutDuration, this._options)
77
76
  .then((json) => {
78
- if (!(0, local_js_1.isJWKSLike)(json)) {
79
- throw new errors_js_1.JWKSInvalid('JSON Web Key Set malformed');
80
- }
81
- this._jwks = { keys: json.keys };
77
+ this._local = (0, local_js_1.createLocalJWKSet)(json);
82
78
  this._jwksTimestamp = Date.now();
83
79
  this._pendingFetch = undefined;
84
80
  })
@@ -3,13 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.unwrap = exports.wrap = void 0;
4
4
  const encrypt_js_1 = require("../runtime/encrypt.js");
5
5
  const decrypt_js_1 = require("../runtime/decrypt.js");
6
- const iv_js_1 = require("./iv.js");
7
6
  const base64url_js_1 = require("../runtime/base64url.js");
8
7
  async function wrap(alg, key, cek, iv) {
9
8
  const jweAlgorithm = alg.slice(0, 7);
10
- iv ||= (0, iv_js_1.default)(jweAlgorithm);
11
- const { ciphertext: encryptedKey, tag } = await (0, encrypt_js_1.default)(jweAlgorithm, cek, key, iv, new Uint8Array(0));
12
- return { encryptedKey, iv: (0, base64url_js_1.encode)(iv), tag: (0, base64url_js_1.encode)(tag) };
9
+ const wrapped = await (0, encrypt_js_1.default)(jweAlgorithm, cek, key, iv, new Uint8Array(0));
10
+ return {
11
+ encryptedKey: wrapped.ciphertext,
12
+ iv: (0, base64url_js_1.encode)(wrapped.iv),
13
+ tag: (0, base64url_js_1.encode)(wrapped.tag),
14
+ };
13
15
  }
14
16
  exports.wrap = wrap;
15
17
  async function unwrap(alg, key, encryptedKey, iv, tag) {
@@ -75,7 +75,7 @@ async function decryptKeyManagement(alg, key, encryptedKey, joseHeader, options)
75
75
  throw new errors_js_1.JWEInvalid('JWE Encrypted Key missing');
76
76
  if (typeof joseHeader.p2c !== 'number')
77
77
  throw new errors_js_1.JWEInvalid(`JOSE Header "p2c" (PBES2 Count) missing or invalid`);
78
- const p2cLimit = options?.maxPBES2Count || 10000;
78
+ const p2cLimit = options?.maxPBES2Count || 10_000;
79
79
  if (joseHeader.p2c > p2cLimit)
80
80
  throw new errors_js_1.JWEInvalid(`JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds`);
81
81
  if (typeof joseHeader.p2s !== 'string')
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const errors_js_1 = require("../util/errors.js");
4
4
  function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {
5
- if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) {
5
+ if (joseHeader.crit !== undefined && protectedHeader?.crit === undefined) {
6
6
  throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected');
7
7
  }
8
8
  if (!protectedHeader || protectedHeader.crit === undefined) {
@@ -9,6 +9,7 @@ const webcrypto_js_1 = require("./webcrypto.js");
9
9
  const crypto_key_js_1 = require("../lib/crypto_key.js");
10
10
  const is_key_object_js_1 = require("./is_key_object.js");
11
11
  const invalid_key_input_js_1 = require("../lib/invalid_key_input.js");
12
+ const iv_js_1 = require("../lib/iv.js");
12
13
  const errors_js_1 = require("../util/errors.js");
13
14
  const ciphers_js_1 = require("./ciphers.js");
14
15
  const is_key_like_js_1 = require("./is_key_like.js");
@@ -27,7 +28,7 @@ function cbcEncrypt(enc, plaintext, cek, iv, aad) {
27
28
  const ciphertext = (0, buffer_utils_js_1.concat)(cipher.update(plaintext), cipher.final());
28
29
  const macSize = parseInt(enc.slice(-3), 10);
29
30
  const tag = (0, cbc_tag_js_1.default)(aad, iv, ciphertext, macSize, macKey, keySize);
30
- return { ciphertext, tag };
31
+ return { ciphertext, tag, iv };
31
32
  }
32
33
  function gcmEncrypt(enc, plaintext, cek, iv, aad) {
33
34
  const keySize = parseInt(enc.slice(1, 4), 10);
@@ -42,7 +43,7 @@ function gcmEncrypt(enc, plaintext, cek, iv, aad) {
42
43
  const ciphertext = cipher.update(plaintext);
43
44
  cipher.final();
44
45
  const tag = cipher.getAuthTag();
45
- return { ciphertext, tag };
46
+ return { ciphertext, tag, iv };
46
47
  }
47
48
  const encrypt = (enc, plaintext, cek, iv, aad) => {
48
49
  let key;
@@ -57,7 +58,12 @@ const encrypt = (enc, plaintext, cek, iv, aad) => {
57
58
  throw new TypeError((0, invalid_key_input_js_1.default)(cek, ...is_key_like_js_1.types, 'Uint8Array'));
58
59
  }
59
60
  (0, check_cek_length_js_1.default)(enc, key);
60
- (0, check_iv_length_js_1.default)(enc, iv);
61
+ if (iv) {
62
+ (0, check_iv_length_js_1.default)(enc, iv);
63
+ }
64
+ else {
65
+ iv = (0, iv_js_1.default)(enc);
66
+ }
61
67
  switch (enc) {
62
68
  case 'A128CBC-HS256':
63
69
  case 'A192CBC-HS384':
@@ -1,6 +1,5 @@
1
1
  import { encode as base64url } from '../../runtime/base64url.js';
2
2
  import encrypt from '../../runtime/encrypt.js';
3
- import generateIv from '../../lib/iv.js';
4
3
  import encryptKeyManagement from '../../lib/encrypt_key_management.js';
5
4
  import { JOSENotSupported, JWEInvalid } from '../../util/errors.js';
6
5
  import isDisjoint from '../../lib/is_disjoint.js';
@@ -92,15 +91,8 @@ export class FlattenedEncrypt {
92
91
  throw new JWEInvalid('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid');
93
92
  }
94
93
  let encryptedKey;
95
- if (alg === 'dir') {
96
- if (this._cek) {
97
- throw new TypeError('setContentEncryptionKey cannot be called when using Direct Encryption');
98
- }
99
- }
100
- else if (alg === 'ECDH-ES') {
101
- if (this._cek) {
102
- throw new TypeError('setContentEncryptionKey cannot be called when using Direct Key Agreement');
103
- }
94
+ if (this._cek && (alg === 'dir' || alg === 'ECDH-ES')) {
95
+ throw new TypeError(`setContentEncryptionKey cannot be called with JWE "alg" (Algorithm) Header ${alg}`);
104
96
  }
105
97
  let cek;
106
98
  {
@@ -125,7 +117,6 @@ export class FlattenedEncrypt {
125
117
  }
126
118
  }
127
119
  }
128
- this._iv ||= generateIv(enc);
129
120
  let additionalData;
130
121
  let protectedHeader;
131
122
  let aadMember;
@@ -142,12 +133,16 @@ export class FlattenedEncrypt {
142
133
  else {
143
134
  additionalData = protectedHeader;
144
135
  }
145
- const { ciphertext, tag } = await encrypt(enc, this._plaintext, cek, this._iv, additionalData);
136
+ const { ciphertext, tag, iv } = await encrypt(enc, this._plaintext, cek, this._iv, additionalData);
146
137
  const jwe = {
147
138
  ciphertext: base64url(ciphertext),
148
- iv: base64url(this._iv),
149
- tag: base64url(tag),
150
139
  };
140
+ if (iv) {
141
+ jwe.iv = base64url(iv);
142
+ }
143
+ if (tag) {
144
+ jwe.tag = base64url(tag);
145
+ }
151
146
  if (encryptedKey) {
152
147
  jwe.encrypted_key = base64url(encryptedKey);
153
148
  }
@@ -14,7 +14,7 @@ function getKtyFromAlg(alg) {
14
14
  throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
15
15
  }
16
16
  }
17
- export function isJWKSLike(jwks) {
17
+ function isJWKSLike(jwks) {
18
18
  return (jwks &&
19
19
  typeof jwks === 'object' &&
20
20
  Array.isArray(jwks.keys) &&
@@ -1,6 +1,6 @@
1
1
  import fetchJwks from '../runtime/fetch_jwks.js';
2
- import { JWKSInvalid, JWKSNoMatchingKey } from '../util/errors.js';
3
- import { isJWKSLike, LocalJWKSet } from './local.js';
2
+ import { JWKSNoMatchingKey } from '../util/errors.js';
3
+ import { createLocalJWKSet } from './local.js';
4
4
  function isCloudflareWorkers() {
5
5
  return (typeof WebSocketPair !== 'undefined' ||
6
6
  (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') ||
@@ -9,10 +9,10 @@ function isCloudflareWorkers() {
9
9
  let USER_AGENT;
10
10
  if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
11
11
  const NAME = 'jose';
12
- const VERSION = 'v5.2.2';
12
+ const VERSION = 'v5.2.4';
13
13
  USER_AGENT = `${NAME}/${VERSION}`;
14
14
  }
15
- class RemoteJWKSet extends LocalJWKSet {
15
+ class RemoteJWKSet {
16
16
  _url;
17
17
  _timeoutDuration;
18
18
  _cooldownDuration;
@@ -20,9 +20,8 @@ class RemoteJWKSet extends LocalJWKSet {
20
20
  _jwksTimestamp;
21
21
  _pendingFetch;
22
22
  _options;
23
+ _local;
23
24
  constructor(url, options) {
24
- super({ keys: [] });
25
- this._jwks = undefined;
26
25
  if (!(url instanceof URL)) {
27
26
  throw new TypeError('url must be an instance of URL');
28
27
  }
@@ -45,17 +44,17 @@ class RemoteJWKSet extends LocalJWKSet {
45
44
  : false;
46
45
  }
47
46
  async getKey(protectedHeader, token) {
48
- if (!this._jwks || !this.fresh()) {
47
+ if (!this._local || !this.fresh()) {
49
48
  await this.reload();
50
49
  }
51
50
  try {
52
- return await super.getKey(protectedHeader, token);
51
+ return await this._local(protectedHeader, token);
53
52
  }
54
53
  catch (err) {
55
54
  if (err instanceof JWKSNoMatchingKey) {
56
55
  if (this.coolingDown() === false) {
57
56
  await this.reload();
58
- return super.getKey(protectedHeader, token);
57
+ return this._local(protectedHeader, token);
59
58
  }
60
59
  }
61
60
  throw err;
@@ -72,10 +71,7 @@ class RemoteJWKSet extends LocalJWKSet {
72
71
  }
73
72
  this._pendingFetch ||= fetchJwks(this._url, this._timeoutDuration, this._options)
74
73
  .then((json) => {
75
- if (!isJWKSLike(json)) {
76
- throw new JWKSInvalid('JSON Web Key Set malformed');
77
- }
78
- this._jwks = { keys: json.keys };
74
+ this._local = createLocalJWKSet(json);
79
75
  this._jwksTimestamp = Date.now();
80
76
  this._pendingFetch = undefined;
81
77
  })
@@ -1,12 +1,14 @@
1
1
  import encrypt from '../runtime/encrypt.js';
2
2
  import decrypt from '../runtime/decrypt.js';
3
- import generateIv from './iv.js';
4
3
  import { encode as base64url } from '../runtime/base64url.js';
5
4
  export async function wrap(alg, key, cek, iv) {
6
5
  const jweAlgorithm = alg.slice(0, 7);
7
- iv ||= generateIv(jweAlgorithm);
8
- const { ciphertext: encryptedKey, tag } = await encrypt(jweAlgorithm, cek, key, iv, new Uint8Array(0));
9
- return { encryptedKey, iv: base64url(iv), tag: base64url(tag) };
6
+ const wrapped = await encrypt(jweAlgorithm, cek, key, iv, new Uint8Array(0));
7
+ return {
8
+ encryptedKey: wrapped.ciphertext,
9
+ iv: base64url(wrapped.iv),
10
+ tag: base64url(wrapped.tag),
11
+ };
10
12
  }
11
13
  export async function unwrap(alg, key, encryptedKey, iv, tag) {
12
14
  const jweAlgorithm = alg.slice(0, 7);
@@ -73,7 +73,7 @@ async function decryptKeyManagement(alg, key, encryptedKey, joseHeader, options)
73
73
  throw new JWEInvalid('JWE Encrypted Key missing');
74
74
  if (typeof joseHeader.p2c !== 'number')
75
75
  throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) missing or invalid`);
76
- const p2cLimit = options?.maxPBES2Count || 10000;
76
+ const p2cLimit = options?.maxPBES2Count || 10_000;
77
77
  if (joseHeader.p2c > p2cLimit)
78
78
  throw new JWEInvalid(`JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds`);
79
79
  if (typeof joseHeader.p2s !== 'string')
@@ -1,6 +1,6 @@
1
1
  import { JOSENotSupported } from '../util/errors.js';
2
2
  function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {
3
- if (joseHeader.crit !== undefined && protectedHeader.crit === undefined) {
3
+ if (joseHeader.crit !== undefined && protectedHeader?.crit === undefined) {
4
4
  throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected');
5
5
  }
6
6
  if (!protectedHeader || protectedHeader.crit === undefined) {
@@ -7,6 +7,7 @@ import { isCryptoKey } from './webcrypto.js';
7
7
  import { checkEncCryptoKey } from '../lib/crypto_key.js';
8
8
  import isKeyObject from './is_key_object.js';
9
9
  import invalidKeyInput from '../lib/invalid_key_input.js';
10
+ import generateIv from '../lib/iv.js';
10
11
  import { JOSENotSupported } from '../util/errors.js';
11
12
  import supported from './ciphers.js';
12
13
  import { types } from './is_key_like.js';
@@ -25,7 +26,7 @@ function cbcEncrypt(enc, plaintext, cek, iv, aad) {
25
26
  const ciphertext = concat(cipher.update(plaintext), cipher.final());
26
27
  const macSize = parseInt(enc.slice(-3), 10);
27
28
  const tag = cbcTag(aad, iv, ciphertext, macSize, macKey, keySize);
28
- return { ciphertext, tag };
29
+ return { ciphertext, tag, iv };
29
30
  }
30
31
  function gcmEncrypt(enc, plaintext, cek, iv, aad) {
31
32
  const keySize = parseInt(enc.slice(1, 4), 10);
@@ -40,7 +41,7 @@ function gcmEncrypt(enc, plaintext, cek, iv, aad) {
40
41
  const ciphertext = cipher.update(plaintext);
41
42
  cipher.final();
42
43
  const tag = cipher.getAuthTag();
43
- return { ciphertext, tag };
44
+ return { ciphertext, tag, iv };
44
45
  }
45
46
  const encrypt = (enc, plaintext, cek, iv, aad) => {
46
47
  let key;
@@ -55,7 +56,12 @@ const encrypt = (enc, plaintext, cek, iv, aad) => {
55
56
  throw new TypeError(invalidKeyInput(cek, ...types, 'Uint8Array'));
56
57
  }
57
58
  checkCekLength(enc, key);
58
- checkIvLength(enc, iv);
59
+ if (iv) {
60
+ checkIvLength(enc, iv);
61
+ }
62
+ else {
63
+ iv = generateIv(enc);
64
+ }
59
65
  switch (enc) {
60
66
  case 'A128CBC-HS256':
61
67
  case 'A192CBC-HS384':
@@ -1,7 +1,5 @@
1
1
  import type { KeyLike, JWSHeaderParameters, JSONWebKeySet, FlattenedJWSInput } from '../types';
2
2
  /** @private */
3
- export declare function isJWKSLike(jwks: unknown): jwks is JSONWebKeySet;
4
- /** @private */
5
3
  export declare class LocalJWKSet<KeyLikeType extends KeyLike = KeyLike> {
6
4
  protected _jwks?: JSONWebKeySet;
7
5
  private _cached;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jose",
3
- "version": "5.2.2",
3
+ "version": "5.2.4",
4
4
  "description": "JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes",
5
5
  "keywords": [
6
6
  "browser",
@@ -65,6 +65,7 @@
65
65
  "deno": "./dist/browser/index.js",
66
66
  "browser": "./dist/browser/index.js",
67
67
  "worker": "./dist/browser/index.js",
68
+ "workerd": "./dist/browser/index.js",
68
69
  "import": "./dist/node/esm/index.js",
69
70
  "require": "./dist/node/cjs/index.js"
70
71
  },
@@ -74,6 +75,7 @@
74
75
  "deno": "./dist/browser/util/errors.js",
75
76
  "browser": "./dist/browser/util/errors.js",
76
77
  "worker": "./dist/browser/util/errors.js",
78
+ "workerd": "./dist/browser/util/errors.js",
77
79
  "import": "./dist/node/esm/util/errors.js",
78
80
  "require": "./dist/node/cjs/util/errors.js"
79
81
  },
@@ -83,6 +85,7 @@
83
85
  "deno": "./dist/browser/util/base64url.js",
84
86
  "browser": "./dist/browser/util/base64url.js",
85
87
  "worker": "./dist/browser/util/base64url.js",
88
+ "workerd": "./dist/browser/util/base64url.js",
86
89
  "import": "./dist/node/esm/util/base64url.js",
87
90
  "require": "./dist/node/cjs/util/base64url.js"
88
91
  },