Core Concepts

  1. 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
  2. Phishing Resistance

    • Keys verify the origin (domain/service) cryptographically
    • Won’t authenticate to lookalike sites
    • Challenge-response tied to the legitimate service identifier
  3. 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
  4. User Presence Verification

    • Physical interaction required (touch, button press)
    • Prevents remote/background use by malware
    • Proof that user authorized the operation
  5. 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:

  1. The private key (not really a file ­- stored on the token)
  2. The public key
  3. 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
  • 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 + metadata
  • id_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:

  1. Signature - Sign documents, git commits
  2. Encryption - Decrypt files/emails (hybrid: encrypts random AES key, AES encrypts file)
  3. 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
> quit

Make 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       # Decrypt
SSH (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@server

Status / 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