Репозиторий проекта: https://github.com/karen07/rp2040-totp-hid
Мне всегда было лень доставать телефон ради TOTP-кода, а вручную вводить длинные сложные пароли — удовольствие тоже сомнительное. С паролями эту проблему часто решают плохо: делают пароль попроще и покороче, чтобы его было удобно набирать. Но с TOTP так не получится — код живёт отдельно, его всё равно нужно открыть, посмотреть и перепечатать.
Так как небольшой опыт в программировании микроконтроллеров у меня уже был (Послушный YubiKey), я решил сделать маленький «вписыватель паролей»: USB-устройство, которое подключается к компьютеру, притворяется обычной клавиатурой и само вводит TOTP-код или сохранённый пароль.
Чтобы вводить пароль и TOTP так, будто их набирает пользователь, устройство должно уметь работать как USB HID-клавиатура. Для этого подходит не любой микроконтроллер: нужна поддержка USB device и HID. Мой выбор пал на RP2040-Zero — его легко достать, он недорогой, компактный, хорошо документирован, и по нему есть много туториалов.
Что купить и как спаять
Необходимо купить сам RP2040-Zero, но для работы TOTP требуется Unix time время с точность хотя бы 5 секунд.
Для этого есть внешние платы, самым подходящим мне по форм фактору оказался DS3231.
Общение между RP2040-Zero и DS3231 идет через I2C.
У DS3231 можно отпаять черную колодку, и соединить RP2040-Zero и DS3231 через два пина, тем самым мы получим компактную 3D структуру и общую жесткость конструкции. Но при отпайке будьте аккуратные с батарейкой у DS3231, она может перегреться. Лучше вообще перед всеми работами её отпаять батарейку, и уже припаять в самом конце.
Получается чип к чипу, рисков по нагреву и короткому замыканию нет.
Остается проводами подключить +3.3 и минус.
Прошивка для RP2040-Zero
Прошивка написана на C под Raspberry Pi Pico SDK и собирается через CMake. Для USB я использую TinyUSB: устройство объявляется как HID с двумя report ID. Первый report — обычная USB-клавиатура, через него я «печатаю» TOTP-код или сохранённый пароль. Второй report — служебный vendor-defined HID-канал для настройки через WebHID.
Когда WebHID-страница отправляет команду, она попадает в callback tud_hid_set_report_cb(). Первый байт пакета — это код команды, остальные байты — данные. Так я устанавливаю время в DS3231, записываю TOTP-секрет, записываю пароль, очищаю данные или возвращаю статус устройства.
Время хранится не во flash, а в RTC DS3231. При настройке браузер отправляет Unix time, а прошивка переводит его в обычные поля даты и времени: год, месяц, день, часы, минуты и секунды. Потом эти значения записываются в DS3231 в BCD-формате. При генерации TOTP происходит обратное: прошивка читает из DS3231 секунды, минуты, часы, дату, месяц и год, переводит BCD в обычные числа и собирает из них Unix time в UTC.
Сам TOTP считается стандартным способом: Base32-секрет декодируется в бинарный ключ, текущее Unix time делится на шаг 30 секунд, получившийся счётчик кодируется в 8 байт и подписывается через HMAC-SHA1. Потом из результата берётся динамическое усечение HOTP, число приводится к 6 цифрам через % 1000000, форматируется с ведущими нулями и отправляется в компьютер как нажатия клавиш USB-клавиатуры.
Как собрать прошивку
Подробно процесс сборки расписывать не буду, он стандартный для проекта на Pico SDK и CMake. Достаточно склонировать репозиторий с submodules и собрать release-вариант:
git clone --recurse-submodules https://github.com/karen07/rp2040-totp-hid.gitcd rp2040-totp-hidcmake --preset releasecmake --build --preset release
После сборки UF2-файл появится здесь:
build/release/rp2040_totp_hid.uf2
Дальше всё как обычно для RP2040: зажимаем BOOTSEL, подключаем плату по USB, отпускаем BOOTSEL и копируем rp2040_totp_hid.uf2 на появившийся диск RPI-RP2. После копирования плата перезагрузится уже с новой прошивкой.
Web GUI для настройки
Чтобы не писать отдельную desktop-утилиту, я сделал простую WebHID-панель. Она открывается прямо в браузере и позволяет настроить ключ через USB: записать TOTP-секрет, сохранить пароль, выставить время в RTC DS3231 и посмотреть текущее состояние устройства.
Работает это через WebHID, поэтому нужен браузер с его поддержкой, например Chrome или Edge. Пользователь открывает HTML-страницу, нажимает кнопку подключения, выбирает устройство в системном диалоге, и после этого страница может отправлять служебные HID-команды в прошивку.
Самое удобное — установка времени. Web GUI берёт текущее время с компьютера, отправляет его в устройство как Unix time, а прошивка уже переводит его в формат DS3231 и записывает в RTC. После этого устройство может считать TOTP автономно, без телефона и без связи с браузером.
Через эту же страницу можно записать Base32-секрет для TOTP и сохранённый пароль. После настройки браузер больше не нужен: ключ работает как обычная USB-клавиатура и по нажатию кнопки сам вводит TOTP-код или пароль.
Использование и безопасность
В итоге пользоваться устройством очень просто: одно нажатие BOOTSEL вводит текущий TOTP-код, двойное нажатие вводит сохранённый пароль. В обоих случаях для компьютера это выглядит как обычный ввод с USB-клавиатуры, поэтому важно заранее поставить курсор в нужное поле.
Конечно, я понимаю, что с точки зрения безопасности это не идеальное решение. Пароль и TOTP-секрет хранятся на самом устройстве, и если кто-то получит к нему физический доступ, он потенциально сможет их скопировать. Я отношусь к этому как к обычному физическому ключу: если потерять ключ от машины или дать его украсть, машину тоже можно угнать.
Поэтому такое устройство нужно хранить соответственно: не оставлять без присмотра, не подключать к чужим компьютерам и иметь возможность быстро отозвать пароль и TOTP-секрет. Для меня это в первую очередь DIY-проект и удобный «вписыватель» кодов и паролей, а не замена полноценному аппаратному security token.
ссылка на оригинал статьи https://habr.com/ru/articles/1055506/