efuse(esp32c2): Support eFuse key APIs

This commit is contained in:
KonstantinKondrashov
2021-12-03 01:48:47 +08:00
parent 3a23340e40
commit ebdc52d4e2
41 changed files with 1706 additions and 460 deletions
@@ -0,0 +1,401 @@
/*
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_efuse.h"
#include "esp_efuse_utility.h"
#include "soc/efuse_periph.h"
#include "assert.h"
#include "sdkconfig.h"
#include "esp_efuse_table.h"
/*
* Each eFuse key block has a special field that defines the purpose of this block.
* This special field is called key_purpose.
*/
const static char *TAG = "efuse";
/**
* @brief Keys and their attributes are packed into a structure
*/
typedef struct {
const esp_efuse_desc_t** key; /**< Key */
const esp_efuse_desc_t** keypurpose; /**< Key purpose */
const esp_efuse_desc_t** key_rd_dis; /**< Read protection of a key */
const esp_efuse_desc_t** key_wr_dis; /**< Write protection of a key*/
const esp_efuse_desc_t** keypurpose_wr_dis; /**< Write protection of a key purpose*/
} esp_efuse_keys_t;
typedef struct {
const esp_efuse_desc_t** revoke;
const esp_efuse_desc_t** revoke_wr_dis;
const esp_efuse_purpose_t digest_purpose;
} esp_efuse_revokes_t;
const esp_efuse_keys_t s_table[EFUSE_BLK_KEY_MAX - EFUSE_BLK_KEY0] = {
{ESP_EFUSE_KEY0, ESP_EFUSE_KEY_PURPOSE_0, ESP_EFUSE_RD_DIS_KEY0, ESP_EFUSE_WR_DIS_KEY0, ESP_EFUSE_WR_DIS_KEY0_PURPOSE},
{ESP_EFUSE_KEY1, ESP_EFUSE_KEY_PURPOSE_1, ESP_EFUSE_RD_DIS_KEY1, ESP_EFUSE_WR_DIS_KEY1, ESP_EFUSE_WR_DIS_KEY1_PURPOSE},
{ESP_EFUSE_KEY2, ESP_EFUSE_KEY_PURPOSE_2, ESP_EFUSE_RD_DIS_KEY2, ESP_EFUSE_WR_DIS_KEY2, ESP_EFUSE_WR_DIS_KEY2_PURPOSE},
{ESP_EFUSE_KEY3, ESP_EFUSE_KEY_PURPOSE_3, ESP_EFUSE_RD_DIS_KEY3, ESP_EFUSE_WR_DIS_KEY3, ESP_EFUSE_WR_DIS_KEY3_PURPOSE},
{ESP_EFUSE_KEY4, ESP_EFUSE_KEY_PURPOSE_4, ESP_EFUSE_RD_DIS_KEY4, ESP_EFUSE_WR_DIS_KEY4, ESP_EFUSE_WR_DIS_KEY4_PURPOSE},
{ESP_EFUSE_KEY5, ESP_EFUSE_KEY_PURPOSE_5, ESP_EFUSE_RD_DIS_KEY5, ESP_EFUSE_WR_DIS_KEY5, ESP_EFUSE_WR_DIS_KEY5_PURPOSE},
#if 0
{ESP_EFUSE_KEY6, ESP_EFUSE_KEY_PURPOSE_6, ESP_EFUSE_RD_DIS_KEY6, ESP_EFUSE_WR_DIS_KEY6, ESP_EFUSE_WR_DIS_KEY6_PURPOSE},
#endif
};
#if SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY
const esp_efuse_revokes_t s_revoke_table[] = {
{ESP_EFUSE_SECURE_BOOT_KEY_REVOKE0, ESP_EFUSE_WR_DIS_SECURE_BOOT_KEY_REVOKE0, ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST0},
{ESP_EFUSE_SECURE_BOOT_KEY_REVOKE1, ESP_EFUSE_WR_DIS_SECURE_BOOT_KEY_REVOKE1, ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST1},
{ESP_EFUSE_SECURE_BOOT_KEY_REVOKE2, ESP_EFUSE_WR_DIS_SECURE_BOOT_KEY_REVOKE2, ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST2},
};
#endif
bool esp_efuse_block_is_empty(esp_efuse_block_t block)
{
const unsigned blk_len_bit = 256;
uint32_t key[8];
esp_err_t err = esp_efuse_read_block(block, &key, 0, blk_len_bit);
if (err != ESP_OK) {
return false;
}
unsigned zeros = 0;
for (unsigned i = 0; i < blk_len_bit / 32; ++i) {
if (key[i] == 0) {
++zeros;
}
}
if (zeros == blk_len_bit / 32) {
return true;
}
return false;
}
// Sets a write protection for the whole block.
esp_err_t esp_efuse_set_write_protect(esp_efuse_block_t blk)
{
if (blk == EFUSE_BLK1) {
return esp_efuse_write_field_cnt(ESP_EFUSE_WR_DIS_BLK1, 1);
} else if (blk == EFUSE_BLK2) {
return esp_efuse_write_field_cnt(ESP_EFUSE_WR_DIS_SYS_DATA_PART1, 1);
} else if (blk == EFUSE_BLK3) {
return esp_efuse_write_field_cnt(ESP_EFUSE_WR_DIS_USER_DATA, 1);
} else if (blk == EFUSE_BLK10) {
return esp_efuse_write_field_cnt(ESP_EFUSE_WR_DIS_SYS_DATA_PART2, 1);
} else if (blk >= EFUSE_BLK_KEY0 && blk < EFUSE_BLK_KEY_MAX) {
unsigned idx = blk - EFUSE_BLK_KEY0;
return esp_efuse_write_field_cnt(s_table[idx].key_wr_dis, 1);
}
return ESP_ERR_NOT_SUPPORTED;
}
// read protect for blk.
esp_err_t esp_efuse_set_read_protect(esp_efuse_block_t blk)
{
if (blk >= EFUSE_BLK_KEY0 && blk < EFUSE_BLK_KEY_MAX) {
unsigned idx = blk - EFUSE_BLK_KEY0;
return esp_efuse_write_field_cnt(s_table[idx].key_rd_dis, 1);
}
else if (blk == EFUSE_BLK10) {
return esp_efuse_write_field_cnt(ESP_EFUSE_RD_DIS_SYS_DATA_PART2, 1);
}
return ESP_ERR_NOT_SUPPORTED;
}
// get efuse coding_scheme.
esp_efuse_coding_scheme_t esp_efuse_get_coding_scheme(esp_efuse_block_t blk)
{
esp_efuse_coding_scheme_t scheme;
if (blk == EFUSE_BLK0) {
scheme = EFUSE_CODING_SCHEME_NONE;
} else {
scheme = EFUSE_CODING_SCHEME_RS;
}
return scheme;
}
const esp_efuse_desc_t **esp_efuse_get_purpose_field(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return NULL;
}
unsigned idx = block - EFUSE_BLK_KEY0;
return s_table[idx].keypurpose;
}
const esp_efuse_desc_t** esp_efuse_get_key(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return NULL;
}
unsigned idx = block - EFUSE_BLK_KEY0;
return s_table[idx].key;
}
bool esp_efuse_get_key_dis_read(esp_efuse_block_t block)
{
assert(block >= EFUSE_BLK_KEY0 && block < EFUSE_BLK_KEY_MAX);
unsigned idx = block - EFUSE_BLK_KEY0;
return esp_efuse_read_field_bit(s_table[idx].key_rd_dis);
}
esp_err_t esp_efuse_set_key_dis_read(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return ESP_ERR_INVALID_ARG;
}
unsigned idx = block - EFUSE_BLK_KEY0;
return esp_efuse_write_field_bit(s_table[idx].key_rd_dis);
}
bool esp_efuse_get_key_dis_write(esp_efuse_block_t block)
{
assert(block >= EFUSE_BLK_KEY0 && block < EFUSE_BLK_KEY_MAX);
unsigned idx = block - EFUSE_BLK_KEY0;
return esp_efuse_read_field_bit(s_table[idx].key_wr_dis);
}
esp_err_t esp_efuse_set_key_dis_write(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return ESP_ERR_INVALID_ARG;
}
unsigned idx = block - EFUSE_BLK_KEY0;
return esp_efuse_write_field_bit(s_table[idx].key_wr_dis);
}
esp_efuse_purpose_t esp_efuse_get_key_purpose(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return ESP_EFUSE_KEY_PURPOSE_MAX;
}
unsigned idx = block - EFUSE_BLK_KEY0;
uint8_t value = 0;
esp_err_t err = esp_efuse_read_field_blob(s_table[idx].keypurpose, &value, s_table[idx].keypurpose[0]->bit_count);
if (err != ESP_OK) {
return ESP_EFUSE_KEY_PURPOSE_MAX;
}
return value;
}
esp_err_t esp_efuse_set_key_purpose(esp_efuse_block_t block, esp_efuse_purpose_t purpose)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return ESP_ERR_INVALID_ARG;
}
unsigned idx = block - EFUSE_BLK_KEY0;
return esp_efuse_write_field_blob(s_table[idx].keypurpose, &purpose, s_table[idx].keypurpose[0]->bit_count);
}
bool esp_efuse_get_keypurpose_dis_write(esp_efuse_block_t block)
{
assert(block >= EFUSE_BLK_KEY0 && block < EFUSE_BLK_KEY_MAX);
unsigned idx = block - EFUSE_BLK_KEY0;
return esp_efuse_read_field_bit(s_table[idx].keypurpose_wr_dis);
}
esp_err_t esp_efuse_set_keypurpose_dis_write(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return ESP_ERR_INVALID_ARG;
}
unsigned idx = block - EFUSE_BLK_KEY0;
return esp_efuse_write_field_bit(s_table[idx].keypurpose_wr_dis);
}
bool esp_efuse_find_purpose(esp_efuse_purpose_t purpose, esp_efuse_block_t *block)
{
esp_efuse_block_t dummy;
if (block == NULL) {
block = &dummy;
}
for (esp_efuse_block_t b = EFUSE_BLK_KEY0; b < EFUSE_BLK_KEY_MAX; b++) {
if (esp_efuse_get_key_purpose(b) == purpose) {
*block = b;
return true;
}
}
return false;
}
esp_efuse_block_t esp_efuse_find_unused_key_block(void)
{
for (esp_efuse_block_t b = EFUSE_BLK_KEY0; b < EFUSE_BLK_KEY_MAX; b++) {
if (esp_efuse_key_block_unused(b)) {
return b;
}
}
return EFUSE_BLK_KEY_MAX; // nothing
}
unsigned esp_efuse_count_unused_key_blocks(void)
{
unsigned r = 0;
for (esp_efuse_block_t b = EFUSE_BLK_KEY0; b < EFUSE_BLK_KEY_MAX; b++) {
if (esp_efuse_key_block_unused(b)) {
r++;
}
}
return r;
}
bool esp_efuse_key_block_unused(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return false; // Not a key block
}
if (esp_efuse_get_key_purpose(block) != ESP_EFUSE_KEY_PURPOSE_USER ||
esp_efuse_get_keypurpose_dis_write(block) ||
esp_efuse_get_key_dis_read(block) ||
esp_efuse_get_key_dis_write(block)) {
return false; // Block in use!
}
if (!esp_efuse_block_is_empty(block)) {
return false; // Block in use!
}
return true; // Unused
}
esp_err_t esp_efuse_write_key(esp_efuse_block_t block, esp_efuse_purpose_t purpose, const void *key, size_t key_size_bytes)
{
esp_err_t err = ESP_OK;
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX || key_size_bytes > 32 || purpose >= ESP_EFUSE_KEY_PURPOSE_MAX) {
return ESP_ERR_INVALID_ARG;
}
esp_efuse_batch_write_begin();
if (!esp_efuse_key_block_unused(block)) {
err = ESP_ERR_INVALID_STATE;
} else {
unsigned idx = block - EFUSE_BLK_KEY0;
ESP_EFUSE_CHK(esp_efuse_write_field_blob(s_table[idx].key, key, key_size_bytes * 8));
ESP_EFUSE_CHK(esp_efuse_set_key_dis_write(block));
if (purpose == ESP_EFUSE_KEY_PURPOSE_XTS_AES_128_KEY ||
#ifdef SOC_FLASH_ENCRYPTION_XTS_AES_256
purpose == ESP_EFUSE_KEY_PURPOSE_XTS_AES_256_KEY_1 ||
purpose == ESP_EFUSE_KEY_PURPOSE_XTS_AES_256_KEY_2 ||
#endif //#ifdef SOC_EFUSE_SUPPORT_XTS_AES_256_KEYS
purpose == ESP_EFUSE_KEY_PURPOSE_HMAC_DOWN_ALL ||
purpose == ESP_EFUSE_KEY_PURPOSE_HMAC_DOWN_JTAG ||
purpose == ESP_EFUSE_KEY_PURPOSE_HMAC_DOWN_DIGITAL_SIGNATURE ||
purpose == ESP_EFUSE_KEY_PURPOSE_HMAC_UP) {
ESP_EFUSE_CHK(esp_efuse_set_key_dis_read(block));
}
ESP_EFUSE_CHK(esp_efuse_set_key_purpose(block, purpose));
ESP_EFUSE_CHK(esp_efuse_set_keypurpose_dis_write(block));
return esp_efuse_batch_write_commit();
}
err_exit:
esp_efuse_batch_write_cancel();
return err;
}
esp_err_t esp_efuse_write_keys(const esp_efuse_purpose_t purposes[], uint8_t keys[][32], unsigned number_of_keys)
{
esp_err_t err = ESP_OK;
if (number_of_keys == 0 || number_of_keys > (EFUSE_BLK_KEY_MAX - EFUSE_BLK_KEY0) || keys == NULL || purposes == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_efuse_purpose_t purpose = 0;
esp_efuse_block_t block = EFUSE_BLK_KEY0;
esp_efuse_batch_write_begin();
unsigned unused_keys = esp_efuse_count_unused_key_blocks();
if (number_of_keys > unused_keys) {
ESP_LOGE(TAG, "Not enough unused key blocks available. Required %d, was %d", number_of_keys, unused_keys);
err = ESP_ERR_NOT_ENOUGH_UNUSED_KEY_BLOCKS;
} else {
for (int i_key = 0; (block < EFUSE_BLK_KEY_MAX) && (i_key < number_of_keys); block++) {
if (esp_efuse_key_block_unused(block)) {
purpose = purposes[i_key];
ESP_LOGI(TAG, "Writing EFUSE_BLK_KEY%d with purpose %d", block - EFUSE_BLK_KEY0, purpose);
ESP_EFUSE_CHK(esp_efuse_write_key(block, purpose, keys[i_key], 32));
i_key++;
}
}
return esp_efuse_batch_write_commit();
err_exit:
ESP_LOGE(TAG, "Failed to write EFUSE_BLK_KEY%d with purpose %d. Can't continue.", block - EFUSE_BLK_KEY0, purpose);
}
esp_efuse_batch_write_cancel();
return err;
}
#if SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY
bool esp_efuse_get_digest_revoke(unsigned num_digest)
{
assert(num_digest < sizeof(s_revoke_table) / sizeof(esp_efuse_revokes_t));
return esp_efuse_read_field_bit(s_revoke_table[num_digest].revoke);
}
esp_err_t esp_efuse_set_digest_revoke(unsigned num_digest)
{
if (num_digest >= sizeof(s_revoke_table) / sizeof(esp_efuse_revokes_t)) {
return ESP_ERR_INVALID_ARG;
}
return esp_efuse_write_field_bit(s_revoke_table[num_digest].revoke);
}
bool esp_efuse_get_write_protect_of_digest_revoke(unsigned num_digest)
{
assert(num_digest < sizeof(s_revoke_table) / sizeof(esp_efuse_revokes_t));
return esp_efuse_read_field_bit(s_revoke_table[num_digest].revoke_wr_dis);
}
esp_err_t esp_efuse_set_write_protect_of_digest_revoke(unsigned num_digest)
{
if (num_digest >= sizeof(s_revoke_table) / sizeof(esp_efuse_revokes_t)) {
return ESP_ERR_INVALID_ARG;
}
return esp_efuse_write_field_bit(s_revoke_table[num_digest].revoke_wr_dis);
}
esp_err_t esp_secure_boot_read_key_digests(ets_secure_boot_key_digests_t *trusted_keys)
{
bool found = false;
esp_efuse_block_t key_block;
if (trusted_keys == NULL) {
return ESP_FAIL;
}
for (unsigned i = 0; i < MAX_KEY_DIGESTS; i++) {
trusted_keys->key_digests[i] = NULL;
if (esp_efuse_get_digest_revoke(i)) {
continue;
}
// Anti-FI check that this efuse really is not revoked
assert(esp_efuse_get_digest_revoke(i) == 0);
if (!esp_efuse_find_purpose(s_revoke_table[i].digest_purpose, &key_block)) {
continue;
}
trusted_keys->key_digests[i] = (const void *)esp_efuse_utility_get_read_register_address(key_block);
found = found || (trusted_keys->key_digests[i] != NULL);
}
trusted_keys->allow_key_revoke = false;
if (!found) {
return ESP_FAIL;
}
return ESP_OK;
}
#endif //#if SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY
@@ -0,0 +1,247 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_efuse.h"
#include "esp_efuse_utility.h"
#include "soc/efuse_periph.h"
#include "assert.h"
#include "sdkconfig.h"
#include "esp_efuse_table.h"
/*
* The chip has only one eFuse key block.
* There are no eFuse key purpose fields (added only virtual key purposes to support key APIs).
* Flash Encryption key and Secure Boot key reside in one eFuse block.
* To burn the FE key and the SB key need to use the batch mode to do it at once.
*/
static esp_err_t esp_efuse_set_key_purpose(esp_efuse_block_t block, esp_efuse_purpose_t purpose);
static __attribute__((unused)) const char *TAG = "efuse";
/**
* @brief Keys and their attributes are packed into a structure
*/
typedef struct {
const esp_efuse_desc_t** key; /**< Key */
esp_efuse_purpose_t purpose; /**< purpose of block */
const esp_efuse_desc_t** key_rd_dis; /**< Read protection of a key */
const esp_efuse_desc_t** key_wr_dis; /**< Write protection of a key*/
} esp_efuse_keys_t;
const esp_efuse_keys_t s_table[EFUSE_BLK_KEY_MAX - EFUSE_BLK_KEY0] = {
{ESP_EFUSE_KEY0, ESP_EFUSE_KEY_PURPOSE_USER, ESP_EFUSE_RD_DIS_KEY0, ESP_EFUSE_WR_DIS_KEY0}, // EFUSE_BLK_KEY0
};
// Sets a write protection for the whole block.
esp_err_t esp_efuse_set_write_protect(esp_efuse_block_t blk)
{
if (blk < EFUSE_BLK_KEY0 || blk >= EFUSE_BLK_KEY_MAX) {
return ESP_ERR_NOT_SUPPORTED;
}
unsigned idx = blk - EFUSE_BLK_KEY0;
return esp_efuse_write_field_cnt(s_table[idx].key_wr_dis, s_table[idx].key_wr_dis[0]->bit_count);
}
// Sets a read protection for the whole block.
esp_err_t esp_efuse_set_read_protect(esp_efuse_block_t blk)
{
if (blk < EFUSE_BLK_KEY0 || blk >= EFUSE_BLK_KEY_MAX) {
return ESP_ERR_NOT_SUPPORTED;
}
unsigned idx = blk - EFUSE_BLK_KEY0;
return esp_efuse_write_field_cnt(s_table[idx].key_rd_dis, s_table[idx].key_rd_dis[0]->bit_count);
}
// get efuse coding_scheme.
esp_efuse_coding_scheme_t esp_efuse_get_coding_scheme(esp_efuse_block_t blk)
{
esp_efuse_coding_scheme_t scheme;
if (blk == EFUSE_BLK0) {
scheme = EFUSE_CODING_SCHEME_NONE;
} else {
scheme = EFUSE_CODING_SCHEME_RS;
}
return scheme;
}
bool esp_efuse_block_is_empty(esp_efuse_block_t block)
{
assert(block >= EFUSE_BLK_KEY0 && block < EFUSE_BLK_KEY_MAX);
const unsigned blk_len_bit = 256;
uint32_t key[8];
esp_err_t err = esp_efuse_read_block(block, &key, 0, blk_len_bit);
if (err != ESP_OK) {
return false;
}
unsigned zeros = 0;
for (unsigned i = 0; i < blk_len_bit / 32; ++i) {
if (key[i] == 0) {
++zeros;
}
}
if (zeros == blk_len_bit / 32) {
return true;
}
return false;
}
bool esp_efuse_get_key_dis_read(esp_efuse_block_t block)
{
assert(block >= EFUSE_BLK_KEY0 && block < EFUSE_BLK_KEY_MAX);
unsigned idx = block - EFUSE_BLK_KEY0;
if (esp_efuse_get_key_purpose(block) == ESP_EFUSE_KEY_PURPOSE_XTS_AES_128_KEY) {
uint8_t value = 0;
esp_err_t err = esp_efuse_read_field_blob(s_table[idx].key_rd_dis, &value, s_table[idx].key_rd_dis[0]->bit_count);
assert(err == ESP_OK);
return (err == ESP_OK) && (value == ((1 << s_table[idx].key_rd_dis[0]->bit_count) - 1));
}
return esp_efuse_read_field_bit(ESP_EFUSE_RD_DIS_KEY0_LOW);
}
esp_err_t esp_efuse_set_key_dis_read(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return ESP_ERR_INVALID_ARG;
}
if (esp_efuse_get_key_purpose(block) == ESP_EFUSE_KEY_PURPOSE_XTS_AES_128_KEY) {
unsigned idx = block - EFUSE_BLK_KEY0;
uint8_t value = (1 << s_table[idx].key_rd_dis[0]->bit_count) - 1;
return esp_efuse_write_field_blob(s_table[idx].key_rd_dis, &value, s_table[idx].key_rd_dis[0]->bit_count);
}
return esp_efuse_write_field_bit(ESP_EFUSE_RD_DIS_KEY0_LOW);
}
bool esp_efuse_get_key_dis_write(esp_efuse_block_t block)
{
assert(block >= EFUSE_BLK_KEY0 && block < EFUSE_BLK_KEY_MAX);
unsigned idx = block - EFUSE_BLK_KEY0;
return esp_efuse_read_field_bit(s_table[idx].key_wr_dis);
}
esp_err_t esp_efuse_set_key_dis_write(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return ESP_ERR_INVALID_ARG;
}
unsigned idx = block - EFUSE_BLK_KEY0;
return esp_efuse_write_field_bit(s_table[idx].key_wr_dis);
}
bool esp_efuse_key_block_unused(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return false; // Not a key block
}
if (esp_efuse_get_key_dis_read(block) || esp_efuse_get_key_dis_write(block) ||
!esp_efuse_block_is_empty(block)) {
return false; // Block in use!
}
return true; // Unused
}
esp_efuse_purpose_t esp_efuse_get_key_purpose(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return ESP_EFUSE_KEY_PURPOSE_MAX;
}
if (esp_efuse_read_field_bit(ESP_EFUSE_XTS_KEY_LENGTH_256)) {
return ESP_EFUSE_KEY_PURPOSE_XTS_AES_128_KEY;
}
return ESP_EFUSE_KEY_PURPOSE_XTS_AES_64_KEY;
}
static esp_err_t esp_efuse_set_key_purpose(esp_efuse_block_t block, esp_efuse_purpose_t purpose)
{
(void)block;
if (purpose == ESP_EFUSE_KEY_PURPOSE_XTS_AES_128_KEY) {
return esp_efuse_write_field_bit(ESP_EFUSE_XTS_KEY_LENGTH_256);
}
return ESP_OK;
}
bool esp_efuse_get_keypurpose_dis_write(esp_efuse_block_t block)
{
(void)block;
return true;
}
bool esp_efuse_find_purpose(esp_efuse_purpose_t purpose, esp_efuse_block_t *block)
{
(void)purpose;
esp_efuse_block_t dummy;
if (block == NULL) {
block = &dummy;
}
*block = EFUSE_BLK_KEY0;
return true;
}
esp_err_t esp_efuse_write_key(esp_efuse_block_t block, esp_efuse_purpose_t purpose, const void *key, size_t key_size_bytes)
{
esp_err_t err = ESP_OK;
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX || key_size_bytes > 32 || purpose >= ESP_EFUSE_KEY_PURPOSE_MAX) {
return ESP_ERR_INVALID_ARG;
}
if ((purpose == ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_V2 || purpose == ESP_EFUSE_KEY_PURPOSE_XTS_AES_64_KEY) && (key_size_bytes != 16)) {
return ESP_ERR_INVALID_ARG;
}
esp_efuse_batch_write_begin();
if (!esp_efuse_key_block_unused(block)) {
err = ESP_ERR_INVALID_STATE;
} else {
size_t offset_in_bits = (purpose == ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_V2) ? 16 * 8 : 0;
ESP_EFUSE_CHK(esp_efuse_write_block(block, key, offset_in_bits, key_size_bytes * 8));
if (purpose == ESP_EFUSE_KEY_PURPOSE_XTS_AES_128_KEY) {
ESP_EFUSE_CHK(esp_efuse_set_key_purpose(block, purpose));
}
if (purpose == ESP_EFUSE_KEY_PURPOSE_XTS_AES_128_KEY || purpose == ESP_EFUSE_KEY_PURPOSE_XTS_AES_64_KEY) {
ESP_EFUSE_CHK(esp_efuse_set_key_dis_read(block));
}
ESP_EFUSE_CHK(esp_efuse_set_key_dis_write(block));
return esp_efuse_batch_write_commit();
}
err_exit:
esp_efuse_batch_write_cancel();
return err;
}
esp_err_t esp_efuse_write_keys(const esp_efuse_purpose_t purposes[], uint8_t keys[][32], unsigned number_of_keys)
{
esp_err_t err = ESP_FAIL;
if (number_of_keys == 0 || number_of_keys > 2 || keys == NULL || purposes == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_efuse_purpose_t purpose = 0;
esp_efuse_batch_write_begin();
for (unsigned i_key = 0; i_key < number_of_keys; i_key++) {
purpose = purposes[i_key];
ESP_LOGI(TAG, "Writing EFUSE_BLK_KEY0 with purpose %d", purpose);
size_t key_size = (purpose == ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_V2 || purpose == ESP_EFUSE_KEY_PURPOSE_XTS_AES_64_KEY) ? 16 : 32;
ESP_EFUSE_CHK(esp_efuse_write_key(EFUSE_BLK_KEY0, purpose, keys[i_key], key_size));
}
return esp_efuse_batch_write_commit();
err_exit:
ESP_LOGE(TAG, "Failed to write EFUSE_BLK_KEY0 with purpose %d. Can't continue.", purpose);
esp_efuse_batch_write_cancel();
return err;
}
esp_err_t esp_secure_boot_read_key_digests(ets_secure_boot_key_digests_t *trusted_keys)
{
if (trusted_keys == NULL) {
return ESP_FAIL;
}
trusted_keys->key_digests[0] = (const void *)(esp_efuse_utility_get_read_register_address(EFUSE_BLK_KEY0) + 16);
return ESP_OK;
}
@@ -0,0 +1,252 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_efuse.h"
#include "esp_efuse_utility.h"
#include "soc/efuse_periph.h"
#include "assert.h"
#include "sdkconfig.h"
#include "esp_efuse_table.h"
/*
* FE uses eFuse block1.
* SB uses eFuse block2.
* Block3 can be used by customer.
* There are no eFuse key purpose fields (added only virtual key purposes to support key APIs).
*/
static __attribute__((unused)) const char *TAG = "efuse";
/**
* @brief Keys and their attributes are packed into a structure
*/
typedef struct {
const esp_efuse_desc_t** key_rd_dis; /**< Read protection of a key */
const esp_efuse_desc_t** key_wr_dis; /**< Write protection of a key*/
} esp_efuse_keys_t;
const esp_efuse_keys_t s_table[EFUSE_BLK_KEY_MAX - EFUSE_BLK_KEY0] = {
{ESP_EFUSE_RD_DIS_BLK1, ESP_EFUSE_WR_DIS_BLK1},
{ESP_EFUSE_RD_DIS_BLK2, ESP_EFUSE_WR_DIS_BLK2},
{ESP_EFUSE_RD_DIS_BLK3, ESP_EFUSE_WR_DIS_BLK3},
};
// Sets a write protection for the whole block.
esp_err_t esp_efuse_set_write_protect(esp_efuse_block_t blk)
{
if (blk == EFUSE_BLK0 || blk >= EFUSE_BLK_MAX) {
return ESP_ERR_NOT_SUPPORTED;
}
unsigned idx = blk - EFUSE_BLK1;
return esp_efuse_write_field_cnt(s_table[idx].key_wr_dis, 1);
}
// read protect for blk.
esp_err_t esp_efuse_set_read_protect(esp_efuse_block_t blk)
{
if (blk == EFUSE_BLK0 || blk >= EFUSE_BLK_MAX) {
return ESP_ERR_NOT_SUPPORTED;
}
unsigned idx = blk - EFUSE_BLK1;
return esp_efuse_write_field_cnt(s_table[idx].key_rd_dis, 1);
}
// get efuse coding_scheme.
esp_efuse_coding_scheme_t esp_efuse_get_coding_scheme(esp_efuse_block_t blk)
{
esp_efuse_coding_scheme_t scheme;
if (blk == EFUSE_BLK0) {
scheme = EFUSE_CODING_SCHEME_NONE;
} else {
uint32_t coding_scheme = REG_GET_FIELD(EFUSE_BLK0_RDATA6_REG, EFUSE_CODING_SCHEME);
if (coding_scheme == EFUSE_CODING_SCHEME_VAL_NONE ||
coding_scheme == (EFUSE_CODING_SCHEME_VAL_34 | EFUSE_CODING_SCHEME_VAL_REPEAT)) {
scheme = EFUSE_CODING_SCHEME_NONE;
} else if (coding_scheme == EFUSE_CODING_SCHEME_VAL_34) {
scheme = EFUSE_CODING_SCHEME_3_4;
} else {
scheme = EFUSE_CODING_SCHEME_REPEAT;
}
}
return scheme;
}
bool esp_efuse_block_is_empty(esp_efuse_block_t block)
{
unsigned blk_len_bit = 256;
uint32_t key[8];
if (esp_efuse_get_coding_scheme(block) == EFUSE_CODING_SCHEME_3_4) {
blk_len_bit = 192;
}
esp_err_t err = esp_efuse_read_block(block, &key, 0, blk_len_bit);
if (err != ESP_OK) {
return false;
}
unsigned zeros = 0;
for (unsigned i = 0; i < blk_len_bit / 32; ++i) {
if (key[i] == 0) {
++zeros;
}
}
if (zeros == blk_len_bit / 32) {
return true;
}
return false;
}
bool esp_efuse_get_key_dis_read(esp_efuse_block_t block)
{
assert(block >= EFUSE_BLK_KEY0 && block < EFUSE_BLK_KEY_MAX);
unsigned idx = block - EFUSE_BLK_KEY0;
return esp_efuse_read_field_bit(s_table[idx].key_rd_dis);
}
esp_err_t esp_efuse_set_key_dis_read(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return ESP_ERR_INVALID_ARG;
}
unsigned idx = block - EFUSE_BLK_KEY0;
return esp_efuse_write_field_bit(s_table[idx].key_rd_dis);
}
bool esp_efuse_get_key_dis_write(esp_efuse_block_t block)
{
assert(block >= EFUSE_BLK_KEY0 && block < EFUSE_BLK_KEY_MAX);
unsigned idx = block - EFUSE_BLK_KEY0;
return esp_efuse_read_field_bit(s_table[idx].key_wr_dis);
}
esp_err_t esp_efuse_set_key_dis_write(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return ESP_ERR_INVALID_ARG;
}
unsigned idx = block - EFUSE_BLK_KEY0;
return esp_efuse_write_field_bit(s_table[idx].key_wr_dis);
}
bool esp_efuse_key_block_unused(esp_efuse_block_t block)
{
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX) {
return false; // Not a key block
}
if (esp_efuse_get_key_dis_read(block) || esp_efuse_get_key_dis_write(block) ||
!esp_efuse_block_is_empty(block)) {
return false; // Block in use!
}
return true; // Unused
}
esp_efuse_purpose_t esp_efuse_get_key_purpose(esp_efuse_block_t block)
{
esp_efuse_purpose_t ret_purpose;
if (block == EFUSE_BLK0) {
ret_purpose = ESP_EFUSE_KEY_PURPOSE_SYSTEM;
} else if (block == EFUSE_BLK_ENCRYPT_FLASH) {
ret_purpose = ESP_EFUSE_KEY_PURPOSE_FLASH_ENCRYPTION;
} else if (block == EFUSE_BLK_SECURE_BOOT) {
ret_purpose = ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_V2;
} else if (block == EFUSE_BLK3) {
ret_purpose = ESP_EFUSE_KEY_PURPOSE_USER;
} else {
ret_purpose = ESP_EFUSE_KEY_PURPOSE_MAX;
}
return ret_purpose;
}
bool esp_efuse_get_keypurpose_dis_write(esp_efuse_block_t block)
{
(void)block;
return true;
}
bool esp_efuse_find_purpose(esp_efuse_purpose_t purpose, esp_efuse_block_t *block)
{
esp_efuse_block_t dummy;
if (block == NULL) {
block = &dummy;
}
for (esp_efuse_block_t b = EFUSE_BLK_KEY0; b < EFUSE_BLK_KEY_MAX; b++) {
if (esp_efuse_get_key_purpose(b) == purpose) {
*block = b;
return true;
}
}
return false;
}
esp_err_t esp_efuse_write_key(esp_efuse_block_t block, esp_efuse_purpose_t purpose, const void *key, size_t key_size_bytes)
{
esp_err_t err = ESP_OK;
if (block < EFUSE_BLK_KEY0 || block >= EFUSE_BLK_KEY_MAX || key_size_bytes > 32 || purpose >= ESP_EFUSE_KEY_PURPOSE_MAX
|| esp_efuse_get_key_purpose(block) != purpose) {
return ESP_ERR_INVALID_ARG;
}
esp_efuse_batch_write_begin();
if (!esp_efuse_key_block_unused(block)) {
err = ESP_ERR_INVALID_STATE;
} else {
ESP_EFUSE_CHK(esp_efuse_write_block(block, key, 0, key_size_bytes * 8));
ESP_EFUSE_CHK(esp_efuse_set_key_dis_write(block));
if (purpose == ESP_EFUSE_KEY_PURPOSE_FLASH_ENCRYPTION) {
ESP_EFUSE_CHK(esp_efuse_set_key_dis_read(block));
}
return esp_efuse_batch_write_commit();
}
err_exit:
esp_efuse_batch_write_cancel();
return err;
}
esp_err_t esp_efuse_write_keys(const esp_efuse_purpose_t purposes[], uint8_t keys[][32], unsigned number_of_keys)
{
esp_err_t err = ESP_FAIL;
if (number_of_keys == 0 || number_of_keys > (EFUSE_BLK_KEY_MAX - EFUSE_BLK_KEY0) || keys == NULL || purposes == NULL) {
return ESP_ERR_INVALID_ARG;
}
size_t key_size = 32;
esp_efuse_coding_scheme_t coding_scheme = esp_efuse_get_coding_scheme(EFUSE_BLK_KEY0);
if (coding_scheme == EFUSE_CODING_SCHEME_3_4) {
key_size = 24;
}
esp_efuse_purpose_t purpose = 0;
esp_efuse_block_t block = EFUSE_BLK_KEY0;
esp_efuse_batch_write_begin();
for (unsigned i_key = 0; (block < EFUSE_BLK_KEY_MAX) && (i_key < number_of_keys); block++) {
purpose = purposes[i_key];
if (esp_efuse_get_key_purpose(block) == purpose) {
ESP_LOGI(TAG, "Writing EFUSE_BLK_KEY%d with purpose %d", block - EFUSE_BLK_KEY0, purpose);
ESP_EFUSE_CHK(esp_efuse_write_key(block, purpose, keys[i_key], key_size));
i_key++;
}
}
return esp_efuse_batch_write_commit();
err_exit:
ESP_LOGE(TAG, "Failed to write EFUSE_BLK_KEY%d with purpose %d. Can't continue.", block - EFUSE_BLK_KEY0, purpose);
esp_efuse_batch_write_cancel();
return err;
}
#if CONFIG_ESP32_REV_MIN_3
esp_err_t esp_secure_boot_read_key_digests(ets_secure_boot_key_digests_t *trusted_keys)
{
if (trusted_keys == NULL) {
return ESP_FAIL;
}
trusted_keys->key_digests[0] = (const void *)esp_efuse_utility_get_read_register_address(EFUSE_BLK_SECURE_BOOT);
return ESP_OK;
}
#endif // CONFIG_ESP32_REV_MIN_3