fix(esp_security): Stop ECDSA and Key Manager resets from corrupting concurrent crypto

ECDSA enable pulses a reset that also holds SHA in reset, and SHA shares
its DMA with AES. Key Manager enable pulses a reset that also covers the
XTS-AES flash encryption key-usage selector. Neither path was serialized
against those victims, so a hardware ECDSA/HMAC/DS operation could
corrupt a concurrent SHA/AES transfer or an in-flight encrypted flash
read.

- Take the SHA/AES lock inside esp_crypto_ecdsa_lock_acquire(), before
  MPI, matching the DS lock order (sha_aes < mpi)
- Add esp_crypto_key_mgr_enable_periph_clk_no_reset() and switch ECDSA,
  HMAC and DS to it; they only need the key-usage selector writable
- Hold esp_crypto_key_manager_lock across those clock enable/disable
  pairs so selector writes stay serialized without resetting KM
This commit is contained in:
radek.tandler
2026-08-28 15:43:46 +02:00
committed by Harshal Patil
parent b10ae7f167
commit ce27a6e7e0
7 changed files with 90 additions and 13 deletions

View File

@@ -110,14 +110,16 @@ void esp_crypto_ecc_lock_release(void);
/**
* @brief Acquire lock for ECDSA cryptography peripheral
*
* Internally also locks the ECC and MPI peripheral, as the ECDSA depends on these peripherals
* Internally also locks the ECC and MPI peripheral, as the ECDSA depends on these peripherals,
* and the SHA/AES peripheral, because the ECDSA reset holds SHA in reset as well
*/
void esp_crypto_ecdsa_lock_acquire(void);
/**
* @brief Release lock for ECDSA cryptography peripheral
*
* Internally also releases the ECC and MPI peripheral, as the ECDSA depends on these peripherals
* Internally also releases the ECC and MPI peripheral, as the ECDSA depends on these peripherals,
* and the SHA/AES peripheral, because the ECDSA reset holds SHA in reset as well
*/
void esp_crypto_ecdsa_lock_release(void);
#endif /* SOC_ECDSA_SUPPORTED */
@@ -126,12 +128,16 @@ void esp_crypto_ecdsa_lock_release(void);
/**
* @brief Acquire lock for Key Manager peripheral
*
* Must be held across esp_crypto_key_mgr_enable_periph_clk(true/false): that
* helper pulses the Key Manager reset, which also covers the XTS-AES flash
* encryption key-usage selector on targets that deploy FE keys through KM.
*/
void esp_crypto_key_manager_lock_acquire(void);
/**
* @brief Release lock for Key Manager peripheral
*
* Must be released only after the matching esp_crypto_key_mgr_enable_periph_clk(false).
*/
void esp_crypto_key_manager_lock_release(void);
#endif /* SOC_KEY_MANAGER_SUPPORT_KEY_DEPLOYMENT */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -63,10 +63,30 @@ void esp_crypto_ecdsa_enable_periph_clk(bool enable);
/**
* @brief Enable or disable the Key Manager peripheral clock
*
* When enable is true this also pulses the Key Manager reset. The caller must
* hold esp_crypto_key_manager_lock across the matching true/false pair, because
* that reset also covers the XTS-AES flash encryption key-usage selector.
*
* Prefer esp_crypto_key_mgr_enable_periph_clk_no_reset() when the caller only
* needs the key-usage selector writable (ECDSA/HMAC/DS).
*
* @param enable true: enable; false: disable
*/
void esp_crypto_key_mgr_enable_periph_clk(bool enable);
/**
* @brief Enable or disable the Key Manager clocks without resetting the peripheral
*
* Use this when a crypto accelerator only needs to write its own key-usage
* selector. Resetting would drop the XTS-AES flash encryption selector that
* MSPI may be using, and flash DMA does not take the Key Manager lock.
* The caller must still hold esp_crypto_key_manager_lock across the matching
* true/false pair to serialize selector writes.
*
* @param enable true: enable; false: disable
*/
void esp_crypto_key_mgr_enable_periph_clk_no_reset(bool enable);
#ifdef __cplusplus
}
#endif

View File

@@ -15,7 +15,9 @@ MPI/RSA: independent
ECC: independent
HMAC: needs SHA
DS: needs HMAC (which needs SHA), AES and MPI
ECDSA: needs ECC and MPI
ECDSA: needs ECC and MPI, and its reset pulse holds SHA (and thus the SHA/AES DMA) in reset
Key Manager: shared key-usage selectors (ECDSA/HMAC/DS/XTS-AES flash);
esp_crypto_key_mgr_enable_periph_clk(true) resets it
*/
#if !NON_OS_BUILD
@@ -140,6 +142,19 @@ void esp_crypto_ecdsa_lock_acquire(void)
{
_lock_acquire(&s_crypto_ecdsa_lock);
esp_crypto_ecc_lock_acquire();
#if defined(SOC_SHA_SUPPORTED) || defined(SOC_AES_SUPPORTED)
/* Enabling the ECDSA peripheral pulses the ECDSA reset
(esp_crypto_ecdsa_enable_periph_clk() -> ecdsa_ll_reset_register()), and on every
target that has an ECDSA peripheral that reset also holds SHA in reset: see the
"otherwise SHA is held in reset" note in sha_ll_reset_register(). SHA shares its
(G)DMA channel with AES, and the SHA/AES lock is what serializes both of them, so
it has to be held across the pulse. Without it, a hardware ECDSA operation on one
core lands in the middle of an unrelated SHA or AES transfer on the other core,
which completes without an error but yields wrong output.
Taken before the MPI lock to keep the acquisition order of
esp_crypto_ds_lock_acquire() (SHA/AES before MPI) and avoid a lock cycle. */
esp_crypto_sha_aes_lock_acquire();
#endif /* defined(SOC_SHA_SUPPORTED) || defined(SOC_AES_SUPPORTED) */
#ifdef SOC_ECDSA_USES_MPI
if (ecdsa_ll_is_mpi_required()) {
esp_crypto_mpi_lock_acquire();
@@ -154,6 +169,9 @@ void esp_crypto_ecdsa_lock_release(void)
esp_crypto_mpi_lock_release();
}
#endif /* SOC_ECDSA_USES_MPI */
#if defined(SOC_SHA_SUPPORTED) || defined(SOC_AES_SUPPORTED)
esp_crypto_sha_aes_lock_release();
#endif /* defined(SOC_SHA_SUPPORTED) || defined(SOC_AES_SUPPORTED) */
esp_crypto_ecc_lock_release();
_lock_release(&s_crypto_ecdsa_lock);
}

View File

@@ -155,16 +155,29 @@ void esp_crypto_ecdsa_enable_periph_clk(bool enable)
#endif
#if SOC_KEY_MANAGER_SUPPORT_KEY_DEPLOYMENT
void esp_crypto_key_mgr_enable_periph_clk(bool enable)
static void key_mgr_configure_periph_clk(bool enable, bool reset)
{
KEY_MANAGER_RCC_ATOMIC() {
esp_crypto_common_clk_enable(enable);
key_mgr_ll_power_up();
key_mgr_ll_enable_bus_clock(enable);
key_mgr_ll_enable_peripheral_clock(enable);
if (enable) {
if (enable && reset) {
key_mgr_ll_reset_register();
}
}
}
void esp_crypto_key_mgr_enable_periph_clk(bool enable)
{
/* Caller must hold esp_crypto_key_manager_lock: this reset also covers
the XTS-AES flash encryption key-usage selector. */
key_mgr_configure_periph_clk(enable, enable);
}
void esp_crypto_key_mgr_enable_periph_clk_no_reset(bool enable)
{
/* Caller must hold esp_crypto_key_manager_lock to serialize selector writes. */
key_mgr_configure_periph_clk(enable, false);
}
#endif

View File

@@ -269,15 +269,21 @@ static void ds_acquire_enable(void)
/* Key Manager holds the key usage selector register(efuse vs own key).
Thus, we need to enable the Key Manager peripheral clock to ensure
that the key usage selector register is properly set.
Taken after the DS lock (SHA/AES + MPI) so the order matches HMAC/ECDSA:
sha_aes < mpi < key_manager.
*/
esp_crypto_key_mgr_enable_periph_clk(true);
esp_crypto_key_manager_lock_acquire();
/* Clock only: a full KM reset would drop the XTS-AES flash encryption
key-usage selector, and spi_flash DMA does not take the KM lock. */
esp_crypto_key_mgr_enable_periph_clk_no_reset(true);
#endif /* SOC_KEY_MANAGER_DS_KEY_DEPLOY */
}
static void ds_disable_release(void)
{
#if SOC_KEY_MANAGER_DS_KEY_DEPLOY
esp_crypto_key_mgr_enable_periph_clk(false);
esp_crypto_key_mgr_enable_periph_clk_no_reset(false);
esp_crypto_key_manager_lock_release();
#endif /* SOC_KEY_MANAGER_DS_KEY_DEPLOY */
esp_crypto_ds_enable_periph_clk(false);

View File

@@ -79,8 +79,14 @@ esp_err_t esp_hmac_calculate(hmac_key_id_t key_id,
/* Key Manager holds the key usage selector register(efuse vs own key).
Thus, we need to enable the Key Manager peripheral clock to ensure
that the key usage selector register is properly set.
Taken after the HMAC lock (SHA/AES) so the order matches ECDSA/DS:
sha_aes < mpi < key_manager. Do not take it earlier: ECDSA already
holds MPI before KM, and reversing that here would deadlock.
*/
esp_crypto_key_mgr_enable_periph_clk(true);
esp_crypto_key_manager_lock_acquire();
/* Clock only: a full KM reset would drop the XTS-AES flash encryption
key-usage selector, and spi_flash DMA does not take the KM lock. */
esp_crypto_key_mgr_enable_periph_clk_no_reset(true);
#endif /* SOC_KEY_MANAGER_HMAC_KEY_DEPLOY */
hmac_hal_start();
@@ -90,7 +96,8 @@ esp_err_t esp_hmac_calculate(hmac_key_id_t key_id,
esp_crypto_sha_enable_periph_clk(false);
esp_crypto_hmac_enable_periph_clk(false);
#if SOC_KEY_MANAGER_HMAC_KEY_DEPLOY
esp_crypto_key_mgr_enable_periph_clk(false);
esp_crypto_key_mgr_enable_periph_clk_no_reset(false);
esp_crypto_key_manager_lock_release();
#endif // SOC_KEY_MANAGER_HMAC_KEY_DEPLOY
esp_crypto_hmac_lock_release();
return ESP_FAIL;
@@ -149,7 +156,8 @@ esp_err_t esp_hmac_calculate(hmac_key_id_t key_id,
hmac_hal_read_result_256(hmac);
#if SOC_KEY_MANAGER_HMAC_KEY_DEPLOY
esp_crypto_key_mgr_enable_periph_clk(false);
esp_crypto_key_mgr_enable_periph_clk_no_reset(false);
esp_crypto_key_manager_lock_release();
#endif /* SOC_KEY_MANAGER_HMAC_KEY_DEPLOY */
esp_crypto_sha_enable_periph_clk(false);

View File

@@ -393,8 +393,13 @@ static void esp_ecdsa_acquire_hardware(void)
/* Key Manager holds the key usage selector register (efuse vs own key).
Thus, we need to enable the Key Manager peripheral clock to ensure
that the key usage selector register is properly set.
Taken after the ECDSA lock (which already holds SHA/AES and MPI) so
the order matches HMAC/DS: sha_aes < mpi < key_manager.
*/
esp_crypto_key_mgr_enable_periph_clk(true);
esp_crypto_key_manager_lock_acquire();
/* Clock only: a full KM reset would drop the XTS-AES flash encryption
key-usage selector, and spi_flash DMA does not take the KM lock. */
esp_crypto_key_mgr_enable_periph_clk_no_reset(true);
#endif /* SOC_KEY_MANAGER_ECDSA_KEY_DEPLOY */
#if SOC_ECDSA_USES_MPI
@@ -414,7 +419,8 @@ static void esp_ecdsa_release_hardware(void)
esp_crypto_ecc_enable_periph_clk(false);
#if SOC_KEY_MANAGER_ECDSA_KEY_DEPLOY
esp_crypto_key_mgr_enable_periph_clk(false);
esp_crypto_key_mgr_enable_periph_clk_no_reset(false);
esp_crypto_key_manager_lock_release();
#endif /* SOC_KEY_MANAGER_ECDSA_KEY_DEPLOY */
#if SOC_ECDSA_USES_MPI