Fix EVP_CIPHER_CTX_copy for AES-GCM.
7578f3f0demade it work, but26ba48a6fbregressed 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>
This commit is contained in:
committed by
Adam Langley
parent
4a8c05ffe8
commit
65dc45cb57
@@ -150,7 +150,8 @@ static bool DoCipher(EVP_CIPHER_CTX *ctx, std::vector<uint8_t> *out,
|
||||
}
|
||||
|
||||
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 @@ static void TestOperation(FileTest *t, const EVP_CIPHER *cipher, bool encrypt,
|
||||
|
||||
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 @@ static void TestCipher(FileTest *t) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,6 +456,9 @@ static int aes_gcm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr) {
|
||||
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 @@ DEFINE_LOCAL_DATA(EVP_CIPHER, aes_128_gcm_generic) {
|
||||
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 @@ DEFINE_LOCAL_DATA(EVP_CIPHER, aes_192_gcm_generic) {
|
||||
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 @@ DEFINE_LOCAL_DATA(EVP_CIPHER, aes_256_gcm_generic) {
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user