YubiKeys are rugged security keys that can do FIDO2 two-factor authentication (2FA), PKCS and OpenPGP operations, all inside a small USB form factor.
Note that if you're looking for OpenPGP documentation, you're not exactly in the right place. There are documentation on using OpenPGP with a YubiKey here, but if you are an OpenPGP novice, you will want to read the OpenPGP documentation first instead.
[[TOC]]
Tutorial
How to
Getting a security key
There is a wild variety of security keys out there. This guide focuses on the YubiKey, but there are alternatives as well.
You may order a YubiKey directly from their store. You can also ask TPA if they have any remaining keys although at the time of writing, the whole stock was depleted at the 2023 Costa Rica meeting.
Form factors
YubiKeys come mainly in two form factor axis:
- USB-C or USB-A
- "normal" or "nano"
The decision on USB-C vs USB-A should be relatively simple:
-
if you have older computers that do not have USB-C, or are worried about backwards-compatibility, use USB-A.
-
if you want to go USB-C only, use USB-C, but be aware there's been anecdotal reports of the USB-C form factor being more fragile than the USB-A form. the connector can apparently get dented, although other reports claim this has been fixed.
The decision between "normal" and "nano" depends mostly on how big you like the key, but also how sturdy you expect it to be.
The author of this guide (@anarcat), has been wearing a YubiKey NEO for over 8 years on his keyring. It has been dropped in water, slush, sand, probably been in a laundry at least once, and has been worn out quite significantly, up to the point that the connector is round instead of square. It still works relatively reliably.
Another example is a YubiKey 5 "nano" that has had a part of it's plastic case uncapped after only a few months of active use.
So it seems the sturdy one is the "normal" form factor, and that the "nano" is designed to be stuck in a USB port and stay there.
Finally, also note that the USB-C "nano" form factor doesn't have a hole to hook on a wire or keyring. The USB-A "nano" form factor does have such a ring and Yubico sells a lanyard to hook it up to your keyring. The lanyard is 0.18" thick, so presumably any wire of that gauge (AWG ~5 or SWG 6-7) would fit as well. Note that a metal wire might wear out faster, consider a fishing line (e.g. Nylon).
YubiKey training
This section holds the notes to the YubiKey training given in Costa Rica in April 2023.
Introduction
- what is a YubiKey? it's a 2FA token with extra capabilities
- why is it called a YubiKey? "Yubico's explanation of the name "YubiKey" is that it derives from the phrase "your ubiquitous key", and that "yubi" is the Japanese word for finger." (Wikipedia, source)
- what is 2FA? two-factor authentication
-
why do we need 2FA?
-
to make hacking your account more difficult,
- ... and because people are not great at remembering good passwords
-
it's required by GitHub and our Nextcloud instance
-
why do we need a Yubikey? it's better than typical 2FA, it can protect against:
- phishing attacks (and say why)
- shoulder surfing and surveillance cameras
- it cannot protect against:
- malware on your computer (as they can control the YubiKey or steal your session cookie)
- successful HTTPS MITM
- what are we going to do today? 2FA only
"There's all sorts of pitfalls and challenges in deploying 2FA and YubiKeys (e.g. "I lost my YubiKey" or "OMG GnuPG is hell"), we're not going to immediately solve all of those issues. We're going to get hardware into people's hands and hopefully train them with U2F/FIDO2 web 2FA, and maybe be able to explore the SSH/OpenPGP side of things as well."
Unpacking and authenticating a YubiKey
- check blister packaging
- login to https://www.yubico.com/genuine/
Setting up 2FA in Nextcloud
We can either follow the upstream guide or our own tutorial. Here's a copy of the latter with only the U2F instructions:
- In NextCloud, select Settings -> Security. The link to your settings can be found by clicking on your "user icon" in the top right corner. Direct link: Settings -> Security.
- Pick either the U2F device as an "second factor".
- Click the "Add U2F device" button under the "U2F device" section
- Insert the token and press the button when prompted by your web browser
- Enter a name for the device and click "Add"
- Click "Generate Backup codes" in the Two-Factor Authentication section of that page.
- Save your backup codes to a password manager of your choice. These will be needed to regain access to your NextCloud account if you ever lose your 2FA token/application.
- Log out and log in again, to verify that you got two factor authentication working.
Setting up 2FA in GitLab
- In gitlab, select "Preferences" and then in the left-hand column, "Account"
-
In section "Register a WebAuthn Device" find the "Set Up a New Device" button
-
IF you are using yubikey-agent for ssh, you need to stop that temporarily (otherwise the setup fails) systemctl --user stop yubikey-agent.service
-
Now click the "Set Up a New Device Button"
- Touch the Yubikey button to confirm
-
Give the device a reference name so you can identify it
-
IF necessary, restart the yubikey-agent.service
- If you have not already done so, generate and save the 2FA backup codes.
- Log out and log back in again, to verify the yubikey 2FA works.
SSH authentication
You can use your YubiKey to authenticate with SSH servers using one three "applets" provided by the device:
- OpenPGP
- FIDO2
- PIV
OpenPGP
You can (and probably should) use your YubiKey in OpenPGP mode to authenticate with SSH servers. This, however, requires a more complex setup than can fit in this section, see the OpenPGP operations section for details.
FIDO2
Recent YubiKeys like the YubiKey 5 ship a "FIDO2" applet that is generally used for two-factor authentication. But SSH also supports using that to store SSH keys, which can therefore be used to authenticate against servers.
This Yubico guide shows you how to configure such keys,
recognizable from their -sk suffix (e.g. ed25519-sk). See also
this GitHub guide.
This is the recommended method for users who want to use their YubiKeys for SSH connections to GitLab, GitHub, Debian servers, etc.
It should be noted that the -sk SSH keys are relatively new, and as such are
often not supported by old devices and servers. Users who would like to to use
their YubiKey to secure connections to such older SSH servers may use one of
the modes below, in addition to native FIDO2 keys.
In particular, -sk keys are currently not supported by our
LDAP configuration, see this ticket for details.
PIV
This guide should be followed if you want to use SSH without depending on OpenPGP or FIDO2.
YubiKey 5-series tokens, which support the FIPS 201 standard also known as PIV, can be used as a convenient second factor to for ssh public key authentication.
While the YubiKey supports either RSA or ECC certificates for this, we'll use
RSA since it's the most compatible across all SSH servers. For example, some BMC
only support ssh-rsa keys. This has also been observed on Pantheon.io, a DevOps
platform for websites. For modern SSH servers, the ed25519-sk key type is
preferred.
WARNING: because yubikey-agent requires exclusive access to the yubikey, this
method is only practical when the YubiKey's OpenPGP interface is not used.
Otherwise, the more practical solution is to use the OpenPGP interface with an
authentication subkey that can be used as an SSH key pair.
Token setup
First, one must install yubikey-manager.
On Debian 11 (bullseye), a simple apt install yubikey-manager is sufficient. On
older versions of Debian, one should install it via pip3 in order to have a
sufficiently recent version of the tool.
- Reset all PIV config/data on the token:
ykman piv reset - Define new PIN (the default is
123456):ykman piv change-pin - The PIN must be between 6 and 8 characters long, and supports any type of alphanumeric characters. For cross-platform compatibility, numeric digits are recommended.
- Define new PUK (Personal Unblocking Key, used when PIN retries have been
exceeded):
ykman piv change-puk - Define a management key:
piv change-management-key -pt - Generate RSA key:
ykman piv keys generate --algorithm RSA2048 --pin-policy ONCE --touch-policy CACHED 9a pubkey.pem - Generate certificate:
ykman piv certificates generate --valid-days 3650 --subject "CN=ssh" 9a pubkey.pem - Verify with
ykman piv info
This will create a 2048-bits RSA certificate in slot 9a of the PIV token device.
The PIN will be required only once per-session (--pin-policy ONCE) but touch
will be required at every use and remembered for 15 seconds afterwards
(--touch-policy CACHED).
The next step is to install and start yubikey-agent which is a small daemon written in Go that act as an ssh-agent for the YubiKey. Installation instructions which work with Debian can be found here:
https://github.com/FiloSottile/yubikey-agent/blob/main/systemd.md
The yubikey-agent -setup step can be skipped, as we've already set up the
token with the above. yubikey-agent's own setup routine makes different,
hard-coded choices with regard to the PIN and PUK (identical), certificate type
(ECC) and other small things.
Once the agent is setup and running in the background, the SSH public key can be
retrieved from the token using the following command: ssh-add -L.
At this point it may be useful to install the libnotify-bin package on Debian
which allows the agent to send a desktop notification when the token needs to be
touched to perform an authentication operation. This is especially useful when
the token LED, which flashes when touch is requested, isn't well into view.
These instructions are derived from this guide from eta.st.
Configure SSH
If not done already, now is a good time to setup the ssh configuration for the TPO jump host, see ssh-jump-host for these instructions.
To have the ssh command use yubikey-agent when connecting to TPO hosts, add
this line in ~/.ssh/config under Host *.torproject.org:
IdentityAgent /run/user/1000/yubikey-agent/yubikey-agent.sock
If you also want to use ed25519_sk-type keys based on the modern FIDO2
protocol for non-TPO hosts, you may add this at the end of ~./ssh/config:
Host *
IdentityAgent /dev/null
IdentityFile ~/.ssh/id_ed25519_sk
OpenPGP operations
The YubiKeys also ship with an "OpenPGP smartcard applet" that allows you to store cryptographic keys. The YubikKey 5 in particular supports ECC keys.
Why OpenPGP
We use OpenPGP here because it's still the "standard" (e.g. specified in RFCs) way to do interoperable offline cryptographic operations in various locations. It's also heavily used at Tor and, until further notice, a requirement to get a working email account.
Finally, the OpenPGP applet provides a way to use SSH with YubiKeys
that is somewhat clunky, but doesn't suffer from backwards
compatibility problems that the SSH sk- keys suffer from.
That said, there are serious issues with using OpenPGP here:
- it's awfully complicated
- it's brittle
- it doesn't support "touch detection" (i.e. there is no user feedback when the device requires a touch, other than the device itself blinking, something that the FIDO2 applet solves, see this discussion and this tool for a workaround)
Why GnuPG, ECC, or air-gapped systems
See our OpenPGP docs for that discussion.
Implementation details
The stack we going to setup is as follows:
- YubiKey (hardware)
- USB connection (or other), bus, CPU, etc (hardware)
scdaemon(GnuPG software that interacts with "smart cards" like the YubiKey)gpg-agent(GnuPG software that holds private keys or passphrases)- GnuPG or SSH commands that interact with the agent
Assertions
This guide assumes the following:
-
a lot of familiarity with the command-line
-
a Debian system, but should be easy to adapt to other operating systems, some hints are provided for Mac OS
-
enough entropy; failing that, this can feed entropy from the YK (from drduh's guide):
echo "SCD RANDOM 512" | gpg-connect-agent | sudo tee /dev/random | hexdump -C -
a trusted device that was not previously compromised; we explicitly do not explain how to do this from an "air-gapped" device, for example, as this is considered an implementation detail (and possibly overkill, a full discussion of those trade-offs would be irrelevant here)
-
the YubiKey has been obtained from trusted channels and verified (see Unpacking and authenticating a YubiKey
Special considerations for storing encryption keys
Here we are storing a full OpenPGP secret keyring on the YubiKey. This implies that encryption keys end up stored on the device. Therefore, special care need to be taken as the loss of a YubiKey could be catastrophic: in such a case, while the encryption key can be revoked, that doesn't allow the operator to recover past material encrypted with the key. This can lead to severe data loss.
Encryption keys, therefore, must not be generated on the YubiKey as they MUST be backed up. They therefore MUST be generated on another device.
The general strategy here is to have three copies of the encryption key:
main key: a first YubiKey used for daily operationbackup key: a second YubiKey available as a backupbackup disk: a copy of the encryption key material stored on a normal disk, encrypted with itself
The rationale here is that if the main key is lost, the backup key
and backup disk can be combined to create a new main key.
If the backup disk did not exist, it would be impossible to recreate
a new main key and, when the backup key is eventually lost or
destroyed, the encrypted contents will not be readable anymore.
This is the strategy taken in this guide.
Install software and preparation
You will need to install GnuPG, its scdaemon component and a
yubikey-manager, a "command line tool for configuring a YubiKey".
apt install gnupg scdaemon yubikey-manager
If you're on a Mac, you'll also need to explicitly install
pinentry-mac, see the Ultimate Yubikey Setup Guide with ed25519!
If you're on Windows, good luck, maybe this section of drduh's guide can help you.
Make sure the key is detected:
ykman list
This should show your key, for example this is with a recent YubiKey 5 NFC (USB-A):
$ ykman list
YubiKey 5 NFC (5.4.3) [OTP+FIDO+CCID] Serial: [REDACTED]
$
In the above, you can see the string [OTP+FIDO+CCID] which basically
tells you that you can use the token for "OTP" (One-Time
Passwords in general, but in this particular case it's actually
TOTP, AKA "Google Authenticator"), FIDO2, and CCID, the
"chip card interface device" protocol.
Those are all different "applications" shipped with the YubiKey that can be enabled or disabled independently. OpenPGP operations rely on the latter (CCID), so it needs to be listed in the above output.
If it isn't, use this to enable it (and, in this example, also FIDO2):
ykman config mode FIDO+CCID
You may want to disable NFC if you are not going to use it:
ykman config nfc --disable-all
The end result should look something like this:
anarcat@angela:~$ ykman info
Device type: YubiKey 5 NFC
Serial number: [REDACTED]
Firmware version: 5.4.3
Form factor: Keychain (USB-A)
Enabled USB interfaces: FIDO, CCID
NFC transport is enabled.
Applications USB NFC
FIDO2 Enabled Disabled
OTP Disabled Disabled
FIDO U2F Enabled Disabled
OATH Enabled Disabled
YubiHSM Auth Disabled Disabled
OpenPGP Enabled Disabled
PIV Enabled Disabled
Note that "You must remove and re-insert your YubiKey for this change to take effect", as indicated.
Finally, you should really set a new PIN and admin PIN on the
key. This is easily done with GnuPG itself, first enter the magic
card-edit shell with:
gpg --card-edit
That will show you the following prompt:
gpg/card>
There you need to type the admin and passwd commands to change the
first normal PIN:
gpg/card> admin
Admin commands are allowed
gpg/card> passwd
gpg: OpenPGP card no. [REDACTED] detected
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
Your selection? 1
This will prompt you for the current PIN. The factory default is
123456 (yes, just like the combination on someone's luggage).
You will want to also set the Admin PIN, but the default is slightly different
from the previous one, it is 12345678:
gpg/card> passwd
gpg: OpenPGP card no. [REDACTED] detected
1 - change PIN
2 - unblock PIN
3 - change Admin PIN
4 - set the Reset Code
Q - quit
Your selection? 3
[... pinentry prompt ...]
PIN changed.
Note that the pin needs to be at least 8 characters long, if you're failing to set a PIN, that might be why!
If this doesn't work, or you messed up and forgot your PIN already, you can try to reset the OpenPGP application altogether which will erase the secret keys on the card, so be careful.
I typically set both to the same value since the basic PIN basically allows any operation I'm worried about (short of changing the keys) anyway. Furthermore, if they would be different, I would very likely forget the admin PIN and wouldn't be able to reset the normal PIN after too many failed attempts.
You may also want to enable the KDF feature on newer
YubiKeys. This allows the YubiKey to store the PIN as a hashed
value instead of cleartext, and makes it harder to bruteforce as the
caller needs to run the KDF function. This was implemented in
2017 and is not necessarily supported by all clients. It's also
irreversible, so beware. To turn it on, simply call kdf-setup on the
gpg/card> prompt.
Details about reset, admin, and PIN
The OpenPGP applet has three different PINs:
-
the normal PIN: used in normal operation, when you need to unlock the card to do a cryptographic operation
-
the Admin PIN: used to unlock the card if the above "normal PIN" gets input incorrectly too many times, or configure card (e.g. add/remove keys, change name, etc)
-
the reset PIN: used to only unlock the card (e.g. like the Admin PIN except it cannot do the other things)
By default, the reset PIN is not used in YubiKeys and, indeed, it might not make sense for a single-user case. According to this comment it's designed for enterprise setups where administrators configure keys for users and do not want to let them change their own keys. See also drduh's guide for a discussion on this.
Key generation
At this point, if you don't already have a key pair to put on the YubiKey, you should generate a new OpenPGP key. Follow the procedure to Generate a Curve25519 key.
If you are rotating keys, do not publish the revocation certificate for the old key just yet, in case the procedure below fails.
Note that we're not generating the keys on the YubiKey itself. There are two reasons for this:
-
we need access to the private key to clone the key and particularly recover the encryption key from backups (see Special considerations for storing encryption keys
-
entropy sources on security keys have been known to be flawed in the past, including on Yubikeys, see for example the infineon vulnerability
Export to backup
At this point, we have an OpenPGP key pair we're ready to put on the security key. But before we do that, we need to make a backup, because the procedure moves the keys onto the security key, which makes it inaccessible.
Follow the procedure in the OpenPGP guide.
After this step, it's assumed you have
$BACKUP_DIR/openpgp-backup-$FINGERPRINT.tar.pgp and
$BACKUP_DIR/openpgp-backup-public-$FINGERPRINT.key files available.
Moving to key
WARNING: this MOVES the key to the security card, make sure you have backups as explained above.
Now we're going to do the sensitive step of moving the secret keys onto the YubiKey. First, edit the key:
gpg --edit-key $FINGERPRINT
Then, to move the primary key, simply call:
keytocard
This will ask you where to store the key, select (1) Signature key.
Then move the two subkeys:
key 1
keytocard
key 1
key 2
keytocard
The double key 1 above is not an error: it deselects the first
subkey to only select the second subkey. (And no, you can't do both at
once.)
You need to pick the right slot for each key, key 1 is likely the (2)
Encryption key and key 2 is likely the (3) Authentication key.
Here's an example run:
anarcat@angela:~$ gpg --edit-key $FINGERPRINT
gpg (GnuPG) 2.2.40; Copyright (C) 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Secret key is available.
sec ed25519/02293A6FA4E53473
created: 2023-05-30 expires: 2024-05-29 usage: SC
trust: unknown validity: unknown
ssb cv25519/9456BA69685EAFFB
created: 2023-05-30 expires: never usage: E
ssb ed25519/9FF21704D101630D
created: 2023-05-30 expires: never usage: A
[ unknown] (1). Antoine Beaupré <anarcat@anarc.at>
gpg> keytocard
Really move the primary key? (y/N) y
Please select where to store the key:
(1) Signature key
(3) Authentication key
Your selection? 1
sec ed25519/02293A6FA4E53473
created: 2023-05-30 expires: 2024-05-29 usage: SC
trust: unknown validity: unknown
ssb cv25519/9456BA69685EAFFB
created: 2023-05-30 expires: never usage: E
ssb ed25519/9FF21704D101630D
created: 2023-05-30 expires: never usage: A
[ unknown] (1). Antoine Beaupré <anarcat@anarc.at>
gpg> key 1
sec ed25519/02293A6FA4E53473
created: 2023-05-30 expires: 2024-05-29 usage: SC
trust: unknown validity: unknown
ssb* cv25519/9456BA69685EAFFB
created: 2023-05-30 expires: never usage: E
ssb ed25519/9FF21704D101630D
created: 2023-05-30 expires: never usage: A
[ unknown] (1). Antoine Beaupré <anarcat@anarc.at>
gpg> keytocard
Please select where to store the key:
(2) Encryption key
Your selection? 2
sec ed25519/02293A6FA4E53473
created: 2023-05-30 expires: 2024-05-29 usage: SC
card-no: 0006 REDACTED
trust: unknown validity: unknown
ssb* cv25519/9456BA69685EAFFB
created: 2023-05-30 expires: never usage: E
ssb ed25519/9FF21704D101630D
created: 2023-05-30 expires: never usage: A
[ unknown] (1). Antoine Beaupré <anarcat@anarc.at>
gpg> key 1
sec ed25519/02293A6FA4E53473
created: 2023-05-30 expires: 2024-05-29 usage: SC
card-no: 0006 REDACTED
trust: unknown validity: unknown
ssb cv25519/9456BA69685EAFFB
created: 2023-05-30 expires: never usage: E
ssb ed25519/9FF21704D101630D
created: 2023-05-30 expires: never usage: A
[ unknown] (1). Antoine Beaupré <anarcat@anarc.at>
gpg> key 2
sec ed25519/02293A6FA4E53473
created: 2023-05-30 expires: 2024-05-29 usage: SC
card-no: 0006 REDACTED
trust: unknown validity: unknown
ssb cv25519/9456BA69685EAFFB
created: 2023-05-30 expires: never usage: E
ssb* ed25519/9FF21704D101630D
created: 2023-05-30 expires: never usage: A
[ unknown] (1). Antoine Beaupré <anarcat@anarc.at>
gpg> keytocard
Please select where to store the key:
(3) Authentication key
Your selection? 3
sec ed25519/02293A6FA4E53473
created: 2023-05-30 expires: 2024-05-29 usage: SC
card-no: 0006 REDACTED
trust: unknown validity: unknown
ssb cv25519/9456BA69685EAFFB
created: 2023-05-30 expires: never usage: E
card-no: 0006 REDACTED
ssb* ed25519/9FF21704D101630D
created: 2023-05-30 expires: never usage: A
[ unknown] (1). Antoine Beaupré <anarcat@anarc.at>
gpg> save
Then keys should not be present in the keyring:
anarcat@angela:1000$ gpg --list-secret-keys
/run/user/1000/.gnupg/pubring.kbx
---------------------------------
sec> ed25519 2023-05-30 [SC] [expires: 2024-05-29]
BBB6CD4C98D74E1358A752A602293A6FA4E53473
Card serial no. = 0006 23638206
uid [ultimate] Antoine Beaupré <anarcat@anarc.at>
ssb> cv25519 2023-05-30 [E]
ssb> ed25519 2023-05-30 [A]
In the above, we can see the secret keys are not present because they
are marked sec> and ssb>, not sec and ssb.
At this point you can try removing the key to confirm that the secret key is not available, for example with the command:
gpg --clear-sign < /dev/null
This should ask you to insert the key. Inserting the key should GnuPG to output a valid signature.
Touch policy
This is optional.
You may want to change the touch policy. This requires you to touch the YubiKey to consent to cryptographic operation. Here is a full touch policy:
ykman openpgp keys set-touch sig cached
ykman openpgp keys set-touch enc cached
ykman openpgp keys set-touch aut cached
NOTE: the above didn't work before the OpenPGP keys were created, that is normal.
The above means that touch is required to confirm signature, encryption and authentication operations, but is cached 15 seconds. The rationale is this:
sig onis absolutely painful if you go through a large rebase and need to re-sign a lot of commitsenc onis similarly hard if you are decrypting a large thread of multiple messagesautis crucial when running batch jobs on multiple servers, as tapping for every one of those would lead to alert fatigue, and in fact I sometimes just flip backaut offfor some batches that take longer than 15 seconds
Another policy could be:
ykman openpgp keys set-touch sig on
ykman openpgp keys set-touch enc on
ykman openpgp keys set-touch aut cached
That means:
- touch is required to confirm signatures
- touch is required to confirm decryption
- touch is required to confirm authentication, but is cached 15 seconds
You can see the current policies with ykman openpgp info, for
example:
$ ykman openpgp info
OpenPGP version: 3.4
Application version: 5.4.3
PIN tries remaining: 3
Reset code tries remaining: 0
Admin PIN tries remaining: 3
Touch policies
Signature key On
Encryption key On
Authentication key Cached
Attestation key Off
If you get an error running the info command, maybe try to
disconnect and reconnect the YubiKey.
The default is to not require touch confirmations.
Do note that touch confirmation is a little counter-intuitive: the operation (sign, authenticate, decrypt) will hang without warning until the button is touched. The only indication is the blinking LED, there's no other warning from the user interface.
Also note that the PIN itself is cached by the YubiKey, not the
agent. There is a wishlist item on GnuPG to expire the password
after a delay, respecting the default-cache-ttl and max-cache-ttl
settings from gpg-agent.conf, but alas this do not currently take
effect.
It should also be noted that the cache setting is a 15 seconds delay
total: it does not reset when a new operation is done. This means
that the entirety of the job needs to take less than 15 seconds, which
is why I sometimes completely disable it for larger runs.
Making a second YubiKey copy
At this point, we have a backup of the keyring that is encrypted with itself. We obviously can't recover this if we lose the YubiKey, so let's exercise that disaster recovery by making a new key, completely from the backups.
-
first, go through the preparation steps above, namely setting the CCID mode, disabling NFC, setting a PIN and so on. you also should have a backup of your secret keys at this point, if not (and you still have a copy of your secret keys in some other keyring), follow the OpenPGP guide to export a backup that we assume to be present in
$BACKUP_DIR. -
create a fresh new GnuPG home:
OTHER_GNUPGHOME=${XDG_RUNTIME_DIR:-/nonexistent}/.gnupg-restore ( umask 0077 && mkdir OTHER_GNUPGHOME ) -
make sure you kill
gpg-agentand related daemons, they can get confused when multiple home directories are involved:killall scdaemon gpg-agent -
restore the public key:
gpg --homedir=$OTHER_GNUPGHOME --import $BACKUP_DIR/openpgp-backup-public-$FINGERPRINT.key -
confirm GnuPG can not see any secret keys:
gpg --homedir=$OTHER_GNUPGHOME --list-secret-keysyou should not see any result from this command.
-
then, crucial step, restore the private key and subkeys:
gpg --decrypt $BACKUP_DIR/openpgp-backup-$FINGERPRINT.tar.pgp | tar -x -f - --to-stdout | gpg --homedir $OTHER_GNUPGHOME --importYou need the first, main key to perform this operation.
-
confirm GnuPG can see the secret keys: you should not see any
Card serial no.,sec>, orssb>in there. If so, it might be because GnuPG got confused and still thinks the old key is plugged in. -
then go through the
keytocardprocess again, which is basically:gpg --homedir $OTHER_GNUPGHOME --edit-key $FINGERPRINTthen remove the main key and plug in the backup yubikey to move the keys to that key:
keytocard 1 key 1 keytocard 2 key 1 key 2 keytocard 3 saveIf that fails with "No such device", you might need to kill gpg-agent again as it's very likely confused:
killall scdaemon gpg-agentOr you might need to plug the key out and back in again.
At this point the new key should be a good copy of the previous YubiKey. If you are following this procedure because you have lost your previous YubiKey, you should actually make another copy of the YubiKey at this stage, to be able to recover when this key is lost.
Agent setup
At this point, GnuPG is likely working well enough for OpenPGP
operations. If you want to use it for OpenSSH as well, however, you'll
need to replace the built-in SSH agent with gpg-agent.
The right configuration for this is tricky, and may vary wildly depending on your operating system, graphical and desktop environment.
The Ultimate Yubikey Setup Guide with ed25519! suggests adding this to your environment:
export "GPG_TTY=$(tty)"
export "SSH_AUTH_SOCK=${HOME}/.gnupg/S.gpg-agent.ssh"
... and this in ~/.gnupg/gpg-agent.conf:
enable-ssh-support
If you are running a version before GnuPG 2.1 (and you really shouldn't), you will also need:
use-standard-socket
Then you can restart gpg-agent with:
gpgconf --kill gpg-agent
gpgconf --launch gpg-agent
If you're on a Mac, you'll also need:
pinentry-program /usr/local/bin/pinentry-mac
In GNOME, there's a keyring agent which also includes an SSH agent, see this guide for how to turn it off.
At this point, SSH should be able to see the key:
ssh-add -L
If not, make sure SSH_AUTH_SOCK is pointing at the GnuPG agent.
Exporting SSH public keys from GnuPG
Newer GnuPG has this:
gpg --export-ssh-key $FINGERPRINT
You can also use the more idiomatic:
ssh-add -L
... assuming the key is present.
Signed Git commit messages
To sign Git commits with OpenPGP, you can use the following configuration:
git config --global user.signingkey $FINGERPRINT
git config --global commit.gpgsign true
Git should be able to find GnuPG and will transparently use the YubiKey to sign commits
Using the YubiKey on a new computer
One of the beauties of using a YubiKey is that you can somewhat easily use the same secret key material material across multiple machines without having to copy the secrets around.
This procedure should be enough to get you started on a new machine.
-
install the required software:
apt install gnupg scdaemon -
restore the public key:
gpg --import $BACKUP_DIR/public.keyNote: this assumes you have a backup of that public key in
$BACKUP_DIR. If that is not the case, you can also fetch the key from key servers or another location, but you must have a copy of the public key for this to work.If you have lost even the public key, you may want to read this guide: recovering lost GPG public keys from your YubiKey – Nicholas Sherlock create, untested.
-
confirm GnuPG can see the secret keys:
gpg --list-secret-keysyou should not see any
Card serial no.,sec>, orssb>in there. If so, it might be because GnuPG got confused and still thinks the old key is plugged in. -
set the trust of the new key to
ultimate:gpg --edit-key $FINGERPRINTThen, in the
gpg>shell, call:trustThen type
5for "I trust ultimately". -
test signing and decrypting a message:
gpg --clearsign < /dev/null gpg --encrypt -r $FINGERPRINT < /dev/null | gpg --decrypt
Preliminary performance evaluation
Preparation:
dd if=/dev/zero count=1400 | gpg --encrypt --recipient 8DC901CE64146C048AD50FBB792152527B75921E > /tmp/test-rsa.pgp
dd if=/dev/zero count=1400 | gpg --encrypt --recipient BBB6CD4C98D74E1358A752A602293A6FA4E53473 > /tmp/test-ecc.pgp
RSA native (non-Yubikey) performance:
$ time gpg --decrypt < /tmp/test-rsa.pgp
gpg: encrypted with 4096-bit RSA key, ID A51D5B109C5A5581, created 2009-05-29
"Antoine Beaupré <anarcat@orangeseeds.org>"
0.00user 0.00system 0:00.03elapsed 18%CPU (0avgtext+0avgdata 6516maxresident)k
0inputs+8outputs (0major+674minor)pagefaults 0swaps
ECC security key (YubiKey 5) performance:
$ time gpg --decrypt < /tmp/test-ecc.pgp
gpg: encrypted with 255-bit ECDH key, ID 9456BA69685EAFFB, created 2023-05-30
"Antoine Beaupré <anarcat@torproject.org>"
0.00user 0.03system 0:00.12elapsed 30%CPU (0avgtext+0avgdata 7672maxresident)k
0inputs+8outputs (0major+1834minor)pagefaults 0swaps
That is, 120ms vs 30ms, the YubiKey is 4 times slower than the normal configuration. An acceptable compromise, perhaps.
Troubleshooting
If an operation fails, check if GnuPG can see the card with:
gpg --card-status
You can also try this incantation, which should output the key's firmware version:
gpg-connect-agent --hex "scd apdu 00 f1 00 00" /bye
For example, this is the output when successfully connecting to an old Yubikey NEO running the 1.10 firmware:
gpg-connect-agent --hex "scd apdu 00 f1 00 00" /bye
D[0000] 01 00 10 90 00 .....
OK
The OK means it can talk to the key correctly. Here's an example
with a Yubikey 5:
$ gpg-connect-agent --hex "scd apdu 00 f1 00 00" /bye
D[0000] 05 04 03 90 00 .....
OK
A possible error is:
ERR 100663404 Card error <SCD>
That could be because of a permission error. Normally, udev rules
are in place to keep this from happening.
See also drduh's troubleshooting guide.
Resetting a YubiKey
If everything goes south and you locked yourself out of your key, you can completely wipe the OpenPGP applet with:
ykman openpgp reset
WARNING: that will WIPE all the keys on the device, make sure you have a backup or that the keys are revoked!
Incorrect TTY
If GnuPG doesn't pop up a dialog prompting you for a password, you
might have an incorrect TTY variable. Try to kick gpg-agent with:
gpg-connect-agent updatestartuptty /bye
Incorrect key grip
If you somehow inserted your backup key and now GnuPG absolutely wants nothing to do with your normal key, it's because GnuPG silently replaced your "key grips". Those are little text files that it uses to know which physical key has a copy of your private key.
You can see the key grip identifiers in GnuPG's output with:
gpg -K --with-keygrip
They look like key fingerprint, but for some reason (WHY!?) are not. You can then move those files out of the way with:
cd ~/.gnupg/private-keys-v1.d
mkdir ../private-keys-v1.d.old
mv 23E56A5F9B45CEFE89C20CD244DCB93B0CAFFC73.key 74D517AB0466CDF3F27D118A8CD3D9018BA72819.key 9826CAB421E15C852DBDD2AB15A866CD0E81D68C.key ../private-keys-v1.d.old
gpg --card-status
You might need to run that --card-status a few times.
We're not instructing you to delete those files because, if you get the identifier wrong, you can destroy precious private key material here. But if you're confident those are actual key grips, you can remove them as well. They should look something like this:
Token: [...] OPENPGP.2 - [SERIAL]
Key: (shadowed-private-key [...]
As opposed to private keys, which start with something like this:
(11:private-key[...]
FAQ
I don't have usb-c in my laptop, would i need an adaptor then?
If you get a USB-A key, yes, but you can get a USB-C key!
Who should use this?
Everyone! If you're using a service like Nextcloud, the Discourse forum, GitLab, you should enable 2FA and preferably with a cryptographic token. That's not yet official policy, but it's probably going to hit the security policy in some shape or form in the future.
I do my work from Tails, do I need a Yubikey?
Yes, because Tails doesn't necessarily protect you against phishing attacks.
Can I use the USB port during my work session, or i need to have the YubiKey plugged all the time?
You don't need to have it plugged in all the time.
One interesting aspect of the YubiKey is that you can unplug it and decide "nope, authentication doesn't happen here anymore".
It's a clear way to secure that cryptographic material, physically.
Any reason why we pick a Yubikey and not a tool with a open design like a NitroKey?
anarcat made a review of the Nitrokey in 2017 and found that their form factor was less reliable than the YubiKey.
The Solokey was also considered but is not quite ready for prime time yet. See also this review. Google's Titan key was also an option but only supports 2FA (not OpenPGP or SSH), see the other alternatives section for more details.
My Yubikey squirts out an OTP code when I accidentally touch it
There are several ways to deal with this issue. Since we don't use Yubico OTP in Tor, the easiest solution is to simply disable the OTP app on the USB interface.
First, ensure the Yubikey is inserted in one of your USB ports.
On the command-line, you can install the yubikey-manager package and run the
command below:
ykman config usb --disable otp
This program is also available with a GUI, installed with yubikey-manager-qt
on Debian-based systems. Installers for other platforms such as Windows and
MacOS can be downloaded from the Yubico website download page.
The procedure with the Yubikey Manager GUI is to open the program, click the
Interfaces tab, and under USB, uncheck OTP and click Save interfaces.
Once this is done, OTP will remain disabled until it's manually re-enabled.
If you want to conserve the ability to generate Yubico OTP codes, there are two
options: either disable sending the <Enter> character using ykman otp
settings --no-enter 1, or swap the OTP to slot 2, which requires a sustained
2-second touch to activate, with ykman otp swap.
I'm getting a "No YubiKey found" error
When running the ykman command, you might stumble upon the following error:
Error: No YubiKey found with the given interface(s)
This might happen because GnuPG (or probably a lock contention between
ykman's pcscd and GnuPG's scdaemon).
The fix is unclear, but a workaround is to disconnect and reconnect your YubiKey.
After upgrading to GnuPG 2.4 I can't use my key anymore
If you're running debian sid or you've just upgraded to trixie, you most probably switched to version 2.4 of GnuPG. You might then encounter this error:
# gpg --card-status
gpg: selecting card failed: No such device
gpg: OpenPGP card not available: No such device
A NEWS item was added to the debian gnupg package to warn about this problem. It used to be that gnupg's scdaemon would first try its internal CCID mechanism, fail and then default to trying with pcscd and that last part was how we were interacting with the yubikeys through GnuPG. The fallback no longer happens, but we can tell scdaemon to directly try with pcscd instead.
Open (or create if it doesn't exist) ~/.gnupg/scdaemon.conf and add the
following line:
disable-ccid
Then restart the scdaemon with systemctl --user restart gpg-agent.service.
That should make you able to interact with your yubikey instead.
Note: it's also possible to use CCID instead of pcscd, but that change was not investigated.
Pager playbook
Disaster recovery
Reference
Installation
When you receive your YubiKey, you need to first inspect the "blister" package to see if it has been tampered with.
Then, open the package, connect the key to a computer and visit this page in a web browser:
https://www.yubico.com/genuine/
This will guide you through verifying the key's integrity.
Out of the box, the key should work for two-factor authentication with FIDO2 on most websites. It is imperative that you keep a copy of the backup or "scratch" codes that are usually provided when you setup 2FA on the site, as you may lose the key and that is the only way to recover from that.
For other setups, see the following how-to guides:
Upgrades
YubiKeys cannot be upgraded, the firmware is read-only.
SLA
N/A
Design and architecture
A YubiKey is an integrated circuit that performs cryptographic operations on behalf of a host. In a sense, it is a tiny air-gapped computer that you connect to a host, typically over USB but Yubikeys can also operate over NFC.
Services
N/A
Storage
The YubiKeys keep private cryptographic information embedded in the key, for example RSA keys for the SSH authentication mechanism. Those keys are supposed to be impossible to extract from the YubiKey, which means they are also impossible to backup.
Queues
N/A
Interfaces
YubiKeys use a few standards for communication:
- FIDO2 for 2FA
- PIV for SSH authentication
- OpenPGP "smart card" applet for OpenPGP signatures, authentication and encryption
Authentication
It's possible to verify the integrity of a key by visiting:
https://www.yubico.com/genuine/
Implementation
The firmware on YubiKeys is proprietary and closed source, a major downside to this platform.
Related services
YubiKeys can be used to authenticate with the following services:
| Service | Authentication type |
|---|---|
| Discourse | 2FA |
| GitLab | 2FA, SSH |
| Nextcloud | 2FA |
Issues
There is no issue tracker specifically for this project, File or search for issues in the team issue tracker with the label ~Foo.
Maintainer
anarcat worked on getting a bunch of YubiKeys shipped to a Tor meeting in 2023, and is generally the go-to person for this, with a fallback on TPA.
Users
All tor-internal people are expected to have access to a YubiKey and know how to use it.
Upstream
YubiKeys are manufactured by Yubico, a company headquartered in Palo Alto in California, but with Swedish origins. It has merged with a holding company from Stockholm in April 2023.
Monitoring and metrics
N/A
Tests
N/A
Logs
N/A
Backups
YubiKeys backups are complicated by the fact that you can't actually extract the secret key from a YubiKey.
FIDO2 keys
For 2FA, there's no way around it: the secret is generated on the key and stays on the key. The mitigation is to keep a copy of the backup codes in your password manager.
OpenPGP keys
For OpenPGP, you may want to generate the key outside the YubiKey and copy it in, that way you can backup the private key somewhere. A robust and secure backup system for this would be made in three parts:
- the main YubiKey, which you use every day
- a backup YubiKey, which you can switch to if you lose the first one
- a copy of the OpenPGP secret key material, encrypted with itself, so you can create a second key when you lose a key
The idea of the last backup is that you can recover the key material from the first key with the second key and make a new key that way. It may seem strange to encrypt a key with itself, but it is actually relevant in this specific use case, because another copy of the secret key material is available on the backup YubiKey.
Other documentation
- A YubiKey cheatsheet: U2F, keepass, LUKS, PIV and age support
- Yubikey + OpenPGP guide, another: basic configuration from Yubico
- Ultimate Yubikey Setup Guide with ed25519!: simple and straightforward guide, Mac-oriented
- drduh's YubiKey Guide: excellent guide, very (too?) detailed, includes instructions on air-gapped systems, LUKS-encrypted backups
- Yubikey + GnuPG + SSH howto: good guide, but also confusingly drifts into PIV
- Artizik: OpenPGP SSH access with Yubikey and GnuPG: recommends
pcscdneedlessly, drifts into other GnuPG configuration but used for ideas on how to turn off GNOME keyring, might be relevant for Estonians needing to have their key work in parallel with the national key ID - Anarcat's old (2015) YubiKey howto: GnuPG, SSH, OATH
- Recovering lost GPG public keys from your YubiKey – Nicholas Sherlock create - untested
- TPA-RFC-53 and discussion ticket
Discussion
While we still have to make an all-encompassing security policy (TPA-RFC-18), we have decided in April 2023 to train our folks to use YubiKeys as security keys, see TPA-RFC-53 and discussion ticket. This was done following a survey posted to tor-internal, the results of which are available in this GitLab comment.
Requirements
The requirements checklist was:
- FIDO2/U2F/whatever this is called now
- physical confirmation button (ideally "touch")
- OpenPGP applet should be available as an option
- USB A or USB-C?
- RSA, and ed5519 or equivalent?
It should cover the following use cases:
- SSH (through the SK stuff or gpg-agent + openpgp auth keys)
- OpenPGP
- web browsers (e.g. gitlab, discourse, nextcloud, etc)
Security and risk assessment
Background
TPA (Tor Project system Administrators) is looking at strengthening our security by making sure we have stronger two-factor authentication (2FA) everywhere. We have mandatory 2FA on some services, but this can often take the form of phone-based 2FA which is prone to social engineering attacks.
This is important because some high profile organizations like ours were compromised by hacking into key people's accounts and destroying critical data or introducing vulnerabilities in their software. Those organisations had 2FA enabled, but attackers were able to bypass that security by hijacking their phones, which is why having a cryptographic token like a YubiKey is important.
We also don't necessarily provide people with the means to more securely store their (e.g. SSH) private keys, used commonly by developers to push and sign code. So we are considering buying a bunch of YubiKeys, bringing them to the next Tor meeting, and training people to use them.
There's all sorts of pitfalls and challenges in deploying 2FA and YubiKeys (e.g. "i lost my YubiKey" or "omg GnuPG is hell"). We're not going to immediately solve all of those issues. We're going to get hardware into people's hands and hopefully train them with U2F/FIDO2 web 2FA, and maybe be able to explore the SSH/OpenPGP side of things as well.
Threat model
The main threat model is phishing, but there's another threat actor to take into account: powerful state-level adversaries. Those have the power to intercept and manipulate packages as they ship for example. For that reason, we were careful in how the devices were shipped, and they were handed out in person at an in-person meeting.
Users are also encouraged to authenticate their YubiKey using the Yubico website, which should provide a reliable attestation that the key was really made by Yubico.
That assumes trust in the corporation, of course. The rationale there is the reputation cost for YubiKey would be too high if they allowed backdoors in their services, but it is of course a possibility that a rogue employee (or Yubico itself) could leverage those devices to successfully attack the Tor project.
Future work
Ideally, there would be a rugged and open-hardware device that could simultaneously offer the tamper-resistance of the YubiKey while at the same time providing an auditable hardware platform.
Technical debt and next steps
At this point, we need to train users on how to use those devices, and factor this in a broader security policy (TPA-RFC-18).
Proposed Solution
This was adopted in TPA-RFC-53, see also the discussion ticket.
Other alternatives
- tillitis.se: not ready for end-user adoption yet
- Passkeys are promising, but have their own pitfalls. They certainly do not provide "2FA" in the sense that they do not add an extra authentication mechanism on top of your already existing passwords. Maybe that's okay? It's still early to tell how well passkeys will be adopted and whether they will displace traditional mechanisms or not.
- Nitrokey: not rugged enough
- Solokey: 2FA only, see also the tomu family
- FST-01: EOL, hard to find, gniibe is working on a smartcard reader
- Titan keys: FIDO2 only, but ships built-in with Pixel phones
- Trezor Safe 3: Crypto coin cold wallet, with built-in security key support. With a screen on the device to display verify what site is actually being logging in to, this device is more safe in the way that it does not require blind sign thus reducing the need to trust the host device is not compromised when the key is used. It comes with some usability issue such as the need to input pin on device before any usage, and when more than one key is inserted at the same time, it is unable to assist the discovery of the security key associated with key handle provided, making it necessary to insert and insert only the right security key when authentication happens. More suitable to be used in unowned and unverified device like someone else's computer or device running proprietary(someone else's) software.
The New York Times Wirecutters recommend the Yubikey, for what it's worth.