Безопасная загрузка в i.MX6


Разрабатывая любой проект для встроенных систем разработчик должен решать два дополнительных вопроса:

  • Как защитить прошивку от подмены в изделии;
  • Как защитить ПО от копирования.

В данной статье описано как защитить процессор i.MX6 от подмены загрузчика в изделии и усложнить процесс копирования прошивки.



Введение


Для защиты интеллектуальной собственности в случае если проект bare metal [1] в основном помогает комплекс аппаратных средств, как присутствующих в микроконтроллере, так и внешних. Примеры приведены на уважаемом сайте [2]. Дополнительные программные методы, такие как [3] применяются, однако основаны они на неизвестности механизма защиты. Связано это с невысокой вычислительной мощностью устройств и неудобством реализации динамической генерации прошивок.

Когда в дело приходят SoC с операционными системами наподобие Linux, защиту необходимо организовывать одновременно на нескольких уровнях:

  • Уровень конечного приложения
  • Уровень операционной системы
  • Уровень загрузчика

В случае получения доступа на любом из уровней, достаточно легко влиять на ПО дальнейших уровней и внести изменения в межуровневые взаимодействия.
Простейшие примеры: Ваше ПО установленное на Linux общается по протоколу UART (например через /dev/ttyACM0) с микроконтроллером. Злоумышленник получив доступ в систему использует тот же ресурс UART и повторяет случайные отправленные посылки. Или например если драйвер встроен в ядро, злоумышленник перекомпилирует ядро переписав код драйвера UART другим способом. Конечное приложение не узнает о происходящем.

Что такое secure boot и зачем он нужен?


Первым уровнем защиты вашего ПО на целевой системе служит secure boot. Здесь и далее secure boot объясняется для процессоров семейства i.MX6.

Secure boot в i.MX6 реализуется через High assurance boot (HAB) и может выполнять две функции:

  • безопасная загрузка,
  • зашифрованная загрузка.

В статье я опишу процесс обеспечения безопасной загрузки процессора. В i.MX6 реализован и алгоритм обеспечивающий шифрование загрузчика. Однако его использование требует генерации уникального загрузчика для каждого изделия, так как используется уникальная для каждого процессора функция шифрования. Это неудобная процедура в серии, поэтому я не стал ее описывать. Если вам будет что сказать на эту тему, буду рад услышать.
Безопасная загрузка пытается обеспечить, чтобы на вашем изделии запускался загрузчик, с ядром которые именно вы в него загрузили.

О определении которое я дал secure boot. Secure boot не спасает вас от копирования изделия. Да, это делает процедуру сложнее, но незначительно. Загрузчик не может подписать всю ФС, следовательно не дает защиты на уровне ОС и на уровне конечного приложения. Работает это все классически: через ассиметричное шифрование. Я опишу процесс на пальцах, более глубоко это можно прочитать в [4]. В загрузчике указывается скрипт для HAB, где указаны подписанные области памяти, и где лежит цифровая подпись. Публичный ключ заранее размещается в One time programmable (OTP) памяти процессора. При загрузке скрипт проверяет указанную область памяти при помощи прикрепленной цифровой подписи и вшитого ключа. Если проверка не осуществилась, процессор не начинает загрузку. Работу HAB необходимо активировать тоже установив бит в регионе OTP памяти.

Недостатки secure boot:

  • Если HAB не активирован, прошивка все равно запустится. Это значит, что без доп. методов защиты ваша прошивка запустится на копии изделия. Для того, чтобы этого не происходило, загрузчик должен самостоятельно активировать HAB при запуске. Но тогда вставив в другое изделие флешку и забыв прошить публичный ключ, вы можете выкинуть процессор.
  • При доступе к памяти процессора, можно скопировать публичный ключ, этого достаточно для копирования изделия.

Реализация


Вообще в интернете есть достаточно много англоязычных статей [5][6] на тему реализации, но ни один предложенный метод у меня сразу не взлетел, а ссылки с ПО начали умирать, поэтому я решил сохранить это как статью в первую очередь для себя, когда я вернусь к данному вопросу в будущем.

Для начала необходимо скачать следующее ПО: code signing tool [7] и необходимые скрипты для генерации ключей и подписывания. В указанном архиве я убрал все лишние утилиты и библиотеки, так как там достаточно много неиспользуемого для нашей задачи.

Далее необходимо сгенерировать ключи:

cd ${CST_PATH}/release/keys
echo ${serial} > serial //8 цифр, число необходимо для генерации сертификата
echo ${password} >  key_pass.txt // Ввод пароля дважды, служит для защиты приватного ключа
echo ${password} >>  key_pass.txt //
./hab4_pki_tree.sh // запуск скрипта для генерации ключей
Do you want to use an existing CA key (y/n)?: n 
Do you want to use Elliptic Curve Cryptography (y/n)?: n
Enter key length in bits for PKI tree: 4096
Enter PKI tree duration (years): 10
How many Super Root Keys should be generated? 4
Do you want the SRK certificates to have the CA flag set? (y/n)?: y
 ../linux64/bin/srktool -h 4 -t SRK_1_2_3_4_table.bin -e SRK_1_2_3_4_fuse.bin  -d sha256 -c ./SRK1_sha256_4096_65537_v3_ca_crt.pem,./SRK2_sha256_4096_65537_v3_ca_crt.pem,./SRK3_sha256_4096_65537_v3_ca_crt.pem,./SRK4_sha256_4096_65537_v3_ca_crt.pem -f 1

Скрипт генерации ключей вызывает указанные выше вопросы. Я привел пример стандартных опций. От ответа на них зависит как будут называться сгенерированные ключи и параметры сгенерированных ключей. Учитывайте при дальнейших командах. Далее считываем публичный ключ, необходимый для записи в процессор. Воспользуемся для этого удобным скриптом от [5].

cd ${CST_PATH}/release/linux64/bin
./var-u-boot_fuse_commands.sh 
fuse prog 3 0 0xFFFFFFFF //Результат в командах uboot.
fuse prog 3 1 0xFFFFFFFF //Эти команды можно сразу забивать 
fuse prog 3 2 0xFFFFFFFF // остановив процедуру загрузки и войдя в консоль.
fuse prog 3 3 0xFFFFFFFF // Важно - память однократно программируемая.
fuse prog 3 4 0xFFFFFFFF // Второй попытки у вас не будет.
fuse prog 3 5 0xFFFFFFFF //
fuse prog 3 6 0xFFFFFFFF  //
fuse prog 3 7 0xFFFFFFFF //

Данная задача может быть применена для uboot c интегрированным SPL или с раздельным SPL и uboot (релиз rocko например). Я пишу релиз для того, чтобы вы знали где они используются стандартно. Если вы будете компилировать uboot сами, то скорее всего у вас SPL будет интегрирован в uboot. Важный момент, в defconfig загрузчика необходимо добавить следующие опции:

CONFIG_SECURE_BOOT=y
CONFIG_SYS_FSL_HAS_SEC=y 
#define CONFIG_SYS_FSL_SEC_COMPAT    4 /* HAB version */
#define CONFIG_FSL_CAAM
#define CONFIG_CMD_DEKBLOB
#define CONFIG_SYS_FSL_SEC_LE
#define CONFIG_FAT_WRITE

Эти опции активируют драйвера для работы с аппаратными модулями. Все как в menuconfig для Linux. При компиляции загрузчика используйте verbose mode (убрать V=1. В гайде написана сборка с этим параметром. Однако я у себя убирал). В таком случае в логи при компиляции добавляется информация о адресе точки входа и прочих вещей необходимых для записи цифровой подписи. После компиляции нам необходимы следующие выходные файлы:

  • u-boot-ivt.img
  • u-boot-ivt.img.log
  • SPL
  • SPL.log

копируете их в ${CST_PATH}/release/linux64/bin. Дальше необходимо выполнить достаточно простой скрипт.

Подробное описание процедуры скрипта
Вам необходимо сформировать файл командной последовательности подписи (*.csf). Сам по себе файл стандартный, но в нем необходимо добавить три параметра:

  • Точка входа
  • Смещение
  • Количество байт для подписи.

Эти данные нужно получить из лога компилятора. После компиляции загрузчика, он создает файл лога в который записывает смещения (В случае если вывод компилятора полный).

Примеры смещений:

Image Name:   U-Boot 2019 // Для U-boot
Created:      XXXXXX
Image Type:   ARM U-Boot Firmware with HABv4 IVT (uncompressed)
Data Size:    339904 Bytes = 331.94 KiB = 0.32 MiB
Load Address: 17800000
Entry Point:  00000000
HAB Blocks:   0x177fffc0   0x0000   0x00051020

Image Type:   Freescale IMX Boot Image // Для SPL
Image Ver:    2 (i.MX53/6/7 compatible)
Mode:         DCD
Data Size:    61440 Bytes = 60.00 KiB = 0.06 MiB
Load Address: 00907420
Entry Point:  00908000
HAB Blocks:   00907400 00000000 0000cc00
DCD Blocks:   00910000 0000002c 00000004

Нам нужны адреса «HAB Blocks». После завершения процедуры записи, вы генерируете подпись следующей командой и соединяете подпись с загрузчиком

cd ${CST_PATH}/release/linux64/bin
./cst --o u-boot_csf.bin --i u-boot.csf
cat u-boot-ivt.img u-boot_csf.bin > u-boot_signed.img


Насколько я помню в случае с интегрированным SPL скорее всего все так же будет работать, но стоит это проверить. Я сейчас уже не помню и не стал из-за этого компилировать при написании статьи.

cd ${CST_PATH}/release/linux64/bin
SOC=mx6 ./var-som_sign_image.sh SPL u-boot-ivt.img

По итогам скрипта вы получите два файла: SPL_signed, u-boot-ivt.img_signed которые необходимо записать на диск целевой системы. Пример скрипта записи на целевую систему прикладываю. Без sudo, чтобы случайно не выстрелило вам в ногу.

dd if=SPL_signed of=${DEVICE(/dev/sd*)} bs=1K seek=1; sync
dd if=u-boot-ivt.img_signed of=${DEVICE(/dev/sd*)} bs=1K seek=69; sync

Записав на чистый носитель вы должны получить загрузчик без ядра, который упадет в консоль, так как не найдет dtb и ядро. В консоли вы можете проверить собственно работает ли то, что вы сделали или не очень. При наличии event — что-то пошло не так. Важно: если загрузчик скомпилирован без поддержки HAB, ошибок тоже не будет.

hab_status
HAB Configuration: 0xf0, HAB State: 0x66
No HAB Events Found!

Последним шагом служит активация HAB, внимание команда однократная, исправить ее нельзя, так что будьте осторожны

fuse prog 0 6 0x2

Заключение


Я не стал описывать процесс интеграции всего этого в yocto, должны же у меня остаться секреты. Однако это реализуемо и не требует много времени.

Справка


[1] bare metal- встроенное программное обеспечение, которое напрямую работает на аппаратном обеспечении без какой либо абстракции в виде операционных систем
[2] we.easyelectronics.ru/Soft/zaschita-ustroystva-ot-vzloma-i-kopirovaniya.html
[3] habr.com/ru/post/350602
[4] HAB4_API.pdf в github.com/BMValeev/CST_TOOL
[5] variwiki.com/index.php?title=High_Assurance_Boot
[6] boundarydevices.com/high-assurance-boot-hab-dummies
[7] github.com/BMValeev/CST_TOOL