It has been some time since my last article on encryption and using PGP. For some time now I have been wanting to stop using the Yubikey for OpenPGP. Why? Well it simply boils down to inconvenience and time. I now spend less time using my Mac desktop (I rarely boot it up) and spend most of my computing time on my late 2013 MacBook. This is the very last Apple computer made that includes an NVIDIA card (important for my live-streaming usage) and is still supported. The computer has been extremely reliable, though it did die on me unexpectedly at times. The battery had more than 1,100 cycles (it is rated for 1,000). I ordered a replacement battery and picked up an 11 inch iPad Pro as screenplay reading device and backup for the MacBook. It's possible to make git commits from the iPad but Yubikey support with USB-C is limited and less portable. So I've been meaning to locate the offline master key to replace the subkey stubs so I could use them on my mobile devices.
To put it into a list, here is why I am moving away from hardware OpenPGP
- It's inconvenient. My laptop has two USB-A ports and one of those is taken by my logitech mouse. The way the Yubikey sits doesn't give me great confidence that it won't break or bend in the port. I would recommend using the Nano if your primary computer device ends up being a laptop.
- It is slow. It takes about a second to sign a git commit and you have to mess with PINS. This doesn't sound like much but as someone who commits frequently the hassle slows down the flow. Changes to my bash profile remain uncommitted as I don't have my Yubikey always plugged in now. Big rebases are painful.
- I'd like to start using PGP more widely. That means decrypting emails on the iPad, remote servers, and more.
- I'm not currently writing missle software. My threat model just doesn't justify a hardware PGP key. Even then there are better tools for encryption such as age which mostly exist outside of the Yubikey.
- I trust my own macOS. The disk is now encrypted. I keep it free from malware and have an inbound firewall. I will still use the Yubikey for OpenPGP operations on less-trusted hosts (i.e. my Windows partitiion if needed).
I probably will eventually pick up a Yubikey Nano for convenience. It is useful for PIV sudo access where a short pin is easier than a long password. Since the hardware locks itself permanently after three incorrect tries, it's sufficiently secure to authorize the admin account (the entire disk is still protected by FileVault so knowledge of the password is still required)
I wanted to quantify just how slow git signing is. Here are the results of a simple benchmark.
RSA 4096 git sign CPU 100x real 0m15.852s user 0m0.920s sys 0m1.497s RSA 4096 git sign card 100x real 1m29.188s user 0m0.947s sys 0m1.550s RSA 2048 git sign CPU 100x real 0m14.233s user 0m0.880s sys 0m1.387s RSA 4096 git sign card 1x real 0m0.888s user 0m0.014s sys 0m0.023s Unsigned commit single 1x real 0m0.028s user 0m0.010s sys 0m0.015s
Not the significant difference between the real time for RSA4096 on a card and RSA2048 on the laptop CPU. Even RSA4096 has similar speeds on the CPU but there's little reason to bother with RSA4096 for the next ten years. After all, GitHub uses RSA2048 keys so a verified commit is only as strong as the weakest signature. Even then a SHA-1 collision could happen before a commit signature is forged. Any motivated attacker would have better luck harming the integrity of a repo by compromising a git push key than finding the right primes.
I'll be honest, I like seeing those green "Verified" badges. That's reason enough for me to sign a commit. Others have pointed out that if you sign tags signing git commits is of dubious value. I haven't seen any real solution to what to do after mandating GPG signatures. How do you bootstrap all those GPG keys, mark them as trusted, and then verify on a new machine? The more committers you have the trickier that gets. Sure it can be done, but it isn't pretty. For these reasons, a shorter signing key that is still sufficiently secure until 2030 is good enough for me.
Keybase was purchased by Zoom. Zoom was well-known for many privacy issues and poor-practices. Others noted cooperation with the CCP.
The aquisition was covered by many in the tech world as "the end of Keybase". I find this unfortunate because the Keybase aquisition was a clear move by Zoom to hire good cryptographers and improve the security stance of the company. Further, the whole point of end-to-end cryptography is that you don't need to trust the server. It's possible that Keybase could maliciously change their client to no longer use the encryption Keybase built, but that appears even less likely now that doing so would harm the image of a large company intent on gaining more user trust and a better security posture.
What Keybase built was very innovative and made something that only appealed to the tech crowd (PGP and asymetric encryption) usable by the masses. The key-per-device model is a saner approach than a long-lived PGP key shared across devices. The saltpack library adds forward secrecy and other useful properties (i.e. signcryption) that you can't get with PGP in practice.
All of that said, the larger developer community has chosen to move on from Keybase since the aquisition. I am not without my critique:
- The future of the Keybase product is unclear. Keybase was still a niche product relative to Zoom and putting new security developer resources on the main product was the obvious business choice.
- Keybase was very innovative but the product felt like a grand experiment. Indeed the product was impressive for a young company. But the product felt heavy on macOS. I noticed that Keybase File System is very chatty with the internet in the background.
- The chat UI felt sluggish. It's something I wanted to recommend to those outside the tech space, but I just couldn't. Signal is a much more user-friendly alternative despite Keybase having more technical innovation. There's wisdom in the old unix philosophy "do one thing and do it well".
Backups are important. I noticed this when switching phones. Many of my 2FA codes only exist on my new phone. Losing said phone would lock me out of many accounts. Securing those is a planned project once my PGP key is secured. It also took me some time to find my PGP master key flash drive. If I keep the master key offline, what happens when the flash drive stops working? I'd have to retire my entire key. Same thing if the subkeys were lost, I'd lose access to any encrypted files. Another reason I am switching away from the Yubikey is that losing it would be disasterous.
Let's get started
With all that said, it is time to update my PGP situation. I'll outline the steps I took here, but this isn't intended as a complete tutorial. Feel free to follow along, and if you are just getting started, I'm going to recommend following the ITPol Guide.
PGP is far from user friendly. I spent a lot of time on research to decide my best course of action. There are so many differing opinions on how best to generate keys and back them up. I spent some time generating bogus PGP keyrings to test different actions such as revoking and expiring subkeys.
After locating my flash drive named GPG, I backed up my existing public and secret key (the secret subkeys were stubs) via GPG keychain and then imported my master key and eventually discovered my password.
In that process, I locking up my Yubikey OpenPGP applet. I even ended up disabling the PUK key unfortunately (a pin that I do not have written down). The Yubikey app situation has since changed and the 8+ Yubikey GUI and CLI utilities have been replaced by "Yubikey Manager". I used
brew uninstall to remove all these apps and then
brew install --cask yubico-yubikey-manager . This includes the ykman CLI in the app bundle, but you need to set up an alias to use it as simply
ykman. I then used
ykman to reset all the Yubikey applets. Resetting PIV produced errors on macOS, likely because macOS was using it for account authentication. For this reason, I used a fresh ubuntu server with ykman to perform the reset. Also note from the smimesign README the OpenSC formula is missing an important component, therefore:
brew uninstall opensc brew install --cask opensc
On macOS, I wanted to start fresh.
brew uninstall gpg-suite rm -rf ~/.gnupg* brew install gpg-suite-no-mail # I don't use Apple Mail
I recommend the
gpg-suite-no-mail cask. It's bundled with pinentry and useful macOS service menus.
At this point I have disabled internet. This is just an abundance of caution while my master PGP key exists on the machine.
I just double clicked the secret gpg key, typed in the password and then edited the key to ultimately trust it. Finally I ejected the USB drive.
gpg --edit-key email@example.com # Now I want to ensure my master key only has Certify capability # gpg is weird. The command isn't listed in help or tab but: gpg> change-usage s q # I'm going to set my master key to expire. You can always extend it later. gpg> expire 2040-01-01 y
At this point I'm going to expire and later replace my subkeys. The reason I am doing this instead of revoking them is that git will complain regardless of the reasoning you give. I just set the expiration.
gpg> key 1 gpg> key 2 gpg> key 3 gpg> expire y # You can use ISO date format Key is valid for? (0) 20210330T070000 Is this correct? (y/N) y gpg> save
Now let's generate new subkeys where KEYID extracts your key fingerprint (also found via
KEYID=$(gpg -k firstname.lastname@example.org | sed -n '2 p' | xargs echo -n) gpg --quick-add-key $KEYID rsa2048 encr 2y gpg --quick-add-key $KEYID rsa2048 sign 2y
Now let's move the GPG key to an external drive. We will also remove the master key from the Mac. The master key is only used to certify (i.e. sign other keys, add notations, update expiration of subkeys, generate revokation certificates). To keep this free from unsecured backups and malware, we keep our master key on an offline device. In my case I am using an encrypted SD card.
cp -rp ~/.gnupg /Volumes/Key/gnupg-backup
Now we can remove the master key
gpg --with-keygrip --list-key $KEYID
The output will look something like this:
pub rsa4096 2017-12-06 [C] [expires: 2019-12-06] 111122223333444455556666AAAABBBBCCCCDDDD Keygrip = AAAA999988887777666655554444333322221111 uid [ultimate] Alice Engineer <email@example.com> uid [ultimate] Alice Engineer <firstname.lastname@example.org> sub rsa2048 2017-12-06 [E] Keygrip = BBBB999988887777666655554444333322221111 sub rsa2048 2017-12-06 [S] Keygrip = CCCC999988887777666655554444333322221111
The keygrip we want is the one below the pub line.
Now you can verify the master secret key has been removed:
If the output for the desired key has
sec# you are good to go. The
# indicates that the secret key is not available.
You will also want to ensure the revocation certificate only exists on the offline drive and not on the laptop. If it is compromised, your identity can be destroyed. We do want it to exist though as if we forget the master key password, we will be able to revoke the key by publishing the pre-generated revokation cert.
If that directory exists, then run
If it doesn't (as in my case, generate the revocation cert and place it on the offline drive)
gpg --homedir=/Volumes/Key/gnup-backup/ --gen-revoke email@example.com > /Volumes/Key/$KEYID.rev
From now on, when we need to work with the master secret key, we mount the drive and ensure commands have the
--homedir overridden as above.
I then uploaded the updated public key to the keyserver. I just used GPG Keychain. You can also follow this process You can then use Keyoxide to verify it exists.
After that, I added keyoxide notations.
After making changes to the master key, you can easily import the public key back into the main database:
gpg --homedir=/Volumes/Key/gnup-backup/ --export firstname.lastname@example.org | gpg --import
Finally, upload to the keyserver and update your published key on GitHub, GitLab, and Keybase.
You can update your Keybase public key with
keybase pgp update