RustKey
A hardware TOTP authenticator device with encrypted secret storage, USB-HID output, and an OLED display.
Author: Mardale Andrei-Bogdan
GitHub Project Link: https://github.com/UPB-PMRust-Students/fils-project-2026-bmardale
Description
RustKey is a hardware TOTP authenticator built on the STM32U545 microcontroller, implemented in async Rust using the Embassy framework. It manages multiple accounts, stores their encrypted secrets on an external EEPROM using XChaCha20-Poly1305, and displays generated codes on an OLED screen. A dedicated button triggers USB HID keyboard emulation, typing the code directly into the host PC. A companion CLI tool handles account provisioning and time synchronisation over UART.
Motivation
The inspiration for this project came from a hardware security key (a YubiKey) sitting on my desk. Having used it regularly for two-factor authentication, I wanted to replicate its functionality as an embedded project. Initially I considered implementing the WebAuthn/FIDO2 component, but that didn't require any external peripherals. I then decided to implement a TOTP authenticator device, which would require multiple external components. An additional goal is to use the device during the development. This allows for better testing of correctness and user experience, while also showing bugs and edge cases that might not be obvious during normal testing.
Architecture
Main components:
- STM32 NUCLEO-U545RE-Q development board, with a STM32U545 microcontroller will act as the central unit of the device. It's used for generating TOTP secrets via hardware-accelerated cryptographic functions (SHA1, HMAC), for driving the peripherals (I2C), UART, USB-HID. Its internal true random number generator (TRNG) is used for XCHaCha20-Poly1305 nonce. The internal flash is used for storing the master key.
- TOTP engine generates the one-time passwords using the selected account secret and the current time. The project will support only SHA1-based, 6-digit TOTP codes, as these are the most widely used in practice.
- Security layer encrypts TOTP secrets before storage and decrypts them only when needed for code generation. The encrypted records are stored externally, while the master key remains in internal flash. Secrets are never stored in plaintext in EEPROM and are never exposed through the host interface.
- Account store uses an external AT24C256 EEPROM over I2C to persist account data. Each record contains the account label, the XChaCha20-Poly1305-encrypted secret, and the corresponding nonce.
- Time service uses the DS3231 RTC module over I2C to keep accurate time across power cycles. Precise timekeeping is essential for correct TOTP generation.
User interface:
- Display UI shows the currently selected account label, the generated TOTP code, and the remaining validity time for the current time window.
- User input layer is implemented using capacitive touch inputs, supporting short and long presses for navigation and interaction.
Host communication layer:
- UART: provisioning is performed through a Rust-based CLI. The CLI can send the current Unix epoch, check for possible time desynchronization, and add new accounts by sending an account label and a Base32-encoded secret.
- USB-HID: sends the currently generated TOTP code to a host computer when triggered by user input.
Diagram
All I2C components share the same bus.
Log
Week 1 - 3
- Got the initial idea.
- Researched the TOTP algorithm, its hardware implementation requirements and potential hardware components for the project.
Week 5
- Received the borrowed STM32 development board.
- Ordered the DS3231 RTC and the SSD1306 screen. First screen was dead on arrival, ordered two more and wired them up successfully.
- Got working codes for RFC 6238 test data.
Week 7
- After using an AT24C256 EEPROM at the laboratory, I decided to order one for the project as well.
- Read about and tested hardware True Random Number Generation (TRNG).
- Decided on the device powering strategy. Ordered 2 x 18650 rechargeable batteries, a dual battery holder, and an LM2596 5V step-down converter to regulate the ~7.4V battery output to the 5V required by the board's E5V pin.
Week 8
- Received and tested the EEPROM.
- Successfully replaced the external crates in TOTP generation (SHA1 and HMAC) with hardware accelerated cryptographic functions, provided by
embassy-stm32::hash. - Attempted to use hardware-accelerated AES for encryption, but the STM32U545's AES peripheral is not exposed by the
embassy-stm32library for this chip variant. - Tested
xchacha20poly1305, encrypted and then decrypted a string and verified that I got the same output.
Hardware
The device is composed of the following physical components:
- STM32 NUCLEO-U545RE-Q (STM32U545 MCU): serves as the main processing unit. It interfaces with all peripherals via I2C and GPIO, and provides UART and USB connectivity.
- SSD1306 (SSD1306 OLED Display (128x64, I2C): used to display the current account label, TOTP code, and remaining validity time.
- DS3231 RTC (I2C): provides accurate timekeeping required for TOTP generation and maintains time across power cycles using a CR2032 backup battery.
- AT24C256 EEPROM (I2C): external non-volatile memory used to store account data.
- TTP223 Capacitive Touch Sensors (x2): provide user input through touch interaction, supporting short and long presses.
Communication Interfaces: UART for provisioning and debugging; USB for USB-HID communication with a host computer.
The device is powered by two series-connected 18650 lithium-ion cells (~7.4V combined), regulated down to 5V by an LM2596 step-down converter. The 5V output feeds the board's E5V pin, which bypasses the onboard voltage regulator.
All peripheral components communicate with the microcontroller primarily over the shared I2C bus.
Schematics
KiCAD schematics will be added here as soon as they're done.
Bill of Materials
| Device | Usage | Price |
|---|---|---|
| STM32 NUCLEO-U545RE-Q | The microcontroller | N/A (borrowed) |
| SSD1306 0.96" 128x64 OLED Screen | Displays accounts and codes | 18.98 RON |
| DS3231 Real-time Clock Module | Keeps time for TOTP | 15.98 RON |
| CR2032 battery | For DS3231 | 6.24 RON |
| 2 x 18650 rechargeable batteries | For powering the device | 38.96 RON |
| 18650 dual battery holder | Holds the 18650 batteries | 3.99 RON |
| LM2596 Step Down - fixed 5V output | Steps down battery voltage to 5V | 12.99 RON |
| EEPROM Module AT24C256 | Stores account data | 8.99 RON |
| Breadboard kit (wires, LEDs, buttons, resistances) | Prototyping and connections | 49.97 RON |
| 2 x TTP223 | For button inputs | 3.98 RON |
Software
| Library | Description | Usage |
|---|---|---|
| embassy-stm32 | HAL for STM32 microcontrollers | Used for I2C, UART, USB, hardware HMAC-SHA1 via HASH peripheral, TRNG |
| embassy-sync | Async-aware synchronisation primitives | Channels and signals for inter-task communication |
| embassy-time | Timekeeping and async delays | Timers for code expiry countdown and debouncing |
| embassy-executor | Async task executor for embedded systems | Runs all concurrent Embassy tasks |
| embassy-usb | Async USB device stack | USB device initialisation and HID transport |
| usbd-hid | USB HID descriptor and report types | HID reports for typing TOTP codes |
| ssd1306 | Display driver for SSD1306 | Used for the display, displays codes, accounts |
| embedded-graphics | 2D graphics library | Used for drawing to the display |
| ds323x | DS3231 RTC Driver | Used for writing and reading the time |
| eeprom24x | Driver for 24x series I2C EEPROMs | Reading and writing TOTP records to the AT24C256 |
| chacha20poly1305 | XChaCha20-Poly1305 AEAD cipher | Encrypting and decrypting TOTP secrets |
| heapless | Static, fixed-capacity data structures | Strings and vecs without a heap allocator |
| defmt | Efficient logging framework for embedded targets | Structured debug logging over RTT |
| defmt-rtt | RTT transport backend for defmt | Transmits defmt log output via RTT to a debug probe |
| panic-probe | Panic handler using probe-rs | Forwards panic messages to the debug probe via defmt |
| serialport | Cross-platform serial port library | UART communication between CLI and device |
| clap | Command line argument parser | Used for CLI argument parsing, defaults |