From efa02ffa2a1b5ff76c9b6ba440e69b68b01f8d7f Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Sun, 30 Jan 2022 18:42:29 +0530 Subject: [PATCH] fix(ext/crypto): enforce 128bits tagLength for AES-GCM decryption (#13536) --- cli/tests/unit/webcrypto_test.ts | 29 +++++++++++++++++++++++++++++ ext/crypto/decrypt.rs | 9 +++++++++ 2 files changed, 38 insertions(+) diff --git a/cli/tests/unit/webcrypto_test.ts b/cli/tests/unit/webcrypto_test.ts index 84cf7d4ca0..ba6aaa3279 100644 --- a/cli/tests/unit/webcrypto_test.ts +++ b/cli/tests/unit/webcrypto_test.ts @@ -1639,3 +1639,32 @@ Deno.test(async function testAESWrapKey() { assertEquals(new Uint8Array(hmacKeyBytes), new Uint8Array(unwrappedKeyBytes)); }); + +// https://github.com/denoland/deno/issues/13534 +Deno.test(async function testAesGcmTagLength() { + const key = await crypto.subtle.importKey( + "raw", + new Uint8Array(32), + "AES-GCM", + false, + ["encrypt", "decrypt"], + ); + + const iv = crypto.getRandomValues(new Uint8Array(12)); + + // encrypt won't fail, it will simply truncate the tag + // as expected. + const encrypted = await crypto.subtle.encrypt( + { name: "AES-GCM", iv, tagLength: 96, additionalData: new Uint8Array() }, + key, + new Uint8Array(32), + ); + + await assertRejects(async () => { + await crypto.subtle.decrypt( + { name: "AES-GCM", iv, tagLength: 96, additionalData: new Uint8Array() }, + key, + encrypted, + ); + }); +}); diff --git a/ext/crypto/decrypt.rs b/ext/crypto/decrypt.rs index 9f11576080..40dd3a5b52 100644 --- a/ext/crypto/decrypt.rs +++ b/ext/crypto/decrypt.rs @@ -295,10 +295,19 @@ fn decrypt_aes_gcm( return Err(type_error("iv length not equal to 12")); } + // The `aes_gcm` crate only supports 128 bits tag length. + // + // Note that encryption won't fail, it instead truncates the tag + // to the specified tag length as specified in the spec. + if tag_length != 128 { + return Err(type_error("tag length not equal to 128")); + } + let nonce = Nonce::from_slice(&iv); let sep = data.len() - (tag_length / 8); let tag = &data[sep..]; + // The actual ciphertext, called plaintext because it is reused in place. let mut plaintext = data[..sep].to_vec(); match length {