Test AEAD interface with aliased buffers.
Cases where the input and output buffers overlap are always a little
odd. This change adds a test to ensures that the (generic) AEADs
function in these situations.
Change-Id: I6f1987a5e10ddef6b2b8f037a6d50737a120bc99
Reviewed-on: https://boringssl-review.googlesource.com/7195
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/cipher/aead_test.cc b/crypto/cipher/aead_test.cc
index 79d7110..f21291e 100644
--- a/crypto/cipher/aead_test.cc
+++ b/crypto/cipher/aead_test.cc
@@ -192,37 +192,158 @@
return 1;
}
-struct AEADName {
+static bool TestWithAliasedBuffers(const EVP_AEAD *aead) {
+ const size_t key_len = EVP_AEAD_key_length(aead);
+ const size_t nonce_len = EVP_AEAD_nonce_length(aead);
+ const size_t max_overhead = EVP_AEAD_max_overhead(aead);
+
+ std::vector<uint8_t> key(key_len, 'a');
+ ScopedEVP_AEAD_CTX ctx;
+ if (!EVP_AEAD_CTX_init(ctx.get(), aead, key.data(), key_len,
+ EVP_AEAD_DEFAULT_TAG_LENGTH, nullptr)) {
+ return false;
+ }
+
+ static const uint8_t kPlaintext[260] =
+ "testing123456testing123456testing123456testing123456testing123456testing"
+ "123456testing123456testing123456testing123456testing123456testing123456t"
+ "esting123456testing123456testing123456testing123456testing123456testing1"
+ "23456testing123456testing123456testing12345";
+ const std::vector<size_t> offsets = {
+ 0, 1, 2, 8, 15, 16, 17, 31, 32, 33, 63,
+ 64, 65, 95, 96, 97, 127, 128, 129, 255, 256, 257,
+ };
+
+ std::vector<uint8_t> nonce(nonce_len, 'b');
+ std::vector<uint8_t> valid_encryption(sizeof(kPlaintext) + max_overhead);
+ size_t valid_encryption_len;
+ if (!EVP_AEAD_CTX_seal(
+ ctx.get(), valid_encryption.data(), &valid_encryption_len,
+ sizeof(kPlaintext) + max_overhead, nonce.data(), nonce_len,
+ kPlaintext, sizeof(kPlaintext), nullptr, 0)) {
+ fprintf(stderr, "EVP_AEAD_CTX_seal failed with disjoint buffers.\n");
+ return false;
+ }
+
+ // First test with out > in, which we expect to fail.
+ for (auto offset : offsets) {
+ if (offset == 0) {
+ // Will be tested in the next loop.
+ continue;
+ }
+
+ std::vector<uint8_t> buffer(offset + valid_encryption_len);
+ memcpy(buffer.data(), kPlaintext, sizeof(kPlaintext));
+ uint8_t *out = buffer.data() + offset;
+
+ size_t out_len;
+ if (!EVP_AEAD_CTX_seal(ctx.get(), out, &out_len,
+ sizeof(kPlaintext) + max_overhead, nonce.data(),
+ nonce_len, buffer.data(), sizeof(kPlaintext),
+ nullptr, 0)) {
+ // We expect offsets where the output is greater than the input to fail.
+ ERR_clear_error();
+ } else {
+ fprintf(stderr,
+ "EVP_AEAD_CTX_seal unexpectedly succeeded for offset %u.\n",
+ static_cast<unsigned>(offset));
+ return false;
+ }
+
+ memcpy(buffer.data(), valid_encryption.data(), valid_encryption_len);
+ if (!EVP_AEAD_CTX_open(ctx.get(), out, &out_len, valid_encryption_len,
+ nonce.data(), nonce_len, buffer.data(),
+ valid_encryption_len, nullptr, 0)) {
+ // We expect offsets where the output is greater than the input to fail.
+ ERR_clear_error();
+ } else {
+ fprintf(stderr,
+ "EVP_AEAD_CTX_open unexpectedly succeeded for offset %u.\n",
+ static_cast<unsigned>(offset));
+ ERR_print_errors_fp(stderr);
+ return false;
+ }
+ }
+
+ // Test with out <= in, which we expect to work.
+ for (auto offset : offsets) {
+ std::vector<uint8_t> buffer(offset + valid_encryption_len);
+ uint8_t *const out = buffer.data();
+ uint8_t *const in = buffer.data() + offset;
+ memcpy(in, kPlaintext, sizeof(kPlaintext));
+
+ size_t out_len;
+ if (!EVP_AEAD_CTX_seal(ctx.get(), out, &out_len,
+ sizeof(kPlaintext) + max_overhead, nonce.data(),
+ nonce_len, in, sizeof(kPlaintext), nullptr, 0)) {
+ fprintf(stderr, "EVP_AEAD_CTX_seal failed for offset -%u.\n",
+ static_cast<unsigned>(offset));
+ return false;
+ }
+
+ if (out_len != valid_encryption_len ||
+ memcmp(out, valid_encryption.data(), out_len) != 0) {
+ fprintf(stderr, "EVP_AEAD_CTX_seal produced bad output for offset -%u.\n",
+ static_cast<unsigned>(offset));
+ return false;
+ }
+
+ memcpy(in, valid_encryption.data(), valid_encryption_len);
+ if (!EVP_AEAD_CTX_open(ctx.get(), out, &out_len,
+ offset + valid_encryption_len, nonce.data(),
+ nonce_len, in, valid_encryption_len, nullptr, 0)) {
+ fprintf(stderr, "EVP_AEAD_CTX_open failed for offset -%u.\n",
+ static_cast<unsigned>(offset));
+ return false;
+ }
+
+ if (out_len != sizeof(kPlaintext) ||
+ memcmp(out, kPlaintext, out_len) != 0) {
+ fprintf(stderr, "EVP_AEAD_CTX_open produced bad output for offset -%u.\n",
+ static_cast<unsigned>(offset));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+struct KnownAEAD {
const char name[40];
const EVP_AEAD *(*func)(void);
+ // limited_implementation indicates that tests that assume a generic AEAD
+ // interface should not be performed. For example, the key-wrap AEADs only
+ // handle inputs that are a multiple of eight bytes in length and the
+ // SSLv3/TLS AEADs have the concept of “direction”.
+ bool limited_implementation;
};
-static const struct AEADName kAEADs[] = {
- { "aes-128-gcm", EVP_aead_aes_128_gcm },
- { "aes-256-gcm", EVP_aead_aes_256_gcm },
- { "chacha20-poly1305", EVP_aead_chacha20_poly1305 },
- { "chacha20-poly1305-old", EVP_aead_chacha20_poly1305_old },
- { "rc4-md5-tls", EVP_aead_rc4_md5_tls },
- { "rc4-sha1-tls", EVP_aead_rc4_sha1_tls },
- { "aes-128-cbc-sha1-tls", EVP_aead_aes_128_cbc_sha1_tls },
- { "aes-128-cbc-sha1-tls-implicit-iv", EVP_aead_aes_128_cbc_sha1_tls_implicit_iv },
- { "aes-128-cbc-sha256-tls", EVP_aead_aes_128_cbc_sha256_tls },
- { "aes-256-cbc-sha1-tls", EVP_aead_aes_256_cbc_sha1_tls },
- { "aes-256-cbc-sha1-tls-implicit-iv", EVP_aead_aes_256_cbc_sha1_tls_implicit_iv },
- { "aes-256-cbc-sha256-tls", EVP_aead_aes_256_cbc_sha256_tls },
- { "aes-256-cbc-sha384-tls", EVP_aead_aes_256_cbc_sha384_tls },
- { "des-ede3-cbc-sha1-tls", EVP_aead_des_ede3_cbc_sha1_tls },
- { "des-ede3-cbc-sha1-tls-implicit-iv", EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv },
- { "rc4-md5-ssl3", EVP_aead_rc4_md5_ssl3 },
- { "rc4-sha1-ssl3", EVP_aead_rc4_sha1_ssl3 },
- { "aes-128-cbc-sha1-ssl3", EVP_aead_aes_128_cbc_sha1_ssl3 },
- { "aes-256-cbc-sha1-ssl3", EVP_aead_aes_256_cbc_sha1_ssl3 },
- { "des-ede3-cbc-sha1-ssl3", EVP_aead_des_ede3_cbc_sha1_ssl3 },
- { "aes-128-key-wrap", EVP_aead_aes_128_key_wrap },
- { "aes-256-key-wrap", EVP_aead_aes_256_key_wrap },
- { "aes-128-ctr-hmac-sha256", EVP_aead_aes_128_ctr_hmac_sha256 },
- { "aes-256-ctr-hmac-sha256", EVP_aead_aes_256_ctr_hmac_sha256 },
- { "", NULL },
+static const struct KnownAEAD kAEADs[] = {
+ { "aes-128-gcm", EVP_aead_aes_128_gcm, false },
+ { "aes-256-gcm", EVP_aead_aes_256_gcm, false },
+ { "chacha20-poly1305", EVP_aead_chacha20_poly1305, false },
+ { "chacha20-poly1305-old", EVP_aead_chacha20_poly1305_old, false },
+ { "rc4-md5-tls", EVP_aead_rc4_md5_tls, true },
+ { "rc4-sha1-tls", EVP_aead_rc4_sha1_tls, true },
+ { "aes-128-cbc-sha1-tls", EVP_aead_aes_128_cbc_sha1_tls, true },
+ { "aes-128-cbc-sha1-tls-implicit-iv", EVP_aead_aes_128_cbc_sha1_tls_implicit_iv, true },
+ { "aes-128-cbc-sha256-tls", EVP_aead_aes_128_cbc_sha256_tls, true },
+ { "aes-256-cbc-sha1-tls", EVP_aead_aes_256_cbc_sha1_tls, true },
+ { "aes-256-cbc-sha1-tls-implicit-iv", EVP_aead_aes_256_cbc_sha1_tls_implicit_iv, true },
+ { "aes-256-cbc-sha256-tls", EVP_aead_aes_256_cbc_sha256_tls, true },
+ { "aes-256-cbc-sha384-tls", EVP_aead_aes_256_cbc_sha384_tls, true },
+ { "des-ede3-cbc-sha1-tls", EVP_aead_des_ede3_cbc_sha1_tls, true },
+ { "des-ede3-cbc-sha1-tls-implicit-iv", EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv, true },
+ { "rc4-md5-ssl3", EVP_aead_rc4_md5_ssl3, true },
+ { "rc4-sha1-ssl3", EVP_aead_rc4_sha1_ssl3, true },
+ { "aes-128-cbc-sha1-ssl3", EVP_aead_aes_128_cbc_sha1_ssl3, true },
+ { "aes-256-cbc-sha1-ssl3", EVP_aead_aes_256_cbc_sha1_ssl3, true },
+ { "des-ede3-cbc-sha1-ssl3", EVP_aead_des_ede3_cbc_sha1_ssl3, true },
+ { "aes-128-key-wrap", EVP_aead_aes_128_key_wrap, true },
+ { "aes-256-key-wrap", EVP_aead_aes_256_key_wrap, true },
+ { "aes-128-ctr-hmac-sha256", EVP_aead_aes_128_ctr_hmac_sha256, false },
+ { "aes-256-ctr-hmac-sha256", EVP_aead_aes_256_ctr_hmac_sha256, false },
+ { "", NULL, false },
};
int main(int argc, char **argv) {
@@ -233,22 +354,28 @@
return 1;
}
- const EVP_AEAD *aead;
+ const struct KnownAEAD *known_aead;
for (unsigned i = 0;; i++) {
- const struct AEADName &aead_name = kAEADs[i];
- if (aead_name.func == NULL) {
+ known_aead = &kAEADs[i];
+ if (known_aead->func == NULL) {
fprintf(stderr, "Unknown AEAD: %s\n", argv[1]);
return 2;
}
- if (strcmp(aead_name.name, argv[1]) == 0) {
- aead = aead_name.func();
+ if (strcmp(known_aead->name, argv[1]) == 0) {
break;
}
}
+ const EVP_AEAD *const aead = known_aead->func();
+
if (!TestCleanupAfterInitFailure(aead)) {
return 1;
}
+ if (!known_aead->limited_implementation && !TestWithAliasedBuffers(aead)) {
+ fprintf(stderr, "Aliased buffers test failed for %s.\n", known_aead->name);
+ return 1;
+ }
+
return FileTestMain(TestAEAD, const_cast<EVP_AEAD*>(aead), argv[2]);
}