fix(mbedtls): harden port layer to zeroize sensitive material

This commit is contained in:
Ashish Sharma
2026-07-17 17:14:10 +08:00
parent 9e9c16c724
commit 4173ed018a
11 changed files with 97 additions and 23 deletions
@@ -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;
}
+3
View File
@@ -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;
}
+30 -18
View File
@@ -502,22 +502,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 */
@@ -544,7 +547,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
@@ -736,7 +745,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" */
@@ -746,8 +755,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 );
}
+11 -4
View File
@@ -37,6 +37,7 @@
#include <string.h>
#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 );
}
+10 -1
View File
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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
@@ -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
@@ -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