Or: how I built a USB password keeper that mostly worked, sometimes lied, and taught me more than any success ever did.
I recently found these project files buried in a folder titled “Never Again.” At first, I thought they didn’t deserve a blog post. Mostly because the device has a mind of its own, it works perfectly when I’m just showing it off, but reliably develops stage fright the moment I actually need to log in. This little monster made it all the way to revision 7 of the PCB. I finally decided to archive the project after adding a Schmitt trigger : the component that was mathematically, logically, and spiritually supposed to solve the debouncing issues and save the day.
Spoiler: it didn’t.
Instead of a revolutionary security device, I ended up with a zero-cost, high-frustration random number generator built from whatever was lying in my junk drawer. It occasionally types my password correctly, provided the moon is in the right phase and I don’t look at it too directly. And yet… here we are.
The Idea That Seemed Reasonable at the Time
A long time ago, when “password manager” still meant a text file named passwords.txt, I had what felt like a good idea:
Build a tiny device that types passwords for me.
No drivers. No software installation. Just plug it in, press a button, and it types the password like a keyboard. From a security point of view, it sounded brilliant:
- The OS already trusts keyboards
- No clipboard
- No background process
- No software attack surface If it only types, it can’t be hacked… right?
(Yes. That sentence aged badly.)
Constraints That Created the Monster
This was not a commercial project. This was a “use what’s on the desk” project.
So the constraints were self-inflicted:
- MCU: ATtiny85 (cheap, tiny, limited)
- Display: HD44780 (old, everywhere, slow)
- USB: bitbanged (no hardware USB)
- GPIOs: basically none
- PCB: single-sided, etched at home
- Budget: close to zero
The only thing I had plenty of was optimism.
Driving an LCD With One Pin (Yes, Really)
The first problem: The ATtiny85 simply does not have enough pins to drive an HD44780 display.
Even in 4-bit mode, the display wants more pins than I could spare. So I did what any reasonable person would do:
I multiplexed everything through one GPIO using RC timing.
By carefully choosing resistor and capacitor values, I could:
- Encode clock, data, and select signals
- Decode them on a 74HC595
- Drive the display using time-based signaling
It worked. Mostly. But it was also:
- Sensitive to temperature
- Sensitive to component tolerances
- Sensitive to how long the board had been powered on
- Fundamentally analog pretending to be digital
Lesson #1:
If your protocol depends on analog behavior, you don’t really control it.
Abusing USB HID for Fun and (Almost) Profit
This is the part I still like the most.
The problem A USB
keyboard is input-only. You can’t send data to it.So how do you update the password database?
The bad idea
Use the keyboard LEDs.
- Caps Lock
- Num Lock
- Scroll Lock
They’re controlled by the host. And yes: you can read them from firmware.
The result
I implemented a synchronous serial protocol over HID LEDs.
- Clocked
- Deterministic
- Host-driven
- No timing guessing
- No race conditions
And surprisingly: This was the most reliable part of the whole project. It was slow, sure. But passwords are small. And since the clock came from the host, it was rock solid.
Lesson #2:
The ugliest hack is sometimes the most reliable one.
The Part Nobody Warns You About: Scancodes
The update tool was a small Linux application that sent password data to the device.
Here’s the catch:
Keyboards don’t send ASCII. They send scancodes. And:
- PS/2 scancodes ≠ USB HID scancodes
- Layout matters
- Locale matters
- Shift state matters
So the database wasn’t a list of characters. It was a list of HID scancodes.
That means:
- The device was layout-dependent
- The database was architecture-dependent
- Portability was not free
This is one of those details nobody tells you until you trip over it yourself.
Lesson #3:
Text is an illusion. Keyboards don’t speak ASCII.
The USB Problem I Couldn’t Outsmart
Now for the real failure. The ATtiny85 has no USB hardware.
So USB had to be:
- Bitbanged
- Cycle-perfect
- Timed in software
- Extremely sensitive to clock drift
Sometimes it worked. Sometimes it didn’t enumerate. Sometimes it worked once and never again. Sometimes it depended on the USB host.
This wasn’t a bug. This was physics.
Lesson #4:
USB is not forgiving, and bitbanging it is an act of optimism.
The Hardware (Yes, It Actually Exists)
Despite everything:
- I built two physical units
- I etched the PCB myself (single-sided)
- I assembled them by hand
- They worked... Most of the time.
I still have them. They still boot. Sometimes.
Repository Structure (For the Curious)
The project is split into three parts:
Repository Structure (For PCB & Schematics)
- Single-layer board
- Home-etched
- All compromises visible
- No hiding from physics
Host-Side Tool
- Linux-based
- Sends HID scancodes
- Talks to the device via LED protocol
- No ASCII anywhere
Firmware
- Arduino-based
- Third-party bootloader
- USB bitbanging
- Display driving
- HID handling
What Actually Failed (and What Didn’t)
Failed
- USB reliability
- Display robustness
- Timing assumptions
- Environmental tolerance
Worked
- HID LED protocol
- Password logic
- Conceptual design
- Learning value
The irony
- The part that looked insane... worked.
- The part that looked standard... didn’t.







No comments:
Post a Comment