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
- vs. SMS: far better — no SIM-swap or interception risk.
- vs. an authenticator app: seeds live on tamper-resistant hardware rather than a phone backup, and some keys require a physical touch to release a code.
- vs. FIDO2: TOTP is not phishing-resistant — a code can be typed into a fake site and replayed within its ~30-second window. Use FIDO2 where the site supports it; use OATH where it only offers "authenticator app."
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
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:
- add: it goes onto the key.
- exists: a code with the same name is already on the key, so it is
left as is.
--replace(in the app, Replace codes already on the key that have the same name) replaces it instead. Some keys refuse a second code with the same name; there, replacing removes the old code first, and if adding the new one then fails, the old code is gone. - skip: the key can't store it, and the preview says why. keyroost never changes an entry to make it fit.
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
- YubiKey: 32 codes, or 64 from firmware 5.7. Names up to 64 bytes
(the period prefix included). SHA-512 from firmware 4.3.1. On firmware 4.0 to 4.3.4,
a name that is the start of another name already on the key is skipped, and
oath addrefuses it, following the same rule as Yubico'sykman. - Solo 2: doesn't report how many codes it holds, so writing
stops if it fills up. Names up to 127 bytes: a longer name is refused by the import,
oath addand the app before anything is sent, because on firmware 2.964 a 128-byte name made the key stop responding until it was unplugged. No SHA-512 codes. - Nitrokey 3: 50 codes. Only 30-second codes, because the key doesn't store the period. No SHA-512 codes. The key asks for a touch to reset its Authenticator, to delete any account, and to save or read one that requires a touch; keyroost says so each time.
- Other keys: keyroost knows no limits up front; the key's own refusal is reported.
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.