Reject invalid IV lengths in EVP_CTRL_GCM_SET_IV_INV While I'm here, move the test added in https://boringssl-review.googlesource.com/c/boringssl/+/95627 to a more appropriate test. CipherTest.SetIVLengthResets is about something else. Change-Id: Idfa75920464045a633ac6a5aef5946b35af910c1 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/96507 Auto-Submit: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com> Presubmit-BoringSSL-Verified: boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com> Commit-Queue: Adam Langley <agl@google.com>
diff --git a/crypto/cipher/cipher_test.cc b/crypto/cipher/cipher_test.cc index a732427..665f344 100644 --- a/crypto/cipher/cipher_test.cc +++ b/crypto/cipher/cipher_test.cc
@@ -1190,6 +1190,42 @@ memcpy(iv + sizeof(kFixedIV), counter2, sizeof(counter2)); ASSERT_NO_FATAL_FAILURE(expect_iv(ctx.get(), iv, /*enc=*/true)); } + + { + // If GCM IV length is less than 8, SET_IV_FIXED(-1) restores the whole IV, + // but a subsequent EVP_CTRL_GCM_IV_GEN call must fail rather than + // underflow. + const uint8_t kIV[7] = {1, 2, 3, 4, 5, 6, 7}; + ScopedEVP_CIPHER_CTX ctx; + ASSERT_TRUE(EVP_EncryptInit_ex(ctx.get(), kCipher, /*impl=*/nullptr, kKey, + /*iv=*/nullptr)); + ASSERT_TRUE( + EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_IVLEN, 7, nullptr)); + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_IV_FIXED, -1, + const_cast<uint8_t *>(kIV))); + uint8_t counter[8]; + EXPECT_FALSE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_IV_GEN, + sizeof(counter), counter)); + } + + { + // EVP_CTRL_GCM_SET_IV_INV should not overflow the IV. + const uint8_t kFixedIV[4] = {1, 2, 3, 4}; + const uint8_t kIVInvTooLarge[13] = {1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13}; + + ScopedEVP_CIPHER_CTX ctx; + ASSERT_TRUE(EVP_DecryptInit_ex(ctx.get(), kCipher, /*impl=*/nullptr, kKey, + /*iv=*/nullptr)); + ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_IV_FIXED, 4, + const_cast<uint8_t *>(kFixedIV))); + // Negative lengths are invalid. + EXPECT_FALSE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IV_INV, -1, + const_cast<uint8_t *>(kIVInvTooLarge))); + // Overflows the IV. + EXPECT_FALSE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IV_INV, 13, + const_cast<uint8_t *>(kIVInvTooLarge))); + } } TEST(CipherTest, SetIVLengthResets) { @@ -1247,22 +1283,6 @@ int out_len; EXPECT_TRUE(EVP_EncryptUpdate(ctx.get(), out, &out_len, in, sizeof(in))); } - - { - // If GCM IV length is less than 8, SET_IV_FIXED(-1) is allowed to succeed, - // but a subsequent EVP_CTRL_GCM_IV_GEN call must fail rather than - // underflow. - ScopedEVP_CIPHER_CTX ctx; - ASSERT_TRUE(EVP_EncryptInit_ex(ctx.get(), kCipher, /*impl=*/nullptr, kKey, - /*iv=*/nullptr)); - ASSERT_TRUE( - EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_IVLEN, 7, nullptr)); - ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_IV_FIXED, -1, - const_cast<uint8_t *>(kIV))); - uint8_t counter[8]; - EXPECT_FALSE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_IV_GEN, - sizeof(counter), counter)); - } } // EVP_CIPHER's buffer management for AES-GCM's variable-length IV is messy.
diff --git a/crypto/fipsmodule/cipher/e_aes.cc.inc b/crypto/fipsmodule/cipher/e_aes.cc.inc index bfdb647..83fa9ff 100644 --- a/crypto/fipsmodule/cipher/e_aes.cc.inc +++ b/crypto/fipsmodule/cipher/e_aes.cc.inc
@@ -323,8 +323,8 @@ gctx->iv_gen = 1; return 1; } - // Fixed field must be at least 4 bytes and invocation field - // at least 8. + // The fixed field must be at least 4 bytes and the invocation field at + // least 8. if (arg < 4 || (gctx->ivlen - arg) < 8) { return 0; } @@ -356,7 +356,8 @@ } case EVP_CTRL_GCM_SET_IV_INV: - if (gctx->iv_gen == 0 || gctx->key_set == 0 || c->encrypt) { + if (gctx->iv_gen == 0 || gctx->key_set == 0 || c->encrypt || arg < 0 || + arg > gctx->ivlen) { return 0; } OPENSSL_memcpy(gctx->iv + gctx->ivlen - arg, ptr, arg);