I recently ordered two Yubikey devices from Yubico, partly because of a special offer from Github. I ordered both a Yubikey NEO and a Yubikey 4, although I am not sure I remember why I ordered two - you can see their Yubikey product comparison if you want to figure that out, but basically, the main difference is that the NEO has support for NFC while the "4" has support for larger RSA key sizes (4096).
This article details my experiment on the matter. It is partly based on first hand experience, but also links to various other tutorials that helped me along the way. Especially thanks to folks on various IRC channels that really helped me out in understanding this.
My objective in getting a hardware security token like this was three-fold:
- use 2FA on important websites like Github, to improve the security of critical infrastructure (like my Borg backup software)
- login to remote SSH servers without exposing my password or my private key material on third party computers
- store OpenPGP key material on the key securely, so that the private key material can never be compromised
To make a long story short: this article documents step 2 and implicitly step 3 (because I use OpenPGP to login to SSH servers). However it is not possible to use the key on arbitrary third party computers, given how much setup was necessary to make the thing work at all. 2FA on the Github site completely failed, but could be used on other sites, although this is not covered by this article.
I have also not experimented in details the other ways the Yubikey can also be used (sorry for the acronym flood) as:
- PIV (Personnal Identity Verification): basically to run your own Certificate Authority
- OTP (One Time Password): allows sites to request a OTP from a Yubikey server validator. I haven't explored that mode since it requires a third party server and more modifications on the server side.
- OATH (Initiative for Open Authentication): a purely "offline" and more "traditionnal" approach to OTP. In this mode, you can authenticate with only the token given by the key and a shared secret. Maybe i will follow this OATH tutorial eventually.
Update: OATH works! It is easy to configure and i added a section below.
After experimenting with the device and doing a little more research, I am not sure it was the right decision to buy a Yubikey. I would not recommend buying Yubikey devices because they don't allow changing the firmware, making the device basically proprietary, even in the face of an embarrassing security vulnerability on the Yubikey NEO that came out in 2015. A security device, obviously, should be as open as the protocols it uses, otherwise it's basically impossible to trust that the crypto hasn't been backdoored or compromised, or, in this case, is vulnerable to the simplest drive-by attacks.
Furthermore, it turns out that the primary use case that Github was promoting is actually not working as advertised: to use the Yubikey on Github, you actually first need to configure 2FA with another tool, either with your phone's text messages (SMS) or with something like Google Authenticator. After contacting Github support, they explained that the Yubikey is seen as a "backup device", which seems really odd to me, especially considering the promotion and the fact that I don't have a "smart" (aka "Google", it seems these days) phone or the desire to share my personal phone number with Github.
Finally, as I mentioned before, the fact that those devices are fairly new and the configuration necessary to make them work at all is completely obtuse, non-standardized or at least not available by default on arbitrary computers makes them basically impossible to use on other computers than your own specially crafted gems.
The Yubikey, when inserted into a USB port, seems to be detected properly. It shows up both as a USB keyboard and a generic device.
déc 14 17:23:26 angela kernel: input: Yubico Yubikey NEO OTP+U2F as /devices/pci0000:00/0000:00:12.0/usb3/3-2/3-2:1.0/0003:1050:0114.0016/input/input127 déc 14 17:23:26 angela kernel: hid-generic 0003:1050:0114.0016: input,hidraw3: USB HID v1.10 Keyboard [Yubico Yubikey NEO OTP+U2F] on usb-0000:00:12.0-2/input0 déc 14 17:23:26 angela kernel: hid-generic 0003:1050:0114.0017: hiddev0,hidraw4: USB HID v1.10 Device [Yubico Yubikey NEO OTP+U2F] on usb-0000:00:12.0-2/input1
We'll be changing this now - we want to to support OTP, U2F and CCID. Don't worry about those acronyms now, but U2F is for the web, CCID is for GPG/SSH, and OTP is for the One Time Passwords stuff mentionned earlier.
I am using the Yubikey Personalization tool from
jessie one is too old, according to Gorzen. Indeed, I found
out that the
jessie version doesn't ship with the proper
rules. Also, note that we need to run as
sudo otherwise we get a
$ sudo apt install yubikey-personalization/stretch $ sudo ykpersonalize -m86 Firmware version 3.4.3 Touch level 1541 Program sequence 1 The USB mode will be set to: 0x86 Commit? (y/n) [n]: y
To understand better what the above does, see the NEO composite device documentation.
The next step is to reconnect the key, for the
udev rules to kick
in. If you were like me, you enthusiastically plugged in the device
before installing the
yubikey-personalization package, and the
udev rules were not present then.
Various operations will require you to enter a PIN when talking to the key. The default PIN is 123456 and the default admin PIN is 12345678. You will want to change that, otherwise someone that gets a hold of your key could do any operation without your consent. For this, you need to use:
$ gpg --card-edit > passwd > admin > passwd
Be sure to remember those passwords! Of course, the key material on the Yubikey can be revoked when you loose the key, but only if you still have control of the master key, or if you have a OpenPGP revocation certification (which you should have).
To do OpenPGP operations (like decryption, signatures and so on), or SSH operations (like authentication on a remote server), you need to talk with GPG. Yes, OpenPGP keys are RSA keys that can be used to authenticate with SSH servers, that's not new and I have already been doing this with Monkeysphere for a while. Now the challenge is how to make GPG talk with the Yubikey.
So the next step is to see if
gpg can see the key alright, as
described in the Yubikey importing keys howto - you will need first
pcscd (according to this howto) for
gpg-agent to be able to talk with the key:
$ sudo apt install scdaemon gnupg-agent pcscd $ gpg-connect-agent --hex "scd apdu 00 f1 00 00" /bye ERR 100663404 Card error <SCD>
Well that failed. At this point, touching the key types a bunch of seemingly random characters wherever my cursor is sitting - fun but totally useless still. That was because I failed to reconnect the key: make sure the udev rules are in place and reconnect the key, the above should work:
$ gpg-connect-agent --hex "scd apdu 00 f1 00 00" /bye D 01 00 10 90 00 ..... OK
This shows it is running the firmware 1.10, which is not vulnerable to the infamous security issue.
(Note: I also happened to install
gpgsm because of
this suggestion but I am not sure they are required at all.)
To make GPG work with SSH, you need somehow to start
ssh support, for example with:
gpg-agent --daemon --enable-ssh-support bash
Of course, this will work better if it's started with your Xsession. Such an agent should already be started, so you just need to add the ssh emulation to its configuration file and restart your X session.
echo 'enable-ssh-support' >> .gnupg/gpg-agent.conf
In Debian Jessie, the
ssh-agent wrapper will not start if it
detects that you have already one running (for example from
gpg-agent) but if that fails, you can try commenting out
/etc/X11/Xsession.options to keep it from
starting up in your session. (Thanks to
stackexchange for that reference.)
Here I assume you have already created an authentication subkey on your PGP key. If you haven't, I suggest trying out simply monkeysphere gen-subkey, which will generate an authentication subkey for you. You can also do it by hand by following one of the OpenPGP/SSH tutorials from Yubikey, especially the more complete one. If you are going to generate a completely new OpenPGP key, you may want to follow this simpler tutorial here.
Then you need to move your authentication subkey to the Yubikey. For
this, you need to edit the key and use the
$ gpg2 --edit-key firstname.lastname@example.org > toggle > key 2 > keytocard > save
Here, we use
toggle to show the OpenPGP private key material. You
should see a key marked with
Authentication. Mine was the
second one so I selected it with
key 2 which put a star next to
keytocard command moved it to the key and
save ensure the
key was removed from the local keyring.
Obviously, backups are essential before doing this, because it's perfectly possible to loose that key in the process, for example if you destroy or lose the key or forget the password. It's probably better to create a completely different authentication subkey for just this purpose, but that may require reconfiguring all remote SSH hosts, and you may not want to do that.
Then SSH should magically talk with the GPG agent and ask you for the PIN! That's pretty much all there is to it - if it doesn't, it means that gpg-agent is not your SSH agent, and obviously things will fail...
Also, you should be able to see the key being loaded in the agent when it is:
$ ssh-add -l 2048 23:f3:be:bf:1e:da:e8:ad:4b:c7:f6:60:5e:03:c2:a6 cardno:000603647189 (RSA)
.. that's about it! I have yet to cover 2FA and OpenPGP support, but that got me going for a while and I'll stop geeking around with that thing for now. It was fun, for sure, but not sure it's worth it for now.
This is pretty neat: it allows you to add two factor authentication to a lot of things. For example, PAM has such a module, which I will configure here to allow myself to login to my server from untrusted machines. While I will expose my main password to keyloggers, the OTP password will prevent that from being reused. This is a simplified version of this OATH tutorial.
We install the PAM module with:
sudo apt install libpam-oath
Then, we can hook it into any PAM consumer, for example with
--- a/pam.d/sshd +++ b/pam.d/sshd @@ -1,5 +1,8 @@ # PAM configuration for the Secure Shell service +# for the yubikey +auth required pam_oath.so usersfile=/etc/users.oath window=5 digits=8 + # Standard Un*x authentication. @include common-auth
We also needed to allow OTP passwords in
sshd explicitely, with:
This will force the user to enter a valid oath token on the server. Unfortunately, this will affect all users, regardless of whether they are present in the
users.oath file. I filed bug #807990 regarding this, with a patch.
Also, it means the main password is still exposed on the client machine - you can use the
sufficient keyword instead of
required to workaround that, but then it means anyone with your key can login to your machine, which is something to keep in mind.
/etc/users.oath file needs to be created with something like:
#type username pin start seed HOTP anarcat - 00
00 is obviously a fake, and insecure string. Generate a proper one with:
dd if=/dev/random bs=1 count=20 status=none | hexdump -v -e '/1 "%02x"' ; echo
Then the shared secret needs to be added to the Yubikey:
ykpersonalize -1 -o oath-hotp -o oath-hotp8 -o append-cr -a
You simply paste the random secret you created above, when prompted, and that shared secret will be saved in a Yubikey slot for future use. Next time you login to the SSH server, you will be prompted for a
OATH password, you just touch the button on the key and it will be pasted there:
$ ssh -o PubkeyAuthentication=no anarc.at One-time password (OATH) for `anarcat': Password: [... logged in!]
Final note: the centralized file approach makes it hard, if not impossible, for users to update their own secret token.. It would be nice if there would be a user-accessible token file, maybe
~/.oath? Filed feature request #807992 about this as well.
It could happen that your key stops functioning because you failed entering the PIN too many times. The solution is to use the admin password to reset the basic PIN password. You can see the retry counter here:
anarcat@angela:monkeysign$ gpg --card-edit [...] Name of cardholder: [not set] Language prefs ...: [not set] Sex ..............: unspecified URL of public key : [not set] Login data .......: [not set] Signature PIN ....: forced Key attributes ...: 2048R 2048R 2048R Max. PIN lengths .: 127 127 127 PIN retry counter : 0 3 3 Signature counter : 0 Signature key ....: [none] Encryption key....: [none] [...]
In the above, you can see I ran out of retry attempts for my main PIN:
PIN retry counter : 0 3 3
It should rather look like this:
PIN retry counter : 3 3 3
To fix this, you need to go back in
gpg/card> admin Admin commands are allowed gpg/card> passwd gpg: OpenPGP card no. [...] detected 1 - chanége PIN 2 - unblock PIN 3 - change Admin PIN 4 - set the Reset Code Q - quit Your selection? 2 PIN unblocked and new PIN set. 1 - change PIN 2 - unblock PIN 3 - change Admin PIN 4 - set the Reset Code Q - quit Your selection? q gpg/card>
passwd step, when selecting
2, I got prompted for the admin
PIN and I could set and confirm a new regular PIN. Of course, the
above assumes that you have the admin PIN correctly set and you
Sometimes, you will get something like this:
$ gpg --card-edit gpg: selecting openpgp failed: ec=6.108 gpg: la carte OpenPGP n'est pas disponible : erreur générale gpg/carte> 2$
This roughly translates to "general error" or "general failure". I
can't figure out exactly what's going on here, but I figured out that
scdaemon fixes the problem:
And off we go again!
It is possible that the key and the server get out of sync if you mistakenly tap the button too many times without actually authenticating. There's a tolerance range on the server side, but it can quickly be exhausted (and with good reason).
To reset the counters, you should simply rerun the configuration
procedure and replace the
users.oath line with a new one.