Skip to content

Instantly share code, notes, and snippets.

@cyphar
Last active September 2, 2023 02:44
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save cyphar/21a1b213901a0a0ec19764ac03646ebe to your computer and use it in GitHub Desktop.
Save cyphar/21a1b213901a0a0ec19764ac03646ebe to your computer and use it in GitHub Desktop.
Document describing how to create PGP-signed releases of projects.

Creating Releases with PGP Signatures

Aleksa Sarai

Creating a release of a free software project with PGP signatures is quite simple, especially if you have everything set up already. This guide uses GnuPG, but it should be roughly applicable to OpenPGP or other implementations. For completeness, I've included a (very) short introduction to how to create a PGP key and how PGP works.

Introduction

The entire reason signatures are useful is because of public-key cryptography. Effectively, it allows for someone to attest that a particular binary is authoritative (it comes from them) and allows anyone to verify that attestation without allowing such individuals to create their own attestations that a different binary comes from the same person. Symmetric cryptographic signatures do not share the property (which is why HMAC doesn't provide non-repudation).

To create a PGP keypair, you can use the following. Note that finding large primes requires plenty of entropy, so it might take a while to generate large keys. At the time of writing, prime-based keys shorter than 2048 bits are not a good idea (with 4096 being a sane future-proof default). Note that while it is possible to create new subkeys with PGP that have different strengths (allowing you to "upgrade"), the master PGP key cannot be changed without changing the keyid -- so make it as secure as possible.

% gpg --full-generate-key
gpg (GnuPG) 2.1.19; Copyright (C) 2017 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)
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 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) 5y
Key expires at Tue 15 Mar 2022 10:37:42 AM AEDT
Is this correct? (y/N) y

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

Real name: Aleksa Sarai
Email address: asarai@suse.de
Comment:
You selected this USER-ID:
    "Aleksa Sarai <asarai@suse.de>"

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 A5CC1D56568C9FFC marked as ultimately trusted
gpg: revocation certificate stored as '/home/cyphar/.gnupg/openpgp-revocs.d/1D88E4232D3A284AA3D1DBD7A5CC1D56568C9FFC.rev'
public and secret key created and signed.

pub   rsa4096 2017-03-15 [SC] [expires: 2022-03-14]
      1D88E4232D3A284AA3D1DBD7A5CC1D56568C9FFC
      1D88E4232D3A284AA3D1DBD7A5CC1D56568C9FFC
uid                      Aleksa Sarai <asarai@suse.de>
sub   rsa4096 2017-03-15 [E] [expires: 2022-03-14]

Note the following things:

  1. Do not fill the Comment: field in your PGP key. It is generally agreed that the comments in PGP userids are evil, because someone signing your key cannot make a claim about whether the comment description is valid. Also they break PGP-signed git commits because git commit -S by default will sign with the committer identity.

  2. Always set an expiry for your PGP keys. Don't worry, you can edit your key and change it in the future, but if you make your key last forever it won't have a built-in failsafe (similar to why we don't allow SSL keys to be valid forever).

  3. While the prompt isn't shown above, you should always set a passphrase for your PGP key. You must always consider your PGP key as a form of identification and attestation -- if it is compromised then you're in big trouble.

  4. In newer version of GPG, a revocation certificate is generated automatically for you. However, you should make sure that you store this revocation certificate somewhere separately from your PGP key, so if you lose access to your PGP key you can revoke it. You can generate one explicitly with gpg --gen-revoke.

Now, a PGP key is sort of useless by itself -- how on earth is anyone meant to trust it? An untrusted PGP key is only slightly better than no PGP key at all. There are three main options for how to do this:

  1. Publish your key on a keyserver with gpg --send-keys (though you should do this anyway), and then go to a local keysigning party to become part of the Web of Trust. While it would be great if this model was applicable to everyone, it has a huge barrier of entry and it's not very clear that the Web of Trust has been as successful as it should've been.

  2. Just publish your public keys (you can use gpg --armor --export to export your public key) on your website and link people to it. This is probably the simplest way of doing things (assuming you have a website).

  3. Use a service like keybase which allows you to use your already-established social media handles to validate that your public key is associated with those social media handles.

Personally I do all three of the above, but any single one is sufficient.

Commit Signing

While this is not necessary, I also recommend that maintainers of projects sign all of their commits (especially merge commits) so that anyone can verify that a particular commit was actually created by the maintainer (and that changes were actually merged by the maintainer).

git makes this quite easy, though I'm sure that hg has some similar flags.

% git config --global commit.gpgsign true

Note that you should make sure that your user.name and user.email match the Full Name and Email fields you've set in your PGP key. Otherwise, git will not be able to automatically decide which key to use.

Signing Tags

Unlike with git commit if you want to sign a tag you have to explicitly sign it, and signing tags really is a no-brainer. I would also recommend that maintainers include some non-trivial text in the annotation mentioning who LGTM'd the release and what is in the release.

With git you can create a signed tag like so (I would always recommend using the full commit-id to ensure you don't accidentally tag something you didn't mean to):

% git tag -as tag-name commit-id

Signing Binaries

Once you have a signed tag, checkout that tag and build the binaries (as you normally would). Once you have the binaries you wish to sign, you can create detached signatures like this with GPG:

% gpg --detach-sign --armor binary-name
% # Detached signature is in binary-name.asc

You can then upload the binary and the detached signature to wherever you're hosting your release binaries. If you want to verify that a detached signature is valid for a particular binary, you can do the following:

% gpg --verify binary-name.asc
gpg: assuming signed data in 'binary-name'
gpg: Signature made Thu 16 Mar 2017 11:02:20 AM AEDT
gpg:                using RSA key 5F36C6C61B5460124A75F5A69E18AA267DDB8DB4
gpg:                issuer "asarai@suse.de"
gpg: Good signature from "Aleksa Sarai <asarai@suse.com>" [ultimate]
gpg:                 aka "Aleksa Sarai <asarai@suse.de>" [ultimate]

If the bottom line about Good signature is not there, that's because the key the binary was signed with was not trusted. The notes in the introduction about how to make sure that the key can be verified by a user explain how you could get the user to trust your key.

Happy signing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment