From e2da031381b7c5327ea9a0ccf58f059fa8af7e92 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Wed, 18 Oct 2023 11:37:31 +0200 Subject: [PATCH] refactor: PBES2 Algorithms require explicit opt-in during verification BREAKING CHANGE: PBES2 Key Management Algorithms' use in decrypt functions now requires the use of the keyManagementAlgorithms option to explicitly opt-in for their use. --- src/jwe/flattened/decrypt.ts | 5 ++++- src/types.d.ts | 6 +++++- tap/cookbook.ts | 15 ++++++++++++--- tap/encrypt.ts | 10 ++++++++-- test/jwe/flattened.decrypt.test.mjs | 23 ++++++++++++++++++++--- 5 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/jwe/flattened/decrypt.ts b/src/jwe/flattened/decrypt.ts index 0b262e4163..3456e2ee1a 100644 --- a/src/jwe/flattened/decrypt.ts +++ b/src/jwe/flattened/decrypt.ts @@ -162,7 +162,10 @@ export async function flattenedDecrypt( options && validateAlgorithms('contentEncryptionAlgorithms', options.contentEncryptionAlgorithms) - if (keyManagementAlgorithms && !keyManagementAlgorithms.has(alg)) { + if ( + (keyManagementAlgorithms && !keyManagementAlgorithms.has(alg)) || + (!keyManagementAlgorithms && alg.startsWith('PBES2')) + ) { throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed') } diff --git a/src/types.d.ts b/src/types.d.ts index 4e348953c9..449df91c40 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -406,7 +406,11 @@ export interface CritOption { /** JWE Decryption options. */ export interface DecryptOptions extends CritOption { - /** A list of accepted JWE "alg" (Algorithm) Header Parameter values. */ + /** + * A list of accepted JWE "alg" (Algorithm) Header Parameter values. By default all "alg" + * (Algorithm) Header Parameter values applicable for the used key/secret are allowed except for + * all PBES2 Key Management Algorithms, these need to be explicitly allowed using this option. + */ keyManagementAlgorithms?: string[] /** diff --git a/tap/cookbook.ts b/tap/cookbook.ts index 6094e69a82..d7d226d4b8 100644 --- a/tap/cookbook.ts +++ b/tap/cookbook.ts @@ -272,7 +272,10 @@ export default (QUnit: QUnit, lib: typeof jose) => { } const result = await encrypt.encrypt(publicKey) - await flattened.decrypt(result, privateKey) + await flattened.decrypt(result, privateKey, { + keyManagementAlgorithms: [vector.input.alg], + contentEncryptionAlgorithms: [vector.input.enc], + }) } const privateKey = await lib.importJWK( @@ -280,10 +283,16 @@ export default (QUnit: QUnit, lib: typeof jose) => { dir ? vector.input.enc : vector.input.alg, ) if (vector.output.json_flat) { - await flattened.decrypt(vector.output.json_flat, privateKey) + await flattened.decrypt(vector.output.json_flat, privateKey, { + keyManagementAlgorithms: [vector.input.alg], + contentEncryptionAlgorithms: [vector.input.enc], + }) } if (vector.output.compact) { - await compact.decrypt(vector.output.compact, privateKey) + await compact.decrypt(vector.output.compact, privateKey, { + keyManagementAlgorithms: [vector.input.alg], + contentEncryptionAlgorithms: [vector.input.enc], + }) } t.ok(1) } diff --git a/tap/encrypt.ts b/tap/encrypt.ts index 8d8d1a5914..310c823706 100644 --- a/tap/encrypt.ts +++ b/tap/encrypt.ts @@ -25,7 +25,10 @@ export async function jwe( .setAdditionalAuthenticatedData(aad) .encrypt(eKey) - const decrypted = await lib.flattenedDecrypt(jwe, dKey) + const decrypted = await lib.flattenedDecrypt(jwe, dKey, { + keyManagementAlgorithms: [alg], + contentEncryptionAlgorithms: [enc], + }) t.deepEqual([...decrypted.plaintext], [...cleartext]) t.deepEqual([...decrypted.additionalAuthenticatedData!], [...aad]) } @@ -44,7 +47,10 @@ export async function jwt( .setProtectedHeader({ alg, enc }) .encrypt(eKey) - const decrypted = await lib.jwtDecrypt(jwt, dKey) + const decrypted = await lib.jwtDecrypt(jwt, dKey, { + keyManagementAlgorithms: [alg], + contentEncryptionAlgorithms: [enc], + }) t.propContains(decrypted, { payload: { foo: 'bar', diff --git a/test/jwe/flattened.decrypt.test.mjs b/test/jwe/flattened.decrypt.test.mjs index 8b0fb2dcbe..88b392a793 100644 --- a/test/jwe/flattened.decrypt.test.mjs +++ b/test/jwe/flattened.decrypt.test.mjs @@ -223,8 +223,25 @@ test('decrypt PBES2 p2c limit', async (t) => { .setKeyManagementParameters({ p2c: 2049 }) .encrypt(new Uint8Array(32)) - await t.throwsAsync(flattenedDecrypt(jwe, new Uint8Array(32), { maxPBES2Count: 2048 }), { - message: 'JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds', - code: 'ERR_JWE_INVALID', + await t.throwsAsync( + flattenedDecrypt(jwe, new Uint8Array(32), { + maxPBES2Count: 2048, + keyManagementAlgorithms: ['PBES2-HS256+A128KW'], + }), + { + message: 'JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds', + code: 'ERR_JWE_INVALID', + }, + ) +}) + +test('decrypt with PBES2 is not allowed by default', async (t) => { + const jwe = await new FlattenedEncrypt(new Uint8Array(0)) + .setProtectedHeader({ alg: 'PBES2-HS256+A128KW', enc: 'A128CBC-HS256' }) + .encrypt(new Uint8Array(32)) + + await t.throwsAsync(flattenedDecrypt(jwe, new Uint8Array(32)), { + message: '"alg" (Algorithm) Header Parameter value not allowed', + code: 'ERR_JOSE_ALG_NOT_ALLOWED', }) })