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]); }