SecureGen - Open-source Hardware TOTP Authenticator & Password Manager on ESP32 T-Display
Posted: Wed Feb 11, 2026 5:47 pm
# The Story
## What Started This Project
I wanted a physical 2FA device that I could trust completely. Most authenticator apps live on my phone - which means they're connected to the internet, running closed-source code, and vulnerable to remote attacks. Hardware tokens like YubiKey are great, but they're expensive and you can't see your codes or manage passwords.
So I built SecureGen: an open-source hardware security device on the ESP32 T-Display that combines a TOTP/HOTP authenticator with a password manager, and can type passwords directly into any device via Bluetooth. Now at v2.1.0 with significant updates since the original post.
Video demo:
https://www.youtube.com/watch?v=YTVQBwgok_E
Flash from browser (Chrome/Edge): https://makepkg.github.io/SecureGen/flash
## How It Works
### Hardware Foundation
The device is built on the LILYGO T-Display ESP32 board, which gives us:

TOTP / HOTP Authenticator Mode:
The device generates time-based one-time passwords (TOTP) compatible with Google Authenticator, Microsoft Authenticator, Authy, and all RFC 6238 services. After initial NTP time sync via WiFi, it works completely offline. Each code rotates every 30 seconds with a visual countdown timer.
HOTP (counter-based, RFC 4226) is also supported — press both buttons to generate the next code. The counter increments atomically and is written to flash after button release, preventing desync on battery power.
With a DS3231 RTC module connected, TOTP works accurately in AP mode and fully air-gapped Offline mode — no WiFi or internet required.
![Image]()
Password Manager Mode:
Stores AES-256-GCM encrypted passwords locally. Press both buttons and the device connects via BLE HID keyboard to type your password automatically — no clipboard, no typing, no shoulder surfing. PIN protection on the device side prevents unauthorized BLE transmission.
### The Technical Challenges
1. BLE HID Keyboard Implementation
Getting the ESP32 to act as a Bluetooth keyboard was harder than expected. The BLE HID descriptor needed manual configuration to support all special characters (!, @, #, $, etc.). Different keyboard layouts handle symbols differently — for example, @ is Shift+2 on US layout but Shift+' on UK layout.
Configurable layout mapping lets users select their keyboard layout in the web interface. The device then translates each character to the correct key combination for that layout.
2. BLE Security with LE Secure Connections
Standard Bluetooth is not secure enough for transmitting passwords. LE Secure Connections with MITM protection uses Numeric Comparison pairing — a 6-digit PIN appears on the device screen for verification before confirming. Once paired, all communication uses AES-128 at the BLE layer on top of application-level AES-256-GCM.
The tricky part was iOS compatibility — Apple enforces stricter security requirements than Android. Adaptive bonding parameters detect the connecting device type and adjust security settings accordingly.
3. Memory Management: BLE + WiFi
The ESP32's BLE stack alone consumes ~70KB of RAM. Running both BLE and WiFi simultaneously causes heap fragmentation and random crashes.
Solution: strict mode separation. TOTP mode uses WiFi only for NTP sync, then disables it completely. Password manager mode runs pure offline with only BLE active. Never both simultaneously.
4. Secure Storage on ESP32
Added AES-256-GCM encryption on top of LittleFS — with authenticated encryption, meaning any tampering with stored data is detected and rejected.
The encryption key is derived from the user's PIN via PBKDF2-HMAC-SHA256 at 25,000 iterations. Even if someone extracts the flash chip, they can't decrypt the data without the PIN. Persistent PIN lockout: 5 failed attempts across reboots locks the device permanently.
5. Web Interface Security — 8 Layers
The device runs an HTTP web server with a full application-level encrypted channel — no TLS certificates needed, works in AP mode:
6. Light Sleep — The Subtle Bug
esp_light_sleep_start() crashed on wake when running on battery power — not reproducible on USB. Root cause: GPIO0 hardware interaction during wake sequence.
Fix: pseudo-sleep instead of the API. CPU clocked to 40MHz, display suspended, peripherals held. Same user experience, zero crashes.
7. DS3231 RTC Sync Accuracy
The chip itself is ±2ppm — rock solid. The hard part is getting accurate time INTO it.
Source is NTP over WiFi, but between request and write to RTC registers you have: network latency (variable) + AES-256-GCM decrypt + JSON parse + I2C write. By the time you write the timestamp it's already 50-200ms stale. At ±2ppm accuracy that's noise larger than the chip's own drift. Simple sync means you're writing the past with confidence.
## Operating Modes
WiFi Client Mode:
Connects to your network. NTP time sync for accurate TOTP. Web management interface accessible from any browser on the network.
Access Point Mode:
Device creates its own isolated WiFi hotspot. Web interface at 192.168.4.1. All 8 security layers active. QR code on screen for easy connection. TOTP works via DS3231 RTC without internet.
Offline Mode (Air-Gapped):
WiFi completely disabled. No network attack surface. Password manager fully functional via BLE. TOTP works if DS3231 RTC is connected. Maximum isolation.
At boot, a 2-second prompt lets you override the saved default mode with button presses.
## Security Philosophy
Everything is open source — audit the code, build it yourself, verify there are no backdoors. The device works offline by default.
Known limitations documented openly: PBKDF2 iteration count (25,000) is below OWASP 2023 recommendations due to ESP32 hardware constraints. No hardware secure enclave by default.
Security through obscurity is not security. Security through architecture is.
## What's Next
You only need:
GitHub: https://github.com/makepkg/SecureGen
User Guide: https://makepkg.github.io/SecureGen/guide
## What Started This Project
I wanted a physical 2FA device that I could trust completely. Most authenticator apps live on my phone - which means they're connected to the internet, running closed-source code, and vulnerable to remote attacks. Hardware tokens like YubiKey are great, but they're expensive and you can't see your codes or manage passwords.
So I built SecureGen: an open-source hardware security device on the ESP32 T-Display that combines a TOTP/HOTP authenticator with a password manager, and can type passwords directly into any device via Bluetooth. Now at v2.1.0 with significant updates since the original post.
Video demo:
https://www.youtube.com/watch?v=YTVQBwgok_E
Flash from browser (Chrome/Edge): https://makepkg.github.io/SecureGen/flash
## How It Works
### Hardware Foundation
The device is built on the LILYGO T-Display ESP32 board, which gives us:
- - Dual-core ESP32 processor with hardware AES encryption
- 1.14" color TFT display (135×240 ST7789)
- Two physical buttons for navigation
- Built-in battery charging circuit
- WiFi and Bluetooth 5.0 LE
- Optional DS3231 RTC module for accurate offline timekeeping (I2C, SDA/SCL)

TOTP / HOTP Authenticator Mode:
The device generates time-based one-time passwords (TOTP) compatible with Google Authenticator, Microsoft Authenticator, Authy, and all RFC 6238 services. After initial NTP time sync via WiFi, it works completely offline. Each code rotates every 30 seconds with a visual countdown timer.
HOTP (counter-based, RFC 4226) is also supported — press both buttons to generate the next code. The counter increments atomically and is written to flash after button release, preventing desync on battery power.
With a DS3231 RTC module connected, TOTP works accurately in AP mode and fully air-gapped Offline mode — no WiFi or internet required.
Password Manager Mode:
Stores AES-256-GCM encrypted passwords locally. Press both buttons and the device connects via BLE HID keyboard to type your password automatically — no clipboard, no typing, no shoulder surfing. PIN protection on the device side prevents unauthorized BLE transmission.
### The Technical Challenges
1. BLE HID Keyboard Implementation
Getting the ESP32 to act as a Bluetooth keyboard was harder than expected. The BLE HID descriptor needed manual configuration to support all special characters (!, @, #, $, etc.). Different keyboard layouts handle symbols differently — for example, @ is Shift+2 on US layout but Shift+' on UK layout.
Configurable layout mapping lets users select their keyboard layout in the web interface. The device then translates each character to the correct key combination for that layout.
2. BLE Security with LE Secure Connections
Standard Bluetooth is not secure enough for transmitting passwords. LE Secure Connections with MITM protection uses Numeric Comparison pairing — a 6-digit PIN appears on the device screen for verification before confirming. Once paired, all communication uses AES-128 at the BLE layer on top of application-level AES-256-GCM.
The tricky part was iOS compatibility — Apple enforces stricter security requirements than Android. Adaptive bonding parameters detect the connecting device type and adjust security settings accordingly.
3. Memory Management: BLE + WiFi
The ESP32's BLE stack alone consumes ~70KB of RAM. Running both BLE and WiFi simultaneously causes heap fragmentation and random crashes.
Solution: strict mode separation. TOTP mode uses WiFi only for NTP sync, then disables it completely. Password manager mode runs pure offline with only BLE active. Never both simultaneously.
4. Secure Storage on ESP32
Added AES-256-GCM encryption on top of LittleFS — with authenticated encryption, meaning any tampering with stored data is detected and rejected.
The encryption key is derived from the user's PIN via PBKDF2-HMAC-SHA256 at 25,000 iterations. Even if someone extracts the flash chip, they can't decrypt the data without the PIN. Persistent PIN lockout: 5 failed attempts across reboots locks the device permanently.
5. Web Interface Security — 8 Layers
The device runs an HTTP web server with a full application-level encrypted channel — no TLS certificates needed, works in AP mode:
- - ECDH P-256 Key Exchange — ephemeral keys, HKDF-derived AES session key
- AES-256-GCM Transport — all request/response bodies encrypted end-to-end
- URL Obfuscation — endpoints rotate every 30 reboots, block automated scanners
- Header Obfuscation — fake headers hide tech stack from fingerprinting
- Traffic Obfuscation — periodic decoy requests mask real usage patterns
- CSRF Protection — session-bound tokens on every mutating request
- Method Tunneling — all HTTP methods hidden behind single POST tunnel
- Session Management — 128-bit IDs, LRU pool, encrypted persistence
6. Light Sleep — The Subtle Bug
esp_light_sleep_start() crashed on wake when running on battery power — not reproducible on USB. Root cause: GPIO0 hardware interaction during wake sequence.
Fix: pseudo-sleep instead of the API. CPU clocked to 40MHz, display suspended, peripherals held. Same user experience, zero crashes.
7. DS3231 RTC Sync Accuracy
The chip itself is ±2ppm — rock solid. The hard part is getting accurate time INTO it.
Source is NTP over WiFi, but between request and write to RTC registers you have: network latency (variable) + AES-256-GCM decrypt + JSON parse + I2C write. By the time you write the timestamp it's already 50-200ms stale. At ±2ppm accuracy that's noise larger than the chip's own drift. Simple sync means you're writing the past with confidence.
## Operating Modes
WiFi Client Mode:
Connects to your network. NTP time sync for accurate TOTP. Web management interface accessible from any browser on the network.
Access Point Mode:
Device creates its own isolated WiFi hotspot. Web interface at 192.168.4.1. All 8 security layers active. QR code on screen for easy connection. TOTP works via DS3231 RTC without internet.
Offline Mode (Air-Gapped):
WiFi completely disabled. No network attack surface. Password manager fully functional via BLE. TOTP works if DS3231 RTC is connected. Maximum isolation.
At boot, a 2-second prompt lets you override the saved default mode with button presses.
## Security Philosophy
Everything is open source — audit the code, build it yourself, verify there are no backdoors. The device works offline by default.
Known limitations documented openly: PBKDF2 iteration count (25,000) is below OWASP 2023 recommendations due to ESP32 hardware constraints. No hardware secure enclave by default.
Security through obscurity is not security. Security through architecture is.
## What's Next
- - Port to T-Display S3 (PSRAM, USB HID, larger screen)
- ECDH P-256 → X25519 migration (~400ms → ~80ms key exchange)
- Flash encryption and secure boot via sdkconfig
- ATECC608 secure element support
You only need:
- - LILYGO T-Display ESP32 (~$10 on AliExpress)
- USB-C cable
- Optional: 3.7V LiPo battery with JST connector
- Optional: DS3231 RTC module (~$2) for offline TOTP
GitHub: https://github.com/makepkg/SecureGen
User Guide: https://makepkg.github.io/SecureGen/guide