HIDRAW
NODE
The kernel sees a USB-style token through /dev/uhid.
Open-source security key/ FIDO2 · CTAP2 · Linux
uKey makes Linux see your Android phone as a virtual FIDO2 security key. No dongle. No cloud relay. Hardware-backed credentials over local loopback.
01 / the idea/ one less thing
Your phone already has a secure element, a fingerprint sensor, and a battery. uKey asks a simple question: why buy, carry, and lose a separate USB key just to prove who you are on Linux?
The answer is not “trust us.” It is a virtual HID device, the same CTAP2 protocol a hardware key speaks, and a key that stays inside Android's secure hardware. What works today sits right next to what is in active progress.
Read the public status ledgerThe trade-off, in plain language
Your phone is the token. The host sees a normal hidraw device.
Android Keystore generates the P-256 key. The daemon only sees public keys and signatures.
Browser, SSH, and PAM are in active development. The current status is documented below.
02 / the handoff/ laptop ↔ phone
A Python daemon creates a virtual HID device. Standard FIDO2 tools do the rest. The phone holds the key and asks for a fingerprint. Switch the scene to see the three CTAP2 commands.
Discover
The host sees a FIDO_2_0 authenticator. No approval is needed for getInfo.
Illustrative UI · no live hardware attached
The daemon opens /dev/uhid and asks the kernel for a HID device. Linux creates a hidraw node. No custom kernel module.
64-byte HID reports carry CBOR requests and signatures. The daemon handles channel IDs, fragmentation, keepalive, and cancellation.
The Android app prompts for a strong biometric, then asks Android Keystore for an ES256 signature. The private key never crosses the bridge.
03 / what works/ STATUS.md, not marketing
Every card that says “working” maps to a command, test, or file in the repository. All in-progress items live in the status ledger.
The daemon asks /dev/uhid for a USB HID device. Linux creates /dev/hidraw6; fido2-token -L lists it like a physical security key.
INIT, PING, CBOR, CANCEL, and KEEPALIVE across 64-byte reports, with channel IDs and fragmentation. The kernel-facing edge is real CTAPHID.
Android Keystore generates a P-256 key. StrongBox is preferred, TEE is the fallback, and software-backed keys are rejected. Every assertion asks for a strong biometric prompt.
Hardware Enclave · StrongBox/TEE hardware-backed key protection.
getInfo, makeCredential, getAssertion. FIDO_2_0, ES256, packed self-attestation, and non-resident credentials only.
A single-use six-digit PIN issues a 256-bit device token. Lockout, persistence, and revocation are covered by tests.
Zero-PIN auto-connect active after first pairing · revoke anytime.
Credential ID, RP ID, and public key persist in ~/.config/ukey/credentials.json. The daemon never sees private key material.
Loopback-only bridge. Auto-lock off. Config mode 0600. Direct loopback via adb reverse is the default.
The local console shows a pairing PIN, lists paired phones, revokes tokens, and pauses assertion approval. It binds to loopback only. No hidden relay.
04 / the control room/ laptop-side console
The daemon serves a small console on 127.0.0.1:8444. It shows a single-use pairing PIN, lists paired phones, revokes a token, and pauses assertion approval. It binds to loopback, not the network.
Enter it in the Android app once. The phone stores a device token; no PIN after that.
Revoking drops that phone on its next connection.
05 / under the hood/ five layers
Follow a cryptographic challenge from standard Linux FIDO2 tools down into the phone's hardware secure element. 100% local loopback ensures private keys never leave physical silicon.
fido2-token, fido2-cred, fido2-assert on the Linux host.
The daemon creates a UHID device; the kernel exposes a /dev/hidraw node.
Python daemon for framing, CBOR, credential metadata, pairing, and the local settings console.
Direct localhost loopback via adb reverse. Fast, low-latency inter-process communication with zero cloud relays.
Kotlin client, per-use BiometricPrompt, then a P-256 signature from AndroidKeyStore.
crosses the bridge
CBOR command payloads, public keys, and raw 64-byte ECDSA signatures converted to DER at the CTAP2 edge.
never crosses it
The FIDO2 private key is generated in AndroidKeyStore and is not exported. Biometric data stays on the phone.
stored on disk
Credential ID, RP ID, and public key. Unencrypted; readable by the local user and root. Not a secret, but integrity-sensitive.
06 / the public ledger/ last verified 2026-11-04
This is a summary of docs/STATUS.md. The first column is what works today; all other items are in active development. Verified on Fedora 43 with libfido2 1.15.0.
Evidence lives in tests/, daemon/, and the commands in STATUS.md.
In progress means actively implemented with test coverage underway.
Active engineering across protocol depth, hardware isolation, and host workflows.
07 / run it/ fedora 43 · libfido2 1.15.0
The verified path is a Fedora machine with /dev/uhid, Python 3.10–3.12, and libfido2 installed. The installer sets up udev permissions, a Python virtual environment, and a systemd user service with safe defaults.
android/.Expected from step 3: a hidraw node, then FIDO_2_0 / ES256 details. The node name is assigned by the kernel and may differ. No success output is fabricated here.
08 / the invitation/ bring a phone
Explore the project. Read the ledger, test the working CTAP2 flow, or follow development on GitHub.
Open Source · GPL-3.0