| 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). [Зашифрованные разделы (Encrypted Partitions)] С разрешенной фичей flash encryption по умолчанию шифруются следующие типы данных: ● Second Stage Bootloader (загрузчик второй стадии [3]). Другие типы данных могут быть зашифрованы по условию: ● Любой раздел, помеченный флагом encrypted в таблице разделов. Подробности см. в секции Encrypted Partition Flag. [Биты 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, перечисленных в таблице выше. Доступ на чтение и запись бит 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 при загрузке). Разрешение 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
Пример вывода последующих загрузок 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
На этой стадии, если вам нужно обновить и перепрошить бинарники, см. далее "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. Разрешение 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. Разрешение 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 используется в процессе производства готовой продукции: ● Не используйте одинаковый ключ шифрования на нескольких устройствах. Тогда атакующий, кто скопировал зашифрованные данные из одного устройства, не сможет их перенести во второе устройство. [Включение внешнего шифрования 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
Замечание: эта ошибка также появится, если содержимое 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 разрешено или запрещено. 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...
[Чтение и запись данных в 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). Важное замечание: 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() для чтения сырых (зашифрованных) данных, которые не будут расшифровываться. Данные, сохраненные 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. Поскольку данные шифруются блоками, минимальный записываемый размер для шифрованных данных составляет 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 Подробное описание таблицы разделов см. [8]. Дополнительная информация по поводу шифрования разделов: ● По умолчанию таблицы разделов не включают никакие зашифрованные разделы data. Разрешение UART Bootloader Encryption/Decryption. При первой загрузке процесс flash encryption прошивает следующие перемычки eFuse: ● DISABLE_DL_ENCRYPT, которая запрещает операцию flash encryption, когда работает режим UART bootloader boot. Однако перед первой загрузкой вы можете оставить любую из этих функций включенной, записав только выбранные eFuse и защитив от записи остальные eFuse, установив для них неустановленное значение 0. Например: idf.py --port PORT efuse-burn DISABLE_DL_DECRYPT Важное замечание: если оставить 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 \ Файл 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 бит и сохраняется как есть (с обратным порядком байт). ● Алгоритм 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. Рекомендуется всегда оставлять значение 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. |