Why Yubikey

I’ve been using 2-factor authentication with my Google account for a few years: when logging in, in addition to my password, I need to enter a code that an application on my phone displays.

Recently Google introduced an alternative to the codes (for Chrome users): a Yubikey USB token that supports U2F - Universal 2nd Factor - protocol. That sounded cool, and I decided to get me one :)

There is no new information in this post; everything is already available on the Internet - and probably in more popular and easier to find places :) Consider this as a note to self - so that I know what to do next time :)

Yubico has different tokens, some of which support U2F, but Yubikey Neo supports everything other tokens do - plus NFC, so it can be used with the smartphones (if they are not from Apple :)).

Yubikey Neo is also a SmartCard, which comes with OATH and GPG applets installed, and seems to allow applet installation. This is important, since firmware on Yubico tokens is not upgradeable, but on Yubikey Neo new functionality can be deployed via applets. Hopefully, that means that I won’t have to replace the $50 Yubikey Neo every time Yubico releases new firmware :)

Recently, I bought myself a Yubikey 4C, which doesn’t have NFC, but it plugs directly into my phone’s USB-C port, so that’s ok.

udev

It seems that recent Linux releases recognize the token out of the box, but custom udev rules are needed to allow non-root users accessing the USB device representing the YubiKey (which is needed to use the smart-card functionality).

On current Fedora releases, you need to install package “ykpers” (or “yubikey-personalization-gui” that drags it in); it puts appropriate udev rules in /lib/udev/rules.d/69-yubikey.rules. Reboot or force udev daemon to reload the rules in some other way.

Enabling yubikey features

Not all features are enabled initially; to turn the Yubikey into “OTP/U2F/CCID composite device” issue command (from the “ykpers” package that you already installed):

$ ykpersonalize -m6

(Adding 80 to the mode activates an “eject flag” that I do not think I need.)

U2F is currently far from being universally supported, so for different sites different approaches to security enhancements have to be used.

Yubikey Authenticator

Yubikey provides two “slots”, both of which can be used for OATH credentials. Length of the tap on the token determines which one the user intends. Although convenient - the user doesn’t need to copy codes from the phone - this approach is limited to 2 distinct OATH credentials (also, varying-length taps are weird :)).

OATH applet installed on Yubikey Neo can be used to store many more than 2 credentials.

To use it, standard Google Authenticator application needs to be replaced by Yubikey Authenticator. Just like the Google’s one, it shows codes that the user has to enter when logging into a site, but unlike Google one, nothing is stored on the phone: Yubikey Neo has to be tapped to the phone to see the codes (or add credentials). Yubikey 4C needs to be plugged into the phone, since it doesn’t support NFC. (Direct plugging of Yubikey wasn’t supported on early versions of the Yubikey Authenticator; it is now.)

2-factor authentication of this nature can be used with sites that support it but do not (yet) support U2F - which used to include, for instance, GitHub and WordPress (it seems that Github does support U2F now).

Inconvenience of entering codes manually can theoretically be alleviated by the yubioath-desktop application, which in practice didn’t work for me.

LastPass

For sites that do not support U2F or codes-based 2-factor authentication, the only line of defence is the password, so it needs to be strong - which means, long, weird, unique - and hard to remember.

While we are waiting for U2F to become universal so that we can go back to having the same short and easy to remember password on all the sites, strong passwords are only practical if you use a password manager - a service that stores your passwords, detects when you are logging into a site it knows about, and supplies the password.

I chose one such password manager - LastPass. I stored login information about the sites I frequent in it, drained passwords from the browser’s built-in password manager and told the browser to stop collecting my passwords.

When all the passwords are stored in a password manager, the question arises: how secure is the password manager itself? Well, one of the reasons why I chose LastPass (in addition to raving reviews) is - it supports multiple flavors of 2-factor authentication, including the Authenticator codes - and the OTP (one-time passwords) that Yubikey Neo supports directly!

I paid for LastPass Premium to gain access to the 2-factor authentication feature ($12/year) and registered my token. Now to log into my LastPass account I need to type in my password and insert and tap the Yubikey Neo.

LastPass allows up to three tokens to be registered for one account, to allow for the recovery in case the token is lost. One can get a (cheaper) OTP-only Yubico token for this purpose - or get Yubike Neo/4C tokens for the family members and cross-register them :)

SSH

Remaining piece of the puzzle: SSH. I use SSH to log into my home server remotely. I also use it to push code into my GitHub repositories.

I want to store my private SSH key on the Yubikey. With this configuration, I do not need my private key on the machine I open SSH connection from, which increases my feeling of security. On top of that, the key can be deleted from any machine that I ssh into and then open further ssh connection from! All it takes is - add ForwardAgent yes in ~./ssh/config for the particular host! This is really cool!

Yubikey as a PIV Smart Card

I do not know how did I overlook the PIV functionality of the Yubikey and decided to use GPG instead (see below), but here is the current state of the PIV support.

To figure out how to set this up, I used Yubikey Handbook and Yubico documentation.

We need the smart-card demon installed and running (as far as I can tell, it doesn’t interfere with the GPG functionality any more):

   # dnf install pcsc-lite opensc yubico-piv-tool
   # systemctl enable pcscd; systemctl start pcscd
   $ opensc-tool -l

Change PIN and PUK:

   $ yubico-piv-tool -a change-pin   # from default 123456
   $ yubico-piv-yool -a change-puk   # from default 12345678

Default Management Key is: 010203040506070801020304050607080102030405060708

(It is possible to generate the private key directly on the Yubikey:

   $ yubico-piv-tool -s 9a -a generate -o key.pub.pem

but I prefer to do it on the outside, so that I can put the same private key onto multiple tokens, stash it somewhere etc.)

Generate the keypair in the current directory:

$ ssh-keygen -f key

Export the public key in PEM format (known to ssh-keygen as PKCS8, not PEM :)):

   $ ssh-keygen -f key -e -m PKCS8 > key.pub.pem

Import the key:

   $ yubico-piv-tool -s 9a -a import-key -i key

Certify the key (for one year):

   $ yubico-piv-tool -s 9a -a verify-pin -a selfsign-certificate -S '/CN=login@domain SSH key' -i key.pub.pem -o cert.pem

If you get error: Failed to begin pcsc transaction, rc=80100003 see Temporary Detour below :)

Import the certificate:

   $ yubico-piv-tool -s 9a -a import-certificate -i cert.pem

Check status:

   $ yubico-piv-tool -a status

Export public key from the token in OpenSSH format:

   $ ssh-keygen -D /usr/lib64/opensc-pkcs11.so -e

To use the key from the token, add to ~/.ssh/config (for specific hosts or globally):

   PKCS11Provider /usr/lib64/opensc-pkcs11.so

This way, SSH agent is running, but identity isn’t added to it - and so it can’t be reused on machines SSHed into; to add Yubikey SSH identity to SSH agent:

   $ ssh-add -s /usr/lib64/opensc-pkcs11.so

See it:

   $ ssh-add -L

Running gpg-agent (needed for the GPG approach below) may cause problems.

When Yubikey is removed and re-inserted, ssh-agent needs to be killed and the key re-added for some reason. I use this alias in ~./bashrc:

  alias yk='pkill ssh-agent; pkill gpg-agent; ssh-add -s /usr/lib64/opensc-pkcs11.so'

QUESTIONS:

  • Is there a way to avoid this?
  • How to hook graphical pin-entry into this flow?
  • How to make the identity from the token default one?

Temporary Detour (no longer needed)

Current version of Fedora ships with yubico-piv-tool version 1.5.0; current latest version is 1.5.2; both seem to suffer from an issue that causes the tool to crash truing to sign the certificate. The issue is fixed in the trunk, but until version 1.5.3 is released and percolates down to the distribution, the workaround is to build the tool from GIT.

Prerequisites (some checked by ./configure, some not):

  # dnf install openssl-devel check-devel pcsc-lib-devel gengetopt help2man

Then, in the sources distribution:

  $ autoreconf --install
  $ ./configure
  $ make
  $ make install   # I used the resulting tool without installing

Yubikey as a GPG Smart Card

Yubikey has a GPG Applet that can be used to store GPG keys. It doesn’t seem to be possible to turn an existing SSH key into a GPG key, but it is possible to use a GPG key for SSH authentication, and that is what I decided to do.

Since I do not use GPG for email, I did not have a GPG key. It is possible to generate the key on the Neo itself, but it is impossible to extract the key from it (for obvious reasons). I want to have a backup of the key (to stash in a safe place), so I needed to generate the key in a normal way and put it on the Neo.

There is a lot of stuff out there about the gpg-agent configuration, ssh support, sshcontrol, keygrips etc. This document by Aric Gardner helped me get the things running.

Configure GPG to use agent

Make sure file ~/.gnupg/gpg.conf exists and contains a line use-agent.

Make sure file ~/.gnupg/gpg-agent.conf exists and contains a line enable-ssh-support.

Make sure file ~/.gnupg/scdaemon.conf exists and contains a line allow-admin.

Ensure GPG agent starts

Put into .bashrc:

  if [ ! -f /tmp/gpg-agent.env ]; then
      killall gpg-agent;
      eval $(gpg-agent --daemon --enable-ssh-support > /tmp/gpg-agent.env);
  fi
  . /tmp/gpg-agent.env

Generate Key and Subkeys

When generating the key, I did not add a photo to it, nor did I publish it to the key servers. People who actually use GPG would know how to do all that :)

To generate the key:

  $ gpg2 --gen-key

I picked “RSA and RSA (default)”, 4096 bits long key, key does not expire.

To create a backup of the generated key XXXXXX:

  $ gpg2 --armor --export XXXXXX > XXXXXX-master.txt

To generate subkeys:

  $ gpg2 --expert --key-edit XXXXXX

In gpg2, I issued command “adkey”, chose “RSA (set your own capabilities)” and 2048 bits long key (Neo can’t store keys longer than that).

Although only authentication key is needed for the SSH logins, I generated subkeys of each flavor: signing, encryption and authentication.

To create backups:

  $ gpg2 --armor --export XXXXXX > XXXXXX-master-with-subkeys.txt
  $ gpg2 --armor --export-secret-keys XXXXXX > XXXXXX-secret-keys.txt
  $ gpg2 --armor --export-secret-subkeys XXXXXX > XXXXXX-secret-subkeys.txt

I am sure that not all the backups are needed, but so far did not learn enough about the subject to figure out which are not ;)

Set Up Yubikey GPG SmartCard

To set up the GPG SmartCard on the Neo, I did:

  $ gpg2 --card-edit

Enable admin commands with “admin”; use “passwd” to change the Admin PIN from default 12345678 and PIN from default 123456; use “name”, “lang”, “sex” and “login” to set the card’s parameters.

Move Keys onto the Card

  $ gpg2 --expert --key-edit XXXXXX

Switch to secret keys with the “toggle” command.

Select the key to move to the card using “key n” commands (toggles the marker on subkey n).

Use “keytocard” command to move the marked key into appropriate slot on the card.

It Works!

When the smart card is connected, “ssh-add -L” should output the public key corresponding to the authentication key from the card (no need to insert any “keygrips” into the sshcontrol file :)). It needs to be listed on the machine you are logging into :)

I never had to do this before, but on fc28 in order for the ssh key to work, I have to do (see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=835394):

  $ gpg-connect-agent updatestartuptty /bye

If the public key is published somewhere, GPG applet on the token can be configured with the URL so that it can fetch the key. On a machine where GPG doesn’t know about my the key, I used “fetch” in gpg2 –card-edit.

Obsolete

Previously, additional steps were required to set things up. In recent releases, some of them are no longer needed - but I preserved previous instructions here just in case :)

udev

Previously, I had to manually install the following udev rule:

  ACTION=="add|change"
  SUBSYSTEM=="usb",
  ATTRS{idVendor}=="1050",
  ATTRS{idProduct}=="0010|0110|0111|0113|0114|0115|0116|0120|0401|0402|0403|0405|0406|0407|0410",
  TAG+="uaccess"

Above rule works for Fedora, but not for Debian-based Raspbian running on my Raspberry Pi. There, instead of TAG, I use MODE:="0666". It is ugly, and only works with “final” assignment (:=), but it works…

Install GnuPG2

Both version 1 (gpg) and version 2 (gpg2) of the Gnu Privacy Guard can be used for the key generation, but only gpg2 is capable of writing the keys to the Neo. Previously, I had to make sure that the following packages are installed: gnupg2, gnupg2-smime (contains scdaemon, which is needed) and pinentry-gtk (GTK UI for PIN entry) (on Raspbian, I had to install packages gnupg2, gnupg-agent and scdaemon).

Uninstall pcsc-lite

There is some kind of a race for the card access between GPG and pcsc-lite (which is installed by default). Previously, I removed pcsc-lite, pcsc-lite-libs, pcsc-lite-ccid, pcsc-perl and dependent packages: opensc, coolkey, openconnect, NetworkManager-openconnect, NetworkManager-openconnect-gnome. I hope this gets resolved soon, and package uninstallation won’t be necessary.

Disable Gnome keyring agent

On Gnome desktop (Fedora, not Raspberry Pi) gnome-keyring-daemon used to interfere with the gpg-agent and had to be disabled before smart-card applet on the Neo could be used:

  $ if [[ $(gconftool-2 --get /apps/gnome-keyring/daemon-components/ssh) != "false" ]]; then
        gconftool-2 --type bool --set /apps/gnome-keyring/daemon-components/ssh false
    fi