mirror of
https://github.com/alexandrebobkov/ESP-Nodes.git
synced 2025-08-11 01:44:22 +00:00
.
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if SOC_HMAC_SUPPORTED
|
||||
static const char *TAG = "esp_secure_cert_crypto";
|
||||
|
||||
#include "esp_hmac.h"
|
||||
#define SHA256_MD_SIZE 32
|
||||
int esp_pbkdf2_hmac_sha256(hmac_key_id_t hmac_key_id, const unsigned char *salt, size_t salt_len,
|
||||
size_t iteration_count, size_t key_length, unsigned char *output)
|
||||
{
|
||||
int ret = -1;
|
||||
int j;
|
||||
unsigned int i;
|
||||
unsigned char md1[SHA256_MD_SIZE] = {};
|
||||
unsigned char work[SHA256_MD_SIZE] = {};
|
||||
// Considering that we only have SHA256, fixing the MD_SIZE to 32 bytes
|
||||
const size_t MD_SIZE = SHA256_MD_SIZE;
|
||||
size_t use_len;
|
||||
unsigned char *out_p = output;
|
||||
unsigned char counter[4] = {};
|
||||
|
||||
counter[3] = 1;
|
||||
uint8_t *hmac_input;
|
||||
esp_err_t esp_ret = ESP_FAIL;
|
||||
hmac_input = (uint8_t *) heap_caps_calloc(1, salt_len + sizeof(counter) + 1, MALLOC_CAP_INTERNAL);
|
||||
if (hmac_input == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for hmac input");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (key_length) {
|
||||
// U1 ends up in work
|
||||
size_t hmac_input_len = 0;
|
||||
memcpy(hmac_input, salt, salt_len);
|
||||
hmac_input_len = hmac_input_len + salt_len;
|
||||
memcpy(hmac_input + salt_len, counter, sizeof(counter));
|
||||
hmac_input_len = hmac_input_len + sizeof(counter);
|
||||
esp_ret = esp_hmac_calculate(hmac_key_id, hmac_input, hmac_input_len, work);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Could not calculate the HMAC value, returned %04X", esp_ret);
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
memcpy(md1, work, MD_SIZE);
|
||||
|
||||
for (i = 1; i < iteration_count; i++) {
|
||||
// U2 ends up in md1
|
||||
esp_ret = esp_hmac_calculate(hmac_key_id, md1, MD_SIZE, md1);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Could not calculate the HMAC value, returned %04X", esp_ret);
|
||||
ret = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
// U1 xor U2
|
||||
//
|
||||
for (j = 0; j < MD_SIZE; j++) {
|
||||
work[j] ^= md1[j];
|
||||
}
|
||||
}
|
||||
|
||||
use_len = (key_length < MD_SIZE) ? key_length : MD_SIZE;
|
||||
memcpy(out_p, work, use_len);
|
||||
|
||||
key_length -= (uint32_t) use_len;
|
||||
out_p += use_len;
|
||||
|
||||
for (i = 4; i > 0; i--) {
|
||||
if (++counter[i - 1] != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Success
|
||||
ret = 0;
|
||||
cleanup:
|
||||
|
||||
/* Zeroise buffers to clear sensitive data from memory. */
|
||||
free(hmac_input);
|
||||
memset(work, 0, SHA256_MD_SIZE);
|
||||
memset(md1, 0, SHA256_MD_SIZE);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,699 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_crc.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_secure_cert_read.h"
|
||||
#include "esp_secure_cert_config.h"
|
||||
#include "esp_secure_cert_tlv_config.h"
|
||||
#include "esp_secure_cert_tlv_private.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
#if __has_include("esp_idf_version.h")
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include "spi_flash_mmap.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static const char *TAG = "esp_secure_cert";
|
||||
|
||||
typedef enum partition_format {
|
||||
ESP_SECURE_CERT_PF_INVALID = -1,
|
||||
ESP_SECURE_CERT_PF_CUST_FLASH,
|
||||
ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY,
|
||||
ESP_SECURE_CERT_PF_NVS,
|
||||
ESP_SECURE_CERT_PF_TLV,
|
||||
} esp_secure_cert_part_fmt_t;
|
||||
|
||||
static esp_secure_cert_part_fmt_t current_partition_format = ESP_SECURE_CERT_PF_INVALID;
|
||||
static const char *nvs_partition_name;
|
||||
static const char *nvs_namespace_name;
|
||||
|
||||
// In case of esp_secure_cert nvs namespace is same as nvs partition name
|
||||
|
||||
#define NVS_STR 1
|
||||
#define NVS_BLOB 2
|
||||
#define NVS_U8 3
|
||||
#define NVS_U16 4
|
||||
|
||||
/* API to find the esp_secure_cert partition
|
||||
* @params
|
||||
* partition_type The type of partition to find
|
||||
* partition_name The name of the partition to find
|
||||
* partition_format The partition_format of the partition,
|
||||
* The global variable current_partition_format shall
|
||||
* be set to this value if find partition operation
|
||||
* was successful
|
||||
*/
|
||||
static const esp_partition_t *esp_secure_cert_find_partition(esp_partition_type_t partition_type, const char *partition_name, esp_secure_cert_part_fmt_t format)
|
||||
{
|
||||
esp_partition_iterator_t it = esp_partition_find(partition_type, ESP_PARTITION_SUBTYPE_ANY, partition_name);
|
||||
if (it != NULL) {
|
||||
current_partition_format = format;
|
||||
const esp_partition_t *part = esp_partition_get(it);
|
||||
if (part == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to get partition");
|
||||
return NULL;
|
||||
} else {
|
||||
return part;
|
||||
}
|
||||
}
|
||||
// if it reaches here that means it is NULL
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const esp_partition_t *esp_secure_cert_get_partition(void)
|
||||
{
|
||||
static const esp_partition_t *part;
|
||||
if (part != NULL) {
|
||||
return part;
|
||||
}
|
||||
|
||||
// This switch case statement is added for increasing readability
|
||||
// The actual behaviour is a simple fall through
|
||||
current_partition_format = ESP_SECURE_CERT_PF_TLV;
|
||||
|
||||
switch (current_partition_format) {
|
||||
case ESP_SECURE_CERT_PF_TLV:
|
||||
part = esp_secure_cert_find_partition(ESP_SECURE_CERT_CUST_FLASH_PARTITION_TYPE, ESP_SECURE_CERT_TLV_PARTITION_NAME, ESP_SECURE_CERT_PF_TLV);
|
||||
if (part != NULL) {
|
||||
if (esp_secure_cert_is_tlv_partition()) {
|
||||
ESP_LOGI(TAG, "Pre-provisioned partition information:");
|
||||
ESP_LOGI(TAG, "partition format: %s, partition name: %s", "CUST_FLASH_TLV", ESP_SECURE_CERT_PARTITION_NAME);
|
||||
return part;
|
||||
}
|
||||
}
|
||||
// fall through
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH:
|
||||
part = esp_secure_cert_find_partition(ESP_SECURE_CERT_CUST_FLASH_PARTITION_TYPE, ESP_SECURE_CERT_PARTITION_NAME, ESP_SECURE_CERT_PF_CUST_FLASH);
|
||||
if (part != NULL) {
|
||||
ESP_LOGI(TAG, "Pre-provisioned partition information:");
|
||||
ESP_LOGI(TAG, "partition format: %s, partition name: %s", "CUST_FLASH", ESP_SECURE_CERT_PARTITION_NAME);
|
||||
return part;
|
||||
}
|
||||
|
||||
// fall through
|
||||
case ESP_SECURE_CERT_PF_NVS:
|
||||
nvs_partition_name = ESP_SECURE_CERT_PARTITION_NAME;
|
||||
nvs_namespace_name = ESP_SECURE_CERT_PARTITION_NAME;
|
||||
part = esp_secure_cert_find_partition(ESP_SECURE_CERT_NVS_PARTITION_TYPE, nvs_partition_name, ESP_SECURE_CERT_PF_NVS);
|
||||
|
||||
if (part != NULL) {
|
||||
if (nvs_flash_init_partition(nvs_partition_name) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS partition %s was found but inititlization failed", nvs_partition_name);
|
||||
return NULL;
|
||||
}
|
||||
ESP_LOGD(TAG, "Partition found, current format is NVS.");
|
||||
ESP_LOGD(TAG, "NVS partition %s is inititalized", nvs_partition_name);
|
||||
esp_err_t err;
|
||||
nvs_handle_t handle;
|
||||
|
||||
err = nvs_open_from_partition(nvs_partition_name, nvs_namespace_name, NVS_READONLY, &handle);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Pre-provisioned partition information:");
|
||||
ESP_LOGI(TAG, "partition format: %s, partition name: %s, partition namespace: %s", "NVS", nvs_partition_name, nvs_namespace_name);
|
||||
nvs_close(handle);
|
||||
return part;
|
||||
}
|
||||
|
||||
nvs_namespace_name = ESP_SECURE_CERT_LEGACY_PARTITION_NAME;
|
||||
err = nvs_open_from_partition(nvs_partition_name, nvs_namespace_name, NVS_READONLY, &handle);
|
||||
nvs_close(handle);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Pre-provisioned partition information:");
|
||||
ESP_LOGI(TAG, "partition format: %s, partition name: %s, partition namespace: %s", "NVS", nvs_partition_name, nvs_namespace_name);
|
||||
return part;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to open nvs partition %s with namespace: %s", nvs_partition_name, nvs_namespace_name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t err;
|
||||
nvs_handle_t handle;
|
||||
nvs_partition_name = ESP_SECURE_CERT_LEGACY_PARTITION_NAME;
|
||||
nvs_namespace_name = ESP_SECURE_CERT_LEGACY_PARTITION_NAME;
|
||||
part = esp_secure_cert_find_partition(ESP_SECURE_CERT_NVS_PARTITION_TYPE, nvs_partition_name, ESP_SECURE_CERT_PF_NVS);
|
||||
if (part != NULL) {
|
||||
if (nvs_flash_init_partition(nvs_partition_name) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "NVS partition %s was found but inititlization failed", nvs_partition_name);
|
||||
return NULL;
|
||||
}
|
||||
err = nvs_open_from_partition(nvs_partition_name, nvs_namespace_name, NVS_READONLY, &handle);
|
||||
nvs_close(handle);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Pre-provisioned partition information:");
|
||||
ESP_LOGI(TAG, "partition format: %s, partition name: %s, partition namespace: %s", "NVS", nvs_partition_name, nvs_namespace_name);
|
||||
return part;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to open nvs partition %s with namespace: %s", nvs_partition_name, nvs_namespace_name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// fall through
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY:
|
||||
part = esp_secure_cert_find_partition(ESP_SECURE_CERT_CUST_FLASH_PARTITION_TYPE, ESP_SECURE_CERT_LEGACY_PARTITION_NAME, ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY);
|
||||
if (part != NULL) {
|
||||
ESP_LOGI(TAG, "Partition found, current format is cust flash legacy.");
|
||||
ESP_LOGI(TAG, "Current partition format: %s, partition name: %s", "CUST_FLASH_LEGACY", ESP_SECURE_CERT_LEGACY_PARTITION_NAME);
|
||||
return part;
|
||||
}
|
||||
|
||||
// fall through
|
||||
case ESP_SECURE_CERT_PF_INVALID:
|
||||
default:
|
||||
current_partition_format = ESP_SECURE_CERT_PF_INVALID;
|
||||
ESP_LOGE(TAG, "Failed to get esp_secure_cert partition");
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void esp_secure_cert_get_partition_format(void)
|
||||
{
|
||||
if (current_partition_format != ESP_SECURE_CERT_PF_INVALID) {
|
||||
return;
|
||||
}
|
||||
|
||||
const esp_partition_t *part = esp_secure_cert_get_partition();
|
||||
if (part == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to obtain the current partition and partition format");
|
||||
current_partition_format = ESP_SECURE_CERT_PF_INVALID;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int nvs_get(const char *name_space, const char *key, char *value, size_t *len, size_t type)
|
||||
{
|
||||
esp_err_t err;
|
||||
nvs_handle_t handle;
|
||||
|
||||
err = nvs_open_from_partition(nvs_partition_name, name_space, NVS_READONLY, &handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Could not open NVS handle (0x%x)!", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case NVS_STR:
|
||||
err = nvs_get_str(handle, key, value, len);
|
||||
break;
|
||||
case NVS_BLOB:
|
||||
err = nvs_get_blob(handle, key, value, len);
|
||||
break;
|
||||
case NVS_U8:
|
||||
err = nvs_get_u8(handle, key, (uint8_t *)value);
|
||||
break;
|
||||
case NVS_U16:
|
||||
err = nvs_get_u16(handle, key, (uint16_t *)value);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid type of NVS data provided");
|
||||
err = ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error (0x%02X) reading NVS data!", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
nvs_close(handle);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_init_nvs_partition(void)
|
||||
{
|
||||
const esp_partition_t *part = esp_secure_cert_get_partition();
|
||||
if (part == NULL) {
|
||||
ESP_LOGE(TAG, "Could not get partition.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (nvs_flash_init_partition(ESP_SECURE_CERT_PARTITION_NAME) == ESP_OK) {
|
||||
return ESP_OK;
|
||||
} else {
|
||||
return nvs_flash_init_partition(ESP_SECURE_CERT_LEGACY_PARTITION_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t esp_secure_cert_read_raw_flash(const esp_partition_t *partition, uint32_t src_offset, void *dst, uint32_t size)
|
||||
{
|
||||
/* Encrypted partitions need to be read via a cache mapping */
|
||||
const void *buf;
|
||||
spi_flash_mmap_handle_t handle;
|
||||
esp_err_t err;
|
||||
|
||||
err = esp_partition_mmap(partition, src_offset, size, SPI_FLASH_MMAP_DATA, &buf, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
memcpy(dst, buf, size);
|
||||
spi_flash_munmap(handle);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const void *esp_secure_cert_mmap(const esp_partition_t *partition, uint32_t src_offset, uint32_t size)
|
||||
{
|
||||
/* Encrypted partitions need to be read via a cache mapping */
|
||||
const void *buf;
|
||||
spi_flash_mmap_handle_t handle;
|
||||
esp_err_t err;
|
||||
|
||||
err = esp_partition_mmap(partition, src_offset, size, SPI_FLASH_MMAP_DATA, &buf, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return NULL;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static esp_err_t esp_secure_cert_read_metadata(esp_secure_cert_metadata *metadata, size_t offset, const esp_partition_t *part, uint32_t *data_len, uint32_t *data_crc)
|
||||
{
|
||||
esp_err_t err;
|
||||
err = esp_secure_cert_read_raw_flash(part, ESP_SECURE_CERT_METADATA_OFFSET, metadata, sizeof(esp_secure_cert_metadata));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Could not read metadata.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (metadata->magic_word != ESP_SECURE_CERT_METADATA_MAGIC_WORD) {
|
||||
ESP_LOGE(TAG, "Metadata magic word does not match");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
switch (offset) {
|
||||
case ESP_SECURE_CERT_METADATA_OFFSET:
|
||||
*data_len = sizeof(metadata);
|
||||
break;
|
||||
case ESP_SECURE_CERT_DEV_CERT_OFFSET:
|
||||
if (current_partition_format == ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY) {
|
||||
/* In the legacy format the order of dev_cert_len, dev_cert_crc have been interchanged with ca_cert_len and ca_cert_crc */
|
||||
/* Returning the appropriate values here */
|
||||
*data_len = metadata->ca_cert_len;
|
||||
*data_crc = metadata->ca_cert_crc;
|
||||
} else {
|
||||
*data_len = metadata->dev_cert_len;
|
||||
*data_crc = metadata->dev_cert_crc;
|
||||
}
|
||||
break;
|
||||
case ESP_SECURE_CERT_CA_CERT_OFFSET:
|
||||
if (current_partition_format == ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY) {
|
||||
/* In the legacy format the order of dev_cert_len, dev_cert_crc have been interchanged with ca_cert_len and ca_cert_crc */
|
||||
/* Returning the appropriate values here */
|
||||
*data_len = metadata->dev_cert_len;
|
||||
*data_crc = metadata->dev_cert_crc;
|
||||
} else {
|
||||
*data_len = metadata->ca_cert_len;
|
||||
*data_crc = metadata->ca_cert_crc;
|
||||
}
|
||||
break;
|
||||
#ifndef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL
|
||||
case ESP_SECURE_CERT_PRIV_KEY_OFFSET:
|
||||
*data_len = metadata->priv_key_len;
|
||||
*data_crc = metadata->priv_key_crc;
|
||||
break;
|
||||
#else /* !CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */
|
||||
case ESP_SECURE_CERT_CIPHERTEXT_OFFSET:
|
||||
*data_len = metadata->ciphertext_len;
|
||||
*data_crc = metadata->ciphertext_crc;
|
||||
break;
|
||||
case ESP_SECURE_CERT_IV_OFFSET:
|
||||
*data_len = metadata->iv_len;
|
||||
*data_crc = metadata->iv_crc;
|
||||
break;
|
||||
#endif /* CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */
|
||||
default:
|
||||
err = ESP_ERR_INVALID_ARG;
|
||||
ESP_LOGE(TAG, "Invalid offset value given");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t esp_secure_cert_get_addr(size_t offset, char **buffer, uint32_t *len)
|
||||
{
|
||||
esp_err_t err;
|
||||
static esp_secure_cert_metadata metadata;
|
||||
uint32_t data_len = 0;
|
||||
uint32_t data_crc = 0;
|
||||
|
||||
const esp_partition_t *part = esp_secure_cert_get_partition();
|
||||
if (part == NULL) {
|
||||
ESP_LOGE(TAG, "Could not get partition.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
err = esp_secure_cert_read_metadata(&metadata, offset, part, &data_len, &data_crc);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in reading the metadata");
|
||||
return err;
|
||||
}
|
||||
|
||||
*len = data_len;
|
||||
*buffer = (char *)esp_secure_cert_mmap(part, offset, *len);
|
||||
if (buffer == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint32_t read_crc = esp_crc32_le(UINT32_MAX, (const uint8_t * )*buffer, data_len);
|
||||
if (read_crc != data_crc) {
|
||||
ESP_LOGE(TAG, "Data has been tampered");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_get_device_cert(char **buffer, uint32_t *len)
|
||||
{
|
||||
// This API sets the global variable current_partition_format
|
||||
esp_secure_cert_get_partition_format();
|
||||
esp_err_t ret;
|
||||
switch (current_partition_format) {
|
||||
case ESP_SECURE_CERT_PF_TLV:
|
||||
return esp_secure_cert_tlv_get_addr(ESP_SECURE_CERT_DEV_CERT_TLV, ESP_SECURE_CERT_SUBTYPE_MAX, buffer, len);
|
||||
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH:
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY:
|
||||
return esp_secure_cert_get_addr(ESP_SECURE_CERT_DEV_CERT_OFFSET, buffer, len);;
|
||||
|
||||
case ESP_SECURE_CERT_PF_NVS:
|
||||
ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_DEV_CERT, NULL, (size_t *)len, NVS_STR);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get device cert length from nvs");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*buffer = (char *)calloc(1, *len + 1);
|
||||
if (*buffer == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for device cert buffer");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
return nvs_get(nvs_namespace_name, ESP_SECURE_CERT_DEV_CERT, *buffer, (size_t *)len, NVS_STR);
|
||||
|
||||
case ESP_SECURE_CERT_PF_INVALID:
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid flash format");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_free_device_cert(char *buffer)
|
||||
{
|
||||
switch(current_partition_format) {
|
||||
// fall through
|
||||
case ESP_SECURE_CERT_PF_TLV:
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH:
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY:
|
||||
return ESP_OK;
|
||||
break;
|
||||
|
||||
case ESP_SECURE_CERT_PF_NVS:
|
||||
free(buffer);
|
||||
return ESP_OK;
|
||||
break;
|
||||
case ESP_SECURE_CERT_PF_INVALID:
|
||||
default:
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_get_ca_cert(char **buffer, uint32_t *len)
|
||||
{
|
||||
// This API sets the global variable current_partition_format
|
||||
esp_secure_cert_get_partition_format();
|
||||
esp_err_t ret;
|
||||
|
||||
switch (current_partition_format) {
|
||||
case ESP_SECURE_CERT_PF_TLV:
|
||||
return esp_secure_cert_tlv_get_addr(ESP_SECURE_CERT_CA_CERT_TLV, ESP_SECURE_CERT_SUBTYPE_MAX, buffer, len);
|
||||
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH:
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY:
|
||||
return esp_secure_cert_get_addr(ESP_SECURE_CERT_CA_CERT_OFFSET, buffer, len);
|
||||
|
||||
case ESP_SECURE_CERT_PF_NVS:
|
||||
ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_CA_CERT, NULL, (size_t *)len, NVS_STR);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get ca cert length from nvs");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*buffer = (char *)calloc(1, *len + 1);
|
||||
if (*buffer == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for ca cert buffer");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
return nvs_get(nvs_namespace_name, ESP_SECURE_CERT_CA_CERT, *buffer, (size_t *)len, NVS_STR);
|
||||
|
||||
case ESP_SECURE_CERT_PF_INVALID:
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid flash format");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_free_ca_cert(char *buffer)
|
||||
{
|
||||
switch(current_partition_format) {
|
||||
// fall through
|
||||
case ESP_SECURE_CERT_PF_TLV:
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH:
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY:
|
||||
return ESP_OK;
|
||||
break;
|
||||
|
||||
case ESP_SECURE_CERT_PF_NVS:
|
||||
free(buffer);
|
||||
return ESP_OK;
|
||||
break;
|
||||
case ESP_SECURE_CERT_PF_INVALID:
|
||||
default:
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL
|
||||
esp_err_t esp_secure_cert_get_priv_key(char **buffer, uint32_t *len)
|
||||
{
|
||||
|
||||
// This API sets the global variable current_partition_format
|
||||
esp_secure_cert_get_partition_format();
|
||||
esp_err_t ret;
|
||||
|
||||
switch (current_partition_format) {
|
||||
case ESP_SECURE_CERT_PF_TLV:
|
||||
return esp_secure_cert_tlv_get_addr(ESP_SECURE_CERT_PRIV_KEY_TLV, ESP_SECURE_CERT_SUBTYPE_MAX, buffer, len);
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH:
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY:
|
||||
return esp_secure_cert_get_addr(ESP_SECURE_CERT_PRIV_KEY_OFFSET, buffer, len);;
|
||||
|
||||
case ESP_SECURE_CERT_PF_NVS:
|
||||
ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_PRIV_KEY, NULL, (size_t *)len, NVS_STR);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get priv key length from nvs");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*buffer = (char *)calloc(1, *len + 1);
|
||||
if (*buffer == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for priv key buffer");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
return nvs_get(nvs_namespace_name, ESP_SECURE_CERT_PRIV_KEY, *buffer, (size_t *)len, NVS_STR);
|
||||
|
||||
case ESP_SECURE_CERT_PF_INVALID:
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid flash format");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_free_priv_key(char *buffer)
|
||||
{
|
||||
switch(current_partition_format) {
|
||||
// fall through
|
||||
case ESP_SECURE_CERT_PF_TLV:
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH:
|
||||
case ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY:
|
||||
return ESP_OK;
|
||||
break;
|
||||
|
||||
case ESP_SECURE_CERT_PF_NVS:
|
||||
free(buffer);
|
||||
return ESP_OK;
|
||||
break;
|
||||
case ESP_SECURE_CERT_PF_INVALID:
|
||||
default:
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL
|
||||
static esp_err_t esp_secure_cert_read(size_t offset, unsigned char *buffer, uint32_t *len)
|
||||
{
|
||||
esp_err_t err;
|
||||
static esp_secure_cert_metadata metadata;
|
||||
uint32_t data_len = 0;
|
||||
uint32_t data_crc = 0;
|
||||
|
||||
const esp_partition_t *part = esp_secure_cert_get_partition();
|
||||
if (part == NULL) {
|
||||
ESP_LOGE(TAG, "Could not get partition.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
err = esp_secure_cert_read_metadata(&metadata, offset, part, &data_len, &data_crc);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in reading the metadata");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (buffer == NULL) {
|
||||
*len = data_len;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (*len < data_len) {
|
||||
ESP_LOGE(TAG, "Insufficient length of buffer. buffer size: %"PRIu32", required: %"PRIu32"", *len, data_len);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* If the requested offset belongs to the medatada, return the already read metadata */
|
||||
if (offset == ESP_SECURE_CERT_METADATA_OFFSET) {
|
||||
memcpy(buffer, &metadata, sizeof(metadata));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
err = esp_secure_cert_read_raw_flash(part, offset, buffer, data_len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Could not read data.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint32_t read_crc = esp_crc32_le(UINT32_MAX, (const uint8_t * )buffer, data_len);
|
||||
if (read_crc != data_crc) {
|
||||
ESP_LOGE(TAG, "Data has been tampered");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_ds_data_ctx_t *esp_secure_cert_get_ds_ctx(void)
|
||||
{
|
||||
esp_secure_cert_get_partition_format();
|
||||
if (current_partition_format == ESP_SECURE_CERT_PF_TLV) {
|
||||
return esp_secure_cert_tlv_get_ds_ctx();
|
||||
}
|
||||
esp_err_t esp_ret;
|
||||
esp_ds_data_ctx_t *ds_data_ctx;
|
||||
uint32_t len = 0;
|
||||
ds_data_ctx = (esp_ds_data_ctx_t *)heap_caps_malloc(sizeof(esp_ds_data_ctx_t), MALLOC_CAP_INTERNAL);
|
||||
if (ds_data_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Error in allocating memory for esp_ds_data_context");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ds_data_ctx->esp_ds_data = (esp_ds_data_t *)heap_caps_calloc(1, sizeof(esp_ds_data_t), MALLOC_CAP_INTERNAL);
|
||||
if (ds_data_ctx->esp_ds_data == NULL) {
|
||||
ESP_LOGE(TAG, "Could not allocate memory for DS data handle ");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// This API sets the global variable current_partition_format
|
||||
|
||||
if (current_partition_format == ESP_SECURE_CERT_PF_CUST_FLASH || current_partition_format == ESP_SECURE_CERT_PF_CUST_FLASH_LEGACY) {
|
||||
char *buffer;
|
||||
len = 0;
|
||||
esp_ret = esp_secure_cert_get_addr(ESP_SECURE_CERT_CIPHERTEXT_OFFSET, &buffer, &len);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in reading ciphertext");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memcpy((void *)ds_data_ctx->esp_ds_data->c, buffer, len);
|
||||
|
||||
len = 0;
|
||||
esp_ret = esp_secure_cert_get_addr(ESP_SECURE_CERT_IV_OFFSET, &buffer, &len);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in reading initialization vector");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memcpy((void *)ds_data_ctx->esp_ds_data->iv, buffer, len);
|
||||
unsigned char metadata[ESP_SECURE_CERT_METADATA_SIZE] = {};
|
||||
len = sizeof(metadata);
|
||||
esp_err_t err = esp_secure_cert_read(ESP_SECURE_CERT_METADATA_OFFSET, metadata, &len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in reading metadata");
|
||||
goto exit;
|
||||
}
|
||||
ds_data_ctx->rsa_length_bits = ((esp_secure_cert_metadata *)metadata)->rsa_length;
|
||||
ds_data_ctx->efuse_key_id = ((esp_secure_cert_metadata *)metadata)->efuse_key_id;
|
||||
return ds_data_ctx;
|
||||
|
||||
} else if (current_partition_format == ESP_SECURE_CERT_PF_NVS) {
|
||||
len = ESP_DS_C_LEN;
|
||||
esp_ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_CIPHERTEXT, (char *) ds_data_ctx->esp_ds_data->c, (size_t *) &len, NVS_BLOB);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in reading ciphertext");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
len = ESP_DS_IV_LEN;
|
||||
esp_ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_IV, (char *)ds_data_ctx->esp_ds_data->iv, (size_t *)&len, NVS_BLOB);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in reading initialization vector");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
esp_ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_EFUSE_KEY_ID, (void *)&ds_data_ctx->efuse_key_id, 0, NVS_U8);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in reading efuse key id");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
esp_ret = nvs_get(nvs_namespace_name, ESP_SECURE_CERT_RSA_LEN, (void *)&ds_data_ctx->rsa_length_bits, 0, NVS_U16);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in reading rsa key length");
|
||||
goto exit;
|
||||
}
|
||||
return ds_data_ctx;
|
||||
}
|
||||
|
||||
exit:
|
||||
if (ds_data_ctx != NULL) {
|
||||
free(ds_data_ctx->esp_ds_data);
|
||||
}
|
||||
free(ds_data_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void esp_secure_cert_free_ds_ctx(esp_ds_data_ctx_t *ds_ctx)
|
||||
{
|
||||
esp_secure_cert_get_partition_format();
|
||||
if (current_partition_format == ESP_SECURE_CERT_PF_TLV) {
|
||||
esp_secure_cert_tlv_free_ds_ctx(ds_ctx);
|
||||
}
|
||||
|
||||
if (ds_ctx != NULL) {
|
||||
free(ds_ctx->esp_ds_data);
|
||||
}
|
||||
free(ds_ctx);
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,884 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_crc.h"
|
||||
#include "inttypes.h"
|
||||
|
||||
#if __has_include("esp_idf_version.h")
|
||||
#include "esp_idf_version.h"
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
#include "esp_random.h"
|
||||
#else
|
||||
#include "esp_system.h"
|
||||
#endif
|
||||
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_efuse_table.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_fault.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
#include "mbedtls/gcm.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "mbedtls/ecdsa.h"
|
||||
#include "mbedtls/pk.h"
|
||||
#include "mbedtls/version.h"
|
||||
|
||||
#include "esp_secure_cert_read.h"
|
||||
#include "esp_secure_cert_tlv_config.h"
|
||||
#include "esp_secure_cert_tlv_read.h"
|
||||
#include "esp_secure_cert_tlv_private.h"
|
||||
#include "esp_secure_cert_crypto.h"
|
||||
|
||||
#if SOC_HMAC_SUPPORTED
|
||||
#include "esp_hmac.h"
|
||||
#endif
|
||||
|
||||
#if (MBEDTLS_VERSION_NUMBER < 0x03000000)
|
||||
/* mbedtls 2.x backward compatibility */
|
||||
#define MBEDTLS_2X_COMPAT 1
|
||||
/**
|
||||
* Mbedtls-3.0 forward compatibility
|
||||
*/
|
||||
#ifndef MBEDTLS_PRIVATE
|
||||
#define MBEDTLS_PRIVATE(member) member
|
||||
#endif
|
||||
#endif /* (MBEDTLS_VERSION_NUMBER < 0x03000000) */
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include "spi_flash_mmap.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "entropy_poll.h"
|
||||
#else
|
||||
#include "mbedtls/entropy_poll.h"
|
||||
#include "soc/soc_memory_layout.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
static const char *TAG = "esp_secure_cert_tlv";
|
||||
|
||||
#define MIN_ALIGNMENT_REQUIRED 16
|
||||
|
||||
|
||||
#if SOC_HMAC_SUPPORTED
|
||||
static esp_err_t esp_secure_cert_hmac_based_decryption(char *in_buf, uint32_t len, char *output_buf);
|
||||
static esp_err_t esp_secure_cert_gen_ecdsa_key(esp_secure_cert_tlv_subtype_t subtype, char *output_buf, size_t buf_len);
|
||||
#define ESP_SECURE_CERT_ECDSA_DER_KEY_SIZE 121
|
||||
#endif
|
||||
|
||||
/* This is the mininum required flash address alignment in bytes to write to an encrypted flash partition */
|
||||
|
||||
/*
|
||||
* Map the entire esp_secure_cert partition
|
||||
* and return the virtual address.
|
||||
*
|
||||
* @note
|
||||
* The mapping is done only once and function shall
|
||||
* simply return same address in case of successive calls.
|
||||
**/
|
||||
const void *esp_secure_cert_get_mapped_addr(void)
|
||||
{
|
||||
// Once initialized, these variable shall contain valid data till reboot.
|
||||
static const void *esp_secure_cert_mapped_addr;
|
||||
if (esp_secure_cert_mapped_addr) {
|
||||
return esp_secure_cert_mapped_addr;
|
||||
}
|
||||
|
||||
esp_partition_iterator_t it = esp_partition_find(ESP_SECURE_CERT_TLV_PARTITION_TYPE,
|
||||
ESP_PARTITION_SUBTYPE_ANY, ESP_SECURE_CERT_TLV_PARTITION_NAME);
|
||||
if (it == NULL) {
|
||||
ESP_LOGE(TAG, "Partition not found.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const esp_partition_t *partition = esp_partition_get(it);
|
||||
if (partition == NULL) {
|
||||
ESP_LOGE(TAG, "Could not get partition.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Encrypted partitions need to be read via a cache mapping */
|
||||
spi_flash_mmap_handle_t handle;
|
||||
esp_err_t err;
|
||||
|
||||
/* Map the entire partition */
|
||||
err = esp_partition_mmap(partition, 0, partition->size, SPI_FLASH_MMAP_DATA, &esp_secure_cert_mapped_addr, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return NULL;
|
||||
}
|
||||
return esp_secure_cert_mapped_addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the padding length applicable for the given TLV
|
||||
*
|
||||
* @input The pointer to the start of the TLV (TLV Header)
|
||||
* @note The padding length is defined in the design of the TLV.
|
||||
* It says that each TLV data entry should be a multiple of MIN_ALIGNMENT_REQUIRED
|
||||
* The actual tlv data is automatically padded to the nearest multiple of MIN_ALIGNMENT_REQUIRED
|
||||
*/
|
||||
static uint8_t esp_secure_cert_get_padding_length(esp_secure_cert_tlv_header_t *tlv_header)
|
||||
{
|
||||
return ((MIN_ALIGNMENT_REQUIRED - (tlv_header->length % MIN_ALIGNMENT_REQUIRED)) % MIN_ALIGNMENT_REQUIRED);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the total length of the TLV pointed by tlv_header
|
||||
* @input
|
||||
* tlv_header The pointer to the start of the TLV (TLV Header)
|
||||
*
|
||||
* @note
|
||||
* The total length of the TLV consists of following parts
|
||||
* 1) TLV Header
|
||||
* 2) TLV data
|
||||
* 3) Padding
|
||||
* 4) TLV footer
|
||||
*
|
||||
* It is ensured by design that this length shall always be a multiple of MIN_ALIGNMENT_REQUIRED
|
||||
*
|
||||
*/
|
||||
static uint16_t esp_secure_cert_get_tlv_total_length(esp_secure_cert_tlv_header_t *tlv_header)
|
||||
{
|
||||
uint16_t padding_length = esp_secure_cert_get_padding_length(tlv_header);
|
||||
uint16_t total_length = sizeof(esp_secure_cert_tlv_header_t) + tlv_header->length + padding_length + sizeof(esp_secure_cert_tlv_footer_t);
|
||||
return total_length;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify the TLV integrity
|
||||
*
|
||||
* @input
|
||||
* tlv_header The pointer to the start of the TLV (TLV header)
|
||||
*
|
||||
* @note
|
||||
* This API calculates the crc value of header + tlv_data + padding
|
||||
* This value is compared with the value stored in the TLV footer.
|
||||
*
|
||||
* @return
|
||||
* True TLV integrity verified
|
||||
* False TLV intefrity could not be verified
|
||||
*/
|
||||
static bool esp_secure_cert_verify_tlv_integrity(esp_secure_cert_tlv_header_t *tlv_header)
|
||||
{
|
||||
ESP_LOGD(TAG, "Verifying the TLV integrity");
|
||||
if (!(tlv_header->magic == ESP_SECURE_CERT_TLV_MAGIC)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t padding_length = esp_secure_cert_get_padding_length(tlv_header);
|
||||
ESP_LOGD(TAG, "Padding length obtained = %u", padding_length);
|
||||
size_t crc_data_len = sizeof(esp_secure_cert_tlv_header_t) + tlv_header->length + padding_length;
|
||||
uint32_t data_crc = esp_crc32_le(UINT32_MAX, (const uint8_t * )tlv_header, crc_data_len);
|
||||
esp_secure_cert_tlv_footer_t *tlv_footer = (esp_secure_cert_tlv_footer_t *)((void*)tlv_header + crc_data_len);
|
||||
if (tlv_footer->crc != data_crc) {
|
||||
ESP_LOGD(TAG, "Calculated crc = %04X does not match with crc"
|
||||
" read from esp_secure_cert partition = %04X", (unsigned int)data_crc, (unsigned int)tlv_footer->crc);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the offset of tlv structure of given type in the esp_secure_cert partition
|
||||
*
|
||||
* Note: This API also validates the crc of the respective tlv before returning the offset
|
||||
* @input
|
||||
* esp_secure_cert_addr Memory mapped address of the esp_secure_cert partition
|
||||
* type Type of the tlv structure.
|
||||
* for calculating current crc for esp_secure_cert
|
||||
* subtype Subtype of the given tlv structure. If subtype is given as ESP_SECURE_CERT_SUBTYPE_MAX then the entry with highest value of subtype is given as output
|
||||
*
|
||||
* tlv_address Void pointer to store tlv address
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_secure_cert_find_tlv(const void *esp_secure_cert_addr, esp_secure_cert_tlv_type_t type, uint8_t subtype, void **tlv_address)
|
||||
{
|
||||
/* start from the begining of the partition */
|
||||
uint16_t tlv_offset = 0;
|
||||
uint8_t latest_subtype = 0;
|
||||
esp_secure_cert_tlv_header_t *latest_tlv_header = NULL;
|
||||
bool read_latest_tlv = 0;
|
||||
|
||||
if (subtype == ESP_SECURE_CERT_SUBTYPE_MAX) {
|
||||
read_latest_tlv = 1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
esp_secure_cert_tlv_header_t *tlv_header = (esp_secure_cert_tlv_header_t *)(esp_secure_cert_addr + tlv_offset);
|
||||
ESP_LOGD(TAG, "Reading from offset of %d from base of esp_secure_cert", tlv_offset);
|
||||
if (tlv_header->magic != ESP_SECURE_CERT_TLV_MAGIC) {
|
||||
if (read_latest_tlv) {
|
||||
if (latest_tlv_header != NULL) {
|
||||
// Verifying the latest TLV here because we only need to verify the last TLV entry of given subtype
|
||||
// instead of all available entries of the given subtype
|
||||
ESP_LOGD(TAG, "Verify integrity of latest tlv obtained of subtype = %d", latest_subtype);
|
||||
if (esp_secure_cert_verify_tlv_integrity(latest_tlv_header)) {
|
||||
ESP_LOGD(TAG, "tlv structure of type %d and subtype %d found and verified", type, latest_subtype);
|
||||
*tlv_address = (void *)latest_tlv_header;
|
||||
return ESP_OK;
|
||||
} else {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type == ESP_SECURE_CERT_TLV_END) {
|
||||
/* The invalid magic means last tlv read successfully was the last valid tlv structure present,
|
||||
* so send the end address of the tlv.
|
||||
* This address can be used to add a new tlv structure. */
|
||||
*tlv_address = (void *)tlv_header;
|
||||
return ESP_OK;
|
||||
}
|
||||
ESP_LOGD(TAG, "Unable to find tlv of type: %d", type);
|
||||
ESP_LOGD(TAG, "Expected magic byte is %04X, obtained magic byte = %04X", ESP_SECURE_CERT_TLV_MAGIC, (unsigned int) tlv_header->magic);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (((esp_secure_cert_tlv_type_t)tlv_header->type) == type) {
|
||||
if (read_latest_tlv) {
|
||||
ESP_LOGD(TAG, "TLV entry of type: %d and subtype:%d found", tlv_header->type, tlv_header->subtype);
|
||||
ESP_LOGD(TAG, "Continuing to find more recent entry of given subtype");
|
||||
latest_subtype = tlv_header->subtype;
|
||||
// Store the tlv address of the latest entry found with given type
|
||||
latest_tlv_header = (void *)tlv_header;
|
||||
// Dont stop here and keep traversing till last TLV entry
|
||||
} else {
|
||||
if (tlv_header->subtype == subtype) {
|
||||
if (esp_secure_cert_verify_tlv_integrity(tlv_header)) {
|
||||
ESP_LOGD(TAG, "tlv structure of type %d and subtype %d found and verified", type, subtype);
|
||||
*tlv_address = (void *)tlv_header;
|
||||
return ESP_OK;
|
||||
} else {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Move the offset to the start of the next tlv entry
|
||||
tlv_offset = tlv_offset + esp_secure_cert_get_tlv_total_length(tlv_header);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@brief Retrieve the header of a specific ESP Secure Certificate TLV record.
|
||||
This function retrieves the header of a specific ESP Secure Certificate TLV record, identified by the type parameter. The tlv_header parameter is a pointer to a buffer where the header will be stored. The function returns ESP_OK if the header is successfully retrieved, otherwise it returns an error code.
|
||||
@param[in] type The type of the TLV record to retrieve the header for.
|
||||
@param[out] tlv_header The pointer at this location shall be updated with tlv_header pointer value.
|
||||
@return ESP_OK Success
|
||||
ESP_FAIL/
|
||||
relevant error Failure
|
||||
*/
|
||||
static esp_err_t esp_secure_cert_tlv_get_header(esp_secure_cert_tlv_type_t type, uint8_t subtype, esp_secure_cert_tlv_header_t **tlv_header)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
char *esp_secure_cert_addr = (char *)esp_secure_cert_get_mapped_addr();
|
||||
if (esp_secure_cert_addr == NULL) {
|
||||
ESP_LOGE(TAG, "Error in obtaining esp_secure_cert memory mapped address");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
err = esp_secure_cert_find_tlv(esp_secure_cert_addr, type, subtype, (void **)tlv_header);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Could not find the tlv of type %d and subtype %d", type, subtype);
|
||||
return err;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_tlv_get_addr(esp_secure_cert_tlv_type_t type, esp_secure_cert_tlv_subtype_t subtype, char **buffer, uint32_t *len)
|
||||
{
|
||||
esp_err_t err;
|
||||
esp_secure_cert_tlv_header_t *tlv_header = NULL;
|
||||
err = esp_secure_cert_tlv_get_header(type, (uint8_t)subtype, &tlv_header);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Could not find header for TLV type %d and subtype %d", type, subtype);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Special case : We return the end address of the last
|
||||
// valid TLV in this case
|
||||
// and length is set to the total length of valid TLV entries
|
||||
if (type == ESP_SECURE_CERT_TLV_END) {
|
||||
*buffer = (char *)tlv_header;
|
||||
const void *esp_secure_cert_addr = esp_secure_cert_get_mapped_addr();
|
||||
*len = (void*)tlv_header - esp_secure_cert_addr;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
*buffer = (char *)&tlv_header->value;
|
||||
*len = tlv_header->length;
|
||||
|
||||
if (ESP_SECURE_CERT_IS_TLV_ENCRYPTED(tlv_header->flags)) {
|
||||
#if SOC_HMAC_SUPPORTED
|
||||
ESP_LOGD(TAG, "TLV data is encrypted");
|
||||
char *output_buf = (char *)heap_caps_calloc(1, sizeof(char) * (*len - HMAC_ENCRYPTION_TAG_LEN), MALLOC_CAP_INTERNAL);
|
||||
if (output_buf == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
err = esp_secure_cert_hmac_based_decryption(*buffer, *len, output_buf);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to decrypt the data");
|
||||
free(output_buf);
|
||||
return err;
|
||||
}
|
||||
ESP_FAULT_ASSERT(err == ESP_OK);
|
||||
*buffer = output_buf;
|
||||
*len = *len - HMAC_ENCRYPTION_TAG_LEN;
|
||||
#else
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
} else if (ESP_SECURE_CERT_HMAC_ECDSA_KEY_DERIVATION(tlv_header->flags)) {
|
||||
#if SOC_HMAC_SUPPORTED
|
||||
ESP_LOGD(TAG, "ECDSA private key shall be generated with help of HMAC");
|
||||
char *output_buf = (char *)heap_caps_calloc(1, sizeof(char) * (ESP_SECURE_CERT_ECDSA_DER_KEY_SIZE), MALLOC_CAP_INTERNAL);
|
||||
if (output_buf == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
err = esp_secure_cert_gen_ecdsa_key(subtype, output_buf, ESP_SECURE_CERT_ECDSA_DER_KEY_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
free(output_buf);
|
||||
ESP_LOGE(TAG, "Failed to generate ECDSA key, returned %04X", err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_FAULT_ASSERT(err == ESP_OK);
|
||||
*buffer = output_buf;
|
||||
*len = ESP_SECURE_CERT_ECDSA_DER_KEY_SIZE;
|
||||
#else
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
} else {
|
||||
ESP_LOGD(TAG, "TLV data is not encrypted");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_get_tlv_info(esp_secure_cert_tlv_config_t *tlv_config, esp_secure_cert_tlv_info_t *tlv_info)
|
||||
{
|
||||
if (tlv_config == NULL || tlv_info == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
memset(tlv_info, 0, sizeof(esp_secure_cert_tlv_info_t));
|
||||
esp_err_t esp_ret;
|
||||
|
||||
esp_secure_cert_tlv_header_t *tlv_header = NULL;
|
||||
|
||||
// Try to obtain the TLV header
|
||||
esp_ret = esp_secure_cert_tlv_get_header(tlv_config->type, tlv_config->subtype, &tlv_header);
|
||||
if (esp_ret != ESP_OK) {
|
||||
return esp_ret;
|
||||
}
|
||||
|
||||
// Try to obtain the TLV data
|
||||
esp_ret = esp_secure_cert_tlv_get_addr(tlv_config->type, tlv_config->subtype, &tlv_info->data, &tlv_info->length);
|
||||
if (esp_ret != ESP_OK) {
|
||||
tlv_info->data = NULL;
|
||||
tlv_info->length = 0;
|
||||
return esp_ret;
|
||||
}
|
||||
|
||||
// Everything successfully obtained, copy to output tlv_info
|
||||
tlv_info->type = tlv_header->type;
|
||||
tlv_info->subtype = tlv_header->subtype;
|
||||
tlv_info->flags = tlv_header->flags;
|
||||
|
||||
// Special case
|
||||
if (tlv_info->type == ESP_SECURE_CERT_TLV_END) {
|
||||
// In this case the user is requesting the end address
|
||||
// of the TLV entries, invalidate the subtype field
|
||||
// in the tlv_info struct
|
||||
tlv_info->subtype = ESP_SECURE_CERT_SUBTYPE_INVALID;
|
||||
}
|
||||
return esp_ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_free_tlv_info(esp_secure_cert_tlv_info_t *tlv_info)
|
||||
{
|
||||
if (tlv_info) {
|
||||
if (!esp_ptr_in_drom((const void *) tlv_info->data)) {
|
||||
/* Free the buffer only if it is not from the drom section */
|
||||
free(tlv_info->data);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_iterate_to_next_tlv(esp_secure_cert_tlv_iterator_t *tlv_iterator)
|
||||
{
|
||||
if (tlv_iterator == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_secure_cert_tlv_header_t *tlv_header = (esp_secure_cert_tlv_header_t *)tlv_iterator->iterator;
|
||||
if (tlv_header == NULL) {
|
||||
ESP_LOGD(TAG, "tlv_header value is NULL, finding the first TLV entry");
|
||||
tlv_header = (esp_secure_cert_tlv_header_t *) esp_secure_cert_get_mapped_addr();
|
||||
// Verify the TLV entry integrity before returning
|
||||
if (esp_secure_cert_verify_tlv_integrity(tlv_header)) {
|
||||
tlv_iterator->iterator = (void*)tlv_header;
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "The esp_secure_cert partition does not appear to be valid");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the current TLV entry integrity
|
||||
if (!esp_secure_cert_verify_tlv_integrity(tlv_header)) {
|
||||
ESP_LOGE(TAG, "Invalid iterator value provided");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Move to the next TLV entry
|
||||
tlv_header = ((void*) tlv_header + esp_secure_cert_get_tlv_total_length(tlv_header));
|
||||
if (esp_secure_cert_verify_tlv_integrity(tlv_header)) {
|
||||
// Verify the TLV entry integrity before returning
|
||||
tlv_iterator->iterator = (void*)tlv_header;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// We shall come here if the integrity verification
|
||||
// fails above, this means no more valid TLV entries
|
||||
ESP_LOGD(TAG, "Iterator is at the last TLV entry");
|
||||
// Keeping the log level as debug as this
|
||||
// is acceptable failure in some cases
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_get_tlv_info_from_iterator(esp_secure_cert_tlv_iterator_t *tlv_iterator, esp_secure_cert_tlv_info_t *tlv_info)
|
||||
{
|
||||
if (tlv_iterator == NULL || tlv_info == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_secure_cert_tlv_header_t *tlv_header = (esp_secure_cert_tlv_header_t*)tlv_iterator->iterator;
|
||||
if (!esp_secure_cert_verify_tlv_integrity(tlv_header)) {
|
||||
ESP_LOGE(TAG, "Iterator does not point to a valid TLV entry");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_secure_cert_tlv_config_t tlv_config = {};
|
||||
tlv_config.type = tlv_header->type;
|
||||
tlv_config.subtype = tlv_header->subtype;
|
||||
return esp_secure_cert_get_tlv_info(&tlv_config, tlv_info);
|
||||
}
|
||||
|
||||
static void esp_secure_cert_print_tlv_info(esp_secure_cert_tlv_info_t *tlv_info)
|
||||
{
|
||||
if (tlv_info != NULL) {
|
||||
ESP_LOGI(TAG, "Type: %d, Subtype: %d, Data length: %"PRIu32"", tlv_info->type, tlv_info->subtype, tlv_info->length);
|
||||
ESP_LOGI(TAG, "Flags: %X", tlv_info->flags);
|
||||
}
|
||||
}
|
||||
|
||||
void esp_secure_cert_list_tlv_entries(void)
|
||||
{
|
||||
esp_secure_cert_tlv_iterator_t tlv_iterator = {};
|
||||
size_t count = 0;
|
||||
while (esp_secure_cert_iterate_to_next_tlv(&tlv_iterator) == ESP_OK) {
|
||||
esp_secure_cert_tlv_info_t tlv_info;
|
||||
if (esp_secure_cert_get_tlv_info_from_iterator(&tlv_iterator, &tlv_info) == ESP_OK) {
|
||||
count++;
|
||||
printf("\n");
|
||||
ESP_LOGI(TAG, "TLV Entry No: %d", count);
|
||||
esp_secure_cert_print_tlv_info(&tlv_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if SOC_HMAC_SUPPORTED
|
||||
esp_err_t esp_secure_cert_calculate_hmac_encryption_iv(uint8_t *iv)
|
||||
{
|
||||
if (iv == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
const uint32_t iv_message[HMAC_ENCRYPTION_MESSAGE_LEN / 4] = {[0 ... 7] = 0xABCDABCD};
|
||||
esp_err_t esp_ret = ESP_FAIL;
|
||||
esp_efuse_block_t efuse_block = EFUSE_BLK_MAX;
|
||||
if (!esp_efuse_find_purpose(ESP_EFUSE_KEY_PURPOSE_HMAC_UP, &efuse_block)) {
|
||||
ESP_LOGE(TAG, "Failed to get the block with purpose set to HMAC_UP");
|
||||
return ESP_FAIL;;
|
||||
}
|
||||
|
||||
hmac_key_id_t hmac_key_id = efuse_block - (int)EFUSE_BLK_KEY0;
|
||||
uint8_t hmac[HMAC_ENCRYPTION_AES_GCM_KEY_LEN];
|
||||
esp_ret = esp_hmac_calculate(hmac_key_id, (uint8_t *)iv_message, HMAC_ENCRYPTION_MESSAGE_LEN, hmac);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Could not calculate the HMAC value, returned %04X", esp_ret);
|
||||
ESP_LOGE(TAG, "returned %04X", esp_ret);
|
||||
return esp_ret;
|
||||
}
|
||||
memcpy(iv, hmac, HMAC_ENCRYPTION_IV_LEN);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_calculate_hmac_encryption_key(uint8_t *aes_key)
|
||||
{
|
||||
if (aes_key == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
const uint32_t key_message[HMAC_ENCRYPTION_MESSAGE_LEN / 4] = {[0 ... 7] = 0xFFFFFFFF};
|
||||
esp_err_t esp_ret = ESP_FAIL;
|
||||
esp_efuse_block_t efuse_block = EFUSE_BLK_MAX;
|
||||
if (!esp_efuse_find_purpose(ESP_EFUSE_KEY_PURPOSE_HMAC_UP, &efuse_block)) {
|
||||
ESP_LOGE(TAG, "Failed to get the block with purpose set to HMAC_UP");
|
||||
return ESP_FAIL;;
|
||||
}
|
||||
|
||||
hmac_key_id_t hmac_key_id = efuse_block - (int)EFUSE_BLK_KEY0;
|
||||
esp_ret = esp_hmac_calculate(hmac_key_id, (uint8_t *)key_message, HMAC_ENCRYPTION_MESSAGE_LEN, aes_key);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Could not calculate the HMAC value, returned %04X", esp_ret);
|
||||
return esp_ret;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#define HMAC_ENCRYPTION_RANDOM_DELAY_LIMIT 100
|
||||
/*
|
||||
* @info
|
||||
* Decrypt the data encrypted using hmac based encryption
|
||||
*
|
||||
* in_buf This is a pointer in flash that points to the encrypted data
|
||||
* len Length of the encrypted data
|
||||
* output_buf Buffer to write the decrypted data
|
||||
* This must be of size = len - HMAC_ENCRYPTION_TAG_LEN
|
||||
*/
|
||||
static esp_err_t esp_secure_cert_hmac_based_decryption(char *in_buf, uint32_t len, char *output_buf)
|
||||
{
|
||||
esp_err_t esp_ret = ESP_FAIL;
|
||||
int ret = -1;
|
||||
uint8_t aes_gcm_key[HMAC_ENCRYPTION_AES_GCM_KEY_LEN];
|
||||
uint8_t iv[HMAC_ENCRYPTION_IV_LEN];
|
||||
|
||||
esp_ret = esp_secure_cert_calculate_hmac_encryption_iv(iv);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to calculate hmac encryption iv");
|
||||
return esp_ret;
|
||||
}
|
||||
esp_ret = esp_secure_cert_calculate_hmac_encryption_key(aes_gcm_key);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to calculate hmac encryption key");
|
||||
return esp_ret;
|
||||
}
|
||||
|
||||
mbedtls_gcm_context gcm_ctx;
|
||||
mbedtls_gcm_init(&gcm_ctx);
|
||||
ret = mbedtls_gcm_setkey(&gcm_ctx, MBEDTLS_CIPHER_ID_AES, (unsigned char *)aes_gcm_key, HMAC_ENCRYPTION_AES_GCM_KEY_LEN * 8);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failure at mbedtls_gcm_setkey with error code : -0x%04X", -ret);
|
||||
mbedtls_gcm_free(&gcm_ctx);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint32_t rand_delay;
|
||||
rand_delay = esp_random() % HMAC_ENCRYPTION_RANDOM_DELAY_LIMIT;
|
||||
esp_rom_delay_us(rand_delay);
|
||||
|
||||
len = len - HMAC_ENCRYPTION_TAG_LEN;
|
||||
ret = mbedtls_gcm_auth_decrypt(&gcm_ctx, len, iv,
|
||||
HMAC_ENCRYPTION_IV_LEN, NULL, 0,
|
||||
(unsigned char *) (in_buf + len),
|
||||
HMAC_ENCRYPTION_TAG_LEN,
|
||||
(const unsigned char *)(in_buf),
|
||||
(unsigned char *)output_buf);
|
||||
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to decrypt the data, mbedtls_gcm_crypt_and_tag returned %02X", ret);
|
||||
mbedtls_gcm_free(&gcm_ctx);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_FAULT_ASSERT(ret == ESP_OK);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int myrand(void *rng_state, unsigned char *output, size_t len)
|
||||
{
|
||||
size_t olen;
|
||||
(void) olen;
|
||||
return mbedtls_hardware_poll(rng_state, output, len, &olen);
|
||||
}
|
||||
|
||||
/*
|
||||
* The API converts the 256 bit ECDSA key to DER format.
|
||||
* @input
|
||||
* key_buf The readable buffer containing the plaintext key
|
||||
* key_buf_len The length of the key buf in bytes
|
||||
* output_buf The writable buffer to write the DER key
|
||||
* output_buf_len Length of the output buffer
|
||||
*
|
||||
*/
|
||||
static esp_err_t esp_secure_cert_convert_key_to_der(char *key_buf, size_t key_buf_len, char* output_buf, size_t output_buf_len)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
// Convert the private key to der
|
||||
mbedtls_pk_context key;
|
||||
mbedtls_pk_init(&key);
|
||||
ret = mbedtls_pk_setup(&key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to setup pk key, returned %04X", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mbedtls_ecdsa_context *key_ctx = mbedtls_pk_ec(key);
|
||||
ret = mbedtls_ecp_group_load(&key_ctx->MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP256R1);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to load the ecp group, returned %04X", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = mbedtls_mpi_read_binary(&key_ctx->MBEDTLS_PRIVATE(d), (const unsigned char *) key_buf, key_buf_len);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to read binary, returned %04X", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Calculate the public key
|
||||
ret = mbedtls_ecp_mul(&key_ctx->MBEDTLS_PRIVATE(grp), &key_ctx->MBEDTLS_PRIVATE(Q), &key_ctx->MBEDTLS_PRIVATE(d), &key_ctx->MBEDTLS_PRIVATE(grp).G, myrand, NULL);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to generate public key, returned %04X", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Write the private key in DER format
|
||||
ret = mbedtls_pk_write_key_der(&key, (unsigned char *) output_buf, output_buf_len);
|
||||
if (ret != ESP_SECURE_CERT_ECDSA_DER_KEY_SIZE) {
|
||||
ESP_LOGE(TAG, "Failed to write the pem key, returned %04X", ret);
|
||||
goto exit;
|
||||
}
|
||||
ret = ESP_OK;
|
||||
|
||||
exit:
|
||||
mbedtls_pk_free(&key);
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* @info
|
||||
* Generate the ECDSA private key (DER format) with help of the PBKDF2 hmac implementation.
|
||||
* In this case the first eFuse key block with purpose set to HMAC_UP shall be used for generating the private key.
|
||||
* The key shall be generated for the SECP256R1 curve, the length of the key in DER format shall be ESP_SECURE_CERT_ECDSA_DER_KEY_SIZE bytes.
|
||||
* The API assumes that a valid salt is stored in the esp_secure_cert partition, Otherwise the API would return with failure.
|
||||
*
|
||||
* @input
|
||||
* output_buf A writable buffer to store the DER formatted ECDSA private key
|
||||
* buf_len The length of the buffer in bytes. This must be exactly ESP_SECURE_CERT_ECDSA_DER_KEY_SIZE bytes.
|
||||
*
|
||||
*/
|
||||
static esp_err_t esp_secure_cert_gen_ecdsa_key(esp_secure_cert_tlv_subtype_t subtype, char *output_buf, size_t buf_len)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
int ret = 0;
|
||||
if (buf_len != ESP_SECURE_CERT_ECDSA_DER_KEY_SIZE) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// Obtain the salt stored in the esp_secure_cert partition
|
||||
uint8_t *salt = NULL;
|
||||
uint32_t salt_len = 0;
|
||||
err = esp_secure_cert_tlv_get_addr(ESP_SECURE_CERT_HMAC_ECDSA_KEY_SALT, subtype, (void *)&salt, &salt_len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in reading salt, returned %04X", err);
|
||||
}
|
||||
ESP_FAULT_ASSERT(err == ESP_OK);
|
||||
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("SALT", salt, salt_len, ESP_LOG_DEBUG);
|
||||
|
||||
esp_efuse_block_t efuse_block = EFUSE_BLK_KEY_MAX;
|
||||
bool res = esp_efuse_find_purpose(ESP_EFUSE_KEY_PURPOSE_HMAC_UP, &efuse_block);
|
||||
if (!res) {
|
||||
ESP_LOGE(TAG, "Failed to get the block with purpose set to HMAC_UP");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_FAULT_ASSERT(res);
|
||||
|
||||
// Allocate memory for private key
|
||||
char *key_buf = (char *)heap_caps_calloc(1, sizeof(char) * (ESP_SECURE_CERT_DERIVED_ECDSA_KEY_SIZE), MALLOC_CAP_INTERNAL);
|
||||
if (key_buf == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
// Generate the private key
|
||||
ret = esp_pbkdf2_hmac_sha256(efuse_block - (int)EFUSE_BLK_KEY0, salt, salt_len, ESP_SECURE_CERT_KEY_DERIVATION_ITERATION_COUNT, ESP_SECURE_CERT_DERIVED_ECDSA_KEY_SIZE, (unsigned char *)key_buf);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to derive the ECDSA key using HMAC, returned %04X", ret);
|
||||
free(key_buf);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_FAULT_ASSERT(ret == 0);
|
||||
|
||||
err = esp_secure_cert_convert_key_to_der(key_buf, ESP_SECURE_CERT_DERIVED_ECDSA_KEY_SIZE, output_buf, ESP_SECURE_CERT_ECDSA_DER_KEY_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
free(key_buf);
|
||||
free(output_buf);
|
||||
ESP_LOGE(TAG, "Failed to convert the plaintext key to DER format");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
// Free the plaintext private key as it is no longer needed
|
||||
free(key_buf);
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL
|
||||
esp_ds_data_ctx_t *esp_secure_cert_tlv_get_ds_ctx(void)
|
||||
{
|
||||
esp_err_t esp_ret;
|
||||
esp_ds_data_ctx_t *ds_data_ctx;
|
||||
|
||||
ds_data_ctx = (esp_ds_data_ctx_t *)calloc(1, sizeof(esp_ds_data_ctx_t));
|
||||
if (ds_data_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Error in allocating memory for esp_ds_data_context");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
uint32_t len;
|
||||
esp_ds_data_t *esp_ds_data;
|
||||
esp_ret = esp_secure_cert_tlv_get_addr(ESP_SECURE_CERT_DS_DATA_TLV, ESP_SECURE_CERT_SUBTYPE_0, (void *) &esp_ds_data, &len);
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in reading ds_data, returned %04X", esp_ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
esp_ds_data_ctx_t *ds_data_ctx_flash;
|
||||
esp_ret = esp_secure_cert_tlv_get_addr(ESP_SECURE_CERT_DS_CONTEXT_TLV, 0, (void *) &ds_data_ctx_flash, &len);
|
||||
memcpy(ds_data_ctx, ds_data_ctx_flash, len);
|
||||
ds_data_ctx->esp_ds_data = esp_ds_data;
|
||||
if (esp_ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error in reading ds_context, returned %04X", esp_ret);
|
||||
goto exit;
|
||||
}
|
||||
return ds_data_ctx;
|
||||
exit:
|
||||
free(ds_data_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void esp_secure_cert_tlv_free_ds_ctx(esp_ds_data_ctx_t *ds_ctx)
|
||||
{
|
||||
free(ds_ctx);
|
||||
}
|
||||
#endif /* CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */
|
||||
|
||||
bool esp_secure_cert_is_tlv_partition(void)
|
||||
{
|
||||
char *esp_secure_cert_addr = (char *)esp_secure_cert_get_mapped_addr();
|
||||
if (esp_secure_cert_addr == NULL) {
|
||||
return 0;
|
||||
}
|
||||
esp_secure_cert_tlv_header_t *tlv_header = (esp_secure_cert_tlv_header_t *)(esp_secure_cert_addr );
|
||||
if (tlv_header->magic == ESP_SECURE_CERT_TLV_MAGIC) {
|
||||
ESP_LOGD(TAG, "TLV partition identified");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS
|
||||
esp_err_t esp_secure_cert_get_device_cert(char **buffer, uint32_t *len)
|
||||
{
|
||||
return esp_secure_cert_tlv_get_addr(ESP_SECURE_CERT_DEV_CERT_TLV, ESP_SECURE_CERT_SUBTYPE_0, buffer, len);
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_free_device_cert(char *buffer)
|
||||
{
|
||||
(void) buffer; /* nothing to do */
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_get_ca_cert(char **buffer, uint32_t *len)
|
||||
{
|
||||
return esp_secure_cert_tlv_get_addr(ESP_SECURE_CERT_CA_CERT_TLV, ESP_SECURE_CERT_SUBTYPE_0, buffer, len);
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_free_ca_cert(char *buffer)
|
||||
{
|
||||
(void) buffer; /* nothing to do */
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL
|
||||
esp_err_t esp_secure_cert_get_priv_key(char **buffer, uint32_t *len)
|
||||
{
|
||||
return esp_secure_cert_tlv_get_addr(ESP_SECURE_CERT_PRIV_KEY_TLV, ESP_SECURE_CERT_SUBTYPE_0, buffer, len);
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_free_priv_key(char *buffer)
|
||||
{
|
||||
if (!esp_ptr_in_drom((const void *) buffer)) {
|
||||
free(buffer);
|
||||
return ESP_OK;
|
||||
}
|
||||
(void) buffer; /* nothing to do */
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#else /* !CONFIG_ESP_SECURE_CERT_DS_PEIPHERAL */
|
||||
|
||||
esp_ds_data_ctx_t *esp_secure_cert_get_ds_ctx(void)
|
||||
{
|
||||
return esp_secure_cert_tlv_get_ds_ctx();
|
||||
}
|
||||
|
||||
void esp_secure_cert_free_ds_ctx(esp_ds_data_ctx_t *ds_ctx)
|
||||
{
|
||||
esp_secure_cert_tlv_free_ds_ctx(ds_ctx);
|
||||
}
|
||||
#endif /* CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL */
|
||||
|
||||
esp_err_t esp_secure_cert_get_priv_key_type(esp_secure_cert_key_type_t *priv_key_type) {
|
||||
esp_err_t err;
|
||||
if (priv_key_type == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_secure_cert_tlv_header_t *tlv_header = NULL;
|
||||
err = esp_secure_cert_tlv_get_header(ESP_SECURE_CERT_PRIV_KEY_TLV, 0, &tlv_header);
|
||||
if (err != ESP_OK) {
|
||||
*priv_key_type = ESP_SECURE_CERT_INVALID_KEY;
|
||||
ESP_LOGE(TAG, "Could not find header for priv key");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (ESP_SECURE_CERT_HMAC_ECDSA_KEY_DERIVATION(tlv_header->flags)) {
|
||||
*priv_key_type = ESP_SECURE_CERT_HMAC_DERIVED_ECDSA_KEY;
|
||||
} else if (ESP_SECURE_CERT_KEY_ECDSA_PERIPHERAL(tlv_header->flags)) {
|
||||
*priv_key_type = ESP_SECURE_CERT_ECDSA_PERIPHERAL_KEY;
|
||||
} else if (ESP_SECURE_CERT_IS_TLV_ENCRYPTED(tlv_header->flags)) {
|
||||
*priv_key_type = ESP_SECURE_CERT_HMAC_ENCRYPTED_KEY;
|
||||
} else {
|
||||
*priv_key_type = ESP_SECURE_CERT_DEFAULT_FORMAT_KEY;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_cert_get_priv_key_efuse_id(uint8_t *efuse_block_id) {
|
||||
esp_err_t err;
|
||||
esp_secure_cert_tlv_header_t *tlv_header = NULL;
|
||||
if (efuse_block_id == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
err = esp_secure_cert_tlv_get_header(ESP_SECURE_CERT_TLV_SEC_CFG, 0, &tlv_header);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Could not find header for TLV security configurations");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
esp_secure_cert_tlv_sec_cfg_t *tlv_sec_cfg;
|
||||
tlv_sec_cfg = (esp_secure_cert_tlv_sec_cfg_t *) tlv_header->value;
|
||||
*efuse_block_id = tlv_sec_cfg->priv_key_efuse_id;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif /* CONFIG_ESP_SECURE_CERT_SUPPORT_LEGACY_FORMATS */
|
Reference in New Issue
Block a user