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.

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 greyed 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 set-pin --pin-stdin     # protect this key (PIN on stdin, never argv)
keyroostctl otp verify --pin-stdin      # open the read window for this connection
keyroostctl otp change-pin --pin-stdin  # current PIN on line 1, new PIN on line 2
keyroostctl otp remove-pin --pin-stdin  # back to unprotected
keyroostctl otp list --pin-stdin        # unlock in passing; same for add and delete

The PIN is read from stdin (--pin-stdin) or from a named environment variable (--pin-env) — never from the command line, where it would land in your shell history and in the process list. list, add and delete all take both flags.

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 erase-all, 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 get --account <name>   # read a live code
keyroostctl otp config                 # interface states & slot status
keyroostctl otp button-hotp --seed-stdin --digits 6   # provision HID-HOTP (seed via stdin)
keyroostctl otp interface --fido --ccid --keyboard    # enable interfaces (re-plug after)

Every otp subcommand accepts --transport auto|hid|ccid (default auto: USB-HID first, CCID/NFC fallback). Recent Token2 models ship with the HID channel disabled by design and carry OTP over CCID — if a command reports the key declining over USB-HID, --transport ccid is the path, with the smart-card service (pcscd) running or the key on a reader.

Authoritative resources