From b90ca07590d75bf5c6b004383e006d33fdb7cb89 Mon Sep 17 00:00:00 2001 From: Justin W Smith <103147162+justsmth@users.noreply.github.com> Date: Thu, 10 Aug 2023 15:20:34 -0400 Subject: [PATCH] Detect GCM-SIV alignment change (#1144) --- crypto/cipher_extra/aead_test.cc | 112 ++++++++++++++++++++++++++++++ crypto/cipher_extra/e_aesgcmsiv.c | 32 ++++++++- crypto/cipher_extra/internal.h | 4 +- include/openssl/aead.h | 1 + include/openssl/cipher.h | 1 + 5 files changed, 147 insertions(+), 3 deletions(-) diff --git a/crypto/cipher_extra/aead_test.cc b/crypto/cipher_extra/aead_test.cc index d63d68cb4a..abbccbde9c 100644 --- a/crypto/cipher_extra/aead_test.cc +++ b/crypto/cipher_extra/aead_test.cc @@ -25,6 +25,7 @@ #include "../fipsmodule/cipher/internal.h" #include "../internal.h" +#include "./internal.h" #include "../test/abi_test.h" #include "../test/file_test.h" #include "../test/test_util.h" @@ -1263,3 +1264,114 @@ TEST(AEADTest, AEADAES256GCMDetIVGen) { EXPECT_TRUE(EVP_AEAD_get_iv_from_ipv4_nanosecs(ip_address, fake_time, out)); EXPECT_EQ(Bytes(out, sizeof(out)), Bytes(expected, sizeof(expected))); } + +static int awslc_encrypt(EVP_AEAD_CTX *ctx, uint8_t *nonce, + uint8_t *ct, uint8_t *pt) { + size_t ct_len = 0; + GTEST_LOG_(INFO) << "awslc_encrypt: Ctx.State Location: " + << &ctx->state; + if (EVP_AEAD_CTX_seal(ctx, ct, &ct_len, 32, nonce, 12, pt, 16, NULL, 0) != + 1) { + return 1; + } + + return 0; +} + +static int awslc_decrypt(const EVP_AEAD *cipher, uint8_t ct[32], uint8_t *key, + size_t key_len, uint8_t nonce[12], uint8_t pt[16]) { + + EVP_AEAD_CTX ctx; + size_t pt_len = 0; + + EVP_AEAD_CTX_zero(&ctx); + if (EVP_AEAD_CTX_init(&ctx, cipher, key, key_len, 16, NULL) != 1) { + return 1; + } + GTEST_LOG_(INFO) << "awslc_decrypt: Ctx.State Location: " << &ctx.state; + + if (EVP_AEAD_CTX_open(&ctx, pt, &pt_len, 16, nonce, 12, ct, 32, NULL, 0) != + 1) { + return 1; + } + + return 0; +} + +TEST(AEADTest, TestGCMSIV128Change16Alignment) { + uint8_t key[16] = {0}; + uint8_t nonce[12] = {0}; + uint8_t pt[16] = {0}; + uint8_t ct[32] = {0}; + EVP_AEAD_CTX* encrypt_ctx_128 = (EVP_AEAD_CTX*)malloc(sizeof(EVP_AEAD_CTX) + 8); + + const EVP_AEAD *cipher_128 = EVP_aead_aes_128_gcm_siv(); + + EVP_AEAD_CTX_zero(encrypt_ctx_128); + ASSERT_TRUE(EVP_AEAD_CTX_init(encrypt_ctx_128, cipher_128, key, 16, 16, NULL)) + << ERR_error_string(ERR_get_error(), NULL); + ASSERT_FALSE(awslc_encrypt(encrypt_ctx_128, nonce, ct, pt)) + << ERR_error_string(ERR_get_error(), NULL); + ASSERT_FALSE(awslc_decrypt(cipher_128, ct, key, 16, nonce, pt)) + << ERR_error_string(ERR_get_error(), NULL); + + GTEST_LOG_(INFO) << "Orig. Ctx.State Location: " << &encrypt_ctx_128->state; + EVP_AEAD_CTX *moved_encrypt_ctx_128 = + (EVP_AEAD_CTX *)(((uint8_t *)encrypt_ctx_128) + 8); + memmove(moved_encrypt_ctx_128, encrypt_ctx_128, sizeof(EVP_AEAD_CTX)); + GTEST_LOG_(INFO) << "Moved Ctx.State Location: " + << &moved_encrypt_ctx_128->state; + + if (awslc_encrypt(moved_encrypt_ctx_128, nonce, ct, pt) != 1) { + if (x86_64_assembly_implementation_FOR_TESTING()) { + FAIL() << "Expected failure in awslc_encrypt"; + } + } else { + if (!x86_64_assembly_implementation_FOR_TESTING()) { + FAIL() << "Failure in awslc_encrypt"; + } + uint32_t err = ERR_get_error(); + EXPECT_EQ(ERR_R_CIPHER_LIB, ERR_GET_LIB(err)); + EXPECT_EQ(CIPHER_R_ALIGNMENT_CHANGED, ERR_GET_REASON(err)); + } + free(encrypt_ctx_128); +} + +TEST(AEADTest, TestGCMSIV256Change16Alignment) { + uint8_t nonce[12] = {0}; + uint8_t key[32] = {0}; + uint8_t pt[16] = {0}; + uint8_t ct[32] = {0}; + EVP_AEAD_CTX* encrypt_ctx_256 = (EVP_AEAD_CTX*)malloc(sizeof(EVP_AEAD_CTX) + 8); + + const EVP_AEAD *cipher_256 = EVP_aead_aes_256_gcm_siv(); + + EVP_AEAD_CTX_zero(encrypt_ctx_256); + ASSERT_TRUE(EVP_AEAD_CTX_init(encrypt_ctx_256, cipher_256, key, 32, 16, NULL)) + << ERR_error_string(ERR_get_error(), NULL); + ASSERT_FALSE(awslc_encrypt(encrypt_ctx_256, nonce, ct, pt)) + << ERR_error_string(ERR_get_error(), NULL); + ASSERT_FALSE(awslc_decrypt(cipher_256, ct, key, 32, nonce, pt)) + << ERR_error_string(ERR_get_error(), NULL); + + GTEST_LOG_(INFO) << "Orig. Ctx.State Location: " << &encrypt_ctx_256->state; + EVP_AEAD_CTX *moved_encrypt_ctx_256 = + (EVP_AEAD_CTX *)(((uint8_t *)encrypt_ctx_256) + 8); + memmove(moved_encrypt_ctx_256, encrypt_ctx_256, sizeof(EVP_AEAD_CTX)); + GTEST_LOG_(INFO) << "Moved Ctx.State Location: " + << &moved_encrypt_ctx_256->state; + + if (awslc_encrypt(moved_encrypt_ctx_256, nonce, ct, pt) != 1) { + if (x86_64_assembly_implementation_FOR_TESTING()) { + FAIL() << "Expected failure in awslc_encrypt"; + } + } else { + if (!x86_64_assembly_implementation_FOR_TESTING()) { + FAIL() << "Failure in awslc_encrypt"; + } + uint32_t err = ERR_get_error(); + EXPECT_EQ(ERR_R_CIPHER_LIB, ERR_GET_LIB(err)); + EXPECT_EQ(CIPHER_R_ALIGNMENT_CHANGED, ERR_GET_REASON(err)); + } + free(encrypt_ctx_256); +} diff --git a/crypto/cipher_extra/e_aesgcmsiv.c b/crypto/cipher_extra/e_aesgcmsiv.c index df72b0acaa..00d7972279 100644 --- a/crypto/cipher_extra/e_aesgcmsiv.c +++ b/crypto/cipher_extra/e_aesgcmsiv.c @@ -22,6 +22,7 @@ #include "../fipsmodule/cipher/internal.h" #include "../internal.h" +#include "./internal.h" #define EVP_AEAD_AES_GCM_SIV_NONCE_LEN 12 @@ -53,8 +54,11 @@ static struct aead_aes_gcm_siv_asm_ctx *asm_ctx_from_ctx( const EVP_AEAD_CTX *ctx) { // ctx->state must already be 8-byte aligned. Thus, at most, we may need to // add eight to align it to 16 bytes. - const uintptr_t offset = ((uintptr_t)&ctx->state) & 8; - return (struct aead_aes_gcm_siv_asm_ctx *)(&ctx->state.opaque[offset]); + const uintptr_t actual_offset = ((uintptr_t)&ctx->state) & 8; + if(ctx->state_offset != actual_offset) { + return NULL; + } + return (struct aead_aes_gcm_siv_asm_ctx *)(&ctx->state.opaque[actual_offset]); } // aes128gcmsiv_aes_ks writes an AES-128 key schedule for |key| to @@ -85,7 +89,12 @@ static int aead_aes_gcm_siv_asm_init(EVP_AEAD_CTX *ctx, const uint8_t *key, return 0; } + ctx->state_offset = ((uintptr_t)&ctx->state) & 8; struct aead_aes_gcm_siv_asm_ctx *gcm_siv_ctx = asm_ctx_from_ctx(ctx); + if(gcm_siv_ctx == NULL) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_INITIALIZATION_ERROR); + return 0; + } assert((((uintptr_t)gcm_siv_ctx) & 15) == 0); if (key_bits == 128) { @@ -332,6 +341,10 @@ static int aead_aes_gcm_siv_asm_seal_scatter( size_t nonce_len, const uint8_t *in, size_t in_len, const uint8_t *extra_in, size_t extra_in_len, const uint8_t *ad, size_t ad_len) { const struct aead_aes_gcm_siv_asm_ctx *gcm_siv_ctx = asm_ctx_from_ctx(ctx); + if(gcm_siv_ctx == NULL) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_ALIGNMENT_CHANGED); + return 0; + } const uint64_t in_len_64 = in_len; const uint64_t ad_len_64 = ad_len; @@ -419,6 +432,10 @@ static int aead_aes_gcm_siv_asm_open(const EVP_AEAD_CTX *ctx, uint8_t *out, } const struct aead_aes_gcm_siv_asm_ctx *gcm_siv_ctx = asm_ctx_from_ctx(ctx); + if(gcm_siv_ctx == NULL) { + OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_ALIGNMENT_CHANGED); + return 0; + } const size_t plaintext_len = in_len - EVP_AEAD_AES_GCM_SIV_TAG_LEN; const uint8_t *const given_tag = in + plaintext_len; @@ -849,10 +866,21 @@ const EVP_AEAD *EVP_aead_aes_256_gcm_siv(void) { return &aead_aes_256_gcm_siv; } +int x86_64_assembly_implementation_FOR_TESTING(void) { + if (CRYPTO_is_AVX_capable() && CRYPTO_is_AESNI_capable()) { + return 1; + } + return 0; +} + #else const EVP_AEAD *EVP_aead_aes_128_gcm_siv(void) { return &aead_aes_128_gcm_siv; } const EVP_AEAD *EVP_aead_aes_256_gcm_siv(void) { return &aead_aes_256_gcm_siv; } +int x86_64_assembly_implementation_FOR_TESTING(void) { + return 0; +} + #endif // AES_GCM_SIV_ASM diff --git a/crypto/cipher_extra/internal.h b/crypto/cipher_extra/internal.h index 2c9ecba22e..050afb8175 100644 --- a/crypto/cipher_extra/internal.h +++ b/crypto/cipher_extra/internal.h @@ -70,6 +70,8 @@ extern "C" { #endif +OPENSSL_EXPORT int x86_64_assembly_implementation_FOR_TESTING(void); + #if !defined(OPENSSL_NO_ASM) && defined(OPENSSL_X86_64) && \ !defined(MY_ASSEMBLER_IS_TOO_OLD_FOR_AVX) && !defined(AWSLC_FIPS) #define AES_CBC_HMAC_SHA_STITCH @@ -149,7 +151,7 @@ int EVP_tls_cbc_digest_record(const EVP_MD *md, uint8_t *md_out, const uint8_t *mac_secret, unsigned mac_secret_length); -// EVP_tls_cbc_digest_record_sha256 performs the same functionality of +// EVP_tls_cbc_digest_record_sha256 performs the same functionality of // EVP_tls_cbc_digest_record except it internally calls SHA256 instead of SHA1. int EVP_tls_cbc_digest_record_sha256(const EVP_MD *md, uint8_t *md_out, size_t *md_out_size, const uint8_t header[13], diff --git a/include/openssl/aead.h b/include/openssl/aead.h index 2ee26097f4..9d1e2edfa3 100644 --- a/include/openssl/aead.h +++ b/include/openssl/aead.h @@ -221,6 +221,7 @@ union evp_aead_ctx_st_state { struct evp_aead_ctx_st { const EVP_AEAD *aead; union evp_aead_ctx_st_state state; + uint8_t state_offset; // tag_len may contain the actual length of the authentication tag if it is // known at initialization time. uint8_t tag_len; diff --git a/include/openssl/cipher.h b/include/openssl/cipher.h index 143d8fffca..7b2c68e9e6 100644 --- a/include/openssl/cipher.h +++ b/include/openssl/cipher.h @@ -709,5 +709,6 @@ BSSL_NAMESPACE_END #define CIPHER_R_XTS_DATA_UNIT_IS_TOO_LARGE 139 #define CIPHER_R_CTRL_OPERATION_NOT_PERFORMED 140 #define CIPHER_R_SERIALIZATION_INVALID_EVP_AEAD_CTX 141 +#define CIPHER_R_ALIGNMENT_CHANGED 142 #endif // OPENSSL_HEADER_CIPHER_H