Skip to content

Token2 PIN+: bio-enrollment, on-device OTP, FIDO MDS, friendlier CTAP…#30

Closed
token2 wants to merge 1 commit into
framefilter:mainfrom
token2:token2-otp-pr
Closed

Token2 PIN+: bio-enrollment, on-device OTP, FIDO MDS, friendlier CTAP…#30
token2 wants to merge 1 commit into
framefilter:mainfrom
token2:token2-otp-pr

Conversation

@token2

@token2 token2 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Token2 PIN+: fingerprint management, on-device OTP, FIDO MDS, friendlier errors, and docs

Closes #29.

This delivers the two features announced in #29fingerprint enrolling/management on bio keys and MDS data on the Overview tab — and rounds out Token2 PIN+ support with on-device OTP (TOTP + HID-HOTP) management, a protocol-level fix to device-config reads, clearer CTAP2 error messages, and HTML docs for the new features.

Each commit is self-contained; the series applies in order on current main. Validated on a Token2 PIN+ FIDO2 key (AAGUID eabb46cc-…, PIN+ ) on Windows and Linux.


What's included

1. Fingerprint management (bio keys) — addresses #29

Enroll, rename, and delete fingerprints over CTAP2 authenticatorBioEnrollment, so a bio key can satisfy user verification by touch instead of the PIN.

  • GUI: a Fingerprints card in the Passkeys tab (appears only when the key reports biometric support), with live enrollment progress, rename, and delete.
  • CLI: fido-fingerprint-list, -enroll, -rename, -delete.
  • Fix: the CTAPHID keepalive timeout is extended for the duration of an enrollment, so long multi-touch captures no longer abort mid-way. Delete is made robust by re-deriving a fresh bio token per operation (the key invalidates the token after some biometric writes).

2. Device metadata (FIDO MDS) on the Overview tab — addresses #29

A compact card that looks up the key's AAGUID and shows the vendor name + icon, certification level (e.g. FIDO Certified L2) and date, protocol family, and supported versions.

  • Data comes from a bundled, slimmed projection of the official FIDO MDS3 BLOB — no network access at runtime.
  • The dataset is embedded at build time but also loaded from disk at startup if present, so packaged builds (AppImage / signed .exe / .dmg) can be refreshed without rebuilding. Lookup order: $KEYROOST_MDS_FILE -> platform config dir (~/.config/keyroost/, ~/Library/Application Support/keyroost/, %APPDATA%\keyroost\) -> next to the executable -> embedded.
  • tools/gen_mds_data.py regenerates the dataset from the live BLOB (or a local --blob file.jwt for offline use); hardened against the MDS endpoint's rate-limiting with a browser User-Agent and retry/backoff.

3. On-device OTP (TOTP + HID-HOTP) management

  • TOTP: live codes with a countdown ring that honours each entry's own period (30/60 s) and auto-refreshes when the window rolls over.
  • Touch-required entries: a Read button that triggers the touch and reveals the code (previously a dead "touch to view" label).
  • HID-HOTP (HOTP-on-touch): provision the slot, read its real status, and edit the typing options (Send Enter / long touch / numeric keypad). Saving with the secret field blank changes only the typing options and leaves the seed untouched; entering a secret writes/replaces it. Digit length is gated to seed writes (the firmware has no standalone command for it), and the control reflects that.
  • Interface toggle: enable/disable the keyboard-HID interface, guarded so at least two interfaces always stay enabled.
  • The OTP-tab secondary actions (transport selector, Configure HID-HOTP, interface toggle) are collected under a single overflow menu to keep the header uncluttered.
  • An on-device OTP card is also surfaced on the Overview tab when the key supports it.

4. Protocol fix: READ_CONFIG / GET_ECDH_PUBKEY were missing their Le byte

This is the substantive protocol contribution and it underpins #2/#3 working over PC/SC.

READ_CONFIG (80 C5 02 00) and GET_ECDH_PUBKEY (80 C5 01 00) were built without a trailing Le byte. Over PC/SC the device then answered READ_CONFIG with a 1-byte stub (61 01), so every config flag decoded as false — e.g. a provisioned HID-HOTP seed showed as "empty", and the pubkey read (the first step of any seed write, and the HID probe) returned a stub that forced a CCID fallback.

Building them as the spec's short case-2 form — 80 C5 02 00 <Le> and 80 C5 01 00 00, matching Token2's OTP-on-FIDO command manual — makes the device return the full 10-byte device-info block over CCID.

Hardware-confirmed: READ_CONFIG now returns 64 bytes and the HID-HOTP slot status reads correctly over CCID/NFC, with no USB-HID access or elevation required. A unit test locks both APDU forms. (Note: this change also sits on the seed-write path via the pubkey read — seed writes were re-verified and still succeed.)

A defensive has_config_byte() guard treats any short read as "status unknown" rather than reporting a misleading "empty".

5. Friendlier CTAP2 error messages

Opaque status codes now lead with a plain-English explanation, keeping the spec name + hex for reference. For example, a failed PIN change on a PIN+ key now reads:

the new PIN doesn't meet this key's complexity requirements — try a longer PIN or a different mix of characters … (CTAP2 status 0x37 CTAP2_ERR_PIN_POLICY_VIOLATION)

Hints are added for the PIN-domain codes users most commonly hit (0x31/0x32/0x33/0x34/0x35/0x36/0x3B/0x3C/0x2F). Touches only keyroost-ctap.

6. Linux OTP "stuck on Reading" fix

On Linux, when the keyboard-HID interface is enabled, the hidraw read() could block forever instead of falling back to CCID. The HID probe is now time-bounded so detection fails over cleanly.

7. Documentation

Native-template docs/fingerprint.html, docs/otp.html (with a #hid-hotp section), and docs/mds.html, wired into the site nav on every page. The in-app "?" help dots already point at these slugs.


Commit list

  1. Add CTAP2 bio-enrollment (fingerprint) management
  2. Show on-device OTP card on the Overview tab
  3. Fix Linux OTP probe blocking when keyboard-HID is enabled
  4. Add FIDO MDS card, OTP/HID-HOTP management, and READ_CONFIG Le fix
  5. Add plain-English hints to CTAP2 error messages
  6. Add fingerprint/OTP/MDS docs pages and site nav links

Testing

  • cargo fmt --all, cargo clippy --all-targets -- -D warnings, cargo test --workspace — clean.
  • Hardware (Token2 PIN+ FIDO2, fw 2.1.2): fingerprint enroll/rename/delete; HID-HOTP provision, options-only update, and clear; touch-required TOTP read; the READ_CONFIG fix (raw keyroostctl --debug otp config shows the full 64-byte block and HOTP-on-touch slot: configured); seed write re-verified after the pubkey APDU change.

framefilter added a commit that referenced this pull request Jun 18, 2026
Token2's PR #30 was CI-red on clippy::needless_return (token2otp.rs detect_debug)
and rustfmt (otp_pane.rs and friends). Fix both so the integrated branch is green;
the feature commit keeps Token2's authorship.
framefilter added a commit that referenced this pull request Jun 18, 2026
framefilter added a commit that referenced this pull request Jun 18, 2026
@framefilter

Copy link
Copy Markdown
Owner

Folded into the v0.6.0 release — your work is in as commit 7c76a6b with your authorship preserved, and it ships in v0.6.0 (fingerprint enroll → fido fingerprint-*, FIDO MDS in the GUI, on-device OTP, friendlier CTAP errors). During integration I re-nested the CLI under the new fido … groups and adapted it to the shared device model. Closing since the commits are already on main — thank you @token2, this was a substantial contribution. The CTAP 2.1 authenticatorConfig follow-up is tracked in #33.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add fingerprint management and MDS data

2 participants