kkeyroost

Learn · OATH

OATH — TOTP & HOTP

The familiar 6-digit "authenticator app" codes — but generated on a hardware key instead of an app that can be backed up to the cloud.

keyroost screenshot — Authenticator codes: live TOTP codes with a countdown.
Authenticator codes: live TOTP codes with a countdown.

What it is

OATH is the open family of one-time-password algorithms behind authenticator apps: TOTP (time-based, RFC 6238) and HOTP (counter-based, RFC 4226). Each account shares a secret seed with the service once, at setup; afterward both sides independently compute the same short code from that seed plus the current time (or a counter). Nothing is transmitted but the code you read off.

Storing those seeds on a hardware key (via the Yubico/Trussed OATH applet, reached over PC/SC) keeps them off a phone that syncs to the cloud and out of an app that can be cloned. The seeds don't leave the key; codes are computed on-device when asked.

How it compares

Optional applet password

The OATH applet can be protected by a password that must be presented before codes can be listed or computed — a useful extra gate if the key is shared or lost. Forget it and the only way back is keyroostctl oath reset, which needs no password but wipes every stored seed with it. See resetting →

What keyroost does with OATH

k

Over PC/SC: list credentials, add and delete them, compute codes, and set / clear / unlock the applet password — all with an in-tree, pure-Rust OATH byte layer.

keyroostctl oath list
keyroostctl oath code <name> --device my-yubikey
keyroostctl oath delete <name>      # asks y/N first; a script adds --yes
keyroostctl oath import -i export.json  # add every code in an authenticator export

In the desktop app, + Add credential takes the account name and the base32 setup key, or scans the setup QR code, and can require a touch; a file of otpauth:// URIs goes through Import file…. On the CLI the seed is typed at a hidden prompt by oath add, or in a script given as --seed env:NAME or --seed stdin (base32 unless --encoding hex). This works on any card exposing the OATH applet — YubiKey, Nitrokey, SoloKeys, Feitian, Token2 (experimental) and others — over USB or an NFC / PC-SC reader. With one key connected keyroost uses it; with several, a terminal shows a numbered list and a script names one with --device (a saved name, a serial, or the number keyroostctl list shows). Deleting a credential and oath reset ask y/N first, naming the key.

Codes that need a touch

A code saved with require touch shows Read code in the app instead of a code. Click it and touch the key when it blinks. keyroost asks the key for the code 10 seconds ahead, as Yubico Authenticator does, so a code you touch for near the end of its 30 seconds doesn't run out moments later. When that happens, you get the code for the next 30 seconds: the app shows it in gray with wait 4s (counting down) until those 30 seconds begin. Enter it once its countdown starts. Some sites accept it a little earlier, but every site accepts it then. The code stays on screen until its time is up. keyroostctl oath code prints the code for the current 30 seconds.

Importing codes from an authenticator app

keyroost can add every code in an authenticator export to a key's OATH storage in one step (YubiKey, Solo 2, Nitrokey 3, and other keys with an OATH applet). It reads Aegis exports (plain or encrypted), 2FAS exports, a text file with one otpauth:// URI per line, and QR screenshots, including a Google Authenticator export.

In the desktop app, drop the file on the window, or click Import file… in the key's Authenticator view. The Import authenticator codes dialog lists the entries (an encrypted export asks for its password first), lets you pick the key under Add to, and shows what each entry will become before anything is written. On the command line:

keyroostctl oath import -i export.json --dry-run   # show what would happen; writes nothing
keyroostctl oath import -i export.json             # asks once, then adds the codes
keyroostctl oath import -i export.json --file-password env:EXPORT_PW   # encrypted Aegis export

Before writing, keyroost reads what is already on the key and marks each entry:

keyroost asks once before writing (a script adds --yes), writes one code at a time, and stops at the first error, saying what was added and what wasn't. Running the same import again is safe: codes already added show as exists. A dry run needs the key connected, because the preview depends on what is on it.

What each key can store

Codes with a period other than 30 seconds

A YubiKey or Solo 2 doesn't store a TOTP code's period. Instead, the name starts with it, such as 60/GitHub:alice (the naming Yubico's own tools use), and an import names codes that way. keyroost reads the prefix: oath code uses it as the period unless --period says otherwise, and the app shows those codes with their period.

Authoritative resources

From the command line: see Authenticator codes on a key and Import codes from an authenticator app on the Command line page.