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.
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: [email protected]
Comment:
You selected this USER-ID:
"Aleksa Sarai <[email protected]>"
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 <[email protected]>
sub rsa4096 2017-03-15 [E] [expires: 2022-03-14]
Note the following things:
-
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-signedgit
commits becausegit commit -S
by default will sign with the committer identity. -
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).
-
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.
-
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:
-
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. -
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). -
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.
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.
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
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 "[email protected]"
gpg: Good signature from "Aleksa Sarai <[email protected]>" [ultimate]
gpg: aka "Aleksa Sarai <[email protected]>" [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!