kkeyroost

Learn · On-device OTP

On-device OTP

TOTP and HOTP codes stored on the key's own applet — seeds sealed on the device — plus HID-HOTP that types a code as keyboard input when you touch the key.

What it is

Token2 keys can store OATH-style OTP entries directly on a hardware applet, read over CCID/NFC or USB-HID. Unlike an authenticator app, the seeds are sealed on the secure element: keyroost provisions them with an encrypted write and reads live codes, but the secret material cannot be exported.

Adding & reading codes

In the On-device OTP tab, choose + Add entry, then enter the issuer/app, account and Base32 secret and pick TOTP (time-based) or HOTP (counter-based), the algorithm, digits, and — for TOTP — the period (commonly 30 or 60 seconds). TOTP entries show a live code with a countdown ring that refreshes when the window rolls over. Entries marked require touch show a Read button — press it and touch the key to reveal the code.

Importing codes from an authenticator app

keyroost can add every code in an authenticator export to the key's OTP storage in one step: an Aegis export (plain or encrypted), a 2FAS export, a text file with one otpauth:// URI per line, or a QR screenshot, including a Google Authenticator export. In the On-device OTP tab, click Import file… (or drop the file on the window); the Import authenticator codes dialog shows what each entry will become before anything is written. On the command line:

keyroostctl otp import -i export.json --dry-run   # show what would happen; writes nothing
keyroostctl otp import -i export.json             # asks once, then adds the codes

An entry whose app and account are already on the key is left as is (exists); --replace replaces it. An entry the key can't store is skipped, with the reason. keyroost asks once, writes one code at a time, and stops at the first error, saying what was added and what wasn't. What the OTP storage takes:

When both the export's password and the OTP PIN are piped in, the export's password (--file-password stdin) is the first line and the PIN the second. Import into OTP storage follows Token2's published OTP specification.

HID-HOTP (HOTP-on-touch)

HID-HOTP is a single dedicated slot that makes the key act like a keyboard: touch it outside any session and it types a fresh HOTP code wherever your cursor is — handy for systems that accept a typed one-time code.

k

HID-HOTP types over the key's keyboard (HID) interface, so that interface must be enabled. keyroost shows an Enable HID-HOTP / Disable HID-HOTP item in the pane's … menu; because it reconfigures the key's USB interfaces you must type the phrase change interface to confirm, and the change needs a re-plug to take effect. keyroost refuses any change that would leave fewer than two interfaces enabled, so you can't lock yourself out.

Provisioning

From the pane's … (more actions) menu choose Configure HID-HOTP…, enter the Base32 secret, choose 6 or 8 digits, and set the typing options: Send Enter (append Return), Long touch (2s) (require a two-second press), Numeric keypad (type with keypad scancodes). Save, and the card shows the slot as configured.

Changing options without re-entering the seed

Once a seed is provisioned you can change the typing options without re-entering the secret: reopen the form, leave the secret field blank, and press Save. The seed slot is left untouched (digit length can only change as part of a seed write, so it is grayed out until you type a new secret). To replace the seed, enter a new secret and Save; to remove it, use Clear slot.

Putting codes behind a PIN (R3.4+)

Keys running the R3.4 OTP applet can require a PIN before they hand out any codes. Set one and the entry list stays sealed until you unlock it; the key keeps the window open for a few minutes and then closes it again. Older keys simply don't have the feature — they answer the capability question with "no such command", and everything on this page works exactly as it always did.

In the GUI, the On-device OTP tab shows an unlock prompt in place of the list, and the pane's … menu grows Set PIN…, Change PIN…, Remove PIN… and Lock now.

On the command line:

keyroostctl otp pin status      # is a PIN set, and how many tries are left
keyroostctl otp pin set         # protect this key (asks for the new PIN twice, hidden)
keyroostctl otp pin verify      # open the read window for this connection
keyroostctl otp pin change      # asks for the current PIN, then the new one
keyroostctl otp pin clear       # back to unprotected
keyroostctl otp list            # asks for the PIN when the key needs it; same for add and delete
keyroostctl otp pin change --pin stdin --new-pin stdin   # in a script: current PIN on line 1, new on line 2

The PIN comes from a hidden prompt in a terminal or, in a script, from --pin env:NAME (an environment variable) or --pin stdin — never from the command line, where it would land in your shell history and in the process list. list, add and delete all take --pin.

Unlocking with a fingerprint (Bio3)

On a Bio3 key with at least one fingerprint enrolled, a fingerprint touch can release the protected codes as an alternative to entering the PIN. Fingerprint enrollment itself is done through the key's FIDO2 fingerprint setup, not here; keyroost turns the OTP-side option on or off and unlocks with it. Turning it on or off needs the current PIN; turning it on also needs an enrolled fingerprint.

keyroostctl otp fingerprint status        # is fingerprint unlock available, and is it on
keyroostctl otp fingerprint enable        # turn it on (asks for the current PIN)
keyroostctl otp fingerprint disable       # turn it off (asks for the current PIN)
keyroostctl otp list --unlock fingerprint # touch the sensor to unlock, then list
keyroostctl otp list --unlock auto --pin env:OTP_PIN   # try the fingerprint, fall back to that PIN

When fingerprint unlock is on, the GUI's unlock prompt shows a fingerprint-touch button alongside the PIN field, and either one opens the read window.

A blocked PIN costs every code on the key

There is no PIN reset. Wrong attempts count down a retry counter, and once it reaches zero the applet refuses the PIN for good — the only way back is keyroostctl otp reset, which destroys every OTP entry on the key. Keep a record of the PIN somewhere you trust before you set one.

What the PIN does and does not protect

The PIN never travels in the clear: host and key agree a session key first, and the PIN is only ever sent encrypted under it. The key enforces the retry counter itself, so guessing costs attempts on the key, not on the host. Treat the PIN as protection against someone picking up your key; what it protects against beyond that is a property of the key's firmware rather than of keyroost, and Token2 is the authority on it.

Command line

keyroostctl otp list                  # stored entries
keyroostctl otp code --account <name>  # read a live code
keyroostctl otp info                   # interface states & slot status
keyroostctl otp import -i export.json  # add every code in an authenticator export
keyroostctl otp button set --digits 6                    # provision HID-HOTP (asks for the base32 seed, hidden)
keyroostctl otp interface --fido --ccid --keyboard        # enable interfaces (re-plug after)

Some keys answer otp info with a short configuration block, which doesn't include the on-device OTP and HOTP-on-touch details; keyroost then shows those three rows as unknown rather than guessing.

Every otp subcommand accepts --transport auto|hid|ccid (default auto: over USB first — through the key's FIDO interface, using Token2's documented vendor command, then the older HID framing — with CCID/NFC as the fallback). Token2 models without HOTP-over-HID ship with that HID channel disabled by design; if a command still reports the key declining over USB, --transport ccid is the path, with the smart-card service (pcscd) running or the key on a reader.

With several Token2 keys connected, a terminal shows a numbered list to pick from; a script names one with --device (or --reader / --path). delete, reset, button clear, and button set on a key that already has a button seed name the key and ask y/N first; a script, or a command whose seed or PIN is piped in, adds --yes. interface asks you to type reconfigure interfaces at the terminal, or takes --yes.

Authoritative resources

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