veil

Sign your releases with a key on your card

A signature on a release only helps if users know whose key made it. When the key is on your Veil card, anyone can see that it belongs to the person behind your GitHub profile, your domain and your other accounts, and check that in their browser.

Written on

Why signatures often go unchecked#

Most projects publish a SHA256SUMS file next to their downloads, and many sign it. Few users check, because the signing key is a fingerprint in a README, served from the same place as the files. If an attacker can replace the download, they can usually replace the fingerprint as well.

Veil moves the question somewhere else. The key that signs is on your card, the card is tied to the accounts people already know you by, and every change to it is in a public log that independent witnesses cosign. Swapping the files on your server doesn’t change your card.

Sign a release#

Install the veil command line and pair the machine with your card once, with veil pair. The machine makes its own device key, which never leaves it. Then point it at the folder that holds the release:

veil sign --release dist/

It signs the folder’s SHA256SUMS, making the file from the folder’s contents when there is none, and writes SHA256SUMS.veil beside it. Publish both with the release. To sign a single file, veil sign --file app.tar.gz writes app.tar.gz.veil. The card page has the same thing under Sign a release, if you would rather not use a terminal.

How users check it#

In a browser, they open veil.pub/verify, choose File or release, and drop the files with SHA256SUMS and SHA256SUMS.veil. The browser hashes the files itself, in pieces, and never uploads them. The page says which card signed the list and checks the card against the log.

In a terminal or a CI job, two commands do it:

sha256sum --check --ignore-missing SHA256SUMS
veil verify --release SHA256SUMS --sig SHA256SUMS.veil --card <your card>

veil verify exits with 0 when the signature is valid and the card is in good standing, 1 when it isn’t, and 2 when it couldn’t read the log. A build step can stop on anything but 0. With --card, the check also fails when someone else’s card signed the list.

An example with apt#

A Debian repository signs its indexes with an OpenPGP key, and users install that key once before their first apt update. That first step is where they have to trust a file downloaded from your server. Put the repository’s public key on your card as a public key from elsewhere, with veil external add --pgp <key id>. The command makes a challenge and has GnuPG sign it, so the private key never leaves your machine.

Your install guide can then tell users to print the fingerprint of the key they downloaded and compare it with your card:

gpg --show-keys /usr/share/keyrings/example.gpg

They can also paste the 40 hex digits into the search on veil.pub and land on the card that holds the key, with the accounts that confirm it. After that, apt checks every update against the key by itself. Veil’s own command line ships through apt.veil.pub this way, and its release script refuses to publish until veil verify --release passes.

Questions#

Do I have to stop using GPG or minisign?

No. The verify page reads detached and clear-signed GPG signatures, ssh-keygen -Y sign signatures and minisign signatures. Put the public key on your card and those signatures are tied to your card too.

Can an organisation sign a release?

An organisation card has no keys of its own. A member signs with their own card, and the organisation’s card shows who its members are, each membership signed by both sides.

What if the key that signed a release is later revoked?

What the key signed before the revocation stays valid. From the revocation on, it no longer signs for your card, and the log keeps both versions of the card for anyone who wants to see when it changed.

Does the verify page send my files anywhere?

No. The browser reads the files and computes their hashes on your computer, and the files never leave it.