Fix EVP_CIPHER_CTX_copy for AES-GCM.
7578f3f0dea091a3392f0be3216989bdc7355ad2 made it work, but
26ba48a6fbbf7b25bbfb521d3f5591e2d5a0b4bd regressed it by losing the
EVP_CIPH_CUSTOM_COPY flag. Additionally, we've since added an alignment
requirement to EVP_AES_GCM_CTX, which complicates things.
Thanks to Guido Vranken for catching this!
Bug: 270
Change-Id: I71784593dc5a34d1334c92a4daa93546ec0ee2c3
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/35624
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/cipher_extra/cipher_test.cc b/crypto/cipher_extra/cipher_test.cc
index 62ef939..0a96bf3 100644
--- a/crypto/cipher_extra/cipher_test.cc
+++ b/crypto/cipher_extra/cipher_test.cc
@@ -150,7 +150,8 @@
}
static void TestOperation(FileTest *t, const EVP_CIPHER *cipher, bool encrypt,
- size_t chunk_size, const std::vector<uint8_t> &key,
+ bool copy, size_t chunk_size,
+ const std::vector<uint8_t> &key,
const std::vector<uint8_t> &iv,
const std::vector<uint8_t> &plaintext,
const std::vector<uint8_t> &ciphertext,
@@ -167,45 +168,52 @@
bool is_aead = EVP_CIPHER_mode(cipher) == EVP_CIPH_GCM_MODE;
- bssl::ScopedEVP_CIPHER_CTX ctx;
- ASSERT_TRUE(EVP_CipherInit_ex(ctx.get(), cipher, nullptr, nullptr, nullptr,
- encrypt ? 1 : 0));
+ bssl::ScopedEVP_CIPHER_CTX ctx1;
+ ASSERT_TRUE(EVP_CipherInit_ex(ctx1.get(), cipher, nullptr, nullptr, nullptr,
+ encrypt ? 1 : 0));
if (t->HasAttribute("IV")) {
if (is_aead) {
- ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_IVLEN,
+ ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx1.get(), EVP_CTRL_AEAD_SET_IVLEN,
iv.size(), 0));
} else {
- ASSERT_EQ(iv.size(), EVP_CIPHER_CTX_iv_length(ctx.get()));
+ ASSERT_EQ(iv.size(), EVP_CIPHER_CTX_iv_length(ctx1.get()));
}
}
+
+ bssl::ScopedEVP_CIPHER_CTX ctx2;
+ EVP_CIPHER_CTX *ctx = ctx1.get();
+ if (copy) {
+ ASSERT_TRUE(EVP_CIPHER_CTX_copy(ctx2.get(), ctx1.get()));
+ ctx = ctx2.get();
+ }
+
if (is_aead && !encrypt) {
- ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_TAG,
- tag.size(),
+ ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag.size(),
const_cast<uint8_t *>(tag.data())));
}
// The ciphers are run with no padding. For each of the ciphers we test, the
// output size matches the input size.
ASSERT_EQ(in->size(), out->size());
- ASSERT_TRUE(EVP_CIPHER_CTX_set_key_length(ctx.get(), key.size()));
- ASSERT_TRUE(EVP_CipherInit_ex(ctx.get(), nullptr, nullptr, key.data(),
- iv.data(), -1));
+ ASSERT_TRUE(EVP_CIPHER_CTX_set_key_length(ctx, key.size()));
+ ASSERT_TRUE(
+ EVP_CipherInit_ex(ctx, nullptr, nullptr, key.data(), iv.data(), -1));
// Note: the deprecated |EVP_CIPHER|-based AEAD API is sensitive to whether
// parameters are NULL, so it is important to skip the |in| and |aad|
// |EVP_CipherUpdate| calls when empty.
if (!aad.empty()) {
int unused;
ASSERT_TRUE(
- EVP_CipherUpdate(ctx.get(), nullptr, &unused, aad.data(), aad.size()));
+ EVP_CipherUpdate(ctx, nullptr, &unused, aad.data(), aad.size()));
}
- ASSERT_TRUE(EVP_CIPHER_CTX_set_padding(ctx.get(), 0));
+ ASSERT_TRUE(EVP_CIPHER_CTX_set_padding(ctx, 0));
std::vector<uint8_t> result;
- ASSERT_TRUE(DoCipher(ctx.get(), &result, *in, chunk_size));
+ ASSERT_TRUE(DoCipher(ctx, &result, *in, chunk_size));
EXPECT_EQ(Bytes(*out), Bytes(result));
if (encrypt && is_aead) {
uint8_t rtag[16];
ASSERT_LE(tag.size(), sizeof(rtag));
- ASSERT_TRUE(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_TAG,
- tag.size(), rtag));
+ ASSERT_TRUE(
+ EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag.size(), rtag));
EXPECT_EQ(Bytes(tag), Bytes(rtag, tag.size()));
}
}
@@ -252,14 +260,18 @@
// By default, both directions are run, unless overridden by the operation.
if (operation != kDecrypt) {
SCOPED_TRACE("encrypt");
- TestOperation(t, cipher, true /* encrypt */, chunk_size, key, iv,
- plaintext, ciphertext, aad, tag);
+ TestOperation(t, cipher, true /* encrypt */, false /* no copy */,
+ chunk_size, key, iv, plaintext, ciphertext, aad, tag);
+ TestOperation(t, cipher, true /* encrypt */, true /* copy */, chunk_size,
+ key, iv, plaintext, ciphertext, aad, tag);
}
if (operation != kEncrypt) {
SCOPED_TRACE("decrypt");
- TestOperation(t, cipher, false /* decrypt */, chunk_size, key, iv,
- plaintext, ciphertext, aad, tag);
+ TestOperation(t, cipher, false /* decrypt */, false /* no copy */,
+ chunk_size, key, iv, plaintext, ciphertext, aad, tag);
+ TestOperation(t, cipher, false /* decrypt */, true /* copy */, chunk_size,
+ key, iv, plaintext, ciphertext, aad, tag);
}
}
}
diff --git a/crypto/fipsmodule/cipher/e_aes.c b/crypto/fipsmodule/cipher/e_aes.c
index dc94166..1ea012d 100644
--- a/crypto/fipsmodule/cipher/e_aes.c
+++ b/crypto/fipsmodule/cipher/e_aes.c
@@ -456,6 +456,9 @@
case EVP_CTRL_COPY: {
EVP_CIPHER_CTX *out = ptr;
EVP_AES_GCM_CTX *gctx_out = aes_gcm_from_cipher_ctx(out);
+ // |EVP_CIPHER_CTX_copy| copies this generically, but we must redo it in
+ // case |out->cipher_data| and |in->cipher_data| are differently aligned.
+ OPENSSL_memcpy(gctx_out, gctx, sizeof(EVP_AES_GCM_CTX));
if (gctx->iv == c->iv) {
gctx_out->iv = out->iv;
} else {
@@ -590,7 +593,7 @@
out->key_len = 16;
out->iv_len = 12;
out->ctx_size = sizeof(EVP_AES_GCM_CTX) + EVP_AES_GCM_CTX_PADDING;
- out->flags = EVP_CIPH_GCM_MODE | EVP_CIPH_CUSTOM_IV |
+ out->flags = EVP_CIPH_GCM_MODE | EVP_CIPH_CUSTOM_IV | EVP_CIPH_CUSTOM_COPY |
EVP_CIPH_FLAG_CUSTOM_CIPHER | EVP_CIPH_ALWAYS_CALL_INIT |
EVP_CIPH_CTRL_INIT | EVP_CIPH_FLAG_AEAD_CIPHER;
out->init = aes_gcm_init_key;
@@ -658,7 +661,7 @@
out->key_len = 24;
out->iv_len = 12;
out->ctx_size = sizeof(EVP_AES_GCM_CTX) + EVP_AES_GCM_CTX_PADDING;
- out->flags = EVP_CIPH_GCM_MODE | EVP_CIPH_CUSTOM_IV |
+ out->flags = EVP_CIPH_GCM_MODE | EVP_CIPH_CUSTOM_IV | EVP_CIPH_CUSTOM_COPY |
EVP_CIPH_FLAG_CUSTOM_CIPHER | EVP_CIPH_ALWAYS_CALL_INIT |
EVP_CIPH_CTRL_INIT | EVP_CIPH_FLAG_AEAD_CIPHER;
out->init = aes_gcm_init_key;
@@ -726,7 +729,7 @@
out->key_len = 32;
out->iv_len = 12;
out->ctx_size = sizeof(EVP_AES_GCM_CTX) + EVP_AES_GCM_CTX_PADDING;
- out->flags = EVP_CIPH_GCM_MODE | EVP_CIPH_CUSTOM_IV |
+ out->flags = EVP_CIPH_GCM_MODE | EVP_CIPH_CUSTOM_IV | EVP_CIPH_CUSTOM_COPY |
EVP_CIPH_FLAG_CUSTOM_CIPHER | EVP_CIPH_ALWAYS_CALL_INIT |
EVP_CIPH_CTRL_INIT | EVP_CIPH_FLAG_AEAD_CIPHER;
out->init = aes_gcm_init_key;