
Репозиторий проекта: 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.git cd rp2040-totp-hid cmake --preset release cmake --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.
Комментарии (6)

Void-Cowboy
04.07.2026 13:41интересный проект. По идее можно даже "взросло" сделать. Вынести все ключи в отдельный крипточип специально под это заточеный. Добавить простенький tft дисплей для визуализации что бы можно было без веб-интерфейса переключатся по ключам. Опционально добавить сканер отпечатка пальцев.
Плюс я думаю можно просто "ключи" хранить. Отдельная кнопка что бы с TOTP переключится на текстовые ключи. Причем текстовые можно прямо в памяти хранить, просто криптовать внешним чипом и все будет безопасно максимально.
Как плюс, если взять что то по типу нордика или прочее нормальное беспроводное, то можно парить с телефоном/компом по блутузу и сделать вообще "беспроводным".

kenomimi
04.07.2026 13:41Люди заморачивались этим еще лет 10-12-15 назад, тут на хабре с пяток проектов было уже. Как показала практика, "гражданскому" рынку этот девайс попросту не нужен, так как пароли почти везде заменились смс-кодами. Зайти же к корпоратам или в госы, где оно может и было бы нужно, без больших связей/затрат врядли возможно (получить сертификацию не так-то просто, а без нее это просто игрушка).

kenomimi
04.07.2026 13:41Ношу точно такую же штуку на базе Waveshare RP2350-Touch-LCD-1.28. Очень удобно. Изначально делал вообще ради эксперимента, можно ли сгенерить код для железки с нуля и "под ключ" при помощи claude. Можно, он даже с круглым экраном разобрался и не сломал UI... Причем это не ардуино-код, все на взрослом C++.
А вот с паролями я поступил так - при вводе пин-кода мы его просто запоминаем, и от него и внутреннего ключа генерим пароль. Неправильный пин-код? Получим неправильный пароль, то есть ручной перебор попросту невозможен.
gibson_dev
Если взять 2350 то у него с безопасностью вроде как получше, 2040 просто вычитать можно.