Test AES mode wrappers.
AES_ctr128_encrypt, in particular, has a decent number of external
callers but is completely untested. I haven't included
AES_cfb128_encrypt because its EVP_CIPHER counterpart is tested in
decrept_test. But the EVP_CIPHER counterpart simply calls
AES_cfb128_encrypt, so it's tested transitively.
Change-Id: I0133dbd5b13c2b4045a89a04f29240008a279186
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/41425
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/cipher_extra/cipher_test.cc b/crypto/cipher_extra/cipher_test.cc
index 5ff308c..af7e0e7 100644
--- a/crypto/cipher_extra/cipher_test.cc
+++ b/crypto/cipher_extra/cipher_test.cc
@@ -61,8 +61,10 @@
#include <gtest/gtest.h>
+#include <openssl/aes.h>
#include <openssl/cipher.h>
#include <openssl/err.h>
+#include <openssl/nid.h>
#include <openssl/span.h>
#include "../test/file_test.h"
@@ -221,6 +223,91 @@
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag.size(), rtag));
EXPECT_EQ(Bytes(tag), Bytes(rtag, tag.size()));
}
+
+ // Additionally test low-level AES mode APIs. Skip runs where |copy| because
+ // it does not apply.
+ if (!copy) {
+ int nid = EVP_CIPHER_nid(cipher);
+ bool is_ctr = nid == NID_aes_128_ctr || nid == NID_aes_192_ctr ||
+ nid == NID_aes_256_ctr;
+ bool is_cbc = nid == NID_aes_128_cbc || nid == NID_aes_192_cbc ||
+ nid == NID_aes_256_cbc;
+ bool is_ofb = nid == NID_aes_128_ofb128 || nid == NID_aes_192_ofb128 ||
+ nid == NID_aes_256_ofb128;
+ if (is_ctr || is_cbc || is_ofb) {
+ AES_KEY aes;
+ if (encrypt || !is_cbc) {
+ ASSERT_EQ(0, AES_set_encrypt_key(key.data(), key.size() * 8, &aes));
+ } else {
+ ASSERT_EQ(0, AES_set_decrypt_key(key.data(), key.size() * 8, &aes));
+ }
+
+ // The low-level APIs all work in-place.
+ bssl::Span<const uint8_t> input = *in;
+ result.clear();
+ if (in_place) {
+ result = *in;
+ input = result;
+ } else {
+ result.resize(out->size());
+ }
+ bssl::Span<uint8_t> output = bssl::MakeSpan(result);
+ ASSERT_EQ(input.size(), output.size());
+
+ // The low-level APIs all use block-size IVs.
+ ASSERT_EQ(iv.size(), size_t{AES_BLOCK_SIZE});
+ uint8_t ivec[AES_BLOCK_SIZE];
+ OPENSSL_memcpy(ivec, iv.data(), iv.size());
+
+ if (is_ctr) {
+ unsigned num = 0;
+ uint8_t ecount_buf[AES_BLOCK_SIZE];
+ if (chunk_size == 0) {
+ AES_ctr128_encrypt(input.data(), output.data(), input.size(), &aes,
+ ivec, ecount_buf, &num);
+ } else {
+ do {
+ size_t todo = std::min(input.size(), chunk_size);
+ AES_ctr128_encrypt(input.data(), output.data(), todo, &aes, ivec,
+ ecount_buf, &num);
+ input = input.subspan(todo);
+ output = output.subspan(todo);
+ } while (!input.empty());
+ }
+ EXPECT_EQ(Bytes(*out), Bytes(result));
+ } else if (is_cbc && chunk_size % AES_BLOCK_SIZE == 0) {
+ // Note |AES_cbc_encrypt| requires block-aligned chunks.
+ if (chunk_size == 0) {
+ AES_cbc_encrypt(input.data(), output.data(), input.size(), &aes, ivec,
+ encrypt);
+ } else {
+ do {
+ size_t todo = std::min(input.size(), chunk_size);
+ AES_cbc_encrypt(input.data(), output.data(), todo, &aes, ivec,
+ encrypt);
+ input = input.subspan(todo);
+ output = output.subspan(todo);
+ } while (!input.empty());
+ }
+ EXPECT_EQ(Bytes(*out), Bytes(result));
+ } else if (is_ofb) {
+ int num = 0;
+ if (chunk_size == 0) {
+ AES_ofb128_encrypt(input.data(), output.data(), input.size(), &aes,
+ ivec, &num);
+ } else {
+ do {
+ size_t todo = std::min(input.size(), chunk_size);
+ AES_ofb128_encrypt(input.data(), output.data(), todo, &aes, ivec,
+ &num);
+ input = input.subspan(todo);
+ output = output.subspan(todo);
+ } while (!input.empty());
+ }
+ EXPECT_EQ(Bytes(*out), Bytes(result));
+ }
+ }
+ }
}
static void TestCipher(FileTest *t) {