Как создать защищенное зашифрованное устройство

Как создать защищенное зашифрованное устройство

Мы не раз рассказывали об уязвимостях самых разных устройств, а вот о том, как создать защищенное устройство самому, мы никогда не говорили. Настало время об этом поговорить. В сегодняшней статье мы создадим свой девайс, запустив шифрованное соединение TLS на микроконтроллере STM32.

Железо

Нам понадобятся официальная оценочная плата Discovery компании STMicroelectronics и модуль WiFi на микроконтроллере ESP8266. Последних существует большое количество, подойдет практически любой. Главное, чтобы там имелись выводы RX/TX UART и прошивка по умолчанию для работы с АТ-командами. Я использую отечественный Troyka (модуль компании «Амперка») просто потому, что он у меня уже есть.

Из этого мы будем делать защищенное зашифрованное устройство
Из этого мы будем делать защищенное зашифрованное устройство

F746G Discovery — это практически Ferrari в мире любительской электроники. Мощный микроконтроллер (Cortex-M7, 216 МГц) тут сочетается с микросхемой SDRAM внушительных объемов (целых 8 Мбайт) и полезной периферией: экран 480 х 272 с 24-битным цветом, интерфейс SDIO для карт памяти, разъемы аудио и Ethernet.

Я не стану перечислять все доступные возможности данной платы — вы можете найти подробности на сайте производителя STMicroelectronics. Все то, что раньше приходилось добавлять к Arduino с помощью дочерних плат расширения или модулей, здесь доступно «из коробки». Да, цена соответствующая — порядка 6500 рублей. Но когда это спортивные автомобили были дешевыми?

F746G Discovery — не единственный вариант демоплаты для этого проекта. Вы можете с таким же успехом взять, например, отладочную плату из серии Nucleo: F767ZI Nucleo. Для нее тоже есть поддержка в пакете STM32duino, и она намного дешевле — примерно 3000 рублей. Такого разнообразия периферии у нее, правда, нет.

Полноценно нагрузить все компоненты и использовать возможности F746 Discovery на сто процентов в одном проекте не легко. Сегодня с помощью библиотеки bearSSL мы протестируем только вычислительную мощь ядра Cortex-M7 в прикладных задачах криптографии.

Программирование под STM32 предполагает внимательное изучение сопроводительной документации. В этой статье я постараюсь осветить все подробности и интересности, которые встретятся нам на пути, но в дальнейшем все же настоятельно рекомендую обратиться к следующим материалам (все ссылки — на PDF):

  • UM1907 — вся информация о плате Discovery: компоненты, схемы, назначение разъемов и прочее.
  • DS10916 — даташит на микроконтроллер. Полезен распиновкой микросхемы F746NG в различных корпусах.
  • RM0385 — говорят, на тех, кто осилит прочитать все 1700+ страниц мана, снисходит божественное просветление.

Настройка IDE

В качестве среды разработки я выбрал Arduino IDE. Проект открытый, и вокруг него успело сложиться большое сообщество, так что это хороший выбор как для новичка, так и для продвинутого гуру. Кроме того, этот выбор позволит нам воспользоваться уже готовой кодовой базой. Так мы пропустим тривиальную настройку периферии и сразу же перейдем к вещам более интересным.

Изначально в Arduino IDE доступны только платы на AVR-микроконтроллерах, ни о каком ARM она и не слышала. Чтобы включить поддержку плат STM32duino, необходимо перейти в раздел Arduino —> Preferences и добавить дополнительную ссылку. После этого в «Менеджере плат» вы сможете скачать все необходимые файлы пакета.

Во время установки опция с STM32duino может и не появиться в списке доступных для установки расширений. В этом случае проверьте разрешения для Arduino IDE на работу по сети или добавьте в исключения антивируса.

Поиск пропавшего Serial

Если вы еще не проверили корректность работы компилятора и самой платы на парочке простых примеров из числа встроенных, то сейчас самое время. Потому что мы переходим к правке исходных файлов по адресу «Библиотеки —> Arduino15 —> packages —> STM32 —> hardware —> stm32» (советую сделать копию папки, вы знаете, как оно бывает). Все дело в том, что проект STM32duino поддерживается энтузиастами (хотя сейчас им заинтересовалась сама STMicroelectronics) и, как следствие, часть функций Arduino реализована, а часть почему-то оказалась забыта.

Например, по умолчанию отсутствует последовательный порт (Serial) на выводах D0 и D1 разъема Arduino. Если вы знакомы с оригинальными итальянскими платами (и их многочисленными китайскими клонами), то знаете, что этот порт связан с разъемом USB через UART — USB-преобразователь в основном используется для общения с компьютером и при перепрошивке флеш-памяти. На Discovery все немного по-другому.

Микроконтроллер F746NGH6 прошивается с помощью встроенного программатора ST-Link/V2-1 (небольшая микросхема возле miniUSB-порта). Он же выступает в качестве преобразователя интерфейсов при отправке данных на компьютер. Но все это делается по другому UART — вовсе не тому, который выведен на плате на разъем Arduino (да, у контроллеров ST богатая периферия, об этом следовало сказать заранее).

РЕКОМЕНДУЕМ:
Безопасность UEFI

Если в прошлом вы использовали плату Arduino Leonardo, то можете предположить, что для этого интерфейса используется объект Serial1. Но нет! В проекте STM32duino нумерация программных портов Serial соответствует номеру аппаратного периферийного блока UART, и поэтому в любом коде для нашей Discovery Serial и Serial1 — это один и тот же объект (используется переопределение).

Как тогда обращаться к UART на разъеме Arduino? Изучив таблицу 4 на странице 23 документации демоплаты, вы узнаете, что это RX/TX-выводы UART6.

Впрочем, одного лишь этого знания будет недостаточно: компилятор сообщит, что Serial6 не определен. Чтобы добавить порт UART для работы с ESP8266, требуется отредактировать конфигурационные файлы самой платы — они располагаются по адресу stm32 → 1.4.0 → variants > DISCO_746NG. В самих файлах нет ни намека на возможное решение проблемы, но тщательное изучение исходников проекта STM32duino (папка cores на уровне с variants) должно навести на определенные идеи. На самом деле нам достаточно добавить несколько строчек в файл variants.h:

Почему их нельзя было оставить в файле закомментированными, чтобы пользователи не тратили время на знакомство с нюансами реализации Arduino-окружения на платах от ST, — это для меня остается загадкой. Однако теперь объект Serial6 станет доступен в нашем коде, и мы уже сможем наладить взаимодействие с модулем WiFi.

Следующие шаги опциональные, однако, если вы хотите получить максимум быстродействия от связки F746 и ESP8266, советую их не пропускать. Тем более что после них с настройкой окружения будет наконец покончено.

Дополнительно

Нам нужно настроить размеры внутренних буферов класса HardwareSerial на прием и передачу информации по интерфейсу UART. По умолчанию размеры подобраны для плат Arduino с микроконтроллерами AVR, и это может стать проблемой при передаче больших массивов данных (как раз наш случай). В папке cores → arduino открой файл HardwareSerial.h и найди в нем такие строчки:

Это размер буферов в байтах. Исправь 64 на 512, разрядность переменной-индекса при этом будет скорректирована автоматически. Вообще говоря, размеры буферов не обязаны совпадать, и, если в вашем проекте памяти у микроконтроллера остается впритык, стоит увеличивать только буфер RX. Также обратите внимание, что эти изменения затронут все ваши скетчи для плат STM32duino (но не для Arduino AVR или ARM).

Теперь осталось только ускорить UART. По умолчанию все модули ESP8266 со стандартной прошивкой для работы с АТ-командами настроены на скорость 115 200 бод. Если вы используете программный порт SoftwareSerial, скорость рекомендуют уменьшить до 9600. В нашем случае, напротив, разумней проводить прием и передачу быстрее, контроллер справится, а время терять незачем. Подключите модуль WiFi к плате и залейте следующий скетч:

С помощью «Монитора порта» в Arduino IDE вы сможете изменить настройки UART на ESP8266, используя плату Discovery в качестве ретранслятора (если у вас есть отдельный преобразователь USB — UART, то все еще проще). Убедитесь, что конец строки в терминале — это NL & CR. Наберите АТ-команду AT+UART_DEF=1000000,8,1,0,0. Это установит скорость работы в 1 Мбод, длину 8 бит и один стартовый бит вначале. Все, пора переходить к главному!

AT-команды

Порой бывает удобно проверить состояние модуля WiFi через «Монитор порта» простыми AT-командами, не используя библиотечные функции. Вот самые полезные:

  • AT — базовая команда, если модуль не отвечает на нее, стоит проверить подключение или питание.
  • AT+CWLAP — показывает список доступных точек доступа.
  • AT+CIPSTATUS — состояние текущего подключения.

Остальные команды вы найдете в документации (PDF).

SSL/TLS — самое необходимое

В этой статье я не буду подробно останавливаться на протоколах SSL/TLS по трем причинам.

  1. Вполне вероятно, вы знаете их лучше меня.
  2. Они доступно описаны в документах RFC (RFC5246 и RFC6101 для TLS 1.2 и SSL 3.0 соответственно).
  3. Порог вхождения там не велик, и вас вряд ли ждут большие затруднения.

По сути, нужно знать следующее: код TLS работает непосредственно между приложением и уже готовым TCP-соединением, обеспечивая приватность и конфиденциальность передачи информации между сервером и клиентом (ну и еще, как плюс, успокаивающе-зеленый замочек в браузере).

В нашем примере мы попробуем получить у какого-нибудь сервера в интернете страничку по HTTPS с нашей демоплаты на STM32. Случай с простым HTTP-запросом особого интереса не представляет и хорошо освещен в интернете. TCP у нас уже есть (благодаря ESP8266 и рабочему Serial6), какого-то особо сложного приложения пока не предвидится, так что вся задача сводится к обеспечению корректной работы библиотеки. Осталось выбрать, какой именно.

Зоопарк диких зверей

Если вы до этого когда-нибудь интересовались доступными вариантами библиотек SSL/TLS, то наверное слышали о проекте OpenSSL. Это весьма распространенное решение, но для встраиваемых систем, с их относительно скромными ресурсами, желательно найти библиотеку с хорошей портативностью и небольшим объемом итогового кода.

Зачастую для коммерческого использования разработчики выбирают wolfSSL — она предназначена специально для мобильных устройств. Это библиотека с открытым исходным кодом, лицензированным по GPL (есть и коммерческая лицензия), собственным сайтом, форумом и даже некоторой поддержкой со стороны авторов.

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

РЕКОМЕНДУЕМ:
Как написать безопасный код на JavaScript

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

Может показаться, что документации меньше в сравнении c wolfSSL, но при этом ее достаточно, и она не отвлекает от главного. Исходный код также открыт, а условия лицензии даже мягче — это MIT, что в некоторых случаях может быть дополнительным аргументом.

И хотя автор говорит о статусе бета-версии, библиотека полноценно поддерживает стандарт TLS 1.2 (wolfSSL уже доступна с TLS 1.3, но официально RFC8446 был принят только в августе 2018-го).

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

Ни автор материала, ни редакция сайта tech-geek.ru, ни тем более автор библиотеки никакой ответственности за возможный причиненный ущерб, упущенную прибыль и прочие неприятности не несут. Код предоставляется «как есть», без каких-либо гарантий, включая гарантию соответствия какому-либо назначению (но не ограничиваясь ей).

Приручение библиотеки

Для работы bearSSL требуется платформозависимая реализация трех вещей: текущего времени, пула случайных чисел и базовых функций ввода-вывода. Их придется написать самостоятельно, но это не займет много времени. Наш код будет взаимодействовать с периферией платы и микроконтроллера, поэтому тут как нельзя кстати пригодятся ссылки, которые я давал в начале статьи.

Дата и время

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

Библиотека bearSSL зависит от стандартной функции _gettimeofday(). Если вы попытаетесь скомпилировать проект сейчас, то компоновщик сообщит, что этот символ не определен. Оно и понятно: сразу после подачи питания на микроконтроллер нет какого-то общего способа узнать текущее системное время (тем более в отсутствие ОС). Но у нас есть модуль WiFi, поэтому предлагаю воспользоваться им и сделать запрос к серверу точного времени.

 

К слову, вы можете задавать время либо в процессе компиляции (даже приблизительно), либо воспользовавшись модулем RTC. На результатах проверки срока действия сертификата это сказаться не должно.

Аппаратный ГСЧ

Традиционный подход к генерации случайных чисел в среде Arduino выглядит примерно так:

Такой программный метод имеет право на жизнь в простых ситуациях, но для криптографического применения он решительно не подходит. К счастью, у микроконтроллера F746NG богатая периферия и есть ГСЧ (TRNG, True Random Number Generator). Им мы и воспользуемся, предварительно заглянув в документацию (RM0385, с. 543). Там всего-то три регистра, которые отвечают за взаимодействие с аппаратной частью, — RNG_CR (Control Register), RNG_SR (Status Register) и RNG_DR (Data Register).

Без знакомства с документацией такой код может быть сложен для восприятия, но зато это эффективно и, так как мы работаем максимально близко к железу (для языка С, разумеется), требует минимум машинных инструкций (в отличие от библиотек HAL, Hardware Abstraction Layer). Конечно, настройка остальной периферии занимает чуть больше времени и места, но принцип остается тем же. Так что со временем изучение CMSIS обязательно окупит себя.

Функции ввода-вывода

BearSSL внутри состоит из трех глобальных объектов: движок, контекст клиента (или сервера) и контекст проверки сертификатов. Это платформонезависимые вещи, и для связи с внешним миром им нужны всего две базовые функции на прием и передачу, которые необходимо зарегистрировать в библиотеке для обратного вызова. Это ключевая часть пользовательского кода, поэтому есть смысл потратить время и изучить ее подробнее.

Обратите внимание на размер буфера и его расположение. Протокол SSL/TLS предусматривает длину сообщений до 16 Кбайт. На практике вы вряд ли встретите настолько большие сообщения, поэтому 8 Кбайт выглядят разумным компромиссом. Кроме того, буфер размещен во внутренней SRAM микроконтроллера. Так как буферов у нас будет больше одного, возможно, это не самое удачное место.

В самом начале я говорил о внешней микросхеме SDRAM, которая имеет гораздо больший объем (8 Мбайт против 320 Кбайт) при сравнительно одинаковой скорости и времени доступа. После инициализации FMC и банков памяти это пространство будет доступно для программы, и, скорее всего, это более подходящее место. Однако это увело бы нас чуть в сторону от основной темы статьи.

РЕКОМЕНДУЕМ:
Полнодисковое шифрование с LUKS2

Основное место в коде занимают методы recv() и send() класса ESP8266. Именно для использования готовой библиотеки пришлось искать пропавший Serial6 в исходниках STM32duino. Зато, уже имея на руках отлаженный код, сравнительно просто организовать учет данных, принятых и переданных bearSSL.

Здесь есть нюанс: иногда метод recv() не находит доступного сегмента и возвращает ноль, хотя прием еще не был завершен. К счастью, библиотека bearSSL лучше знает, сколько байтов должно поступить (для этого в заголовке каждого сообщения SSL/TLS есть поле с количеством байтов в теле), поэтому доверяемся ей и не выходим из внешнего цикла while до окончания передачи.

Подключение к API

Теперь, когда все готово, надо протестировать наш код на чем-то интересном. Вообще, потребность подключения и настройки библиотеки bearSSL под STM32 лично у меня возникла, когда я захотел парсить данные с рынка в одной популярной многопользовательской игре и выводить их на дополнительный дисплей. Уверен, у вас уже есть свои мысли, как с пользой применить полученные знания, поэтому буду краток и просто покажу как пример.

как сделать защищенное устройство
Доска объявлений в Final Fantasy X|V

Самый простой способ получить актуальные рыночные цены для Final Fantasy X|V — это посылать запросы на сторонний сервис, который предоставляет API, он называется XIVAPI. Но сам сервер ограничивает число запросов за секунду, и, чтобы вести подсчет, требуется вместе с запросом посылать уникальный ключ клиента или приложения (стандартно). Именно чтобы сохранять ключ в секрете и не позволить третьей стороне использовать сервис от нашего лица, запрос следует посылать по HTTPS-протоколу с шифрованием SSL/TLS.

Если вы запустите этот код (и если Хаканубис услышит ваши молитвы), то в «Мониторе порта» увидите стандартный ответ сервера HTTP и полезную нагрузку — JSON с ценами на сервере Cerberus для предмета с ID 18189 (к слову, это High Steel Longsword).

Распарсить объект и получить любую интересующую информацию теперь легко с помощью библиотеки rapidjson. Я обрабатываю данные сразу на микроконтроллере и вывожу на дисплей с помощью периферийного модуля LTDC. Это и близко не исчерпывает возможности микросхемы F746NG, поэтому лично для меня впереди еще много работы по этому проекту.

как сделать защищенное устройство
Конкурентное преимущество на рынке выглядит как-то так. Интересно, за это банят?

Заключение

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

К сожалению, я не смог в объеме статьи уместить полноценную обработку ошибок или вывод отладочной информации (попробуйте ради любопытства выводить тело сообщений SSL/TLS и сравните их с описанием в RFC). Но возможно, это и к лучшему. У вас есть стимул дополнить и самостоятельно улучшить код, как вам будет нужно.

Кроме того, рекомендую все-таки завести внешнюю SDRAM и расположить буферы ввода-вывода там или подумать об использовании прямого доступа к памяти (DMA). Не то чтобы это было так уж необходимо (по крайней мере, на нашем этапе реализации), но откладывать оптимизацию на самый конец — не самая лучшая идея.

РЕКОМЕНДУЕМ:
Какую информацию собирают голосовые ассистенты

Еще один важный аспект производительности — это время, которое приходится потратить на обработку запроса. Сейчас первое подключение к сервису и рукопожатие занимают от 4 до 6 секунд, последующие сообщения от 2 до 2,5 секунды (когда параметры сессии уже в кеше). Это не так уж плохо, все-таки криптография требует приличного количества ресурсов, да и запрос у нас с реальной полезной нагрузкой, а не просто HTTP/1.0 200 OK.

На самом деле в линейке F7 от STMicroelectronics уже есть микросхемы с аппаратными блоками HASH и CRYP. По тестам, это позволяет сократить время некоторых вычислений от двух до четырех раз! К сожалению, на F746NG эта периферия недоступна, да и bearSSL их пока не поддерживает.

Вместе с дополнительными библиотеками (для JSON и GUI) код занимает всего 186 Кбайт ПЗУ (ROM) и 45 Кбайт ОЗУ (RAM). При этом около 40% уходит на графический интерфейс и шрифты для экрана. Если использовать внешнее хранилище в виде QSPI flash или карты microSD, то расход по памяти можно еще сократить. Напомню, у микроконтроллера целый мегабайт ПЗУ и 320 Кбайт ОЗУ, так что реализовать можно еще много интересного.

В продолжение этого материала я написал статью про поиск энтропии и генерации случайных чисел.

Понравилась статья? Поделиться с друзьями:
Добавить комментарий