Skip to content
Snippets Groups Projects
yubikey.md 49.33 KiB
title: YubiKey documentation

YubiKeys are rugged security keys that can do FIDO2 two-factor authentication (2FA), PKCS and OpenPGP operations, all inside a small USB form factor.

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:

  1. USB-C or USB-A
  2. "normal" or "nano"

The decision on USB-C vs USB-A should be relatively simple:

  1. if you have older computers that do not have USB-C, or are worried about backwards-compatibility, use USB-A.

  2. 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

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:

  1. 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.
  2. Pick either the U2F device as an "second factor".
  3. Click the "Add U2F device" button under the "U2F device" section
  4. Insert the token and press the button when prompted by your web browser
  5. Enter a name for the device and click "Add"
  6. Click "Generate Backup codes" in the Two-Factor Authentication section of that page.
  7. 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.
  8. Log out and log in again, to verify that you got two factor authentication working.

Setting up 2FA in GitLab

  1. In gitlab, select "Preferences" and then in the left-hand column, "Account"

  2. In section "Register a WebAuthn Device" find the "Set Up a New Device" button

  3. IF you are using yubikey-agent for ssh, you need to stop that temporarily (otherwise the setup fails) systemctl --user stop yubikey-agent.service

  4. Now click the "Set Up a New Device Button"

  5. Touch the Yubikey button to confirm

  6. Give the device a reference name so you can identify it

  7. IF necessary, restart the yubikey-agent.service

  8. If you have not already done so, generate and save the 2FA backup codes.

  9. Log out and log back in again, to verify the yubikey 2FA works.

SSH authentication in FIDO2 mode

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.

SSH RSA authentication in PIV mode

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.

  1. Reset all PIV config/data on the token: ykman piv reset
  2. 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.
  1. Define new PUK (Personal Unblocking Key, used when PIN retries have been exceeded): ykman piv change-puk
  2. Define a management key: piv change-management-key -pt
  3. Generate RSA key: ykman piv keys generate --algorithm RSA2048 --pin-policy ONCE --touch-policy CACHED 9a pubkey.pem
  4. Generate certificate: ykman piv certificates generate --valid-days 3650 --subject "CN=ssh" 9a pubkey.pem
  5. 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 spinned off from those found at: https://eta.st/2021/03/06/yubikey-5-piv.html

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

SSH authentication in OpenPGP mode

See below.

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:

  1. it's awfully complicated
  2. it's brittle
  3. 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:

  1. YubiKey (hardware)
  2. USB connection (or other), bus, CPU, etc (hardware)
  3. scdaemon (GnuPG software that interacts with "smart cards" like the YubiKey)
  4. gpg-agent (GnuPG software that holds private keys or passphrases)
  5. 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:

  1. main key: a first YubiKey used for daily operation
  2. backup key: a second YubiKey available as a backup
  3. backup 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 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.

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:

  1. the normal PIN: used in normal operation, when you need to unlock the card to do a cryptographic operation

  2. 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)

  3. 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:

  1. 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

  2. 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.

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.

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's one policy, for example:

ykman openpgp keys set-touch sig on
ykman openpgp keys set-touch enc on
ykman openpgp keys set-touch aut cached

NOTE: the above didn't work before the OpenPGP keys were created, that is normal.

That means:

  1. touch is required to confirm signatures
  2. touch is required to confirm decryption
  3. touch is required to confirm authentication, but is cached 15 seconds

The latter merits some explanation. I operate a lot of servers, and sometimes run batch commands on many of them at once. Tapping for every one of those would lead to alert fatigue and be extremely annoying. The 15 seconds delay is a good compromise.

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.

Ultimately, I ended up setting all to cached:

ykman openpgp keys set-touch sig cached
ykman openpgp keys set-touch enc cached
ykman openpgp keys set-touch aut cached

The rationale is this:

  • sig on is absolutely painful if you go through a large rebase and need to re-sign a lot of commits
  • enc on is similarly hard if you are decrypting a large thread of multiple messages
  • aut is mentioned above, and in fact I sometimes just flip back aut off for some batches that take longer than 15 seconds

It should 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.

  1. first, go through the preparation steps above, namely setting the CCID mode, disabling NFC, setting a PIN and so on

  2. create a fresh new GnuPG home:

    export GNUPGHOME=${XDG_RUNTIME_DIR:-/nonexistent}/.gnupg-restore
    mkdir $GNUPGHOME
  3. make sure you kill gpg-agent and related daemons, they can get confused when multiple home directories are involved:

    killall scdaemon gpg-agent
  4. restore the public key:

    gpg --import $BACKUP_DIR/public.key
  5. confirm GnuPG can not see any secret keys:

    gpg --list-secret-keys

    you should not see any result from this command.

  6. then, crucial step, restore the private key and subkeys:

    gpg --decrypt $BACKUP_DIR/gnupg-backup.tar.pgp | tar -x -f - --to-stdout | gpg --import
  7. confirm GnuPG can see the secret keys: you should not see any Card serial no., sec>, or ssb> in there. If so, it might be because GnuPG got confused and still thinks the old key is plugged in.

  8. then go through the keytocard process again, which is basically:

    gpg --edit-key $FINGERPRINT

    then:

    keytocard
    1
    key 1
    keytocard
    key 2
    keytocard

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.

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.

  1. install the required software:

    apt install gnupg scdaemon
  2. restore the public key:

    gpg --import $BACKUP_DIR/public.key

    Note: 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.

  3. confirm GnuPG can see the secret keys:

    gpg --list-secret-keys

    you should not see any Card serial no., sec>, or ssb> in there. If so, it might be because GnuPG got confused and still thinks the old key is plugged in.

  4. set the trust of the new key to ultimate:

    gpg --edit-key $FINGERPRINT

    Then, in the gpg> shell, call:

    trust

    Then type 5 for "I trust ultimately".

  5. test signing and decrypting a message:

    gpg --clearsign < /dev/null
    gpg --encrypt -r $FINGERPRINT < /dev/null | gpg --decrypt

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 has been used at least once.

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

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.

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!

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

See also drduh's troubleshooting guide.

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.

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