|
| 1 | +/* |
| 2 | + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | +#include <inttypes.h> |
| 7 | +#include <stdio.h> |
| 8 | +#include <string.h> |
| 9 | +#include <sys/param.h> |
| 10 | + |
| 11 | +#include "esp_err.h" |
| 12 | +#include "esp_flash_encrypt.h" |
| 13 | +#include "esp_partition.h" |
| 14 | +#include "nvs_sec_provider.h" |
| 15 | +#include "unity.h" |
| 16 | + |
| 17 | +#include "nvs_bootloader.h" |
| 18 | + |
| 19 | +static esp_err_t configure_nvs_sec_cfg(nvs_sec_cfg_t *cfg, nvs_sec_scheme_t **sec_scheme_handle) |
| 20 | +{ |
| 21 | + const esp_partition_t* nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); |
| 22 | + TEST_ASSERT(nvs_part && "partition table must have an NVS partition"); |
| 23 | + printf("\n nvs_part size:%" PRId32 "\n", nvs_part->size); |
| 24 | + ESP_ERROR_CHECK(esp_partition_erase_range(nvs_part, 0, nvs_part->size)); |
| 25 | + |
| 26 | +#if CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC |
| 27 | + if (!esp_flash_encryption_enabled()) { |
| 28 | + TEST_IGNORE_MESSAGE("flash encryption disabled, skipping nvs_api tests with encryption enabled"); |
| 29 | + } |
| 30 | + |
| 31 | + extern const char nvs_key_start[] asm("_binary_encryption_keys_bin_start"); |
| 32 | + extern const char nvs_key_end[] asm("_binary_encryption_keys_bin_end"); |
| 33 | + extern const char nvs_data_sch0_start[] asm("_binary_partition_encrypted_bin_start"); |
| 34 | + extern const char nvs_data_sch0_end[] asm("_binary_partition_encrypted_bin_end"); |
| 35 | + |
| 36 | + const esp_partition_t* key_part = esp_partition_find_first( |
| 37 | + ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, NULL); |
| 38 | + |
| 39 | + assert(key_part && "partition table must have a KEY partition"); |
| 40 | + TEST_ASSERT_TRUE((nvs_key_end - nvs_key_start - 1) == key_part->erase_size); |
| 41 | + |
| 42 | + ESP_ERROR_CHECK(esp_partition_erase_range(key_part, 0, key_part->size)); |
| 43 | + |
| 44 | + for (int i = 0; i < key_part->size; i+= key_part->erase_size) { |
| 45 | + ESP_ERROR_CHECK( esp_partition_write(key_part, i, nvs_key_start + i, key_part->erase_size) ); |
| 46 | + } |
| 47 | + |
| 48 | + const int content_size = nvs_data_sch0_end - nvs_data_sch0_start - 1; |
| 49 | + TEST_ASSERT_TRUE((content_size % key_part->erase_size) == 0); |
| 50 | + |
| 51 | + const int size_to_write = MIN(content_size, nvs_part->size); |
| 52 | + for (int i = 0; i < size_to_write; i+= nvs_part->erase_size) { |
| 53 | + ESP_ERROR_CHECK( esp_partition_write(nvs_part, i, nvs_data_sch0_start + i, nvs_part->erase_size) ); |
| 54 | + } |
| 55 | + |
| 56 | + nvs_sec_config_flash_enc_t sec_scheme_cfg = { |
| 57 | + .nvs_keys_part = key_part |
| 58 | + }; |
| 59 | + |
| 60 | + TEST_ESP_OK(nvs_sec_provider_register_flash_enc(&sec_scheme_cfg, sec_scheme_handle)); |
| 61 | + return nvs_flash_read_security_cfg_v2(*sec_scheme_handle, cfg); |
| 62 | + |
| 63 | +#elif SOC_HMAC_SUPPORTED |
| 64 | + extern const char nvs_data_sch1_start[] asm("_binary_partition_encrypted_hmac_bin_start"); |
| 65 | + extern const char nvs_data_sch1_end[] asm("_binary_partition_encrypted_hmac_bin_end"); |
| 66 | + |
| 67 | + const int content_size = nvs_data_sch1_end - nvs_data_sch1_start - 1; |
| 68 | + TEST_ASSERT_TRUE((content_size % nvs_part->erase_size) == 0); |
| 69 | + |
| 70 | + const int size_to_write = MIN(content_size, nvs_part->size); |
| 71 | + for (int i = 0; i < size_to_write; i+= nvs_part->erase_size) { |
| 72 | + ESP_ERROR_CHECK( esp_partition_write(nvs_part, i, nvs_data_sch1_start + i, nvs_part->erase_size) ); |
| 73 | + } |
| 74 | + |
| 75 | +#ifndef CONFIG_NVS_ENCRYPTION |
| 76 | + nvs_sec_config_hmac_t sec_scheme_cfg = { |
| 77 | + .hmac_key_id = HMAC_KEY0, |
| 78 | + }; |
| 79 | +#else |
| 80 | + nvs_sec_config_hmac_t sec_scheme_cfg = NVS_SEC_PROVIDER_CFG_HMAC_DEFAULT(); |
| 81 | +#endif /* CONFIG_NVS_ENCRYPTION */ |
| 82 | + |
| 83 | + TEST_ESP_OK(nvs_sec_provider_register_hmac(&sec_scheme_cfg, sec_scheme_handle)); |
| 84 | + return nvs_flash_read_security_cfg_v2(*sec_scheme_handle, cfg); |
| 85 | +#endif |
| 86 | + |
| 87 | + return ESP_FAIL; |
| 88 | +} |
| 89 | + |
| 90 | +static void restore_nvs_partition(void) |
| 91 | +{ |
| 92 | + const esp_partition_t* nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); |
| 93 | + TEST_ASSERT(nvs_part && "partition table must have an NVS partition"); |
| 94 | + printf("\n nvs_part size:%" PRId32 "\n", nvs_part->size); |
| 95 | + ESP_ERROR_CHECK(esp_partition_erase_range(nvs_part, 0, nvs_part->size)); |
| 96 | + |
| 97 | + extern const char nvs_data_start[] asm("_binary_nvs_partition_bin_start"); |
| 98 | + extern const char nvs_data_end[] asm("_binary_nvs_partition_bin_end"); |
| 99 | + |
| 100 | + const int content_size = nvs_data_end - nvs_data_start - 1; |
| 101 | + TEST_ASSERT_TRUE((content_size % nvs_part->erase_size) == 0); |
| 102 | + |
| 103 | + const int size_to_write = MIN(content_size, nvs_part->size); |
| 104 | + for (int i = 0; i < size_to_write; i+= nvs_part->erase_size) { |
| 105 | + ESP_ERROR_CHECK(esp_partition_write(nvs_part, i, nvs_data_start + i, nvs_part->erase_size)); |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +TEST_CASE("Verify encrypted nvs bootloader read_list result_code and value if bootloader read is successful", "[nvs_encrypted_bootloader]") |
| 110 | +{ |
| 111 | + nvs_sec_cfg_t xts_cfg; |
| 112 | + nvs_sec_scheme_t *sec_scheme_handle = NULL; |
| 113 | + TEST_ESP_OK(configure_nvs_sec_cfg(&xts_cfg, &sec_scheme_handle)); |
| 114 | + |
| 115 | + nvs_bootloader_read_list_t read_list[] = { |
| 116 | +// {namespace_name, key_name, value_type, result_code, value, namespace_index}} |
| 117 | + { .namespace_name = "storage", .key_name = "u8_key", .value_type = NVS_TYPE_U8 }, //0 OK |
| 118 | + { .namespace_name = "storage", .key_name = "u16_key", .value_type = NVS_TYPE_U16 }, //1 OK |
| 119 | + { .namespace_name = "storage", .key_name = "u32_key", .value_type = NVS_TYPE_U32 }, //2 OK |
| 120 | + { .namespace_name = "storage", .key_name = "i32_key", .value_type = NVS_TYPE_I32 }, //3 OK |
| 121 | + { .namespace_name = "storage", .key_name = "i8_key", .value_type = NVS_TYPE_U8 }, //4 Type mismatch |
| 122 | + { .namespace_name = "storage", .key_name = "i16_key", .value_type = NVS_TYPE_I16 }, //5 Not found |
| 123 | + }; |
| 124 | + uint8_t size = sizeof(read_list) / sizeof(read_list[0]); |
| 125 | + |
| 126 | + TEST_ESP_OK(nvs_bootloader_secure_init(&xts_cfg)); |
| 127 | + TEST_ESP_OK(nvs_bootloader_read("nvs", size, read_list)); |
| 128 | + nvs_bootloader_secure_deinit(); |
| 129 | + |
| 130 | + TEST_ASSERT_EQUAL(ESP_OK, read_list[0].result_code); |
| 131 | + TEST_ASSERT_EQUAL(ESP_OK, read_list[1].result_code); |
| 132 | + TEST_ASSERT_EQUAL(ESP_OK, read_list[2].result_code); |
| 133 | + TEST_ASSERT_EQUAL(ESP_OK, read_list[3].result_code); |
| 134 | + TEST_ASSERT_EQUAL(ESP_ERR_NVS_TYPE_MISMATCH, read_list[4].result_code); |
| 135 | + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, read_list[5].result_code); |
| 136 | + |
| 137 | + TEST_ASSERT_EQUAL(255, read_list[0].value.u8_val); |
| 138 | + TEST_ASSERT_EQUAL(65535, read_list[1].value.u16_val); |
| 139 | + TEST_ASSERT_EQUAL(4294967295, read_list[2].value.u32_val); |
| 140 | + TEST_ASSERT_EQUAL(-2147483648, read_list[3].value.i32_val); |
| 141 | + |
| 142 | + TEST_ESP_OK(nvs_sec_provider_deregister(sec_scheme_handle)); |
| 143 | + restore_nvs_partition(); |
| 144 | +} |
| 145 | + |
| 146 | +TEST_CASE("Verify encrypted nvs bootloader read_list result_code if bootloader read fails", "[nvs_encrypted_bootloader]") |
| 147 | +{ |
| 148 | + nvs_sec_cfg_t xts_cfg; |
| 149 | + nvs_sec_scheme_t *sec_scheme_handle = NULL; |
| 150 | + TEST_ESP_OK(configure_nvs_sec_cfg(&xts_cfg, &sec_scheme_handle)); |
| 151 | + |
| 152 | + nvs_bootloader_read_list_t read_list[] = { |
| 153 | +// {namespace_name, key_name, value_type, result_code, value, namespace_index}} |
| 154 | + { .namespace_name = "too_long_namespace", .key_name = "i32_key", .value_type = NVS_TYPE_I32 }, //0 Invalid name |
| 155 | + { .namespace_name = "nvs", .key_name = "too_long_key_name", .value_type = NVS_TYPE_I32 }, //1 Key too long |
| 156 | + { .namespace_name = "nvs", .key_name = "str_key", .value_type = NVS_TYPE_BLOB }, //2 Invalid arg |
| 157 | + { .namespace_name = "nvs", .key_name = "i32_key", .value_type = NVS_TYPE_I32 }, //3 Not found |
| 158 | + }; |
| 159 | + uint8_t size = sizeof(read_list) / sizeof(read_list[0]); |
| 160 | + |
| 161 | + TEST_ESP_OK(nvs_bootloader_secure_init(&xts_cfg)); |
| 162 | + esp_err_t ret = nvs_bootloader_read("nvs", size, read_list); |
| 163 | + |
| 164 | + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, ret); |
| 165 | + TEST_ASSERT_EQUAL(ESP_ERR_NVS_INVALID_NAME, read_list[0].result_code); |
| 166 | + TEST_ASSERT_EQUAL(ESP_ERR_NVS_KEY_TOO_LONG, read_list[1].result_code); |
| 167 | + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, read_list[2].result_code); |
| 168 | + TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, read_list[3].result_code); |
| 169 | + |
| 170 | + nvs_bootloader_secure_deinit(); |
| 171 | + |
| 172 | + TEST_ESP_OK(nvs_sec_provider_deregister(sec_scheme_handle)); |
| 173 | + restore_nvs_partition(); |
| 174 | +} |
| 175 | + |
| 176 | +TEST_CASE("Verify nvs_bootloader_read_encrypted failure cases", "[nvs_encrypted_bootloader]") |
| 177 | +{ |
| 178 | + nvs_sec_cfg_t xts_cfg; |
| 179 | + nvs_sec_scheme_t *sec_scheme_handle = NULL; |
| 180 | + TEST_ESP_OK(configure_nvs_sec_cfg(&xts_cfg, &sec_scheme_handle)); |
| 181 | + |
| 182 | + nvs_bootloader_read_list_t read_list[] = { |
| 183 | +// {namespace_name, key_name, value_type, result_code, value, namespace_index}} |
| 184 | + { "nvs", "i32_key", NVS_TYPE_I32, ESP_OK, {0}, 0} |
| 185 | + }; |
| 186 | + uint8_t size = sizeof(read_list) / sizeof(read_list[0]); |
| 187 | + |
| 188 | + TEST_ESP_OK(nvs_bootloader_secure_init(&xts_cfg)); |
| 189 | + esp_err_t ret = nvs_bootloader_read("nvs_partition_name_too_long", size, read_list); |
| 190 | + TEST_ASSERT_EQUAL(ESP_ERR_NVS_INVALID_NAME, ret); |
| 191 | + |
| 192 | + ret = nvs_bootloader_read("nvs_part", size, read_list); |
| 193 | + TEST_ASSERT_EQUAL(ESP_ERR_NVS_PART_NOT_FOUND, ret); |
| 194 | + |
| 195 | + nvs_bootloader_secure_deinit(); |
| 196 | + TEST_ESP_OK(nvs_sec_provider_deregister(sec_scheme_handle)); |
| 197 | + restore_nvs_partition(); |
| 198 | +} |
0 commit comments