Biometric FIDO2/U2F SSH Keys
Background
Smartcards and SSH
Smartcards have traditionally supported SSH public key authentication using two methods: PIV and PGP.
In PIV mode, you end up hacking around with opensc
to get ssh-agent
to use the certificate as a key.
This also doesn’t play well with gpg-agent
, which tends to acquire a lock on the smartcard.
In PGP mode, you have to ditch ssh-agent
and use gpg-agent
instead, which is a significantly degraded experience.
OpenSSH
As of OpenSSH version 8.0 you can use SSH keys to sign arbitrary data.
As of OpenSSH version 8.2 you can use SSH keys derived from the FIDO2 application on smartcards, using either Ed25519 or ECDSA.
YubiKeys
Yubico manufacture a few different smartcards (YubiKeys, from here on out). The YubiKey 5 series support all of the above modes, whereas the YubiKey Security Key only supports FIDO2 (since it’s designed just for 2FA on websites and so on). The Security Key is also significantly cheaper.
There’s also the YubiKey Bio, which has the same support as the Security Key, but has a fingerprint reader and comes in at a little over 3 times the price.
I held off on buying the Bio because I store my PGP key on my YubiKey and wanted to retain this functionality. But I finally decided to try it out, for several reasons:
- I can use it for SSH,
- I can use that SSH key for signing files and Git commits,
- I can secure the SSH key with a passphrase and a fingerprint,
- I expensed it to my employer,
- nobody sends me PGP-encrypted emails any more anyway 🙁.
Adding a Fingerprint
Out of the box, when I plugged it in it was flashing orange. I don’t know why.
ykman info Device type: YubiKey C Bio - FIDO Edition Serial number: 17244581 Firmware version: 5.5.6 Form factor: Bio (USB-C) Enabled USB interfaces: FIDO Applications FIDO2 Enabled OTP Not available FIDO U2F Enabled OATH Not available YubiHSM Auth Not available OpenPGP Not available PIV Not available
To add a fingerprint, we first have to set a PIN. The PIN can be alphanumeric (extended ASCII) and relatively long (63 characters).
ykman fido access change-pin Enter your new PIN: Repeat for confirmation:
Then add a fingerprint.
ykman fido fingerprints add "right-index" Enter your PIN: Place your finger against the sensor now... 4 more scans needed. Place your finger against the sensor now... 3 more scans needed. Place your finger against the sensor now... 2 more scans needed. Place your finger against the sensor now... 1 more scans needed. Place your finger against the sensor now... Capture complete.
We can then list the fingerprints.
ykman fido fingerprints list Enter your PIN: ID: c83b (right-index)
I remain optimistic that revealing which finger I’ve enrolled doesn’t put me at significantly greater risk of losing it. If your threat model includes this, rest easy knowing that the list is protected by the PIN.
Generating a Resident Ed25519 SSH Key
There are two types of FIDO2 SSH keys: resident and non-resident.
I’m using a resident key because the PIN/fingerprint access control is good enough for me and I value the ability to move the YubiKey between different devices.
We generate the key with ssh-keygen -t ed25519-sk
. This will fail if your YubiKey’s firmware is older than 5.2.3, before they supported Ed25519 curves.
If so, you can fall back to ecdsa-sk
, or get a new YubiKey (recommended).
The below command makes a key that requires not just user presence (a touch) but also verification. This means a PIN for normal non-biometric YubiKeys but, despite the ssh-keygen
documentation insisting otherwise, the Bio will accept a fingerprint to unlock the key.
verify-required
Require signatures made using this key indicate that the user was first verified. This option only makes sense for the FIDO authenticator algorithmsecdsa-sk
anded25519-sk
. Currently PIN authentication is the only supported verification method, but other methods may be supported in the future.
I also increase the key derivation rounds and add a comment.
ssh-keygen -t ed25519-sk -O resident -O verify-required -a 128 -C 17244581 Generating public/private ed25519-sk key pair. You may need to touch your authenticator to authorize key generation. Enter PIN for authenticator: Enter file in which to save the key (/home/fionn/.ssh/id_ed25519_sk): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/fionn/.ssh/id_ed25519_sk Your public key has been saved in /home/fionn/.ssh/id_ed25519_sk.pub The key fingerprint is: SHA256:2fl1x8+ACfUB2UA3eV5mXLJY5Wr04K93N7nH6iN4OoY 17244581 The key's randomart image is: +[ED25519-SK 256]-+ | .==*++| | ..=o**| | . . ==o| | o o = =.| | S o o * =| | . o =o| | . .. .=| | E + o .+*| | ..+ o+=*| +----[SHA256]-----+
Using the FIDO2 SSH Key
Now test it out. I usually use GitHub for this. Upload ~/.ssh/id_ed25519_sk.pub
, add
Host github.com User git PubkeyAuthentication yes IdentityFile ~/.ssh/id_ed25519_sk
to ~/.ssh/config
and try to connect.
ssh -T github.com Enter passphrase for key '/home/fionn/.ssh/id_ed25519_sk': Confirm user presence for key ED25519-SK SHA256:2fl1x8+ACfUB2UA3eV5mXLJY5Wr04K93N7nH6iN4OoY User presence confirmed Hi fionn! You've successfully authenticated, but GitHub does not provide shell access.
Then try to connect again and notice that ssh-agent
has cached the passphrase.
To use it on another machine, plug it into the new machine and run
ssh-keygen -K Enter PIN for authenticator: You may need to touch your authenticator to authorize key download. Enter passphrase (empty for no passphrase): Enter same passphrase again: Saved ED25519-SK key to id_ed25519_sk_rk
to download the key handles to the working directory. Move them to ~/.ssh/
and connect like usual.