tpm-fido is a FIDO2/WebAuthn authenticator for Linux that uses your system's TPM 2.0 and fingerprint reader to act as a virtual security key. It works with Chromium, Google Chrome, and Firefox — no browser extension required.
The daemon creates a virtual FIDO2 HID device via /dev/uhid that browsers discover automatically, just like a plugged-in YubiKey. Private keys are protected by the TPM and user verification is done through the fingerprint sensor.
- Lenovo ThinkPad P14s Gen 5 (AMD)
- Fingerprint reader: Synaptics 06cb:00f9 (via fprintd)
- TPM: AMD TPM 2.0 (
/dev/tpmrm0) - OS: Ubuntu 24.04+, Linux 6.14
Other laptops with a TPM 2.0 and an fprintd-compatible fingerprint reader should work as well.
- Virtual security key: Appears as a USB FIDO2 device to all browsers — no extension needed
- TPM-backed keys: Private keys never leave the TPM hardware
- Fingerprint verification: User presence and verification via fprintd
- PRF extension: hmac-secret support for deriving cryptographic material from credentials
- Resident keys: Discoverable credentials stored locally
- Multi-browser: Works with Chromium (snap), Firefox (snap), Google Chrome, and native builds
- System tray toggle: GNOME top bar icon to enable/disable the virtual key (e.g. to switch to a physical YubiKey)
- YubiKey auto-switch: Automatically disables the virtual key when a physical YubiKey is plugged in and re-enables it on removal
- Systemd service: Runs as a user service, starts on login
git clone https://github.com/mc256/tpm-fido2-thinkpad-linux.git
cd tpm-fido2-thinkpad-linux
./contrib/install-daemon.shThe installer will:
- Check prerequisites (Go, TPM, fprintd)
- Build a static binary and install it to
~/bin/tpm-fido - Install a systemd user service
- Set up udev rules and load the
uhidkernel module (requires sudo) - Detect installed browsers and connect snap interfaces as needed
- Start the virtual security key daemon
After installation, open your browser and test at webauthn.io.
- TPM 2.0: Your system must have
/dev/tpmrm0 - Fingerprint reader: Compatible with fprintd, with at least one finger enrolled
- Go 1.21+: Required to build from source
- Linux kernel:
uhidmodule (loaded automatically by the installer)
# Grant TPM access
sudo usermod -aG tss $USER
# Grant uhid access
sudo usermod -aG plugdev $USER
# Log out and back in for group changes to take effect
# Enroll a fingerprint (if not already done)
fprintd-enrollWhen run with --tray (the default in the systemd service), a fingerprint icon appears in the GNOME top bar. This lets you toggle the virtual FIDO2 device on and off without restarting the daemon — useful when you want to use a physical YubiKey instead.
- Active (filled fingerprint): virtual key is active, browsers will use it
- Disabled (slashed fingerprint): virtual key is off, plug in your YubiKey
The tray uses the D-Bus StatusNotifierItem protocol via fyne.io/systray (pure Go, no CGo or GTK). It works with Ubuntu's pre-installed gnome-shell-extension-appindicator.
When a physical YubiKey (Yubico VID 1050) is plugged in, the daemon automatically disables the virtual FIDO2 device so the browser uses the YubiKey. When the YubiKey is removed, the virtual device is re-enabled. This is controlled by a checkbox in the tray menu ("Auto-switch on YubiKey") and is on by default.
To start with auto-switch disabled:
tpm-fido --mode=daemon --tray --auto-switch=falseTo run without the tray icon (headless):
tpm-fido --mode=daemonBrowser (Chromium/Firefox/Chrome)
|
| /dev/hidrawN (FIDO2 HID, usage page 0xF1D0)
|
kernel HID subsystem
|
| /dev/uhid
|
tpm-fido daemon (systemd user service)
| |
v v
TPM 2.0 fprintd
The daemon runs outside any browser sandbox, so it has full access to the TPM and fingerprint reader. Browsers discover the virtual HID device through standard OS mechanisms — the same way they find a physical YubiKey.
If you prefer to install step by step instead of using the installer script:
# Build
CGO_ENABLED=0 go build -ldflags="-s -w" -o tpm-fido tpmfido.go
# Install binary
install -m 755 tpm-fido ~/bin/tpm-fido
# Install systemd service
install -m 644 contrib/tpm-fido.service ~/.config/systemd/user/tpm-fido.service
# Install udev rules and uhid module config (requires sudo)
sudo cp contrib/90-tpm-fido-uhid.rules /etc/udev/rules.d/
sudo cp contrib/uhid.conf /etc/modules-load.d/
sudo udevadm control --reload-rules && sudo udevadm trigger
sudo modprobe uhid
# Connect snap interfaces (if using snap browsers)
sudo snap connect chromium:u2f-devices # for Chromium snap
sudo snap connect firefox:u2f-devices # for Firefox snap
# Start the service
systemctl --user enable --now tpm-fidoOnce the service is running, any FIDO2/WebAuthn request from a browser will trigger a fingerprint prompt via a desktop notification. Touch your fingerprint sensor to approve.
# Check service status
systemctl --user status tpm-fido
# Follow logs
journalctl --user -u tpm-fido -f
# Restart after updates
systemctl --user restart tpm-fidoRegistration (MakeCredential):
- Browser sends a WebAuthn create request to the virtual HID device
- Daemon generates a P-256 key pair in the TPM, bound to the site's RP ID
- Fingerprint verification confirms user presence
- The public key and attestation are returned to the browser
Authentication (GetAssertion):
- Browser sends a WebAuthn get request with credential IDs
- Daemon loads the matching key from the TPM
- Fingerprint verification confirms user presence
- The TPM signs the assertion, which is returned to the browser
Key handles are a concatenation of the TPM child key's public/private blobs and a random seed. They can only be used on the same TPM that created them.
Resident credential metadata is stored at:
~/.local/share/tpm-fido/credentials.json
This file contains credential metadata only (user info, RP info, credential ID). Private keys remain in the TPM and are never written to disk.
Ubuntu ships Chromium and Firefox as snap packages, which run in a confined sandbox. The virtual FIDO2 device works with snaps because:
- AppArmor already allows
/dev/hidraw*access in the snap profiles - Device cgroup access is granted via custom udev rules that tag the virtual hidraw device with
snap_chromium_chromiumandsnap_firefox_firefox - The
u2f-devicessnap interface may need to be connected for sysfs descriptor access
The installer handles all of this automatically.
- Check the daemon is running:
systemctl --user status tpm-fido - Check a hidraw device exists:
ls -la /dev/hidraw* - Verify snap tags:
udevadm info /dev/hidrawN | grep TAGS - For snap browsers, ensure u2f-devices is connected:
snap connections chromium | grep u2f
sudo usermod -aG tss $USER
# Log out and back insudo usermod -aG plugdev $USER
# Log out and back in- Check fingerprints are enrolled:
fprintd-list $USER - Enroll a fingerprint:
fprintd-enroll - Check that fprintd is running:
systemctl status fprintd
- Ensure uhid module loads:
lsmod | grep uhid - If not:
sudo modprobe uhidand check/etc/modules-load.d/uhid.confexists
See LICENSE file.