From 0f03194e621d621993cf9c992576e0b124c8703c Mon Sep 17 00:00:00 2001 From: Ashish Sharma Date: Tue, 16 Jun 2026 13:06:58 +0800 Subject: [PATCH] fix(mbedtls): harden port layer to zeroize sensitive material --- .../mbedtls/port/aes/dma/esp_aes_dma_core.c | 11 +++++ components/mbedtls/port/aes/esp_aes.c | 3 ++ components/mbedtls/port/aes/esp_aes_gcm.c | 48 ++++++++++++------- components/mbedtls/port/aes/esp_aes_xts.c | 15 ++++-- components/mbedtls/port/ecc/ecc_alt.c | 11 ++++- .../esp_aes/psa_crypto_driver_esp_aes.c | 5 ++ .../esp_mac/psa_crypto_driver_esp_cmac.c | 8 ++++ .../psa_crypto_driver_esp_hmac_opaque.c | 3 ++ .../esp_sha/core/psa_crypto_driver_esp_sha1.c | 4 ++ .../core/psa_crypto_driver_esp_sha256.c | 4 ++ .../core/psa_crypto_driver_esp_sha512.c | 8 ++++ 11 files changed, 97 insertions(+), 23 deletions(-) diff --git a/components/mbedtls/port/aes/dma/esp_aes_dma_core.c b/components/mbedtls/port/aes/dma/esp_aes_dma_core.c index 544bb3a4e58..bc2b2e586dc 100644 --- a/components/mbedtls/port/aes/dma/esp_aes_dma_core.c +++ b/components/mbedtls/port/aes/dma/esp_aes_dma_core.c @@ -1222,6 +1222,11 @@ cleanup: if (ret != 0) { mbedtls_platform_zeroize(output, len); } + /* s_stream_in/out are static DRAM buffers that held the trailing + * plaintext/ciphertext block for the DMA transfer. Scrub them so the block + * does not linger in fixed RAM between operations. */ + mbedtls_platform_zeroize(s_stream_in, AES_BLOCK_BYTES); + mbedtls_platform_zeroize(s_stream_out, AES_BLOCK_BYTES); free(block_desc); return ret; } @@ -1393,6 +1398,12 @@ cleanup: if (ret != 0) { mbedtls_platform_zeroize(output, len); } + /* stream_in/stream_out held the trailing plaintext/ciphertext block and + * stream_in_aad the trailing AAD block for the DMA transfer. Scrub them so + * the data does not linger in stack RAM. */ + mbedtls_platform_zeroize(stream_in, sizeof(stream_in)); + mbedtls_platform_zeroize(stream_out, sizeof(stream_out)); + mbedtls_platform_zeroize(stream_in_aad, sizeof(stream_in_aad)); free(block_desc); return ret; } diff --git a/components/mbedtls/port/aes/esp_aes.c b/components/mbedtls/port/aes/esp_aes.c index 9b73eac8bfe..39a803d08b4 100644 --- a/components/mbedtls/port/aes/esp_aes.c +++ b/components/mbedtls/port/aes/esp_aes.c @@ -424,6 +424,9 @@ int esp_aes_crypt_cfb8(esp_aes_context *ctx, } #endif /* !SOC_AES_SUPPORT_DMA || CONFIG_MBEDTLS_AES_HW_SMALL_DATA_LEN_OPTIM */ cleanup: + /* ov holds the CFB8 feedback register (key/IV-derived state). Scrub it + * before returning so it cannot be recovered from stack RAM. */ + mbedtls_platform_zeroize( ov, sizeof( ov ) ); esp_aes_release_hardware(); return ret; } diff --git a/components/mbedtls/port/aes/esp_aes_gcm.c b/components/mbedtls/port/aes/esp_aes_gcm.c index 4ffb820c430..e915259ea8f 100644 --- a/components/mbedtls/port/aes/esp_aes_gcm.c +++ b/components/mbedtls/port/aes/esp_aes_gcm.c @@ -494,22 +494,25 @@ int esp_aes_gcm_update( esp_gcm_context *ctx, /* Output = GCTR(J0, Input): Encrypt/Decrypt the input */ int ret = esp_aes_crypt_ctr(&ctx->aes_ctx, input_length, &nc_off, nonce_counter, stream, input, output); - if (ret != 0) { - return ret; + if (ret == 0) { + /* ICB gets auto incremented after GCTR operation here so update the context */ + memcpy(ctx->J0, nonce_counter, AES_BLOCK_BYTES); + + /* Keep updating the length counter for final tag calculation */ + ctx->data_len += input_length; + + /* Perform intermediate GHASH on "encrypted" data during encryption*/ + if (ctx->mode == ESP_AES_ENCRYPT) { + esp_gcm_ghash(ctx, output, input_length, ctx->ghash); + } } - /* ICB gets auto incremented after GCTR operation here so update the context */ - memcpy(ctx->J0, nonce_counter, AES_BLOCK_BYTES); - - /* Keep updating the length counter for final tag calculation */ - ctx->data_len += input_length; - - /* Perform intermediate GHASH on "encrypted" data during encryption*/ - if (ctx->mode == ESP_AES_ENCRYPT) { - esp_gcm_ghash(ctx, output, input_length, ctx->ghash); - } - - return 0; + /* stream holds the AES-CTR keystream and nonce_counter the live CTR state; + * both are key-derived secrets. Scrub them on every exit so they cannot be + * recovered from stack RAM. */ + mbedtls_platform_zeroize(stream, sizeof(stream)); + mbedtls_platform_zeroize(nonce_counter, sizeof(nonce_counter)); + return ret; } /* Function to read the tag value */ @@ -532,7 +535,13 @@ int esp_aes_gcm_finish( esp_gcm_context *ctx, esp_gcm_ghash(ctx, len_block, AES_BLOCK_BYTES, ctx->ghash); /* Tag T = GCTR(J0, ) where T is truncated to tag_len */ - return esp_aes_crypt_ctr(&ctx->aes_ctx, tag_len, &nc_off, ctx->ori_j0, stream, ctx->ghash, tag); + int ret = esp_aes_crypt_ctr(&ctx->aes_ctx, tag_len, &nc_off, ctx->ori_j0, stream, ctx->ghash, tag); + + /* stream holds the AES-CTR keystream used to encrypt the tag (key-derived); + * len_block holds the GHASH length block. Scrub both before returning. */ + mbedtls_platform_zeroize(stream, sizeof(stream)); + mbedtls_platform_zeroize(len_block, sizeof(len_block)); + return ret; } #if CONFIG_MBEDTLS_HARDWARE_GCM @@ -711,7 +720,7 @@ int esp_aes_gcm_auth_decrypt( esp_gcm_context *ctx, if ( ( ret = esp_aes_gcm_crypt_and_tag( ctx, ESP_AES_DECRYPT, length, iv, iv_len, aad, aad_len, input, output, tag_len, check_tag ) ) != 0 ) { - return ( ret ); + goto cleanup; } /* Check tag in "constant-time" */ @@ -721,8 +730,11 @@ int esp_aes_gcm_auth_decrypt( esp_gcm_context *ctx, if ( diff != 0 ) { mbedtls_platform_zeroize( output, length ); - return ( PSA_ERROR_INVALID_SIGNATURE ); + ret = PSA_ERROR_INVALID_SIGNATURE; } - return ( 0 ); +cleanup: + /* check_tag holds the locally recomputed authentication tag. */ + mbedtls_platform_zeroize( check_tag, sizeof(check_tag) ); + return ( ret ); } diff --git a/components/mbedtls/port/aes/esp_aes_xts.c b/components/mbedtls/port/aes/esp_aes_xts.c index b46b4317978..74871408a3b 100644 --- a/components/mbedtls/port/aes/esp_aes_xts.c +++ b/components/mbedtls/port/aes/esp_aes_xts.c @@ -37,6 +37,7 @@ #include #include "aes/esp_aes.h" #include "psa/crypto_values.h" +#include "mbedtls/platform_util.h" void esp_aes_xts_init( esp_aes_xts_context *ctx ) { @@ -205,7 +206,7 @@ int esp_aes_crypt_xts( esp_aes_xts_context *ctx, ret = esp_aes_crypt_ecb( &ctx->tweak, ESP_AES_ENCRYPT, data_unit, tweak ); if ( ret != 0 ) { - return ( ret ); + goto cleanup; } while ( blocks-- ) { @@ -227,7 +228,7 @@ int esp_aes_crypt_xts( esp_aes_xts_context *ctx, ret = esp_aes_crypt_ecb( &ctx->crypt, mode, tmp, tmp ); if ( ret != 0 ) { - return ( ret ); + goto cleanup; } for ( i = 0; i < 16; i++ ) { @@ -268,7 +269,7 @@ int esp_aes_crypt_xts( esp_aes_xts_context *ctx, ret = esp_aes_crypt_ecb( &ctx->crypt, mode, tmp, tmp ); if ( ret != 0 ) { - return ret; + goto cleanup; } /* Write the result back to the previous block, overriding the previous @@ -278,5 +279,11 @@ int esp_aes_crypt_xts( esp_aes_xts_context *ctx, } } - return ( 0 ); +cleanup: + /* tweak/prev_tweak are key-derived and tmp holds plaintext-derived data. + * Scrub all three on every exit so they cannot be recovered from stack RAM. */ + mbedtls_platform_zeroize( tweak, sizeof( tweak ) ); + mbedtls_platform_zeroize( prev_tweak, sizeof( prev_tweak ) ); + mbedtls_platform_zeroize( tmp, sizeof( tmp ) ); + return ( ret ); } diff --git a/components/mbedtls/port/ecc/ecc_alt.c b/components/mbedtls/port/ecc/ecc_alt.c index c12f60707b3..df5af8e6c43 100644 --- a/components/mbedtls/port/ecc/ecc_alt.c +++ b/components/mbedtls/port/ecc/ecc_alt.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -34,6 +34,9 @@ static int esp_mbedtls_ecp_point_multiply(const mbedtls_ecp_group *grp, mbedtls_ MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary_le(m, m_le, MAX_SIZE)); ret = esp_ecc_point_multiply(&p_pt, m_le, &r_pt, false); + if (ret != 0) { + goto cleanup; + } for (int i = 0; i < MAX_SIZE; i++) { x_tmp[MAX_SIZE - i - 1] = r_pt.x[i]; @@ -43,9 +46,15 @@ static int esp_mbedtls_ecp_point_multiply(const mbedtls_ecp_group *grp, mbedtls_ MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&R->MBEDTLS_PRIVATE(X), x_tmp, MAX_SIZE)); MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&R->MBEDTLS_PRIVATE(Y), y_tmp, MAX_SIZE)); MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&R->MBEDTLS_PRIVATE(Z), 1)); + /* m_le holds the secret scalar (EC private key / per-signature nonce) in + * little-endian form. Scrub it on the success path so it cannot be + * recovered from stack RAM after the operation completes. */ + mbedtls_platform_zeroize(m_le, sizeof(m_le)); return ret; cleanup: + /* Same scrub on every error path that reached here via MBEDTLS_MPI_CHK. */ + mbedtls_platform_zeroize(m_le, sizeof(m_le)); return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; } diff --git a/components/mbedtls/port/psa_driver/esp_aes/psa_crypto_driver_esp_aes.c b/components/mbedtls/port/psa_driver/esp_aes/psa_crypto_driver_esp_aes.c index fc191cdd3e5..38451370cb1 100644 --- a/components/mbedtls/port/psa_driver/esp_aes/psa_crypto_driver_esp_aes.c +++ b/components/mbedtls/port/psa_driver/esp_aes/psa_crypto_driver_esp_aes.c @@ -555,6 +555,11 @@ psa_status_t esp_aes_cipher_finish( exit: mbedtls_platform_zeroize(temp_output_buffer, sizeof(temp_output_buffer)); + /* finish() has consumed the buffered partial block; on the encrypt path + * unprocessed_data held plaintext. Scrub it (and the length) now instead of + * relying on a later abort, in case a fault skips the abort. */ + mbedtls_platform_zeroize(esp_aes_driver_ctx->unprocessed_data, sizeof(esp_aes_driver_ctx->unprocessed_data)); + esp_aes_driver_ctx->unprocessed_len = 0; return status; } diff --git a/components/mbedtls/port/psa_driver/esp_mac/psa_crypto_driver_esp_cmac.c b/components/mbedtls/port/psa_driver/esp_mac/psa_crypto_driver_esp_cmac.c index 5c0d38e16bf..e49a59455ac 100644 --- a/components/mbedtls/port/psa_driver/esp_mac/psa_crypto_driver_esp_cmac.c +++ b/components/mbedtls/port/psa_driver/esp_mac/psa_crypto_driver_esp_cmac.c @@ -207,6 +207,14 @@ psa_status_t esp_cmac_update(esp_cmac_operation_t *esp_cmac_ctx, const uint8_t * status = PSA_SUCCESS; exit: + if (status != PSA_SUCCESS) { + /* On failure the CBC-MAC chaining state and any buffered block are + * key-derived intermediates; scrub them now in case a fault skips the + * abort. On success they must persist for the next update/finish. */ + mbedtls_platform_zeroize(esp_cmac_ctx->state, sizeof(esp_cmac_ctx->state)); + mbedtls_platform_zeroize(esp_cmac_ctx->unprocessed_block, sizeof(esp_cmac_ctx->unprocessed_block)); + esp_cmac_ctx->unprocessed_len = 0; + } return status; } diff --git a/components/mbedtls/port/psa_driver/esp_mac/psa_crypto_driver_esp_hmac_opaque.c b/components/mbedtls/port/psa_driver/esp_mac/psa_crypto_driver_esp_hmac_opaque.c index 31851d1dad4..64769fb2783 100644 --- a/components/mbedtls/port/psa_driver/esp_mac/psa_crypto_driver_esp_hmac_opaque.c +++ b/components/mbedtls/port/psa_driver/esp_mac/psa_crypto_driver_esp_hmac_opaque.c @@ -398,6 +398,9 @@ psa_status_t esp_hmac_finish_opaque( } if (mac_size == 0 || mac_size > ESP_HMAC_RESULT_SIZE) { + /* A prior update may have left the full HMAC in ctx->hmac; scrub it + * before this error return in case a fault skips the later abort. */ + mbedtls_platform_zeroize(esp_hmac_ctx->hmac, sizeof(esp_hmac_ctx->hmac)); return PSA_ERROR_INVALID_ARGUMENT; } diff --git a/components/mbedtls/port/psa_driver/esp_sha/core/psa_crypto_driver_esp_sha1.c b/components/mbedtls/port/psa_driver/esp_sha/core/psa_crypto_driver_esp_sha1.c index b4193d122c2..9375d29c88a 100644 --- a/components/mbedtls/port/psa_driver/esp_sha/core/psa_crypto_driver_esp_sha1.c +++ b/components/mbedtls/port/psa_driver/esp_sha/core/psa_crypto_driver_esp_sha1.c @@ -295,6 +295,10 @@ int esp_sha1_update(esp_sha1_context *ctx, const unsigned char *input, size_t il int ret = esp_sha_dma(SHA1, input, len, ctx->buffer, local_len, ctx->first_block); if (ret != 0) { esp_sha_release_hardware(); + /* On HW failure the partial-block message bytes in ctx->buffer + * are no longer usable; scrub them so a fault that skips the + * later abort cannot leave secret input (e.g. an HMAC key) in RAM. */ + mbedtls_platform_zeroize(ctx->buffer, sizeof(ctx->buffer)); return ret; } } else diff --git a/components/mbedtls/port/psa_driver/esp_sha/core/psa_crypto_driver_esp_sha256.c b/components/mbedtls/port/psa_driver/esp_sha/core/psa_crypto_driver_esp_sha256.c index 078b54a2770..e393c62e50f 100644 --- a/components/mbedtls/port/psa_driver/esp_sha/core/psa_crypto_driver_esp_sha256.c +++ b/components/mbedtls/port/psa_driver/esp_sha/core/psa_crypto_driver_esp_sha256.c @@ -97,6 +97,10 @@ static int esp_sha256_update(esp_sha256_context *ctx, const unsigned char *input int ret = esp_sha_dma(ctx->mode, input, len, ctx->buffer, local_len, ctx->first_block); if (ret != 0) { esp_sha_release_hardware(); + /* On HW failure the partial-block message bytes in ctx->buffer + * are no longer usable; scrub them so a fault that skips the + * later abort cannot leave secret input (e.g. an HMAC key) in RAM. */ + mbedtls_platform_zeroize(ctx->buffer, sizeof(ctx->buffer)); return ret; } } else diff --git a/components/mbedtls/port/psa_driver/esp_sha/core/psa_crypto_driver_esp_sha512.c b/components/mbedtls/port/psa_driver/esp_sha/core/psa_crypto_driver_esp_sha512.c index c76af5b0eec..d707d26f2bd 100644 --- a/components/mbedtls/port/psa_driver/esp_sha/core/psa_crypto_driver_esp_sha512.c +++ b/components/mbedtls/port/psa_driver/esp_sha/core/psa_crypto_driver_esp_sha512.c @@ -122,6 +122,10 @@ static int esp_sha512_update(esp_sha512_context *ctx, const unsigned char *input if (ret != 0) { esp_sha_release_hardware(); + /* Operation failed and will be aborted; scrub the partial-block + * message bytes in ctx->buffer so a fault that skips the later + * abort cannot leave secret input (e.g. an HMAC key) in RAM. */ + mbedtls_platform_zeroize(ctx->buffer, sizeof(ctx->buffer)); return ret; } @@ -130,6 +134,10 @@ static int esp_sha512_update(esp_sha512_context *ctx, const unsigned char *input ret = esp_sha_dma(ctx->mode, input, len, ctx->buffer, local_len, ctx->first_block); if (ret != 0) { esp_sha_release_hardware(); + /* On HW failure scrub the partial-block message bytes in + * ctx->buffer so a fault that skips the later abort cannot + * leave secret input (e.g. an HMAC key) in RAM. */ + mbedtls_platform_zeroize(ctx->buffer, sizeof(ctx->buffer)); return ret; } } else