Secure DFU это загрузчик (DFU bootloader), предоставленный в пакете nRF5 SDK v12. Более старый загрузчик из SDK v11 и более старых версий SDK теперь называется Legacy DFU. Secure DFU не обеспечивает обратную совместимость с Legacy DFU.
Примечание: в этой статье приведен перевод документации [1], которая основана на описание примеров загрузчиков DFU [2], документации по модулям загрузчика [3] и службы Secure DFU [4]. Даны пошаговые инструкции ля упрощения изучения и тестирования Secure DFU. Непонятные термины и сокращения см. в Словарике [17].
[Необходимые компоненты]
1. Последняя версия nRF5 SDK (как минимум версия SDK v12). 2. Python 3 с установленной утилитой pip. 3. Установленная версия 4.9-2015-q3-update (or newer) компилятора GCC тулчейна для ARM. 4. Утилита make (MinGW, GNU Make или Xcode). 5. Тулчейн ARM GNU (для компиляции uECC). 6. Плата разработчика nRF5x DK или другая плата на основе чипа nRF5x. 7. Смартфон с поддержкой BLE, или PC с еще одним комплектом платы nRF5x DK.
Используемое программное обеспечение можно скачать по ссылке [18].
[Шаг A. Генерация ключей]
Нам понадобится пара из публичного (Public) и приватного (Private) ключей, чтобы зашифровать сигнатуру и подписать образ DFU по алгоритму ECDSA_P256_SHA256. Nordic предоставляет утилиту nRFutil [6] для генерации этих ключей. nRFutil.exe можно загрузить с сайта GitHub [5], руководство по использованию см. [6].
Можно установить nrfutil из python командой pip3 install nrfutil. Для проверки обновления вызовите команду pip3 install nrfutil --upgrade (или используйте python -m pip install nrfutil).
Модуль public_key.c будет использоваться для сборки кода загрузчика.
[Шаг B. Сборка загрузчика]
Загрузчик находится в папке SDK \examples\dfu\bootloader_secure. Обратите внимание, что существует 2 версии загрузчика, предлагаемые в папках pca100xx и pca100xx_debug (здесь xx обозначает вариант отладочной платы разработчика для pca10028 или pca10040). Версия с суффиксом _debug не делает проверку версии, к этой версии мы вернемся позже, сейчас будем использовать нормальную версию загрузчика.
B1a. Компиляция библиотеки uECC. Библиотека uECC используется в загрузчике для расшифровки сигнатуры. uECC это внешняя библиотека, её можно загрузить с сайта GitHub [7]. Обратите внимание, что эта версия библиотеки поставляется с лицензионным соглашением. Если Вы не хотите использовать сборку с uECC, то можете взять скомпилированную библиотеку Oberon, см. шаг B1b.
Загрузите или склонируйте библиотеку в каталог SDK: \external\micro-ecc\micro-ecc. В Проводнике это будет выглядеть следующим образом:
В папке \external\micro-ecc\ выберите IDE и компилятор, который хотите использовать для сборки загрузчика. Автор [1] выбрал SDKFolder\external\micro-ecc\nrf52_keil\armgcc и выполнил команду make, чтобы запустить сборку uECC.
После сборки библиотеки uECC переходите к шагу B2.
B1b. Использование Oberon crypto library вместо uECC. Если Вы не хотите использовать uECC, то можете использовать вместо неё готовую скомпилированную библиотеку Oberon. При использовании Oberon не нужно загружать и выполнять сборку uECC, и не нужно выполнять условия лицензии uECC. Для использования Oberon поменяйте следующее в заголовке sdk_config.h:
NRF_CRYPTO_BACKEND_MICRO_ECC_ENABLED установите в 0 NRF_CRYPTO_BACKEND_NRF_SW_ENABLED установите в 0 NRF_CRYPTO_BACKEND_OBERON_ENABLED установите в 1
И удалите micro_ecc_lib_nrf52.lib из списка файлов проекта.
Позднее, когда будете компилировать нормальную, не _debug версию, Вы увидите сообщение об ошибке, что образ загрузчика не помещается в установленный размер области flash. Для исправления ошибки нужно увеличить размер области загрузчика путем перемещения области начала приложения (flash start address) ниже на 0x1000 и увеличьте размер области flash на 0x1000. Например, для проекта nRF52840 поменяйте IROM1 start с 0x78000 на 0x77000, и IROM1 size с 0x6000 на 0x7000. Это будет означать, что размер загрузчика теперь стал больше на 4 килобайта. Увеличение образа загрузчика - недостаток использования библиотеки Oberon по сравнению с uEcc.
B2. Скопируйте файл модуля public_key.c, сгенерированный на шаге A2, в Вашу папку загрузчика или в выбранную папку, и подключите этот модуль к своему проекту. Удалите файл dfu_public_key.c из проекта, потому что он будет заменен на public_key.c.
Теперь все готово к сборке загрузчика.
B3. Выполните сборку загрузчика. Если были корректно выполнены шаги B1 и B2, то сборка пройдет без ошибок. Обратите внимание, что всякий раз, когда генерируется новая пара публичного и приватного ключей, нужно обновить файл public_key.c и заново выполнить сборку загрузчика.
[Шаг C. Генерация DFU-пакета .zip]
Этот пакет нужен для DFU master, чтобы отправить новый образ (образы) в обновляемый DFU target. Пакет представляет собой .zip-файл, содержащий hex-файл (файлы) образов кода, который мы хотим обновить, и init-пакет [15], включающий сигнатуру пакета. В основной части этого руководства мы будем обновлять только образ приложения. Для обновления самого загрузчика и SoftDevice см. Appendix далее.
C1. Подготовка hex-файла приложения. Выполните сборку своего приложения, и найдите его .hex-файл в папке _build. Обычно двоичный код скомпилированного приложения называется nrf52832_xxaa.hex, если Вы компилируете один из примеров SDK. Прошейте hex-файл приложения в память микроконтроллера и проверьте его работоспособность, он должен нормально работать без загрузчика (если необходимо, прошейте также двоичный код SoftDevice).
C2. Генерация .zip-файла. Скопируйте private.key, сгенерированный на шаге A1 в ту же папку, где находится .hex-файл приложения. Для генерации .zip-файла автор [1] использовал команду:
--hw-version: по умолчанию эта опция должна соответствовать семейству используемого MCU. Если используется чип nRF51, то нужно указать "51". Если Вы хотите получить свою собственную версию кода аппаратуры, то можете определить это в своем загрузчике, используя #define NRF_DFU_HW_VERSION your_hw_number.
--application-version: по умолчанию начальный номер для версии приложения 0. Чтобы было возможно обновить приложение, его версия должна быть равна или больше, чем сохраненная в загрузчике. В моем случае используется 1. Подробнее про схему принятия версий см. раздел "Acceptance rules for versions" документации [8].
--sd-req: приложение автора [1] работало под управлением Softdevice S132 v4.0.2. Кодовый номер для этой версии SoftDevice 0x98. Вы можете найти номер кода версии SoftDevice путем запуска команды nrfutil pkg generate --help, или см. номера версий в __main__.py.
--application: говорит nrfutil, что Вы обновляете приложение, и предоставлен образ приложения.
[Шаг D. Выполнение процедуры DFU]
Теперь у нас есть файл DFU .zip и загрузчик, настало время попробовать выполнить DFU.
D1. Прошейте загрузчик и SoftDevice в flash-память MCU. Используйте nRFGo Studio, или nrfjprog, или IDE, или JLink.exe [9], чтобы сначала прошить SoftDevice, а потом загрузчик. Проверьте, что загрузчик запустился, и посылает advertising-сообщения как устройство "DFUTarg".
D2. Скопируйте DFU .zip файл, сгенерированный на шаге C2 на смартфон, или в выбранную папку на PC.
D3. Используйте приложение nRFConnect/nRFToolbox на смартфоне или PC, чтобы подключиться к загрузчику и выполнить OTA DFU, используя скопированный .zip-файл (кликните на кнопке DFU).
D4. Проверьте, что новое приложение работает, после того, как процесс DFU достигнет 100%. Например, приложение начнет выдавать advertising с именем, установленным для Вашего приложения. Если приложение запустилось и нормально работает, то можно Вас поздравить - выполнили свое первое обновление Secure OTA DFU!
[Шаг E. Обновление приложение новой версией]
E1. Сгенерируйте новое firmware. Чтобы можно было проверить результат обновления, поменяйте advertising-имя устройства, либо поменяйте индикацию LED.
E2. Сгенерируйте новый пакет DFU .zip. Установите новую версию приложения (как минимум равную или большую, чем оригинальная версия).
E3. Переключите устройство в DFU mode, выполните DFU update и проверьте, что теперь работает новый загруженный образ.
[Appendix - продвинутые функции]
1. Комбинирование образа приложения, загрузчика и SoftDevice - настройка загрузчика
Загрузчик просматривает свои настройки (bootloader setting) чтобы определить, прошито ли правильное приложение в память MCU, или нет. Эта проверка выполняется при загрузке (включение питания или сброс). Если мы просто запрограммируем приложение с загрузчиком с помощью программатора, то загрузчик не сможет определить, что загружено достоверное приложение, и вместо его запуска перейдет в режим загрузки (DFU mode). Приложение в этом случае работать не будет. Только после успешной процедуры DFU загрузчик выполняет запись в bootloader setting, чтобы сделать метку о достоверности приложения.
Однако во время производства Вы можете не захотеть устанавливать приложение через OTA DFU для тысяч устройств, потому что это займет дополнительное время. То же самое может быть и при разработке. В каких-то случаях Вы можете не хотеть делать DFU для любых изменений, сделанных в приложении.
Для решения этой проблемы Вы можете сгенерировать bootloader setting вручную, и прикрепить их к загрузчику, чтобы он принял записанное программатором приложение. Мы будем использовать nrfutil для генерации bootloader setting, и утилиту mergehex.exe для объединения hex-файлов. Если Вы не хотите делать объединение hex-файлов, то можете сначала прошить hex-файл для bootloader setting, и потом прошить hex-файл загрузчика.
Примечание: для nRF52840 используйте --family NRF52840.
После этой команды сгенерируется файл bootloader_setting.hex. Он будет содержать в себе версию приложения и CRC, которые совпадают с предоставленным hex-файлом Вашего приложения yourApplication.hex. Файл bootloader_setting.hex должен использоваться для перезаписи настроек по умолчанию загрузчика.
Назначение опций команды:
--bl-settings-version: для версий SDK от v12.0 до SDK v15.2 здесь должна быть указана 1. Для SDK v15.3 и более новой версии нужно указать 2.
--application-version и --bootloader-version: начальная версия приложения и загрузчика соответственно, по вашему выбору. В этом примере автор [1] использовал 0.
--family: семейство устройства, совпадающее с Вашим используемым MCU. Если используете nRF52840, то нужно указать NRF52840 вместо NRF52.
После этого слейте друг с другом bootloader_setting.hex с загрузчиком и приложением, и если хотите то еще и с SoftDevice. Для этого можно использовать утилиту mergehex. Пример слияния двух hex-файлов:
Примечание: если Ваше приложение требует записи UICR [16], то хорошей идеей будет сделать такое слияние, чтобы записать приложение программатором вместо выполнения OTA. При выполнении OTA DFU загрузчик не будет записывать в UICR.
После того, как получили один объединенный hex-файл, мы можем записать его программатором сразу в несколько устройств, и не нужно будет делать OTA DFU для прошивки приложения в каждое устройство.
2. Secure Boot Validation
Как уже упоминалось выше, на фазе загрузки bootloader выполняет специальную проверку приложения (boot validation). В SDK v15.2 и более старых версиях применяется простая проверка CRC, чтобы проверить достоверность образа приложения. Сигнатура используется только когда загружается новый образ в процессе DFU, но не при загрузке приложения. Начиная с SDK v15.3 реализована более продвинутая boot validation для приложения, SoftDevice и bootloader. Последовательность загрузки теперь считается защищенной (secure boot sequence). Подробнее про secure boot и firmware update см. документацию [10].
Если кратко, то обычная процедура загрузки начинается с запуска MBR, которая затем запускает код загрузчика. Загрузчик выполняет 2 задачи: защищает сам себе от перезаписи с помощью BPROT/ACL, и затем проверяет образы SoftDevice и приложения. Существует несколько опций такой проверки, которыми можно выбрать: signature validation, hash validation, CRC validation (последний вариант это оригинальная опция из SDK v12) и no validation. Полное описание см. в секции "Boot validation modes" документации [10].
Вы можете выбрать опцию валидации, когда генерируете пакет DFU. Используйте --sd-boot-validation для проверки SoftDevice, и --app-boot-validation, когда генерируется пакет DFU .zip с помощью nrfutil. У Вас 2 опции на выбор:
Это также применяется для bootloader setting. Вы можете генерировать bootloader setting с такой же опцией для boot validating приложения и/или SoftDevice, вместо того чтобы просто проверить CRC приложения, как было раньше.
3. Buttonless DFU
Самый простой способ войти в загрузчик - удерживать Bootloader Button (button 4 на плате DK) и сбросить устройство. Однако иногда может быть так, что у Вашего устройства нет никакой кнопки, или Вы просто хотите переключиться с приложения на загрузчик без физического контакта с устройством. В этом случае понадобится "бескнопочный DFU" (Buttonless DFU), который указывает приложению переключиться в загрузчик. Это указание передается через пакет BLE.
Пример Buttonless DFU находится в папке \examples\ble_peripheral\ble_app_buttonless_dfu, описание см. по ссылке [11]. Пример бескнопочного DFU не отличается от обычного приложения. Вам все еще нужно прошить загрузчик. Важно убедиться, что загрузчик нормально работает с обычным приложением, перед тем как мы начнем разбирать пример без кнопки.
Способ запуска без кнопки довольно прост, мы записываем в регистр GPREGRET флаг (BOOTLOADER_DFU_START = 0xB1), и затем выполняем программный сброс (soft reset). Это делается в bootloader_start() из модуля ble_dfu.c.
Поскольку регистр GPREGRET сохраняет свое значение после сброса, загрузчик будет проверять это значение после сброса, и может тогда войти в DFU mode вместо запуска обычного приложения. Это дает тот же эффект, что и удержание Bootloader button при сбросе.
Обратите внимание, что в SDK v12 мы записываем в bootloader setting вместо записи в регистр GPREGRET. Вероятно использование GPREGRET более чистый и безопасный способ передачи флага входа в режим DFU.
Теперь давайте найдем, когда вызывается bootloader_start(). В SDK v13 была введена характеристика DFU buttonless. Эта характеристика имеет свойства индикации (indication) и записи (write). Нам нужно просто разрешить индикацию на характеристике и записать в неё 0x01. Buttonless-устройство отправит индикацию, и будет ожидать подтверждения от DFU master (устройства BLE Central). Когда поступило подтверждение от Central, будет вызвана функция bootloader_start() (см. обработчик on_hvc() в модуле ble_dfu.c). После этого соединение будет сброшено.
Во время написания [1] пример experimental_ble_app_buttonless_dfu не выполняет никаких перенаправлений привязки (bond forwarding). Поэтому Вы можете столкнуться с проблемой, если делается привязка (bonding) между Central (смартфоном) и устройством. Более подробно см. далее секцию 5.
Руководство по использованию buttonless DFU доступно на GitHub [12].
4. Обновление SoftDevice и загрузчика
Можно обновить не только приложение, но также SoftDevice и даже сам загрузчик. Поддерживается комбинация из этих трех образов, однако возможны не все комбинации, см. таблицу ниже.
Комбинация
Поддерживается
Примечание
BL
Да
SD
Да
У обновляемого SoftDevice должна совпадать Major-версия со старым SoftDevice.
APP
Да
BL+SD
Да
BL+APP
Нет
Вместо этого создайте два .zip-пакета и выполните два обновления одно за другим.
BL+SD+APP
Да
SD+APP
Да
У обновляемого SoftDevice должна совпадать Major-версия со старым SoftDevice.
Начиная с версии nRFUtil v3.2 добавлен параметр --sd-id. Он требуется, если пакет содержит SD (+BL) + APP. Для этого --sd-id должна соответствовать softdevice ID нового SoftDevice, а --sd-req должна соответствовать softdevice ID старого SoftDevice.
5. Bond forwarding и buttonless DFU
Перед тем, как перейти к следующему шагу, убедитесь, что Buttonless DFU не делает привязку (no bond working) как было объяснено в Appendix 2. Эта часть основана на последней версии SDK, и на момент написания статьи [1] это была версия SDK v15.2. Просмотрите руководство [11].
Необходимо изменить следующие параметры:
- В sdk_config.h загрузчика:
Поменяйте NRF_DFU_BLE_REQUIRES_BONDS на 1. Поменяйте NRF_SDH_BLE_SERVICE_CHANGED на 1.
- В sdk_config.h приложения ble_app_buttonless_dfu:
Поменяйте NRF_DFU_BLE_BUTTONLESS_SUPPORTS_BONDS на 1. Проверьте, если NRF_SDH_BLE_SERVICE_CHANGED не 1, то установите в 1.
Скомпилируйте 2 проекта.
Сгенерируйте страничку настроек загрузчика (bootloader setting page) для приложения ble_app_buttonless-dfu , как описано в документации [6]. Для nRF52832 должен быть следующий синтаксис:
Причина, по которой нам нужно сгенерировать bootloader setting в том, что нам необходимо разрешить bonding в загрузчике. При такой настройке загрузчик не будет работать, если отсутствует какая-либо привязка (bonding). При первом запуске, если у Вас нет информации привязки (bond information), загрузчик не запустится, и у Вас не будет возможности использовать DFU buttonless приложение. Так что нам нужно создать bootloader setting, которые мы сможем объединить с загрузчиком и приложением, чтобы залить из в flash устройства программатором SWD.
Обратите внимание, что если Вы используете SDK v15.3 или более свежий, то должны использовать --bl-settings-version 2, в то время как загрузчик в SDK v12-15.2 использует bootloader setting версии 1.
После этого вы можете прошить app_bootloader_and_setting.hex (после прошивки SoftDevice). Устройство должно запуститься и делать advertising как Nordic_Buttonless. После этого Вы можете протестировать устройство с помощью приложения nRF Connect [14]. Наблюдайте, что приложение выполнит автоматическую привязку (automatically bond), когда Вы разрешите индикацию, и впоследствии, когда Вы запустите DFU, оно переключится в загрузчик без изменения адреса (+1 к адресу). Убедитесь, что процесс DFU выполняется, когда соединение перейдет в состояние привязки (connection status bonded).
6. Код DFU Master
На момент написания [1] не существовало официального кода DFU Master, работающего на MCU (что позволило бы, к примеру, обновить nRF52 от другого nRF52 через BLE или UART/SPI). Официально предоставляется исходный код для обновления nRF52 с PC или смартфона.
Чтобы помочь в разработке собственного кода, техподдержка предоставила примеры очень простых мастеров SPI/UART DFU, работающих на nRF52, чтобы обновить другой nRF52. Они совместимы с текущей версией протокола Secure DFU. Следующий пример был сделан для SDK v14.2, но он должен работать на SDK v15.x с минимальными изменениями. В каталоге DFU-Master-Code архива [18] находятся следующие файлы:
DFU_SPI_readme.docx Документация для SPI DFU Master. DFUMaster_SPI.zip Код SPI DFU Master (поместите его в /examples/dfu/). DFUMaster_UART.zip Код UART DFU Master. bootloader_secure_SPIS.zip Код SPI DFU bootloader.
7. Background DFU
Пример background DFU можно найти по ссылке [13].
[Ссылки]
1. Getting started with Nordic's Secure DFU bootloader, a step by step guide site:devzone.nordicsemi.com. 2. DFU bootloader examples site:infocenter.nordicsemi.com. 3. Bootloader and DFU modules site:infocenter.nordicsemi.com. 4. Buttonless Secure DFU Service site:infocenter.nordicsemi.com. 5. NordicSemiconductor / pc-nrfutil site:github.com. 6. nRF Util. 7. kmackay / micro-ecc site:github.com. 8. BLE Secure DFU Bootloader site:infocenter.nordicsemi.com. 9. Автоматизация прошивки SoftDevice. 10. Secure boot and firmware updates site:infocenter.nordicsemi.com. 11. Buttonless DFU Template Application site:infocenter.nordicsemi.com. 12. gamnes / nRF52832-buttonless-dfu-development-tutorial site:github.com. 13. Background DFU application source code site:devzone.nordicsemi.com. 14. nRF Connect for Mobile site:nordicsemi.com. 15. nRF5 SDK v12.3.0 DFU bootloader. 16. nRF52: регистры конфигурации пользователя UICR. 17. Bluetooth: аббревиатуры и термины. 18. 211203Nordic-Secure-DFU-bootloader.zip - компилятор ARM GCC, nRF5 SDK v12.3.0, pc-nrfutil, uECC.