From 7fedaf79ef05a8d347031f44a5495847f4abfb21 Mon Sep 17 00:00:00 2001 From: Bob Thomas Date: Fri, 4 Mar 2022 13:59:46 -0500 Subject: [PATCH 1/3] Support decrypting empty ciphertext When encrypting an empty string/buffer in GCM mode, the resulting ciphertext is also empty. Trying to then decrypt this using compact decryption then throws an error because it converts the empty string to undefined and then throws an error. This is probably an edge case, but it is valid to encrypt and decrypt an empty value. This is also not an issue in CBC mode, since the ciphertext of an empty value is a non-empty value. --- src/jwe/compact/decrypt.ts | 2 +- test/jwe/compact.decrypt.test.mjs | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/jwe/compact/decrypt.ts b/src/jwe/compact/decrypt.ts index 3c29f3e4b6..fe41ca1641 100644 --- a/src/jwe/compact/decrypt.ts +++ b/src/jwe/compact/decrypt.ts @@ -77,7 +77,7 @@ export async function compactDecrypt( const decrypted = await flattenedDecrypt( { - ciphertext: (ciphertext || undefined), + ciphertext, iv: (iv || undefined), protected: protectedHeader || undefined, tag: (tag || undefined), diff --git a/test/jwe/compact.decrypt.test.mjs b/test/jwe/compact.decrypt.test.mjs index 4d8682f7d7..b61de81705 100644 --- a/test/jwe/compact.decrypt.test.mjs +++ b/test/jwe/compact.decrypt.test.mjs @@ -1,7 +1,7 @@ import test from 'ava' const root = !('WEBCRYPTO' in process.env) ? '#dist' : '#dist/webcrypto' -const { compactDecrypt } = await import(root) +const { CompactEncrypt, compactDecrypt } = await import(root) test('JWE format validation', async (t) => { await t.throwsAsync(compactDecrypt(null, new Uint8Array(0)), { @@ -13,3 +13,16 @@ test('JWE format validation', async (t) => { code: 'ERR_JWE_INVALID', }) }) + +test('decrypt empty data', async (t) => { + const jwe = await new CompactEncrypt(new Uint8Array(0)) + .setInitializationVector(new Uint8Array(12)) + .setProtectedHeader({ alg: 'dir', enc: 'A128GCM' }) + .encrypt(new Uint8Array(16)) + + const { 3: ciphertext } = jwe.split('.') + t.deepEqual(ciphertext, '') + + const { plaintext } = await compactDecrypt(jwe, new Uint8Array(16)) + t.deepEqual(plaintext.length, 0) +}) From 37745ebfbdcd87e6213aa20f8763199e6a7349cf Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 4 Mar 2022 21:59:20 +0100 Subject: [PATCH 2/3] fixup! Support decrypting empty ciphertext --- test/jwe/compact.decrypt.test.mjs | 5 ++--- test/jwe/flattened.decrypt.test.mjs | 11 +++++++++++ test/jwe/general.test.mjs | 12 ++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/test/jwe/compact.decrypt.test.mjs b/test/jwe/compact.decrypt.test.mjs index b61de81705..a6b17f876b 100644 --- a/test/jwe/compact.decrypt.test.mjs +++ b/test/jwe/compact.decrypt.test.mjs @@ -16,13 +16,12 @@ test('JWE format validation', async (t) => { test('decrypt empty data', async (t) => { const jwe = await new CompactEncrypt(new Uint8Array(0)) - .setInitializationVector(new Uint8Array(12)) .setProtectedHeader({ alg: 'dir', enc: 'A128GCM' }) .encrypt(new Uint8Array(16)) const { 3: ciphertext } = jwe.split('.') - t.deepEqual(ciphertext, '') + t.is(ciphertext, '') const { plaintext } = await compactDecrypt(jwe, new Uint8Array(16)) - t.deepEqual(plaintext.length, 0) + t.is(plaintext.byteLength, 0) }) diff --git a/test/jwe/flattened.decrypt.test.mjs b/test/jwe/flattened.decrypt.test.mjs index 34e3649ae3..7c34a7409c 100644 --- a/test/jwe/flattened.decrypt.test.mjs +++ b/test/jwe/flattened.decrypt.test.mjs @@ -217,3 +217,14 @@ test('AES CBC + HMAC', async (t) => { }) } }) + +test('decrypt empty data', async (t) => { + const jwe = await new FlattenedEncrypt(new Uint8Array(0)) + .setProtectedHeader({ alg: 'dir', enc: 'A128GCM' }) + .encrypt(new Uint8Array(16)) + + t.is(jwe.ciphertext, '') + + const { plaintext } = await flattenedDecrypt(jwe, new Uint8Array(16)) + t.is(plaintext.byteLength, 0) +}) diff --git a/test/jwe/general.test.mjs b/test/jwe/general.test.mjs index 7fd74e1672..1acea2d77a 100644 --- a/test/jwe/general.test.mjs +++ b/test/jwe/general.test.mjs @@ -170,3 +170,15 @@ test('General JWE format validation', async (t) => { await t.notThrowsAsync(generalDecrypt(jwe, t.context.secret)) } }) + +test('decrypt empty data', async (t) => { + const jwe = await new GeneralEncrypt(new Uint8Array(0)) + .setProtectedHeader({ alg: 'dir', enc: 'A128GCM' }) + .addRecipient(new Uint8Array(16)) + .encrypt() + + t.is(jwe.ciphertext, '') + + const { plaintext } = await generalDecrypt(jwe, new Uint8Array(16)) + t.is(plaintext.byteLength, 0) +}) From d7590327a33192b601344f3560f80a5893493455 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Fri, 4 Mar 2022 22:12:42 +0100 Subject: [PATCH 3/3] fixup! Support decrypting empty ciphertext --- test/jwe/compact.decrypt.test.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/jwe/compact.decrypt.test.mjs b/test/jwe/compact.decrypt.test.mjs index a6b17f876b..165d732867 100644 --- a/test/jwe/compact.decrypt.test.mjs +++ b/test/jwe/compact.decrypt.test.mjs @@ -19,8 +19,7 @@ test('decrypt empty data', async (t) => { .setProtectedHeader({ alg: 'dir', enc: 'A128GCM' }) .encrypt(new Uint8Array(16)) - const { 3: ciphertext } = jwe.split('.') - t.is(ciphertext, '') + t.is(jwe.split('.')[3], '') const { plaintext } = await compactDecrypt(jwe, new Uint8Array(16)) t.is(plaintext.byteLength, 0)