From c1512be6601a8b6e5a8193fbda9ecdf25349a1c2 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Wed, 31 Aug 2022 16:50:34 +0200 Subject: [PATCH] fix: limit default PBES2 alg's computational expense Also adds an option to opt-in to higher computation expense. --- docs/README.md | 4 ++++ lib/jwe/decrypt.js | 8 +++++++- test/jwe/sanity.test.js | 10 ++++++++++ types/index.d.ts | 1 + 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index ed1a78c11c..f1d68a3cd1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1490,6 +1490,10 @@ operation. - `keyManagementAlgorithms`: `string[]` Array of algorithms to accept as the `alg` (key management), when the JWE does not use an Key Management algorithm from this list the decryption will fail. **Default:** 'undefined' - accepts all algorithms available on the key or key store. + - `maxPBES2Count`: `number` (PBES2 Key Management Algorithms only) Maximum allowed "p2c" (PBES2 + Count) Header Parameter value. The PBKDF2 iteration count defines the algorithm's computational + expense. + **Default:** '10000' - `complete`: `` When true returns an object with the parsed headers, verified AAD, the content encryption key, the key that was used to unwrap or derive the content encryption key, and cleartext instead of only the cleartext. diff --git a/lib/jwe/decrypt.js b/lib/jwe/decrypt.js index fb5da68aaa..9da8aead45 100644 --- a/lib/jwe/decrypt.js +++ b/lib/jwe/decrypt.js @@ -52,7 +52,7 @@ const validateAlgorithms = (algorithms, option) => { /* * @public */ -const jweDecrypt = (skipValidateHeaders, serialization, jwe, key, { crit = [], complete = false, keyManagementAlgorithms, contentEncryptionAlgorithms } = {}) => { +const jweDecrypt = (skipValidateHeaders, serialization, jwe, key, { crit = [], complete = false, keyManagementAlgorithms, contentEncryptionAlgorithms, maxPBES2Count = 10000 } = {}) => { key = getKey(key, true) keyManagementAlgorithms = validateAlgorithms(keyManagementAlgorithms, 'keyManagementAlgorithms') @@ -142,6 +142,12 @@ const jweDecrypt = (skipValidateHeaders, serialization, jwe, key, { crit = [], c check(key, ...(alg === 'dir' ? ['decrypt', enc] : ['keyManagementDecrypt', alg])) + if (alg.startsWith('PBES2')) { + if (opts && opts.p2c > maxPBES2Count) { + throw new errors.JWEInvalid('JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds') + } + } + try { if (alg === 'dir') { cek = JWK.asKey(key, { alg: enc, use: 'enc' }) diff --git a/test/jwe/sanity.test.js b/test/jwe/sanity.test.js index edfa4bb945..fd8a083742 100644 --- a/test/jwe/sanity.test.js +++ b/test/jwe/sanity.test.js @@ -606,3 +606,13 @@ test('"enc" value must be supported error (when no alg was specified)', t => { JWE.encrypt('foo', k, { enc: 'foo' }) }, { instanceOf: errors.JOSENotSupported, message: 'unsupported encrypt alg: foo' }) }) + +if (!('electron' in process.versions)) { + test('decrypt PBES2 p2c limit', t => { + const k = generateSync('oct', 256) + const jwe = JWE.encrypt('foo', k, { alg: 'PBES2-HS256+A128KW' }) + t.throws(() => { + JWE.decrypt(jwe, k, { maxPBES2Count: 1000 }) + }, { instanceOf: errors.JWEInvalid, message: 'JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds' }) + }) +} diff --git a/types/index.d.ts b/types/index.d.ts index 9c4ff9af4d..30e146a019 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -401,6 +401,7 @@ export namespace JWE { crit?: string[]; contentEncryptionAlgorithms?: string[]; keyManagementAlgorithms?: string[]; + maxPBES2Count?: number; } interface completeDecrypt {