Основные фичи: загрузка образов программного кода из интернета по USB, Wi-Fi и с SD карты, шифрация программного кода с аутентификацией алгоритмом AES 256 GCM, подпись ключом RSA 2048, сжатие разными типами компрессоров. При этом простая подготовка образов программного кода и смена ключей одним кликом.

Надо сказать, что с минимальными изменениями проект загрузчика может быть перенесён на схожее по API семейство RA8 от Renesas. Но и на любую другую платформу перенести будет не сложно, нужно будет только заменить API шифрования и хэширования. Образы прошивок могут быть адаптированы под разные возможности платформ.

Зачем нужен начальный загрузчик

Данный загрузчик (он же bootloader) разработан для модуля на микроконтроллере S7G2. Проект модуля представлен здесь. Затем был разработан более бюджетный вариант и вот он в минимальной комплектации.

На бюджетном варианте модуля убраны все чипы беспроводной коммуникации,  оставлена коммуникация через USB в Host или Device режиме
На бюджетном варианте модуля убраны все чипы беспроводной коммуникации, оставлена коммуникация через USB в Host или Device режиме

На этом модуле был сделан универсальный программируемый контроллер (ПЛК). Такие устройства очень нуждаются в защищённых начальных загрузчиках. Начальный загрузчик должен принять основной программный код через удобный и доступный интерфейс и запрограммировать ею flash-память микроконтроллера. В течении жизни устройства загрузчику неоднократно придётся повторять эту операцию, поскольку поддержка ПЛК невозможна без постоянного обновления софта. Сам загрузчик записывается в микроконтроллер через SWD адаптер. Наш загрузчик по сути - вторичный загрузчик. Первичный находится в ROM микроконтроллера изначально, и предоставляет возможность загружать программный код по USB и UART. Но первичный загрузчик весьма ограничен в возможностях. Он годится только для локальной загрузки.

Образ загружаемого программного кода

Образ загружаемого программного кода дальше буду называть прошивкой. Прошивка, как правило лежит в свободном доступе, скачивается или передаётся заказчикам через открытые публичные каналы. Поэтому прошивка должна быть зашифрована, чтобы исключить дизассемблирование, должна быть подписана, чтобы исключить подделку, должна содержать хэш, чтобы распознать повреждение данных. Желательно прошивку сжать, чтобы сэкономить трафик. Попав в микроконтроллер прошивка не должна быть считана простыми средствами из Flash. Крипто-ресурсы и пароли в прошивке должны быть уникальными, чтобы исключить взлом других аналогичных устройств при взломе одного из них.
Из всего следует, что для получения надёжной прошивки надо программный код упаковать в транспортный формат и снабдить мета-информацией.

Транспортный формат прошивки загрузчика

Транспортный формат разработан с учётом разных возможностей платформ и целей использования. Не всегда требуется шифрование и подпись, особенно на этапе отладки. Поэтому данные могут быть в прошивке незашифрованными и неподписанными. Все это указывается в заголовке файла. В заголовке данных более детализируется способ сжатия и хэширования данных. Расширение формата и дополнительные данные о версии могут помещаться в текстовом поле информации о прошивке. Предполагается что прошивка несёт в себе информацию об одном сплошном блоке кода. Если нужно передать несколько прошивок для разных областей, то создаётся несколько прошивок. Данные в прошивке сегментированы на блоки по 16 байт чтобы соответствовать требованиям алгоритма шифрации AES 256. Даже если нет подписи защиту данных от изменений обеспечивает алгоритм AES GCM с использованием контрольного тэга.

Алгоритмы сжатия требуют значительных объёмов динамической памяти, поэтому если в контроллере нет такого количества, то сжатие можно отключить.

Хэш данных может быть разных видов. Выбор зависит опять же от возможностей целевого микроконтроллера. Не все контроллеры имею аппаратный блок вычисления хэша. S7G2 такой блок имеет и на нем, к примеру, MD5 вычисляется быстрее чем табличный CRC32. Чем длиннее хэш тем меньше вероятность коллизий, когда разные данные дают одинаковый хэш. Хэш SHA 512 оставлен для применения в RA8, где есть аппаратный блок для него. S7G2 хэш SHA 512 не поддерживает.

Транспортный формат прошивки. Области заголовка данных и самих данных шифруются AES. Подпись шифруется RSA.
Транспортный формат прошивки.
Области заголовка данных и самих данных шифруются AES. Подпись шифруется RSA.

Утилита подготовки прошивок

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

Утилита генерирует прошивки по списку из таблицы. Таблица хранится в файле FirmwareList.json
Утилита генерирует прошивки по списку из таблицы. Таблица хранится в файле FirmwareList.json

Утилита работает в среде Windows 10 и 11, используется для криптографии библиотеку OpenSSL. В директории утилиты должен присутствовать файл libcrypto-3.dll

Для шифрования и подписи утилита использует ключи из файла Keys.c (имя может быть произвольным) содержащем объявления массивов на языке C.

Пример содержимого файла Keys.c
#include "S7V30.h"
// Не менять формат этого файла !!!
const unsigned char AESKey[AESKey_SIZE] __attribute__ ((aligned (4)))= 
{
  0x3B,0x77,0xFF,0x09,0x48,0x1B,0x67,0x6F,0xAA,0xB4,0x6B,0xE6,0x39,0x01,0xDD,0x5A,
  0x8C,0x24,0xCB,0x92,0x43,0x0C,0xF8,0x21,0x08,0x9F,0xF9,0x48,0xF9,0xBC,0x55,0x28
};
 
const unsigned char AES_init_vector[AES_init_vector_SIZE] __attribute__ ((aligned (4)))=
{
  0xE7,0x94,0x1B,0x8F,0x4C,0xBE,0x9B,0x46,0x58,0x68,0x1D,0xF5,0x00,0x00,0x00,0x01
};
 
const unsigned char AES_aad[AES_aad_SIZE] __attribute__ ((aligned (4)))=
{
  0x8F,0x7C,0xBC,0xF1,0x03,0xAF,0x55,0xA6
};
 
const unsigned char RSA_public_key[RSA_public_key_SIZE] __attribute__ ((aligned (4)))= // DER encoded RSA public key
{
  0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xD7,0xCA,0x28,0x43,0x89,0xB3,0x14,
  0x86,0x6C,0x12,0x9E,0x97,0xF8,0x04,0x75,0x73,0x06,0x59,0x42,0x28,0xEE,0x60,0x46,
  0xB8,0xF4,0xFF,0x10,0x65,0xFE,0x63,0x46,0x64,0xDC,0x4F,0x6A,0x2F,0x1F,0xEB,0x0D,
  0x46,0x5E,0x37,0x9C,0x32,0xFC,0x0A,0xAB,0x2B,0x4C,0x6C,0x44,0x6B,0x7D,0xFA,0x3A,
  0x9C,0x28,0x3B,0xB4,0xC4,0xA5,0xD9,0x50,0xCE,0x17,0xE9,0x24,0x66,0xB4,0x83,0x14,
  0xF5,0xA3,0xB4,0xE1,0x07,0xAB,0x17,0xBD,0xDE,0x1F,0x64,0x04,0x7B,0x0B,0x83,0x72,
  0x04,0x81,0xD7,0xFF,0x9F,0xEF,0x39,0x8B,0x21,0x6A,0x58,0xBA,0x7B,0x37,0x2A,0x3E,
  0x55,0x40,0x33,0x33,0x76,0x93,0xA3,0x02,0xCA,0x29,0x6C,0x70,0x3A,0x77,0x13,0x48,
  0xAF,0x4A,0xB5,0x20,0x53,0xE5,0x86,0x4A,0x95,0x5D,0xAC,0x96,0xDF,0x73,0x8A,0x40,
  0x0F,0x99,0x07,0xD8,0x46,0x4E,0xB4,0x08,0x0F,0x41,0x54,0x57,0x1A,0x41,0x0B,0x0A,
  0x9C,0x06,0xBF,0x0F,0x38,0xF7,0x50,0x11,0x8F,0xD1,0xB1,0x7F,0x8E,0xDB,0xD2,0xF7,
  0x52,0xE7,0xC3,0xB9,0x50,0x58,0x83,0x89,0x35,0x4C,0xE5,0xBC,0xF0,0x46,0x0F,0xF3,
  0xAC,0xDA,0x01,0x97,0x14,0xF3,0xD3,0xD4,0x98,0x8D,0x9C,0x85,0x03,0x68,0xFF,0x00,
  0x2E,0xD9,0x5D,0xE0,0xA7,0x3C,0x38,0x49,0x81,0x31,0xB8,0x89,0x27,0x2D,0x95,0x17,
  0x46,0x74,0x19,0x9D,0x8D,0x70,0x57,0xB6,0x18,0x0D,0x3B,0xF3,0xFF,0x4B,0xD3,0x2B,
  0x74,0x71,0x52,0x03,0xD0,0x7A,0x86,0xE3,0x01,0x7D,0x29,0x21,0x75,0x7B,0x8B,0x6E,
  0x77,0x89,0x3A,0xC8,0xD8,0x91,0xF3,0xE4,0xBD,0x02,0x03,0x01,0x00,0x01
};
 
const unsigned char RSA_private_key[RSA_private_key_SIZE] __attribute__ ((aligned (4)))= // DER encoded RSA private key
{
  0x30,0x82,0x04,0xA4,0x02,0x01,0x00,0x02,0x82,0x01,0x01,0x00,0xD7,0xCA,0x28,0x43,
  0x89,0xB3,0x14,0x86,0x6C,0x12,0x9E,0x97,0xF8,0x04,0x75,0x73,0x06,0x59,0x42,0x28,
  0xEE,0x60,0x46,0xB8,0xF4,0xFF,0x10,0x65,0xFE,0x63,0x46,0x64,0xDC,0x4F,0x6A,0x2F,
  0x1F,0xEB,0x0D,0x46,0x5E,0x37,0x9C,0x32,0xFC,0x0A,0xAB,0x2B,0x4C,0x6C,0x44,0x6B,
  0x7D,0xFA,0x3A,0x9C,0x28,0x3B,0xB4,0xC4,0xA5,0xD9,0x50,0xCE,0x17,0xE9,0x24,0x66,
  0xB4,0x83,0x14,0xF5,0xA3,0xB4,0xE1,0x07,0xAB,0x17,0xBD,0xDE,0x1F,0x64,0x04,0x7B,
  0x0B,0x83,0x72,0x04,0x81,0xD7,0xFF,0x9F,0xEF,0x39,0x8B,0x21,0x6A,0x58,0xBA,0x7B,
  0x37,0x2A,0x3E,0x55,0x40,0x33,0x33,0x76,0x93,0xA3,0x02,0xCA,0x29,0x6C,0x70,0x3A,
  0x77,0x13,0x48,0xAF,0x4A,0xB5,0x20,0x53,0xE5,0x86,0x4A,0x95,0x5D,0xAC,0x96,0xDF,
  0x73,0x8A,0x40,0x0F,0x99,0x07,0xD8,0x46,0x4E,0xB4,0x08,0x0F,0x41,0x54,0x57,0x1A,
  0x41,0x0B,0x0A,0x9C,0x06,0xBF,0x0F,0x38,0xF7,0x50,0x11,0x8F,0xD1,0xB1,0x7F,0x8E,
  0xDB,0xD2,0xF7,0x52,0xE7,0xC3,0xB9,0x50,0x58,0x83,0x89,0x35,0x4C,0xE5,0xBC,0xF0,
  0x46,0x0F,0xF3,0xAC,0xDA,0x01,0x97,0x14,0xF3,0xD3,0xD4,0x98,0x8D,0x9C,0x85,0x03,
  0x68,0xFF,0x00,0x2E,0xD9,0x5D,0xE0,0xA7,0x3C,0x38,0x49,0x81,0x31,0xB8,0x89,0x27,
  0x2D,0x95,0x17,0x46,0x74,0x19,0x9D,0x8D,0x70,0x57,0xB6,0x18,0x0D,0x3B,0xF3,0xFF,
  0x4B,0xD3,0x2B,0x74,0x71,0x52,0x03,0xD0,0x7A,0x86,0xE3,0x01,0x7D,0x29,0x21,0x75,
  0x7B,0x8B,0x6E,0x77,0x89,0x3A,0xC8,0xD8,0x91,0xF3,0xE4,0xBD,0x02,0x03,0x01,0x00,
  0x01,0x02,0x82,0x01,0x00,0x1E,0x1C,0x0A,0x97,0x35,0xE9,0x03,0x75,0xA8,0x31,0xC6,
  0xE8,0x4C,0x86,0x8E,0xBE,0xD0,0x85,0x76,0xDA,0x50,0x3C,0xA5,0xD1,0x9B,0xF9,0xD5,
  0x17,0x6E,0x31,0xFA,0xFA,0x0A,0xD5,0x97,0xEA,0xE0,0x68,0xC3,0x4E,0xEC,0xC9,0x94,
  0xA5,0x76,0x8E,0xA4,0x88,0xA3,0x9E,0xBD,0xC3,0x43,0xEE,0x3C,0xEC,0x5A,0x1E,0xCE,
  0x9F,0xDB,0xC6,0x61,0x64,0x88,0x68,0x93,0x18,0x33,0x9A,0xD0,0xBB,0x5F,0xAE,0xD6,
  0xF1,0x63,0x79,0xAA,0x18,0x0A,0xC8,0x41,0x69,0xCB,0xD6,0xF8,0xFB,0x51,0x04,0xCB,
  0x19,0xCD,0x5E,0xF9,0x9F,0x5D,0x7F,0xE8,0xF1,0x55,0x66,0xB3,0x10,0xE1,0x26,0xE3,
  0xC8,0xE9,0x5B,0xC7,0x01,0x5F,0x5B,0x4E,0xA3,0x91,0xC6,0xE2,0x8E,0xF6,0xFD,0x23,
  0xF8,0x20,0xE0,0x26,0x9A,0xC4,0x2F,0x27,0xE5,0xC8,0x79,0x44,0x00,0x25,0xAE,0x2B,
  0x2D,0x72,0x53,0x72,0x0E,0xFE,0x33,0xC1,0x4C,0xE8,0x12,0x92,0x50,0xFA,0xE9,0x5B,
  0x78,0xDD,0x14,0xCB,0xC1,0xDD,0x37,0xB1,0xAB,0x5E,0x9E,0xFB,0x0F,0xD2,0x6A,0x1F,
  0xDE,0xC2,0x44,0x3D,0xFB,0x86,0xF0,0xEE,0xD0,0xBD,0x22,0x8B,0xD3,0x44,0xA9,0xAD,
  0x76,0x6D,0x3D,0x5D,0x92,0xA7,0xB5,0x12,0x9A,0x7E,0x8B,0xF7,0xB6,0x98,0xEC,0xB7,
  0xEE,0x07,0xDD,0x4D,0x89,0xEE,0xA5,0x3D,0x37,0x63,0x3A,0x94,0x47,0x96,0xB7,0x8E,
  0x54,0x5A,0xA4,0xCF,0xF6,0xF9,0x34,0xDF,0x94,0x81,0xE1,0xF9,0x8C,0xDD,0xB5,0x71,
  0x2D,0xE3,0x57,0x7D,0xBC,0x82,0x71,0xE6,0x8C,0xC8,0x4B,0xF2,0xCF,0x1F,0xA6,0x81,
  0xAD,0x83,0xE5,0xFD,0x1F,0x02,0x81,0x81,0x00,0xF6,0x97,0xE3,0x74,0x1C,0x2E,0x6D,
  0xF2,0x01,0x98,0x67,0xAE,0x80,0xC7,0xA7,0x54,0xF5,0x4C,0x6C,0xB6,0xCC,0x0F,0xFF,
  0x75,0xA9,0xE8,0x91,0x92,0x30,0x6E,0xCF,0x75,0x51,0x80,0xCE,0x09,0xA3,0xE9,0x76,
  0x58,0x33,0xF6,0x4D,0x03,0xCC,0xCC,0x7C,0x5C,0x83,0x46,0xAF,0x8D,0x8F,0x39,0x71,
  0xEB,0x5C,0x9E,0x0B,0x9F,0x09,0x80,0x28,0xC5,0xA7,0x42,0x86,0xAF,0x6B,0xB9,0xE6,
  0x60,0xC5,0xE6,0x50,0x15,0xDD,0x1C,0x92,0x6D,0x05,0xDE,0xA6,0xF3,0x40,0x28,0x68,
  0x6D,0x49,0x60,0xB8,0x86,0xCF,0x87,0xA6,0x24,0x50,0x2E,0x53,0xFD,0x6D,0x09,0x91,
  0xE0,0xE2,0x60,0x08,0x82,0x92,0x79,0x7B,0x96,0xD7,0xB4,0x3D,0xC1,0x32,0x8C,0xED,
  0xE4,0x6C,0xBB,0x0E,0xF8,0x1C,0xD7,0x69,0x63,0x02,0x81,0x81,0x00,0xE0,0x05,0x74,
  0x8E,0xED,0x2C,0x21,0xCE,0x43,0x42,0x74,0xB4,0x7C,0x31,0xEF,0x40,0x78,0x36,0xCB,
  0x07,0x94,0xBD,0xD5,0xEE,0x12,0x87,0x9E,0x45,0xFF,0x2C,0x5F,0x8C,0xBC,0x23,0xCA,
  0x79,0xE2,0xE5,0x45,0x7E,0xDA,0x6F,0xDE,0xB0,0x91,0x65,0xE2,0xC3,0x4A,0x22,0x21,
  0xAD,0x59,0xA6,0x10,0x0B,0xA2,0x82,0xFE,0x5C,0xF0,0x2B,0x6B,0x6F,0x50,0x59,0x1D,
  0xEE,0xBA,0x31,0xEE,0x53,0xB4,0xC7,0x7A,0xFD,0xA2,0x42,0x44,0x55,0xB7,0x63,0x21,
  0xFF,0xF4,0xD0,0xEE,0xC8,0xBB,0xE2,0xD8,0x02,0xC0,0x6A,0x16,0x6C,0xC8,0xB0,0x7D,
  0xE2,0x80,0xC0,0xBD,0xCA,0xD7,0x58,0x79,0x70,0xBE,0xE0,0x09,0x33,0x12,0x3B,0xCB,
  0x3B,0x44,0x65,0x81,0xBC,0x03,0x5B,0x15,0xE1,0xB3,0x7B,0xE3,0x5F,0x02,0x81,0x80,
  0x57,0x57,0xB3,0x43,0xD4,0x1B,0x89,0xEB,0xD7,0x2E,0xD1,0x42,0x98,0xF0,0x9E,0xCF,
  0x53,0xD7,0x4F,0x06,0x6C,0x3F,0x1A,0x5F,0xE5,0xDF,0xAF,0x78,0x15,0x59,0x9A,0x77,
  0xD6,0x77,0x86,0x03,0x1A,0x20,0xBE,0x3F,0x3D,0xA0,0x76,0xE4,0xEA,0xAC,0x5F,0x3C,
  0x31,0x6F,0x5F,0x0D,0x07,0xF6,0xF3,0xE7,0xC6,0xB5,0x60,0x2E,0x63,0xA2,0x16,0x4E,
  0xF4,0x10,0x77,0x3E,0x39,0x2C,0xEE,0x71,0xC6,0x86,0xEE,0x44,0xE0,0x3D,0x97,0x8B,
  0xB0,0x31,0x8D,0xF6,0xC8,0xF9,0xC7,0x0B,0x36,0x9B,0x60,0xA0,0xB7,0x6F,0x37,0xA4,
  0xD3,0x15,0xA5,0xC0,0x34,0xD7,0xD7,0xE8,0xCC,0xA5,0xF1,0x3D,0xB6,0x7D,0xFB,0x0D,
  0x91,0xB7,0x90,0xCC,0x16,0x88,0x9E,0x56,0x73,0x40,0xCA,0x6B,0x8D,0x63,0xAB,0x3B,
  0x02,0x81,0x81,0x00,0x84,0xC4,0x96,0xEC,0xF2,0x41,0x7A,0x93,0x9D,0x27,0x30,0xB5,
  0x9A,0xF9,0x99,0x3B,0x53,0x43,0xDE,0x16,0x53,0x20,0x23,0x3E,0xE2,0xBA,0x7F,0xA2,
  0x2A,0x76,0x68,0xF6,0xA6,0xB6,0x6B,0x94,0xCB,0x55,0x68,0x57,0xA3,0x2C,0x34,0x05,
  0xDF,0x56,0x4C,0x0B,0xC3,0xCE,0xF0,0xB8,0xA5,0x6E,0x17,0x0A,0x5E,0x39,0x56,0x79,
  0x4C,0x59,0xDF,0x65,0x6C,0x70,0x34,0x9E,0x3C,0xB9,0xC4,0xEE,0xD5,0x30,0x78,0x83,
  0x03,0x7C,0x54,0xB5,0x9C,0x67,0x63,0x4A,0x28,0x14,0xFF,0xBF,0xF6,0x22,0x9E,0x35,
  0x1B,0x46,0x84,0x9D,0xDA,0x5F,0x47,0x6C,0x4B,0x34,0x70,0x07,0x5C,0x93,0x71,0xFD,
  0xD1,0x12,0x44,0x41,0x81,0x67,0xF0,0xAD,0xE2,0x7E,0x6A,0x62,0x3C,0x76,0xEA,0x44,
  0x06,0xB4,0xC0,0xEF,0x02,0x81,0x81,0x00,0xD7,0xEE,0x03,0xF2,0x74,0x3B,0xBF,0xFD,
  0xEA,0x9E,0x9C,0x97,0x00,0x8B,0x4D,0x9B,0xD8,0xF0,0x79,0x0E,0x82,0x2A,0x23,0x5D,
  0x9B,0x19,0xBC,0xE2,0x6D,0x40,0x4A,0xD1,0x6E,0xFA,0xE0,0xF9,0x37,0x18,0xD0,0x95,
  0xFE,0xCF,0x90,0x3D,0x80,0x2F,0x0D,0x19,0x53,0x70,0x33,0xF0,0x5B,0x04,0x17,0x3B,
  0x76,0xC5,0x12,0x05,0x9D,0xDE,0x7F,0x50,0xF2,0xE1,0x1C,0xE9,0x42,0xC1,0xB2,0x46,
  0xF1,0xD3,0xAC,0x4D,0x0C,0x64,0x19,0x1B,0xC5,0xEB,0x51,0xD6,0xDA,0x18,0x65,0xD0,
  0x18,0x6C,0xBA,0xA3,0x24,0x09,0x11,0xD1,0x95,0x22,0x27,0x55,0x49,0x00,0x5D,0x84,
  0x0A,0xE7,0x93,0xC3,0x45,0x05,0x3E,0x3D,0x5D,0xAE,0x4C,0x43,0xF8,0x63,0x57,0xA8,
  0xAD,0x2F,0x6A,0x65,0xAA,0x68,0x5F,0x9D
};
 
const unsigned char RSA_priv_exp_modul[RSA_priv_exp_modul_SIZE] __attribute__ ((aligned (4)))= 
{
  0x1E,0x1C,0x0A,0x97,0x35,0xE9,0x03,0x75,0xA8,0x31,0xC6,0xE8,0x4C,0x86,0x8E,0xBE,
  0xD0,0x85,0x76,0xDA,0x50,0x3C,0xA5,0xD1,0x9B,0xF9,0xD5,0x17,0x6E,0x31,0xFA,0xFA,
  0x0A,0xD5,0x97,0xEA,0xE0,0x68,0xC3,0x4E,0xEC,0xC9,0x94,0xA5,0x76,0x8E,0xA4,0x88,
  0xA3,0x9E,0xBD,0xC3,0x43,0xEE,0x3C,0xEC,0x5A,0x1E,0xCE,0x9F,0xDB,0xC6,0x61,0x64,
  0x88,0x68,0x93,0x18,0x33,0x9A,0xD0,0xBB,0x5F,0xAE,0xD6,0xF1,0x63,0x79,0xAA,0x18,
  0x0A,0xC8,0x41,0x69,0xCB,0xD6,0xF8,0xFB,0x51,0x04,0xCB,0x19,0xCD,0x5E,0xF9,0x9F,
  0x5D,0x7F,0xE8,0xF1,0x55,0x66,0xB3,0x10,0xE1,0x26,0xE3,0xC8,0xE9,0x5B,0xC7,0x01,
  0x5F,0x5B,0x4E,0xA3,0x91,0xC6,0xE2,0x8E,0xF6,0xFD,0x23,0xF8,0x20,0xE0,0x26,0x9A,
  0xC4,0x2F,0x27,0xE5,0xC8,0x79,0x44,0x00,0x25,0xAE,0x2B,0x2D,0x72,0x53,0x72,0x0E,
  0xFE,0x33,0xC1,0x4C,0xE8,0x12,0x92,0x50,0xFA,0xE9,0x5B,0x78,0xDD,0x14,0xCB,0xC1,
  0xDD,0x37,0xB1,0xAB,0x5E,0x9E,0xFB,0x0F,0xD2,0x6A,0x1F,0xDE,0xC2,0x44,0x3D,0xFB,
  0x86,0xF0,0xEE,0xD0,0xBD,0x22,0x8B,0xD3,0x44,0xA9,0xAD,0x76,0x6D,0x3D,0x5D,0x92,
  0xA7,0xB5,0x12,0x9A,0x7E,0x8B,0xF7,0xB6,0x98,0xEC,0xB7,0xEE,0x07,0xDD,0x4D,0x89,
  0xEE,0xA5,0x3D,0x37,0x63,0x3A,0x94,0x47,0x96,0xB7,0x8E,0x54,0x5A,0xA4,0xCF,0xF6,
  0xF9,0x34,0xDF,0x94,0x81,0xE1,0xF9,0x8C,0xDD,0xB5,0x71,0x2D,0xE3,0x57,0x7D,0xBC,
  0x82,0x71,0xE6,0x8C,0xC8,0x4B,0xF2,0xCF,0x1F,0xA6,0x81,0xAD,0x83,0xE5,0xFD,0x1F,
  0xD7,0xCA,0x28,0x43,0x89,0xB3,0x14,0x86,0x6C,0x12,0x9E,0x97,0xF8,0x04,0x75,0x73,
  0x06,0x59,0x42,0x28,0xEE,0x60,0x46,0xB8,0xF4,0xFF,0x10,0x65,0xFE,0x63,0x46,0x64,
  0xDC,0x4F,0x6A,0x2F,0x1F,0xEB,0x0D,0x46,0x5E,0x37,0x9C,0x32,0xFC,0x0A,0xAB,0x2B,
  0x4C,0x6C,0x44,0x6B,0x7D,0xFA,0x3A,0x9C,0x28,0x3B,0xB4,0xC4,0xA5,0xD9,0x50,0xCE,
  0x17,0xE9,0x24,0x66,0xB4,0x83,0x14,0xF5,0xA3,0xB4,0xE1,0x07,0xAB,0x17,0xBD,0xDE,
  0x1F,0x64,0x04,0x7B,0x0B,0x83,0x72,0x04,0x81,0xD7,0xFF,0x9F,0xEF,0x39,0x8B,0x21,
  0x6A,0x58,0xBA,0x7B,0x37,0x2A,0x3E,0x55,0x40,0x33,0x33,0x76,0x93,0xA3,0x02,0xCA,
  0x29,0x6C,0x70,0x3A,0x77,0x13,0x48,0xAF,0x4A,0xB5,0x20,0x53,0xE5,0x86,0x4A,0x95,
  0x5D,0xAC,0x96,0xDF,0x73,0x8A,0x40,0x0F,0x99,0x07,0xD8,0x46,0x4E,0xB4,0x08,0x0F,
  0x41,0x54,0x57,0x1A,0x41,0x0B,0x0A,0x9C,0x06,0xBF,0x0F,0x38,0xF7,0x50,0x11,0x8F,
  0xD1,0xB1,0x7F,0x8E,0xDB,0xD2,0xF7,0x52,0xE7,0xC3,0xB9,0x50,0x58,0x83,0x89,0x35,
  0x4C,0xE5,0xBC,0xF0,0x46,0x0F,0xF3,0xAC,0xDA,0x01,0x97,0x14,0xF3,0xD3,0xD4,0x98,
  0x8D,0x9C,0x85,0x03,0x68,0xFF,0x00,0x2E,0xD9,0x5D,0xE0,0xA7,0x3C,0x38,0x49,0x81,
  0x31,0xB8,0x89,0x27,0x2D,0x95,0x17,0x46,0x74,0x19,0x9D,0x8D,0x70,0x57,0xB6,0x18,
  0x0D,0x3B,0xF3,0xFF,0x4B,0xD3,0x2B,0x74,0x71,0x52,0x03,0xD0,0x7A,0x86,0xE3,0x01,
  0x7D,0x29,0x21,0x75,0x7B,0x8B,0x6E,0x77,0x89,0x3A,0xC8,0xD8,0x91,0xF3,0xE4,0xBD
};
 
 
const unsigned char Flash_access_pass[Flash_access_pass_SIZE] __attribute__ ((aligned (4)))=
{
  // SWD/JTAG access code strings:
  // For protection type 0 : JTAG/SWD no need access code
  // For protection type 1 : 9C3B6C6C62D3D7940528C578B10813D0
  // For protection type 2 : 9C3B6C6C62D3D7940528C578B1081390
  // For protection type 3 : JTAG/SWD disabled
  0x9C,0x3B,0x6C,0x6C,0x62,0xD3,0xD7,0x94,0x05,0x28,0xC5,0x78,0xB1,0x08,0x13,0x10
};
const unsigned char Monitor_pass[Monitor_pass_SIZE] = "SjPrCuheJ0J1DvJv";
const unsigned char Engnr_menu_pass[Engnr_menu_pass_SIZE] = "0lcowCKCJwMtCVpo";
const unsigned char WIFI_pass[WIFI_pass_SIZE] = "vwb4DlQfns5erFK8";

Это же самый файл используется при компиляции загрузчика. Таким образом и загрузчик и упаковщик прошивок обладают одинаковыми ключами. Если файла ключей ещё нет, то в этой же утилите его можно сгенерировать. В файле хранятся ключи и дополнительные артефакты для алгоритма AES 256 GCM, ключи RSA 2048, пароль для доступа к отладочному интерфейсу JTAG/SWD, и другие пароли необходимые для IoT. Пароли генерируются через API OpenSSL.
Все ключи и пароли в одном месте значительно облегчают работу программиста.

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

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

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

Уровни защиты Flash памяти от считывания в Synergy

В микроконтроллерах Synergy можно выставить 3 уровня защиты. Они определяются содержимым 16-байтной области ID по адресу 0x40120050 (OCD/Serial Programmer ID Setting Register или OSIS). Ниже приведена цитата из даташита с обозначением номеров уровней принятых в загрузчике.

Уровни защиты из даташита
Уровни защиты из даташита

Загрузчик после старта сразу выставляет для Flash уровень защиты 1. При этом череp SWD доступ к отладке открыт, но при подключении через SWD адаптер надо будет ввести пароль. Отформатированные нужным образом пароли можно найти в файле ключей. Для разный уровней пароли отличаются в одном бите. Пароль остаётся действующим до следующего включения питания.

Но даже на 3-ем уровне защиты из терминала загрузчика можно вернуться на уровень без защиты. Однако для этого надо будет ввести пароль Engnr_menu_pass.

Если загружается прошивка, то она поменяет уровень защиты на тот, который установлен в её мета-данных. Если уровень защиты равен 3, то кроме смены уровня защиты будет заменены пароли терминала, FTP сервера и Wi-Fi на пароли из файла ключей.

Характеристики загрузчика

Загрузчик представляет собой полнофункциональную программу с операционной системой Azure RTOS, файловой системой exFAT, TCP (NetX Duo) и USB стеками, с криптографической библиотекой на аппаратном ускорителе SCE7 Synergy и драйвером Wi-Fi от Infineon. Поэтому размер загрузчика достаточно большой - 781 Кбайт. При этом прошивки Wi-Fi модуля хранятся на SD карте. Если прошивок Wi-Fi модуля нет, то не включается Wi-Fi.
В полной версии модуля для хранения прошивок Wi-Fi устанавливается чип SPI Flash.

SD карта также используется для промежуточного хранения файла прошивки после скачивания из интернета.

Сам загрузчик программируется в микроконтроллер череp SWD и занимает первый сектор Flash размером в 1 Мбайт. От границы 0х100000 начинается область куда записываются рабочие приложения. В вариантах чипа S7G2 это могут быть области размером 2 и 3 Мбайт.

Загрузчик не использует внешнюю SDRAM для своей работы. Все происходит внутри чипа. Загрузчик имеет множество настроек. Настройки хранятся в EEPROM чипа. Настройками можно управлять через терминал или по MQTT. Настройки автоматически импортируются модулем из JSON файла настроек, если таковой будет на SD карте.

Порядок действий при создании, загрузке и передаче управления прошивкам.

  • Сначала создаётся файл ключей с помощью утилиты Firmware Packer

  • С файлом ключей компилируется начальный загрузчик

  • Загрузчик программируется в модуль

  • С помощью утилиты Firmware Packer упаковывается программный код приложения из HEX файла. По ходу выбирается тип защиты Flash, компрессия, тип хэша и шифрование.

  • Файл прошивки передаётся на модуль. Путь передачи зависит от настроек загрузчика в модуле. Загрузчик может работать как Mass Storage, как RNDIS сетевой адаптер, как переходник USB ECM-Ethernet, как WiFi точка доступа или как Wi-Fi станция. Протоколы верхнего уровня сетевого подключения для транспорта прошивки - FTP, MQTT, HTTP

  • Прошивка сохраняется на SD карте модуля

  • Выполняется команда сброса модуля через терминал или через MQTT или просто сбросом питания.

  • Вновь запускается загрузчик, инициализирует тактирование ядра и периферии, запускает RTOS и файловую систему и ищет на SD карте файл прошивки. Если находит, то проверяет её на целостность, расшифровывает и распаковывает, после чего программирует во Flash и отдаёт ей управление.

  • Если прошивки на карте нет или SD карты нет, то загрузчик проверяет текущее содержимое области пользовательской Flash на соответствие контрольной сумме в конце области и если все правильно, то передаёт управление пользовательскому коду.

  • Пользовательский код чтобы не выполнять сложные и многочисленные операции реинициализации периферии чипа записывает в неочищаемую low power RAM условный код и делает сброс чипа.

  • Загрузчик вновь получив управление обнаруживает в low power RAM условный код готового к запуску приложения и сразу отдаёт управление пользовательскому приложению не трогая никакую периферию. Так получаем чистый запуск приложения с гарантировано установленной в дефолтные состояния периферией.

  • Загрузчик может проигнорировать передачу управления коду пользователя если в момент рестарта держать нажатой кнопку BT2. Можно и в настройках установить отказ от передачи управления.

Результаты тестирования скорости загрузки прошивки

Иногда важно насколько быстро прошивка может быть обработана микроконтроллером.
На микроконтроллере S7G2 с частотой ядра 240 МГц длительности загрузки и обработки файла прошивки размером 2 Мбайт с SD карты при разных опциях выглядят так:

В результат не включено время программирования и стирания секторов, поскольку оно константное.

Исходные тексты проекта

Исходные тексты проекта и утилита упаковщик прошивок находятся здесь. Для компиляции используется файл рабочего пространства проекта S7V30_bootloader.eww . Он в формате IDE IAR Embedded Workbench for ARM 9.40.

Непосредственно обработка прошивок производится в файле Loader.c.
К программному коду пользователя предъявляется требование наличия контрольной суммы в конце области кода. В IDE IAR такая контрольная сумма добавляется автоматически при компиляции проекта. По этой контрольной сумме проверяется целостность прошивки во Flash. Но если контрольной суммы в пользовательском коде нет, то её проверку в загрузчике можно выключить исправив эти фрагменты: проверка при флешировании, проверка при старте.
Расшифровка AES GCM в Synergy требует 12 байтного начального вектора IV. Но загружать в API надо 16 байтный буфер. Последние 4 байта должны иметь значения 00 00 00 01. Это важный нюанс, плохо описанный в документации Synergy. При шифровании в OpenSSL надо использовать 12 байтный вектор.

Комментарии (2)


  1. kenny5660
    18.03.2024 14:56

    1. Хранить zip архив с бинарниками упаковщика прям в репозитории это конечно сильно. Исходный код упаковщика я так понимаю закрыт?
      Такие вещи обычно хранят в Release секции гитхаба.

    2. У утилиты упаковщика есть консольный режим? Если бы был, то можно было все манипуляции с прошивками засунуть в CI/CD и совсем исключить человеческий фактор при очередной сборке релиза. А там уже и нужность GUI будет под вопросом...


    1. Indemsys Автор
      18.03.2024 14:56

      Код утилиты закрыт потому что сделан на закрытых компонентах. По другому не получилось.

      Сущности типа Release я в своей практике не применяю. Release как некий флаг так же как CI/CD - артефакты командной работы. А здесь DIY проект, даже исследовательский. Все делается единолично и необходимости в процессах нет.