The purpose of this exercise is to gain more familiarity with the Yubikey and how to better use the hardware encryption to secure development activities.

Part of the alure of a Yubikey (or hardware security key) is their ability to generate a private key on the hardware itself and is completely unique. However, it is usually recommended to have a “backup key” in the case one is compromized. In general, there can be two ways to think about redundancy for a security key:

  1. A hardware key with identical credentials (e.g The same GPG key is present on both sets of hardware)
  2. A redundant hardware key registered to a common service (e.g. Both keys are authorized, but are individually unique) For some activities, it may be better to take the approach of deploying the same set of keys to two sets of hardware, such as preserving your digital identiy with a GPG signing key. Whereas in the case of a password manager like Bitwarden, it makes more sense to register multiple hardware keys with the service to have a backup.

In this post, I’ll go over some of the capabilities of the Yubikey 5 and how I use the hardware to improve privacy and security.

Yubikey Setup

Yubico has EOL’d the GUI yubikey-manager and are now suggesting the usage of ykman which can be installed via python. There were a couple pre-requisites that should be installed first prior to installing the python app.

# PCS is PC/SC Smart Card Daemon
apt install pcscd libpcsclite-dev scdaemon opensc
python3 -m pip install --user yubikey-manager

Assuming the setup all is good valid, you should be able to see some information from the yubikey.

gpg --card-edit

If you encounter an issue with the device not being found, restart the service for the smart card daemon:

systemctl restart pcscd

GPG

We’ll start with GnuPG, GPG, or Gnu Privacy Guard which is an implmentation of the OpenPGP standard (Pretty Good Privacy). GnuPG allows you to sign and encrypt data for your privacy, it’s widely used in the realm of software for securing packages. We’ll import the GPG key on the hardware security device so that it is able to sign certificates and be used with software development tools like git or when we need to sign published packages. It’s also fun to go full Edward Snowden:

Arguing that you don’t care about the right to privacy because you have nothing to hide is no different from saying you don’t care about free speech because you have nothing to say. – Edward Snowden

Since we are living in the future - there are also some advancements in OpenPGP 3.4 such as support for Elliptic Curve Cryptography. If we wanted to take full advantage of the security of Yubikey, you could opt to generate the asymmetric key on the device in which case you can retrieve an attestation certificate from the key.

We’ll use the RSA Algorithm to generate our keys for this process, and opt to keep duplicate yubikeys by importing the keys instead of generating them on the device. This way if the Yubikey is ever comprimized, there will be a backup key and can revoke the existing subkeys.

The general process we’ll follow is similar to this walkthrough from Yubikey, but we’ll use an airgapped system for the original gpg key generation.

The airgapped system will be a bootable USB running The Amnesic Incognito Live System (Tails). In general, the setup follows this guide from debian. The main thing to note is to backup the original main keys before tranferring them to the Yubikey if you plan on having a backup, this is because after being transferred the keys on the host system will be replaced with stubs.

In general the process is:

  • Set up the main gpg key (gpg --full-gen-key)
  • Add subkeys for encryption, signing, and authentication (gpg --expert --edit-key 0xKEYID)
  • Generate a revocation certificate (gpg --gen-revoke 0xKEYID)
  • Export the public keys (gpg --export 0xKEYID)
  • Transfer the key to the hardware key (gpg --card-edit)

In the end, you’ll likely end up with three USB thumbdrives:

  • tails live USB with the main GPG keys stored in persistant storage
  • Revokation Certificate USB
  • Public GPG key / Private Key (stub) used to import the keys on a day-to-day development device

Note that the USB drive that is used to import the keys only contains the stub for the private key, which is used to tell the host system that the private key is stored on the hardware security key. The hardware security key is really what is used to sign and encrypt.

If you prefer longform documentation, there’s an advanced guide to gnupg for key creation.

Once the key(s) have been set up, it’s time to put them to test. There are a couple examples in this Yubikey guide for a quick test of signing and encrypting.

Keeping in the idea of privacy, you can integrate GPG encryption and signing into your email workflow using Thunderbird and a couple plugins. I go over this in more detail in my other post on how to setup a custom email mailbox.

To have my public key hosted at https://kenyon.app/kenyon-signing.key. As I use the key for more purposes and need it available to a wider audience, commonly GPG keys can be published the key to keyservers such as https://keys.openpgp.org/. You can find out more about who uses the key server and how it works here: https://keys.openpgp.org/about/usage/

Signing a Debian Package

One of the core use cases is to sign my own packages. To do this - I need to certify another set of keys that I’ll put on a build server. https://davesteele.github.io/gpg/2015/08/01/intermediate-gpg/

It appears that majority of the time, the debian packages themselves are not signed, but the repo instead. There’s a good article here explaining the steps using reprepro: https://blog.packagecloud.io/how-to-gpg-sign-and-verify-deb-packages-and-apt-repositories/

There’s also a getting started guide here: https://wiki.debian.org/DebianRepository/SetupWithReprepro

Alternatives to PGP for Signing Packages

Looking at alternatives for signing the packages. It appears there are some issues with PGP

Creating a Server Certificate

This process is to generate X.509 certificates with the tool gpgsm which is inherently different than gpg which is an implemenation of OpenPGP. https://www.gnupg.org/documentation/manuals/gnupg/Howto-Create-a-Server-Cert.html

Passkeys, FIDO2, and Webauthn

Welcome to the future with passkeys! Passkeys are the long awaited successor of passwords. Passkeys increase security and reduce authentication friction - they can be used to perform authentication in a single step using FIDO2.

For example, Bitwarden has beta login with passkeys as an alternative to user/password. In essense, WebAuthn is used and requires a browser with a capable psuedo-random function (PRF) extension. FIDO2 is based off WebAuthn and Client to Authenticator Protocol (CTAP) to perform authentication. A more detailed overview is available from Yubikey’s developer documentation.

OTP

One-time passwords (OTP) is another supported protocol of the Yubikey, which is supported out of the box. OTP is a common method of multi-factor authentication (MFA) and is commonly used in combination with a username and password. Yubikey’s OTP works with YubiCloud to validate the OTP. A OTP is generated when the hardware key is physically touched - from the Yubikey documentation:

A Yubico OTP is a 44-character, one use, secure, 128-bit encrypted Public ID and Password, near impossible to spoof. The OTP is comprised of two major parts: the first 12 characters remain constant and represent the Public ID of the YubiKey device itself. The remaining 32 characters make up a unique passcode for each OTP generated.

U2F

Another practical use for FIDO is with a Pluggable Authentication Module which uses the FIDO Universal 2nd Factor (U2F) which works to authenticate a user locally. It is used as a second factor to existing username password flows. Originally developed by google, it was contributed to the FIDO alliance - there are three core ways to use the protocol

  • Passwordless or without a token
  • With hardware security key
  • NFC with the security key (mobile)

There’s a good visualization of this from Yubikey’s notes on the authentication standard. The approach taken with the PAM is complement the user/password flow, where the user is prompted for the security key. After the key is tapped, the login flow is completed. This approach is what we’ll use for the logging into my desktop.

For implementing the flow, we’ll use the pam-u2f module offered from Yubico. There’s currently a security vulnerability in the version offered through my distro package manager, and instead opted to install a newer release from Yubico to resolve the CVE. There’s an accompanying guide for the U2F login on Ubuntu. Fedora also offers guidance for using Yubikeys with their OS.

SSH

You can also use the yubikey FIDO2 capabilities to generate a ssh key pair that is stored on the yubikey. This will let us sign all our commits - which validates we are the official author of the commit, and not just someone who forged the email and username. https://developers.yubico.com/SSH/Securing_SSH_with_FIDO2.html

Github supports ssh commit verification as of late 2022: https://github.blog/changelog/2022-08-23-ssh-commit-verification-now-supported/

In order to configure git to use the signing key - follow the documentation: https://git-scm.com/docs/git-config#Documentation/git-config.txt-usersigningKey https://calebhearth.com/sign-git-with-ssh

# Generate the hardware key (requires v5.2.3+)
ssh-keygen ssh-keygen -t ed25519-sk -O resident -O verify-required -C "your_email@example.com"

# Configure git to use the hardware key for signing
git config --global --add gpg.format ssh
git config --global --add user.signingkey "~/.ssh/id_ed25519_sk.pub"
git config --global --add commit.gpgsign true

# Configure allowed signees format is <email> <keytype> <pubkey> 
touch ~/.ssh/allowed_signers
# e.g. rike2277@colorado.edu sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAINwSYa2sVwsmaW118xvdLQ2PXcI/yhvo1qUJAtp6SnUBAAAABHNzaDo=

git config --global  gpg.ssh.allowedSignersFile "~/.ssh/allowed_signers"

# To import the resident keys (stored on the Yubikey) for a new machine, plug in and run:
ssh-keygen -K

# Need to install a way for git to prompt when signing - otherwise error with:
# error: Signing file /tmp/.git_signing_buffer_tmp4BdmyE
# Couldn't sign message (signer): agent refused operation?
# Signing /tmp/.git_signing_buffer_tmp4BdmyE failed: agent refused operation?
apt install ssh-askpass
eval "$(ssh-agent -s; SSH_ASKPASS=/usr/bin/ssh-askpass)"

GDM

Gnome Display Manager (GDM) is the display manager for my system. My desktop runs POP OS! 22.04 LTS with GDM 42.0 and encountered an issue implementing logging in with the Yubikey. To avoid the issue when logging in - needed to update /etc/gdm3/greeter.dconf-defaults

[org/gnome/login-screen]
enable-smartcard-authentication=false # Issues with Yubikey on login

Depending on how integrated you’d like the Yubikey usage, the relevant configuration files are listed below:

  • /etc/pam.d/login
  • /etc/pam.d/gdm-password

Optional to integrate with sudo:

  • /etc/pam.d/sudo
  • /etc/pam.d/su
  • /etc/pam.d/sudo-i

In general the line item required is to load the pam_u2f shared library with the u2f_keys authorization file. I opted to cue the user, otehrwise the prompt will await user input (touching the hardware security key) without notification.

auth       required   pam_u2f.so authfile=/etc/Yubico/u2f_keys cue

When updating the files, I needed to change the ownership of the authfile (/etc/Yubico/u2f_keys) which was giving a warning in the journal. I ended up keeping the security key touch to log in, but removed the functionality for sudo since it became tedious quickly.

Closing Notes

This was primarily targeted at using hardware security keys in day-to-day activities, getting a securely setup importing custom OpenPGP keys to the hardware, and some example applications that use passkeys and hardware based authentication.

Yubikey offers a passkey workshop for developing an application using passkeys, which will be of interest as we use the technology more in development activities.