The example demonstrates the flash encryption application by providing a code using FATFS and NVS partitions on a device with the flash encryption enabled.
As the flash encryption can be enabled either in Development or Release mode, a guidance of how to use either mode is provided.
The example code checks if the flash encryption feature is enabled/disabled and if enabled, it prints a status information of all the eFuses related to the flash encryption mode (Development/Release) and FLASH_CRYPT_CNT (for ESP32) or SPI_BOOT_CRYPT_CNT (for ESP32-S2 and newer targets).
1. Writing and reading encrypted partitions in the flash memory.
2. Initialization of FATFS, formatting, writing and reading file. Both encrypted as well as non-encrypted.
3. Initialization of encrypted NVS partition. Both auto-generated as well as pre-generated NVS key scenarios are presented.
4. Flashing the example in Development as well as Release mode.
### NVS example
The example demonstrates default and custom NVS partition initialisation when the flash encryption is enabled. From the code perspective the use of NVS API is transparent regardless of the flash encryption mode.
### FATFS example
FATFS example function finds non-encrypted partition `fat_not_encr`, erases it, creates FATFS, formats and mounts the file system. Then it creates file `/spiflash/inner.txt` using `fopen`, writes test string using `fprintf` and closes the file using `fclose`. Then it uses `fopen` and `fgets` to read the string back to verify it is correctly written. The last step of the example on unencrypted partition uses `esp_flash_read` to locate the test string in the underlying partition without involving potential cryptographic layer. The test string offset from the beginning of the partition is returned to the second part of the example.
The second part of the example repeats FATFS related steps on the encrypted partition named `fat_encrypted`. The same sequence of steps as in the non encrypted partition means, that the file data should be present on the same offset in the partition. Therefore, direct partition reading using `esp_flash_read` uses the offset returned by the first part of example. Depending on encryption settings in the menuconfig, the test string is then read in the visible form `(SECURE_FLASH_ENC_ENABLED not set)` or in encrypted form `(SECURE_FLASH_ENC_ENABLED=y)`
Enable the flash encryption mode (Development or Release) under (`menuconfig -> Security Features`). Default usage mode is Development (recommended during test and development phase).
Note: After enabling flash encryption, the bootloader size increases, which means that default offset of the partition table (`menuconfig -> Partition Table -> Offset of partition table`) has to be increased from default value of 0x8000 to prevent the bootloader from overlapping with the partition table. In this example, the offset of the partition table is incereased to 0xD000.
If you chose the Release mode, for better security, you can also disable UART ROM download mode using (`menuconfig -> Security features -> UART ROM download mode -> Permanently disabled`)
Note: This option, after first start of application, makes the device forever inaccessible using UART. Use it with care.
This example demonstrates working with several non-default partitions, therefore a custom partition configuration file is selected. The custom file option (`menuconfig -> Partition Table -> Partition table = Custom partition table CSV`) and the name of file is set in (`menuconfig -> Partition Table -> Custom partition CSV file`). The additional entries in the custom partition table CSV file on top of default ones are described in the NVS and FATFS related subsections below.
For better security, the NVS encryption (`menuconfig -> Component config -> NVS -> Enable NVS encryption`) is enabled by default when the flash encryption is enabled in the menuconfig. If you decide to disable the NVS encryption, you can skip the NVS configuration step given below.
With the NVS encryption is enabled, the partition table must contain additional [NVS key partition](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_flash.html#nvs-key-partition) atop of all the NVS partitions holding data. This example uses two NVS partitions (`storage`, `nvs`) and one NVS key partition `nvs_keys`. All the partitions required are already preconfigured in `partitions_example.csv`
The partition configuration for the NVS encryption involves generating NVS encryption keys to be stored in [NVS key partition](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_flash.html#nvs-key-partition). The NVS encryption keys can be either automatically generated by ESP32-based chip during the first run of the application, or generated and flashed to the device by the application developer.
Automatic generation of NVS encryption keys on ESP32-based chips:
When the NVS encryption is enabled, the `nvs_flash_init` API function can internally generate the XTS encryption keys on the ESP32-based chip. The API function finds the first [NVS key partition](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_flash.html#nvs-key-partition), i.e. a partition of type `data` and subtype `nvs_keys` and when this partition is empty, the API function automatically generates NVS keys. (Consult the [`nvs_flash_init`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_flash.html#_CPPv414nvs_flash_initv) API documentation in the ESP-IDF programming guide for more details).
**Please note that `nvs_keys` partition has to be completely erased before starting the application. Otherwise the application may generate `ESP_ERR_NVS_CORRUPT_KEY_PART` error code assuming that `nvs_keys` partition was not empty and contains malformed data.**
Flash pre-generated NVS encryption keys:
This method is useful when application developer requires presence of known NVS encryption keys on a device. I.e. to allow easier analysis of the NVS partition on a host computer.
For convenience, file `sample_encryption_keys.bin` with a sample of pre-generated NVS encryption keys is provided.
FATFS encryption example uses two additional partitions in the partition table. Both partitions are of type `data` and subtype `fat`. The first partition `fat_encrypted` has the encrypted flag set, the second partition `fat_not_encr` has the encryption flag off. Both partitions are defined in the partition configuration file `partitions_example.csv`
There are two flash encryption modes: Development and Release.
ESP32-based device's bootloader in the Development mode can encrypt the data to be flashed, whilst the bootloader in the Release mode cannot. Therefore the data to be flashed in Release mode has to be encrypted on a host system in advance. The workflows for both options are described below.
2. If you want use pre-generated NVS encryption keys, flash the `sample_encryption_keys.bin` into the `nvs_key`partition (on the flash) with the help of [parttool.py](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html#partition-tool-parttool-py):
The sample [NVS key partition](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_flash.html#nvs-key-partition) partition used in this example is generated with the help of [NVS Partition Generator Utility](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_partition_gen.html#nvs-partition-generator-utility)
3. When building the project and flashing it to the board FOR THE FIRST TIME (after enabling the flash encryption feature in menuconfig), run the following command to program the target and monitor the output:
4. When reprogramming the device, subsequently use the following command for encrypted write of the new non-encrypted application's binary:
```
idf.py --port $PORT encrypted-app-flash monitor
```
5. Please note that above-mentioned command programs only the 'app' partition. In order to reprogram all the partitions (bootloader, partition table and application) and let the bootloader encrypt them on the device, use:
```
idf.py --port $PORT encrypted-flash monitor
```
#### Release mode
Full example including host generated NVS encryption keys and pre-encryption of the flash on the host is provided for ESP32 ECO3.
Steps:
1. Generate an unique flash encryption key on a host
Alternatively, if you want to further develop the workflow, it might be useful to use all-zeroes "easy to restore" flash encryption key instead. However, never use this key in a production!
The offsets of the bootloader, application code and the partition table binaries can be found in `build/flash_args`
```
0x1000 bootloader/bootloader.bin
0x20000 flash_encryption.bin
0xd000 partition_table/partition-table.bin
```
From the example application perspective it is necessary to encrypt every artefact except for 'nvs', 'custom_nvs' and 'fat_not_encr' partitions, as their content is expected to be plaintext.
Ignore the warning `Warning: Image file at 0x1000 doesn't look like an image file, so not changing any flash settings.`
Output flash binary file is `build\merged_encrypted_flash.bin`
6. Burn the encryption key and set the efuses in ESP32-based device
This step can be performed only once. If the workflow is using non-zero flash encryption key generated in step 1, keep the encryption key file on a safe place for future application updates.
Once the flash encryption is enabled the device will reset itself. At this stage the flash contents are in encrypted form. The output would be similar to:
```
Example to check Flash Encryption status
This is ESP32 chip with 2 CPU cores, WiFi/BT/BLE, silicon revision 0, 4MB external flash
FLASH_CRYPT_CNT eFuse value is 1
Flash encryption feature is enabled in DEVELOPMENT mode
In the Release mode no in-place encryption happens after the first reset, as the bootloader assumes the flashed data is already encrypted. The log below shows the run of application after successfully finished flashing.
```
Example to check Flash Encryption status
This is esp32 chip with 2 CPU core(s), WiFi/BT/BLE, silicon revision v1.0, 4MB external flash
FLASH_CRYPT_CNT eFuse value is 127
Flash encryption feature is enabled in RELEASE mode