Core Concepts
-
Asymmetric Cryptography Foundation
- Security keys use public-key cryptography (private key never leaves device)
- Private key performs cryptographic operations; public key stored by the service
- Prevents key theft via network interception
-
Phishing Resistance
- Keys verify the origin (domain/service) cryptographically
- Won’t authenticate to lookalike sites
- Challenge-response tied to the legitimate service identifier
-
Hardware-Bound Secrets
- Cryptographic material stored in tamper-resistant hardware
- Extraction extremely difficult even with physical access
- Unlike passwords/OTPs that exist in transmittable form
-
User Presence Verification
- Physical interaction required (touch, button press)
- Prevents remote/background use by malware
- Proof that user authorized the operation
-
Challenge-Response Protocol
- Service sends unique challenge
- Key signs it with private key
- Service verifies signature with stored public key
- Prevents replay attacks
FIDO2
Overview
FIDO2 is an authentication standard that enables passwordless login using cryptographic keys. It consists of:
- WebAuthn (W3C standard) - browser API for authentication
- CTAP (Client-to-Authenticator Protocol) - communication between devices and authenticators (security keys, biometrics)
Use Cases
- Passwordless login to websites/apps
- Two-factor authentication (2FA)
- Phishing-resistant authentication (keys bound to specific domains)
- Works with: USB security keys, platform authenticators (Face ID, Windows Hello), mobile devices
Setup
macOS: brew install libfido2
Arch: sudo pacman -S libfido2
Setup PIN, thumprints and more using fido2-token or at chrome://settings/securityKeys
Info / Usage
Check if FIDO2 token is plugged in (Linux): fido2-token -L
Get detailed token info: fido2-token -I /dev/hidraw0
Use chrome://settings/securityKeys
SSH via ED25519-SK
ed25519-sk = Ed25519 algorithm + FIDO2 protocol
Advantages
- Phishing-resistant: Cryptographic proof of server identity (unlike passwords/TOTP)
- No private key exfiltration: Malware can’t steal what’s not on disk
- Physical presence proof: Attacker needs your token + touch action
- Biometric on token: Adds second factor (PIN + fingerprint)
How to use
Note: If Apple’s built-in OpenSSH doesn’t support ed25519-sk, use homebrew’s version instead: brew install openssh.
- Generate resident ed25519 security key:
ssh-keygen -t ed25519-sk -O resident - Download resident keys from token: `cd ~/.ssh && ssh-keygen -K
These two commands create files id_ed25519_sk(_rk) and id_ed25519_sk(_rk).pub .
id_ed25519_sk is only a key handle: it is needed to use the key but is useless without the token (see “How it works” below).
Once these files are present in .ssh/ and the token in plugged in, ssh can be used as usual:
ssh -i ~/.ssh/id_ed25519_sk user@server (.ssh/id_ed25519_sk is typically a default key path so shouldn’t even be necessary)
(not tested) SSH connection multiplexing (reuse connections, avoid repeated touches). In ~/.ssh/config:
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h:%p
ControlPersist 14400
How it works
Architecture
- Private key stays on hardware token - never touches your computer
- Public key is exported and added to servers (like normal SSH)
- Challenge-response authentication: Server sends challenge → token signs it → server verifies signature
- Physical presence required: You must touch the device to authenticate
Files and Resident mode
Three items:
- The private key (not really a file - stored on the token)
- The public key
- The private key handle (specific to -sk)
- A key handle is a unique identifier/pointer that tells the token “use THIS specific key”
- Safety deposit box analogy:
- The safety deposit box (token) holds the actual private key
- The key handle is like the box number + access code
- You need both the handle AND the physical token to access the private key
- Safety deposit box analogy:
- So both the private key and its handle are needed to use SSH
- The handle must always be stored locally on the machine (OpenSSH needs to know the key exists)
The difference between resident and non-resident keys is that resident keys also store the key handle + metadata on the token, which allows re-generating the key handle from the token itself using ssh-keygen -K. It makes resident keys more resilient to file loss, simpler to manage, and portable across computers.
ssh-keygen -K creates two files:
id_ed25519_sk- Contains the key handle + public key + metadataid_ed25519_sk.pub- Public key only (for servers’ authorized_keys)
OpenPGP
What It Is
OpenPGP is a smart card standard that stores cryptographic keys on hardware tokens. Unlike FIDO2 (authentication only), OpenPGP provides three key slots for different cryptographic operations.
How It Works
Core principle: Private keys never leave the device
- Device performs crypto operations internally (sign/decrypt/authenticate)
- You send data to device → device returns result
- Requires PIN to unlock operations
The three key slots and usage examples:
- Signature - Sign documents, git commits
- Encryption - Decrypt files/emails (hybrid: encrypts random AES key, AES encrypts file)
- Authentication - SSH login
Algorithms used (among others): ed25519 (signing/auth), cv25519/x25519 (encryption)
Setup
Initial setup
# Install (Arch) - if needed
sudo pacman -S gnupg pcscd
sudo systemctl enable --now pcscd
# Configure GPG for SSH - if needed (not tested)
echo "enable-ssh-support" >> ~/.gnupg/gpg-agent.conf
export GPG_TTY=$(tty)
export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)Generate keys on token
gpg --card-edit
> admin
> passwd # Change default PINs (User: 123456, Admin: 12345678)
> key-attr # Set to ECC → Curve 25519 for all 3 slots
> generate # Creates keys on device
> quitMake sure to backup the revocation certificate at ~/.gnupg/openpgp-revocs.d/\*.rev. Nothing else needs to be backed up as the public key can be re-generated from the token (just gpg --card-status, which auto-imports it).
Daily Usage
File encryption
gpg -e -r [email protected] file.txt # Encrypt
gpg -d file.txt.gpg > file.txt # DecryptSSH (not tested)
gpg --export-ssh-key [email protected] > ~/.ssh/id_gpg_token.pub
ssh-copy-id -i ~/.ssh/id_gpg_token.pub user@server
ssh user@serverStatus / troubleshooting
gpg --card-status # View token info
gpg -k / -K # List public / secret keys
ssh-add -L # Verify SSH key loaded
gpgconf --kill gpg-agent # Restart agent