A software-based FIDO2/WebAuthn passkey authenticator for Linux. This project lets you use passwordless authentication (passkeys) on websites and applications without needing a physical USB security key.
This software implements the W3C WebAuthn specification and CTAP2 protocol, allowing your Linux system to act as a passkey authenticator.
- Testing and Development: Test FIDO2/WebAuthn implementations without physical hardware. Supports various attestation formats for compatibility testing
- Daily Use: Use passkeys for authentication on Linux systems/applications which support USB FIDO2/passkey authenticators.
Note: This is experimental software. Use at your own risk, especially for production systems.
- Python: 3.9 or higher
Automatically installed with pip:
asn1 >= 2.2.0cryptography >= 38.0.1cbor2 >= 4.1.2PyJWT >= 0.6.1
Install via pip:
pip3 install soft_fido2from soft_fido2 import Fido2Authenticator
# Create an authenticator instance
authenticator = Fido2Authenticator()
# Register with a website (attestation/registration)
attestation_options = {
"rp": {
"id": "www.myrp.ibm.com",
},
"user": {
"id": "rOIpHRr9St-YqugsfyZgAw",
"name": "testuser",
"displayName": "testuser"
},
"timeout": 60000,
"challenge": "Vi6gvN2yIvNRL9KVwo8FtR-fH3gR92LwCtneQueyawY",
"excludeCredentials": [],
"extensions": {},
"authenticatorSelection": {
"userVerification": "preferred"
},
"attestation": "direct",
"pubKeyCredParams": [
{
"alg": -7,
"type": "public-key"
}
],
}
attestation_response = authenticator.credential_create(attestation_options, atteStmtFmt='packed-self')
print(json.dumps(attestation_response, indent=4)) # print not required but useful for debugging
rp_response = requests.post("https://www.myrp.ibm.com/attestation/result",
json=attestation_response)
##Assertion
assertion_options = {
"rpId": "www.myrp.ibm.com",
"timeout": 60000,
"challenge": "kI9SKJRxv4zpICnG1Ls9FMwQ4t4Zq6t8HqKAJKzeyXI",
"extensions": {},
}
# Get assertion (authentication)
auth_response = authenticator.credential_request(assertion_options)
print(json.dumps(assertion_response, indent=4))For a simple testing environment, you can use the python-fido2 library which provides a complete FIDO2 server implementation:
from soft_fido2 import Fido2Authenticator
from fido2.server import Fido2Server
from fido2.webauthn import PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity
# Initialize FIDO2 server
rp = PublicKeyCredentialRpEntity(id="example.com", name="Example RP")
server = Fido2Server(rp)
# Create user
user = PublicKeyCredentialUserEntity(
id=b"user_id_123",
name="[email protected]",
display_name="Test User"
)
# Registration (Attestation)
attestation_options, state = server.register_begin(user)
attestation_options = dict(attestation_options)['publicKey']
# Create authenticator and generate credential
authenticator = Fido2Authenticator()
attestation_response = authenticator.credential_create(attestation_options)
# Verify registration with server
response = {
'id': attestation_response['id'],
'rawId': attestation_response['rawId'],
'response': {
'clientDataJSON': attestation_response['response']['clientDataJSON'],
'attestationObject': attestation_response['response']['attestationObject']
},
'type': 'public-key'
}
auth_data = server.register_complete(state, response)
print(f"Registration successful! Credential ID: {auth_data.credential_data.credential_id.hex()}")
# Authentication (Assertion)
assertion_options, state = server.authenticate_begin()
assertion_options = dict(assertion_options)['publicKey']
# Generate authentication response
assertion_response = authenticator.credential_request(assertion_options)
# Verify authentication with server
response = {
'id': assertion_response['id'],
'rawId': assertion_response['rawId'],
'response': {
'clientDataJSON': assertion_response['response']['clientDataJSON'],
'authenticatorData': assertion_response['response']['authenticatorData'],
'signature': assertion_response['response']['signature']
},
'type': 'public-key'
}
server.authenticate_complete(state, [auth_data.credential_data], response)
print("Authentication successful!")- Create a directory for your passkey files:
mkdir -p ~/.fido2
export FIDO_HOME="$HOME/.fido2"- The authenticator will automatically create encrypted passkey files in this directory when you register with websites.
Note
The authenticator requires the FIDO_HOME environment variable to be set to read and write private key files.
# Create a registration response
python3 -m soft_fido2.authenticator attestation packed-self '{
"rp": {
"id": "www.myrp.ibm.com",
"name": "ISAM_Unit_test"
},
"user": {
"id": "3RH-c7d8Ss60BKau7mLKXA",
"name": "testuser",
"displayName": "testuser"
},
"timeout": 60000,
"challenge": "mjqlXDT4RySLMyRCEePZgHpbgRCkFq9Gip4apBxcvTg",
"excludeCredentials": [],
"extensions": {},
"authenticatorSelection": {
"userVerification": "preferred"
},
"attestation": "direct",
"pubKeyCredParams": [
{
"alg": -7,
"type": "public-key"
}
]
}'Response:
{
"id": "EyOlQBLvCZUK96Z9DpCKYBw_aLOh4FikSd3h-1fKukk=",
"rawId": "EyOlQBLvCZUK96Z9DpCKYBw_aLOh4FikSd3h-1fKukk=",
"response": {
"clientDataJSON": "eyJvcmlnaW4iOiAiaHR0cHM6Ly93d3cubXlpZHAuaWJtLmNvbSIsICJjaGFsbGVuZ2UiOiAiVmk2Z3ZOMnlJdk5STDlLVndvOEZ0Ui1mSDNnUjkyTHdDdG5lUXVleWF3WT0iLCAidHlwZSI6ICJ3ZWJhdXRobi5jcmVhdGUifQ==",
"attestationObject": "o2hhdXRoRGF0YVkBbi-RrhkzFXpmZDWmVjlcmnlaWE_ET4cAHsNcOr-craCzRQAAAAAAAAAAAAAAAAAAAAAAAAAAACATI6VAEu8JlQr3pn0OkIpgHD9os6HgWKRJ3eH7V8q6SaRhMQNhMzkBAGItMVkBANNSB4BmS7RVWYwmuTyQmkmOZjiULIEgU_YpmgYX2yxTDgwf36TEwZDuoq-dfJiKGyPux5hnPSNia0iYGR8ABtO5pt9Ay5fHiHQ9Io5qcXw29gm8VPdHJhvcc0hMtctTCWy87QXiaI85MP-Uxd6fdEcGySnmhlUBjR5REJY89bql4BYLoK8wR90bohppGT0Dxh3kwY6QpXdZFVek2aGKA7YF4IM0lquRqMSvy9b_j2tl7NvNcoAU_-Kv-UufpyFqvWn1psjUMFyUvTBeP5dH_VWuuIINnbrgYuloei3IlA6DjIu7dvMuExXpFTTbILnstvOJGkrofboB8ELPnYK87P1iLTJEAAEAAWNmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZzkBAGNzaWdZAQBPiPQ22-D-hqHKBDGtp6qKo8PuIttaD9qvXLU6IsfYVK9xUban1teHTqfCZ6bvubnSQc7SzR-DmrAGh4GvQA38ag__W-3uWQ3x2el_dvIWd5fZRtbYuf0n7v4WCIHru79AyIaNszECOIZu--0QoWRbrmcpjgsDQbS6Rm3eqqczKAWHUWAJuKtCp1Evv1V3ChYSmpMIKBTvDmOltF1YncY6goCt-Xa3auWm9VwbXi6LH_wAtSWCLrdyp6VcIS8n7w9m7fTiGALIi_y1xaiVJz5U5rYlHpTElKTvI4ceO23mlEqgi_O9Pfqg8dA1ejXxpc4yvTTMaihbZq_vtEgMup4h"
},
"type": "public-key",
"getClientExtensionResults": "oA==",
"nickname": "some_name"
}# Create an authentication response
python3 -m soft_fido2.authenticator assertion '{
"rpId": "www.myrp.ibm.com",
"timeout": 60000,
"challenge": "kI9SKJRxv4zpICnG1Ls9FMwQ4t4Zq6t8HqKAJKzeyXI",
"extensions": {},
}'Response:
{
"id": "EyOlQBLvCZUK96Z9DpCKYBw_aLOh4FikSd3h-1fKukk=",
"rawId": "EyOlQBLvCZUK96Z9DpCKYBw_aLOh4FikSd3h-1fKukk=",
"response": {
"clientDataJSON": "eyJvcmlnaW4iOiAiaHR0cHM6Ly93d3cubXlpZHAuaWJtLmNvbSIsICJjaGFsbGVuZ2UiOiAia0k5U0tKUnh2NHpwSUNuRzFMczlGTXdRNHQ0WnE2dDhIcUtBSkt6ZXlYST0iLCAidHlwZSI6ICJ3ZWJhdXRobi5nZXQifQ==",
"authenticatorData": "L5GuGTMVemZkNaZWOVyaeVpYT8RPhwAew1w6v5ytoLMFAAAAAA==",
"signature": "Tn1J7kTWVL_MmSVimB95r7MDhG8T18pm-CD7TQn5dsbcTec6M8E_4-TFS-U3xto6bYlmciw8YYXpINCag0KetdnCMhm0D23ElcUGcEbdJmpzuMdotjW6AZRnLMe6aZU7uSyzwvcustYeKlAtSziSAw7qHL4ucnJYQZhsaCpya325UgpNshAHXcG3an_nRbogvKd__zjg3Fr-2qltP8r9CneuOSpphnBTWTmNk8cC16Nluhi81rugjlMdDgP6_pyYcpxSR1FVN_fJnnqmwRyundR29C-SCe3-NGHcgKOdeZf6izpw1FXfET4LRKpxoPiIApWLGb7tg6jIVQieT_QXsQ=="
},
"type": "public-key"
}The FIDO_HOME directory stores your encrypted passkey files (.passkey files). Each file contains:
- Private keys for authentication
- Credential metadata
- User information
These files are encrypted and protected by your system.
export FIDO_HOME="$HOME/.fido2"Skip user presence checks during authentication (for testing only).
export SOFT_FIDO2_SKIP_UP=trueSet logging level: DEBUG, INFO, WARNING, ERROR
export SOFT_FIDO2_DEBUG_LEVEL=DEBUGLog file path (relative to FIDO_HOME). Defaults to stdout.
export SOFT_FIDO2_LOG_FILE=authenticator.logAdvanced users can use the soft-fido2 module to provide passkey authentication to
applications that support USB HID authenticators.
For system-wide passkey support, integrate the authenticator as a virtual USB device using UHID (User-space HID).
- UHID kernel module
- Root or appropriate permissions for
/dev/uhid - install soft dependencies
- Qt6
- PyQt6
- notify-send
- Configure UHID permissions:
# Load UHID module at boot
echo 'uhid' | sudo tee /etc/modules-load.d/uhid.conf
# Create uhid group
sudo groupadd uhid
sudo usermod -aG uhid $USER
# Set permissions
echo 'KERNEL=="uhid", GROUP="uhid", MODE="0660"' | sudo tee /etc/udev/rules.d/10-uhid.rules
# Apply changes
sudo udevadm control --reload-rules && sudo udevadm trigger- Create encryption key:
mkdir -p $HOME/.fido2
openssl ecparam -name prime256v1 -genkey -noout -out $HOME/.fido2/platform.key- Install as systemd user service:
# Create virtual environment in /opt
sudo mkdir -p /opt/soft_fido2
sudo chown $USER:$USER /opt/soft_fido2
virtualenv /opt/soft_fido2
/opt/soft_fido2/bin/python -m pip install --upgrade pip soft_fido2
# Create passkey storage directory
mkdir -p $HOME/.fido2
# Create environment file
echo "FIDO_HOME=${HOME}/.fido2" > /opt/soft_fido2/passkey.env
# Create user service directory
mkdir -p ~/.config/systemd/user
# Create systemd user service
tee ~/.config/systemd/user/passkey.service > /dev/null <<'EOF'
[Unit]
Description=Software FIDO2 Passkey Authenticator
PartOf=graphical-session.target
After=graphical-session.target
[Service]
Type=simple
ExecStart=/opt/soft_fido2/bin/python -m soft_fido2
Restart=on-failure
RestartSec=5
EnvironmentFile=/opt/soft_fido2/passkey.env
TimeoutStopSec=10
KillMode=mixed
[Install]
WantedBy=graphical-session.target
EOF
# Enable and start user service
systemctl --user daemon-reload
systemctl --user enable passkey
systemctl --user start passkey
# Check service status
systemctl --user status passkeyImportant Notes for User Services:
- User services run in your graphical session - they start when you log in and stop when you log out
- Requires
/dev/uhidaccess - ensure you're in theuhidgroup (log out/in after adding) - Service management commands:
- Check status:
systemctl --user status passkey - View logs:
journalctl --user -u passkey -f - Stop service:
systemctl --user stop passkey - Restart service:
systemctl --user restart passkey - Disable autostart:
systemctl --user disable passkey
- Check status:
Troubleshooting User Service Issues:
- GUI dialogs don't appear: Ensure
graphical-session.targetis active:systemctl --user list-units --type=target | grep graphical - Service times out on stop: The service includes
TimeoutStopSec=10for graceful shutdown - Permission denied on /dev/uhid: Run
groupsto verify you're in theuhidgroup, then log out and back in
- Verify the authenticator:
# Check if the HID device is registered
hexdump -C "/sys/bus/hid/devices/$(ls /sys/bus/hid/devices | grep 1337:1337)/report_descriptor"Expected output:
00000000 06 d0 f1 09 01 a1 01 09 20 15 00 26 ff 00 75 08 |........ ..&..u.|
00000010 95 40 81 02 09 21 15 00 26 ff 00 75 08 95 40 91 |.@...!..&..u..@.|
00000020 02 c0 |..|
For remote or networked authenticator access:
- Start the USB/IP server:
python -m soft_fido2.hid_device- Connect from client:
# List available devices
usbip list -r 127.0.0.1
# Attach device
sudo modprobe vhci-hcd
sudo usbip attach -r 127.0.0.1 -b 1-1.1The util/ directory contains helper scripts:
./util/generate_passkey.shCreates a new passkey file in $FIDO_HOME.
python util/manage_creds.pyView and manage resident credentials (passwordless authentication).
./util/verify_passkey.sh <passkey_file>Verify the integrity of a passkey file.
# Set up environment
export FIDO_HOME="$HOME/.fido2"
mkdir -p $FIDO_HOME
# Create virtual environment
virtualenv $FIDO_HOME
$FIDO_HOME/bin/python -m pip install --upgrade pip
# Install development dependencies
$FIDO_HOME/bin/python -m pip install -r dev-requirements.txt
# Build and install
export GITHUB_RUN_NUMBER=9999
$FIDO_HOME/bin/python -m build
$FIDO_HOME/bin/python -m pip install dist/soft_fido2-*-py3-none-any.whl
# Run the authenticator
$FIDO_HOME/bin/python -m soft_fido2# Run unit tests
./tests/unit_test.sh
# Run scenario tests
./tests/scenario_test.sh"FIDO_HOME not set" error:
export FIDO_HOME="$HOME/.fido2"
mkdir -p $FIDO_HOMEPermission denied on /dev/uhid:
- Ensure you're in the
udevgroup - Log out and back in after adding yourself to the group
- Check udev rules are properly configured
No system tray icon on GNOME:
- Install the AppIndicator extension
- Restart GNOME Shell (Alt+F2, type 'r', press Enter)
Passkey not recognized by browser:
- Ensure the UHID service is running:
systemctl status passkey - Check the HID device is registered:
ls /sys/bus/hid/devices/ | grep 1337 - Verify browser supports WebAuthn (Chromium, Firefox, Edge all support it)
- Passkey files are encrypted but stored on disk
- The
SOFT_FIDO2_SKIP_UPoption bypasses user checks - use only for testing - For production use, ensure proper file permissions on
$FIDO_HOME - This is experimental software - review the code before using for sensitive accounts
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
For issues, questions, or contributions, please use the project's issue tracker.