YubiKey, Void Linux and GPG

April 30, 2018 3 minutes

Context

The Yubikey 4 is an HSM that can be used to store GPG keys, among other things.

After following Lars Wirzenius’ tutorial in an air-gapped Tails live session, we had 3 subkeys in our Yubikey, which then we tested in the same live session with simple commands like:

$ echo "Secret Message" > test.txt
$ gpg --encrypt -r user@example.com test.txt
... here we enter our YK pin, test.txt.gpg is created.
$ gpg --decrypt --output test.output.txt test.txt.gpg
$ diff test.txt test.output.txt

Signing also worked properly, and after removing the Yubikey decryption was not possible: a pinentry popup asked us to insert the card, and then enter the PIN.

After that, we imported our public key in our main Void Linux system, connected our Yubikey and ran gpg2 --card-status to generate the private key stubs that tell GPG to look for private keys in the Yubikey, instead of the keyring.

Problem

Interacting with the card is possible only as root.

$ gpg2 --card-status
gpg: selecting openpgp failed: No such device
gpg: OpenPGP card not available: No such device

# gpg2 --card-status

Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240102000087596366710000
Version ..........: 2.1
Manufacturer .....: Yubico
Serial number ....: [redacted]
Name of cardholder: Cristian Ojeda Flores
... and so on.

Solution

Install the following packages from the repo:

  • gnupg2-scdaemon
  • libu2f-host

From the following pinentry packages, install those you need:

  • pinentry: for CUI/CLI programs, like gpg2 and [maybe] mutt.
  • pinentry-emacs: for GNU/Linux/Emacs OS users. Haven’t tried it myself, though.
  • pinentry-gtk: required for pinentry in Thunderbird with the Enigmail plugin.
  • pinentry-qt: Qt programs, but haven’t used it myself.

Copy /usr/lib/udev/rules.d/70-u2f.rules to /etc/udev/rules.d/70-u2f.rules (if there’s no /etc/udev/rules.d/ then create it).

Change the following in /etc/udev/rules.d/70-u2f.rules:

# Yubico YubiKey
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0113|0114|0115|0116|0120|0200|0402|0403|0406|0407|0410", MODE="0660", GROUP="plugdev"

To:

# Yubico YubiKey
ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0113|0114|0115|0116|0120|0200|0402|0403|0406|0407|0410", MODE="0660", GROUP="plugdev"

Create the plugdev group if needed (friendly reminder: check /etc/groups). Add your user to it.

# groupadd plugdev
# usermod -a -G plugdev YOUR_USER

Finally, you may also want to install ykpers, which contains the ykpersonalize command to make additional changes in the Yubikey (change operation modes, etc.). I don’t use it.

Relog or reboot.

How it works?

The root user can access the device, so we know it’s not an issue of missing drivers or any other similar fuckery. What’s not so obvious however, is the message that GPG gives us:

$ gpg2 --card-status
gpg: selecting openpgp failed: No such device
gpg: OpenPGP card not available: No such device

However, trying to use ykpersonalize uncovers the real issue:

$ ykpersonalize
USB error: access denied

The access error as normal user (but not as root) has to do with udev rules. As usual, there’s info on the ArchWiki: udev - ArchWiki.

Now, at some point I read about installing libu2f-host, but I don’t remember where (maybe it came preinstalled?). The important part is, that it contains a very useful udev rule written for us.

KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0113|0114|0115|0116|0120|0200|0402|0403|0406|0407|0410", MODE="0660", GROUP="plugdev"

1050 is the vendor id for Yubico, and 0407 is the product id of our YubiKey 4, so this rule should give any member of the plugdev group r+w access to the card.

We can verify the vendor and product id of USB devices by running lsusb:

$ lsusb | grep -i yubikey
Bus 005 Device 012: ID 1050:0407 Yubico.com Yubikey 4 OTP+U2F+CCID

But, there’s something odd: the rule matches only the HID raw subsystem, and we got an USB error.

We found a related issue on GitHub: libu2f-host udev rule not triggering. And just as it says, removing the KERNEL and SUBSYSTEM attributes causes the rule to match USB devices too. We can check that by simply running ls:

ls -la /dev/bus/usb/005/012
crw-rw---- 1 root plugdev 189, 524 Apr 30 17:38 /dev/bus/usb/005/012