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.
Video DemoHow It WorksHardware FoundationThe 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 and progress bar.
HOTP (counter-based) is also supported — press both buttons to generate the next code. The counter increments atomically and is saved to flash immediately after release, preventing desync even on battery power.
With a DS3231 RTC module connected, TOTP works accurately even in AP and Offline modes without any WiFi connection.
Password Manager Mode:Stores encrypted passwords locally. When you need to log in somewhere, 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 Challenges1. 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.
I implemented configurable layout mapping so users can 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. I implemented LE Secure Connections with MITM protection using Numeric Comparison pairing.
When you pair the device, a 6-digit PIN appears on the device screen. You verify it matches before confirming. This prevents anyone from intercepting the pairing process. Once paired, all communication uses AES-128 encryption at the BLE layer — on top of the application-level AES-256-GCM encryption that protects the data itself.
The tricky part was iOS compatibility — Apple enforces stricter security requirements than Android. I had to implement adaptive bonding parameters that detect the connecting device type and adjust security settings accordingly.
3. Memory Management: BLE + WiFi Simultaneously
The ESP32's BLE stack alone consumes about 70KB of RAM. When you enable WiFi, heap memory becomes critically tight. Running both simultaneously causes random crashes.
My solution: strict mode separation. TOTP mode uses WiFi only for initial NTP sync, then disables it completely. Password manager mode runs pure offline with only BLE active. When transmitting passwords via BLE, WiFi is always off.
4. Secure Storage on ESP32
The ESP32's built-in NVS provides basic flash storage, but it's not encrypted by default. I added AES-256-GCM encryption on top, with authenticated encryption — meaning any tampering with stored data is detected.
The encryption key is derived from a user PIN using PBKDF2-HMAC-SHA256 with 25,000 iterations. Even if someone extracts the flash chip, they can't decrypt the data without the PIN.
5. Web Interface Security
The device runs a web server for configuration and management — no TLS certificates, but a full application-level encrypted channel implemented from scratch. Details in the Security Architecture section below.
6. Offline Timekeeping with DS3231 RTC
TOTP requires accurate time. Without WiFi, the device can't sync via NTP. I added optional DS3231 RTC module support — a hardware clock that maintains time with ±2ppm accuracy independently of WiFi.
This enables TOTP in AP mode and fully air-gapped Offline mode. The RTC is configured once via the web interface and keeps time even when the device is off.
7. Power Optimization
Battery life was crucial. The display is the biggest power consumer — after configurable timeout, the device enters light sleep with the display off. Button 2 wakes it instantly. Deep sleep with PIN lock is available for longer idle periods.
WiFi is disabled by default and only enabled when needed. Battery ADC required calibration — ESP32's ADC is non-linear, so voltage is mapped to percentage via a calibrated curve (3200–3800mV range).
8. Display Stability After Deep Sleep
The ST7789 display wouldn't reinitialize properly after deep sleep — GPIO states weren't being reset correctly. I had to implement a full hardware reset sequence with specific timing to reliably wake the display without artifacts.
Security Architecture: 8 Layers of DefenseSecureGen implements defense-in-depth with 8 independent layers. Each targets different attack vectors — breaching one layer doesn't compromise the system.
Layer 1: ECDH Key Exchange 🔐What it does: Generates unique session encryption keys using ephemeral P-256 elliptic curve Diffie-Hellman. HKDF derives the final AES session key from the shared secret.
Why it matters: Keys are ephemeral — never reused, never stored. Even if traffic is captured, there's no static key to extract.
Prevents: Man-in-the-Middle attacks, session replay
Layer 2: AES-256-GCM Session Encryption 🔒What it does: All request and response bodies are encrypted end-to-end with AES-256-GCM using the session key from Layer 1. GCM provides both confidentiality and authenticity — tampered data is rejected.
Why it matters: Even on an unencrypted HTTP channel, all application data is protected. Works in AP mode without certificates.
Prevents: Eavesdropping, packet injection, data tampering
Layer 3: URL Obfuscation 🎲What it does: All API endpoint paths are obfuscated and rotated every 30 reboots (epoch). Real paths are never exposed in traffic.
Why it matters: Automated scanners expect /api/totp or /passwords. Obfuscated paths require manual reverse engineering of the firmware.
Prevents: Automated API discovery, vulnerability scanning
Layer 4: Header Obfuscation 🎭What it does: Real HTTP headers are replaced with fake ones. The device presents itself as a different server on each session.
Before:
Server: ESP-AsyncWebServer
Content-Type: application/jsonAfter:
X-AspNetMvc-Version: 5.2.7
X-Powered-By: PHP/7.4.3Prevents: Tech stack fingerprinting, known vulnerability targeting
Layer 5: Traffic Obfuscation 👻What it does: The device periodically sends decoy requests to mask real traffic patterns. Real actions are indistinguishable from background noise in traffic analysis.
Prevents: Traffic analysis, behavioral fingerprinting, timing correlation
Layer 6: CSRF Protection 🛡️What it does: Every mutating request requires a valid CSRF token verified server-side. Tokens are session-bound and rotate with each session.
Prevents: Cross-site request forgery, unauthorized actions from malicious pages
Layer 7: Method Tunneling 🔀What it does: All HTTP methods (GET, POST, DELETE) are hidden behind POST requests to a single tunnel endpoint. The real method is encrypted inside the request body.
Why it matters: Automated scanners look for specific method patterns. Tunneling breaks their detection logic entirely.
Prevents: Pattern-based automated attacks, endpoint enumeration
What it does: 128-bit session IDs stored encrypted in LittleFS. Sessions survive reboots when epoch time is valid. LRU pool of 5 concurrent sessions with automatic eviction.
Why it matters: Sessions are cryptographically strong and tied to device state — impossible to guess or forge.
Prevents: Session hijacking, brute force authentication
Defense in Depth in Action[Automated scanner attempts reconnaissance]
→ Layer 3: Obfuscated URLs — scanner finds nothing recognizable
→ Layer 4: Fake headers — fingerprinting returns wrong stack
→ Layer 5: Decoy traffic — real requests hidden in noise
[Attacker intercepts traffic]
→ Layer 1: ECDH — no static key to extract
→ Layer 2: AES-256-GCM — ciphertext with authentication tag
→ Decryption without session key is computationally infeasible
[Attacker attempts CSRF or forged request]
→ Layer 6: CSRF token missing or invalid — request rejected
→ Layer 7: Method not tunneled correctly — dispatcher drops it
Result: Attack fails at multiple independent checkpointsOperating Modes
WiFi Client Mode:Connects to your home 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 available at 192.168.4.1. All 8 security layers active. QR code displayed 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.
This project follows verifiable security. Everything is open source — audit the code, build it yourself, verify there are no backdoors. The device works offline by default; your secrets never touch the internet unless you explicitly connect it.
Security through obscurity is not security. Security through architecture is.
Known limitations are documented openly: PBKDF2 iteration count (25,000) is below OWASP 2023 recommendations due to ESP32 hardware constraints. No hardware secure enclave by default. Full details in the Security Overview.
What's Next- Adapt firmware for T-Display S3 (larger screen, PSRAM, USB HID)
- Flash encryption and secure boot (optional hardening via sdkconfig)
- ECDH P-256 → X25519 migration for faster key exchange (~400ms → ~80ms)
- ATECC608 secure element support
- Quick search, favorites, and tag grouping in web interface
- mbedTLS — ECDH P-256, AES-256-GCM, PBKDF2, HKDF, SHA-256
- ESP32 BLE Arduino — Bluetooth Low Energy HID stack
- TFT_eSPI — display driver
- ESPAsyncWebServer — async web server
- ArduinoJson — JSON parsing
- sjcl + elliptic (embedded) — client-side crypto in web interface
- PlatformIO IDE
- Arduino Framework
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
Flash from browser (no tools needed): https://makepkg.github.io/SecureGen/flash
GitHub: https://github.com/makepkg/SecureGen











_1x_bGT19vVAby.png?auto=compress%2Cformat&w=40&h=40&fit=fillmax&bg=fff&dpr=2)




Comments