Программирование ARM ESP32 Flash Encryption Sun, November 09 2025  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


ESP32 Flash Encryption Печать
Добавил(а) microsin   

Это руководство для быстрого старта применения технологии шифровании памяти кода программ микроконтроллеров ESP32 (flash encryption, перевод документации [1]). В качестве примера используется код приложения, на котором демонстрируется процесс тестирования и верификации операций flash encryption во время разработки приложения и во время выпуска готовой продукции.

Flash Encryption предназначена для шифрования содержимого внешнего чипа памяти программ (SPI flash) ESP32. Как только эта фича разрешена, firmware приложения прошивается как plaintext, и затем данные шифруются по месту при первой загрузке. В результате физического считывания флеш-памяти будет недостаточно для восстановления большей части её содержимого.

Secure Boot [2] это отдельная фича, которая может использоваться совместно с flash encryption, чтобы добиться еще более защищенного программного окружения.

Важные замечания:

1. Для использования в производстве готовой продукции (production) технология flash encryption должна быть разрешена только в режиме релиза (Release mode).
2. Разрешение flash encryption ограничивает опции для будущего обновления ESP32. Перед использованием этой фичи ознакомьтесь с документацией и убедитесь, что понимаете последствия.

[Зашифрованные разделы (Encrypted Partitions)]

С разрешенной фичей flash encryption по умолчанию шифруются следующие типы данных:

● Second Stage Bootloader (загрузчик второй стадии [3]).
● Partition Table (таблица разделов)
● NVS Key Partition (таблица ключей энергонезависимой памяти)
● Otadata (данные беспроводного обновления OTA)
● Все разделы приложений (app type partitions)

Другие типы данных могут быть зашифрованы по условию:

● Любой раздел, помеченный флагом encrypted в таблице разделов. Подробности см. в секции Encrypted Partition Flag.
● Цифровая подпись загрузчика (Secure Boot bootloader digest), если разрешена фича Secure Boot (описание см. далее).

[Биты eFuse, относящиеся к Flash Encryption]

Работа flash encryption управляется различными однократно прошиваемыми битами (eFuses), доступными на чипе ESP32. Список этих фьюзов приведен в таблице ниже. Имена в столбце eFuse также используется утилитой командной строки espefuse.py и eFuse-командами idf.py. Для использования eFuse API измените имя добавлением префикса ESP_EFUSE_, например: esp_efuse_read_field_bit (ESP_EFUSE_DISABLE_DL_ENCRYPT).

Таблица 1. Фьюзы, используемые в технологии Flash Encryption.

eFuse Описание Колич. бит
CODING_SCHEME Управляет фактическим количеством бит block1, используемых для генерации конечного 256-разрядного ключа AES. Возможные значения: 0 для 256 бит, 1 для 192 бит, 2 для 128 бит. Финальный ключ AES вычисляется на базе значения FLASH_CRYPT_CONFIG. 2
flash_encryption (block1) Хранилище ключа AES. 256 bit key block
FLASH_CRYPT_CONFIG Управляет процессом шифрования AES. 4
DISABLE_DL_ENCRYPT Если установлен, то запрещает операцию flash encryption, когда работает режим Firmware Download. 1
DISABLE_DL_DECRYPT Если установлен, то запрещает flash decryption, когда работает режим UART Firmware Download. 1
FLASH_CRYPT_CNT 2n число, которое показывает, было ли зашифровано содержимое flash.
● Если установлено нечетное количество бит (например 0b0000001 или 0b0000111), то это показывает, что содержимое flash зашифровано. Содержимое нужно прозрачно расшифровать при чтении.
● Если установлено четное количество бит (например 0b0000000 или 0b0000011), то это показывает, что содержимое flash не зашифровано (т. е. выглядит как обычный текст, plain text).

С каждым последующим обновлением незашифрованной флэш-памяти (например, прошивкой нового незашифрованного двоичного файла) и шифрованием флэш-памяти (с помощью опции Enable flash encryption on boot) устанавливается следующий MSB-бит FLASH_CRYPT_CNT.
7

Замечания:

● Управление доступом для чтения/записи доступно для всех битов eFuse, перечисленных в таблице выше.
● После изготовления чипа значение этих битов по умолчанию — 0.

Доступ на чтение и запись бит eFuse управляется соответствующими полями в регистрах WR_DIS и RD_DIS. Для дополнительной информации по фьюзам ESP32 см. описание eFuse manager [5]. Чтобы поменять биты защиты поля eFuse с помощью idf.py, используйте следующие 2 команды: efuse-read-protect и efuse-write-protect (псевдонимы espefuse.py-команд write_protect_efuse и read_protect_efuse на основе idf.py). Например idf.py efuse-write-protect DISABLE_DL_ENCRYPT.

[Процесс шифрования содержимого Flash]

Предположим, что значения бит eFuse находятся в их состояниях по умолчанию и second stage bootloader скомпилирован для поддержки flash encryption. В этом случае процесс шифрования памяти flash (flash encryption process) выполняется, как показано ниже:

1. На первом сбросе по питанию (power-on reset) все данные в flash не зашифрованы (plaintext). Загрузчик первой стадии, находящийся в ПЗУ (first stage (ROM) bootloader) загружает second stage bootloader.

2. Second stage bootloader читает значение FLASH_CRYPT_CNT eFuse (0b0000000). Поскольку прочитанное значение 0 (установлено четное количество бит), он конфигурирует и разрешает flash encryption block. Он также установит FLASH_CRYPT_CONFIG eFuse в 0xF. Для дополнительной информации по flash encryption block см. ESP32 Technical Reference Manual -> eFuse Controller (eFuse) > Flash Encryption Block ([PDF]).

3. Second stage bootloader сначала проверяет, присутствует ли уже в eFuse корректный ключ (т. е. прошитый с помощью утилиты espefuse), тогда процесс генерации ключа пропускается, и тот же самый ключ используется для flash encryption process. Иначе Second stage bootloader использует модуль RNG (генератор случайных чисел, random) для генерации AES-256 бит ключа, и записывает его в flash_encryption eFuse. К этому ключу нельзя получить доступ из программы, поскольку установлены биты защиты записи и чтения для flash_encryption eFuse. Операции flash encryption происходят полностью аппаратно, и ключ шифрования недоступен программно.

4. Flash encryption block шифрует содержимое flash - second stage bootloader, приложения и разделы, помеченные как encrypted. Шифрование по месту может занимать некоторое время, до минуты для больших разделов.

5. Second stage bootloader установит первый доступный бит в FLASH_CRYPT_CNT (0b0000001), чтобы пометить содержимое flash как зашифрованное. Этим установлено нечетное количество бит.

6. Для Development Mode режима second stage bootloader установит только eFuse биты DISABLE_DL_DECRYPT и DISABLE_DL_CACHE, чтобы позволить UART перепрошить зашифрованные бинарники. Также FLASH_CRYPT_CNT биты eFuse НЕ защищены от записи.

7. Для Release Mode режима second stage bootloader установит eFuse биты DISABLE_DL_ENCRYPT, DISABLE_DL_DECRYPT и DISABLE_DL_CACHE в 1, чтобы не дать возможности для UART bootloader расшифровать содержимое flash. Он также защитит от записи биты FLASH_CRYPT_CNT eFuse. Чтобы изменить это поведение, см. "Разрешение UART Bootloader Encryption/Decryption".

8. Затем устройство перезагружается, чтобы начать выполнять зашифрованный образ. Second stage bootloader вызывает flash decryption block для расшифровки содержимого flash, и затем загружает расшифрованное содержимое в IRAM.

На стадии разработки существует часто необходимость программировать различные не зашифрованные образы (plaintext flash images) и тестировать процесс шифрования (flash encryption process). Это требует, чтобы режим Firmware Download смог загрузить новые plaintext images столько раз, сколько может потребоваться. Однако во время производства готовой продукции режим Firmware Download не должен быть разрешен по соображениям безопасности, чтобы не было доступа к содержимому flash.

Как следствие были созданы две разные конфигурации шифрования flash: для разработки (Development Mode) и для производства (Release Mode). Дополнительные сведения об этих конфигурациях см. далее в разделе "Конфигурация Flash Encryption".

[Конфигурация Flash Encryption]

Доступны 2 режима шифрования flash:

Development Mode - этот режим рекомендуется использовать только во время разработки. В этом режиме все еще можно записать новое plaintext firmware в устройство, и bootloader будет прозрачно шифровать это firmware, используя сохраненный аппаратно ключ. Это позволяет, косвенно, прочитать plaintext firmware в flash.

Release Mode - рекомендуется для производства готовых изделий. В этом режиме без знания ключа шифрования больше невозможно прошить plaintext firmware в устройство.

В этой секции предоставлена информация по этим режимам flash encryption, а также пошаговые инструкции для их использования.

Во время разработки вы можете зашифровать flash, используя либо сгенерированный с помощью ESP32 ключ, либо сгенерированный внешне на хосте разработки ключ.

Использование ключа, сгенерированного ESP32. Режим разработки (development mode) позволяет вам загрузить несколько не зашифрованных образов (plaintext images), используя Firmware Download mode.

Для проверки процесса шифрования flash выполните следующие шаги:

1. Убедитесь, что на вашем устройстве ESP32 биты flash encryption eFuse находятся в состоянии по умолчанию, как показано в таблице 1 выше. Как проверить состояние этих бит eFuse, см. врезку "Проверка ESP32 Flash Encryption Status".

2. В конфигураторе проекта (menuconfig) сделайте следующее:

● Enable flash encryption on boot (разрешение шифрования flash при загрузке).
● Select encryption mode (выберите режим шифрования, по умолчанию выбран Development).
● Select UART ROM download mode (выберите режим загрузчика ПЗУ, по умолчанию разрешен). Обратите внимание, что для ESP32 target выбор возможен только когда уровень CONFIG_ESP32_REV_MIN установлен на 3 (ESP32 V3).
● Select the appropriate bootloader log verbosity (выберите подходящий уровень подробности загрузчика второго уровня; для экономии памяти обычно отключают вывод загрузчика: No output).
● Сохраните конфигурацию и выйдите из menuconfig.

Разрешение flash encryption увеличит размер bootloader, что может потребовать обновления смещения таблицы разделов (partition table offset). См. Bootloader Size.

3. Для сборки образов кода и прошивки выполните команду:

idf.py flash monitor

Замечание: эта команда не включает пользовательские файлы, которые следует записать в разделы flash-памяти. Пожалуйста, запишите их вручную перед выполнением этой команды, в противном случае эти файлы необходимо будет зашифровать отдельно перед записью.

Эта команда запишет в память flash не зашифрованные образы: second stage bootloader (загрузчик второй стадии загрузки), partition table (таблица разделов) и applications (приложения). Как только прошивка завершится, ESP32 будет сброшен. При последующей загрузке second stage bootloader зашифрует: second stage bootloader, разделы приложений и разделы, помеченные как encrypted, и затем снова выполнит сброс. Шифрование по месту может занять некоторое время, до одной минуты на больших разделах. После этого приложение расшифровывается runtime и выполняется.

Пример вывода первой загрузки ESP32 после разрешения flash encryption (содержимое flash еще не зашифровано):

--- idf_monitor on /dev/cu.SLAB_USBtoUART 115200 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
ets Jun  8 2016 00:22:57
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) configsip: 0, SPIWP:0xee clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 mode:DIO, clock div:2 load:0x3fff0018,len:4 load:0x3fff001c,len:8452 load:0x40078000,len:13608 load:0x40080400,len:6664 entry 0x40080764 I (28) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader I (29) boot: compile time 15:37:14 I (30) boot: Enabling RNG early entropy source... I (35) boot: SPI Speed : 40MHz I (39) boot: SPI Mode : DIO I (43) boot: SPI Flash Size : 4MB I (47) boot: Partition Table: I (51) boot: ## Label Usage Type ST Offset Length I (58) boot: 0 nvs WiFi data 01 02 0000a000 00006000 I (66) boot: 1 phy_init RF data 01 01 00010000 00001000 I (73) boot: 2 factory factory app 00 00 00020000 00100000 I (81) boot: End of partition table I (85) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x0808c ( 32908) map I (105) esp_image: segment 1: paddr=0x000280b4 vaddr=0x3ffb0000 size=0x01ea4 ( 7844) load I (109) esp_image: segment 2: paddr=0x00029f60 vaddr=0x40080000 size=0x00400 ( 1024) load 0x40080000: _WindowOverflow4 at esp-idf/esp-idf/components/freertos/xtensa_vectors.S:1778
I (114) esp_image: segment 3: paddr=0x0002a368 vaddr=0x40080400 size=0x05ca8 ( 23720) load I (132) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x126a8 ( 75432) map 0x400d0018: _flash_cache_start at ??:?
I (159) esp_image: segment 5: paddr=0x000426c8 vaddr=0x400860a8 size=0x01f4c ( 8012) load 0x400860a8: prvAddNewTaskToReadyList at esp-idf/esp-idf/components/freertos/tasks.c:4561
I (168) boot: Loaded app from partition at offset 0x20000 I (168) boot: Checking flash encryption... I (168) flash_encrypt: Generating new flash encryption key... I (187) flash_encrypt: Read & write protecting new key... I (187) flash_encrypt: Setting CRYPT_CONFIG efuse to 0xF W (188) flash_encrypt: Not disabling UART bootloader encryption I (195) flash_encrypt: Disable UART bootloader decryption... I (201) flash_encrypt: Disable UART bootloader MMU cache... I (208) flash_encrypt: Disable JTAG... I (212) flash_encrypt: Disable ROM BASIC interpreter fallback... I (219) esp_image: segment 0: paddr=0x00001020 vaddr=0x3fff0018 size=0x00004 ( 4) I (227) esp_image: segment 1: paddr=0x0000102c vaddr=0x3fff001c size=0x02104 ( 8452) I (239) esp_image: segment 2: paddr=0x00003138 vaddr=0x40078000 size=0x03528 ( 13608) I (249) esp_image: segment 3: paddr=0x00006668 vaddr=0x40080400 size=0x01a08 ( 6664) I (657) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x0808c ( 32908) map I (669) esp_image: segment 1: paddr=0x000280b4 vaddr=0x3ffb0000 size=0x01ea4 ( 7844) I (672) esp_image: segment 2: paddr=0x00029f60 vaddr=0x40080000 size=0x00400 ( 1024) 0x40080000: _WindowOverflow4 at esp-idf/esp-idf/components/freertos/xtensa_vectors.S:1778
I (676) esp_image: segment 3: paddr=0x0002a368 vaddr=0x40080400 size=0x05ca8 ( 23720) I (692) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x126a8 ( 75432) map 0x400d0018: _flash_cache_start at ??:?
I (719) esp_image: segment 5: paddr=0x000426c8 vaddr=0x400860a8 size=0x01f4c ( 8012) 0x400860a8: prvAddNewTaskToReadyList at esp-idf/esp-idf/components/freertos/tasks.c:4561
I (722) flash_encrypt: Encrypting partition 2 at offset 0x20000... I (13229) flash_encrypt: Flash encryption completed I (13229) boot: Resetting with flash encryption enabled...

Пример вывода последующих загрузок ESP32, где просто упоминается, что flash encryption уже разрешено (содержимое flash зашифровано):

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:8452
load:0x40078000,len:13652
ho 0 tail 12 room 4
load:0x40080400,len:6664
entry 0x40080764
I (30) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader
I (30) boot: compile time 16:32:53
I (31) boot: Enabling RNG early entropy source...
I (37) boot: SPI Speed      : 40MHz
I (41) boot: SPI Mode       : DIO
I (45) boot: SPI Flash Size : 4MB
I (49) boot: Partition Table:
I (52) boot: ## Label            Usage          Type ST Offset   Length
I (60) boot:  0 nvs              WiFi data        01 02 0000a000 00006000
I (67) boot:  1 phy_init         RF data          01 01 00010000 00001000
I (75) boot:  2 factory          factory app      00 00 00020000 00100000
I (82) boot: End of partition table
I (86) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x0808c ( 32908) map
I (107) esp_image: segment 1: paddr=0x000280b4 vaddr=0x3ffb0000 size=0x01ea4 (  7844) load
I (111) esp_image: segment 2: paddr=0x00029f60 vaddr=0x40080000 size=0x00400 (  1024) load
0x40080000: _WindowOverflow4 at esp-idf/esp-idf/components/freertos/xtensa_vectors.S:1778
I (116) esp_image: segment 3: paddr=0x0002a368 vaddr=0x40080400 size=0x05ca8 ( 23720) load I (134) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x126a8 ( 75432) map 0x400d0018: _flash_cache_start at ??:?
I (162) esp_image: segment 5: paddr=0x000426c8 vaddr=0x400860a8 size=0x01f4c ( 8012) load 0x400860a8: prvAddNewTaskToReadyList at esp-idf/esp-idf/components/freertos/tasks.c:4561
I (171) boot: Loaded app from partition at offset 0x20000 I (171) boot: Checking flash encryption... I (171) flash_encrypt: flash encryption is enabled (3 plaintext flashes left) I (178) boot: Disabling RNG early entropy source... I (184) cpu_start: Pro cpu up. I (188) cpu_start: Application information: I (193) cpu_start: Project name: flash-encryption I (198) cpu_start: App version: v4.0-dev-850-gc4447462d-dirty I (205) cpu_start: Compile time: Jun 17 2019 16:32:52 I (211) cpu_start: ELF file SHA256: 8770c886bdf561a7... I (217) cpu_start: ESP-IDF: v4.0-dev-850-gc4447462d-dirty I (224) cpu_start: Starting app cpu, entry point is 0x40080e4c 0x40080e4c: call_start_cpu1 at esp-idf/esp-idf/components/esp32/cpu_start.c:265
I (0) cpu_start: App cpu up. I (235) heap_init: Initializing. RAM available for dynamic allocation: I (241) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM I (247) heap_init: At 3FFB2EC8 len 0002D138 (180 KiB): DRAM I (254) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM I (260) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM I (266) heap_init: At 40087FF4 len 0001800C (96 KiB): IRAM I (273) cpu_start: Pro cpu start user code I (291) cpu_start: Starting scheduler on PRO CPU. I (0) cpu_start: Starting scheduler on APP CPU.
Sample program to check Flash Encryption This is ESP32 chip with 2 CPU cores, WiFi/BT/BLE, silicon revision 1, 4MB external flash Flash encryption feature is enabled Flash encryption mode is DEVELOPMENT Flash in encrypted mode with flash_crypt_cnt = 1 Halting...

На этой стадии, если вам нужно обновить и перепрошить бинарники, см. далее "Re-flashing Updated Partitions".

Использование ключа, сгенерированного на хосте (Host Generated Key). Можно предварительно сгенерировать flash encryption key на компьютере хоста, и прошить его в eFuse. Это позволит вам предварительно зашифровать данные на хосте, и прошить эти зашифрованные данные, без необходимости обновлять flash незашифрованными данными (не требуется plaintext flash update). Эта фича может использоваться как в Development Mode, так и в Release Mode. Без предварительно сгенерированного ключа данные прошиваются в незашифрованном виде (in plaintext) и ESP32 шифрует их по месту.

Замечание: эта опция не рекомендуется для использования в готовых изделиях, за исключением ситуации, когда для каждого устройства используется свой отдельный, индивидуальный ключ шифрования.

Для использования host generated ключа выполните следующие шаги:

1. Убедитесь, что на вашем устройстве ESP32 биты flash encryption eFuse находятся в состоянии по умолчанию, как показано в таблице 1 выше. Как проверить состояние этих бит eFuse, см. врезку "Проверка ESP32 Flash Encryption Status".

2. Сгенерируйте случайный ключ шифрования командой (в этом примере сгенерируется файл ключа my_flash_encryption_key.bin):

idf.py secure-generate-flash-encryption-key my_flash_encryption_key.bin

3. Перед первой загрузкой из зашифрованного flash (first encrypted boot) прошейте этот ключ в биты eFuse вашего устройства следующей командой. Это действие производится для этого экземпляра устройства только один раз.

idf.py --port PORT efuse-burn-key flash_encryption my_flash_encryption_key.bin

Если ключ не прошит не прошит, и устройство запустилось после разрешения flash encryption, то ESP32 сгенерирует случайный ключ, к которому программа не сможет получить доступ, не сможет его прочитать или модифицировать.

4. В редактировании конфигурации (menuconfig) сделайте следующее:

1. Enable flash encryption on boot.
2. Select encryption mode (по умолчанию выбран Development mode).
3. Выберите подходящий уровень лога загрузчика (bootloader log verbosity).
4. Сохраните конфигурацию и выйдите из menuconfig.

Разрешение flash encryption увеличит размер bootloader, что может потребовать обновления смещения таблицы разделов (partition table offset). См. Bootloader Size.

5. Для сборки образов кода и прошивки выполните команду:

idf.py flash monitor

Замечание: эта команда не включает пользовательские файлы, которые следует записать в разделы flash-памяти. Пожалуйста, запишите их вручную перед выполнением этой команды, в противном случае эти файлы необходимо будет зашифровать отдельно перед записью.

Эта команда запишет в память flash не зашифрованные образы: second stage bootloader (загрузчик второй стадии загрузки), partition table (таблица разделов) и applications (приложения). Как только прошивка завершится, ESP32 будет сброшен. При последующей загрузке second stage bootloader зашифрует: second stage bootloader, разделы приложений и разделы, помеченные как encrypted, и затем снова выполнит сброс. Шифрование по месту может занять некоторое время, до одной минуты на больших разделах. После этого приложение расшифровывается runtime и выполняется.

Если используется Development Mode, то самый простой способ обновить и перепрошить бинарники - использовать "Re-flashing Updated Partitions".

Если используется Release Mode, то можно предварительно зашифровать бинарники на хосте, и затем прошить их как зашифрованные (ciphertext). Для этого необходим ключ шифрования, см. "Шифрование файлов вручную".

Re-flashing Updated Partitions. Если вы обновили ваш код приложения (как незашифрованные данные, in plaintext) и хотите его перепрошить, то перед прошивкой его надо зашифровать. Чтобы зашифровать и прошить приложение одной командой, запустите:

idf.py encrypted-app-flash monitor

Если все разделы должны быть обновлены в зашифрованном формате, то запустите команду:

idf.py encrypted-flash monitor

В Release mode загрузчик UART не может выполнить операции по шифрованию flash. Новые plaintext образы могут быть загружены ТОЛЬКО с использованием схемы over-the-air (OTA), при которой plaintext image шифруется перед записью во flash. Либо прошивка производится однократно, без возможности последующего обновления.

Для использования Release Mode выполните следующие шаги:

1. Убедитесь, что на вашем устройстве ESP32 биты flash encryption eFuse находятся в состоянии по умолчанию, как показано в таблице 1 выше. Как проверить состояние этих бит eFuse, см. врезку "Проверка ESP32 Flash Encryption Status".

2. В редактировании конфигурации (menuconfig) выполните следующее:

● Enable flash encryption on boot.
● Выберите Release mode. Обратите внимание, что как только будет выбран Release mode, будут прошиты eFuse-биты DISABLE_DL_ENCRYPT и DISABLE_DL_DECRYPT, чтобы запретить flash encryption hardware в ROM Download Mode.
● Select UART ROM download mode (Permanently disabled (рекомендуется)). Обратите внимание, что эта опция становится доступной только когда CONFIG_ESP32_REV_MIN установлена в 3 (ESP32 V3). Выбор по умолчанию для этой опции - UART ROM download mode enabled, однако рекомендуется постоянно её запретить, чтобы уменьшить количество опций, доступных для атакующего.
● Выберите подходящий уровень лога загрузчика (bootloader log verbosity).
● Сохраните конфигурацию и выйдите из menuconfig.

Разрешение flash encryption увеличит размер bootloader, что может потребовать обновления смещения таблицы разделов (partition table offset). См. Bootloader Size.

3. Для сборки образов кода и прошивки выполните команду:

idf.py flash monitor

Замечание: эта команда не будет включать прошивку любых других пользовательских файлов, которые должны быть записаны в разделы flash-памяти. Пожалуйста запишите их вручную перед запуском этой команды, иначе файлы должны быть зашифрованы отдельно перед записью.

Эта команда запишет в память flash не зашифрованные образы: second stage bootloader, partition table и applications. Как только прошивка завершится, ESP32 будет сброшен при последующей загрузке second stage bootloader зашифрует: second stage bootloader, разделы приложения и разделы, помеченные как encrypted, и затем выполнит сброс. Шифрование по месту может занять некоторое время, до минуты на больших разделах. После этого приложение расшифровывается runtime и выполняется executed.

Как только flash encryption было разрешено в Release mode, bootloader защитит от записи FLASH_CRYPT_CNT eFuse.

Для последующих обновлений plaintext данных используйте схему OTA [7].

Если у вас есть предварительно сгенерированный ключ шифрования (flash encryption key) и сохранена его копия, и UART download mode постоянно запрещен через CONFIG_SECURE_UART_ROM_DL_MODE (только для ESP32 V3), то можно обновить flash локально путем предварительного шифрования файлов и прошивки их в зашифрованном виде (ciphertext). См. "Шифрование файлов вручную".

[Лучшие практики]

Когда Flash Encryption используется в процессе производства готовой продукции:

● Не используйте одинаковый ключ шифрования на нескольких устройствах. Тогда атакующий, кто скопировал зашифрованные данные из одного устройства, не сможет их перенести во второе устройство.
● Когда используется ESP32 V3, если UART ROM Download Mode не нужен для производимого устройства, то этот режим должен быть запрещен, чтобы обеспечить дополнительный уровень защиты. Сделайте это вызовом esp_efuse_disable_rom_download_mode() при старте приложения (application startup). Альтернативно сконфигурируйте проект CONFIG_ESP32_REV_MIN level в значение 3 (нацеливаясь только на ESP32 V3) и выберите CONFIG_SECURE_UART_ROM_DL_MODE для "Permanently disable ROM Download Mode (recommended)". Возможность запретить ROM Download Mode недоступна для более ранних версий ESP32.
● Включите Secure Boot [2] в качестве дополнительного уровня защиты и предотвратите выборочное повреждение злоумышленником любой части флэш-памяти перед загрузкой.

[Включение внешнего шифрования Flash]

В описанном выше процессе электронные предохранители (eFuse), связанные с шифрованием Flash, которые в конечном итоге обеспечивают шифрование Flash, программируются через загрузчик второго уровня. Кроме того, все eFuse можно запрограммировать с помощью инструмента espefuse.

Возможные ошибки. Как только разрешено flash encryption, значение FLASH_CRYPT_CNT eFuse будет содержать нечетное значение. Это означает, что все разделы, помеченные флагом encryption, ожидаются как содержащие зашифрованный ciphertext. Ниже описаны 3 возможные случаи отказов, если ESP32 ошибочно загружена незашифрованными (plaintext) данными:

1. Если раздел загрузчика (bootloader partition) перепрошит незашифрованным образом second stage bootloader, то загрузчик первого уровня из ПЗУ (first stage, ROM bootloader) не сможет загрузить second stage, со следующей ошибкой:

rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371
ets Jun  8 2016 00:22:57
rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) flash read err, 1000 ets_main.c 371 ets Jun 8 2016 00:22:57
rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) flash read err, 1000 ets_main.c 371 ets Jun 8 2016 00:22:57
rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) flash read err, 1000 ets_main.c 371 ets Jun 8 2016 00:22:57
rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) flash read err, 1000 ets_main.c 371 ets Jun 8 2016 00:22:57 Note

Замечание: эта ошибка также появится, если содержимое flash стерто или повреждено.

2. Если second stage bootloader зашифрован, но таблица разделов перепрошита в незашифрованном виде (plaintext partition table image), то загрузчик не сможет прочитать таблицу разделов со следующей ошибкой:

rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:10464
ho 0 tail 12 room 4
load:0x40078000,len:19168
load:0x40080400,len:6664
entry 0x40080764
I (60) boot: ESP-IDF v4.0-dev-763-g2c55fae6c-dirty 2nd stage bootloader
I (60) boot: compile time 19:15:54
I (62) boot: Enabling RNG early entropy source...
I (67) boot: SPI Speed      : 40MHz
I (72) boot: SPI Mode       : DIO
I (76) boot: SPI Flash Size : 4MB
E (80) flash_parts: partition 0 invalid magic number 0x94f6
E (86) boot: Failed to verify partition table
E (91) boot: load partition table error!

3. Если загрузчик и таблица разделов зашифрованы, но приложение перепрошито в не зашифрованном виде (plaintext application image), то загрузчик не сможет загрузить приложение со следующей ошибкой:

rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:8452
load:0x40078000,len:13616
load:0x40080400,len:6664
entry 0x40080764
I (56) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader
I (56) boot: compile time 15:37:14
I (58) boot: Enabling RNG early entropy source...
I (64) boot: SPI Speed      : 40MHz
I (68) boot: SPI Mode       : DIO
I (72) boot: SPI Flash Size : 4MB
I (76) boot: Partition Table:
I (79) boot: ## Label            Usage          Type ST Offset   Length
I (87) boot:  0 nvs              WiFi data        01 02 0000a000 00006000
I (94) boot:  1 phy_init         RF data          01 01 00010000 00001000
I (102) boot:  2 factory          factory app      00 00 00020000 00100000
I (109) boot: End of partition table
E (113) esp_image: image at 0x20000 has invalid magic byte
W (120) esp_image: image at 0x20000 has invalid SPI mode 108
W (126) esp_image: image at 0x20000 has invalid SPI size 11
E (132) boot: Factory app partition is not bootable
E (138) boot: No bootable app partitions in the partition table

Убедитесь, то настройки flash encryption eFuse вашего устройства ESP32 находятся в состоянии по умолчанию, как показано в таблице 1.

Чтобы проверить, разрешено ли flash encryption на вашем устройстве ESP32, выполните одно из следующего:

● Прошейте пример приложения security/flash_encryption, и запустите его. Это приложение напечатает значение FLASH_CRYPT_CNT, если flash encryption разрешено или запрещено.
● Найдите имя последовательного порта, под которым отображается устройство ESP32 в вашей операционной системе хоста разработки, замените этим именем PORT в следующей команде, и запустите её:

idf.py -p PORT efuse-summary

.. или (здесь -p /dev/ttyUSB1 указывает порт подключения как пример для PORT):

espefuse -p /dev/ttyUSB1 summary

Пример вывода содержимого бит eFuse микроконтроллера ESP32-C3 показан ниже. В этом примере видно, что все биты установлены в состояние по умолчанию:

$ idf.py -p /dev/ttyACM0 efuse-summary
Executing action: efuse-summary
Running espefuse in directory /home/user/ESP32-IFSK-beacon/build
Executing "/home/user/.espressif/python_env/idf5.4_py3.13_env/bin/python -mespefuse summary -p /dev/ttyACM0 --chip esp32c3"...
espefuse.py v4.8.1
Connecting...
=== Run "summary" command === EFUSE_NAME (Block) Description = [Meaningful Value] [Readable/Writeable] (Hex Value) ---------------------------------------------------------------------------------------- Calibration fuses: K_RTC_LDO (BLOCK1) BLOCK1 K_RTC_LDO = -12 R/W (0b1000011) K_DIG_LDO (BLOCK1) BLOCK1 K_DIG_LDO = 8 R/W (0b0000010) V_RTC_DBIAS20 (BLOCK1) BLOCK1 voltage of rtc dbias20 = 16 R/W (0x04) V_DIG_DBIAS20 (BLOCK1) BLOCK1 voltage of digital dbias20 = 52 R/W (0x0d) DIG_DBIAS_HVT (BLOCK1) BLOCK1 digital dbias when hvt = -16 R/W (0b10100) THRES_HVT (BLOCK1) BLOCK1 pvt threshold when hvt = 2000 R/W (0b0111110100) TEMP_CALIB (BLOCK2) Temperature calibration data = -19.5 R/W (0b111000011) OCODE (BLOCK2) ADC OCode = 83 R/W (0x53) ADC1_INIT_CODE_ATTEN0 (BLOCK2) ADC1 init code at atten0 = 1516 R/W (0b0101111011) ADC1_INIT_CODE_ATTEN1 (BLOCK2) ADC1 init code at atten1 = -40 R/W (0b1000001010) ADC1_INIT_CODE_ATTEN2 (BLOCK2) ADC1 init code at atten2 = -116 R/W (0b1000011101) ADC1_INIT_CODE_ATTEN3 (BLOCK2) ADC1 init code at atten3 = -608 R/W (0b1010011000) ADC1_CAL_VOL_ATTEN0 (BLOCK2) ADC1 calibration voltage at atten0 = -548 R/W (0b1010001001) ADC1_CAL_VOL_ATTEN1 (BLOCK2) ADC1 calibration voltage at atten1 = -336 R/W (0b1001010100) ADC1_CAL_VOL_ATTEN2 (BLOCK2) ADC1 calibration voltage at atten2 = -492 R/W (0b1001111011) ADC1_CAL_VOL_ATTEN3 (BLOCK2) ADC1 calibration voltage at atten3 = -652 R/W (0b1010100011)
Config fuses: WR_DIS (BLOCK0) Disable programming of individual eFuses = 0 R/W (0x00000000) RD_DIS (BLOCK0) Disable reading from BlOCK4-10 = 0 R/W (0b0000000) DIS_ICACHE (BLOCK0) Set this bit to disable Icache = False R/W (0b0) DIS_TWAI (BLOCK0) Set this bit to disable CAN function = False R/W (0b0) DIS_DIRECT_BOOT (BLOCK0) Disable direct boot mode = False R/W (0b0) UART_PRINT_CONTROL (BLOCK0) Set the default UARTboot message output mode = Enable R/W (0b00) ERR_RST_ENABLE (BLOCK0) Use BLOCK0 to check error record registers = with check R/W (0b1) BLOCK_USR_DATA (BLOCK3) User data = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W BLOCK_SYS_DATA2 (BLOCK10) System data part 2 (reserved) = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W
Flash fuses: FLASH_TPUW (BLOCK0) Configures flash waiting time after power-up; in = 0 R/W (0x0) unit of ms. If the value is less than 15; the waiting time is the configurable value; Otherwise; the waiting time is twice the configurable value FORCE_SEND_RESUME (BLOCK0) Set this bit to force ROM code to send a resume = False R/W (0b0) command during SPI boot FLASH_CAP (BLOCK1) Flash capacity = 4M R/W (0b001) FLASH_TEMP (BLOCK1) Flash temperature = 105C R/W (0b01) FLASH_VENDOR (BLOCK1) Flash vendor = XMC R/W (0b001)
Identity fuses: DISABLE_WAFER_VERSION_MAJOR (BLOCK0) Disables check of wafer version major = False R/W (0b0) DISABLE_BLK_VERSION_MAJOR (BLOCK0) Disables check of blk version major = False R/W (0b0) WAFER_VERSION_MINOR_LO (BLOCK1) WAFER_VERSION_MINOR least significant bits = 4 R/W (0b100) PKG_VERSION (BLOCK1) Package version = 0 R/W (0b000) BLK_VERSION_MINOR (BLOCK1) BLK_VERSION_MINOR = 3 R/W (0b011) WAFER_VERSION_MINOR_HI (BLOCK1) WAFER_VERSION_MINOR most significant bit = False R/W (0b0) WAFER_VERSION_MAJOR (BLOCK1) WAFER_VERSION_MAJOR = 0 R/W (0b00) OPTIONAL_UNIQUE_ID (BLOCK2) Optional unique 128-bit ID = 6a d0 9e 37 52 79 39 fd 26 c8 0c ad d0 0e f0 60 R/W BLK_VERSION_MAJOR (BLOCK2) BLK_VERSION_MAJOR of BLOCK2 = With calibration R/W (0b01) WAFER_VERSION_MINOR (BLOCK0) calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI = 4 R/W (0x4) << 3 + WAFER_VERSION_MINOR_LO (read only)
Jtag fuses: SOFT_DIS_JTAG (BLOCK0) Set these bits to disable JTAG in the soft way = 0 R/W (0b000) (odd number 1 means disable ). JTAG can be enabled in HMAC module DIS_PAD_JTAG (BLOCK0) Set this bit to disable JTAG in the hard way. JTAG = False R/W (0b0) is disabled permanently
Mac fuses: MAC (BLOCK1) MAC address = 50:78:7d:45:be:2c (OK) R/W CUSTOM_MAC (BLOCK3) Custom MAC address = 00:00:00:00:00:00 (OK) R/W
Security fuses: DIS_DOWNLOAD_ICACHE (BLOCK0) Set this bit to disable Icache in download mode = False R/W (0b0) (boot_mode[3:0] is 0; 1; 2; 3; 6; 7) DIS_FORCE_DOWNLOAD (BLOCK0) Set this bit to disable the function that forces = False R/W (0b0) chip into download mode DIS_DOWNLOAD_MANUAL_ENCRYPT (BLOCK0) Set this bit to disable flash encryption when in = False R/W (0b0) download boot modes SPI_BOOT_CRYPT_CNT (BLOCK0) Enables flash encryption when 1 or 3 bits are set = Disable R/W (0b000) and disables otherwise SECURE_BOOT_KEY_REVOKE0 (BLOCK0) Revoke 1st secure boot key = False R/W (0b0) SECURE_BOOT_KEY_REVOKE1 (BLOCK0) Revoke 2nd secure boot key = False R/W (0b0) SECURE_BOOT_KEY_REVOKE2 (BLOCK0) Revoke 3rd secure boot key = False R/W (0b0) KEY_PURPOSE_0 (BLOCK0) Purpose of Key0 = USER R/W (0x0) KEY_PURPOSE_1 (BLOCK0) Purpose of Key1 = USER R/W (0x0) KEY_PURPOSE_2 (BLOCK0) Purpose of Key2 = USER R/W (0x0) KEY_PURPOSE_3 (BLOCK0) Purpose of Key3 = USER R/W (0x0) KEY_PURPOSE_4 (BLOCK0) Purpose of Key4 = USER R/W (0x0) KEY_PURPOSE_5 (BLOCK0) Purpose of Key5 = USER R/W (0x0) SECURE_BOOT_EN (BLOCK0) Set this bit to enable secure boot = False R/W (0b0) SECURE_BOOT_AGGRESSIVE_REVOKE (BLOCK0) Set this bit to enable revoking aggressive secure = False R/W (0b0) boot DIS_DOWNLOAD_MODE (BLOCK0) Set this bit to disable download mode (boot_mode[3 = False R/W (0b0) :0] = 0; 1; 2; 3; 6; 7) ENABLE_SECURITY_DOWNLOAD (BLOCK0) Set this bit to enable secure UART download mode = False R/W (0b0) SECURE_VERSION (BLOCK0) Secure version (used by ESP-IDF anti-rollback = 0 R/W (0x0000) feature) BLOCK_KEY0 (BLOCK4) Purpose: USER Key0 or user data = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W BLOCK_KEY1 (BLOCK5) Purpose: USER Key1 or user data = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W BLOCK_KEY2 (BLOCK6) Purpose: USER Key2 or user data = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W BLOCK_KEY3 (BLOCK7) Purpose: USER Key3 or user data = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W BLOCK_KEY4 (BLOCK8) Purpose: USER Key4 or user data = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W BLOCK_KEY5 (BLOCK9) Purpose: USER Key5 or user data = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W
Spi Pad fuses: SPI_PAD_CONFIG_CLK (BLOCK1) SPI PAD CLK = 0 R/W (0b000000) SPI_PAD_CONFIG_Q (BLOCK1) SPI PAD Q(D1) = 0 R/W (0b000000) SPI_PAD_CONFIG_D (BLOCK1) SPI PAD D(D0) = 0 R/W (0b000000) SPI_PAD_CONFIG_CS (BLOCK1) SPI PAD CS = 0 R/W (0b000000) SPI_PAD_CONFIG_HD (BLOCK1) SPI PAD HD(D3) = 0 R/W (0b000000) SPI_PAD_CONFIG_WP (BLOCK1) SPI PAD WP(D2) = 0 R/W (0b000000) SPI_PAD_CONFIG_DQS (BLOCK1) SPI PAD DQS = 0 R/W (0b000000) SPI_PAD_CONFIG_D4 (BLOCK1) SPI PAD D4 = 0 R/W (0b000000) SPI_PAD_CONFIG_D5 (BLOCK1) SPI PAD D5 = 0 R/W (0b000000) SPI_PAD_CONFIG_D6 (BLOCK1) SPI PAD D6 = 0 R/W (0b000000) SPI_PAD_CONFIG_D7 (BLOCK1) SPI PAD D7 = 0 R/W (0b000000)
Usb fuses: DIS_USB_JTAG (BLOCK0) Set this bit to disable function of usb switch to = False R/W (0b0) jtag in module of usb device DIS_USB_SERIAL_JTAG (BLOCK0) USB-Serial-JTAG = Enable R/W (0b0) USB_EXCHG_PINS (BLOCK0) Set this bit to exchange USB D+ and D- pins = False R/W (0b0) DIS_USB_SERIAL_JTAG_ROM_PRINT (BLOCK0) USB printing = Enable R/W (0b0) DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE (BLOCK0) Disable UART download mode through USB-Serial-JTAG = False R/W (0b0)
Vdd fuses: VDD_SPI_AS_GPIO (BLOCK0) Set this bit to vdd spi pin function as gpio = False R/W (0b0)
Wdt fuses: WDT_DELAY_SEL (BLOCK0) RTC watchdog timeout threshold; in unit of slow = 40000 R/W (0b00) clock cycle

[Чтение и запись данных в Encrypted Flash]

Код приложения ESP32 может проверить, было ли в настоящий момент разрешено flash encryption, путем вызова esp_flash_encryption_enabled(). Также устройство может идентифицировать режим шифрования (flash encryption mode) вызовом esp_get_flash_encryption_mode().

Как только шифрование flash encryption было разрешено, будьте осторожны при доступе к содержимому flash из кода.

Область действия Flash Encryption. Если параметру FLASH_CRYPT_CNT eFuse задано значение с нечётным числом бит, всё содержимое flash-памяти, доступ к которому осуществляется через кэш flash-памяти MMU, прозрачно расшифровывается. Это включает в себя:

● Исполняемый код приложения, находящийся в flash (IROM).
● Все данные только для чтения (read-only), сохраненные в flash (DROM).
● Любые данные, к которым происходит обращение через spi_flash_mmap().
● Образ second stage bootloader, когда его читает first stage (ROM) bootloader.

Важное замечание: MMU flash cache безусловно расшифровывает все существующие данные. Данные, которые сохранены в памяти flash не зашифрованными, также будут "прозрачно расшифрованы" через flash cache, и будут видны в программе как случайный мусор.

Чтение из Encrypted Flash. Чтобы прочитать данные без использования flash cache MMU mapping, вы можете использовать функцию чтения раздела esp_partition_read(). Эта функция будет расшифровывать данные только когда читает их из encrypted partition. Данные, которые она читает из unencrypted разделов, не будут расшифровываться. Таким методом программа может однообразно обращаться как к шифрованным, так и не зашифрованным областям flash.

Вы также можете использовать следующие SPI flash API функции:

esp_flash_read() для чтения сырых (зашифрованных) данных, которые не будут расшифровываться.
esp_flash_read_encrypted() для чтения и расшифровки данных.

Данные, сохраненные API-функциями энергонезависимого хранилища (Non-Volatile Storage, NVS API [9]) всегда сохраняются и считываются расшифрованными с точки зрения flash encryption. При необходимости библиотека может предоставить фичу шифрования, подробности см. в документации NVS Encryption [6].

Запись в Encrypted Flash. Для этого рекомендуется использовать функцию записи раздела esp_partition_write(). Эта функция будет шифровать данные только тогда, когда записывает их в encrypted partition. Данные, записываемые в незашифрованные разделы, не будут шифроваться. Таким методом программа может однообразно обращаться как к шифрованным, так и не зашифрованным областям flash.

Вы также можете предварительно зашифровать и записать данные с помощью функции esp_flash_write_encrypted().

Также существуют следующие ROM-функции, не поддерживаемые в приложениях ESP-IDF:

esp_rom_spiflash_write_encrypted предварительно шифрует и записывает данные в flash.
SPIWrite записывает не зашифрованные данные в flash.

Поскольку данные шифруются блоками, минимальный записываемый размер для шифрованных данных составляет 16 байт, и их начальный адрес также должен быть выровнен на 16 байт.

[Обновление Encrypted Flash]

Обновления OTA. OTA для зашифрованных разделов будет автоматически записывать зашифрованные данные, если используется функция esp_partition_write().

Перед сборкой образа приложения для OTA updating уже зашифрованного устройства, разрешите опцию Enable flash encryption on boot в меню конфигурации проекта.

Для общего описания технологии обновления ESP-IDF OTA см. [7].

Обновление Encrypted Flash через Serial. Прошивка зашифрованного устройства через serial bootloader требует, чтобы интерфейс загрузки serial bootloader не был необратимо (permanently) запрещен перемычками eFuse.

В Development Mode рекомендуемый метод перепрошивки это "Re-flashing Updated Partitions".

В Release Mode, если есть копия ключа, которая сохранена в eFuse, то хост может предварительно зашифровать файлы у себя и затем прошить их. См. "Шифрование файлов вручную".

[Запрет Flash Encryption]

Если flash encryption было разрешено случайно, то прошивка plaintext данных приведет к программному "окирпичиванию" (soft-brick) чипа ESP32. Устройство будет постоянно перезагружаться, печатая ошибку error flash read err, 1000 или invalid header: 0xXXXXXX.

Для flash encryption в Development mode шифрование можно запретить прошивкой FLASH_CRYPT_CNT eFuse. Это можно сделать только 3 раза на чип, если выполнить следующие шаги:

1. В редактировании конфигурации запретите опцию Enable flash encryption on boot, затем сохраните конфигурацию и выйдите из menuconfig.

2. Снова откройте меню конфигурации проекта, и дважды проверьте, что вы запретили эту опцию! Если эта опция осталась разрешенной, то bootloader немедленно заново разрешит шифрование при своей загрузке.

3. С запрещенным flash encryption выполните сборку и прошивку нового bootloader и приложения путем запуска команды idf.py flash.

4. Используйте idf.py для запрета FLASH_CRYPT_CNT запуском команды:

idf.py efuse-burn FLASH_CRYPT_CNT

Выполните сброс ESP32. Flash encryption будет запрещено, и bootloader будет выполнять загрузку как обычно.

[Основные замечания по Flash Encryption]

● Содержимое памяти Flash шифруется алгоритмом AES-256. Ключ шифрования (flash encryption key) сохраняется во внутренних перемычках чипа (flash_encryption eFuse) и по умолчанию к ним нет программного доступа.

● В AES-256 ключ "подстраивается" под адрес смещения каждого 32-байтного блока flash. Это означает, что каждый 32-байтовый блок (два последовательных 16-байтовых блока AES) шифруется уникальным ключом, полученным из ключа флеш-шифрования.

● Доступ к flash прозрачен благодаря фиче flash cache mapping feature ESP32 - любые регионы flash, отображаемые на адресное пространство, будут прозрачно дешифрованы при чтении.

Некоторые разделы данных могут не шифроваться для удобства доступа, или требовать использования алгоритмов обновления, совместимых с Flash-памятью, которые неэффективны, если данные зашифрованы. Разделы NVS для энергонезависимого хранения данных не могут быть зашифрованы, поскольку библиотека NVS [9] несовместима напрямую с flash encryption. Подробности см. в документации [6].

● Если flash encryption пока не используется, но может использоваться в будущем, то программист должен помнить об этом и принимать определенные меры предосторожности при написании кода, использующего flash encryption (см. "Чтение и запись данных в Encrypted Flash").

● Если включена безопасная загрузка (Secure Boot [2]), то для перепрошивки загрузчика зашифрованного устройства требуется перепрошиваемая (Re-flashable) цифровая подпись безопасной загрузки (см. "Flash Encryption и Secure Boot").

Разрешение шифрования flash encryption увеличит размер bootloader, что может потребовать обновления смещения таблицы разделов. См. Bootloader Size.

Важное замечание: не отключайте питание ESP32, пока идет процесс шифрования первой загрузки (first boot encryption pass). Если в этот момент прервать питание, то содержимое повредится, и потребует повторной перепрошивки незашифрованными данными. В этом случае повторная перепрошивка не будет засчитываться в лимит перепрошивок.

Ограничения Flash Encryption. Технология шифрования flash защищает firmware от неавторизованного чтения и модификации. Важно понимать ограничения фичи flash encryption:

● Шифрование flash настолько же надёжно, насколько надёжен ключ. Поэтому рекомендуется генерировать ключи на устройстве во время первой загрузки (режим по умолчанию). При генерации ключей вне устройства убедитесь, что соблюдена правильная процедура, и не используйте один и тот же ключ на всех рабочих устройствах.

● Не все сохраняемые данные шифруются. Если вы сохраняете данные на flash то проверьте, что используемый вами метод сохранения (библиотека, API, и т. п.) поддерживает flash encryption.

● Шифрование flash-памяти не мешает злоумышленнику понять её высокоуровневую структуру. Это связано с тем, что для каждой пары смежных 16-байтовых блоков AES используется один и тот же ключ AES. Если эти смежные 16-байтовые блоки содержат одинаковое содержимое (например, пустые области или области заполнения), эти блоки будут зашифрованы для создания совпадающих пар зашифрованных блоков. Это может позволить злоумышленнику проводить высокоуровневые сравнения между зашифрованными устройствами (то есть определять, используют ли два устройства одинаковую версию прошивки).

● По той же причине злоумышленник всегда может определить, когда пара смежных 16-байтовых блоков (выровненных по 32 байта) содержит две идентичные 16-байтовые последовательности. Имейте это в виду, если вы храните конфиденциальные данные во flash-памяти: спроектируйте своё flash-хранилище таким образом, чтобы это не происходило (достаточно использовать байт-счётчик или другое неидентичное значение на каждые 16 байт). Шифрование NVS [6] решает эту проблему и подходит для множества применений.

● Flash encryption само по себе не предотвращает для атакующего модификацию firmware на устройстве. Чтобы запретить запуск не авторизованного firmware на устройстве, используйте flash encryption в комбинации с Secure Boot [2].

[Flash Encryption и Secure Boot]

Рекомендуется использовать flash encryption в комбинации с Secure Boot. Однако если фича Secure Boot разрешена, то для перепрошивки устройства накладываются дополнительные ограничения:

● OTA Updates не ограничены при условии, что новое приложение правильно подписано с помощью ключа подписи Secure Boot.

● Обновление прошивки через обычный последовательный порт (Python Plaintext serial flash, см. "Обновление Encrypted Flash через Serial") возможно только при выборе режима безопасной загрузки Re-flashable Secure Boot и предварительной генерации ключа безопасной загрузки (Secure Boot) и его записи в ESP32 (см. [2]). В такой конфигурации команда idf.py bootloader создаст предварительно подготовленный загрузчик и файл цифровой подписи secure boot для прошивки по смещению 0x0. При выполнении шагов по перепрошивке через обычный последовательный порт необходимо перепрошить этот файл перед записью других plaintext данных.

● Перепрошивка через предварительно сгенерированный файл ключа (Flash Encryption Key, см. "Использование ключа, сгенерированного на хосте") все еще возможна, если не перепрошивался предоставленный bootloader. Перепрошивка bootloader-а требует той же разрешенной опции Re-flashable в конфигурации Secure Boot.

[Продвинутые фичи Flash Encryption]

Encrypted Partition Flag. Некоторые разделы encrypted по умолчанию. Другие разделы могут быть помечены а таблице разделов как требующие шифрования путем добавления флага encrypted в поле Flags раздела. В результате данные этих помеченных разделов будут обрабатываться как зашифрованные точно так же, как и раздел app.

# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     0x9000,  0x6000
phy_init, data, phy,     0xf000,  0x1000
factory,  app,  factory, 0x10000, 1M
secret_data, 0x40, 0x01, 0x20000, 256K, encrypted

Подробное описание таблицы разделов см. [8].

Дополнительная информация по поводу шифрования разделов:

● По умолчанию таблицы разделов не включают никакие зашифрованные разделы data.
● Когда разрешена фича flash encryption, раздел app всегда считается как encrypted, и для него не требуется соответствующая маркировка флагом.
● Если flash encryption не разрешено, то флаг "encrypted" не дает никакого эффекта.
● Вы также можете рассмотреть возможность защиты phy_init от физического доступа, чтения или модификации, путём пометки опционального phy раздела флагом encrypted.
● Раздел nvs не может быть encrypted, потому что библиотека NVS [9] не имеет прямой совместимости с flash encryption.

Разрешение UART Bootloader Encryption/Decryption. При первой загрузке процесс flash encryption прошивает следующие перемычки eFuse:

● DISABLE_DL_ENCRYPT, которая запрещает операцию flash encryption, когда работает режим UART bootloader boot.
● DISABLE_DL_DECRYPT, которая запрещает прозрачную расшифровку flash, когда работает режим UART bootloader, даже если eFuse FLASH_CRYPT_CNT установлен, чтобы разрешить её нормальное функционирование.
● DISABLE_DL_CACHE, которая запрещает всю MMU flash cache, когда работает режим UART bootloader.

Однако перед первой загрузкой вы можете оставить любую из этих функций включенной, записав только выбранные eFuse и защитив от записи остальные eFuse, установив для них неустановленное значение 0. Например:

idf.py --port PORT efuse-burn DISABLE_DL_DECRYPT
idf.py --port PORT efuse-write-protect DISABLE_DL_ENCRYPT

Важное замечание: если оставить DISABLE_DL_DECRYPT не установленным (0), то это сделает flash encryption бесполезным. Злоумышленник, имеющий физический доступ к чипу, может использовать режим загрузчика UART с пользовательским кодом-заглушкой для считывания содержимого flash-памяти.

Установка FLASH_CRYPT_CONFIG. Перемычка eFuse FLASH_CRYPT_CONFIG определяет количество бит в ключе шифрования, которые "подстраиваются" под смещение блока. Подробности см. в описании алгоритма шифрования врезки "Технические подробности по алгоритму Flash Encryption".

При первой загрузке second stage bootloader это значение устанавливается на максимум 0xF.

Можно прошить этот фьюз вручную, и защитить от записи перед первой загрузкой, чтобы выбрать другие значения "подстройки" AES. Однако это не рекомендуется.

Настоятельно рекомендуется никогда не защищать от записи FLASH_CRYPT_CONFIG, когда он не установлен. Иначе его значение навсегда останется нулевым, и не будут подстраиваться никакие биты в ключе flash encryption. В результате алгоритм flash encryption будет эквивалентен режиму AES ECB.

Представьте, что у вас есть большая картина (ваши данные), разбитая на множество маленьких пазлов (блоки данных). Режим ECB — это как если бы вы взяли одинаковый трафарет и применили его к каждому пазлу по отдельности, не глядя на соседние.

Результат: если на двух пазлах был одинаковый рисунок, то после применения трафарета они будут выглядеть абсолютно одинаково. В этом появляется проблема: на итоговой зашифрованной картине будут видны узоры и повторения, потому что исходные закономерности не скрываются.

Техническое описание. AES ECB (Electronic Codebook — электронная кодовая книга) — это самый простой режим использования симметричного блочного шифра, такого как AES.

1. Разделение на блоки: исходные данные (открытый текст) разбиваются на блоки фиксированного размера. Для AES это блоки по 128 бит (16 байт).

2. Независимое шифрование: каждый блок шифруется отдельно и независимо от других с использованием одного и того же ключа.

3. Отсутствие связи: процесс шифрования блока №2 никак не зависит от блока №1, блока №3 и т.д. Нет никакой "обратной связи" или случайного элемента (вектора инициализации).

Ключевые характеристики ECB:

Характеристика Описание
Простота Реализация очень проста и не требует дополнительных компонентов, кроме самого алгоритма шифрования.
Параллелизм Поскольку блоки независимы, их можно шифровать и расшифровывать параллельно, что может увеличить скорость на многопроцессорных системах.
Детерминированность Один и тот же открытый текст, зашифрованный одним и тем же ключом, всегда даст один и тот же шифротекст.

Главные недостатки и проблемы ECB. ECB не считается безопасным для защиты конфиденциальных данных в большинстве случаев. Вот почему:

1. Не скрывает шаблоны данных (уязвимость к анализу). Это самая большая проблема. Если в исходных данных есть повторяющиеся блоки, то в зашифрованных данных появятся повторяющиеся блоки шифротекста. Злоумышленник, даже не зная ключа, может увидеть эти закономерности.

Классический пример: зашифрованное изображение в режиме ECB сохраняет видимые контуры исходного изображения.

2. Уязвимость к атакам "подстановка блоков". Так как блоки независимы, злоумышленник может:

   - Переставлять блоки шифротекста местами, и при расшифровке получится осмысленный, но измененный текст.
   - Копировать и вставлять блоки из одного сообщения в другое.

Например, если в системе команда "Перевести 100$ на счет А" и "Перевести 100$ на счет Б" имеют разные блоки только в части счета, атаку можно перехватить и подменить блок, чтобы деньги ушли на другой счет.

Когда (очень ограниченно) можно использовать ECB?

ECB можно использовать только в очень специфических сценариях, когда шифруется один-единственный блок данных (например, шифрование одного PIN-кода или ключа). Для шифрования любых структурных данных (файлы, сообщения, сообщения с заголовками) он категорически не подходит.

Безопасные альтернативы. Для безопасного шифрования данных используются другие режимы работы, которые устраняют недостатки ECB:

CBC (Cipher Block Chaining): каждый блок шифротекста зависит от предыдущего. Требует случайного вектора инициализации (IV). Классический и надежный режим, но он не поддерживает параллельное шифрование.

CTR (Counter): превращает блочный шифр в поточный. Использует счетчик и IV. Поддерживает параллельное шифрование и расшифровку.

GCM (Galois/Counter Mode): расширение режима CTR, которое также обеспечивает аутентификацию данных (проверяет, что данные не были изменены). Это современный и рекомендуемый режим для многих протоколов (например, TLS).

Отладка JTAG. По умолчанию, когда Flash Encryption разрешено (либо в Development, либо в Release mode), отладка JTAG запрещена через eFuse. Bootloader делает это при первой загрузке, в то же самое время, когда разрешает flash encryption.

См. "JTAG with Flash Encryption or Secure Boot" для дополнительной информации по отладке JTAG Debugging вместе с Flash Encryption.

Шифрование файлов вручную. Шифрование или расшифровка файлов вручную требует, чтобы был предварительно прошит ключ шифрования в перемычки eFuse (см. "Использование ключа, сгенерированного на хосте"), и что есть на хосте его сохраненная копия. Если flash encryption сконфигурировано в Development Mode, то не обязательно хранить копию ключа или выполнять описанные ниже шаги, поскольку можно использовать упрощенные шаги "Re-flashing Updated Partitions".

Файл ключа должен быть обычным двоичным файлом (здесь для примера используется имя файла ключа key.bin).

Например, следующая команда зашифрует файл my-app.bin для его прошивки со смещением 0x10000:

idf.py secure-encrypt-flash-data --keyfile /path/to/key.bin --address 0x10000 \
 --output my-app-ciphertext.bin my-app.bin

Файл my-app-ciphertext.bin может быть затем прошит со смещением 0x10000 с помощью утилиты esptool.py. Чтобы увидеть все опции командной строки, рекомендуемые для esptool.py, см. вывод успешной сборки команды idf.py build.

Замечание: если прошитый зашифрованный (ciphertext) не распознан устройством ESP32 при его загрузке, то проверьте, что точно совпадают ключи и аргументы командной строки включая правильность смещения. Если ваш ESP32 использует не умолчательное значение FLASH_CRYPT_CONFIG в eFuse, то вам нужно передать аргумент --flash-crypt-conf в команду idf.py, чтобы установить соответствующее значение. Этого не произойдет, если устройство настроило шифрование flash-памяти самостоятельно, но может произойти, если запись eFuses вручную для включения шифрования flash-памяти.

Команда idf.py decrypt-flash-data может использоваться с теми же опциями (и другими файлами input/output), чтобы расшифровать ciphertext содержимого flash или ранее зашифрованный файл.

Здесь приведена некоторая информация о том, как работает шифрование flash.

● AES-256 работает на 16-байтных блоках данных. Подсистема flash encryption engine шифрует и расшифровывает данные 32-байтными блоками - два последовательных блока AES.

● Основной ключ шифрования (flash encryption key) сохранен в поле flash_encryption перемычек eFuse, и по умолчанию он защищен от последующих записей или чтений со стороны программы.

● Размер ключа AES-256 составляет 256 бит (32 байта), прочитанных из flash_encryption eFuse. Аппаратная подсистема AES использует этот ключ в обратном порядке байт в сравнении с порядком байт, как они сохранены в flash_encryption.

   - Если CODING_SCHEME eFuse установлен в 0 (по умолчанию, "None" Coding Scheme), то eFuse key block имеет размер 256 бит и сохраняется как есть (с обратным порядком байт).
   - Если CODING_SCHEME eFuse установлен в 1 (3/4 Encoding), то eFuse key block имеет размер 192 бита (с обратным порядком байт), так что общая энтропия ключа уменьшается. Аппаратура шифрования flash все еще работает на 256-разрядном ключе, после чего чтения (и обратного реверса байт) ключ расширяется как key = key[0:255] + key[64:127].

● Алгоритм AES используется в обратном порядке во flash-шифровании, поэтому операция шифрования на зашифрованном flash дает расшифровку AES, и наоборот, расшифровка расшифрованного дает шифрование AES. Это сделано для повышения производительности и не влияет на эффективность алгоритма.

● Каждый 32-байтный блок (2 соседних 16-байтных блока AES) шифруется уникальным ключом. Этот ключ генерируется из основного flash encryption key, сохраненного в flash_encryption, путем операции XOR со смещением этого блока в flash (так называемая подстройка ключа, "key tweak").

● Определенная подстройка зависит от содержимого FLASH_CRYPT_CONFIG eFuse. Это 4-битое значение, каждый бит которого разрешает операцию XOR над определенным диапазоном бит ключа:

   - Бит 1, биты 0-66 ключа подвергаются операции XOR.
   - Бит 2, биты 67-131 ключа подвергаются операции XOR.
   - Бит 3, биты 132-194 ключа подвергаются операции XOR.
   - Бит 4, биты 195-256 ключа подвергаются операции XOR.

Рекомендуется всегда оставлять значение FLASH_CRYPT_CONFIG максимальным значением по умолчанию 0xF, чтобы все биты ключа подвергались XOR со смещением блока. Подробнее см. "Установка FLASH_CRYPT_CONFIG".

● Старшие 19 бит смещения блока (биты 5 .. 23) подвергаются операции XOR с основным ключом шифрования (flash encryption key). Этот диапазон выбран по двум причинам: максимальный размер flash равен 16 мегабайт (24 бита), и каждый блок из 32 байт адресуется 5 младшими битами, которые всегда равны 0.

● Существует определённое соответствие между каждым из 19 бит смещения блока и 256 битами ключа флеш-шифрования, позволяющее определить, какой бит с каким битом выполняется операция XOR. Полное соответствие см. в переменной _FLASH_ENCRYPTION_TWEAK_PATTERN исходного кода espsecure.py.

● Чтобы посмотреть полный алгоритм шифрования flash, реализованный на Python, см. функцию _flash_encryption_operation() в исходном коде espsecure.py.

[Ссылки]

1. ESP32 Flash Encryption site:docs.espressif.com.
2. ESP32 Secure Boot V2.
3. Second Stage Bootloader.
4. NVS Key Partition.
5. ESP32 eFuse Manager.
6. NVS Encryption site:docs.espressif.com.
7. ESP32: обновление по радиоканалу (OTA).
8. ESP32: таблицы разделов.
9ESP32: библиотека энергонезависимого хранилища данных.

 

Добавить комментарий


Защитный код
Обновить

Top of Page