mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
security: Added example for HMAC-based NVS encr-keys protection scheme
This commit is contained in:
parent
72f703ccd4
commit
3aa6f97c72
@ -5,3 +5,11 @@ examples/security/flash_encryption:
|
||||
- if: IDF_TARGET in ["esp32s2", "esp32s3", "esp32c6", "esp32h2", "esp32c2"]
|
||||
temporary: true
|
||||
reason: lack of runners
|
||||
|
||||
examples/security/nvs_encryption_hmac:
|
||||
disable:
|
||||
- if: SOC_HMAC_SUPPORTED != 1
|
||||
disable_test:
|
||||
- if: IDF_TARGET not in ["esp32c3"]
|
||||
temporary: true
|
||||
reason: lack of runners
|
||||
|
6
examples/security/nvs_encryption_hmac/CMakeLists.txt
Normal file
6
examples/security/nvs_encryption_hmac/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(nvs_encryption_hmac)
|
81
examples/security/nvs_encryption_hmac/README.md
Normal file
81
examples/security/nvs_encryption_hmac/README.md
Normal file
@ -0,0 +1,81 @@
|
||||
| Supported Targets | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# NVS Encryption with HMAC-based encryption key protection scheme
|
||||
|
||||
## Overview
|
||||
|
||||
This example demonstrates NVS encryption using the HMAC peripheral, wherein the encryption keys are derived from the HMAC key burnt in eFuse. Since the derivation of the encryption keys occurs at runtime, they are not stored in the flash. Thus, this feature does not require a separate `nvs_keys` partition and _also does not require flash encryption enabled_.
|
||||
|
||||
## How to use the example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
This example can be executed on any development board with a supported Espressif SOC chip - possessing a built-in HMAC peripheral (see `Supported Targets` table above).
|
||||
|
||||
### Configure the project
|
||||
|
||||
Before the project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||
|
||||
Open the project configuration menu (`idf.py menuconfig`).
|
||||
|
||||
#### Configure the eFuse key ID storing the HMAC key
|
||||
|
||||
- Set the eFuse key ID storing the HMAC key at `Component config → NVS Security Provider → eFuse key ID storing the HMAC key`.
|
||||
The HMAC key stored at this key block will be used to generate the encryption keys for the default NVS partition (`nvs`), initialised with `nvs_flash_init()`. Note that the example will fail to build without setting this config option to the correct value (the default value is out of range).
|
||||
|
||||
- Users can program their own HMAC key in the configured block before running the example - refer below snippet. The example checks if the configured block is empty or already programmed with an HMAC key - if empty, a new key is generated at runtime and stored in the block, or else the provided key is used. While burning the key prior to flashing the app, please make sure that the config value is set to the eFuse block holding the HMAC key.
|
||||
|
||||
```shell
|
||||
# Burning the HMAC-key in eFuse block 0 - key ID 0
|
||||
espefuse.py -p PORT burn_key BLOCK_KEY0 hmac_key_file.bin HMAC_UP
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run the monitor tool to view the serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type `Ctrl-]`.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
### Example Output
|
||||
|
||||
```log
|
||||
I (300) nvs_sec_provider: NVS Encryption - Registering HMAC-based scheme...
|
||||
I (308) app_start: Starting scheduler on CPU0
|
||||
I (313) main_task: Started on CPU0
|
||||
I (313) main_task: Calling app_main()
|
||||
I (313) example: Initialising the default NVS partition
|
||||
I (333) nvs: NVS partition "nvs" is encrypted.
|
||||
I (603) example: Initialising the custom NVS partition
|
||||
I (613) example: NVS partition "custom_nvs" is encrypted.
|
||||
I (623) example: Key: u8_key | Val: 255
|
||||
I (623) example: Key: i8_key | Val: -128
|
||||
I (623) example: Key: u16_key | Val: 65535
|
||||
I (633) example: Key: u32_key | Val: 4294967295
|
||||
I (633) example: Key: i32_key | Val: -2147483648
|
||||
I (643) example: Key: str_key | Val: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Fusce quis risus justo.
|
||||
Suspendisse egestas in nisi sit amet auctor.
|
||||
Pellentesque rhoncus dictum sodales.
|
||||
In justo erat, viverra at interdum eget, interdum vel dui.
|
||||
|
||||
I (663) custom_nvs: 0x3ffc5f5c fe ff ff ff 00 00 00 00 fe ff ff ff ff ff ff ff |................|
|
||||
I (673) custom_nvs: 0x3ffc5f6c ff ff ff ff ff ff ff ff ff ff ff ff 84 2d ba b9 |.............-..|
|
||||
I (683) custom_nvs: 0x3ffc5f7c aa aa aa fa ff ff ff ff ff ff ff ff ff ff ff ff |................|
|
||||
I (693) custom_nvs: 0x3ffc5f8c ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
|
||||
I (703) custom_nvs: 0x3ffc5f9c ca 8b c3 bb 2d c2 33 d6 6b d4 a7 3d 31 0e 9c 36 |....-.3.k..=1..6|
|
||||
I (713) custom_nvs: 0x3ffc5fac 39 7f bc d4 5c 6d f8 98 de 0a 90 50 21 23 ff 04 |9...\m.....P!#..|
|
||||
I (723) custom_nvs: 0x3ffc5fbc ce f9 23 6f 2c d6 07 08 2d 0e d2 f2 a5 af 5a 2e |..#o,...-.....Z.|
|
||||
I (733) custom_nvs: 0x3ffc5fcc c9 61 bd fc 96 fc 12 87 1b 8c cb fb 51 2c ed a2 |.a..........Q,..|
|
||||
...
|
||||
...
|
||||
...
|
||||
I (1133) custom_nvs: 0x3ffc624c ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
|
||||
I (1143) main_task: Returned from app_main()
|
||||
```
|
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
INCLUDE_DIRS ".")
|
205
examples/security/nvs_encryption_hmac/main/main.c
Normal file
205
examples/security/nvs_encryption_hmac/main/main.c
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* NVS Encryption with HMAC-based encryption key protection scheme example
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "esp_flash.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_hmac.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs_sec_provider.h"
|
||||
|
||||
#define CUSTOM_NVS_PART_LABEL "custom_nvs"
|
||||
#define CUSTOM_NVS_PART_NAMESPACE "storage"
|
||||
#define CUSTOM_NVS_PART_DUMP_SIZE (512 + 16)
|
||||
|
||||
static const char* str_val = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n"
|
||||
"Fusce quis risus justo.\n"
|
||||
"Suspendisse egestas in nisi sit amet auctor.\n"
|
||||
"Pellentesque rhoncus dictum sodales.\n"
|
||||
"In justo erat, viverra at interdum eget, interdum vel dui.\n";
|
||||
|
||||
static const char* TAG = "example";
|
||||
|
||||
static esp_err_t example_custom_nvs_part_init(const char *label)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
#if defined(CONFIG_NVS_ENCRYPTION) && defined(CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC)
|
||||
nvs_sec_cfg_t cfg = {};
|
||||
nvs_sec_scheme_t *sec_scheme_handle = NULL;
|
||||
|
||||
nvs_sec_config_hmac_t sec_scheme_cfg = NVS_SEC_PROVIDER_CFG_HMAC_DEFAULT();
|
||||
|
||||
ret = nvs_sec_provider_register_hmac(&sec_scheme_cfg, &sec_scheme_handle);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nvs_flash_read_security_cfg_v2(sec_scheme_handle, &cfg);
|
||||
if (ret != ESP_OK) {
|
||||
/* We shall not generate keys here as that must have been done in default NVS partition initialization case */
|
||||
ESP_LOGE(TAG, "Failed to read NVS security cfg: [0x%02X] (%s)", ret, esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nvs_flash_secure_init_partition(label, &cfg);
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "NVS partition \"%s\" is encrypted.", label);
|
||||
}
|
||||
memset(&cfg, 0x00, sizeof(nvs_sec_cfg_t));
|
||||
#else
|
||||
ret = nvs_flash_init_partition(label);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t example_custom_nvs_part_write(const char *label, const char *namespace)
|
||||
{
|
||||
nvs_handle_t my_handle;
|
||||
esp_err_t err = ESP_FAIL;
|
||||
|
||||
// Open
|
||||
err = nvs_open_from_partition(label, namespace, NVS_READWRITE, &my_handle);
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// Write
|
||||
err = nvs_set_u8(my_handle, "u8_key", UINT8_MAX);
|
||||
if (err != ESP_OK) goto exit;
|
||||
|
||||
err = nvs_set_i8(my_handle, "i8_key", INT8_MIN);
|
||||
if (err != ESP_OK) goto exit;
|
||||
|
||||
err = nvs_set_u16(my_handle, "u16_key", UINT16_MAX);
|
||||
if (err != ESP_OK) goto exit;
|
||||
|
||||
err = nvs_set_u32(my_handle, "u32_key", UINT32_MAX);
|
||||
if (err != ESP_OK) goto exit;
|
||||
|
||||
err = nvs_set_i32(my_handle, "i32_key", INT32_MIN);
|
||||
if (err != ESP_OK) goto exit;
|
||||
|
||||
err = nvs_set_str(my_handle, "str_key", str_val);
|
||||
if (err != ESP_OK) goto exit;
|
||||
|
||||
// Commit
|
||||
err = nvs_commit(my_handle);
|
||||
if (err != ESP_OK) goto exit;
|
||||
|
||||
exit:
|
||||
// Close
|
||||
nvs_close(my_handle);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t example_custom_nvs_part_read(const char *label, const char *namespace)
|
||||
{
|
||||
nvs_handle_t my_handle;
|
||||
esp_err_t err = ESP_FAIL;
|
||||
|
||||
// Open
|
||||
err = nvs_open_from_partition(label, namespace, NVS_READWRITE, &my_handle);
|
||||
if (err != ESP_OK) return err;
|
||||
|
||||
// Write
|
||||
uint8_t u8_val = 0;
|
||||
err = nvs_get_u8(my_handle, "u8_key", &u8_val);
|
||||
if (err != ESP_OK) goto exit;
|
||||
ESP_LOGI(TAG, "Key: u8_key | Val: %" PRIu8, u8_val);
|
||||
|
||||
int8_t i8_val = 0;
|
||||
err = nvs_get_i8(my_handle, "i8_key", &i8_val);
|
||||
if (err != ESP_OK) goto exit;
|
||||
ESP_LOGI(TAG, "Key: i8_key | Val: %" PRIi8, i8_val);
|
||||
|
||||
uint16_t u16_val = 0;
|
||||
err = nvs_get_u16(my_handle, "u16_key", &u16_val);
|
||||
if (err != ESP_OK) goto exit;
|
||||
ESP_LOGI(TAG, "Key: u16_key | Val: %" PRIu16, u16_val);
|
||||
|
||||
uint32_t u32_val = 0;
|
||||
err = nvs_get_u32(my_handle, "u32_key", &u32_val);
|
||||
if (err != ESP_OK) goto exit;
|
||||
ESP_LOGI(TAG, "Key: u32_key | Val: %" PRIu32, u32_val);
|
||||
|
||||
int32_t i32_val = 0;
|
||||
err = nvs_get_i32(my_handle, "i32_key", &i32_val);
|
||||
if (err != ESP_OK) goto exit;
|
||||
ESP_LOGI(TAG, "Key: i32_key | Val: %" PRIi32, i32_val);
|
||||
|
||||
size_t str_val_len = 0;
|
||||
err = nvs_get_str(my_handle, "str_key", NULL, &str_val_len);
|
||||
if (err != ESP_OK) goto exit;
|
||||
|
||||
char* str_key_val = malloc(str_val_len);
|
||||
assert(str_val);
|
||||
|
||||
err = nvs_get_str(my_handle, "str_key", str_key_val, &str_val_len);
|
||||
if (err != ESP_OK) goto cleanup;
|
||||
ESP_LOGI(TAG, "Key: str_key | Val: %s", str_key_val);
|
||||
|
||||
cleanup:
|
||||
free(str_key_val);
|
||||
exit:
|
||||
// Close
|
||||
nvs_close(my_handle);
|
||||
return err;
|
||||
}
|
||||
|
||||
void dump_custom_nvs_partition(const char *label, size_t len)
|
||||
{
|
||||
const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, label);
|
||||
assert(partition);
|
||||
|
||||
uint8_t *read_data = calloc(len, sizeof(uint8_t));
|
||||
assert(read_data != NULL);
|
||||
|
||||
ESP_ERROR_CHECK(esp_partition_read(partition, 0, read_data, len));
|
||||
ESP_LOG_BUFFER_HEXDUMP(label, read_data, len, ESP_LOG_INFO);
|
||||
|
||||
free(read_data);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initialising the default NVS partition");
|
||||
|
||||
/* Initialising the default NVS partition */
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
|
||||
/* Erasing the custom NVS partition */
|
||||
ESP_ERROR_CHECK(nvs_flash_erase_partition(CUSTOM_NVS_PART_LABEL));
|
||||
|
||||
ESP_LOGI(TAG, "Initialising the custom NVS partition");
|
||||
/* Initialize the custom NVS partition with encryption enabled */
|
||||
esp_err_t ret = example_custom_nvs_part_init(CUSTOM_NVS_PART_LABEL);
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase_partition(CUSTOM_NVS_PART_LABEL));
|
||||
ret = example_custom_nvs_part_init(CUSTOM_NVS_PART_LABEL);
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ret = example_custom_nvs_part_write(CUSTOM_NVS_PART_LABEL, CUSTOM_NVS_PART_NAMESPACE);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to write NVS partition (%s | %s): %s", CUSTOM_NVS_PART_LABEL, CUSTOM_NVS_PART_NAMESPACE, esp_err_to_name(ret));
|
||||
};
|
||||
|
||||
ret = example_custom_nvs_part_read(CUSTOM_NVS_PART_LABEL, CUSTOM_NVS_PART_NAMESPACE);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read NVS partition (%s | %s): %s", CUSTOM_NVS_PART_LABEL, CUSTOM_NVS_PART_NAMESPACE, esp_err_to_name(ret));
|
||||
};
|
||||
|
||||
dump_custom_nvs_partition(CUSTOM_NVS_PART_LABEL, CUSTOM_NVS_PART_DUMP_SIZE);
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, , 24K,
|
||||
factory, app, factory, , 1M,
|
||||
custom_nvs, data, nvs, , 24K,
|
|
@ -0,0 +1,45 @@
|
||||
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import logging
|
||||
import os
|
||||
|
||||
import pytest
|
||||
from pytest_embedded_idf.dut import IdfDut
|
||||
|
||||
STR_KEY_VAL = ['Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
||||
'Fusce quis risus justo.',
|
||||
'Suspendisse egestas in nisi sit amet auctor.',
|
||||
'Pellentesque rhoncus dictum sodales.',
|
||||
'In justo erat, viverra at interdum eget, interdum vel dui.']
|
||||
|
||||
ENCR_TEXT_ARR = ['fe ff ff ff 00 00 00 00 fe ff ff ff ff ff ff ff',
|
||||
'ca 8b c3 bb 2d c2 33 d6 6b d4 a7 3d 31 0e 9c 36',
|
||||
'bd c1 2a 10 87 44 5e 1c 4b 2c 7c 5d ac 97 48 63']
|
||||
|
||||
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.nvs_encr_hmac
|
||||
@pytest.mark.parametrize('config', ['nvs_encr_hmac'], indirect=True)
|
||||
def test_nvs_flash_encr_keys_hmac(dut: IdfDut) -> None:
|
||||
# Logging example binary details
|
||||
binary_file = os.path.join(dut.app.binary_path, 'nvs_encryption_hmac.bin')
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
logging.info('nvs_encryption_hmac_bin_size : {}KB'.format(bin_size // 1024))
|
||||
|
||||
# Start test and verify serial output
|
||||
dut.expect('NVS partition "nvs" is encrypted.', timeout=30)
|
||||
dut.expect('NVS partition "custom_nvs" is encrypted', timeout=30)
|
||||
|
||||
dut.expect('Key: u8_key | Val: 255', timeout=30)
|
||||
dut.expect('Key: i8_key | Val: -128', timeout=30)
|
||||
dut.expect('Key: u16_key | Val: 65535', timeout=30)
|
||||
dut.expect('Key: u32_key | Val: 4294967295', timeout=30)
|
||||
dut.expect('Key: i32_key | Val: -2147483648', timeout=30)
|
||||
|
||||
for string in STR_KEY_VAL:
|
||||
dut.expect(string, timeout=30)
|
||||
|
||||
for encr_txt in ENCR_TEXT_ARR:
|
||||
dut.expect(encr_txt, timeout=30)
|
||||
|
||||
dut.expect('Returned from app_main()', timeout=30)
|
@ -0,0 +1,16 @@
|
||||
# Partition Table
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0x9000
|
||||
|
||||
# NOTE: The runner for this example has flash-encryption enabled
|
||||
# Flash Encryption
|
||||
CONFIG_SECURE_FLASH_ENC_ENABLED=y
|
||||
CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT=y
|
||||
CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC=y
|
||||
CONFIG_SECURE_BOOT_ALLOW_JTAG=y
|
||||
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC=y
|
||||
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC=y
|
||||
CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE=y
|
||||
CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=y
|
||||
|
||||
# NVS Encryption
|
||||
CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID=0
|
7
examples/security/nvs_encryption_hmac/sdkconfig.defaults
Normal file
7
examples/security/nvs_encryption_hmac/sdkconfig.defaults
Normal file
@ -0,0 +1,7 @@
|
||||
# This example uses an extra partition to demonstrate encrypted/non-encrypted reads/writes.
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
|
||||
|
||||
CONFIG_NVS_ENCRYPTION=y
|
||||
CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC=y
|
Loading…
x
Reference in New Issue
Block a user