A virtual CTAP2 authenticator for Linux with TPM 2.0-bound keys, biometric user verification, and enterprise attestation.
vauth creates a virtual FIDO2 security key via Linux uhid. Browsers see it as a hardware authenticator — register and sign in with passkeys using your face or a password, backed by keys that never leave your TPM.
- TPM 2.0-bound keys — private keys generated inside and never exported from the TPM
- Native face recognition — dlib-based face detection and 128-d encoding, compatible with howdy models
- Liveness detection — EAR (eye blink) and MAR (mouth movement) via 68-point landmarks, prevents photo spoofing
- Camera capture — V4L2 via nokhwa at ~27 fps (release build)
- GTK4 verification overlay — circle-masked camera feed, timeout ring, Caelestia theme colors (optional
gtk-overlayfeature) - Packed attestation — self-signed CA with x5c certificate chain for enterprise enforcement
- Privilege separation — drops from root to real user after init, retains only
CAP_DAC_READ_SEARCH - Audit logging — single JSONL file, every operation logged with RP, user, result, counter
- UV caching — (CID, RP ID)-bound, use-once, 10s TTL — no double-prompts, no attack window
- Lockout — configurable failure threshold and cooldown
- Credential management —
list,info,revoke,wipesubcommands - Monotonic counter — TPM NV counter for clone detection
- Linux with kernel uhid support
- TPM 2.0 (
/dev/tpmrm0) - Rust 1.91+
libpam(PAM development headers)tpm2-tss(TPM2 Software Stack)- V4L2-compatible webcam
The face engine requires dlib model files. If you have howdy installed, the models are already at /lib/security/howdy/dlib-data/.
Required models:
shape_predictor_5_face_landmarks.dat— face alignment for encodingdlib_face_recognition_resnet_model_v1.dat— 128-d face encodingshape_predictor_68_face_landmarks.dat— liveness detection (EAR/MAR)
To download the 68-point model (required for liveness):
wget http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
bunzip2 shape_predictor_68_face_landmarks.dat.bz2
sudo mv shape_predictor_68_face_landmarks.dat /lib/security/howdy/dlib-data/- howdy — provides dlib models and enrolled face data
zenity— GUI password dialog fallbackgtk4,libgtk4-layer-shell— for the face verification overlay (build with--features gtk-overlay)
# Headless (no GUI overlay)
cargo build --release
# With GTK4 face verification overlay
cargo build --release --features gtk-overlayThe binary is at target/release/vauth.
sudo cp dist/udev/99-vauth.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules
sudo udevadm trigger /dev/uhidEnsure your user is in the input group:
sudo usermod -aG input $USERsudo cp dist/pam.d/vauth /etc/pam.d/
# If using --face (native face verification), install the password-only PAM config:
sudo cp dist/pam.d/vauth-face /etc/pam.d/The default vauth PAM config tries howdy (face), then password. When --face is enabled, the daemon automatically uses vauth-face (password-only) since native face verification replaces howdy.
Generate a self-signed attestation CA and device certificate:
sudo vauth setup-attestationThe CA cert can be imported into your identity provider (e.g., Authentik) to enforce "only vauth" credentials. The cert is at:
~/.local/share/fidorium/attestation_ca.pem
Add to your ~/.config/hypr/hyprland.lua:
hl.window_rule({
name = "vauth-face-overlay",
match = { class = "GTK Application" },
float = true,
pin = true,
})sudo cp dist/systemd/vauth.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now vauth# PAM-only mode (default, proven path)
sudo vauth run -vv --audit-log /var/log/vauth/audit.jsonl
# Face verification + PAM fallback (opt-in)
sudo vauth run -vv --face --audit-log /var/log/vauth/audit.jsonlThe daemon starts as root (for TPM + uhid), then drops to your user. Open a browser, navigate to a WebAuthn-enabled site, and register a passkey.
With --face, the daemon tries native face verification (camera + liveness + identity) before falling back to PAM password. Face failures never block authentication — they silently fall through to the password dialog.
The vauth_verify binary tests the face recognition and liveness pipeline:
# Full liveness + identity verification
./target/release/vauth_verify
# Diagnostic mode — shows EAR/MAR per frame + identity
./target/release/vauth_verify --diagsudo vauth list # List all stored credentials
sudo vauth info <id-prefix> # Show credential details
sudo vauth revoke <id-prefix> # Delete a credential
sudo vauth wipe # Delete everything + reset TPM counterCredential IDs support prefix matching — vauth info 8d94 matches 8d945861f3a4....
vauth [OPTIONS] [COMMAND]
Commands:
run Start the authenticator daemon (default)
list List all stored credentials
info <ID> Show details of a specific credential
revoke <ID> Revoke (delete) a credential
wipe Delete all credentials and reset TPM NV counter
setup-attestation Generate attestation CA and device certificate
Options:
-v, --verbose Increase log verbosity (use -vv for debug)
--tpm-device <PATH> TPM device [default: /dev/tpmrm0]
--nv-index <HEX> TPM NV counter index [default: 0x01800100]
vauth run [OPTIONS]
Options:
--pam-service <NAME> PAM service name [default: vauth]
--audit-log <PATH> Audit log path [default: /var/log/vauth/audit.jsonl]
--max-uv-failures <N> Failures before lockout [default: 5]
--lockout-secs <N> Lockout duration [default: 300]
--face Enable native face verification (opt-in)
--face-model-dir <PATH> dlib model directory [default: /lib/security/howdy/dlib-data]
--face-threshold <F64> Match distance threshold [default: 0.6]
--face-liveness-secs <N> Liveness timeout [default: 5]
See THREAT_MODEL.md for a detailed analysis of what vauth does and does not defend against.
Key points:
- Private keys never leave the TPM
- Every signing operation requires fresh user verification
- Liveness detection prevents static image bypass (blink or mouth movement required)
- The daemon runs as an unprivileged user after initialization
- A compromised local OS can bypass all protections — no software authenticator can prevent this
Browser (WebAuthn JS API)
│ HID reports via /dev/uhid
▼
vauth daemon (unprivileged after init)
├── CTAPHID framing + dispatch
├── CTAP2 protocol (makeCredential, getAssertion, getInfo)
├── Face verification pipeline
│ ├── Camera capture (V4L2 via nokhwa, ~27 fps)
│ ├── Liveness detection (68-point landmarks → EAR/MAR)
│ ├── Identity verification (5-point landmarks → 128-d encoding)
│ └── GTK4 overlay (circle mask, timeout ring, theme-aware)
├── PAM fallback (password via zenity dialog)
├── UV cache (CID+RP bound, use-once, TTL)
├── Attestation signing (device cert + CA chain)
├── Audit logger (JSONL)
└── TPM 2.0 (key generation, signing, NV counter, sealing)
vauth is a fork of fidorium by Edgar Luque, which provided the foundational CTAP2/CTAPHID/TPM implementation. Licensed under MIT/Apache-2.0.
MIT OR Apache-2.0
- AUR PKGBUILD
- Install script
- Cross-browser testing (Chromium)
- FIDO Alliance conformance test vectors
- Hybrid transport / caBLE (QR code auth from other devices)
- Encrypted credential sync (portable passkey support)
- Platform authenticator integration (emerging Linux credential-provider APIs)
- seccomp filter (restrict syscalls post-init)
- TPM policy sessions (bind UV cryptographically to signing, not just type-level gate)