We are introducing a method of verifying Haskell packages with OpenPGP signatures. The initial code to sign your packages has been included in Stack as an experimental feature for some time. We are going to be improving it and included verification options soon. However, we need signatures from package authors before verification is useful.
The first step in submitting package signatures is to create a secure OpenPGP key for yourself and publish it. This post will walk you through creating an offline master OpenPGP key-set with GnuPG.
If you’ve never used GPG before and need to generate a secure set of keys, then continue reading.
If you already have GPG keys that you are happy with, continue to ‘Signing Packages with Stack’ towards the bottom of this article.
There are 3 main flavors of GnuPG:
It is recommended by the GnuPG project version to use the Stable GPG-2 release (2.0). I’ve included instructions for using GPG 1.4 but I don’t recommend using such an old version unless you have no choice. I’ve also included instructions for GnuPG 2.1 because I use it personally and find the new features useful.
Ideally your offline keys will never be on a machine connected to the outside world. The offline box will be the place where you keep your master key, manage your keys & sign other keys. This assures that you can always revoke keys and recover if your online machine(s) are somehow compromised.
To have an offline box you’ll need two “machines”. These can just be the same machine if you utilize a “live” Linux USB/DVD boot drive and a USB stick for the “sneaker net”.
We’ll follow the same procedure to generate your keys, even if you don’t want to use an offline machine to generate them. It’s highly recommended using an offline master though and keeping secure backups. It’s the least hassle for everyone else in your “web of trust” in an event where keys are compromised.
With an offline master key, if your laptop is compromised, you can just revoke the compromised sub-keys, create new sub-keys and publish them. If you get into the situation where your master key is compromised, you will have to revoke all your keys, approach people about your new keys & going through the process of reestablishing trust with people in your web-of-trust.
Setup $GNUPGHOME directory
umask 077
export GNUPGHOME=$HOME/.gnupg ; # default location but env var rules
mkdir -p $GNUPGHOME
Install the secure key-server certificate (Ubuntu/Debian):
mkdir -p /usr/local/share/ca-certificates/
curl -s https://sks-keyservers.net/sks-keyservers.netCA.pem
| sudo tee /usr/local/share/ca-certificates/sks-keyservers.netCA.crt
sudo update-ca-certificates
Put some or all of the following in your $GNUPGHOME/gpg.conf file: (taken from the riseup.net best gpg practices page)
no-emit-version
no-comments
keyid-format 0xlong
with-fingerprint
list-options show-uid-validity
verify-options show-uid-validity
use-agent
keyserver hkps://hkps.pool.sks-keyservers.net
# or less secure: keyserver hkp://pool.sks-keyservers.net
keyserver-options no-honor-keyserver-url
keyserver-options include-revoked
personal-cipher-preferences AES256 AES192 AES CAST5
personal-digest-preferences SHA512 SHA384 SHA256 SHA224
cert-digest-algo SHA512
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
Public and Secret
Generate Key
GPG 1.4
gpg --expert --gen-key
GPG 2.0
gpg2 --expert --gen-key
GPG 2.1
gpg2 --expert --full-gen-key
Backup Your Keys
GPG 1.4
gpg --armor --export > $GNUPGHOME/public.asc
gpg --armor --export-secret-key > $GNUPGHOME/secret.asc
GPG 2.0 or 2.1
gpg2 --armor --export > $GNUPGHOME/public.asc
gpg2 --armor --export-secret-key > $GNUPGHOME/secret.asc
Try Recovering Your Keys
Delete your public and secret key and re-import them.
GPG 1.4
gpg --delete-secret-key <KEYID>
gpg --delete-key <KEYID>
gpg --import $GNUPGHOME/public.asc $GNUPGHOME/secret.asc
gpg --expert --edit-key <KEYID>
GPG 2.0 & 2.1
gpg2 --delete-secret-key <KEYID>
gpg2 --delete-key <KEYID>
gpg2 --import $GNUPGHOME/public.asc $GNUPGHOME/secret.asc
gpg2 --expert --edit-key <KEYID>
Generate Keys
Start by editing your key again.
GPG 1.4
gpg --expert --edit-key <KEYID>
GPG 2.0 or 2.1
gpg2 --expert --edit-key <KEYID>
Create a Signing Sub-key:
Create an Encryption Sub-key:
Create an Authentication Sub-key:
Now try encrypting and signing stuff with your keys.
GPG 1.4
echo 'hello' | gpg --armor --clearsign | gpg --verify
echo 'sekrats!' | gpg --armor --encrypt | gpg --decrypt
GPG 2.0 or 2.1
echo 'hello' | gpg2 --armor --clearsign | gpg2 --verify
echo 'sekrats!' | gpg2 --armor --encrypt | gpg2 --decrypt
Backup Keys
GPG 1.4
gpg --armor --export > $GNUPGHOME/public.asc
gpg --armor --export-secret-keys > $GNUPGHOME/secret.asc
gpg --armor --export-secret-subkeys > $GNUPGHOME/subkey.asc
GPG 2.0 or 2.1
gpg2 --armor --export > $GNUPGHOME/public.asc
gpg2 --armor --export-secret-keys > $GNUPGHOME/secret.asc
gpg2 --armor --export-secret-subkeys > $GNUPGHOME/subkey.asc
Try Recovery
Delete your public and secret key and re-import the offline keys (the full set).
GPG 1.4
gpg --delete-secret-key <KEYID>
gpg --delete-key <KEYID>
gpg --import $GNUPGHOME/public.asc $GNUPGHOME/secret.asc
gpg --expert --edit-key <KEYID>
GPG 2.0 or 2.1
gpg2 --delete-secret-key <KEYID>
gpg2 --delete-key <KEYID>
gpg2 --import $GNUPGHOME/public.asc $GNUPGHOME/secret.asc
gpg2 --expert --edit-key <KEYID>
Now try encrypting and signing stuff with your keys.
GPG 1.4
echo 'hello' | gpg --armor --clearsign | gpg --verify
echo 'sekrats!' | gpg --armor --encrypt | gpg --decrypt
GPG 2.0 or 2.1
echo 'hello' | gpg2 --armor --clearsign | gpg2 --verify
echo 'sekrats!' | gpg2 --armor --encrypt | gpg2 --decrypt
Delete your secret keys and re-import the offline secret keys (the full set).
GPG 1.4
gpg --delete-secret-key <KEYID>
gpg --import $GNUPGHOME/secret.asc
GPG 2.0 or 2.1
gpg2 --delete-secret-key <KEYID>
gpg2 --import $GNUPGHOME/secret.asc
With the full set in use (offline only of course!) you can import your buddy’s keys, sign them and trust them. Use gpg –import and gpg –export to move keys around to/from USB drives to/from your online machine.
If you list your secret keys you should see a plain “sec” next to your key. This indicates a full secret key is present. You may now manage keys and sign other keys.
GPG 1.4
gpg --list-secret-keys
GPG 2.0 or 2.1
gpg2 --list-secret-keys
Delete your secret keys and re-import the online secret keys (the subset).
GPG 1.4
gpg --delete-secret-key <KEYID>
gpg --import $GNUPGHOME/subkey.asc
GPG 2.0 or 2.1
gpg2 --delete-secret-key <KEYID>
gpg2 --import $GNUPGHOME/subkey.asc
You won’t be able to sign other people’s keys or create/revoke keys with this key-set. (This is a good thing in case your online machine and it’s subkeys are compromised later.)
If you list your secret keys you should see a “sec#” next to your key. The ‘#’ indicates that the secret is missing for your master key. If you are following good practices, from now on, it will only be available on your offline computer.
GPG 1.4
gpg --list-secret-keys
GPG 2.0 or 2.1
gpg2 --list-secret-keys
Now that we have our online secret keys ready, we can sign our Hackage-bound packages with Stack. There are currently two methods to sign keys in Stack. The first is to sign your package while uploading (the default). Just perform an upload as you might have in the past.
stack upload
This will upload your package to Hackage just like it did before. After that finishes it will GPG detach-sign the package and send that signature to sig-service. This service just collects signatures & periodically commits them to sig-archive. Later we will use these signatures and our web of trust to verify package contents as they download with stack.
If you don’t want to sign for some reason. You can just skip the signature by adding a flag.
stack upload --no-signature
Another way to sign is to create an sdist file. If you add a flag you can publish a signature at the same time.
stack sdist --sign
For those package authors out there with more than a few packages to sign, we have written a simple tool to bulk sign all your packages published to Hackage. It’s called sig-tool.
To bootstrap you need to run the following:
cd sig-tool
stack install
stack exec -- sig-tool setup <myHackageUser>
This will download all of your packages to the current directory & generate a manifest with SHA256 sums for all the releases. You should inspect the files to make sure they are correct at this stage. You can sign all of the releases or you can trim the manifest file if you like.
To begin signing run the following:
stack exec -- sig-tool sign
Subscribe to our blog via email
Email subscriptions come from our Atom feed and are handled by Blogtrottr. You will only receive notifications of blog posts, and can unsubscribe any time.