GPG, Yubikey, and Pass

2022-04-30

Recently I've wanted to move back to using pass as a password manager. In the past, I used pass with my GPG key on a yubikey and it worked out well. I do still have the key I used, but I can't remember how I set pass/GPG up to use the yubikey. So, I thought I'd do it from scratch, write about it, and maybe learn something.

Initial setup

The gpg and scdaemon packages need to be installed. Once the yubikey is inserted it should be used immediately by GPG. GPG runs a daemon called gpg-agent which talks through scdaemon to communicate with the yubikey.

Generate a key

To start, we need to generate some keys! Run gpg --expert --full-gen-key and choose 1, "RSA and RSA", as the key type. The other options should be self-explanatory.

❯ gpg --expert --full-gen-key
gpg (GnuPG) 2.2.20; Copyright (C) 2020 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (7) DSA (set your own capabilities)
   (8) RSA (set your own capabilities)
   (9) ECC and ECC
  (10) ECC (sign only)
  (11) ECC (set your own capabilities)
  (13) Existing key
  (14) Existing key from card
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want for the subkey? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 2y
Key expires at Mon 29 Apr 2024 17:17:40 IST
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: Stephen OBrien
Email address: wayofthepie@protonmail.com
Comment:
You selected this USER-ID:
    "Stephen OBrien <wayofthepie@protonmail.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key 01ED8E917ECF4036 marked as ultimately trusted
gpg: revocation certificate stored as '/home/chaospie/.gnupg/openpgp-revocs.d/985DB4AF50F89C5B9ED41803817494BAD88093CB.rev'
public and secret key created and signed.

pub   rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
      985DB4AF50F89C5B9ED41803817494BAD88093CB
uid                      Stephen OBrien <wayofthepie@protonmail.com>
sub   rsa4096 2022-04-30 [E] [expires: 2024-04-29]

You can run gpg --list-secret-keys to get a list of the secret keys generated:

❯ gpg --list-secret-keys
/home/chaospie/.gnupg/pubring.kbx
---------------------------------
sec   rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
      985DB4AF50F89C5B9ED41803817494BAD88093CB
uid           [ultimate] Stephen OBrien <wayofthepie@protonmail.com>
ssb   rsa4096 2022-04-30 [E] [expires: 2024-04-29]

To see the public keys:

❯ gpg --list-keys
/home/chaospie/.gnupg/pubring.kbx
---------------------------------
pub   rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
      985DB4AF50F89C5B9ED41803817494BAD88093CB
uid           [ultimate] Stephen OBrien <wayofthepie@protonmail.com>
sub   rsa4096 2022-04-30 [E] [expires: 2024-04-29]

GPG Terminology

Another thing I always forget when using GPG after some time is what all the jargon means!

sec   rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
      985DB4AF50F89C5B9ED41803817494BAD88093CB
uid           [ultimate] Stephen OBrien <wayofthepie@protonmail.com>
ssb   rsa4096 2022-04-30 [E] [expires: 2024-04-29]

Let's start from the first line sec rsa4096 2022-04-30 [SC] [expires: 2024-04-29].

  • sec means this is a secret key.
  • rsa4096 is the algorithm and key size.
  • [SC] is the capability of the key, in this case SC. S is Sign, C is Certify, A is Authenticate, and E is Encrypt. You can create subkeys (more on this later) that have specific capabilities.
  • [expires: 2024-04-29] this just says when the key expires

The second line, 985DB4AF50F89C5B9ED41803817494BAD88093CB, is just the key id. The last line, ssb rsa4096 2022-04-30 [E] [expires: 2024-04-29]:

  • ssb says this key is a subkey. The key denoted sec is the primary key, and all ssb keys are subkeys of this.
  • The rest is similar to the first line.

Move keys to the yubikey

Moving the keys to the yubikey is pretty simple. First, you should back up the secret key before you move it to the yubikey, you won't have access to this after moving. I have a second yubikey that I import all keys to, and I also move the secret key to a USB stick with a LUKS volume. Not the best practice security-wise, but it's a USB stick I never use. You can also print them so they never live anywhere except on paper. To export the secret key:

gpg --export-secret-key --armor $SECRET_KEY_ID > secret.key

secret.key will contain the key. Store this somewhere safe! Time to move the keys to the yubikey. Insert it, and run:

❯ gpg --card-status
Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240103040006120418440000
Application type .: OpenPGP
Version ..........: 3.4
Manufacturer .....: Yubico
...

If it displays the card info, GPG can see it. To import we need the id of the secret key, you can get this by running:

❯ gpg --list-secret-keys
/home/chaospie/.gnupg/pubring.kbx
---------------------------------
sec   rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
      985DB4AF50F89C5B9ED41803817494BAD88093CB
uid           [ultimate] Stephen OBrien <wayofthepie@protonmail.com>
ssb   rsa4096 2022-04-30 [E] [expires: 2024-04-29]

The id in this case is 985DB4AF50F89C5B9ED41803817494BAD88093CB. To move the keys to the yubikey run gpg --edit-key $SECRET_KEY_ID:

❯ gpg --edit-key 985DB4AF50F89C5B9ED41803817494BAD88093CB
gpg (GnuPG) 2.2.20; Copyright (C) 2020 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

sec  rsa4096/817494BAD88093CB
     created: 2022-04-30  expires: 2024-04-29  usage: SC
     trust: unknown       validity: unknown
ssb  rsa4096/5DCD3D0ABD15FA6F
     created: 2022-04-30  expires: 2024-04-29  usage: E
[ unknown] (1). Stephen OBrien <wayofthepie@protonmail.com>

gpg>

In the prompt type keytocard - remember, moving the key to the card means you can never read this key directly again, so make sure you back it up!

gpg> keytocard
Really move the primary key? (y/N) y
Please select where to store the key:
   (1) Signature key
   (3) Authentication key
Your selection? 1

sec  rsa4096/817494BAD88093CB
     created: 2022-04-30  expires: 2024-04-29  usage: SC
     trust: unknown       validity: unknown
ssb  rsa4096/5DCD3D0ABD15FA6F
     created: 2022-04-30  expires: 2024-04-29  usage: E
ssb  rsa4096/F35B9D17C107C8AC
     created: 2022-04-30  expires: 2024-04-29  usage: A
[ unknown] (1). Stephen OBrien <wayofthepie@protonmail.com>

We select the Signature Key slot in the yubikey as the place to store this primary key. It should prompt for the keys passphrase. Once done, select the subkey with key 1:

gpg> key 1

sec  rsa4096/817494BAD88093CB
     created: 2022-04-30  expires: 2024-04-29  usage: SC
     trust: unknown       validity: unknown
ssb* rsa4096/5DCD3D0ABD15FA6F
     created: 2022-04-30  expires: 2024-04-29  usage: E
[ unknown] (1). Stephen OBrien <wayofthepie@protonmail.com>

Note the asterisk (*) after ssb, this means this subkey is selected.

gpg> keytocard
Please select where to store the key:
   (2) Encryption key
Your selection? 2

sec  rsa4096/817494BAD88093CB
     created: 2022-04-30  expires: 2024-04-29  usage: SC
     trust: unknown       validity: unknown
ssb* rsa4096/5DCD3D0ABD15FA6F
     created: 2022-04-30  expires: 2024-04-29  usage: E
[ unknown] (1). Stephen OBrien <wayofthepie@protonmail.com>

This time we select the Encryption Key slot in the yubikey. Once complete, type quit and make sure to say y when prompted to save.

gpg> q
Save changes? (y/N) y

Now, when we list the secret keys, both the primary key and the subkey should be postfixed with a >, this denotes that GPG only has a reference to the keys, they are not stored directly in its keyring.

❯ gpg --list-secret-keys
/home/chaospie/.gnupg/pubring.kbx
---------------------------------
sec>  rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
      985DB4AF50F89C5B9ED41803817494BAD88093CB
      Card serial no. = 0006 12041844
uid           [ unknown] Stephen OBrien <wayofthepie@protonmail.com>
ssb>  rsa4096 2022-04-30 [E] [expires: 2024-04-29]

Export and store the public key

It's important to export and store the public key somewhere publicly accessible. To export:

gpg --output public.key --armor --export $SECRET_KEY_ID

public.key will contain the key. Once you've stored the public key somewhere, edit the card again with gpg --edit-card. This time enable admin commands with the admin command, and then enter the URL to access the public key with url.

❯ gpg --edit-card

Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240103040006120418440000
Application type .: OpenPGP
...

gpg/card> admin
Admin commands are allowed

gpg/card> url
URL to retrieve public key: https://raw.githubusercontent.com/wayofthepie/public-keys/main/proton.key

I stored mine in a github repo, you can also upload it to a keyserver and reference from there.

Set up pass

Install pass, with sudo apt install pass or however else you want to install it. Initialize the password store with the email you used to set up your key (you can also use the full key id instead of the email):

❯ pass init wayofthepie@protonmail.com
mkdir: created directory '/home/chaospie/.password-store/'
Password store initialized for wayofthepie@protonmail.com

Enable git support:

❯ pass git init
Initialized empty Git repository in /home/chaospie/.password-store/.git/
[main (root-commit) ac03bd2] Add current contents of password store.
 1 file changed, 1 insertion(+)
 create mode 100644 .gpg-id
[main 894baaa] Configure git repository for gpg file diff.
 1 file changed, 1 insertion(+)
 create mode 100644 .gitattributes

Configure the gpg-agent to open any prompts on the terminal by editing/creating ~/.gnupg/gpg-agent.conf and adding:

pinentry-program /usr/bin/pinentry-curses

Either pinentry-curses or pinentry-tty can be used here, if neither is installed you should be able to install it directly through the package manager. Kill the agent to force it to reload its config:

❯ gpgconf --kill gpg-agent

It will restart automatically. Let's do a quick test, insert a key test with the value test:

❯ pass insert test -e
Enter password for test: test

The -e flag just means echo back the value I type, without it the value is hidden. Now, test retrieval, run pass test and it should prompt you for your PIN:

┌──────────────────────────────────────────────┐
│ Please unlock the card                       │
│                                              │
│ Number: 0006 33333333                        │
│ Holder:                                      │
│                                              │
│ PIN ________________________________________ │
│                                              │
│      <OK>                        <Cancel>    │
└──────────────────────────────────────────────┘

The default PIN is 123456, you should change that if it's still that value. Once entered, pass should print the value of test. This means it's working!

We need to test that the key is only accessible on the yubikey also. Pull out the yubikey and try to retrieve the value for test again, it should show a prompt like the following:

┌────────────────────────────────────────────┐
│ Please insert the card with serial number: │
│                                            │
│ 0006 33333333                              │
│                                            │
│      <OK>                      <Cancel>    │
└────────────────────────────────────────────┘

Now pop it back in and run pass test, it should ask for the PIN again.

Set up on another computer

This is the bit that always drives me mad when I haven't used GPG for a while. Initially, on a new computer, you'll have no public or secret keys:

❯ gpg --list-keys
gpg: checking the trustdb
gpg: no ultimately trusted keys found

❯ gpg --list-secret-keys

If you try to use pass to get a secret in the store, you will get the following error:

❯ pass test
gpg: decryption failed: No secret key

Let's fix it!

Fetch the public key

To get a reference to the keys on the yubikey, plug it in and run:

❯ gpg --card-edit

Reader ...........: 1050:0407:X:0
Application ID ...: D2760001240103040006120418440000
Application type .: OpenPGP
...

gpg/card>

In the prompt type fetch:

gpg/card> fetch
gpg: requesting key from 'https://raw.githubusercontent.com/wayofthepie/public-keys/main/proton.key'
gpg: key 01ED8E917ECF4036: public key "Stephen OBrien <wayofthepie@protonmail.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1

That's it! Now, GPG should have a reference to the keys, and pass test should prompt you for your pin and read the value of our test secret test correctly.

❯ gpg --list-keys
/home/chaospie/.gnupg/pubring.kbx
---------------------------------
pub   rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
      985DB4AF50F89C5B9ED41803817494BAD88093CB
uid           [ unknown] Stephen OBrien <wayofthepie@protonmail.com>
sub   rsa4096 2022-04-30 [E] [expires: 2024-04-29]

❯ gpg --list-secret-keys
/home/chaospie/.gnupg/pubring.kbx
---------------------------------
sec>  rsa4096 2022-04-30 [SC] [expires: 2024-04-29]
      985DB4AF50F89C5B9ED41803817494BAD88093CB
      Card serial no. = 0006 12041844
uid           [ unknown] Stephen OBrien <wayofthepie@protonmail.com>
ssb>  rsa4096 2022-04-30 [E] [expires: 2024-04-29]

❯ pass test
test

Phew...

This is the third time I have had to go re-learn how GPG works. It's a bit of a nightmare usability-wise. Hopefully, this serves as a good reference for future me at the very least!