totp-gba

May 14, 2026 · View on GitHub

Native Game Boy Advance TOTP (RFC 6238) authenticator. Same protocol as Google Authenticator — add accounts, get rotating 6-digit codes — but running on real ARM7TDMI hardware off a flashcart.

Sibling repo to totp-gb, which targets the original DMG / GBC. The crypto core (SHA1 / HMAC / Base32 / TOTP) is shared verbatim; this project adds GBA-specific platform glue (libtonc text engine, EWRAM-mapped SRAM, hardware RTC via Seiko S-3511A with software-VBlank fallback, 240×160 layout, OAM sprite cursor).

ROM Built with devkitARM Algorithm verified


Screens

MainViewSettings
Main list with live code + countdown barView account with bar urgency colorsSettings with theme picker
SolarizedAddSet time
Same screen, Solarized themeAdd account char dialSet time with field cursor

Full gallery + reproduction notes: docs/screens/README.md.


Try it

Pre-built ROMs are checked into artifacts/:

FileTargetNotes
totp-gba.gbaGBA / GBA SP / GBA Micro / DS / DS Lite / DSi (slot-2)Native ARM7 build, ~36 KB
totp-gba-test.gbaSameDebug build that auto-seeds a Test account on every boot
totp-gba-test.gbcGBA, GBA SP, original DS slot-2Byte-identical copy of the GBC build for hardware backward-compat testing

Load totp-gba.gba in mGBA or any GBA emulator, or flash it to a slot-2 cart. First boot drops you into a Set Time screen (UTC). After that you land on the live list — Start adds an account, A views, B re-syncs the clock, SELECT opens settings.


Build from source

Requires devkitPro with the gba-dev package (includes devkitARM, libgba, libtonc): https://github.com/devkitPro/installer/releases

.\build.bat            # build totp-gba.gba and copy to artifacts\
.\build.bat test       # build totp-gba-test.gba (auto-seeded for testing)
.\build.bat clean      # nuke build/ + outputs

The wrapper bypasses PowerShell's WSL routing of make. On Linux/macOS or in CI, just make / make test.


Features

  • HMAC-SHA1 / TOTP per RFC 6238 — pure C from totp-gb, ARM7-clean
  • Persistent storage for up to 8 accounts in cart SRAM at 0x0E000000
  • Hardware RTC support — bit-banged Seiko S-3511A driver over the cart-edge GPIO at 0x080000C4 (the same chip the Pokémon Ruby/Sapphire carts use). Auto-detected at boot with conservative fail-closed probing; falls back to software VBlank counting on carts without an RTC chip. Settings screen shows which backend is active.
  • Software RTC fallback — VBlank-counted (60 Hz), heartbeat-saved every 30 s so persistence is never more than one window stale.
  • Per-frame screen state caching — TOTP only re-runs when the 30 s window flips or the user scrolls the list
  • Twelve color themes — Phosphor green, Amber CRT, Pip-Boy, Solarized, Cool blue, Hot pink, Game Boy DMG, Game Boy Pocket gray, Atari 2600, GB Camera sepia, Magenta dusk, Ice. Settings screen cycles them; choice persists in SRAM.
  • Sprite cursor — OAM-driven bobbing arrow on selectable rows (Settings, Set Time), independent of the BG-tile text layer.
  • Sound effects — APU click / confirm / error / window-flip beeps on PSG channels 1+4. Mutable from settings.
  • Color-coded urgency countdown — bar transitions phosphor → amber → red as the 30 s window winds down; final 5 s show one '!' per second.
  • Auto-scrolling long account names — selected row's name ticker-scrolls if it overflows the 16-char display window.
  • Animated boot splash ("TOTP-GBA / Authenticator" with a marquee).
  • mGBA self-test on boot — 4 known-answer vectors logged via the mGBA debug-log register; visible in mGBA's Log window without any user interaction. The C code also writes a probe byte to EWRAM 0x02000000 for headless verifier polling.

Testing

Two layers, like the sibling project:

  1. Boot self-test — runs at every boot under mGBA. Logs PASS/FAIL for JBSWY3DPEHPK3PXP at four hardcoded epochs:
    [INFO] === TOTP-GBA self-test (Hello! secret) ===
    [INFO]   epoch=0           got=282760  expected=282760  PASS
    [INFO]   epoch=1234567890  got=742275  expected=742275  PASS
    [INFO]   epoch=1778088090  got=283711  expected=283711  PASS
    [INFO]   epoch=1778088141  got=113232  expected=113232  PASS
    [INFO] Self-test result: 4/4 PASS
    
  2. Headless verifier — same self-test, but the C code also writes 0x55 (PASS) or 0xAA (FAIL) to EWRAM 0x02000000 after running. tests/verify_test_rom.lua polls that cell and writes the verdict to tests/result.txt; tests/run_headless.sh wraps mgba-sdl + xvfb-run + a timeout for CI use.
mgba --script tests/verify_test_rom.lua artifacts/totp-gba.gba
# or for CI:
./tests/run_headless.sh

CI runs on every push (see .github/workflows/build.yml).


Layout

source/                 C sources
  main.c                  boot + self-test + dispatch
  clock.c                 dual-backend RTC (hardware S-3511 / software VBlank)
  gba_rtc.c               Seiko S-3511A bit-banger over cart GPIO
  storage.c               SRAM I/O + Account CRUD
  datetime.c              epoch <-> Y/M/D
  audio.c                 PSG SFX (click/confirm/error/window-flip)
  ui_common.c             shared text/palette helpers + theme apply
  ui_main.c               main list screen + theme table
  ui_view.c               view/delete account
  ui_add.c                two-step add flow
  ui_charpicker.c         text-entry dial
  ui_timeset.c            modal time setter
  ui_settings.c           palette / sound / RTC backend display
  ui_cursor.c             OAM sprite cursor (bobbing arrow)
  mgba_log.c              mGBA debug-log register wrapper
  sha1.c hmac.c base32.c  crypto core (shared with totp-gb)
  totp.c
include/                C headers
artifacts/              Prebuilt ROM (committed)
tests/                  Headless verifier
.github/workflows/      CI
Makefile                devkitARM template + libtonc
build.bat               Windows wrapper

Acknowledgments


License

Released under the MIT License.