mirror of
https://github.com/espressif/esp-idf
synced 2025-03-09 17:19:09 -04:00
Merge branch 'feat/bootloader_nvs_read_encrypted' into 'master'
Support reading encrypted NVS partitions by the bootloader Closes IDF-11791 See merge request espressif/esp-idf!35466
This commit is contained in:
commit
b5ee028273
@ -27,7 +27,7 @@ bootloader_usable_dram_end = 0x3fcdcb70;
|
||||
bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */
|
||||
bootloader_dram_seg_len = 0x5000;
|
||||
bootloader_iram_loader_seg_len = 0x7000;
|
||||
bootloader_iram_seg_len = 0x2000;
|
||||
bootloader_iram_seg_len = 0x2800;
|
||||
|
||||
/* Start of the lower region is determined by region size and the end of the higher region */
|
||||
bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead;
|
||||
|
@ -27,7 +27,7 @@ bootloader_usable_dram_end = 0x3fcdc710;
|
||||
bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */
|
||||
bootloader_dram_seg_len = 0x5000;
|
||||
bootloader_iram_loader_seg_len = 0x7000;
|
||||
bootloader_iram_seg_len = 0x2000;
|
||||
bootloader_iram_seg_len = 0x2800;
|
||||
|
||||
/* Start of the lower region is determined by region size and the end of the higher region */
|
||||
bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead;
|
||||
|
@ -24,7 +24,7 @@ bootloader_usable_dram_end = 0x4085c9a0;
|
||||
bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */
|
||||
bootloader_dram_seg_len = 0x5000;
|
||||
bootloader_iram_loader_seg_len = 0x7000;
|
||||
bootloader_iram_seg_len = 0x2200;
|
||||
bootloader_iram_seg_len = 0x2A00;
|
||||
|
||||
/* Start of the lower region is determined by region size and the end of the higher region */
|
||||
bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead;
|
||||
|
@ -24,7 +24,7 @@ bootloader_usable_dram_end = 0x4087c610;
|
||||
bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */
|
||||
bootloader_dram_seg_len = 0x5000;
|
||||
bootloader_iram_loader_seg_len = 0x7000;
|
||||
bootloader_iram_seg_len = 0x2500;
|
||||
bootloader_iram_seg_len = 0x2D00;
|
||||
|
||||
/* Start of the lower region is determined by region size and the end of the higher region */
|
||||
bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead;
|
||||
|
@ -24,7 +24,7 @@ bootloader_usable_dram_end = 0x4084ca70;
|
||||
bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */
|
||||
bootloader_dram_seg_len = 0x5000;
|
||||
bootloader_iram_loader_seg_len = 0x7000;
|
||||
bootloader_iram_seg_len = 0x2500;
|
||||
bootloader_iram_seg_len = 0x2D00;
|
||||
|
||||
/* Start of the lower region is determined by region size and the end of the higher region */
|
||||
bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead;
|
||||
|
@ -25,7 +25,7 @@ bootloader_usable_dram_end = 0x4084cfd0;
|
||||
bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */
|
||||
bootloader_dram_seg_len = 0x5000;
|
||||
bootloader_iram_loader_seg_len = 0x7000;
|
||||
bootloader_iram_seg_len = 0x2500;
|
||||
bootloader_iram_seg_len = 0x2D00;
|
||||
|
||||
/* Start of the lower region is determined by region size and the end of the higher region */
|
||||
bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead;
|
||||
|
@ -25,7 +25,7 @@ bootloader_usable_dram_end = 0x40849a78;
|
||||
bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */
|
||||
bootloader_dram_seg_len = 0x5000;
|
||||
bootloader_iram_loader_seg_len = 0x7000;
|
||||
bootloader_iram_seg_len = 0x2500;
|
||||
bootloader_iram_seg_len = 0x2D00;
|
||||
|
||||
/* Start of the lower region is determined by region size and the end of the higher region */
|
||||
bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead;
|
||||
|
@ -26,7 +26,7 @@ bootloader_usable_dram_end = 0x4ff3abd0;
|
||||
bootloader_stack_overhead = 0x2000; /* For safety margin between bootloader data section and startup stacks */
|
||||
bootloader_dram_seg_len = 0x5000;
|
||||
bootloader_iram_loader_seg_len = 0x7000;
|
||||
bootloader_iram_seg_len = 0x2000;
|
||||
bootloader_iram_seg_len = 0x2D00;
|
||||
|
||||
/* Start of the lower region is determined by region size and the end of the higher region */
|
||||
bootloader_dram_seg_end = bootloader_usable_dram_end - bootloader_stack_overhead;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
@ -46,9 +46,9 @@ const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, esp_p
|
||||
|
||||
// if everything matches, populate the internal_partition
|
||||
if (partition->type == type
|
||||
&& partition->subtype == subtype
|
||||
&& strncmp((char*) partition->label, label, sizeof(partition->label) - 1) == 0) {
|
||||
|
||||
&& partition->subtype == subtype
|
||||
&& (!label || (label && (strncmp((char*) partition->label, label, sizeof(partition->label) - 1) == 0)))
|
||||
) {
|
||||
ESP_LOGV(TAG, "Matched", partition->type, partition->subtype, partition->label);
|
||||
internal_partition.flash_chip = NULL; //esp_flash_default_chip;
|
||||
internal_partition.type = partition->type;
|
||||
@ -74,6 +74,18 @@ const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, esp_p
|
||||
esp_err_t esp_partition_read(const esp_partition_t *partition,
|
||||
size_t src_offset, void *dst, size_t size)
|
||||
{
|
||||
assert(partition != NULL);
|
||||
if (src_offset > partition->size) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (size > partition->size - src_offset) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (!partition->encrypted) {
|
||||
return bootloader_flash_read(partition->address + src_offset, dst, size, false);
|
||||
}
|
||||
|
||||
const void *buf;
|
||||
|
||||
// log call to mmap
|
||||
|
@ -156,6 +156,13 @@ if(BOOTLOADER_BUILD)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER)
|
||||
rom_linker_script("mbedtls")
|
||||
# For ESP32C2(ECO4), mbedTLS in ROM has been updated to v3.6.0-LTS
|
||||
if(CONFIG_ESP32C2_REV_MIN_FULL GREATER_EQUAL 200)
|
||||
rom_linker_script("mbedtls.eco4")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
else() # Regular app build
|
||||
if(target STREQUAL "esp32")
|
||||
|
@ -289,13 +289,13 @@ function(esptool_py_partition_needs_encryption retencrypted partition_name)
|
||||
# - DATA 0x01
|
||||
# Subtypes:
|
||||
# - ota 0x00
|
||||
# - nvs 0x02
|
||||
# If the partition is an app, an OTA or an NVS partition, then it should
|
||||
# - nvs_keys 0x04
|
||||
# If the partition is an app, an OTA or an NVS keys partition, then it should
|
||||
# be encrypted
|
||||
if(
|
||||
(${type} EQUAL 0) OR
|
||||
(${type} EQUAL 1 AND ${subtype} EQUAL 0) OR
|
||||
(${type} EQUAL 1 AND ${subtype} EQUAL 2)
|
||||
(${type} EQUAL 1 AND ${subtype} EQUAL 4)
|
||||
)
|
||||
set(encrypted TRUE)
|
||||
endif()
|
||||
|
@ -5,6 +5,18 @@ idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
|
||||
if(esp_tee_build)
|
||||
include(${COMPONENT_DIR}/esp_tee/esp_tee_mbedtls.cmake)
|
||||
return()
|
||||
|
||||
elseif(BOOTLOADER_BUILD) # TODO: IDF-11673
|
||||
if(CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER)
|
||||
set(include_dirs "${COMPONENT_DIR}/mbedtls/include"
|
||||
"port/mbedtls_rom")
|
||||
set(srcs "port/mbedtls_rom/mbedtls_rom_osi_bootloader.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "${include_dirs}"
|
||||
PRIV_REQUIRES hal)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT ${IDF_TARGET} STREQUAL "linux")
|
||||
@ -88,7 +100,6 @@ if(CONFIG_MBEDTLS_CERTIFICATE_BUNDLE)
|
||||
"${crt_bundle}")
|
||||
endif()
|
||||
|
||||
|
||||
# Only build mbedtls libraries
|
||||
set(ENABLE_TESTING CACHE BOOL OFF)
|
||||
set(ENABLE_PROGRAMS CACHE BOOL OFF)
|
||||
|
@ -1212,6 +1212,17 @@ menu "mbedTLS"
|
||||
Disabling this config can save some code/rodata size as the error
|
||||
string conversion implementation is replaced with an empty stub.
|
||||
|
||||
config MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER
|
||||
bool "Use ROM implementation of the crypto algorithm in the bootloader"
|
||||
depends on ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB
|
||||
default "n"
|
||||
select MBEDTLS_AES_C
|
||||
help
|
||||
Enable this flag to use mbedtls crypto algorithm from ROM instead of ESP-IDF
|
||||
in case of a bootloader build.
|
||||
Similar to the MBEDTLS_USE_CRYPTO_ROM_IMPL config but enables usage of the
|
||||
mbedtls crypto algorithm from ROM for the bootloader build.
|
||||
|
||||
config MBEDTLS_USE_CRYPTO_ROM_IMPL
|
||||
bool "Use ROM implementation of the crypto algorithm"
|
||||
depends on ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB
|
||||
|
@ -1,11 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "mbedtls/aes.h"
|
||||
#include "mbedtls/asn1.h"
|
||||
#include "mbedtls/asn1write.h"
|
||||
@ -43,6 +44,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef BOOTLOADER_BUILD
|
||||
#if (!defined(CONFIG_MBEDTLS_THREADING_C))
|
||||
#error CONFIG_MBEDTLS_THREADING_C
|
||||
#endif
|
||||
@ -51,6 +53,9 @@ typedef void (*_rom_mbedtls_threading_set_alt_t)(void (*mutex_init)(mbedtls_thre
|
||||
void (*mutex_free)(mbedtls_threading_mutex_t *),
|
||||
int (*mutex_lock)(mbedtls_threading_mutex_t *),
|
||||
int (*mutex_unlock)(mbedtls_threading_mutex_t *));
|
||||
#else /* BOOTLOADER_BUILD */
|
||||
typedef void mbedtls_threading_mutex_t;
|
||||
#endif /* BOOTLOADER_BUILD */
|
||||
|
||||
typedef struct mbedtls_rom_funcs {
|
||||
void (*_rom_mbedtls_aes_init)( mbedtls_aes_context *ctx );
|
||||
@ -659,8 +664,8 @@ typedef struct mbedtls_rom_eco4_funcs {
|
||||
#define STRUCT_OFFSET_CHECK(x, y, z) _Static_assert((offsetof(x,y)==(z)), "The variables type of "#x" before "#y" should be "#z)
|
||||
#define STRUCT_SIZE_CHECK(x, y) _Static_assert((sizeof(x)==(y)), "The sizeof "#x" should be "#y)
|
||||
|
||||
#if (!defined(CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL))
|
||||
#error "CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL"
|
||||
#if (!defined(CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL) && !defined(CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER))
|
||||
#error "Please enable CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL or CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER"
|
||||
#endif
|
||||
|
||||
/* platform_util.c */
|
||||
@ -668,6 +673,7 @@ typedef struct mbedtls_rom_eco4_funcs {
|
||||
#error "MBEDTLS_PLATFORM_ZEROIZE_ALT"
|
||||
#endif
|
||||
|
||||
#ifndef BOOTLOADER_BUILD
|
||||
/* sha1.c */
|
||||
STRUCT_OFFSET_CHECK(mbedtls_sha1_context, total, 0);
|
||||
STRUCT_OFFSET_CHECK(mbedtls_sha1_context, state, 8);
|
||||
@ -788,6 +794,11 @@ STRUCT_OFFSET_CHECK(mbedtls_md5_context, MBEDTLS_PRIVATE(state), 8);
|
||||
STRUCT_OFFSET_CHECK(mbedtls_md5_context, MBEDTLS_PRIVATE(buffer), 24);
|
||||
STRUCT_SIZE_CHECK(mbedtls_md5_context, 88);
|
||||
#endif
|
||||
#endif /* BOOTLOADER_BUILD */
|
||||
|
||||
#if BOOTLOADER_BUILD
|
||||
void mbedtls_rom_osi_functions_init_bootloader(void);
|
||||
#endif /* BOOTLOADER_BUILD */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/chip_revision.h"
|
||||
#include "hal/efuse_hal.h"
|
||||
#include "mbedtls_rom_osi.h"
|
||||
|
||||
/* This structure can be automatically generated by the script with rom.mbedtls.ld. */
|
||||
static const mbedtls_rom_funcs_t mbedtls_rom_funcs_table = {
|
||||
/* Fill the ROM functions into mbedtls rom function table. */
|
||||
/* aes module */
|
||||
._rom_mbedtls_aes_init = mbedtls_aes_init,
|
||||
._rom_mbedtls_aes_free = mbedtls_aes_free,
|
||||
._rom_mbedtls_aes_setkey_enc = mbedtls_aes_setkey_enc,
|
||||
._rom_mbedtls_aes_setkey_dec = mbedtls_aes_setkey_dec,
|
||||
._rom_mbedtls_aes_crypt_ecb = mbedtls_aes_crypt_ecb,
|
||||
._rom_mbedtls_aes_crypt_cbc = mbedtls_aes_crypt_cbc,
|
||||
._rom_mbedtls_internal_aes_encrypt = mbedtls_internal_aes_encrypt,
|
||||
._rom_mbedtls_internal_aes_decrypt = mbedtls_internal_aes_decrypt,
|
||||
};
|
||||
|
||||
/* This structure can be automatically generated by the script with rom.mbedtls.ld. */
|
||||
static const mbedtls_rom_eco4_funcs_t mbedtls_rom_eco4_funcs_table = {
|
||||
/* Fill the ROM functions into mbedtls rom function table. */
|
||||
/* aes module */
|
||||
._rom_mbedtls_aes_init = mbedtls_aes_init,
|
||||
._rom_mbedtls_aes_free = mbedtls_aes_free,
|
||||
._rom_mbedtls_aes_setkey_enc = mbedtls_aes_setkey_enc,
|
||||
._rom_mbedtls_aes_setkey_dec = mbedtls_aes_setkey_dec,
|
||||
._rom_mbedtls_aes_crypt_ecb = mbedtls_aes_crypt_ecb,
|
||||
._rom_mbedtls_aes_crypt_cbc = mbedtls_aes_crypt_cbc,
|
||||
._rom_mbedtls_internal_aes_encrypt = mbedtls_internal_aes_encrypt,
|
||||
._rom_mbedtls_internal_aes_decrypt = mbedtls_internal_aes_decrypt,
|
||||
|
||||
._rom_mbedtls_aes_xts_init = mbedtls_aes_xts_init,
|
||||
._rom_mbedtls_aes_xts_free = mbedtls_aes_xts_free,
|
||||
._rom_mbedtls_aes_xts_setkey_enc = mbedtls_aes_xts_setkey_enc,
|
||||
._rom_mbedtls_aes_xts_setkey_dec = mbedtls_aes_xts_setkey_dec,
|
||||
._rom_mbedtls_aes_crypt_xts = mbedtls_aes_crypt_xts,
|
||||
};
|
||||
|
||||
void mbedtls_rom_osi_functions_init_bootloader(void)
|
||||
{
|
||||
// /* Export the rom mbedtls functions table pointer */
|
||||
extern void *mbedtls_rom_osi_funcs_ptr;
|
||||
|
||||
unsigned chip_version = efuse_hal_chip_revision();
|
||||
if ( ESP_CHIP_REV_ABOVE(chip_version, 200) ) {
|
||||
/* Initialize the pointer of rom eco4 mbedtls functions table. */
|
||||
mbedtls_rom_osi_funcs_ptr = (mbedtls_rom_eco4_funcs_t *)&mbedtls_rom_eco4_funcs_table;
|
||||
} else {
|
||||
/* Initialize the pointer of rom mbedtls functions table. */
|
||||
mbedtls_rom_osi_funcs_ptr = (mbedtls_rom_funcs_t *)&mbedtls_rom_funcs_table;
|
||||
}
|
||||
}
|
@ -21,5 +21,10 @@ components/nvs_flash/test_apps_bootloader:
|
||||
- spi_flash
|
||||
- nvs_flash
|
||||
- esp_partition
|
||||
disable:
|
||||
- if: CONFIG_NAME == "nvs_enc_flash_enc" and (SOC_AES_SUPPORTED != 1 and ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB != 1)
|
||||
- if: (CONFIG_NAME == "nvs_enc_hmac" or CONFIG_NAME == "nvs_enc_hmac_no_cfg") and (SOC_HMAC_SUPPORTED != 1 or (SOC_HMAC_SUPPORTED == 1 and (SOC_AES_SUPPORTED != 1 and ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB != 1)))
|
||||
|
||||
reason: As of now in such cases, we do not have any way to perform AES operations in the bootloader build
|
||||
disable_test:
|
||||
- if: IDF_TARGET not in ["esp32", "esp32c3"]
|
||||
|
@ -1,10 +1,14 @@
|
||||
if(BOOTLOADER_BUILD)
|
||||
# bootloader build simplified version
|
||||
set(srcs "src/nvs_bootloader.c")
|
||||
set(srcs "src/nvs_bootloader.c"
|
||||
"src/nvs_bootloader_aes.c"
|
||||
"src/nvs_bootloader_xts_aes.c")
|
||||
|
||||
set(requires "esp_partition")
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
REQUIRES "${requires}"
|
||||
PRIV_REQUIRES "mbedtls"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_INCLUDE_DIRS "private_include"
|
||||
)
|
||||
@ -52,7 +56,9 @@ else()
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC --coverage)
|
||||
target_link_libraries(${COMPONENT_LIB} PUBLIC --coverage)
|
||||
else()
|
||||
target_sources(${COMPONENT_LIB} PRIVATE "src/nvs_encrypted_partition.cpp")
|
||||
target_sources(${COMPONENT_LIB} PRIVATE "src/nvs_encrypted_partition.cpp"
|
||||
"src/nvs_bootloader_aes.c"
|
||||
"src/nvs_bootloader_xts_aes.c")
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE idf::mbedtls)
|
||||
endif()
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -8,6 +8,7 @@
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h" // esp_err_t
|
||||
#include "nvs.h" // nvs entry data types
|
||||
#include "nvs_flash.h" // nvs_sec_cfg_t
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -104,6 +105,37 @@ esp_err_t nvs_bootloader_read(const char* partition_name,
|
||||
const size_t read_list_count,
|
||||
nvs_bootloader_read_list_t read_list[]);
|
||||
|
||||
/**
|
||||
* @brief Initialize internal NVS security context, thus, enabling the NVS bootloader read API to decrypt encrypted NVS partitions
|
||||
*
|
||||
* @note Once `nvs_bootloader_secure_init()` is performed, `nvs_bootloader_read()` can correctly read only those NVS partitions
|
||||
* that are encrypted using the given `nvs_sec_cfg_t` security config, until `nvs_bootloader_secure_deinit()` clears the internal
|
||||
* NVS security context.
|
||||
*
|
||||
* @param sec_cfg NVS security key that would be used for decrypting the NVS partition
|
||||
* @return ESP_OK if security initialization is successful
|
||||
*/
|
||||
esp_err_t nvs_bootloader_secure_init(const nvs_sec_cfg_t *sec_cfg);
|
||||
|
||||
/**
|
||||
* @brief Clear the internal NVS security context
|
||||
*/
|
||||
void nvs_bootloader_secure_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Reads NVS bootloader security configuration set by the specified security scheme
|
||||
*
|
||||
* @param[in] scheme_cfg Security scheme specific configuration
|
||||
*
|
||||
* @param[out] cfg Security configuration (encryption keys)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK, if cfg was read successfully;
|
||||
* - ESP_ERR_INVALID_ARG, if scheme_cfg or cfg is NULL;
|
||||
* - ESP_FAIL, if the key reading process fails
|
||||
*/
|
||||
esp_err_t nvs_bootloader_read_security_cfg(nvs_sec_scheme_t *scheme_cfg, nvs_sec_cfg_t* cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
36
components/nvs_flash/private_include/nvs_bootloader_aes.h
Normal file
36
components/nvs_flash/private_include/nvs_bootloader_aes.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "soc/soc_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32) || !defined(SOC_AES_SUPPORTED)
|
||||
enum AES_TYPE {
|
||||
AES_ENC,
|
||||
AES_DEC,
|
||||
};
|
||||
|
||||
#if !SOC_AES_SUPPORTED
|
||||
enum AES_BITS {
|
||||
AES128,
|
||||
AES192,
|
||||
AES256
|
||||
};
|
||||
#endif /* !SOC_AES_SUPPORTED */
|
||||
#endif /* CONFIG_IDF_TARGET_ESP32 || !SOC_AES_SUPPORTED */
|
||||
|
||||
#if SOC_AES_SUPPORTED
|
||||
#include "rom/aes.h"
|
||||
|
||||
int nvs_bootloader_aes_crypt_ecb(enum AES_TYPE mode,
|
||||
const unsigned char *key,
|
||||
enum AES_BITS key_bits,
|
||||
const unsigned char input[16],
|
||||
unsigned char output[16]);
|
||||
|
||||
#endif /* SOC_AES_SUPPORTED */
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -9,6 +9,8 @@
|
||||
#include "nvs_constants.h" // NVS_CONST_ENTRY_SIZE and all size related constants shared with cpp implementation of NVS
|
||||
#include "nvs_bootloader.h" // nvs_bootloader_read_list_t and function prototypes
|
||||
#include "esp_partition.h" // esp_partition_t
|
||||
#include "nvs_flash.h" // nvs_sec_cfg_t
|
||||
#include "nvs_bootloader_xts_aes.h" // nvs_bootloader_xts_aes_context
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "nvs_bootloader_aes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief The XTS-AES context-type definition.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t crypt_key[32]; /*!< The AES context to use for AES block
|
||||
encryption or decryption. */
|
||||
uint8_t tweak_key[32]; /*!< The AES context used for tweak
|
||||
computation. */
|
||||
} nvs_bootloader_xts_aes_context;
|
||||
|
||||
/** Invalid key length. */
|
||||
#define NVS_BOOTLOADER_ERR_AES_INVALID_KEY_LENGTH -0x0020
|
||||
/** Invalid data input length. */
|
||||
#define NVS_BOOTLOADER_ERR_AES_INVALID_INPUT_LENGTH -0x0022
|
||||
|
||||
/**
|
||||
* @brief Initializes the specified XTS-AES context.
|
||||
*
|
||||
* @param ctx The XTS-AES context to be initialized. This must not be \c NULL.
|
||||
*/
|
||||
void nvs_bootloader_xts_aes_init(nvs_bootloader_xts_aes_context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Clears the specified XTS-AES context.
|
||||
*
|
||||
* @param ctx The XTS-AES context to clear.
|
||||
* If this is \c NULL, this function does nothing.
|
||||
* Otherwise, the context must have been at least initialized.
|
||||
*/
|
||||
void nvs_bootloader_xts_aes_free(nvs_bootloader_xts_aes_context *ctx);
|
||||
|
||||
/**
|
||||
* @brief Sets the XTS-AES encryption-decryption key
|
||||
*
|
||||
* @param ctx The XTS-AES context to which the key should be bound.
|
||||
* It must be initialized.
|
||||
* @param key The encryption key.
|
||||
* This must be a readable buffer of size \p key_bytes bytes.
|
||||
* @param key_bytes The size of data passed in bits. Valid options are:
|
||||
* <ul><li>128 bits</li>
|
||||
* <li>192 bits</li>
|
||||
* <li>256 bits</li></ul>
|
||||
* @return \c 0 indicating success.
|
||||
*/
|
||||
int nvs_bootloader_xts_aes_setkey(nvs_bootloader_xts_aes_context *ctx,
|
||||
const unsigned char *key,
|
||||
unsigned int key_bytes);
|
||||
|
||||
/**
|
||||
* @brief Performs an XTS-AES encryption or decryption operation for an entire XTS data unit.
|
||||
*
|
||||
* @param ctx The XTS-AES context to use for XTS-AES operation
|
||||
* It must be initialized and bound to a key.
|
||||
* @param mode The AES operation: AES_ENC or AES_DEC.
|
||||
* @param length The length of a data unit in bytes.
|
||||
* @param data_unit The address of the data unit encoded as an array of 16
|
||||
* bytes in little-endian format. For disk encryption, this
|
||||
* is typically the index of the block device sector that
|
||||
* contains the data.
|
||||
* @param input The buffer holding the input data (which is an entire
|
||||
* data unit). This function reads \p length Bytes from \p
|
||||
* input.
|
||||
* @param output The buffer holding the output data (which is an entire
|
||||
* data unit). This function writes \p length Bytes to \p
|
||||
* output.
|
||||
* @return \c 0 on success.
|
||||
* 1 on failure.
|
||||
*/
|
||||
int nvs_bootloader_aes_crypt_xts(nvs_bootloader_xts_aes_context *ctx,
|
||||
enum AES_TYPE mode,
|
||||
size_t length,
|
||||
const unsigned char data_unit[16],
|
||||
const unsigned char *input,
|
||||
unsigned char *output);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -53,6 +53,7 @@ function(nvs_create_partition_image partition csv)
|
||||
if(arg_FLASH_IN_PROJECT)
|
||||
esptool_py_flash_to_partition(flash "${partition}" "${image_file}")
|
||||
add_dependencies(flash nvs_${partition}_bin)
|
||||
add_dependencies(encrypted-flash nvs_${partition}_bin)
|
||||
endif()
|
||||
else()
|
||||
set(message
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -9,95 +9,115 @@
|
||||
#include "nvs_bootloader.h"
|
||||
#include "nvs_bootloader_private.h"
|
||||
#include "esp_assert.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "esp_partition.h"
|
||||
#include "nvs_constants.h"
|
||||
#include <esp_rom_crc.h>
|
||||
|
||||
#if CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER && BOOTLOADER_BUILD
|
||||
#include "mbedtls_rom_osi.h"
|
||||
#endif
|
||||
|
||||
static const char* TAG = "nvs_bootloader";
|
||||
const bool const_is_encrypted = false;
|
||||
|
||||
// Static asserts ensuring that the size of the c structures match NVS physical footprint on the flash
|
||||
ESP_STATIC_ASSERT(sizeof(nvs_bootloader_page_header_t) == NVS_CONST_ENTRY_SIZE, "nvs_bootloader_page_header_t size is not 32 bytes");
|
||||
ESP_STATIC_ASSERT(sizeof(nvs_bootloader_page_entry_states_t) == NVS_CONST_ENTRY_SIZE, "nvs_bootloader_page_entry_states_t size is not 32 bytes");
|
||||
ESP_STATIC_ASSERT(sizeof(nvs_bootloader_single_entry_t) == NVS_CONST_ENTRY_SIZE, "nvs_bootloader_single_entry_t size is not 32 bytes");
|
||||
|
||||
esp_err_t nvs_bootloader_read(const char* partition_name,
|
||||
const size_t read_list_count,
|
||||
nvs_bootloader_read_list_t read_list[])
|
||||
#define NVS_KEY_SIZE 32 // AES-256
|
||||
|
||||
/* Currently we support only single-threaded use-cases of reading encrypted NVS partitions */
|
||||
static nvs_bootloader_xts_aes_context dec_ctx;
|
||||
static bool is_nvs_partition_encrypted = false;
|
||||
|
||||
static esp_err_t decrpyt_data(uint32_t src_offset, void* dst, size_t size)
|
||||
{
|
||||
// decrypt data
|
||||
// sector num, could have been just uint64/32.
|
||||
uint8_t data_unit[16];
|
||||
uint8_t *destination = (uint8_t *)dst;
|
||||
uint32_t relAddr = src_offset;
|
||||
|
||||
memset(data_unit, 0, sizeof(data_unit));
|
||||
memcpy(data_unit, &relAddr, sizeof(relAddr));
|
||||
|
||||
return nvs_bootloader_aes_crypt_xts(&dec_ctx, AES_DEC, size, data_unit, destination, destination);
|
||||
}
|
||||
|
||||
static esp_err_t nvs_bootloader_partition_read_string_value(const esp_partition_t *partition, size_t src_offset, void *block, size_t block_len)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
|
||||
/* For the bootloader build, the esp_partition_read() API internally is calls bootloader_flash_read() that
|
||||
* requires the src_address, length and the destination address to be word aligned.
|
||||
* src_address: NVS keys and values are always stored at a word aligned offset
|
||||
* length: Reading bytes of length divisible by 4 at a time (BOOTLOADER_FLASH_READ_LEN)
|
||||
* destination address: Using a word aligned buffer to read the flash contents (bootloader_flash_read_buffer)
|
||||
*/
|
||||
#define BOOTLOADER_FLASH_READ_LEN 32 // because it matches the below discussed XTS-AES requirements as well
|
||||
|
||||
/*
|
||||
The flow:
|
||||
* When reading an encrypted partition, we implement the splitting method, that is, we read and decrypt data size in the multiples of 16.
|
||||
* This is necessary because of a bug present in the mbedtls_aes_crypt_xts() function wherein "inplace" encryption/decryption
|
||||
* calculation fails if the length of buffers is not a multiple of 16.
|
||||
* Reference: https://github.com/Mbed-TLS/mbedtls/issues/4302
|
||||
*
|
||||
* Thus, in this case we first operate over the aligned down to 16 length of data and then over the remaining data, by copying
|
||||
* it to a buffer of size 16.
|
||||
*
|
||||
* Also, until https://github.com/Mbed-TLS/mbedtls/issues/9827 is resolved, we need to operate over chunks of length 32.
|
||||
*/
|
||||
#define XTS_AES_PROCESS_BLOCK_LEN 32
|
||||
|
||||
1. validate parameters and if there are any, report errors.
|
||||
2. check if the partition exists
|
||||
3. read the partition and browse all NVS pages to figure out whether there is any page in the state of "FREEING"
|
||||
3.1. if there is a page in the state of "FREEING", then reading from the page marked as "ACTIVE" will be skipped
|
||||
4. read entries from pages marked as "FULL" or ("ACTIVE" xor "FREEING") to identify namespace indexes of the requested namespaces
|
||||
5. read the requested entries, same skipping of pages in the state of "ACTIVE" as in step 4
|
||||
if (src_offset & 3 || block_len & 3 || (intptr_t) block & 3
|
||||
|| is_nvs_partition_encrypted
|
||||
) {
|
||||
WORD_ALIGNED_ATTR uint8_t bootloader_flash_read_buffer[BOOTLOADER_FLASH_READ_LEN] = { 0 };
|
||||
|
||||
*/
|
||||
// load input parameters
|
||||
ESP_LOGD(TAG, "nvs_bootloader_read called with partition_name: %s, read_list_count: %u", partition_name, (unsigned)read_list_count);
|
||||
size_t block_data_len = block_len / BOOTLOADER_FLASH_READ_LEN * BOOTLOADER_FLASH_READ_LEN;
|
||||
size_t remaining_data_len = block_len % BOOTLOADER_FLASH_READ_LEN;
|
||||
|
||||
// Placeholder return value, replace with actual error handling
|
||||
esp_err_t ret = ESP_OK;
|
||||
/* Process block data */
|
||||
if (block_data_len > 0) {
|
||||
for (size_t data_processed = 0; data_processed < block_data_len; data_processed += BOOTLOADER_FLASH_READ_LEN) {
|
||||
ret = esp_partition_read(partition, src_offset + data_processed, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Check if the parameters are valid
|
||||
ret = nvs_bootloader_check_parameters(partition_name, read_list_count, read_list);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGD(TAG, "nvs_bootloader_check_parameters failed");
|
||||
return ret;
|
||||
if (is_nvs_partition_encrypted) {
|
||||
ret = decrpyt_data(src_offset + data_processed, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(block + data_processed, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
/* Process remaining data */
|
||||
if (remaining_data_len) {
|
||||
ret = esp_partition_read(partition, src_offset + block_data_len, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (is_nvs_partition_encrypted) {
|
||||
ret = decrpyt_data(src_offset + block_data_len, bootloader_flash_read_buffer, BOOTLOADER_FLASH_READ_LEN);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(block + block_data_len, bootloader_flash_read_buffer, remaining_data_len);
|
||||
}
|
||||
} else {
|
||||
ret = esp_partition_read(partition, src_offset, block, block_len);
|
||||
}
|
||||
|
||||
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, partition_name);
|
||||
if (partition == NULL) {
|
||||
ESP_LOGV(TAG, "esp_partition_find_first failed");
|
||||
return ESP_ERR_NVS_PART_NOT_FOUND;
|
||||
}
|
||||
|
||||
// log the partition details
|
||||
ESP_LOGV(TAG, "Partition %s found, size is: %" PRIu32 "", partition->label, partition->size);
|
||||
|
||||
// visit pages to get the number of pages in the state of "ACTIVE" and "FREEING"
|
||||
nvs_bootloader_page_visitor_param_get_page_states_t page_states_count = {0};
|
||||
|
||||
ret = nvs_bootloader_visit_pages(partition, nvs_bootloader_page_visitor_get_page_states, (nvs_bootloader_page_visitor_param_t*)&page_states_count);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGV(TAG, "Failed reading page states");
|
||||
return ret;
|
||||
}
|
||||
if (page_states_count.no_freeing_pages > 1) {
|
||||
ESP_LOGV(TAG, "Multiple pages in the state of FREEING");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (page_states_count.no_active_pages > 1) {
|
||||
ESP_LOGV(TAG, "Multiple pages in the state of ACTIVE");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Here we have at most 1 active page and at most 1 freeing page. If there exists a freeing page, we will skip reading from the active page
|
||||
// Visit pages to get the namespace indexes of the requested namespaces
|
||||
nvs_bootloader_page_visitor_param_read_entries_t read_entries = { .skip_active_page = page_states_count.no_freeing_pages > 0, .read_list_count = read_list_count, .read_list = read_list };
|
||||
|
||||
// log the visitor parameters
|
||||
ESP_LOGV(TAG, "nvs_bootloader_read - visiting pages to get namespace indexes");
|
||||
|
||||
ret = nvs_bootloader_visit_pages(partition, nvs_bootloader_page_visitor_get_namespaces, (nvs_bootloader_page_visitor_param_t*) &read_entries);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGV(TAG, "nvs_bootloader_page_visitor_get_namespaces failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// log the visitor parameters
|
||||
ESP_LOGV(TAG, "nvs_bootloader_read - visiting pages to get key - value pairs");
|
||||
|
||||
// Visit pages to read the requested key - value pairs
|
||||
ret = nvs_bootloader_visit_pages(partition, nvs_bootloader_page_visitor_get_key_value_pairs, (nvs_bootloader_page_visitor_param_t*) &read_entries);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -215,7 +235,7 @@ esp_err_t nvs_bootloader_page_visitor_get_namespaces(nvs_bootloader_page_visitor
|
||||
// iterate over all entries with state written
|
||||
// if the entry is namespace entry, then iterate the read_list and populate the namespace index by matching the namespace name
|
||||
uint8_t start_index = 0;
|
||||
nvs_bootloader_single_entry_t item = {0};
|
||||
WORD_ALIGNED_ATTR nvs_bootloader_single_entry_t item = {0};
|
||||
|
||||
// repeat finding single entry items on the page until all entries are processed or error occurs
|
||||
while (ret == ESP_OK) {
|
||||
@ -275,7 +295,7 @@ esp_err_t nvs_bootloader_page_visitor_get_key_value_pairs(nvs_bootloader_page_vi
|
||||
// if the entry is not a namespace entry, then iterate the read_list and populate the value by matching the namespace index, key name and value type
|
||||
uint8_t next_index = 0; // index of the next entry to read, updated to the next entry by the read_next_single_entry_item
|
||||
uint8_t current_index = 0; // index of the actual entry being processed
|
||||
nvs_bootloader_single_entry_t item = {0};
|
||||
WORD_ALIGNED_ATTR nvs_bootloader_single_entry_t item = {0};
|
||||
|
||||
// repeat finding single entry items on the page until all entries are processed or error occurs
|
||||
while (ret == ESP_OK) {
|
||||
@ -446,7 +466,6 @@ esp_err_t nvs_bootloader_read_next_single_entry_item(const esp_partition_t *part
|
||||
uint8_t *entry_index,
|
||||
nvs_bootloader_single_entry_t *item)
|
||||
{
|
||||
|
||||
// log parameters
|
||||
ESP_LOGV(TAG, "nvs_bootloader_read_next_single_entry_item called with page_index: %u, entry_index: %d", (unsigned)page_index, *entry_index);
|
||||
|
||||
@ -463,11 +482,17 @@ esp_err_t nvs_bootloader_read_next_single_entry_item(const esp_partition_t *part
|
||||
if (NVS_BOOTLOADER_GET_ENTRY_STATE(page_entry_states, *entry_index) == NVS_CONST_ENTRY_STATE_WRITTEN) {
|
||||
|
||||
ret = esp_partition_read(partition, page_index * NVS_CONST_PAGE_SIZE + NVS_CONST_PAGE_ENTRY_DATA_OFFSET + (*entry_index) * NVS_CONST_ENTRY_SIZE, (void*)item, sizeof(nvs_bootloader_single_entry_t));
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (is_nvs_partition_encrypted) {
|
||||
ret = decrpyt_data(page_index * NVS_CONST_PAGE_SIZE + NVS_CONST_PAGE_ENTRY_DATA_OFFSET + (*entry_index) * NVS_CONST_ENTRY_SIZE, (void*)item, sizeof(nvs_bootloader_single_entry_t));
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// only look at item with consistent header
|
||||
if (nvs_bootloader_check_item_header_consistency(item, *entry_index)) {
|
||||
// advance the start index
|
||||
@ -544,9 +569,9 @@ esp_err_t nvs_bootloader_read_entries_block(const esp_partition_t *partition,
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t data_offset = page_index * NVS_CONST_PAGE_SIZE + NVS_CONST_PAGE_ENTRY_DATA_OFFSET + entry_index * NVS_CONST_ENTRY_SIZE ;
|
||||
size_t data_offset = page_index * NVS_CONST_PAGE_SIZE + NVS_CONST_PAGE_ENTRY_DATA_OFFSET + entry_index * NVS_CONST_ENTRY_SIZE;
|
||||
|
||||
return esp_partition_read(partition, data_offset, block, block_len);
|
||||
return nvs_bootloader_partition_read_string_value(partition, data_offset, block, block_len);
|
||||
}
|
||||
|
||||
// validates item's header
|
||||
@ -602,3 +627,116 @@ bool nvs_bootloader_check_item_header_consistency(const nvs_bootloader_single_en
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t nvs_bootloader_read(const char* partition_name,
|
||||
const size_t read_list_count,
|
||||
nvs_bootloader_read_list_t read_list[])
|
||||
{
|
||||
/*
|
||||
The flow:
|
||||
|
||||
1. validate parameters and if there are any, report errors.
|
||||
2. check if the partition exists
|
||||
3. read the partition and browse all NVS pages to figure out whether there is any page in the state of "FREEING"
|
||||
3.1. if there is a page in the state of "FREEING", then reading from the page marked as "ACTIVE" will be skipped
|
||||
4. read entries from pages marked as "FULL" or ("ACTIVE" xor "FREEING") to identify namespace indexes of the requested namespaces
|
||||
5. read the requested entries, same skipping of pages in the state of "ACTIVE" as in step 4
|
||||
|
||||
*/
|
||||
// load input parameters
|
||||
ESP_LOGD(TAG, "nvs_bootloader_read called with partition_name: %s, read_list_count: %u", partition_name, (unsigned)read_list_count);
|
||||
|
||||
// Placeholder return value, replace with actual error handling
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
// Check if the parameters are valid
|
||||
ret = nvs_bootloader_check_parameters(partition_name, read_list_count, read_list);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGD(TAG, "nvs_bootloader_check_parameters failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, partition_name);
|
||||
if (partition == NULL) {
|
||||
ESP_LOGV(TAG, "esp_partition_find_first failed");
|
||||
return ESP_ERR_NVS_PART_NOT_FOUND;
|
||||
}
|
||||
|
||||
// log the partition details
|
||||
ESP_LOGV(TAG, "Partition %s found, size is: %" PRIu32 "", partition->label, partition->size);
|
||||
|
||||
// visit pages to get the number of pages in the state of "ACTIVE" and "FREEING"
|
||||
nvs_bootloader_page_visitor_param_get_page_states_t page_states_count = {0};
|
||||
|
||||
ret = nvs_bootloader_visit_pages(partition, nvs_bootloader_page_visitor_get_page_states, (nvs_bootloader_page_visitor_param_t*)&page_states_count);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGV(TAG, "Failed reading page states");
|
||||
return ret;
|
||||
}
|
||||
if (page_states_count.no_freeing_pages > 1) {
|
||||
ESP_LOGV(TAG, "Multiple pages in the state of FREEING");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (page_states_count.no_active_pages > 1) {
|
||||
ESP_LOGV(TAG, "Multiple pages in the state of ACTIVE");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Here we have at most 1 active page and at most 1 freeing page. If there exists a freeing page, we will skip reading from the active page
|
||||
// Visit pages to get the namespace indexes of the requested namespaces
|
||||
nvs_bootloader_page_visitor_param_read_entries_t read_entries = { .skip_active_page = page_states_count.no_freeing_pages > 0, .read_list_count = read_list_count, .read_list = read_list };
|
||||
|
||||
// log the visitor parameters
|
||||
ESP_LOGV(TAG, "nvs_bootloader_read - visiting pages to get namespace indexes");
|
||||
|
||||
ret = nvs_bootloader_visit_pages(partition, nvs_bootloader_page_visitor_get_namespaces, (nvs_bootloader_page_visitor_param_t*) &read_entries);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGV(TAG, "nvs_bootloader_page_visitor_get_namespaces failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// log the visitor parameters
|
||||
ESP_LOGV(TAG, "nvs_bootloader_read - visiting pages to get key - value pairs");
|
||||
|
||||
// Visit pages to read the requested key - value pairs
|
||||
ret = nvs_bootloader_visit_pages(partition, nvs_bootloader_page_visitor_get_key_value_pairs, (nvs_bootloader_page_visitor_param_t*) &read_entries);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t nvs_bootloader_secure_init(const nvs_sec_cfg_t *sec_cfg)
|
||||
{
|
||||
#if CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER && BOOTLOADER_BUILD
|
||||
// To enable usage of mbedtls APIs form the ROM, we need to initialize the rom mbedtls functions table pointer.
|
||||
// In case of application, a constructor present in the mbedtls component initializes the rom mbedtls functions table pointer during boot up.
|
||||
mbedtls_rom_osi_functions_init_bootloader();
|
||||
#endif /* CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER && BOOTLOADER_BUILD */
|
||||
|
||||
nvs_bootloader_xts_aes_init(&dec_ctx);
|
||||
|
||||
esp_err_t ret = nvs_bootloader_xts_aes_setkey(&dec_ctx, (const uint8_t*) sec_cfg, 2 * NVS_KEY_SIZE);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
is_nvs_partition_encrypted = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void nvs_bootloader_secure_deinit(void)
|
||||
{
|
||||
if (is_nvs_partition_encrypted) {
|
||||
nvs_bootloader_xts_aes_free(&dec_ctx);
|
||||
is_nvs_partition_encrypted = false;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t nvs_bootloader_read_security_cfg(nvs_sec_scheme_t *scheme_cfg, nvs_sec_cfg_t* cfg)
|
||||
{
|
||||
if (scheme_cfg == NULL || cfg == NULL || scheme_cfg->nvs_flash_read_cfg == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
return (scheme_cfg->nvs_flash_read_cfg)(scheme_cfg->scheme_data, cfg);
|
||||
}
|
||||
|
111
components/nvs_flash/src/nvs_bootloader_aes.c
Normal file
111
components/nvs_flash/src/nvs_bootloader_aes.c
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_log.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "nvs_bootloader_aes.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if SOC_AES_SUPPORTED
|
||||
int nvs_bootloader_aes_crypt_ecb(enum AES_TYPE mode,
|
||||
const unsigned char *key,
|
||||
enum AES_BITS key_bits,
|
||||
const unsigned char input[16],
|
||||
unsigned char output[16])
|
||||
{
|
||||
int ret = -1;
|
||||
ets_aes_enable();
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
if (mode == AES_ENC) {
|
||||
ret = ets_aes_setkey_enc(key, key_bits);
|
||||
} else {
|
||||
ret = ets_aes_setkey_dec(key, key_bits);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
ets_aes_crypt(input, output);
|
||||
// In case of esp32, ets_aes_setkey_dec returns 1 on success,
|
||||
// whereas for other targets ets_aes_setkey return 0 on success
|
||||
ret = 0;
|
||||
}
|
||||
#else /* CONFIG_IDF_TARGET_ESP32 */
|
||||
ret = ets_aes_setkey(mode, key, key_bits);
|
||||
|
||||
if (ret == 0) {
|
||||
ets_aes_block(input, output);
|
||||
}
|
||||
#endif /* !CONFIG_IDF_TARGET_ESP32 */
|
||||
|
||||
ets_aes_disable();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else /* SOC_AES_SUPPORTED */
|
||||
#if BOOTLOADER_BUILD && !CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER
|
||||
#if CONFIG_ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB
|
||||
#error "Enable `CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER` for non SOC_AES_SUPPORTED targets for supporting NVS encryption in bootloader build"
|
||||
#else /* !CONFIG_ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB */
|
||||
// TODO: IDF-11673
|
||||
// Due to unavailability of an software AES layer for bootloader build,
|
||||
// we cannot support the below NVS bootloader's AES operations
|
||||
// Thus we are adding stub APIs to indicate that the following operation fail.
|
||||
|
||||
static const char *TAG = "nvs_bootloader_aes";
|
||||
static const char *op_unsupported_error = "AES operation in bootloader unsupported for this target";
|
||||
|
||||
int nvs_bootloader_aes_crypt_ecb(enum AES_TYPE mode,
|
||||
const unsigned char *key,
|
||||
enum AES_BITS key_bits,
|
||||
const unsigned char input[16],
|
||||
unsigned char output[16])
|
||||
{
|
||||
ESP_EARLY_LOGE(TAG, "%s", op_unsupported_error);
|
||||
abort();
|
||||
return -1;
|
||||
}
|
||||
#endif /* CONFIG_ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB */
|
||||
#else /* BOOTLOADER_BUILD && !CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER */
|
||||
#include "mbedtls/aes.h"
|
||||
|
||||
int nvs_bootloader_aes_crypt_ecb(enum AES_TYPE mode,
|
||||
const unsigned char *key,
|
||||
enum AES_BITS key_bits,
|
||||
const unsigned char input[16],
|
||||
unsigned char output[16])
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
uint16_t keybits = key_bits == AES256 ? 256 : key_bits == AES192 ? 192 : 128;
|
||||
int mbedtls_aes_mode = mode == AES_ENC ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT;
|
||||
|
||||
mbedtls_aes_context ctx;
|
||||
mbedtls_aes_init(&ctx);
|
||||
|
||||
if (mode == AES_ENC) {
|
||||
ret = mbedtls_aes_setkey_enc(&ctx, key, keybits);
|
||||
} else {
|
||||
ret = mbedtls_aes_setkey_dec(&ctx, key, keybits);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
mbedtls_aes_free(&ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mbedtls_aes_crypt_ecb(&ctx, mbedtls_aes_mode, input, output);
|
||||
|
||||
if (ret != 0) {
|
||||
mbedtls_aes_free(&ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mbedtls_aes_free(&ctx);
|
||||
return ret;
|
||||
}
|
||||
#endif /* !(BOOTLOADER_BUILD && !CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER) */
|
||||
#endif /* !SOC_AES_SUPPORTED */
|
299
components/nvs_flash/src/nvs_bootloader_xts_aes.c
Normal file
299
components/nvs_flash/src/nvs_bootloader_xts_aes.c
Normal file
@ -0,0 +1,299 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "nvs_bootloader_aes.h"
|
||||
#include "nvs_bootloader_xts_aes.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#if SOC_AES_SUPPORTED
|
||||
/*
|
||||
* NOTE: The implementation of the below APIs have been copied
|
||||
* from the mbedtls (v3.6.2) implementation of the XTS-AES APIs.
|
||||
*/
|
||||
void nvs_bootloader_xts_aes_init(nvs_bootloader_xts_aes_context *ctx)
|
||||
{
|
||||
bzero(&ctx->crypt_key, sizeof(ctx->crypt_key));
|
||||
bzero(&ctx->tweak_key, sizeof(ctx->tweak_key));
|
||||
}
|
||||
|
||||
void nvs_bootloader_xts_aes_free(nvs_bootloader_xts_aes_context *ctx)
|
||||
{
|
||||
if (ctx) {
|
||||
bzero(&ctx->crypt_key, sizeof(ctx->crypt_key));
|
||||
bzero(&ctx->tweak_key, sizeof(ctx->tweak_key));
|
||||
}
|
||||
}
|
||||
|
||||
int nvs_bootloader_xts_aes_setkey(nvs_bootloader_xts_aes_context *ctx,
|
||||
const unsigned char *key,
|
||||
unsigned int key_bytes)
|
||||
{
|
||||
size_t xts_key_bytes = key_bytes / 2;
|
||||
|
||||
memcpy(&ctx->crypt_key, key, xts_key_bytes);
|
||||
memcpy(&ctx->tweak_key, &key[xts_key_bytes], xts_key_bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Endianness with 64 bits values */
|
||||
#ifndef GET_UINT64_LE
|
||||
#define GET_UINT64_LE(n,b,i) \
|
||||
{ \
|
||||
(n) = ((uint64_t) (b)[(i) + 7] << 56) \
|
||||
| ((uint64_t) (b)[(i) + 6] << 48) \
|
||||
| ((uint64_t) (b)[(i) + 5] << 40) \
|
||||
| ((uint64_t) (b)[(i) + 4] << 32) \
|
||||
| ((uint64_t) (b)[(i) + 3] << 24) \
|
||||
| ((uint64_t) (b)[(i) + 2] << 16) \
|
||||
| ((uint64_t) (b)[(i) + 1] << 8) \
|
||||
| ((uint64_t) (b)[(i) ] ); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef PUT_UINT64_LE
|
||||
#define PUT_UINT64_LE(n,b,i) \
|
||||
{ \
|
||||
(b)[(i) + 7] = (unsigned char) ((n) >> 56); \
|
||||
(b)[(i) + 6] = (unsigned char) ((n) >> 48); \
|
||||
(b)[(i) + 5] = (unsigned char) ((n) >> 40); \
|
||||
(b)[(i) + 4] = (unsigned char) ((n) >> 32); \
|
||||
(b)[(i) + 3] = (unsigned char) ((n) >> 24); \
|
||||
(b)[(i) + 2] = (unsigned char) ((n) >> 16); \
|
||||
(b)[(i) + 1] = (unsigned char) ((n) >> 8); \
|
||||
(b)[(i) ] = (unsigned char) ((n) ); \
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* GF(2^128) multiplication function
|
||||
*
|
||||
* This function multiplies a field element by x in the polynomial field
|
||||
* representation. It uses 64-bit word operations to gain speed but compensates
|
||||
* for machine endianness and hence works correctly on both big and little
|
||||
* endian machines.
|
||||
*/
|
||||
static void bootloader_gf128mul_x_ble(unsigned char r[16],
|
||||
const unsigned char x[16])
|
||||
{
|
||||
uint64_t a, b, ra, rb;
|
||||
|
||||
GET_UINT64_LE(a, x, 0);
|
||||
GET_UINT64_LE(b, x, 8);
|
||||
|
||||
ra = (a << 1) ^ 0x0087 >> (8 - ((b >> 63) << 3));
|
||||
rb = (a >> 63) | (b << 1);
|
||||
|
||||
PUT_UINT64_LE(ra, r, 0);
|
||||
PUT_UINT64_LE(rb, r, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
* XTS-AES buffer encryption/decryption
|
||||
*/
|
||||
int nvs_bootloader_aes_crypt_xts(nvs_bootloader_xts_aes_context *ctx,
|
||||
enum AES_TYPE mode,
|
||||
size_t length,
|
||||
const unsigned char data_unit[16],
|
||||
const unsigned char *input,
|
||||
unsigned char *output)
|
||||
{
|
||||
int ret;
|
||||
size_t blocks = length / 16;
|
||||
size_t leftover = length % 16;
|
||||
unsigned char tweak[16] = {};
|
||||
unsigned char prev_tweak[16] = {};
|
||||
unsigned char tmp[16] = {};
|
||||
|
||||
/* Sectors must be at least 16 bytes. */
|
||||
if (length < 16) {
|
||||
return NVS_BOOTLOADER_ERR_AES_INVALID_INPUT_LENGTH;
|
||||
}
|
||||
|
||||
/* NIST SP 80-38E disallows data units larger than 2**20 blocks. */
|
||||
if (length > ( 1 << 20 ) * 16) {
|
||||
return NVS_BOOTLOADER_ERR_AES_INVALID_INPUT_LENGTH;
|
||||
}
|
||||
|
||||
/* Compute the tweak. */
|
||||
ret = nvs_bootloader_aes_crypt_ecb(AES_ENC, (const unsigned char *) &ctx->tweak_key,
|
||||
AES256, data_unit, tweak);
|
||||
if (ret != 0) {
|
||||
return (ret);
|
||||
}
|
||||
|
||||
while (blocks--) {
|
||||
size_t i;
|
||||
|
||||
if (leftover && (mode == AES_DEC) && blocks == 0) {
|
||||
/* We are on the last block in a decrypt operation that has
|
||||
* leftover bytes, so we need to use the next tweak for this block,
|
||||
* and this tweak for the lefover bytes. Save the current tweak for
|
||||
* the leftovers and then update the current tweak for use on this,
|
||||
* the last full block. */
|
||||
memcpy(prev_tweak, tweak, sizeof(tweak));
|
||||
bootloader_gf128mul_x_ble(tweak, tweak);
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
tmp[i] = input[i] ^ tweak[i];
|
||||
}
|
||||
|
||||
ret = nvs_bootloader_aes_crypt_ecb(mode, (const unsigned char *) &ctx->crypt_key, AES256, tmp, tmp);
|
||||
if (ret != 0) {
|
||||
return (ret);
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
output[i] = tmp[i] ^ tweak[i];
|
||||
}
|
||||
|
||||
/* Update the tweak for the next block. */
|
||||
bootloader_gf128mul_x_ble(tweak, tweak);
|
||||
|
||||
output += 16;
|
||||
input += 16;
|
||||
}
|
||||
|
||||
if (leftover) {
|
||||
/* If we are on the leftover bytes in a decrypt operation, we need to
|
||||
* use the previous tweak for these bytes (as saved in prev_tweak). */
|
||||
unsigned char *t = mode == AES_DEC ? prev_tweak : tweak;
|
||||
|
||||
/* We are now on the final part of the data unit, which doesn't divide
|
||||
* evenly by 16. It's time for ciphertext stealing. */
|
||||
size_t i;
|
||||
unsigned char *prev_output = output - 16;
|
||||
|
||||
/* Copy ciphertext bytes from the previous block to our output for each
|
||||
* byte of ciphertext we won't steal. At the same time, copy the
|
||||
* remainder of the input for this final round (since the loop bounds
|
||||
* are the same). */
|
||||
for (i = 0; i < leftover; i++) {
|
||||
output[i] = prev_output[i];
|
||||
tmp[i] = input[i] ^ t[i];
|
||||
}
|
||||
|
||||
/* Copy ciphertext bytes from the previous block for input in this
|
||||
* round. */
|
||||
for (; i < 16; i++) {
|
||||
tmp[i] = prev_output[i] ^ t[i];
|
||||
}
|
||||
|
||||
ret = nvs_bootloader_aes_crypt_ecb(mode, (const unsigned char *) &ctx->crypt_key, AES256, tmp, tmp);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Write the result back to the previous block, overriding the previous
|
||||
* output we copied. */
|
||||
for (i = 0; i < 16; i++) {
|
||||
prev_output[i] = tmp[i] ^ t[i];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else /* SOC_AES_SUPPORTED */
|
||||
#if BOOTLOADER_BUILD && !CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER
|
||||
#if CONFIG_ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB
|
||||
#error "Enable `CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER` for non SOC_AES_SUPPORTED targets for supporting NVS encryption in bootloader build"
|
||||
#else /* !CONFIG_ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB */
|
||||
// TODO: IDF-11673
|
||||
// Due to unavailability of an software AES layer for bootloader build,
|
||||
// we cannot support the below NVS bootloader's AES operations
|
||||
// Thus we are adding stub APIs to indicate that the following operation fail.
|
||||
|
||||
static const char *TAG = "nvs_bootloader_xts_aes";
|
||||
static const char *op_unsupported_error = "XTS-AES operation in bootloader unsupported for this target";
|
||||
|
||||
void nvs_bootloader_xts_aes_init(nvs_bootloader_xts_aes_context *ctx)
|
||||
{
|
||||
(void) ctx;
|
||||
ESP_EARLY_LOGE(TAG, "%s", op_unsupported_error);
|
||||
abort();
|
||||
}
|
||||
|
||||
void nvs_bootloader_xts_aes_free(nvs_bootloader_xts_aes_context *ctx)
|
||||
{
|
||||
(void) ctx;
|
||||
ESP_EARLY_LOGE(TAG, "%s", op_unsupported_error);
|
||||
abort();
|
||||
}
|
||||
|
||||
int nvs_bootloader_xts_aes_setkey(nvs_bootloader_xts_aes_context *ctx,
|
||||
const unsigned char *key,
|
||||
unsigned int key_bytes)
|
||||
{
|
||||
(void) ctx;
|
||||
ESP_EARLY_LOGE(TAG, "%s", op_unsupported_error);
|
||||
abort();
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* XTS-AES buffer encryption/decryption
|
||||
*/
|
||||
int nvs_bootloader_aes_crypt_xts(nvs_bootloader_xts_aes_context *ctx,
|
||||
enum AES_TYPE mode,
|
||||
size_t length,
|
||||
const unsigned char data_unit[16],
|
||||
const unsigned char *input,
|
||||
unsigned char *output)
|
||||
{
|
||||
(void) ctx;
|
||||
ESP_EARLY_LOGE(TAG, "XTS-AES operation in bootloader unsupported");
|
||||
abort();
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB */
|
||||
#else /* BOOTLOADER_BUILD && !CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER */
|
||||
#include "mbedtls/aes.h"
|
||||
|
||||
static mbedtls_aes_xts_context ctx_xts;
|
||||
|
||||
void nvs_bootloader_xts_aes_init(nvs_bootloader_xts_aes_context *ctx)
|
||||
{
|
||||
(void) ctx;
|
||||
mbedtls_aes_xts_init(&ctx_xts);
|
||||
}
|
||||
|
||||
void nvs_bootloader_xts_aes_free(nvs_bootloader_xts_aes_context *ctx)
|
||||
{
|
||||
(void) ctx;
|
||||
mbedtls_aes_xts_free(&ctx_xts);
|
||||
}
|
||||
|
||||
int nvs_bootloader_xts_aes_setkey(nvs_bootloader_xts_aes_context *ctx,
|
||||
const unsigned char *key,
|
||||
unsigned int key_bytes)
|
||||
{
|
||||
(void) ctx;
|
||||
return mbedtls_aes_xts_setkey_dec(&ctx_xts, key, key_bytes * 8);
|
||||
}
|
||||
/*
|
||||
* XTS-AES buffer encryption/decryption
|
||||
*/
|
||||
int nvs_bootloader_aes_crypt_xts(nvs_bootloader_xts_aes_context *ctx,
|
||||
enum AES_TYPE mode,
|
||||
size_t length,
|
||||
const unsigned char data_unit[16],
|
||||
const unsigned char *input,
|
||||
unsigned char *output)
|
||||
{
|
||||
(void) ctx;
|
||||
|
||||
int mbedtls_aes_mode = mode == AES_ENC ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT;
|
||||
return mbedtls_aes_crypt_xts(&ctx_xts, mbedtls_aes_mode, length, data_unit, input, output);
|
||||
}
|
||||
#endif /* !(BOOTLOADER_BUILD && !CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER) */
|
||||
#endif /* !SOC_AES_SUPPORTED */
|
@ -1,6 +1,19 @@
|
||||
idf_component_register(SRCS "test_app_main.c" "test_nvs_bootloader.c"
|
||||
set(srcs "test_app_main.c" "test_nvs_bootloader.c")
|
||||
set(embed_txtfiles "")
|
||||
|
||||
if(CONFIG_NVS_ENCRYPTION OR SOC_HMAC_SUPPORTED)
|
||||
list(APPEND srcs "test_encrypted_nvs_bootloader.c")
|
||||
list(APPEND embed_txtfiles "nvs_partition.bin" "partition_encrypted.bin" "partition_encrypted_hmac.bin")
|
||||
endif()
|
||||
|
||||
if(CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC)
|
||||
list(APPEND embed_txtfiles "encryption_keys.bin")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES unity nvs_flash
|
||||
REQUIRES unity nvs_flash nvs_sec_provider bootloader_support
|
||||
EMBED_TXTFILES "${embed_txtfiles}"
|
||||
WHOLE_ARCHIVE
|
||||
)
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
"""""""""""""""""""""""""""""""",锵<<3C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
@ -0,0 +1,2 @@
|
||||
|
||||
|
BIN
components/nvs_flash/test_apps_bootloader/main/nvs_partition.bin
Normal file
BIN
components/nvs_flash/test_apps_bootloader/main/nvs_partition.bin
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_flash_encrypt.h"
|
||||
#include "esp_partition.h"
|
||||
#include "nvs_sec_provider.h"
|
||||
#include "unity.h"
|
||||
|
||||
#include "nvs_bootloader.h"
|
||||
|
||||
static esp_err_t configure_nvs_sec_cfg(nvs_sec_cfg_t *cfg, nvs_sec_scheme_t **sec_scheme_handle)
|
||||
{
|
||||
const esp_partition_t* nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
|
||||
TEST_ASSERT(nvs_part && "partition table must have an NVS partition");
|
||||
printf("\n nvs_part size:%" PRId32 "\n", nvs_part->size);
|
||||
ESP_ERROR_CHECK(esp_partition_erase_range(nvs_part, 0, nvs_part->size));
|
||||
|
||||
#if CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC
|
||||
if (!esp_flash_encryption_enabled()) {
|
||||
TEST_IGNORE_MESSAGE("flash encryption disabled, skipping nvs_api tests with encryption enabled");
|
||||
}
|
||||
|
||||
extern const char nvs_key_start[] asm("_binary_encryption_keys_bin_start");
|
||||
extern const char nvs_key_end[] asm("_binary_encryption_keys_bin_end");
|
||||
extern const char nvs_data_sch0_start[] asm("_binary_partition_encrypted_bin_start");
|
||||
extern const char nvs_data_sch0_end[] asm("_binary_partition_encrypted_bin_end");
|
||||
|
||||
const esp_partition_t* key_part = esp_partition_find_first(
|
||||
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, NULL);
|
||||
|
||||
assert(key_part && "partition table must have a KEY partition");
|
||||
TEST_ASSERT_TRUE((nvs_key_end - nvs_key_start - 1) == key_part->erase_size);
|
||||
|
||||
ESP_ERROR_CHECK(esp_partition_erase_range(key_part, 0, key_part->size));
|
||||
|
||||
for (int i = 0; i < key_part->size; i+= key_part->erase_size) {
|
||||
ESP_ERROR_CHECK( esp_partition_write(key_part, i, nvs_key_start + i, key_part->erase_size) );
|
||||
}
|
||||
|
||||
const int content_size = nvs_data_sch0_end - nvs_data_sch0_start - 1;
|
||||
TEST_ASSERT_TRUE((content_size % key_part->erase_size) == 0);
|
||||
|
||||
const int size_to_write = MIN(content_size, nvs_part->size);
|
||||
for (int i = 0; i < size_to_write; i+= nvs_part->erase_size) {
|
||||
ESP_ERROR_CHECK( esp_partition_write(nvs_part, i, nvs_data_sch0_start + i, nvs_part->erase_size) );
|
||||
}
|
||||
|
||||
nvs_sec_config_flash_enc_t sec_scheme_cfg = {
|
||||
.nvs_keys_part = key_part
|
||||
};
|
||||
|
||||
TEST_ESP_OK(nvs_sec_provider_register_flash_enc(&sec_scheme_cfg, sec_scheme_handle));
|
||||
return nvs_flash_read_security_cfg_v2(*sec_scheme_handle, cfg);
|
||||
|
||||
#elif SOC_HMAC_SUPPORTED
|
||||
extern const char nvs_data_sch1_start[] asm("_binary_partition_encrypted_hmac_bin_start");
|
||||
extern const char nvs_data_sch1_end[] asm("_binary_partition_encrypted_hmac_bin_end");
|
||||
|
||||
const int content_size = nvs_data_sch1_end - nvs_data_sch1_start - 1;
|
||||
TEST_ASSERT_TRUE((content_size % nvs_part->erase_size) == 0);
|
||||
|
||||
const int size_to_write = MIN(content_size, nvs_part->size);
|
||||
for (int i = 0; i < size_to_write; i+= nvs_part->erase_size) {
|
||||
ESP_ERROR_CHECK( esp_partition_write(nvs_part, i, nvs_data_sch1_start + i, nvs_part->erase_size) );
|
||||
}
|
||||
|
||||
#ifndef CONFIG_NVS_ENCRYPTION
|
||||
nvs_sec_config_hmac_t sec_scheme_cfg = {
|
||||
.hmac_key_id = HMAC_KEY0,
|
||||
};
|
||||
#else
|
||||
nvs_sec_config_hmac_t sec_scheme_cfg = NVS_SEC_PROVIDER_CFG_HMAC_DEFAULT();
|
||||
#endif /* CONFIG_NVS_ENCRYPTION */
|
||||
|
||||
TEST_ESP_OK(nvs_sec_provider_register_hmac(&sec_scheme_cfg, sec_scheme_handle));
|
||||
return nvs_flash_read_security_cfg_v2(*sec_scheme_handle, cfg);
|
||||
#endif
|
||||
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static void restore_nvs_partition(void)
|
||||
{
|
||||
const esp_partition_t* nvs_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
|
||||
TEST_ASSERT(nvs_part && "partition table must have an NVS partition");
|
||||
printf("\n nvs_part size:%" PRId32 "\n", nvs_part->size);
|
||||
ESP_ERROR_CHECK(esp_partition_erase_range(nvs_part, 0, nvs_part->size));
|
||||
|
||||
extern const char nvs_data_start[] asm("_binary_nvs_partition_bin_start");
|
||||
extern const char nvs_data_end[] asm("_binary_nvs_partition_bin_end");
|
||||
|
||||
const int content_size = nvs_data_end - nvs_data_start - 1;
|
||||
TEST_ASSERT_TRUE((content_size % nvs_part->erase_size) == 0);
|
||||
|
||||
const int size_to_write = MIN(content_size, nvs_part->size);
|
||||
for (int i = 0; i < size_to_write; i+= nvs_part->erase_size) {
|
||||
ESP_ERROR_CHECK(esp_partition_write(nvs_part, i, nvs_data_start + i, nvs_part->erase_size));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Verify encrypted nvs bootloader read_list result_code and value if bootloader read is successful", "[nvs_encrypted_bootloader]")
|
||||
{
|
||||
nvs_sec_cfg_t xts_cfg;
|
||||
nvs_sec_scheme_t *sec_scheme_handle = NULL;
|
||||
TEST_ESP_OK(configure_nvs_sec_cfg(&xts_cfg, &sec_scheme_handle));
|
||||
|
||||
nvs_bootloader_read_list_t read_list[] = {
|
||||
// {namespace_name, key_name, value_type, result_code, value, namespace_index}}
|
||||
{ .namespace_name = "storage", .key_name = "u8_key", .value_type = NVS_TYPE_U8 }, //0 OK
|
||||
{ .namespace_name = "storage", .key_name = "u16_key", .value_type = NVS_TYPE_U16 }, //1 OK
|
||||
{ .namespace_name = "storage", .key_name = "u32_key", .value_type = NVS_TYPE_U32 }, //2 OK
|
||||
{ .namespace_name = "storage", .key_name = "i32_key", .value_type = NVS_TYPE_I32 }, //3 OK
|
||||
{ .namespace_name = "storage", .key_name = "i8_key", .value_type = NVS_TYPE_U8 }, //4 Type mismatch
|
||||
{ .namespace_name = "storage", .key_name = "i16_key", .value_type = NVS_TYPE_I16 }, //5 Not found
|
||||
};
|
||||
uint8_t size = sizeof(read_list) / sizeof(read_list[0]);
|
||||
|
||||
TEST_ESP_OK(nvs_bootloader_secure_init(&xts_cfg));
|
||||
TEST_ESP_OK(nvs_bootloader_read("nvs", size, read_list));
|
||||
nvs_bootloader_secure_deinit();
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, read_list[0].result_code);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, read_list[1].result_code);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, read_list[2].result_code);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, read_list[3].result_code);
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NVS_TYPE_MISMATCH, read_list[4].result_code);
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, read_list[5].result_code);
|
||||
|
||||
TEST_ASSERT_EQUAL(255, read_list[0].value.u8_val);
|
||||
TEST_ASSERT_EQUAL(65535, read_list[1].value.u16_val);
|
||||
TEST_ASSERT_EQUAL(4294967295, read_list[2].value.u32_val);
|
||||
TEST_ASSERT_EQUAL(-2147483648, read_list[3].value.i32_val);
|
||||
|
||||
TEST_ESP_OK(nvs_sec_provider_deregister(sec_scheme_handle));
|
||||
restore_nvs_partition();
|
||||
}
|
||||
|
||||
TEST_CASE("Verify encrypted nvs bootloader read_list result_code if bootloader read fails", "[nvs_encrypted_bootloader]")
|
||||
{
|
||||
nvs_sec_cfg_t xts_cfg;
|
||||
nvs_sec_scheme_t *sec_scheme_handle = NULL;
|
||||
TEST_ESP_OK(configure_nvs_sec_cfg(&xts_cfg, &sec_scheme_handle));
|
||||
|
||||
nvs_bootloader_read_list_t read_list[] = {
|
||||
// {namespace_name, key_name, value_type, result_code, value, namespace_index}}
|
||||
{ .namespace_name = "too_long_namespace", .key_name = "i32_key", .value_type = NVS_TYPE_I32 }, //0 Invalid name
|
||||
{ .namespace_name = "nvs", .key_name = "too_long_key_name", .value_type = NVS_TYPE_I32 }, //1 Key too long
|
||||
{ .namespace_name = "nvs", .key_name = "str_key", .value_type = NVS_TYPE_BLOB }, //2 Invalid arg
|
||||
{ .namespace_name = "nvs", .key_name = "i32_key", .value_type = NVS_TYPE_I32 }, //3 Not found
|
||||
};
|
||||
uint8_t size = sizeof(read_list) / sizeof(read_list[0]);
|
||||
|
||||
TEST_ESP_OK(nvs_bootloader_secure_init(&xts_cfg));
|
||||
esp_err_t ret = nvs_bootloader_read("nvs", size, read_list);
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, ret);
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NVS_INVALID_NAME, read_list[0].result_code);
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NVS_KEY_TOO_LONG, read_list[1].result_code);
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, read_list[2].result_code);
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NVS_NOT_FOUND, read_list[3].result_code);
|
||||
|
||||
nvs_bootloader_secure_deinit();
|
||||
|
||||
TEST_ESP_OK(nvs_sec_provider_deregister(sec_scheme_handle));
|
||||
restore_nvs_partition();
|
||||
}
|
||||
|
||||
TEST_CASE("Verify nvs_bootloader_read_encrypted failure cases", "[nvs_encrypted_bootloader]")
|
||||
{
|
||||
nvs_sec_cfg_t xts_cfg;
|
||||
nvs_sec_scheme_t *sec_scheme_handle = NULL;
|
||||
TEST_ESP_OK(configure_nvs_sec_cfg(&xts_cfg, &sec_scheme_handle));
|
||||
|
||||
nvs_bootloader_read_list_t read_list[] = {
|
||||
// {namespace_name, key_name, value_type, result_code, value, namespace_index}}
|
||||
{ "nvs", "i32_key", NVS_TYPE_I32, ESP_OK, {0}, 0}
|
||||
};
|
||||
uint8_t size = sizeof(read_list) / sizeof(read_list[0]);
|
||||
|
||||
TEST_ESP_OK(nvs_bootloader_secure_init(&xts_cfg));
|
||||
esp_err_t ret = nvs_bootloader_read("nvs_partition_name_too_long", size, read_list);
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NVS_INVALID_NAME, ret);
|
||||
|
||||
ret = nvs_bootloader_read("nvs_part", size, read_list);
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NVS_PART_NOT_FOUND, ret);
|
||||
|
||||
nvs_bootloader_secure_deinit();
|
||||
TEST_ESP_OK(nvs_sec_provider_deregister(sec_scheme_handle));
|
||||
restore_nvs_partition();
|
||||
}
|
@ -1,12 +1,36 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.dut import IdfDut
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize('config', ['default'], indirect=True)
|
||||
def test_nvs_bootloader_support(dut: Dut) -> None:
|
||||
def test_nvs_bootloader_support(dut: IdfDut) -> None:
|
||||
dut.run_all_single_board_cases(group='!nvs_encrypted_bootloader', timeout=120)
|
||||
|
||||
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.nvs_encr_hmac
|
||||
@pytest.mark.parametrize('config', ['nvs_enc_hmac'], indirect=True)
|
||||
def test_nvs_bootloader_support_encr_hmac(dut: IdfDut) -> None:
|
||||
dut.run_all_single_board_cases()
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.flash_encryption
|
||||
@pytest.mark.parametrize('config', ['nvs_enc_flash_enc'], indirect=True)
|
||||
def test_nvs_bootloader_support_encr_flash_enc(dut: IdfDut) -> None:
|
||||
# Erase the nvs_key partition
|
||||
dut.serial.erase_partition('nvs_key')
|
||||
dut.run_all_single_board_cases()
|
||||
|
||||
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.nvs_encr_hmac
|
||||
@pytest.mark.parametrize('config', ['nvs_enc_hmac_no_cfg'], indirect=True)
|
||||
def test_nvs_bootloader_support_encr_hmac_no_cfg(dut: IdfDut) -> None:
|
||||
dut.run_all_single_board_cases()
|
||||
|
@ -0,0 +1,15 @@
|
||||
# Enabling 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
|
||||
|
||||
# Enabling NVS Encryption (Flash Encryption-based scheme)
|
||||
CONFIG_NVS_ENCRYPTION=y
|
||||
CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC=y
|
||||
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_ENCRYPTED_NVS=y
|
@ -0,0 +1,15 @@
|
||||
# NOTE: The runner for this test-app has flash-encryption enabled
|
||||
# Enabling 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
|
||||
|
||||
# Enabling NVS Encryption (HMAC-based scheme)
|
||||
CONFIG_NVS_ENCRYPTION=y
|
||||
CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC=y
|
||||
CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID=0
|
@ -0,0 +1,12 @@
|
||||
# NOTE: The runner for this test-app has flash-encryption enabled
|
||||
# Enabling 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
|
||||
|
||||
CONFIG_NVS_ENCRYPTION=n
|
@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32c2"
|
||||
CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER=y
|
@ -4,7 +4,13 @@ if(${target} STREQUAL "linux")
|
||||
return() # This component is not supported by the POSIX/Linux simulator
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "nvs_sec_provider.c"
|
||||
if(BOOTLOADER_BUILD)
|
||||
set(srcs "nvs_bootloader_sec_provider.c")
|
||||
else()
|
||||
set(srcs "nvs_sec_provider.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS include
|
||||
PRIV_REQUIRES bootloader_support efuse esp_partition nvs_flash)
|
||||
|
||||
|
@ -32,8 +32,8 @@ menu "NVS Security Provider"
|
||||
config NVS_SEC_HMAC_EFUSE_KEY_ID
|
||||
int "eFuse key ID storing the HMAC key"
|
||||
depends on NVS_SEC_KEY_PROTECT_USING_HMAC
|
||||
range 0 6
|
||||
default 6
|
||||
range -1 5
|
||||
default -1
|
||||
help
|
||||
eFuse block key ID storing the HMAC key for deriving the NVS encryption keys
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define EKEY_SEED 0xAEBE5A5A
|
||||
#define TKEY_SEED 0xCEDEA5A5
|
212
components/nvs_sec_provider/nvs_bootloader_sec_provider.c
Normal file
212
components/nvs_sec_provider/nvs_bootloader_sec_provider.c
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_partition.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs_sec_provider.h"
|
||||
#include "esp_rom_crc.h"
|
||||
#include "private/nvs_sec_provider_private.h"
|
||||
|
||||
#if SOC_HMAC_SUPPORTED
|
||||
#include "rom/efuse.h"
|
||||
#include "rom/hmac.h"
|
||||
#endif // SOC_HMAC_SUPPORTED
|
||||
|
||||
static __attribute__((unused)) const char *TAG = "nvs_bootloader_sec_provider";
|
||||
|
||||
static __attribute__((unused)) nvs_sec_scheme_t sec_scheme;
|
||||
|
||||
#if CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC
|
||||
|
||||
static nvs_sec_config_flash_enc_t nvs_sec_config_flash_enc_scheme_data;
|
||||
|
||||
static esp_err_t nvs_bootloader_read_security_cfg(const esp_partition_t* partition, nvs_sec_cfg_t* cfg)
|
||||
{
|
||||
if (cfg == NULL || partition == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
uint32_t crc_read, crc_calc;
|
||||
|
||||
esp_err_t err = esp_partition_read(partition, 0, cfg->eky, NVS_KEY_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_partition_read(partition, NVS_KEY_SIZE, cfg->tky, NVS_KEY_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_partition_read(partition, 2 * NVS_KEY_SIZE, &crc_read, 4);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
crc_calc = esp_rom_crc32_le(0xffffffff, cfg->eky, NVS_KEY_SIZE);
|
||||
crc_calc = esp_rom_crc32_le(crc_calc, cfg->tky, NVS_KEY_SIZE);
|
||||
|
||||
if (crc_calc != crc_read) {
|
||||
return ESP_ERR_NVS_CORRUPT_KEY_PART;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t read_security_cfg_flash_enc(const void* sec_scheme_cfg, nvs_sec_cfg_t* cfg)
|
||||
{
|
||||
if (sec_scheme_cfg == NULL || cfg == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nvs_sec_config_flash_enc_t *scheme_cfg_flash_enc = (nvs_sec_config_flash_enc_t *)sec_scheme_cfg;
|
||||
return nvs_bootloader_read_security_cfg(scheme_cfg_flash_enc->nvs_keys_part, cfg);
|
||||
}
|
||||
|
||||
esp_err_t nvs_sec_provider_register_flash_enc(const nvs_sec_config_flash_enc_t *sec_scheme_cfg, nvs_sec_scheme_t **sec_scheme_handle_out)
|
||||
{
|
||||
if (sec_scheme_cfg == NULL || sec_scheme_handle_out == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
bzero(&sec_scheme, sizeof(nvs_sec_scheme_t));
|
||||
|
||||
sec_scheme.scheme_id = NVS_SEC_SCHEME_FLASH_ENC;
|
||||
sec_scheme.nvs_flash_read_cfg = &read_security_cfg_flash_enc;
|
||||
|
||||
bzero(&nvs_sec_config_flash_enc_scheme_data, sizeof(nvs_sec_config_flash_enc_t));
|
||||
sec_scheme.scheme_data = &nvs_sec_config_flash_enc_scheme_data;
|
||||
|
||||
memcpy(sec_scheme.scheme_data, (void *)sec_scheme_cfg, sizeof(nvs_sec_config_flash_enc_t));
|
||||
|
||||
*sec_scheme_handle_out = &sec_scheme;
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_HMAC_SUPPORTED
|
||||
|
||||
static nvs_sec_config_hmac_t nvs_sec_config_hmac_scheme_data;
|
||||
|
||||
#if CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID < 0
|
||||
#error "NVS Encryption (HMAC): Configured eFuse block (CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID) out of range!"
|
||||
#endif
|
||||
|
||||
static esp_err_t nvs_bootloader_hmac_calculate(ets_efuse_block_t hmac_key,
|
||||
const void *message,
|
||||
size_t message_len,
|
||||
uint8_t *hmac)
|
||||
{
|
||||
ets_hmac_enable();
|
||||
|
||||
int hmac_ret = ets_hmac_calculate_message(hmac_key, message, message_len, hmac);
|
||||
|
||||
ets_hmac_disable();
|
||||
|
||||
if (hmac_ret != 0) {
|
||||
return ESP_FAIL;
|
||||
} else {
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t compute_nvs_keys_with_hmac(ets_efuse_block_t hmac_key, nvs_sec_cfg_t* cfg)
|
||||
{
|
||||
uint32_t ekey_seed[8] = {[0 ... 7] = EKEY_SEED};
|
||||
uint32_t tkey_seed[8] = {[0 ... 7] = TKEY_SEED};
|
||||
|
||||
esp_err_t err = nvs_bootloader_hmac_calculate(hmac_key, ekey_seed, sizeof(ekey_seed), (uint8_t *)cfg->eky);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Failed to calculate seed HMAC: [0x%02X]", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = nvs_bootloader_hmac_calculate(hmac_key, tkey_seed, sizeof(tkey_seed), (uint8_t *)cfg->tky);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Failed to calculate seed HMAC: [0x%02X]", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* NOTE: If the XTS E-key and T-key are the same, we have a hash collision */
|
||||
if (memcmp(cfg->eky, cfg->tky, NVS_KEY_SIZE) == 0) {
|
||||
ESP_LOGD(TAG, "The XTS-AES E-key and T-key are the same, we have a hash collision");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static bool is_hmac_key_burnt_in_efuse(ets_efuse_block_t hmac_key_blk)
|
||||
{
|
||||
|
||||
ets_efuse_purpose_t hmac_efuse_blk_purpose = ets_efuse_get_key_purpose(hmac_key_blk);
|
||||
|
||||
if (hmac_efuse_blk_purpose == ETS_EFUSE_KEY_PURPOSE_HMAC_UP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static ets_efuse_block_t convert_key_type(hmac_key_id_t key_id) {
|
||||
return (ets_efuse_block_t)(ETS_EFUSE_BLOCK_KEY0 + (ets_efuse_block_t) key_id);
|
||||
}
|
||||
|
||||
static esp_err_t read_security_cfg_hmac(const void* scheme_cfg, nvs_sec_cfg_t* cfg)
|
||||
{
|
||||
if (cfg == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nvs_sec_config_hmac_t *scheme_cfg_hmac = (nvs_sec_config_hmac_t *)scheme_cfg;
|
||||
|
||||
if (scheme_cfg_hmac->hmac_key_id >= HMAC_KEY_MAX) {
|
||||
ESP_LOGD(TAG, "Invalid HMAC key ID received!");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
ets_efuse_block_t hmac_key = convert_key_type(scheme_cfg_hmac->hmac_key_id);
|
||||
|
||||
if (!is_hmac_key_burnt_in_efuse(hmac_key)) {
|
||||
ESP_LOGD(TAG, "Could not find HMAC key in configured eFuse block!");
|
||||
return ESP_ERR_NVS_SEC_HMAC_KEY_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (compute_nvs_keys_with_hmac(hmac_key, cfg) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "Failed to derive the encryption keys using HMAC!");
|
||||
return ESP_ERR_NVS_SEC_HMAC_XTS_KEYS_DERIV_FAILED;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t nvs_sec_provider_register_hmac(const nvs_sec_config_hmac_t *sec_scheme_cfg, nvs_sec_scheme_t **sec_scheme_handle_out)
|
||||
{
|
||||
if (sec_scheme_cfg == NULL || sec_scheme_handle_out == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
bzero(&sec_scheme, sizeof(nvs_sec_scheme_t));
|
||||
|
||||
sec_scheme.scheme_id = NVS_SEC_SCHEME_HMAC;
|
||||
sec_scheme.nvs_flash_read_cfg = &read_security_cfg_hmac;
|
||||
|
||||
bzero(&nvs_sec_config_hmac_scheme_data, sizeof(nvs_sec_config_hmac_t));
|
||||
sec_scheme.scheme_data = &nvs_sec_config_hmac_scheme_data;
|
||||
|
||||
memcpy(sec_scheme.scheme_data, (void *)sec_scheme_cfg, sizeof(nvs_sec_config_hmac_t));
|
||||
|
||||
*sec_scheme_handle_out = &sec_scheme;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif // SOC_HMAC_SUPPORTED
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -12,6 +12,7 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs_sec_provider.h"
|
||||
#include "private/nvs_sec_provider_private.h"
|
||||
|
||||
#include "esp_private/startup_internal.h"
|
||||
#if SOC_HMAC_SUPPORTED
|
||||
@ -106,14 +107,14 @@ ESP_SYSTEM_INIT_FN(nvs_sec_provider_register_flash_enc_scheme, SECONDARY, BIT(0)
|
||||
|
||||
#if SOC_HMAC_SUPPORTED
|
||||
|
||||
#if CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID > 5
|
||||
#if CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID < 0
|
||||
#error "NVS Encryption (HMAC): Configured eFuse block (CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID) out of range!"
|
||||
#endif
|
||||
|
||||
static esp_err_t compute_nvs_keys_with_hmac(hmac_key_id_t hmac_key_id, nvs_sec_cfg_t* cfg)
|
||||
{
|
||||
uint32_t ekey_seed[8] = {[0 ... 7] = 0xAEBE5A5A};
|
||||
uint32_t tkey_seed[8] = {[0 ... 7] = 0xCEDEA5A5};
|
||||
uint32_t ekey_seed[8] = {[0 ... 7] = EKEY_SEED};
|
||||
uint32_t tkey_seed[8] = {[0 ... 7] = TKEY_SEED};
|
||||
|
||||
esp_err_t err = esp_hmac_calculate(hmac_key_id, ekey_seed, sizeof(ekey_seed), (uint8_t *)cfg->eky);
|
||||
if (err != ESP_OK) {
|
||||
|
@ -14,6 +14,25 @@ The API supports reading all NVS datatypes except for blobs. One call to the API
|
||||
|
||||
To read string entries, the API requires the caller to provide a buffer and its size, due to the heap memory allocation restriction in the bootloader.
|
||||
|
||||
Reading encrypted NVS partitions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The API also supports decrypting NVS data, if the NVS partition is encrypted using one of the schemes as mentioned in the :doc:`nvs_encryption` guide.
|
||||
|
||||
Applications are expected to follow the steps below in order to enable decryption of a NVS partition using the NVS bootloader read API:
|
||||
|
||||
1. Populate the NVS security configuration structure :cpp:type:`nvs_sec_cfg_t` according to the selected NVS encryption scheme (Please refer to :doc:`nvs_encryption` for more details).
|
||||
2. Read NVS security configuration set by the specified security scheme using the :cpp:func:`nvs_bootloader_read_security_cfg` API.
|
||||
3. Initialise the NVS flash partition with the above read security configuration using the :cpp:func:`nvs_bootloader_secure_init` API.
|
||||
4. Perform NVS read operations using the :cpp:func:`nvs_bootloader_read` API.
|
||||
5. Deinitialise and clear the security configuration of the NVS flash partition using the :cpp:func:`nvs_bootloader_secure_deinit` API.
|
||||
|
||||
.. only:: SOC_HMAC_SUPPORTED
|
||||
|
||||
.. note::
|
||||
While using the HMAC-based scheme, the above workflow can be used without enabling any of the config options for NVS encryption - :ref:`CONFIG_NVS_ENCRYPTION`, :ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME` -> ``CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`` and :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` to encrypt the default as well as custom NVS partitions with :cpp:func:`nvs_flash_secure_init` API.
|
||||
|
||||
|
||||
Application Example
|
||||
-------------------
|
||||
|
||||
|
@ -121,7 +121,7 @@ It is possible for an application to use different keys for different NVS partit
|
||||
|
||||
.. note::
|
||||
|
||||
The valid range for the config :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` is from ``0`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY0`) to ``5`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY5`). By default, the config is set to ``6`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY_MAX`), which have to be configured before building the user application.
|
||||
The valid range for the config :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` is from ``0`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY0`) to ``5`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY5`). By default, the config is set to ``-1``, which have to be configured before building the user application.
|
||||
|
||||
- If no key is found, a key is generated internally and stored at the eFuse block specified at :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID`.
|
||||
- If a key is found with the purpose :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`, the same is used for the derivation of the XTS encryption keys.
|
||||
|
@ -121,7 +121,7 @@ NVS 密钥分区
|
||||
|
||||
.. note::
|
||||
|
||||
:ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` 配置的有效范围为 ``0`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY0`) 到 ``5`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY5`)。默认情况下该配置为 ``6`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY_MAX`),须在构建用户应用程序之前进行修改。
|
||||
:ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` 配置的有效范围为 ``0`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY0`) 到 ``5`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY5`)。默认情况下该配置为 ``-1``,须在构建用户应用程序之前进行修改。
|
||||
|
||||
- 如果找不到密钥,会内部生成一个密钥,并储存在 :ref:`CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID` 指定的 eFuse 块中。
|
||||
- 如果找到用于 :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP` 的密钥,该密钥也会用于 XTS 加密密钥的生成。
|
||||
|
@ -16,6 +16,15 @@ examples/storage/emmc:
|
||||
- if: IDF_TARGET == "esp32s3"
|
||||
reason: only support on esp32s3
|
||||
|
||||
examples/storage/nvs_bootloader:
|
||||
depends_components:
|
||||
- nvs_flash
|
||||
- nvs_sec_provider
|
||||
disable:
|
||||
- if: CONFIG_NAME == "nvs_enc_flash_enc" and (SOC_AES_SUPPORTED != 1 and ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB != 1)
|
||||
- if: CONFIG_NAME == "nvs_enc_hmac" and (SOC_HMAC_SUPPORTED != 1 or (SOC_HMAC_SUPPORTED == 1 and (SOC_AES_SUPPORTED != 1 and ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB != 1)))
|
||||
reason: As of now in such cases, we do not have any way to perform AES operations in the bootloader build
|
||||
|
||||
examples/storage/nvs_rw_blob:
|
||||
depends_components:
|
||||
- nvs_flash
|
||||
|
@ -3,7 +3,9 @@
|
||||
|
||||
# NVS Bootloader
|
||||
|
||||
The purpose of this example is to show how to use the simplified, read-only API of NVS flash that can be used as a part of bootloader
|
||||
The purpose of this example is to show how to use the simplified, read-only API of NVS flash that can be used as a part of bootloader.
|
||||
|
||||
A very practical application of being able to access the NVS in the bootloader build would be faster device restoration, where-in the application stores the device's current state/configurations and post a reset it would read the NVS to restore the device's last state, without waiting for application to boot-up.
|
||||
|
||||
## Usage of this example:
|
||||
|
||||
@ -123,3 +125,116 @@ Below is a short explanation of files in the project folder.
|
||||
The example creates request/response array `read_list[]`, populates it with identifiers of the data to be read.
|
||||
Function `nvs_bootloader_read()` tries to find respective data in the partition (here `"nvs"`) and if the data is found, it populates the request/response array with data. For nvs entries either not found or not matching are indicated in response array as well.
|
||||
Function `log_nvs_bootloader_read_list()`is used before and after reading from nvs to show request/response data to the console.
|
||||
|
||||
# Encrypted NVS Bootloader
|
||||
|
||||
This example is extended to support reading encrypted NVS partition when NVS encryption is enabled.
|
||||
|
||||
## Usage of this example:
|
||||
|
||||
Enable NVS encryption using your preferred scheme. Please find more details regarding the `flash encryption based NVS encryption scheme` and the `HMAC based NVS encryption scheme` in the [NVS encryption documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/nvs_encryption.html).
|
||||
|
||||
(Note: In case you select the `HMAC based NVS encryption scheme`, make sure that you burn the below mentioned [HMAC key](./main/nvs_enc_hmac_key.bin) in the efuses.)
|
||||
|
||||
For generating the encrypted NVS partitions, we shall use [NVS partition generator](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/nvs_partition_gen.html#nvs-partition-generator-utility).
|
||||
We shall use the [nvs_partition_gen.py](../../../components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py) script for the operations.
|
||||
|
||||
Along with the above mentioned file structure, the project folder also contains pre-generated encrypted partitions and the partition corresponding to the selected NVS encryption scheme is flashed along with the build artefacts using the `main/CMakeLists.txt`.
|
||||
|
||||
In case the data in `nvs_data.csv` is modified, these encrypted NVS partitions can be re-generated using the following commands:
|
||||
|
||||
1. NVS Encryption using the flash encryption scheme
|
||||
|
||||
```
|
||||
python nvs_partition_gen.py encrypt $IDF_PATH/examples/storage/nvs_bootloader/nvs_data.csv $IDF_PATH/examples/storage/nvs_bootloader/main/nvs_encrypted.bin 0x6000 --inputkey $IDF_PATH/examples/storage/nvs_bootloader/main/encryption_keys.bin
|
||||
```
|
||||
|
||||
2. NVS Encryption using the HMAC scheme
|
||||
|
||||
```
|
||||
python nvs_partition_gen.py encrypt $IDF_PATH/examples/storage/nvs_bootloader/nvs_data.csv $IDF_PATH/examples/storage/nvs_bootloader/main/nvs_encrypted_hmac.bin 0x6000 --keygen --key_protect_hmac --kp_hmac_inputkey $IDF_PATH/examples/storage/nvs_bootloader/main/nvs_enc_hmac_key.bin
|
||||
```
|
||||
|
||||
Build the application using configurations corresponding to the NVS encryption scheme that you have selected:
|
||||
|
||||
```
|
||||
idf.py set-target <target>
|
||||
|
||||
# For NVS encryption using flash encryption scheme
|
||||
cat sdkconfig.ci.nvs_enc_flash_enc >> sdkconfig
|
||||
|
||||
OR
|
||||
|
||||
# For NVS encryption using the HMAC scheme
|
||||
cat sdkconfig.ci.nvs_enc_hmac >> sdkconfig
|
||||
|
||||
idf.py build
|
||||
```
|
||||
|
||||
Then flash it and open the monitor with the following command:
|
||||
```
|
||||
idf.py flash monitor
|
||||
```
|
||||
|
||||
If everything went well, the console output should contain the same three blocks of log messages that are mentioned above.
|
||||
|
||||
### Running the example using QEMU
|
||||
|
||||
You could quickly try out this example using QEMU. Refer this [link](https://github.com/espressif/esp-toolchain-docs/blob/main/qemu/README.md#choose-your-target) to know which targets are currently supported in QEMU.
|
||||
|
||||
#### Using the NVS encryption's flash encryption scheme
|
||||
|
||||
1. Configure the application with the corresponding configurations
|
||||
|
||||
```
|
||||
idf.py set-target <qemu-supported-targets>
|
||||
|
||||
cat sdkconfig.ci.nvs_enc_flash_enc >> sdkconfig
|
||||
|
||||
# Disable the below config as it was enabled as a CI related configuration, thus enabling flash encryption during boot-up in QEMU
|
||||
echo "CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=n" >> sdkconfig
|
||||
|
||||
```
|
||||
|
||||
2. Building the app
|
||||
|
||||
```
|
||||
idf.py build
|
||||
```
|
||||
|
||||
3. Running the app
|
||||
|
||||
```
|
||||
idf.py qemu monitor
|
||||
```
|
||||
|
||||
#### Using the NVS encryption's HMAC scheme
|
||||
|
||||
1. Build the application using the corresponding configurations
|
||||
|
||||
```
|
||||
idf.py set-target <qemu-supported-targets>
|
||||
|
||||
cat sdkconfig.ci.nvs_enc_hmac >> sdkconfig
|
||||
|
||||
# Disable the below config as it was enabled as a CI related configuration, thus enabling flash encryption during boot-up in QEMU
|
||||
echo "CONFIG_SECURE_FLASH_REQUIRE_ALREADY_ENABLED=n" >> sdkconfig
|
||||
```
|
||||
|
||||
2. Building the app
|
||||
|
||||
```
|
||||
idf.py build
|
||||
```
|
||||
|
||||
3. Burn the related HMAC key in the efuses
|
||||
|
||||
```
|
||||
idf.py qemu efuse-burn-key BLOCK_KEY0 main/nvs_enc_hmac_key.bin HMAC_UP
|
||||
```
|
||||
|
||||
4. Running the app
|
||||
|
||||
```
|
||||
idf.py qemu monitor
|
||||
```
|
||||
|
@ -1,6 +1,6 @@
|
||||
idf_component_register( SRCS "src/nvs_bootloader_example.c" "src/nvs_bootloader_example_utils.c"
|
||||
idf_component_register(SRCS "src/nvs_bootloader_example.c" "src/nvs_bootloader_example_utils.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES "nvs_flash" )
|
||||
REQUIRES "nvs_flash" "nvs_sec_provider")
|
||||
|
||||
# We need to force GCC to integrate this static library into the
|
||||
# bootloader link. Indeed, by default, as the hooks in the bootloader are weak,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -8,6 +8,8 @@
|
||||
#include "nvs_bootloader.h"
|
||||
#include "nvs_bootloader_example_utils.h"
|
||||
|
||||
#include "nvs_sec_provider.h"
|
||||
|
||||
static const char* TAG = "nvs_bootloader_example";
|
||||
|
||||
// Function used to tell the linker to include this file
|
||||
@ -21,14 +23,11 @@ void bootloader_before_init(void) {
|
||||
}
|
||||
|
||||
|
||||
void log_request_call_read_evaluate_output(const char* nvs_partition_label, nvs_bootloader_read_list_t read_list[], const size_t read_list_count) {
|
||||
// log the request structure before the read to see the requested keys and namespaces
|
||||
// with the ESP_ERR_NOT_FINISHED return code we are just telling the log function to show the request data and omit printing the result data
|
||||
// it is useful for debugging the request structure
|
||||
log_nvs_bootloader_read_list(ESP_ERR_NOT_FINISHED, read_list, read_list_count);
|
||||
|
||||
void log_request_call_read_evaluate_output(const char* nvs_partition_label, nvs_bootloader_read_list_t read_list[], const size_t read_list_count, const nvs_sec_cfg_t* sec_cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
// call the read function
|
||||
esp_err_t ret = nvs_bootloader_read(nvs_partition_label, read_list_count, read_list);
|
||||
ret = nvs_bootloader_read(nvs_partition_label, read_list_count, read_list);
|
||||
|
||||
// Error code ESP_OK means that the read function was successful and individual, per record results are stored in the read_list
|
||||
if (ret == ESP_OK) {
|
||||
@ -62,10 +61,48 @@ void bootloader_after_init(void) {
|
||||
// we are going to read from the default nvs partition labelled 'nvs'
|
||||
const char* nvs_partition_label = "nvs";
|
||||
|
||||
#define STR_BUFF_LEN 10+1 // 10 characters + null terminator
|
||||
char str_buff[STR_BUFF_LEN];
|
||||
#define STR_BUFF_LEN_10 10+1 // 10 characters + null terminator
|
||||
#define STR_BUFF_LEN_66 66+1 // 66 characters + null terminator
|
||||
char str_buff_10[STR_BUFF_LEN_10];
|
||||
char str_buff_66[STR_BUFF_LEN_66];
|
||||
|
||||
nvs_sec_cfg_t* sec_cfg = NULL;
|
||||
|
||||
#if CONFIG_NVS_ENCRYPTION
|
||||
nvs_sec_cfg_t cfg = {};
|
||||
nvs_sec_scheme_t *sec_scheme_handle = NULL;
|
||||
#if CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC
|
||||
nvs_sec_config_hmac_t sec_scheme_cfg = NVS_SEC_PROVIDER_CFG_HMAC_DEFAULT();
|
||||
if (nvs_sec_provider_register_hmac(&sec_scheme_cfg, &sec_scheme_handle) != ESP_OK) {
|
||||
ESP_EARLY_LOGE(TAG, "Registering the HMAC scheme failed");
|
||||
return;
|
||||
}
|
||||
#elif CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC
|
||||
nvs_sec_config_flash_enc_t sec_scheme_cfg = NVS_SEC_PROVIDER_CFG_FLASH_ENC_DEFAULT();
|
||||
if (sec_scheme_cfg.nvs_keys_part == NULL) {
|
||||
ESP_EARLY_LOGE(TAG, "partition with subtype \"nvs_keys\" not found");
|
||||
}
|
||||
|
||||
if (nvs_sec_provider_register_flash_enc(&sec_scheme_cfg, &sec_scheme_handle) != ESP_OK) {
|
||||
ESP_EARLY_LOGE(TAG, "Registering the Flash Encryption scheme failed");
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC */
|
||||
|
||||
if (nvs_bootloader_read_security_cfg(sec_scheme_handle, &cfg) != ESP_OK) {
|
||||
ESP_EARLY_LOGE(TAG, "Reading the NVS security configuration failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nvs_bootloader_secure_init(&cfg) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Secure initialization of NVS failed");
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_NVS_ENCRYPTION*/
|
||||
|
||||
// --- This is the request structure for the read function showing validation errors - function will return ESP_ERR_INVALID_ARG ---
|
||||
ESP_EARLY_LOGI(TAG, "Trying to read the NVS partition by passing invalid arguments");
|
||||
|
||||
nvs_bootloader_read_list_t bad_read_list_indicate_problems[] = {
|
||||
{ .namespace_name = "sunny_day", .key_name = "u8", .value_type = NVS_TYPE_U8 }, // ESP_ERR_NVS_NOT_FOUND
|
||||
// this is correct request, not found is expected default result code
|
||||
@ -75,19 +112,21 @@ void bootloader_after_init(void) {
|
||||
// too long key name
|
||||
{ .namespace_name = "clowny_day", .key_name = "blobeee", .value_type = NVS_TYPE_BLOB }, // ESP_ERR_INVALID_ARG
|
||||
// not supported data type
|
||||
{ .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff, .buff_len = 0 } },
|
||||
{ .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff_10, .buff_len = 0 } },
|
||||
// ESP_ERR_INVALID_SIZE
|
||||
// buffer size is 0
|
||||
{ .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = NULL, .buff_len = 10 } }
|
||||
{ .namespace_name = "sunny_day", .key_name = "string_66_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = NULL, .buff_len = 66 } }
|
||||
// ESP_ERR_INVALID_SIZE
|
||||
// buffer pointer is invalid
|
||||
};
|
||||
|
||||
size_t bad_read_list_indicate_problems_count = sizeof(bad_read_list_indicate_problems) / sizeof(bad_read_list_indicate_problems[0]);
|
||||
log_request_call_read_evaluate_output(nvs_partition_label, bad_read_list_indicate_problems, bad_read_list_indicate_problems_count);
|
||||
log_request_call_read_evaluate_output(nvs_partition_label, bad_read_list_indicate_problems, bad_read_list_indicate_problems_count, sec_cfg);
|
||||
|
||||
// --- This is the request structure for the read function showing runtime errors - function will return ESP_OK ---
|
||||
// but some records will have result_code set to ESP_ERR_NVS_NOT_FOUND, ESP_ERR_NVS_TYPE_MISMATCH, ESP_ERR_INVALID_SIZE
|
||||
ESP_EARLY_LOGI(TAG, "Trying to read the NVS partition by expecting incorrect data");
|
||||
|
||||
nvs_bootloader_read_list_t good_read_list_bad_results[] = {
|
||||
{ .namespace_name = "sunny_day", .key_name = "u8", .value_type = NVS_TYPE_I8 }, // ESP_ERR_NVS_TYPE_MISMATCH
|
||||
// data in the partition is of different type (NVS_TYPE_U8)
|
||||
@ -95,17 +134,20 @@ void bootloader_after_init(void) {
|
||||
// data in the partition won't be found, because there is a typo in the key name
|
||||
{ .namespace_name = "clowny_day", .key_name = "i8", .value_type = NVS_TYPE_I8 }, // ESP_ERR_NVS_NOT_FOUND
|
||||
// data in the partition won't be found, because there is typo in namespace name
|
||||
{ .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff, .buff_len = 2 } },
|
||||
{ .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff_10, .buff_len = 2 } },
|
||||
// ESP_ERR_INVALID_SIZE
|
||||
// buffer is too small
|
||||
{ .namespace_name = "sunny_day", .key_name = "u32", .value_type = NVS_TYPE_U32 }, // ESP_OK
|
||||
// this value will be read correctly
|
||||
{ .namespace_name = "sunny_day", .key_name = "u32", .value_type = NVS_TYPE_U32 } // ESP_ERR_NVS_NOT_FOUND
|
||||
{ .namespace_name = "sunny_day", .key_name = "u32", .value_type = NVS_TYPE_U32 }, // ESP_ERR_NVS_NOT_FOUND
|
||||
// this value won't be read as function doesn't support duplicate readings
|
||||
{ .namespace_name = "sunny_day", .key_name = "string_66_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff_66, .buff_len = 65 } }
|
||||
// ESP_ERR_INVALID_SIZE
|
||||
// buffer is just small
|
||||
};
|
||||
|
||||
size_t good_read_list_bad_results_count = sizeof(good_read_list_bad_results) / sizeof(good_read_list_bad_results[0]);
|
||||
log_request_call_read_evaluate_output(nvs_partition_label, good_read_list_bad_results, good_read_list_bad_results_count);
|
||||
log_request_call_read_evaluate_output(nvs_partition_label, good_read_list_bad_results, good_read_list_bad_results_count, sec_cfg);
|
||||
|
||||
|
||||
// --- This is the request structure for the read function showing all records found---
|
||||
@ -116,16 +158,23 @@ void bootloader_after_init(void) {
|
||||
// For NVS_TYPE_STR the value field is a structure with a pointer to the buffer and the buffer length is povided
|
||||
// In this case, the buffer is a stack allocated array of 10 characters plus space for the null terminator
|
||||
|
||||
ESP_EARLY_LOGI(TAG, "Trying to read the NVS partition correctly");
|
||||
|
||||
nvs_bootloader_read_list_t good_read_list[] = {
|
||||
{ .namespace_name = "sunny_day", .key_name = "u8", .value_type = NVS_TYPE_U8 },
|
||||
{ .namespace_name = "sunny_day", .key_name = "i32", .value_type = NVS_TYPE_I32 },
|
||||
{ .namespace_name = "cloudy_day", .key_name = "i8", .value_type = NVS_TYPE_I8 }, // mixed in different namespace
|
||||
{ .namespace_name = "sunny_day", .key_name = "u16", .value_type = NVS_TYPE_U16 },
|
||||
{ .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff, .buff_len = STR_BUFF_LEN } }
|
||||
{ .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff_10, .buff_len = STR_BUFF_LEN_10 } },
|
||||
{ .namespace_name = "sunny_day", .key_name = "string_66_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff_66, .buff_len = STR_BUFF_LEN_66 } }
|
||||
};
|
||||
|
||||
size_t good_read_list_count = sizeof(good_read_list) / sizeof(good_read_list[0]);
|
||||
log_request_call_read_evaluate_output(nvs_partition_label, good_read_list, good_read_list_count);
|
||||
log_request_call_read_evaluate_output(nvs_partition_label, good_read_list, good_read_list_count, sec_cfg);
|
||||
|
||||
#if CONFIG_NVS_ENCRYPTION
|
||||
nvs_bootloader_secure_deinit();
|
||||
#endif /* CONFIG_NVS_ENCRYPTION */
|
||||
|
||||
ESP_LOGI(TAG, "Finished bootloader part");
|
||||
}
|
||||
|
@ -1,3 +1,13 @@
|
||||
idf_component_register(SRCS "bootloader_hooks_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
nvs_create_partition_image(nvs ../nvs_data.csv FLASH_IN_PROJECT)
|
||||
|
||||
if(NOT CONFIG_NVS_ENCRYPTION)
|
||||
nvs_create_partition_image(nvs ../nvs_data.csv FLASH_IN_PROJECT)
|
||||
else()
|
||||
if(CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC)
|
||||
esptool_py_flash_to_partition(flash "nvs_key" ${PROJECT_DIR}/main/encryption_keys.bin)
|
||||
esptool_py_flash_to_partition(flash "nvs" ${PROJECT_DIR}/main/nvs_encrypted.bin)
|
||||
else() # NVS Encryption using HMAC (CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC)
|
||||
esptool_py_flash_to_partition(flash "nvs" ${PROJECT_DIR}/main/nvs_encrypted_hmac.bin)
|
||||
endif()
|
||||
endif()
|
||||
|
1
examples/storage/nvs_bootloader/main/encryption_keys.bin
Normal file
1
examples/storage/nvs_bootloader/main/encryption_keys.bin
Normal file
@ -0,0 +1 @@
|
||||
"""""""""""""""""""""""""""""""",锵<<3C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
@ -0,0 +1,2 @@
|
||||
|
||||
|
BIN
examples/storage/nvs_bootloader/main/nvs_encrypted.bin
Normal file
BIN
examples/storage/nvs_bootloader/main/nvs_encrypted.bin
Normal file
Binary file not shown.
BIN
examples/storage/nvs_bootloader/main/nvs_encrypted_hmac.bin
Normal file
BIN
examples/storage/nvs_bootloader/main/nvs_encrypted_hmac.bin
Normal file
Binary file not shown.
@ -10,6 +10,8 @@ i16,data,i16,-20000
|
||||
u32,data,u32,4294967295
|
||||
i32,data,i32,-2147483648
|
||||
string_10_chars,data,string,"Text_67890"
|
||||
# adding the below entry to read non-multiple lengths of XTS-AES block size
|
||||
string_66_chars,data,string,"Text_67890_Text_67890_Text_67890_0_Text_67890_Text_67890_Text_6789"
|
||||
# defines cloudy_day namespace to show it can be read at once with sunny_day one
|
||||
cloudy_day,namespace,,
|
||||
i8,data,i8,-13
|
||||
|
Can't render this file because it has a wrong number of fields in line 4.
|
@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
@ -9,5 +9,27 @@ from pytest_embedded import Dut
|
||||
def test_nvs_bootloader_example(dut: Dut) -> None:
|
||||
# Expect to read hooks messages and data from NVS partition
|
||||
dut.expect_exact('Before reading from NVS partition')
|
||||
dut.expect_exact('Result data. Return code: ESP_OK')
|
||||
dut.expect_exact('u8 U8 255')
|
||||
dut.expect_exact('i32 I32 -2147483648')
|
||||
dut.expect_exact('i8 I8 -13')
|
||||
dut.expect_exact('u16 U16 65535')
|
||||
dut.expect_exact('string_10_chars STR Text_67890')
|
||||
dut.expect_exact('string_66_chars STR Text_67890_Text_67890_Text_67890_0_Text_67890_Text_67890_Text_6789')
|
||||
dut.expect_exact('Finished bootloader part')
|
||||
dut.expect_exact('User application is loaded and running.')
|
||||
|
||||
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.nvs_encr_hmac
|
||||
@pytest.mark.parametrize('config', ['nvs_enc_hmac'], indirect=True)
|
||||
def test_nvs_bootloader_example_nvs_encr_hmac(dut: Dut) -> None:
|
||||
test_nvs_bootloader_example(dut)
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.flash_encryption
|
||||
@pytest.mark.parametrize('config', ['nvs_enc_flash_enc'], indirect=True)
|
||||
def test_nvs_bootloader_example_flash_enc(dut: Dut) -> None:
|
||||
test_nvs_bootloader_example(dut)
|
||||
|
@ -0,0 +1,13 @@
|
||||
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
|
||||
|
||||
CONFIG_NVS_ENCRYPTION=y
|
||||
CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC=y
|
||||
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_ENCRYPTED_NVS=y
|
12
examples/storage/nvs_bootloader/sdkconfig.ci.nvs_enc_hmac
Normal file
12
examples/storage/nvs_bootloader/sdkconfig.ci.nvs_enc_hmac
Normal file
@ -0,0 +1,12 @@
|
||||
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
|
||||
|
||||
CONFIG_NVS_ENCRYPTION=y
|
||||
CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC=y
|
||||
CONFIG_NVS_SEC_HMAC_EFUSE_KEY_ID=0
|
@ -1,4 +1,4 @@
|
||||
# The size of bootloader has been increased, so the partition table offset needs adjustment
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0xA000
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0xB000
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL=2
|
||||
|
@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32c2"
|
||||
CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER=y
|
Loading…
x
Reference in New Issue
Block a user