From 776c06d5bbda9ea59f8eb28a8d328251fe619d61 Mon Sep 17 00:00:00 2001 From: Shubham Patil Date: Sat, 9 Jul 2022 18:23:18 +0530 Subject: [PATCH] [ESP32] Support flash encryption and nvs encryption (#20414) - Added an option to encrypt the factory partition. - Securely initialize the NVS partition if NVS encryption is enabled - Added user guide for flash encryption in lighting-app/esp32 --- examples/lighting-app/esp32/README.md | 48 +++++++++++++++++ .../esp32/partitions_encrypted.csv | 8 +++ .../tools/generate_esp32_chip_factory_bin.py | 54 ++++++++++++------- .../ESP32/ConfigurationManagerImpl.cpp | 51 ++++++++++++++++++ 4 files changed, 143 insertions(+), 18 deletions(-) create mode 100644 examples/lighting-app/esp32/partitions_encrypted.csv diff --git a/examples/lighting-app/esp32/README.md b/examples/lighting-app/esp32/README.md index 2e8d1548db3570..eae01ad14d2978 100644 --- a/examples/lighting-app/esp32/README.md +++ b/examples/lighting-app/esp32/README.md @@ -10,6 +10,7 @@ This example demonstrates the Matter Lighting application on ESP platforms. - [Commissioning over BLE using chip-tool](#commissioning-over-ble-using-chip-tool) - [Cluster Control](#cluster-control) - [Steps to Try Lighting app OTA Requestor](#steps-to-try-lighting-app-ota-requestor) +- [Flash Encryption](#flash-encryption) --- @@ -255,3 +256,50 @@ type below query. Once the transfer is complete, OTA requestor sends ApplyUpdateRequest command to OTA provider for applying the image. Device will restart on successful application of OTA image. + +# Flash encryption + +Below is the quick start guide for encrypting the application and factory +partition but before proceeding further please READ THE DOCS FIRST. +Documentation References: + +- [Flash Encryption](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/flash-encryption.html) +- [NVS Encryption](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_flash.html#nvs-encryption) + +## Enable flash encryption and some factory settings using `idf.py menuconfig` + +- Enable the Flash encryption [Security features → Enable flash encryption on + boot] +- Use `partitions_encrypted.csv` partition table [Partition Table → Custom + partition CSV file] +- Enable ESP32 Factory Data Provider [Component config → CHIP Device Layer → + Commissioning options → Use ESP32 Factory Data Provider] +- Enable ESP32 Device Instance Info Provider [Component config → CHIP Device + Layer → Commissioning options → Use ESP32 Device Instance Info Provider] + +## Generate the factory partition using `generate_esp32_chip_factory_bin.py` script + +- Please provide `-e` option along with other options to generate the + encrypted factory partition +- Two partition binaries will be generated `factory_partition.bin` and + `keys/nvs_key_partition.bin` + +## Flashing the application, factory partition, and nvs keys + +- Flash the application using `idf.py flash`, NOTE: If not flashing for the + first time you will have to use `idf.py encrypted-flash` + +- Flash the factory partition, this SHALL be non encrypted write as NVS + encryption works differently + +``` +esptool.py -p (PORT) write_flash 0x9000 path/to/factory_partition.bin +``` + +- Encrypted flash the nvs keys partition + +``` +esptool.py -p (PORT) write_flash --encrypt 0x317000 path/to/nvs_key_partition.bin +``` + +NOTE: Above command uses the default addressed printed in the boot logs diff --git a/examples/lighting-app/esp32/partitions_encrypted.csv b/examples/lighting-app/esp32/partitions_encrypted.csv new file mode 100644 index 00000000000000..807ee35dc8e645 --- /dev/null +++ b/examples/lighting-app/esp32/partitions_encrypted.csv @@ -0,0 +1,8 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, , 0x6000, +otadata, data, ota, , 0x2000, encrypted +phy_init, data, phy, , 0x1000, encrypted +ota_0, app, ota_0, , 1500K, encrypted +ota_1, app, ota_1, , 1500K, encrypted +nvs_key, data, nvs_keys,, 0x1000, encrypted diff --git a/scripts/tools/generate_esp32_chip_factory_bin.py b/scripts/tools/generate_esp32_chip_factory_bin.py index 76975d706317af..fbf73f4dbc4c16 100755 --- a/scripts/tools/generate_esp32_chip_factory_bin.py +++ b/scripts/tools/generate_esp32_chip_factory_bin.py @@ -41,6 +41,11 @@ TOOLS = {} +FACTORY_PARTITION_CSV = 'nvs_partition.csv' +FACTORY_PARTITION_BIN = 'factory_partition.bin' +NVS_KEY_PARTITION_BIN = 'nvs_key_partition.bin' + + FACTORY_DATA = { # CommissionableDataProvider 'discriminator': { @@ -262,28 +267,39 @@ def generate_nvs_bin(args): continue csv_content += f"{k},{v['type']},{v['encoding']},{v['value']}\n" - with open('nvs_partition.csv', 'w') as f: + with open(FACTORY_PARTITION_CSV, 'w') as f: f.write(csv_content) - nvs_args = SimpleNamespace(input='nvs_partition.csv', - output='partition.bin', - size=hex(args.size), - outdir=os.getcwd(), - version=2) - - nvs_partition_gen.generate(nvs_args) - - -def print_flashing_help(): - logging.info('To flash the generated partition.bin, run the following command:') - logging.info('==============================================================') - logging.info('esptool.py -p write_flash partition.bin') - logging.info('==============================================================') - logging.info('default \"nvs\" partition addr is 0x9000') + if args.encrypt: + nvs_args = SimpleNamespace(version=2, + keygen=True, + keyfile=NVS_KEY_PARTITION_BIN, + inputkey=None, + outdir=os.getcwd(), + input=FACTORY_PARTITION_CSV, + output=FACTORY_PARTITION_BIN, + size=hex(args.size)) + nvs_partition_gen.encrypt(nvs_args) + else: + nvs_args = SimpleNamespace(input=FACTORY_PARTITION_CSV, + output=FACTORY_PARTITION_BIN, + size=hex(args.size), + outdir=os.getcwd(), + version=2) + nvs_partition_gen.generate(nvs_args) + + +def print_flashing_help(encrypt): + logging.info('Run below command to flash {}'.format(FACTORY_PARTITION_BIN)) + logging.info('esptool.py -p (PORT) write_flash (FACTORY_PARTITION_ADDR) {}'.format(os.path.join(os.getcwd(), FACTORY_PARTITION_BIN))) + if (encrypt): + logging.info('Run below command to flash {}'.format(NVS_KEY_PARTITION_BIN)) + logging.info('esptool.py -p (PORT) write_flash --encrypt (NVS_KEY_PARTITION_ADDR) {}'.format( + os.path.join(os.getcwd(), 'keys', NVS_KEY_PARTITION_BIN))) def clean_up(): - os.remove('nvs_partition.csv') + os.remove(FACTORY_PARTITION_CSV) os.remove(FACTORY_DATA['dac-pub-key']['value']) os.remove(FACTORY_DATA['dac-key']['value']) @@ -323,6 +339,8 @@ def any_base_int(s): return int(s, 0) parser.add_argument('-s', '--size', type=any_base_int, required=False, default=0x6000, help='The size of the partition.bin, default: 0x6000') + parser.add_argument('-e', '--encrypt', action='store_true', required=False, + help='Encrypt the factory parititon NVS binary') args = parser.parse_args() validate_args(args) @@ -331,7 +349,7 @@ def any_base_int(s): return int(s, 0) populate_factory_data(args, spake2p_params) gen_raw_ec_keypair_from_der(args.dac_key, FACTORY_DATA['dac-pub-key']['value'], FACTORY_DATA['dac-key']['value']) generate_nvs_bin(args) - print_flashing_help() + print_flashing_help(args.encrypt) clean_up() diff --git a/src/platform/ESP32/ConfigurationManagerImpl.cpp b/src/platform/ESP32/ConfigurationManagerImpl.cpp index 99756eb0fcd393..ba2bdddef6b7d8 100644 --- a/src/platform/ESP32/ConfigurationManagerImpl.cpp +++ b/src/platform/ESP32/ConfigurationManagerImpl.cpp @@ -61,6 +61,56 @@ CHIP_ERROR ConfigurationManagerImpl::Init() CHIP_ERROR err; uint32_t rebootCount; +#ifdef CONFIG_NVS_ENCRYPTION + nvs_sec_cfg_t cfg = {}; + esp_err_t esp_err = ESP_FAIL; + + const esp_partition_t * key_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, NULL); + if (key_part == NULL) + { + ChipLogError(DeviceLayer, + "CONFIG_NVS_ENCRYPTION is enabled, but no partition with subtype nvs_keys found in the partition table."); + SuccessOrExit(MapConfigError(esp_err)); + } + + esp_err = nvs_flash_read_security_cfg(key_part, &cfg); + if (esp_err == ESP_ERR_NVS_KEYS_NOT_INITIALIZED) + { + ChipLogError(DeviceLayer, "NVS key partition empty"); + SuccessOrExit(MapConfigError(esp_err)); + } + else if (esp_err != ESP_OK) + { + ChipLogError(DeviceLayer, "Failed to read NVS security cfg, err:0x%02x", esp_err); + SuccessOrExit(MapConfigError(esp_err)); + } + + // Securely initialize the nvs partitions, + // nvs_flash_secure_init_partition() will initialize the partition only if it is not already initialized. + esp_err = nvs_flash_secure_init_partition(CHIP_DEVICE_CONFIG_CHIP_FACTORY_NAMESPACE_PARTITION, &cfg); + if (esp_err == ESP_ERR_NVS_NO_FREE_PAGES || esp_err == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + ChipLogError(DeviceLayer, "Failed to initialize NVS partition %s err:0x%02x", + CHIP_DEVICE_CONFIG_CHIP_FACTORY_NAMESPACE_PARTITION, esp_err); + SuccessOrExit(MapConfigError(esp_err)); + } + + esp_err = nvs_flash_secure_init_partition(CHIP_DEVICE_CONFIG_CHIP_CONFIG_NAMESPACE_PARTITION, &cfg); + if (esp_err == ESP_ERR_NVS_NO_FREE_PAGES || esp_err == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + ChipLogError(DeviceLayer, "Failed to initialize NVS partition %s err:0x%02x", + CHIP_DEVICE_CONFIG_CHIP_CONFIG_NAMESPACE_PARTITION, esp_err); + SuccessOrExit(MapConfigError(esp_err)); + } + + esp_err = nvs_flash_secure_init_partition(CHIP_DEVICE_CONFIG_CHIP_COUNTERS_NAMESPACE_PARTITION, &cfg); + if (esp_err == ESP_ERR_NVS_NO_FREE_PAGES || esp_err == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + ChipLogError(DeviceLayer, "Failed to initialize NVS partition %s err:0x%02x", + CHIP_DEVICE_CONFIG_CHIP_COUNTERS_NAMESPACE_PARTITION, esp_err); + SuccessOrExit(MapConfigError(esp_err)); + } +#else // Initialize the nvs partitions, // nvs_flash_init_partition() will initialize the partition only if it is not already initialized. esp_err_t esp_err = nvs_flash_init_partition(CHIP_DEVICE_CONFIG_CHIP_FACTORY_NAMESPACE_PARTITION); @@ -69,6 +119,7 @@ CHIP_ERROR ConfigurationManagerImpl::Init() SuccessOrExit(MapConfigError(esp_err)); esp_err = nvs_flash_init_partition(CHIP_DEVICE_CONFIG_CHIP_COUNTERS_NAMESPACE_PARTITION); SuccessOrExit(MapConfigError(esp_err)); +#endif // Force initialization of NVS namespaces if they doesn't already exist. err = ESP32Config::EnsureNamespace(ESP32Config::kConfigNamespace_ChipFactory);