[PROJECT_ID: 0x03][STATUS: FUNCTIONAL][CATEGORY: HARDWARE • CRYPTOGRAPHY]

Hardware TOTP Token

A standalone hardware 2FA token generator on a Raspberry Pi Pico 2 W — with every piece of the crypto hand-rolled in MicroPython. No app, no internet, no external crypto libraries.

the_idea

Every banking app and authenticator app on your phone generates 6-digit codes using TOTP (RFC 6238): a shared secret plus the current time, run through HMAC-SHA1, sliced down to six digits, refreshed every 30 seconds. Both sides compute it independently — no message ever sent back and forth.

This project implements that entire stack from raw primitives on a five-dollar microcontroller, to actually understand how TOTP works under the hood rather than trust it as a black box. It then became something bigger: a three-tiered build structured as a live hardware workshop, so other people could take the same path from "flash and go" to "implement the cryptography yourself."

how_it_works

[Phone connects directly]
        |
        |  Pico 2 W running its own WiFi AP — no venue/internet needed
        v
[Hand-built HTTP server]
        |
        |  raw sockets, no frameworks
        v
[Base32 decoder — hand-rolled]
        |
        |  parses a real secret pasted from an existing authenticator app
        v
[HMAC-SHA1 — built manually from hashlib.sha1, per RFC 2104]
        |
        v
[TOTP per RFC 6238 — 30s step, 6-digit output]
  • WiFi AP mode on the Pico — the phone connects directly, no external network required
  • Raw socket-based HTTP server, no external libraries
  • Base32 decoder written from scratch for secret parsing
  • HMAC-SHA1 constructed by hand from hashlib.sha1, since MicroPython doesn't bundle an hmac module
  • Time set manually via a web form, since there's no NTP in AP-only mode

build_it_yourself

Tiered build

This became a workshop structure, not just a personal project:

  • Beginner — pre-built firmware, flash and go, single account
  • Intermediate — multi-account routing, JS auto-refresh instead of full-page reload
  • Advanced — hand-implement the HMAC-SHA1 construction yourself from the SHA1 primitive, plus a concurrency stretch goal (the raw socket server only handles one connection at a time by default)

Hardware

Raspberry Pi Pico 2 W. That's it.

what_i_learned

MicroPython's string type doesn't implement .zfill() — a method that just exists for free in desktop Python. Found that the hard way on real hardware, fixed with manual zero-padding. Small thing, but a good example of how MicroPython's stdlib is a trimmed-down subset, not a drop-in match for CPython. The same lesson showed up with hmac and base32 decoding — anything you assume is "standard library" needs to be checked before you're two hours into debugging why it doesn't exist.

what_comes_next

The same TOTP core is a legitimate building block for access control, not just logins. Point the verification side at a relay instead of a webpage and it becomes a rotating-code gate lock instead of a rotating-code login. That's an active second track being scoped separately — estate gate access, time-boxed visitor codes, layering on top of existing intercom systems.

  • [ ] Concurrency fix for the raw socket server (currently one connection at a time)
  • [ ] Scope the gate-access track separately
  • [ ] Write the GitHub README