mirror of
https://github.com/espressif/esp-idf
synced 2025-03-09 09:09:10 -04:00
213 lines
6.3 KiB
C
213 lines
6.3 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "esp_err.h"
|
|
#include "esp_log.h"
|
|
#include "soc/soc_caps.h"
|
|
#include "sdkconfig.h"
|
|
#include "esp_partition.h"
|
|
#include "nvs_flash.h"
|
|
#include "nvs_sec_provider.h"
|
|
#include "esp_rom_crc.h"
|
|
#include "private/nvs_sec_provider_private.h"
|
|
|
|
#if SOC_HMAC_SUPPORTED
|
|
#include "rom/efuse.h"
|
|
#include "rom/hmac.h"
|
|
#endif // SOC_HMAC_SUPPORTED
|
|
|
|
static __attribute__((unused)) const char *TAG = "nvs_bootloader_sec_provider";
|
|
|
|
static __attribute__((unused)) nvs_sec_scheme_t sec_scheme;
|
|
|
|
#if CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC
|
|
|
|
static nvs_sec_config_flash_enc_t nvs_sec_config_flash_enc_scheme_data;
|
|
|
|
static esp_err_t nvs_bootloader_read_security_cfg(const esp_partition_t* partition, nvs_sec_cfg_t* cfg)
|
|
{
|
|
if (cfg == NULL || partition == NULL) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
uint32_t crc_read, crc_calc;
|
|
|
|
esp_err_t err = esp_partition_read(partition, 0, cfg->eky, NVS_KEY_SIZE);
|
|
if (err != ESP_OK) {
|
|
return err;
|
|
}
|
|
|
|
err = esp_partition_read(partition, NVS_KEY_SIZE, cfg->tky, NVS_KEY_SIZE);
|
|
if (err != ESP_OK) {
|
|
return err;
|
|
}
|
|
|
|
err = esp_partition_read(partition, 2 * NVS_KEY_SIZE, &crc_read, 4);
|
|
if (err != ESP_OK) {
|
|
return err;
|
|
}
|
|
|
|
crc_calc = esp_rom_crc32_le(0xffffffff, cfg->eky, NVS_KEY_SIZE);
|
|
crc_calc = esp_rom_crc32_le(crc_calc, cfg->tky, NVS_KEY_SIZE);
|
|
|
|
if (crc_calc != crc_read) {
|
|
return ESP_ERR_NVS_CORRUPT_KEY_PART;
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t read_security_cfg_flash_enc(const void* sec_scheme_cfg, nvs_sec_cfg_t* cfg)
|
|
{
|
|
if (sec_scheme_cfg == NULL || cfg == NULL) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
nvs_sec_config_flash_enc_t *scheme_cfg_flash_enc = (nvs_sec_config_flash_enc_t *)sec_scheme_cfg;
|
|
return nvs_bootloader_read_security_cfg(scheme_cfg_flash_enc->nvs_keys_part, cfg);
|
|
}
|
|
|
|
esp_err_t nvs_sec_provider_register_flash_enc(const nvs_sec_config_flash_enc_t *sec_scheme_cfg, nvs_sec_scheme_t **sec_scheme_handle_out)
|
|
{
|
|
if (sec_scheme_cfg == NULL || sec_scheme_handle_out == NULL) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
bzero(&sec_scheme, sizeof(nvs_sec_scheme_t));
|
|
|
|
sec_scheme.scheme_id = NVS_SEC_SCHEME_FLASH_ENC;
|
|
sec_scheme.nvs_flash_read_cfg = &read_security_cfg_flash_enc;
|
|
|
|
bzero(&nvs_sec_config_flash_enc_scheme_data, sizeof(nvs_sec_config_flash_enc_t));
|
|
sec_scheme.scheme_data = &nvs_sec_config_flash_enc_scheme_data;
|
|
|
|
memcpy(sec_scheme.scheme_data, (void *)sec_scheme_cfg, sizeof(nvs_sec_config_flash_enc_t));
|
|
|
|
*sec_scheme_handle_out = &sec_scheme;
|
|
return ESP_OK;
|
|
}
|
|
#endif
|
|
|
|
#if SOC_HMAC_SUPPORTED
|
|
|
|
static nvs_sec_config_hmac_t nvs_sec_config_hmac_scheme_data;
|
|
|
|
#if CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID < 0
|
|
#error "NVS Encryption (HMAC): Configured eFuse block (CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID) out of range!"
|
|
#endif
|
|
|
|
static esp_err_t nvs_bootloader_hmac_calculate(ets_efuse_block_t hmac_key,
|
|
const void *message,
|
|
size_t message_len,
|
|
uint8_t *hmac)
|
|
{
|
|
ets_hmac_enable();
|
|
|
|
int hmac_ret = ets_hmac_calculate_message(hmac_key, message, message_len, hmac);
|
|
|
|
ets_hmac_disable();
|
|
|
|
if (hmac_ret != 0) {
|
|
return ESP_FAIL;
|
|
} else {
|
|
return ESP_OK;
|
|
}
|
|
}
|
|
|
|
static esp_err_t compute_nvs_keys_with_hmac(ets_efuse_block_t hmac_key, nvs_sec_cfg_t* cfg)
|
|
{
|
|
uint32_t ekey_seed[8] = {[0 ... 7] = EKEY_SEED};
|
|
uint32_t tkey_seed[8] = {[0 ... 7] = TKEY_SEED};
|
|
|
|
esp_err_t err = nvs_bootloader_hmac_calculate(hmac_key, ekey_seed, sizeof(ekey_seed), (uint8_t *)cfg->eky);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGD(TAG, "Failed to calculate seed HMAC: [0x%02X]", err);
|
|
return err;
|
|
}
|
|
|
|
err = nvs_bootloader_hmac_calculate(hmac_key, tkey_seed, sizeof(tkey_seed), (uint8_t *)cfg->tky);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGD(TAG, "Failed to calculate seed HMAC: [0x%02X]", err);
|
|
return err;
|
|
}
|
|
|
|
/* NOTE: If the XTS E-key and T-key are the same, we have a hash collision */
|
|
if (memcmp(cfg->eky, cfg->tky, NVS_KEY_SIZE) == 0) {
|
|
ESP_LOGD(TAG, "The XTS-AES E-key and T-key are the same, we have a hash collision");
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static bool is_hmac_key_burnt_in_efuse(ets_efuse_block_t hmac_key_blk)
|
|
{
|
|
|
|
ets_efuse_purpose_t hmac_efuse_blk_purpose = ets_efuse_get_key_purpose(hmac_key_blk);
|
|
|
|
if (hmac_efuse_blk_purpose == ETS_EFUSE_KEY_PURPOSE_HMAC_UP) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
static ets_efuse_block_t convert_key_type(hmac_key_id_t key_id) {
|
|
return (ets_efuse_block_t)(ETS_EFUSE_BLOCK_KEY0 + (ets_efuse_block_t) key_id);
|
|
}
|
|
|
|
static esp_err_t read_security_cfg_hmac(const void* scheme_cfg, nvs_sec_cfg_t* cfg)
|
|
{
|
|
if (cfg == NULL) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
nvs_sec_config_hmac_t *scheme_cfg_hmac = (nvs_sec_config_hmac_t *)scheme_cfg;
|
|
|
|
if (scheme_cfg_hmac->hmac_key_id >= HMAC_KEY_MAX) {
|
|
ESP_LOGD(TAG, "Invalid HMAC key ID received!");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
ets_efuse_block_t hmac_key = convert_key_type(scheme_cfg_hmac->hmac_key_id);
|
|
|
|
if (!is_hmac_key_burnt_in_efuse(hmac_key)) {
|
|
ESP_LOGD(TAG, "Could not find HMAC key in configured eFuse block!");
|
|
return ESP_ERR_NVS_SEC_HMAC_KEY_NOT_FOUND;
|
|
}
|
|
|
|
if (compute_nvs_keys_with_hmac(hmac_key, cfg) != ESP_OK) {
|
|
ESP_LOGD(TAG, "Failed to derive the encryption keys using HMAC!");
|
|
return ESP_ERR_NVS_SEC_HMAC_XTS_KEYS_DERIV_FAILED;
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t nvs_sec_provider_register_hmac(const nvs_sec_config_hmac_t *sec_scheme_cfg, nvs_sec_scheme_t **sec_scheme_handle_out)
|
|
{
|
|
if (sec_scheme_cfg == NULL || sec_scheme_handle_out == NULL) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
bzero(&sec_scheme, sizeof(nvs_sec_scheme_t));
|
|
|
|
sec_scheme.scheme_id = NVS_SEC_SCHEME_HMAC;
|
|
sec_scheme.nvs_flash_read_cfg = &read_security_cfg_hmac;
|
|
|
|
bzero(&nvs_sec_config_hmac_scheme_data, sizeof(nvs_sec_config_hmac_t));
|
|
sec_scheme.scheme_data = &nvs_sec_config_hmac_scheme_data;
|
|
|
|
memcpy(sec_scheme.scheme_data, (void *)sec_scheme_cfg, sizeof(nvs_sec_config_hmac_t));
|
|
|
|
*sec_scheme_handle_out = &sec_scheme;
|
|
return ESP_OK;
|
|
}
|
|
|
|
#endif // SOC_HMAC_SUPPORTED
|