From c15266340859923636d7d5fab6fbbd84e328ef20 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Wed, 30 Jul 2025 15:17:18 +0530 Subject: [PATCH] feat(esp_tee): Added support for PBKDF2-based (HMAC) ECDSA signing --- components/esp_tee/Kconfig.projbuild | 9 +- .../scripts/esp32c5/sec_srv_tbl_default.yml | 4 + .../scripts/esp32c6/sec_srv_tbl_default.yml | 4 + .../scripts/esp32h2/sec_srv_tbl_default.yml | 4 + .../components/tee_sec_storage/CMakeLists.txt | 2 +- .../include/esp_tee_sec_storage.h | 26 +++ .../tee_sec_storage/tee_sec_storage.c | 188 +++++++++++++++--- .../tee_sec_storage/tee_sec_storage_wrapper.c | 5 + .../main/core/esp_secure_services.c | 4 + .../main/core/esp_secure_services_iram.c | 20 ++ .../esp_tee/test_apps/tee_cli_app/README.md | 2 +- .../tee_test_fw/main/test_esp_tee_sec_stg.c | 1 - components/mbedtls/CMakeLists.txt | 4 + .../mbedtls/esp_tee/esp_tee_mbedtls.cmake | 3 + components/mbedtls/port/esp_hmac_pbkdf2.c | 82 ++++++++ .../mbedtls/port/include/esp_hmac_pbkdf2.h | 41 ++++ docs/en/security/tee/tee-sec-storage.rst | 7 + .../security/tee/tee_secure_storage/README.md | 2 +- 18 files changed, 375 insertions(+), 33 deletions(-) create mode 100644 components/mbedtls/port/esp_hmac_pbkdf2.c create mode 100644 components/mbedtls/port/include/esp_hmac_pbkdf2.h diff --git a/components/esp_tee/Kconfig.projbuild b/components/esp_tee/Kconfig.projbuild index fae6c8f0c8..e52ae6aacc 100644 --- a/components/esp_tee/Kconfig.projbuild +++ b/components/esp_tee/Kconfig.projbuild @@ -81,13 +81,20 @@ menu "ESP-TEE (Trusted Execution Environment)" endchoice config SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID - int "Secure Storage: eFuse HMAC key ID" + int "Secure Storage: eFuse HMAC key ID for storage encryption keys" depends on SECURE_TEE_SEC_STG_MODE_RELEASE range -1 5 default -1 help eFuse block key ID storing the HMAC key for deriving the TEE secure storage encryption keys + config SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID + int "Secure Storage: eFuse HMAC key ID for PBKDF2 key derivation" + range -1 5 + default -1 + help + eFuse block key ID storing the HMAC key for deriving PBKDF2-based ECDSA keys + config SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN bool "Secure Storage: Support signing with the ECDSA SECP192R1 curve" default n diff --git a/components/esp_tee/scripts/esp32c5/sec_srv_tbl_default.yml b/components/esp_tee/scripts/esp32c5/sec_srv_tbl_default.yml index 963bb6ef69..112204f08a 100644 --- a/components/esp_tee/scripts/esp32c5/sec_srv_tbl_default.yml +++ b/components/esp_tee/scripts/esp32c5/sec_srv_tbl_default.yml @@ -314,6 +314,10 @@ secure_services: type: custom function: esp_tee_sec_storage_aead_decrypt args: 4 + - id: 181 + type: custom + function: esp_tee_sec_storage_ecdsa_sign_pbkdf2 + args: 5 # ID: 195-199 (5) - OTA - family: ota entries: diff --git a/components/esp_tee/scripts/esp32c6/sec_srv_tbl_default.yml b/components/esp_tee/scripts/esp32c6/sec_srv_tbl_default.yml index ec1bd6654c..8c6a4ef7b5 100644 --- a/components/esp_tee/scripts/esp32c6/sec_srv_tbl_default.yml +++ b/components/esp_tee/scripts/esp32c6/sec_srv_tbl_default.yml @@ -278,6 +278,10 @@ secure_services: type: custom function: esp_tee_sec_storage_aead_decrypt args: 4 + - id: 181 + type: custom + function: esp_tee_sec_storage_ecdsa_sign_pbkdf2 + args: 5 # ID: 195-199 (5) - OTA - family: ota entries: diff --git a/components/esp_tee/scripts/esp32h2/sec_srv_tbl_default.yml b/components/esp_tee/scripts/esp32h2/sec_srv_tbl_default.yml index dc8221367b..39e1563fc0 100644 --- a/components/esp_tee/scripts/esp32h2/sec_srv_tbl_default.yml +++ b/components/esp_tee/scripts/esp32h2/sec_srv_tbl_default.yml @@ -282,6 +282,10 @@ secure_services: type: custom function: esp_tee_sec_storage_aead_decrypt args: 4 + - id: 181 + type: custom + function: esp_tee_sec_storage_ecdsa_sign_pbkdf2 + args: 5 # ID: 195-199 (5) - OTA - family: ota entries: diff --git a/components/esp_tee/subproject/components/tee_sec_storage/CMakeLists.txt b/components/esp_tee/subproject/components/tee_sec_storage/CMakeLists.txt index c93459e419..f59829cdaf 100644 --- a/components/esp_tee/subproject/components/tee_sec_storage/CMakeLists.txt +++ b/components/esp_tee/subproject/components/tee_sec_storage/CMakeLists.txt @@ -5,7 +5,7 @@ set(priv_requires esp_tee) if(esp_tee_build) list(APPEND srcs "tee_sec_storage.c") - list(APPEND priv_requires efuse esp_partition log mbedtls nvs_flash spi_flash tee_flash_mgr) + list(APPEND priv_requires efuse esp_partition esp_security log mbedtls nvs_flash spi_flash tee_flash_mgr) else() if(CONFIG_SECURE_ENABLE_TEE) list(APPEND srcs "tee_sec_storage_wrapper.c") diff --git a/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h b/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h index 97ae8ae432..379ecc8653 100644 --- a/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h +++ b/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h @@ -55,6 +55,16 @@ typedef struct { size_t input_len; /*!< Length of input data */ } esp_tee_sec_storage_aead_ctx_t; +/** + * @brief Context structure for ECDSA signing with PBKDF2 (HMAC) derived key + * + */ +typedef struct { + const uint8_t *salt; /*!< Salt for PBKDF2 */ + size_t salt_len; /*!< Length of the salt */ + esp_tee_sec_storage_type_t key_type; /*!< Key type to be generated and used */ +} esp_tee_sec_storage_pbkdf2_ctx_t; + /** * @brief Structure holding the X and Y components of the ECDSA public key * @@ -149,6 +159,22 @@ esp_err_t esp_tee_sec_storage_aead_encrypt(const esp_tee_sec_storage_aead_ctx_t */ esp_err_t esp_tee_sec_storage_aead_decrypt(const esp_tee_sec_storage_aead_ctx_t *ctx, const uint8_t *tag, size_t tag_len, uint8_t *output); +/** + * @brief Generate and return the signature for the specified message digest using + * the key pair derived via PBKDF2-HMAC-SHA256 using the HMAC peripheral + * with the given salt and the configured HMAC eFuse key ID + * (`CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID`). + * + * @param[in] ctx Pointer to the PBKDF2 context + * @param[in] hash Message digest + * @param[in] hlen Digest length + * @param[out] out_sign Output context holding the signature + * @param[out] out_pubkey Output context holding the public key + * + * @return esp_err_t ESP_OK on success, appropriate error code otherwise. + */ +esp_err_t esp_tee_sec_storage_ecdsa_sign_pbkdf2(const esp_tee_sec_storage_pbkdf2_ctx_t *ctx, const uint8_t *hash, size_t hlen, esp_tee_sec_storage_ecdsa_sign_t *out_sign, esp_tee_sec_storage_ecdsa_pubkey_t *out_pubkey); + #ifdef __cplusplus } #endif diff --git a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c index dcb3a8236e..790dcf916a 100644 --- a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c +++ b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c @@ -11,6 +11,8 @@ #include "esp_fault.h" #include "esp_flash.h" #include "esp_efuse.h" +#include "esp_efuse_chip.h" +#include "esp_hmac.h" #include "esp_random.h" #include "spi_flash_mmap.h" @@ -19,11 +21,8 @@ #include "mbedtls/sha256.h" #include "mbedtls/ecdsa.h" #include "mbedtls/error.h" +#include "esp_hmac_pbkdf2.h" -#include "rom/efuse.h" -#if SOC_HMAC_SUPPORTED -#include "rom/hmac.h" -#endif #include "esp_rom_sys.h" #include "nvs.h" #include "nvs_flash.h" @@ -40,9 +39,13 @@ #define ECDSA_SECP256R1_KEY_LEN 32 #define ECDSA_SECP192R1_KEY_LEN 24 +#define SHA256_DIGEST_SZ 32 + #define EKEY_SEED 0xAEBE5A5A #define TKEY_SEED 0xCEDEA5A5 +#define PBKDF2_HMAC_ITER 2048 + /* Structure to hold ECDSA SECP256R1 key pair */ typedef struct { uint8_t priv_key[ECDSA_SECP256R1_KEY_LEN]; /* Private key for ECDSA SECP256R1 */ @@ -84,6 +87,10 @@ static const char *TAG = "secure_storage"; #error "TEE Secure Storage: Configured eFuse block (CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID) out of range!" #endif +#if CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID == CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID +#error "TEE Secure Storage: Configured eFuse block for storage encryption keys (CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID) and PBKDF2 key derivation (CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID) cannot be the same!" +#endif + static int buffer_hexdump(const char *label, const void *buffer, size_t length) { #if CONFIG_SECURE_TEE_LOG_LEVEL >= 4 @@ -130,24 +137,21 @@ static int rand_func(void *rng_state, unsigned char *output, size_t len) } #if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE -static esp_err_t compute_nvs_keys_with_hmac(ets_efuse_block_t hmac_key, nvs_sec_cfg_t *cfg) +static esp_err_t compute_nvs_keys_with_hmac(hmac_key_id_t hmac_key_id, nvs_sec_cfg_t *cfg) { uint32_t ekey_seed[8] = {[0 ... 7] = EKEY_SEED}; uint32_t tkey_seed[8] = {[0 ... 7] = TKEY_SEED}; memset(cfg, 0x00, sizeof(nvs_sec_cfg_t)); - int ret = -1; - ets_hmac_enable(); - ret = ets_hmac_calculate_message(hmac_key, ekey_seed, sizeof(ekey_seed), (uint8_t *)cfg->eky); - ret = ets_hmac_calculate_message(hmac_key, tkey_seed, sizeof(tkey_seed), (uint8_t *)cfg->tky); - ets_hmac_disable(); - - if (ret != 0) { + esp_err_t err = ESP_FAIL; + err = esp_hmac_calculate(hmac_key_id, ekey_seed, sizeof(ekey_seed), (uint8_t *)cfg->eky); + err |= esp_hmac_calculate(hmac_key_id, tkey_seed, sizeof(tkey_seed), (uint8_t *)cfg->tky); + if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to calculate seed HMAC"); return ESP_FAIL; } - ESP_FAULT_ASSERT(ret == 0); + ESP_FAULT_ASSERT(err == ESP_OK); /* NOTE: If the XTS E-key and T-key are the same, we have a hash collision */ ESP_FAULT_ASSERT(memcmp(cfg->eky, cfg->tky, NVS_KEY_SIZE) != 0); @@ -163,14 +167,14 @@ static esp_err_t read_security_cfg_hmac(nvs_sec_cfg_t *cfg) } #if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE - ets_efuse_block_t hmac_key = (ets_efuse_block_t)(ETS_EFUSE_BLOCK_KEY0 + CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID); - ets_efuse_purpose_t hmac_efuse_blk_purpose = ets_efuse_get_key_purpose(hmac_key); - if (hmac_efuse_blk_purpose != ETS_EFUSE_KEY_PURPOSE_HMAC_UP) { + esp_efuse_block_t hmac_key_blk = (esp_efuse_block_t)(EFUSE_BLK_KEY0 + (esp_efuse_block_t)CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID); + esp_efuse_purpose_t hmac_efuse_blk_purpose = esp_efuse_get_key_purpose(hmac_key_blk); + if (hmac_efuse_blk_purpose != ESP_EFUSE_KEY_PURPOSE_HMAC_UP) { ESP_LOGE(TAG, "HMAC key is not burnt in eFuse block"); return ESP_ERR_NOT_FOUND; } - esp_err_t err = compute_nvs_keys_with_hmac(hmac_key, cfg); + esp_err_t err = compute_nvs_keys_with_hmac((hmac_key_id_t)CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID, cfg); if (err != ESP_OK) { return err; } @@ -304,11 +308,6 @@ static int generate_ecdsa_key(sec_stg_key_t *keyctx, esp_tee_sec_storage_type_t NULL; #endif - ret = mbedtls_mpi_write_binary(&ctxECDSA.MBEDTLS_PRIVATE(d), priv_key, key_len); - if (ret != 0) { - goto exit; - } - ret = mbedtls_mpi_write_binary(&(ctxECDSA.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X)), pub_key, key_len); if (ret != 0) { goto exit; @@ -319,6 +318,11 @@ static int generate_ecdsa_key(sec_stg_key_t *keyctx, esp_tee_sec_storage_type_t goto exit; } + ret = mbedtls_mpi_write_binary(&ctxECDSA.MBEDTLS_PRIVATE(d), priv_key, key_len); + if (ret != 0) { + goto exit; + } + buffer_hexdump("Private key", priv_key, key_len); buffer_hexdump("Public key", pub_key, key_len * 2); @@ -457,18 +461,17 @@ esp_err_t esp_tee_sec_storage_ecdsa_sign(const esp_tee_sec_storage_key_cfg_t *cf } memset(out_sign, 0x00, sizeof(esp_tee_sec_storage_ecdsa_sign_t)); - ret = mbedtls_mpi_write_binary(&r, out_sign->sign_r, key_len); + if (ret == 0) { + ret = mbedtls_mpi_write_binary(&s, out_sign->sign_s, key_len); + } + if (ret != 0) { + memset(out_sign, 0x00, sizeof(esp_tee_sec_storage_ecdsa_sign_t)); err = ESP_FAIL; goto exit; } - ret = mbedtls_mpi_write_binary(&s, out_sign->sign_s, key_len); - if (ret != 0) { - err = ESP_FAIL; - goto exit; - } err = ESP_OK; exit: @@ -606,3 +609,132 @@ esp_err_t esp_tee_sec_storage_aead_decrypt(const esp_tee_sec_storage_aead_ctx_t { return tee_sec_storage_crypt_common(ctx->key_id, ctx->input, ctx->input_len, ctx->aad, ctx->aad_len, (uint8_t *)tag, tag_len, output, false); } + +esp_err_t esp_tee_sec_storage_ecdsa_sign_pbkdf2(const esp_tee_sec_storage_pbkdf2_ctx_t *ctx, + const uint8_t *hash, size_t hlen, + esp_tee_sec_storage_ecdsa_sign_t *out_sign, + esp_tee_sec_storage_ecdsa_pubkey_t *out_pubkey) +{ + if (!ctx || !hash || !out_sign || !out_pubkey || !ctx->salt || ctx->salt_len == 0) { + return ESP_ERR_INVALID_ARG; + } + + if (hlen != SHA256_DIGEST_SZ) { + return ESP_ERR_INVALID_SIZE; + } + + esp_err_t err = ESP_FAIL; + size_t key_len; + mbedtls_ecp_group_id curve_id = MBEDTLS_ECP_DP_NONE; + + switch (ctx->key_type) { + case ESP_SEC_STG_KEY_ECDSA_SECP256R1: + key_len = ECDSA_SECP256R1_KEY_LEN; + curve_id = MBEDTLS_ECP_DP_SECP256R1; + break; +#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN + case ESP_SEC_STG_KEY_ECDSA_SECP192R1: + key_len = ECDSA_SECP192R1_KEY_LEN; + curve_id = MBEDTLS_ECP_DP_SECP192R1; + break; +#endif + default: + ESP_LOGE(TAG, "Unsupported key type"); + return ESP_ERR_INVALID_ARG; + } + + hmac_key_id_t key_id = (hmac_key_id_t)(CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID); + esp_efuse_block_t blk = EFUSE_BLK_KEY0 + (esp_efuse_block_t)(key_id); + if (esp_efuse_get_key_purpose(blk) != ESP_EFUSE_KEY_PURPOSE_HMAC_UP) { + ESP_LOGE(TAG, "HMAC key is not burnt in the specified eFuse block ID"); + return ESP_ERR_NOT_FOUND; + } + + uint8_t *derived_key = calloc(1, key_len); + if (!derived_key) { + return ESP_ERR_NO_MEM; + } + + err = esp_hmac_derive_pbkdf2_key(key_id, ctx->salt, ctx->salt_len, PBKDF2_HMAC_ITER, key_len, derived_key); + if (err != ESP_OK) { + goto exit; + } + + mbedtls_ecp_keypair keypair; + mbedtls_mpi r, s; + + mbedtls_ecp_keypair_init(&keypair); + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + + int ret = -1; + + ret = mbedtls_ecp_group_load(&keypair.MBEDTLS_PRIVATE(grp), curve_id); + if (ret != 0) { + err = ESP_FAIL; + goto exit; + } + + ret = mbedtls_mpi_read_binary(&keypair.MBEDTLS_PRIVATE(d), derived_key, key_len); + if (ret != 0) { + err = ESP_FAIL; + goto exit; + } + + ret = mbedtls_ecp_check_privkey(&keypair.MBEDTLS_PRIVATE(grp), &keypair.MBEDTLS_PRIVATE(d)); + if (ret != 0) { + ESP_LOGE(TAG, "Invalid private key!"); + err = ESP_FAIL; + goto exit; + } + + ret = mbedtls_ecp_keypair_calc_public(&keypair, rand_func, NULL); + if (ret != 0) { + err = ESP_FAIL; + goto exit; + } + + ret = mbedtls_ecdsa_sign(&keypair.MBEDTLS_PRIVATE(grp), &r, &s, + &keypair.MBEDTLS_PRIVATE(d), hash, hlen, + rand_func, NULL); + if (ret != 0) { + err = ESP_FAIL; + goto exit; + } + + memset(out_sign, 0x00, sizeof(esp_tee_sec_storage_ecdsa_sign_t)); + ret = mbedtls_mpi_write_binary(&r, out_sign->sign_r, key_len); + if (ret == 0) { + ret = mbedtls_mpi_write_binary(&s, out_sign->sign_s, key_len); + } + + if (ret != 0) { + memset(out_sign, 0x00, sizeof(esp_tee_sec_storage_ecdsa_sign_t)); + err = ESP_FAIL; + goto exit; + } + + memset(out_pubkey, 0x00, sizeof(esp_tee_sec_storage_ecdsa_pubkey_t)); + ret = mbedtls_mpi_write_binary(&keypair.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), out_pubkey->pub_x, key_len); + if (ret == 0) { + ret = mbedtls_mpi_write_binary(&keypair.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), out_pubkey->pub_y, key_len); + } + + if (ret != 0) { + memset(out_pubkey, 0x00, sizeof(esp_tee_sec_storage_ecdsa_pubkey_t)); + err = ESP_FAIL; + goto exit; + } + + err = ESP_OK; + +exit: + if (derived_key) { + memset(derived_key, 0x00, key_len); + free(derived_key); + } + mbedtls_ecp_keypair_free(&keypair); + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + return err; +} diff --git a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage_wrapper.c b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage_wrapper.c index 2d5bba684f..828b6bd550 100644 --- a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage_wrapper.c +++ b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage_wrapper.c @@ -37,3 +37,8 @@ esp_err_t esp_tee_sec_storage_aead_decrypt(const esp_tee_sec_storage_aead_ctx_t { return esp_tee_service_call_with_noniram_intr_disabled(5, SS_ESP_TEE_SEC_STORAGE_AEAD_DECRYPT, ctx, tag, tag_len, output); } + +esp_err_t esp_tee_sec_storage_ecdsa_sign_pbkdf2(const esp_tee_sec_storage_pbkdf2_ctx_t *ctx, const uint8_t *hash, size_t hlen, esp_tee_sec_storage_ecdsa_sign_t *out_sign, esp_tee_sec_storage_ecdsa_pubkey_t *out_pubkey) +{ + return esp_tee_service_call(6, SS_ESP_TEE_SEC_STORAGE_ECDSA_SIGN_PBKDF2, ctx, hash, hlen, out_sign, out_pubkey); +} diff --git a/components/esp_tee/subproject/main/core/esp_secure_services.c b/components/esp_tee/subproject/main/core/esp_secure_services.c index 9995165cc5..1a8553eaa7 100644 --- a/components/esp_tee/subproject/main/core/esp_secure_services.c +++ b/components/esp_tee/subproject/main/core/esp_secure_services.c @@ -220,6 +220,7 @@ esp_err_t _ss_esp_hmac_calculate(hmac_key_id_t key_id, const void *message, size #if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID); #endif + valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID); if (!valid_addr) { return ESP_ERR_INVALID_ARG; @@ -236,6 +237,7 @@ esp_err_t _ss_esp_hmac_jtag_enable(hmac_key_id_t key_id, const uint8_t *token) #if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID); #endif + valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID); if (!valid_addr) { return ESP_ERR_INVALID_ARG; @@ -262,6 +264,7 @@ esp_err_t _ss_esp_ds_sign(const void *message, #if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID); #endif + valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID); if (!valid_addr) { return ESP_ERR_INVALID_ARG; @@ -283,6 +286,7 @@ esp_err_t _ss_esp_ds_start_sign(const void *message, #if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID); #endif + valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID); if (!valid_addr) { return ESP_ERR_INVALID_ARG; diff --git a/components/esp_tee/subproject/main/core/esp_secure_services_iram.c b/components/esp_tee/subproject/main/core/esp_secure_services_iram.c index 8263a6e728..173ff34323 100644 --- a/components/esp_tee/subproject/main/core/esp_secure_services_iram.c +++ b/components/esp_tee/subproject/main/core/esp_secure_services_iram.c @@ -194,6 +194,26 @@ esp_err_t _ss_esp_tee_sec_storage_aead_decrypt(const esp_tee_sec_storage_aead_ct return esp_tee_sec_storage_aead_decrypt(ctx, tag, tag_len, output); } +esp_err_t _ss_esp_tee_sec_storage_ecdsa_sign_pbkdf2(const esp_tee_sec_storage_pbkdf2_ctx_t *ctx, const uint8_t *hash, size_t hlen, esp_tee_sec_storage_ecdsa_sign_t *out_sign, esp_tee_sec_storage_ecdsa_pubkey_t *out_pubkey) +{ + bool valid_addr = (esp_tee_ptr_in_ree((void *)ctx) && + esp_tee_ptr_in_ree((void *)hash) && + esp_tee_ptr_in_ree((void *)out_sign) && + esp_tee_ptr_in_ree((void *)out_pubkey)); + + valid_addr &= (esp_tee_ptr_in_ree((void *)((char *)ctx + sizeof(esp_tee_sec_storage_pbkdf2_ctx_t))) && + esp_tee_ptr_in_ree((void *)(hash + hlen)) && + esp_tee_ptr_in_ree((void *)((char *)out_sign + sizeof(esp_tee_sec_storage_ecdsa_sign_t))) && + esp_tee_ptr_in_ree((void *)((char *)out_pubkey + sizeof(esp_tee_sec_storage_ecdsa_pubkey_t)))); + + if (!valid_addr) { + return ESP_ERR_INVALID_ARG; + } + ESP_FAULT_ASSERT(valid_addr); + + return esp_tee_sec_storage_ecdsa_sign_pbkdf2(ctx, hash, hlen, out_sign, out_pubkey); +} + /* ---------------------------------------------- MMU HAL ------------------------------------------------- */ void _ss_mmu_hal_map_region(uint32_t mmu_id, mmu_target_t mem_type, uint32_t vaddr, diff --git a/components/esp_tee/test_apps/tee_cli_app/README.md b/components/esp_tee/test_apps/tee_cli_app/README.md index 66cfc49649..b9f4c39908 100644 --- a/components/esp_tee/test_apps/tee_cli_app/README.md +++ b/components/esp_tee/test_apps/tee_cli_app/README.md @@ -21,7 +21,7 @@ Configure the Secure Storage mode for determining how the NVS XTS encryption key - **Development** Mode: Encryption keys are embedded in the ESP-TEE firmware (identical across all instances). - **Release** Mode: Encryption keys are derived via the HMAC peripheral using a key stored in eFuse. - - Set the eFuse key ID storing the HMAC key at `ESP-TEE (Trusted Execution Environment) → Secure Services → Secure Storage: eFuse HMAC key ID`. + - Set the eFuse key ID storing the HMAC key at `ESP-TEE (Trusted Execution Environment) → Secure Services → Secure Storage: eFuse HMAC key ID for storage encryption keys`. - Snippet for burning the secure storage key in eFuse is given below. ```shell diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c index ef2ac6dbe1..2f979eb137 100644 --- a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c @@ -252,7 +252,6 @@ TEST_CASE("Test TEE Secure Storage - Operations with invalid/non-existent keys", // Test ECDSA key with AES operation ESP_LOGI(TAG, "Key ID: %s - Trying AES operation with ECDSA key...", key_cfg.id); esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id); - ESP_LOGW(TAG, "err: %x", err); TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND); TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg)); TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_aead_encrypt(&aead_ctx, tag, sizeof(tag), ciphertext)); diff --git a/components/mbedtls/CMakeLists.txt b/components/mbedtls/CMakeLists.txt index 0b42e78c4e..b3aed5dd4a 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -280,6 +280,10 @@ target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/esp_ds/esp_ds_common.c") endif() +if(CONFIG_SOC_HMAC_SUPPORTED) + target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/esp_hmac_pbkdf2.c") +endif() + # Note: some mbedTLS hardware acceleration can be enabled/disabled by config. # # We don't need to filter aes.c as this uses a different prefix (esp_aes_x) and the diff --git a/components/mbedtls/esp_tee/esp_tee_mbedtls.cmake b/components/mbedtls/esp_tee/esp_tee_mbedtls.cmake index 6b89f92859..d2f10d5215 100644 --- a/components/mbedtls/esp_tee/esp_tee_mbedtls.cmake +++ b/components/mbedtls/esp_tee/esp_tee_mbedtls.cmake @@ -65,3 +65,6 @@ target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/sha/core/sha.c" target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/ecc/esp_ecc.c" "${COMPONENT_DIR}/port/ecc/ecc_alt.c") + +# HMAC-based PBKDF2 implementation +target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/esp_hmac_pbkdf2.c") diff --git a/components/mbedtls/port/esp_hmac_pbkdf2.c b/components/mbedtls/port/esp_hmac_pbkdf2.c new file mode 100644 index 0000000000..05dc5f2d44 --- /dev/null +++ b/components/mbedtls/port/esp_hmac_pbkdf2.c @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#include +#include "esp_hmac.h" +#include "sdkconfig.h" + +#include "esp_hmac_pbkdf2.h" + +#define SHA256_DIGEST_SZ 32 + +esp_err_t esp_hmac_derive_pbkdf2_key(hmac_key_id_t key_id, const uint8_t *salt, size_t salt_len, int iterations, size_t key_len, uint8_t *out_key) +{ + if (!out_key || !salt || key_len == 0 || salt_len == 0 || iterations <= 0) { + return ESP_ERR_INVALID_ARG; + } + + if (key_id >= HMAC_KEY_MAX) { + return ESP_ERR_INVALID_ARG; + } + + uint8_t U[SHA256_DIGEST_SZ] = {0}; + uint8_t T[SHA256_DIGEST_SZ] = {0}; + uint8_t counter[4] = {0, 0, 0, 1}; + + uint8_t *hmac_input = calloc(1, salt_len + sizeof(counter)); + if (!hmac_input) { + return ESP_ERR_NO_MEM; + } + + esp_err_t err = ESP_FAIL; + size_t remaining = key_len; + uint8_t *out_p = out_key; + + while (remaining > 0) { + memcpy(hmac_input, salt, salt_len); + memcpy(hmac_input + salt_len, counter, sizeof(counter)); + + err = esp_hmac_calculate(key_id, hmac_input, salt_len + sizeof(counter), U); + if (err != ESP_OK) { + goto cleanup; + } + + memcpy(T, U, SHA256_DIGEST_SZ); + for (int i = 1; i < iterations; i++) { + err = esp_hmac_calculate(key_id, U, SHA256_DIGEST_SZ, U); + if (err != ESP_OK) { + goto cleanup; + } + + for (int j = 0; j < SHA256_DIGEST_SZ; j++) { + T[j] ^= U[j]; + } + } + + size_t copy_len = (remaining < SHA256_DIGEST_SZ) ? remaining : SHA256_DIGEST_SZ; + memcpy(out_p, T, copy_len); + out_p += copy_len; + remaining -= copy_len; + + for (int i = 3; i >= 0; i--) { + if (++counter[i]) { + break; + } + } + } + + err = ESP_OK; + +cleanup: + memset(U, 0x00, sizeof(U)); + memset(T, 0x00, sizeof(T)); + if (hmac_input) { + memset(hmac_input, 0x00, salt_len + sizeof(counter)); + free(hmac_input); + } + return err; +} diff --git a/components/mbedtls/port/include/esp_hmac_pbkdf2.h b/components/mbedtls/port/include/esp_hmac_pbkdf2.h new file mode 100644 index 0000000000..77ed5c2309 --- /dev/null +++ b/components/mbedtls/port/include/esp_hmac_pbkdf2.h @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "esp_err.h" +#include "hal/hmac_types.h" + +/** + * @brief Derive a key using PBKDF2-HMAC-SHA256 algorithm with HMAC peripheral + * + * This function implements PBKDF2 (Password-Based Key Derivation Function 2) using HMAC-SHA256 + * to derive a cryptographic key from the efuse key and salt. The function uses the HMAC peripheral + * in upstream mode to perform the underlying HMAC calculations. + * + * @param key_id Determines which of the 6 key blocks in the efuses should be used as the base key. + * The corresponding purpose field of the key block must be set to HMAC upstream purpose. + * @param salt Input salt for the PBKDF2 algorithm + * @param salt_len Length of the salt in bytes (must be > 0) + * @param iterations Number of iterations (recommended >= 2048) + * @param key_len Length of the derived key to generate in bytes (must be > 0) + * @param[out] out_key Buffer to store the derived key. Must be at least key_len bytes. + * @return esp_err_t + * - ESP_OK: Key derivation successful + * - ESP_ERR_INVALID_ARG: Invalid arguments (null pointers, zero lengths, or key_id out of range) + * - ESP_ERR_NO_MEM: Memory allocation failed + * - ESP_FAIL: HMAC calculation failed + */ +esp_err_t esp_hmac_derive_pbkdf2_key(hmac_key_id_t key_id, const uint8_t *salt, size_t salt_len, int iterations, size_t key_len, uint8_t *out_key); + +#ifdef __cplusplus +} +#endif diff --git a/docs/en/security/tee/tee-sec-storage.rst b/docs/en/security/tee/tee-sec-storage.rst index ad269779c0..22c37b11f8 100644 --- a/docs/en/security/tee/tee-sec-storage.rst +++ b/docs/en/security/tee/tee-sec-storage.rst @@ -21,6 +21,13 @@ Additionally, the secure storage provides interfaces for performing the followin As per the current implementation, the TEE Secure Storage partition **must** have the label ``secure_storage``. +TEE secure storage also supports ECDSA signing with keys derived via PBKDF2 (Password-Based Key Derivation Function 2), using an HMAC key programmed in eFuse along with a user-provided salt. This mechanism enables ECDSA signing on both P-256 and P-192 curves without requiring storage of the actual private keys. The eFuse HMAC key ID for the PBKDF2 operations is specified via the :ref:`CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID` option. + +.. important:: + + - The eFuse HMAC key ID used for PBKDF2-based signing **CANNOT** be the same as the one used for deriving TEE secure storage encryption keys (i.e., :ref:`CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID`). + - This eFuse ID is also exclusive to the TEE and **CANNOT** be used by the REE for any purpose. + Internals --------- diff --git a/examples/security/tee/tee_secure_storage/README.md b/examples/security/tee/tee_secure_storage/README.md index 6ecc359260..194e22420e 100644 --- a/examples/security/tee/tee_secure_storage/README.md +++ b/examples/security/tee/tee_secure_storage/README.md @@ -39,7 +39,7 @@ TEE Secure Storage follows the NVS partition format and uses an AES-XTS encrypti #### Configure the eFuse key ID storing the HMAC key - Navigate to `ESP-TEE (Trusted Execution Environment) → Secure Services → Secure Storage: Mode` and enable the `Release` mode configuration. -- Set the eFuse key ID storing the HMAC key at `ESP-TEE (Trusted Execution Environment) → Secure Services → Secure Storage: eFuse HMAC key ID`. +- Set the eFuse key ID storing the HMAC key at `ESP-TEE (Trusted Execution Environment) → Secure Services → Secure Storage: eFuse HMAC key ID for storage encryption keys`. **Note:** Before running the example, users must program the HMAC key into the configured eFuse block - refer to the snippet below. The TEE checks whether the specified eFuse block is empty or already programmed with a key. If the block is empty, an error will be returned; otherwise, the pre-programmed key will be used.