Espruino Pico: програмирование USB-микроконтроллера на JavaScript

Espruino Pico

Несмотря на огромное количество устройств на базе микроконтроллеров, созданных на волне успеха Arduino, считаные единицы из них имеют форм-фактор обычной флешки, подходящий для непосредственного включения в разъем USB компьютера (USB Type-A). Один из наиболее любопытных их представителей — Espruino Pico.

Вообще говоря, Espruino — это несколько вариантов микроконтроллерных устройств, в которых прошит встроенный интерпретатор JavaScript. Espruino Pico — самое миниатюрное из них. Оригинальный интерпретатор JavaScript, используемый в Espruino, предназначен для быстрой разработки на устройствах с ограниченными процессорными ресурсами. Есть его версии для целого перечня платформ, начиная с ESP8266 и до Raspberry Pi.

Для Espruino существует большое число готовых подгружаемых модулей для самого разнообразного периферийного оборудования, совместимого с экосистемой Arduino (см. раздел модули на сайте Espruino) и инструкций по его подключению (в разделе инструкций и примеров). Есть официальный форум.

История Espruino

Платформа Espruino — не самый молодой проект. Первые публичные упоминания о ней датируются еще 2012-м, и в 2013-м была проведена успешная кампания на Kickstarter по сбору средств на развитие проекта. Ее успех позволил в конце 2014 года провести следующую кампанию, на проект более компактной версии под названием Espruino Pico в форм-факторе размером в половину обычной флешки.

Характеристики Espruino Pico

  • Espruino Pico — самая миниатюрная из плат Espruino, ее размеры 3,3 × 1,5 см.
  • На контакты платы выведено 22 порта ввода-вывода общего назначения, в том числе девять аналоговых входов, 21 с поддержкой ШИМ (PWM), два последовательных порта, три порта SPI, три порта I2C; все GPIO могут работать с 5 В (что важно для совместимости с модулями, разработанными для Arduino).
  • Встроенный разъем USB Type-A позволяет включать устройство непосредственно в USB-порт компьютера без дополнительных кабелей аналогично обычной флешке.
  • Два встроенных светодиода и одна кнопка позволяют реализовать минимальное управление устройством без внешних компонентов.
  • 32-битный процессор ARM Cortex M4 84 МГц — STM32F401CDU6.
  • 384 Кбайт флеш-памяти, 96 Кбайт ОЗУ.
  • Встроенный регулятор напряжения 3,3 В 250 мА, работающий в диапазоне от 3,5 до 16 В, позволяет подключать внешний аккумулятор без дополнительных компонентов.
  • Потребляемый ток в режиме сна: < 0,05 мА — более двух с половиной лет от батареи 2500 мА ∙ ч.
  • Встроенный полевой транзистор для управления цепями с высоким рабочим током.

Espruino Pico

Дисклеймер! Все примеры кода предназначены исключительно для образовательных целей. Данное решение обладает пониженным уровнем безопасности, секретный ключ хранится на устройстве в открытом виде. Не используй этот код в критически важных системах. Применяй для авторизации промышленно выпускаемые устройства.

Быстрый старт

Для Windows тебе, скорее всего, понадобится установить драйвер виртуального COM-порта.

Под Linux нужно будет сделать следующее: копируем файл 45-espruino.rules в /etc/udev/rules.d, перегружаем правила командой sudo udevadm control —reload-rules и проверяем командой groups, что текущий пользователь входит в группу plugdev. Если это не так, исправляем командой sudo adduser $USER plugdev.

На Mac никаких дополнительных манипуляций потребоваться не должно.

Устанавливаем среду Espruino Web IDE из Crome Web Store.

Espruino Pico

Рабочая область Espruino Web IDE разделена на две части. В левой расположено окно консоли, в правой — редактор. Нажав на символ </>, редактор можно переключить в графический режим, основанный на среде Blockly, аналогичной Scratch, что может подойти начинающим.

Espruino Pico

Для подключения к плате необходимо нажать на желтую иконку с разъемом в верхнем левом углу окна. Будет выведен запрос на выбор порта.

Espruino Pico

При успешном подключении будет выведено приглашение консоли Espruino:

Проверим работоспособность вводом 1+2:

Обновление прошивки

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

Загрузка первой программы в Espruino

Следующий шаг стандартный — «помигать светодиодом». Для этого в правой части среды разработки уже есть готовый код:

Единственное, что можно прокомментировать в этих строках, — это использование встроенного объекта LED1, представляющего собой экземпляр специального класса Pin, который предназначен для управления портами ввода-вывода. В данном случае метод write используется для задания уровня на выходе порта (логические ноль/единица), к которому подключен красный светодиод, установленный на плате.

Нажмем на иконку «Send to Espruino».

Espruino Pico

Через мгновение в консоль будет выведен лого и замигает красный светодиод:

Для отключения функции setInterval()можно ввести в консоли clearInterval().

LED, LED1 и B2 (номер контакта микроконтроллера, к которому подключен светодиод) соответствуют красному светодиоду, LED2 и B12(он подключен к контакту B12) — зеленому. Адресовать светодиоды можно и с помощью номеров контактов, к которым они подключены: B2 эквивалентно LED и LED1, B12 — LED2.

Можно воспользоваться и хорошо знакомой ардуинщикам функцией digitalWrite():

Есть возможность изменить состояние на заданный период функцией digitalPulse():

digitalPulse(LED1, 1, 50);

Или задать целую последовательность:

Порты, к которым подключены светодиоды, смонтированные на плате, не поддерживают режим широтно-импульсной модуляции (ШИМ), или Pulse-Width Modulation (PWM), и попытка изменить их яркость, записав что-то в эти порты функцией analogWrite(), приведет к ошибке. Но управление яркостью светодиодов все-таки возможно с помощью встроенного режима программной эмуляции ШИМ:

analogWrite(B2, 0.1, {soft:true});

Более того, можно одновременно с этим задать периодическое включение и отключение:

Отработка нажатий на встроенную кнопку

Прочитать состояние кнопки позволяет функция digitalRead():

Мониторить состояние кнопки можно, периодически считывая ее состояние с помощью setInterval(), однако более корректным будет использование функции watch():

Режим HID, эмуляция клавиатуры

Espruino поддерживает работу через USB в режиме HID (Human Interface Device), что позволяет эмулировать клавиатуру, мышь или планшет (подробнее — здесь).

Для эмуляции USB-клавиатуры необходимо подключить внешний модуль USBKeyboard. Введем в правой части окна в текстовом редакторе:

Espruino Web IDE определит, что используется внешний модуль, и произведет его поиск в подкаталоге modules каталога проекта (его предварительно необходимо задать в настройках) и в каталоге https://www.espruino.com/modules/ на сайте Espruino, минифицирует его (в соответствии с настройками), затем вместе с текстом программы загрузит в Espruino. Можно указать не только имя модуля, можно указать URL файла с его исходным текстом, в этом случае модуль будет загружен с указанного ресурса.

Запись программы во Flash-память

Для того чтобы режим HID начал работать, плату нужно отключить и подключить снова. Чтобы программа не стерлась при отключении питания, ее нужно сохранить во внутренней Flash-памяти. Для этого предназначена функция save():

Запуск Espruino без загрузки пользовательской программы из внутренней Flash-памяти

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

Объект E

Объект E объединяет различные вспомогательные функции, специфичные для Espruino. В числе его методов — обработка сигналов (реализация на C быстрого преобразования Фурье и свертки), интерполяция значений для одномерных и двумерных массивов), низкоуровневые функции для работы с флеш-памятью, сторожевым таймером (watchdog timer), позволяющим автоматически перезагрузить устройство при зависании, управление частотой процессора и другие интересные штуки.

Например, можно считать значение термистора, встроенного в STM32:

Мультифакторная авторизация

Мультифакторная авторизация (multi-factor authorization, MFA) заслуженно считается эффективным способом (конечно, со своими ограничениями) повышения безопасности доступа к ресурсам.

Алгоритм TOTP

Один из наиболее распространенных методов такой авторизации — одноразовые пароли (one-time passwords) и в частности TOTP (time-base one time passwords), одноразовые пароли, основанные на времени. Их поддержка реализована на большом количестве сайтов. Со стороны клиента их реализация представлена такими популярными приложениями, как Google Authenticator и Authy.

TOTP — это частный случай алгоритма HOTP (HMAC-based one-time password), в котором в качестве счетчика, используемого для генерации хеша, берется порядковый номер временного интервала, начиная с некоторого момента.

HMAC — hash-based message authentication code (код аутентификации, подтверждающий сообщения на основе хеш-функции). Алгоритм HMAC позволяет проверить целостность и подлинность некоторого сообщения с использованием хеш-функции, секретного ключа и самого сообщения. С их помощью генерируется код аутентификации, который может проверить вторая сторона, выполнив такой же алгоритм. В случае HOTP в качестве сообщения берется некий общий для обеих сторон синхронный счетчик.

Алгоритм HOTP описан в RFC 4226:

  • вычисляется функция HMAC от значений секретного ключа и счетчика с использованием функции хеширования SHA-1;
  • для полученной 20-байтной строки выполняется «динамическое отсечение», для этого берутся ее младшие четыре бита и их значение считается смещением, по которому из этой же строки выделяются четыре
  • байта; старший бит полученного значения отбрасывается;
  • полученные 31 бит переводятся в десятичный вид, и от них берется нужное количество цифр результирующего пароля, начиная с наименее значимых.

Генерация TOTP-паролей описана в RFC 6238. В качестве общего счетчика в нем используется количество интервалов, начиная с некоторого начального момента, общего для обоих устройств. Как правило, начальным моментом считается начало отсчета времени в Unix-системах, а в качестве интервала принимаются 30-секундные отрезки времени.

Для работы этого алгоритма необходимо, чтобы часы используемых устройств были синхронизированы. Хотя у Espruino Pico есть свои встроенные часы реального времени, они не энергонезависимы, и необходимо либо организовать питание от аккумулятора, либо подключить внешний модуль часов реального времени со встроенным аккумулятором.

Для синхронизации времени с компьютером, на котором ведется разработка, в настройках Espruino Web IDE необходимо включить следующий параметр: Settings → Communications → Set Current Time. Теперь при загрузке кода программы время устройства будет синхронизироваться с основным компьютером.

Секретный ключ на сайтах приводится обычно в виде строки в кодировке Base32 (дополнительно для мобильных устройств может выводиться QR-код, чтобы считать его с помощью встроенной камеры).

Кодировка Base32

Существует несколько вариантов кодировки Base32. В данном случае используется вариант, описанный в стандарте RFC 4648. При этом исходная последовательность байтов рассматривается как непрерывная последовательность битов. Эта битовая последовательность разбивается на блоки по пять бит, и каждому такому блоку ставится в соответствие один из 32 базовых символов. Символы (соответствующие блокам по пять бит) объединяются в группы по пять (40 бит). Если общая длина последовательности не кратна пяти символам (40 битам исходной последовательности), она может дополняться символами =.

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

Так как мы реализуем только токен, нам необходима лишь функция декодирования строки Base32 (сформированной сервером).

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

Загрузим в Espruino и проверим:

Вычисление HMAC SHA-1 с помощью библиотеки jsSHA

Espruino не содержит встроенной функции для вычисления HMAC SHA-1. Для вычисления OTP можно воспользоваться библиотекой jsSHA, взяв из нее только тот модуль, в котором реализована именно нужная хеш-функция. Функции require() можно напрямую указать URL с кодом необходимого модуля прямо с GitHub.

Добавим в начале файла с исходным кодом:

Затем объявим новый класс TOTP:

И создадим объект этого класса:

Добавим внутри класса TOTP метод вычисления значения счетчика на основе текущего времени.

Добавим метод вычисления HMAC для SHA-1 с использованием библиотеки jsSHA:

Метод динамического отсечения:

И главный метод вычисления одноразового пароля, собирающий остальные:

Основная точка входа с индикацией и логированием:

Подключим модуль клавиатуры в начале исходного текста программы:

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

Первая версия токена готова.

Проверка

Проверить работоспособность можно, вызвав метод напрямую:

Или нажатием на кнопку устройства, поместив указатель мыши в каком-либо текстовом поле.

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

Перед тем как пробовать код из примеров на каком-либо реальном сайте:

продублируй код, сохранив его с помощью проверенной программы генерации одноразовых паролей, такой как Google Authenticator или Authy;

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

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

Встроенные средства для вычисления HMAC и SHA-1

Генерация пароля устройством занимает более секунды, это не очень комфортно. Библиотека jsSHA реализована на чистом JavaScript, и, хотя время, затрачиваемое на вычисление HMAC SHA-1, практически незаметно на обычном компьютере, на Espruino длительность вычислений становится критичной.

У Espruino есть собственные низкоуровневые нативные реализации вычисления хеш-функций в библиотеках crypto и haslib. Есть и собственная библиотека hmac (хотя она написана на JavaScript, в этом алгоритме сложных вычислений нет и на производительность это не повлияет, в отличие от SHA-1).

Однако использовать напрямую их не получится — в библиотеке haslib нет реализации алгоритма SHA-1, а готовая реализация crypto.SHA1 несовместима с библиотекой hmac. Поэтому для работы со встроенными функциями придется написать «обертку» вокруг функции crypto.SHA1 в класс, совместимый с классом HASH, который используется библиотеками hmac и haslib (исходный код модуля hmac можно посмотреть в каталоге /modules/ непосредственно на сайте espruino.com/, откуда и подгружаются модули функцией require() в Espruino Web IDE). У этого класса всего три метода, причем один из них библиотекой не используется. Еще один момент — объекты типа HASH создаются без ключевого слова new, вызовом конструктора напрямую, поэтому ключевое слово this мы не применяем, создавая новый объект (в переменной self).

Дополнительно понадобится вспомогательная функция, преобразующая объект типа ArrayBuffer в обычную строку, код каждого символа которой равен значению соответствующего байта исходного объекта:

И еще пара функций для конверсии различных представлений данных:

Теперь готовы все «кирпичики» для того, чтобы написать новый метод вычисления HMAC SHA-1 класса TOTP:

И внутри метода generateHotp() заменим вызов функции this.hmacSha1() на this.hmacSha1_new():

Длительность вычислений при этом существенно изменится в лучшую сторону, снизившись до 0,17 с, и перестанет вызывать дискомфорт.

Возможные проблемы

Если плата работает только с подключенным USB, а без него зависает, наиболее вероятная причина — блокировка вывода на консоль, которая по умолчанию настроена на USB и блокирует работу, когда он недоступен. Для устранения проблемы нужно либо убрать все console.log, print(), либо переключить консоль на последовательный порт (только так, чтобы оставить возможность возвращения обратно на USB).

Подробнее об устранении этих и других проблем — на странице Troubleshooting сайта Espruino.

Заключение

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

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

Интересным развитием концепции одноразовых паролей стал стандарт FIDO U2F, поддерживаемый Yubico и Google. Авторизация происходит без ввода кода с клавиатуры, достаточно только нажатия на кнопку токена, поддерживающего этот стандарт. При этом токен выступает в роли HID-устройства, с которым браузер взаимодействует напрямую для обмена ключами с сайтом. К сожалению, ресурсов Espruino Pico недостаточно для его поддержки, по крайней мере с использованием реализаций шифрования на JavaScript.

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

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