OpenPGP is an encryption and authentication system which is extensively used at Tor.
- Tutorial
- How-to
- Reference
- Discussion
Tutorial
This documentation assumes minimal technical knowledge, but it should be noted that OpenPGP is notoriously hard to implement correctly, and that user interfaces have been known to be user-hostile in the past. This documentation tries to alleviate those flaws, but users should be aware that there are challenges in using OpenPGP safely.
If you're looking for documentation on how to use OpenPGP with a YubiKey, that lives in the YubiKey documentation.
OpenPGP with Thunderbird training
Rough notes for the OpenPGP training to be given at the 2023 Tor meeting in Costa Rica.
- Upgrade Thunderbird to version 78.2.1 or later at https://www.thunderbird.net/ (Mac, Windows, Linux) or through you local package manager (Linux), if you do not have Thunderbird installed, you will need to install it and follow the email setup instructions to setup the Tor mail server
- Set a
Primary PasswordinEdit->Settings->Privacy & Security- Check
Use a primary password - Enter the password and click OK
- Check
- Select the
@torproject.orguser identity asDefaultinEdit->Account Settings->Manage Identities - Generate key with expiration date in
Tools->OpenPGP Key Manager->Generate->New Key Pair- Make sure you select an expiration date, can be somewhere between one to 3 years, preferably one year
- Optionally, select
ECC (Elliptic Curve)as aKey typeinAdvanced Settings - Click
Generate Keyand confirm - Make a backup:
File->Backup secret key(s) to File
- Send a signed email to another user, have another user send you such an email as well
- Send an encrypted mail to a new recipient:
- click
Encrypt - big yellow warning, click
Resolve... Discover public keys online...A key is available, but hasn't been accepted yet, clickResolve...- Select the first key
- click
- Setting up a submission server account, see the email
tutorial which involves a LDAP password reset (assuming
you already have an LDAP account, otherwise getting TPA to make
you one) and sending a signed OpenPGP mail to
chpasswd@db.torproject.orgwith the contentPlease change my Tor password - send your key to TPA:
Tools->OpenPGP Key Manager- select the key
File->Export public key(s) to File- in a new ticket, attach the file
- Verifying incoming mail:
- OpenPGP menu:
This message claims to contain the sender's OpenPGP public key, clickImport... - Click
accepted (unverified) - You should now see a little "seal" logo with a triangle
"warning sign", click on it and then
View signer's key - There you can verify the key
- OpenPGP menu:
- Renewing your OpenPGP key:
Edit->Account Settings->End-to-End encryption- on the affected key, click
Change Expiration Date - send your key to TPA, as detailed above
- Verifying and trusting keys, a short discussion on "TOFU" and the web of trust, WKD and autocrypt
Notes:
- we do not use key servers and instead rely on WKD and Autocrypt for key discovery
- it seems like Thunderbird and RNP do not support generating revocation certificates, only revoking the key directly
- sequoia-octopus-librnp can provide a drop-in replacement for Thunderbird's RNP library and give access to a normal keyring, but is more for advanced users and not covered here
- openpgp.org is a good entry point, good list of software for example, website source on GitHub
- we must set a master password in thunderbird, it's the password that protects the keyring (to be verified)
Other tutorials:
- How-to geek has a good reference which could be used as a basis, but incorrectly suggests to not have an expiry date, and does not suggest doing a backup
- Tails: uses Kleopatra and Thunderbird, but with the Enigmail stuff, outdated, Linux-specific
- boum's guide: french, but otherwise good reference
- Thunderbird's documentation is a catastrophe. basic, cryptic wiki page that points to a howto and FAQ that is just a pile of questions, utterly useless, other than as a FAQ, their normal guide is still outdated and refers to Enigmail
- the EFF Surveillance Self-Defense guide is also outdated, their Linux, Windows and Mac are marked as "retired"
How-to
Diffing OpenPGP keys, signatures and encrypted files from Git
Say you store OpenPGP keyrings in git. For example, you track package repositories public signing keys or you have a directory of user keys. You need to update those keys but want to make sure the update doesn't add untrusted key material.
This guide will setup your git commands to show a meaningful diff of binary or ascii-armored keyrings.
-
add this to your
~/.gitconfig(or, if you want to restrict it to a single repository, in.git/config:# handler to parse keyrings [diff "key"] textconv = gpg --batch --no-tty --with-sig-list --show-keys < # handler to verify signatures [diff "sig"] textconv = gpg --batch --no-tty --verify < # handler to decrypt files [diff "pgp"] textconv = gpg --batch --no-tty --decrypt < -
add this to your
~/.config/git/attributes(or, the per repository.gitattributesfile), so that those handlers are mapped to file extensions:*.key diff=key *.sig diff=sig *.pgp diff=pgp.key,.sig, and.pgpare "standard" extensions (as per/etc/mime.types), but people frequently use other extensions, so you might want to have this too:*.gpg diff=key *.asc diff=key
Then, when you change a key, git diff will show you something like
this, which is when the GitLab package signing key was renewed:
commit c29047357669cb86cf759ecb8a44e14ca6d5c130
Author: Antoine Beaupré <anarcat@debian.org>
Date: Wed Mar 2 15:31:36 2022 -0500
renew gitlab's key which expired yesterday
diff --git a/modules/profile/files/gitlab/gitlab-archive-keyring.gpg b/modules/profile/files/gitlab/gitlab-archive-keyring.gpg
index e38045da..3e57c8e0 100644
--- a/modules/profile/files/gitlab/gitlab-archive-keyring.gpg
+++ b/modules/profile/files/gitlab/gitlab-archive-keyring.gpg
@@ -1,7 +1,7 @@
-pub rsa4096/3F01618A51312F3F 2020-03-02 [SC] [expired: 2022-03-02]
+pub rsa4096/3F01618A51312F3F 2020-03-02 [SC] [expires: 2024-03-01]
F6403F6544A38863DAA0B6E03F01618A51312F3F
uid GitLab B.V. (package repository signing key) <packages@gitlab.com>
-sig 3 3F01618A51312F3F 2020-03-02 GitLab B.V. (package repository signing key) <packages@gitlab.com>
-sub rsa4096/1193DC8C5FFF7061 2020-03-02 [E] [expired: 2022-03-02]
-sig 3F01618A51312F3F 2020-03-02 GitLab B.V. (package repository signing key) <packages@gitlab.com>
+sig 3 3F01618A51312F3F 2022-03-02 GitLab B.V. (package repository signing key) <packages@gitlab.com>
+sub rsa4096/1193DC8C5FFF7061 2020-03-02 [E] [expires: 2024-03-01]
+sig 3F01618A51312F3F 2022-03-02 GitLab B.V. (package repository signing key) <packages@gitlab.com>
[...]
The reasoning behind each file extension goes as follows:
.key- OpenPGP key material. process it with --show-keys < file.sig- OpenPGP signature. process it with --verify < file.pgp- OpenPGP encrypted material. process it with --decrypt < file.gpg- informal. can be anything, but generally assumed to be binary. we treat those as OpenPGP keys, because that's the safest thing to do.asc- informal. can be anything, but generally assumed to be ASCII-armored, assumed to be the same as.gpgotherwise.
We also use those options:
--batchis, well, never sure what--batchis for, but seems reasonable?--no-ttyis to force GnuPG to not assume a terminal which may make it prompt the user for things, which could break the pager
Note that, you might see the advice to run gpg < file (without any
arguments) elsewhere, but we advise against it. In theory, gpg < file can do anything, but it will typically:
- decrypt encrypted material, or;
- verify signed material, or;
- show public key material
From what I can tell in the source code, it will also process private
key material and other nasty stuff, so it's unclear if it's actually
safe to run at all. See do_proc_packets() that is called with
opt.list_packets == 0 in the GnuPG source code.
Also note that, without <, git passes a the payload to gpg through
a binary file, and GnuPG then happily decrypts it and puts in publicly
readable in /tmp. boom. This behavior was filed in 2017 as a bug
upstream (T2945) but was downgraded to a "feature request" by the
GnuPG maintainer a few weeks later. No new activity at the time of
writing (2022, five years later).
All of this is somewhat brittle: gpg < foo is not supposed to work
and may kill your cat. Bugs should be filed to have something that
does the right thing, or at least not kill defenseless animals.
Generate a Curve25519 key
Here we're generating a new OpenPGP key as we're transitioning from an old RSA4096 key. DO NOT follow those steps if you wish to keep your old key, of course.
Note that the procedure below generates the key in a temporary,
memory-backed, filesystem (/run is assumed to be a tmpfs). The key
will be completely lost on next reboot unless it's moved to a security
key or to an actual home. See the YubiKey documentation for how
to move it to a YubiKey, for example, and see the Airgapped
systems for a discussion on that approach.
GnuPG (still) requires --expert mode to generate Curve25519 keys,
unfortunately. Note that you could also accomplish this by sending a
"batch" file, for example drduh has this example for ed25519
keys, see also GnuPG's guide.
Here's the transcript of a Curve25519 key generation with an encryption and authentication subkey:
export GNUPGHOME=${XDG_RUNTIME_DIR:-/nonexistent}/.gnupg/
anarcat@angela:~[SIGINT]$ gpg --full-gen-key --expert
gpg (GnuPG) 2.2.40; Copyright (C) 2022 g10 Code GmbH
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? 11
Possible actions for a ECDSA/EdDSA key: Sign Certify Authenticate
Current allowed actions: Sign Certify
(S) Toggle the sign capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? q
Please select which elliptic curve you want:
(1) Curve 25519
(3) NIST P-256
(4) NIST P-384
(5) NIST P-521
(6) Brainpool P-256
(7) Brainpool P-384
(8) Brainpool P-512
(9) secp256k1
Your selection? 1
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) 1y
Key expires at mer 29 mai 2024 15:27:14 EDT
Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: Antoine Beaupré
Email address: anarcat@anarc.at
Comment:
You are using the 'utf-8' character set.
You selected this USER-ID:
"Antoine Beaupré <anarcat@anarc.at>"
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.
gpg: directory '/home/anarcat/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/home/anarcat/.gnupg/openpgp-revocs.d/D0D396D08E761095E2910413DDE8A0D1D4CFEE10.rev'
public and secret key created and signed.
pub ed25519/DDE8A0D1D4CFEE10 2023-05-30 [SC] [expires: 2024-05-29]
D0D396D08E761095E2910413DDE8A0D1D4CFEE10
uid Antoine Beaupré <anarcat@anarc.at>
anarcat@angela:~$
Let's put this fingerprint aside, as we'll be using it over and over again:
FINGERPRINT=D0D396D08E761095E2910413DDE8A0D1D4CFEE10
Let's look at this key:
anarcat@angela:~$ gpg --edit-key $FINGERPRINT
gpg (GnuPG) 2.2.40; Copyright (C) 2022 g10 Code GmbH
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.
gpg: checking the trustdb
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2024-05-29
sec ed25519/02293A6FA4E53473
created: 2023-05-30 expires: 2024-05-29 usage: SC
trust: ultimate validity: ultimate
ssb cv25519/0E1C0B264FC7ADEA
created: 2023-05-30 expires: 2024-05-29 usage: E
[ultimate] (1). Antoine Beaupré <anarcat@anarc.at>
gpg>
As we can see, this created two key pairs:
-
"primary key" which is a public/private key with the
S(Signing) andC(Certification) purposes. that key can be used to sign messages, certify other keys, new identities, and subkeys (see why we use both in Separate certification key) -
an
E(encryption) "sub-key" pair which is used to encrypt and decrypt messages
Note that the encryption key expires here, which can be annoying. You can delete the key and recreate it this way:
anarcat@angela:~[SIGINT]$ gpg --expert --edit-key $FINGERPRINT
gpg (GnuPG) 2.2.40; Copyright (C) 2022 g10 Code GmbH
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 ed25519/02293A6FA4E53473
created: 2023-05-30 expires: 2024-05-29 usage: SC
trust: ultimate validity: ultimate
ssb cv25519/0E1C0B264FC7ADEA
created: 2023-05-30 expires: 2024-05-29 usage: E
[ultimate] (1). Antoine Beaupré <anarcat@anarc.at>
gpg> addkey
Please select what kind of key you want:
(3) DSA (sign only)
(4) RSA (sign only)
(5) Elgamal (encrypt only)
(6) RSA (encrypt only)
(7) DSA (set your own capabilities)
(8) RSA (set your own capabilities)
(10) ECC (sign only)
(11) ECC (set your own capabilities)
(12) ECC (encrypt only)
(13) Existing key
(14) Existing key from card
Your selection? 12
Please select which elliptic curve you want:
(1) Curve 25519
(3) NIST P-256
(4) NIST P-384
(5) NIST P-521
(6) Brainpool P-256
(7) Brainpool P-384
(8) Brainpool P-512
(9) secp256k1
Your selection? 1
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)
Key does not expire at all
Is this correct? (y/N) y
Really create? (y/N) y
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.
sec ed25519/02293A6FA4E53473
created: 2023-05-30 expires: 2024-05-29 usage: SC
trust: ultimate validity: ultimate
ssb cv25519/0E1C0B264FC7ADEA
created: 2023-05-30 expires: 2024-05-29 usage: E
ssb cv25519/9456BA69685EAFFB
created: 2023-05-30 expires: never usage: E
[ultimate] (1). Antoine Beaupré <anarcat@anarc.at>
gpg> key 1
sec ed25519/02293A6FA4E53473
created: 2023-05-30 expires: 2024-05-29 usage: SC
trust: ultimate validity: ultimate
ssb* cv25519/0E1C0B264FC7ADEA
created: 2023-05-30 expires: 2024-05-29 usage: E
ssb cv25519/9456BA69685EAFFB
created: 2023-05-30 expires: never usage: E
[ultimate] (1). Antoine Beaupré <anarcat@anarc.at>
gpg> delkey
Do you really want to delete this key? (y/N) y
sec ed25519/02293A6FA4E53473
created: 2023-05-30 expires: 2024-05-29 usage: SC
trust: ultimate validity: ultimate
ssb cv25519/9456BA69685EAFFB
created: 2023-05-30 expires: never usage: E
[ultimate] (1). Antoine Beaupré <anarcat@anarc.at>
See also the Expiration dates discussion.
We'll also add a third key here, which is an A (Authentication) key,
which will be used for SSH authentication:
gpg> addkey
Please select what kind of key you want:
(3) DSA (sign only)
(4) RSA (sign only)
(5) Elgamal (encrypt only)
(6) RSA (encrypt only)
(7) DSA (set your own capabilities)
(8) RSA (set your own capabilities)
(10) ECC (sign only)
(11) ECC (set your own capabilities)
(12) ECC (encrypt only)
(13) Existing key
(14) Existing key from card
Your selection? 11
Possible actions for a ECDSA/EdDSA key: Sign Authenticate
Current allowed actions: Sign
(S) Toggle the sign capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? a
Possible actions for a ECDSA/EdDSA key: Sign Authenticate
Current allowed actions: Sign Authenticate
(S) Toggle the sign capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? s
Possible actions for a ECDSA/EdDSA key: Sign Authenticate
Current allowed actions: Authenticate
(S) Toggle the sign capability
(A) Toggle the authenticate capability
(Q) Finished
Your selection? q
Please select which elliptic curve you want:
(1) Curve 25519
(3) NIST P-256
(4) NIST P-384
(5) NIST P-521
(6) Brainpool P-256
(7) Brainpool P-384
(8) Brainpool P-512
(9) secp256k1
Your selection? 1
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)
Key does not expire at all
Is this correct? (y/N) y
Really create? (y/N) y
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.
sec ed25519/02293A6FA4E53473
created: 2023-05-30 expires: 2024-05-29 usage: SC
trust: ultimate validity: ultimate
ssb cv25519/9456BA69685EAFFB
created: 2023-05-30 expires: never usage: E
ssb ed25519/9FF21704D101630D
created: 2023-05-30 expires: never usage: A
[ultimate] (1)* Antoine Beaupré <anarcat@anarc.at>
At this point, you should have a functional and valid set of OpenPGP
certificates! It's a good idea to check the key with with hokey lint, from hopenpgp-tools:
gpg --export $FINGERPRINT | hokey lint
Following the above guide, I ended up with a key that is all green
except for the authentication key having False in embedded cross-cert. According to drduh's guide, that doesn't matter:
hokey may warn (orange text) about cross certification for the authentication key. GPG's Signing Subkey Cross-Certification documentation has more detail on cross certification, and gpg v2.2.1 notes "subkey does not sign and so does not need to be cross-certified".
Also make sure you generate a revocation certificate, see below.
Generating a revocation certificate
If you do not have one already, you should generate a revocation certificate with:
gpg --generate-revocation $FINGERPRINT
This should be stored in a safe place.
The point of a revocation certificate is to provide a last safety measure if you lose control of your key. It allows you to mark your key as unusable to the outside world, which will make it impossible for a compromised key to be used to impersonate you, provided the certificate is distributed properly, of course.
It will not keep an attacker from reading your encrypted material, nor will it allow you to read encrypted material for a key you have lost, however. It will keep people from encrypting new material to you, however.
A good practice is to print this on paper (yes, that old thing) and store it among your other precious papers. The risk to that document is that someone could invalidate your key if they lay their hands on it. But the reverse is that losing it might make you unable to decrypt some messages sent to you if you lost your original key material.
When printing the key, you can optionally add a more "scannable" version by embedding a QR code in the document. One of those tools might be able to help:
Make sure you can recover from the QR codes before filing them away. Also make sure the printer is plugged in, has toner or ink, no paper jam, and use a fresh ream of paper as used paper tends to jam more. Also send a donation to your local anarchist bookstore, pet your cat, or steal a book to please the printer gods.
Revoking a key
Note: this assumes you generated a revocation certificate when you created the key. If you still have access to the private key material and have not generated a revocation certificate, go ahead and do that right now, see above.
To revoke an OpenPGP key, you first need to find the revocation certificate and, if on paper, digitize it in a text file. Then import the document:
gpg --import < revocation.key
The key can then be published as normal, say:
gpg --send-keys $FINGERPRINT
Rotating keys
First, generate a key as detailed above.
When you are confident the new key can be put in use, sign the the new key with old key:
gpg --default-key $OLDKEY --sign-key $FINGERPRINT
And revoke the old key:
gpg --generate-revocation $OLDKEY
Then you need to publish the new key and retire the old one everywhere. This will vary wildly according to how you have used the old key and intend to use the new one.
In my case, this implied:
-
change the default key in GnuPG:
sed -i "s/default-key.*/default-key $FINGERPRINT/" ~/.gnupg/gpg.conf -
changing the
PASSWORD_STORE_SIGNING_KEYenvironment:export PASSWORD_STORE_SIGNING_KEY=$FINGERPRINT echo PASSWORD_STORE_SIGNING_KEY=$FINGERPRINT >> ~/.config/environment.d/shenv.conf -
re-encrypt the whole password manager:
pass init $FINGERPRINT -
change the fingerprint in my WKD setup, which means changing the
FINGERPRINTin this Makefile and calling:make -C ~/wikis/anarc.at/.well-known/openpgpkey/ hu -
upload the new key everywhere which, in my case, means:
gpg --keyserver keyring.debian.org --send-keys $FINGERPRINT gpg --keyserver keys.openpgp.org --send-keys $FINGERPRINT gpg --keyserver pool.sks-keyservers.net --send-keys $FINGERPRINT... and those sites:
* <https://gitlab.torproject.org/-/profile/gpg_keys> * <https://gitlab.com/-/profile/gpg_keys> * <https://github.com/settings/keys> -
change my OpenPGP SSH key in a lot of
authorized_keysfiles, namely:- home network (Puppet)
- work (Puppet)
- https://gitlab.torproject.org/-/profile/keys
- https://gitlab.com/-/profile/keys
- https://github.com/settings/keys
-
change your Git signing key:
git config --global user.signingkey $FINGERPRINT -
follow the Debian.org key replacement procedure
-
consider publishing a full "key transition statement" (example), signed with both keys:
gpg --local-user $FINGERPRINT --local-user $OLD_FINGERPRINT --clearsign openpgp-transition-2023.txt
You may also want to backup your old encryption key, also removing
the password! Otherwise you will likely not remember the password. To
do this, first enter the --edit-key mode:
gpg --edit-key $OLD_FINGERPRINT
Then remove the password on the old keyring:
toggle
passwd
Then export the private keys and encrypt them with your key:
gpg --export-secret-keys $OLD_FINGERPRINT | gpg --encrypt -r $FINGERPRINT
Then you can delete the old secret subkeys:
gpg --delete-secret-keys $OLD_FINGERPRINT
Note that the above exports all secret subkeys associated with the
$OLD_FINGERPRINT. If you only want to export the encryption subkey,
you need to remove the other keys first. You can remove keys by using
the "keygrip", which should look something like this:
$ gpg --with-keygrip --list-secret-keys
/run/user/1000/ssss/gnupg/pubring.kbx
-------------------------------------
sec ed25519 2023-05-30 [SC] [expires: 2024-05-29]
BBB6CD4C98D74E1358A752A602293A6FA4E53473
Keygrip = 23E56A5F9B45CEFE89C20CD244DCB93B0CAFFC73
uid [ unknown] Antoine Beaupré <anarcat@anarc.at>
ssb cv25519 2023-05-30 [E]
Keygrip = 74D517AB0466CDF3F27D118A8CD3D9018BA72819
$ gpg-connect-agent "DELETE_KEY 23E56A5F9B45CEFE89C20CD244DCB93B0CAFFC73" /bye
$ gpg --list-secret-keys BBB6CD4C98D74E1358A752A602293A6FA4E53473
sec# ed25519 2023-05-30 [SC] [expires: 2024-05-29]
BBB6CD4C98D74E1358A752A602293A6FA4E53473
uid [ unknown] Antoine Beaupré <anarcat@anarc.at>
ssb cv25519 2023-05-30 [E]
In the above, the first line of the second gpg output shows that the
primary ([SC]) key is "unusable" (#).
Backing up an OpenPGP key
OpenPGP keys can typically be backed up normally, unless they are in really active use. For example, an OpenPGP-backed CA that would see a lot of churn in its keyring might have an inconsistent database if a normal backup program is ran while a key is added. This is highly implementation-dependent of course...
You might also want to do a backup for other reasons, for example with a scheme like Shamir's secret sharing to delegate this responsibility to others in case you are somewhat incapacitated.
Therefore, here is a procedure to make a full backup of an OpenPGP key pair stored in a GnuPG keyring, in an in-memory temporary filesystem:
export TMP_BACKUP_DIR=${XDG_RUNTIME_DIR:-/nonexistent}/openpgp-backup-$FINGERPRINT/ &&
(
umask 0077 &&
mkdir $TMP_BACKUP_DIR &&
gpg --export-secret-keys $FINGERPRINT > $TMP_BACKUP_DIR/openpgp-backup-$FINGERPRINT-secret.key &&
gpg --export $FINGERPRINT > $TMP_BACKUP_DIR/openpgp-backup-public-$FINGERPRINT.key &&
)
The files in $TMP_BACKUP_DIR can now be copied to a safe
location. They retain their password encryption, which is fine for
short-term backups. If you are doing a backup that you might only use
in the far future or want to share with others (see secret sharing
below), however, you will probably want to remove the password
protection on the secret keys, so that you use some other mechanism
to protect the keys, for example with a shared secret or encryption
with a security token.
This procedure, therefore, should probably happen in a temporary keyring:
umask 077 &&
TEMP_DIR=${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/gpg-unsafe/ &&
mkdir $TEMP_DIR &&
export GNUPGHOME=$TEMP_DIR/gnupg &&
cp -Rp ~/.gnupg/ $GNUPGHOME
Then remove the password protection on the keyring:
gpg --edit-key $FINGERPRINT
... then type the passwd command and just hit enter when prompted
for the password. Ignore the warnings.
Then export the entire key bundle into a temporary in-memory directory, tar all those files and self-encrypt:
BACKUP_DIR=/mnt/...
export TMP_BACKUP_DIR=${XDG_RUNTIME_DIR:-/nonexistent}/openpgp-backup-$FINGERPRINT/ &&
(
umask 0077 &&
mkdir $TMP_BACKUP_DIR &&
gpg --export-secret-keys $FINGERPRINT > $TMP_BACKUP_DIR/openpgp-backup-$FINGERPRINT-secret.key &&
gpg --export $FINGERPRINT > $TMP_BACKUP_DIR/openpgp-backup-public-$FINGERPRINT.key &&
tar -C ${XDG_RUNTIME_DIR:-/nonexistent} -c -f - openpgp-backup-$FINGERPRINT \
| gpg --encrypt --recipient $FINGERPRINT - \
> $BACKUP_DIR/openpgp-backup-$FINGERPRINT.tar.pgp &&
cp $TMP_BACKUP_DIR/openpgp-backup-public-$FINGERPRINT.key $BACKUP_DIR
)
Next, test decryption:
gpg --decrypt $BACKUP_DIR/openpgp-backup-$FINGERPRINT.tar.pgp | file -
Where you store this backup ($BACKUP_DIR above) is up to you. See
the OpenPGP backups discussion for details.
Also note how we keep a plain-text copy of the public key. This is an important precaution, especially if you're the paranoid type that doesn't public their key anywhere. You can recover a working setup from a backup secret key only (for example from a YubiKey), but it's much harder if you don't have the public key, so keep that around.
Secret sharing
A backup is nice, but it still assumes you are alive and able to operate your OpenPGP keyring or security key. If you go missing or lose your memory, you're in trouble. To protect you and your relatives from the possibility of total loss of your personal data, you may want to consider a scheme like Shamir's secret sharing.
The basic idea is that you give a symmetrically encrypted file to multiple, trusted people. The decryption key is split among a certain number (N) of tokens, out of which a smaller number (say K) tokens is required to reassemble the secret.
The file contains the private key material and public key. In our specific case, we're only interested in the encryption key: the logic behind this is that this is the important part that cannot be easily recovered from loss. Signing, authentication or certification key can all be revoked and recreated, but the encryption key, if lost, leads to more serious problems as the encrypted data cannot be recovered.
So, in this procedure, we'll take an OpenPGP key, strip out the primary secret key material, export the encryption subkey into an encrypted archive, and split its password into multiple parts. We'll also remove the password on the OpenPGP key so that our participants can use the key without having to learn another secret, the rationale here is that the symmetric encryption is sufficient to protect the key.
-
first, work on a temporary, in-memory copy of your keyring:
umask 077 TEMP_DIR=${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/ssss/ mkdir $TEMP_DIR export GNUPGHOME=$TEMP_DIR/gnupg cp -Rp ~/.gnupg/ $GNUPGHOMEThis simply copies your GnuPG home into a temporary location, an in-memory filesystem (
/run). You could also restore from the backup created in the previous section with:umask 077 TEMP_DIR=${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/ssss/ mkdir $TEMP_DIR $TEMP_DIR/gnupg gpg --decrypt $BACKUP_DIR/openpgp-backup-$FINGERPRINT.tar.pgp | tar -x -f - --to-stdout | gpg --homedir $TEMP_DIR/gnupg --import export GNUPGHOME=$TEMP_DIR/gnupgAt this point, your
GNUPGHOMEvariable should point at/run, make sure it does:echo $GNUPGHOME gpgconf --list-dir homedirIt's extremely important that GnuPG doesn't start using your normal keyring, as you might delete the key in the wrong keyring. Feel free to move
~/.gnupgout of the way to make sure it doesn't destroy private key material there. -
remove the passwword on the key with:
gpg --edit-key $FINGERPRINTthen type the
passwdcommand and just hit enter when prompted for the password. Ignore the warnings. -
(optional) delete the primary key, for this we need to manipulate the key in a special way, using the "keygrip":
$ gpg --with-keygrip --list-secret-keys /run/user/1000/ssss/gnupg/pubring.kbx ------------------------------------- sec# ed25519 2023-05-30 [SC] [expires: 2024-05-29] BBB6CD4C98D74E1358A752A602293A6FA4E53473 Keygrip = 23E56A5F9B45CEFE89C20CD244DCB93B0CAFFC73 uid [ unknown] Antoine Beaupré <anarcat@anarc.at> ssb cv25519 2023-05-30 [E] Keygrip = 74D517AB0466CDF3F27D118A8CD3D9018BA72819 $ gpg-connect-agent "DELETE_KEY 23E56A5F9B45CEFE89C20CD244DCB93B0CAFFC73" /bye $ gpg --list-secret-keys BBB6CD4C98D74E1358A752A602293A6FA4E53473 sec# ed25519 2023-05-30 [SC] [expires: 2024-05-29] BBB6CD4C98D74E1358A752A602293A6FA4E53473 uid [ unknown] Antoine Beaupré <anarcat@anarc.at> ssb cv25519 2023-05-30 [E] -
create a password and split it in tokens:
tr -dc '[:alnum:]' < /dev/urandom | head -c 30 ; echo ssss-split -t 3 -n 5Note: consider using SLIP-0039 instead, see below.
-
export the secrets and create the encrypted archive:
mkdir openpgp-ssss-backup-$FINGERPRINT gpg --export $FINGERPRINT > openpgp-ssss-backup-$FINGERPRINT/openpgp-backup-public-$FINGERPRINT.key gpg --export-secret-keys $FINGERPRINT > openpgp-ssss-backup-$FINGERPRINT/openpgp-ssss-backup-$FINGERPRINT-secret.key tar -c -f - openpgp-ssss-backup-$FINGERPRINT | gpg --symmetric - > openpgp-ssss-backup-$FINGERPRINT.tar.pgp rm -rf openpgp-ssss-backup-$FINGERPRINTNote that if you expect your peers to access all your data, the above might not be sufficient. It is, for example, typical to store home directories on full disk encryption. The above will therefore not be sufficient to access (say) your OpenPGP-encrypted password manager or emails. So you might want to also include a password for one of the LUKS slots in the directory as well.
-
send a
README, the.pgpfile and one token for each person
Dry runs
You might want to periodically check in with those people. It's perfectly natural for people to forget or lose things. Ensure they still have control of their part of the secrets and the files, know how to use it and can still contact each other, possibly as a yearly event.
This is a message I send everyone in the group once a year:
Hi!
You're in this group and receiving this message because you
volunteered to be one of my backups. At about this time of the year in
2023, I sent you a secret archive encrypted with a secret spread among
you, that 3 out of 5 people need to share to recover.
Now we're one year later and i'd like to test that this still
works. please try to find the encrypted file, the instructions (which
should be stored in a README along side the encrypted file) and the
sharded secret, and then come back here to confirm that you still have
access to those.
DO NOT share the secret, i am not dead and still fully functional,
this is just a drill.
If anyone fails to reply after 6 weeks, or around mid-august, I'll
start the procedure to reroll the keys to a new group without that
person.
If you want out of the group, now is a good time to say so as well.
If you don't understand what this is about, it's an excellent time to
ask, don't be shy, it's normal to forget that kind of stuff after a
year, it's why i run those drills!
so TL;DR: confirm that you still have:
1. the secret archive, should be named `openpgp-ssss-backup-BBB6CD4C98D74E1358A752A602293A6FA4E53473.tar.gpg`
2. the instructions (optional), should be named `README.md`
3. the shared secret (should be in your password manager)
thanks!
Sample README file
The README file needs to explain how to recover from all of this. Consider that your peers (or yourself!) might not actually remember any of how this works, so it should be detailed more than less, and should be available in clear text.
Here's an example:
# About this file
You are receiving this information because you are deemed trustworthy
to carry out the instructions in this file.
Some of the data you've been given is secret and must be handled with
care. It is important that it is not lost. Your current
operational security and procedures are deemed sufficient to handle
this data. It is expected, for example, that you store those secrets
in your password manager, and that the password manager is backed up.
You can use any name to store the secret token, but I suggest you file
that secret under the name "anarcat-openpgp-ssss-token".
You are among 5 other persons to receive this data. Those people are:
* [redacted name, email, phone, etc]
* [redacted name, email, phone, etc]
* [...]
Three of you are necessary to recover this data. See below for
instructions on how to do so.
It is expected that if you end up in a position to not be able to
recover those secrets, you will notify me or, failing that, the other
participants so that appropriate measures be taken.
It is also expected that, if you completely lose contact with me and
are worried about my disappearance, you will contact next of kin. You
can reach my partner and family at:
* [redacted name, email, phone, etc]
* [...]
Those people are the ones responsible for making decisions on
sensitive issues about my life, and should be reached in the event of
my death or incapacity.
Those instructions were written on YYYY-MM-DD and do not constitute a
will.
# Recovery instructions
What follows describes the recovery of anarcat's secrets in case of
emergency, written by myself, anarcat.
## Background
I own and operate a handful of personal servers dispersed around the
globe. Some documentation of those machines is available on the
website:
<https://anarc.at/hardware>
and:
<https://anarc.at/services>
If all goes well, `marcos` is the main server where everything
is. There's a backup server named `tubman` currently hosted at
REDACTED by REDACTED.
Those instructions aim at being able to recover the data on those
servers if I am incapacitated, dead, or somehow loses my memory.
## Recovery
You are one of five people with a copy of those instructions.
Alongside those instructions, you should have received two things:
* a secret token
* an encrypted file
The secret token, when assembled with two of the other parties in this
group, should be able to recover the full decryption key for the
OpenPGP-encrypted file. This is done with Shamir's Secret Sharing
Scheme (SSSS):
<https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing>
The encrypted file, in turn, contains two important things:
1. a password to decrypt the LUKS partition on any of my machines
2. a password-less copy of my OpenPGP keyring
The latter allows you to access my password manager, typically stored
in `/home/anarcat/.password-store/` on the main server (or my laptop).
So the exact procedure is:
1. gather three of the five people together
2. assemble the three tokens with the command `ssss-combine -t 3`
3. decrypt the file with `gpg --decrypt anarcat-rescue.tar.pgp`
4. import the OpenPGP secret key material with `gpg --import
openpgp-BBB6CD4C98D74E1358A752A602293A6FA4E53473-secret.key`
5. the LUKS decryption key is in the `luks.gpg` file
## Example
Here, three people are there to generate the secret. They call the
magic command and type each their token in turn, it should look
something like this:
$ ssss-combine -t 3
Enter 3 shares separated by newlines:
Share [1/3]: 2-e9b89a7bd56abf0164e57a7e9a0629a268f57e1d1b0475ff5062e101
Share [2/3]: 5-869c193144bcc58ed864d6648661ab83c7ce5b0751d649d5c54f77a9
Share [3/3]: 1-039c2941fb73620acf9be7eabb2191160b7474a7cdebc405e612beb0
Resulting secret: YXtJpJwzCqd1ELh3KQCEuJSvu84d
(Obviously, the above is just an example and not the actual secret.)
Then the "Resulting secret" can be used to decrypt the file:
$ gpg --decrypt openpgp-ssss-backup-BBB6CD4C98D74E1358A752A602293A6FA4E53473.tar.gpg > openpgp-ssss-backup-BBB6CD4C98D74E1358A752A602293A6FA4E53473.tar
gpg: AES256.CFB encrypted data
gpg: encrypted with 1 passphrase
Then from there, the `tar` archive can be extracted:
$ tar xfv openpgp-ssss-backup-BBB6CD4C98D74E1358A752A602293A6FA4E53473.tar
openpgp-ssss-backup-BBB6CD4C98D74E1358A752A602293A6FA4E53473/
openpgp-ssss-backup-BBB6CD4C98D74E1358A752A602293A6FA4E53473/openpgp-ssss-backup-BBB6CD4C98D74E1358A752A602293A6FA4E53473-secret.key
openpgp-ssss-backup-BBB6CD4C98D74E1358A752A602293A6FA4E53473/luks.gpg
The encryption subkey should be importable with:
gpg --import < anarcat-secrets/openpgp-backup-BBB6CD4C98D74E1358A752A602293A6FA4E53473-secret-subkeys.key
To get access to more resources, you might need to unlock a LUKS (on
the main server, currently `marcos`) or encrypted ZFS (on the
backup server, currently `tubman`) partition. The key should be
readable in the `luks.gpg` file:
gpg --decrypt luks.gpg
From there you should be able to access either the backup or main
server and, from there, access the password manager in
`.password-store`.
For example, this will show the unlock code for my phone:
gpg --decrypt < ~/.password-store/phone-lock.gpg
You will need to adapt this to your purposes.
Other approaches
I am considering a more standard secret sharing scheme based on SLIP-039, established in the Bitcoin community, but that is applicable everywhere. The python-shamir-mnemonic implementation, for example, provides human-readable secrets:
anarcat@angela:~> shamir create 3of5
Using master secret: 608e920fc59a6cf2d23bcfe6cb889771
Group 1 of 1 - 3 of 5 shares required:
yield pecan academic acne body teacher elder twin detect vegan solution maiden home switch dryer member purple voice acquire username
yield pecan academic agree ajar cause critical leader admit viral taxi puny curious sled often satoshi lips afraid stadium froth
yield pecan academic amazing blanket decision crystal vexed trial fitness shaped timber helpful beard strategy curious episode sniff object heat
yield pecan academic arcade alcohol vampire employer package tactics extra window sympathy darkness adapt laundry genius laser closet example ruler
yield pecan academic axle aquatic have racism debris spew dive human thumb weapon satoshi curly lobe lecture visitor example alarm
Notice how the first three words are the same in all tokens? That's also useful to identify the secret itself...
Note that if you are comfortable sharing all your secret keys with those peers, a simpler procedure is to re-encrypt your own backup with a symmetric key instead of your Yubikey encryption key. This is much simpler:
gpg --decrypt $BACKUP_DIR/gnupg-backup.tar.pgp | gpg --symmetric - > anarcat-secrets.tar.pgp
Note that a possibly simpler approach to this would be to have an OpenPGP key generated from a passphrase, which itself would then be the shared secret. Software like passphrase2pgp can accomplished this but haven't been reviewed or tested. See also this blog post for background.
Pager playbook
Disaster recovery
Reference
Installation
SLA
Design
OpenPGP is standardized as RFC4880, which defines it as such:
OpenPGP software uses a combination of strong public-key and symmetric cryptography to provide security services for electronic communications and data storage.
The most common OpenPGP implementation is GnuPG, but there are others.
Issues
There is no issue tracker specifically for this project, File or search for issues in the team issue tracker.
Maintainer, users, and upstream
Monitoring and testing
Logs and metrics
Backups
Other documentation
Discussion
Overview
Goals
Must have
Nice to have
Non-Goals
Approvals required
Proposed Solution
Cost
Alternatives considered
Expiration dates
Note that we set an expiration date on generated key. This is to protect against total loss of all backups and revocation certificates, not getting the key stolen, as the thief could extend the expiration key on their own.
This does imply that you'll need to renew your key every time the expiration date comes. I set a date in my planner and typically don't miss the renewals.
Separate certification key
Note that some guides favor separating the signing (S) subkey from
the certification (C) key. In this guide, we keep the default which
is to have both together. This is mostly because we use a YubiKey
as storage and it only supports three key slots.
But even if there would be four, the point of having a separate certification key is that it can be stored offline. In my experience, this is risky: the key could be lost and will be less often used so memory of how do use it could be lost. Having an expiration date will help with this in the sense that the user will have to reuse the certification key regularly.
One approach could be to have a separate YubiKey for certification, stored offline and used only for renewals and third-party certifications.
Airgapped systems
In the key generation procedure, we do not explicitly say where the key should be generated, this is left as a precaution to the reader.
Some guides like drduh's guide says this:
To create cryptographic keys, a secure environment that can be reasonably assured to be free of adversarial control is recommended. Here is a general ranking of environments most to least likely to be compromised:
- Daily-use operating system
- Virtual machine on daily-use host OS (using virt-manager, VirtualBox, or VMware)
- Separate hardened Debian or OpenBSD installation which can be dual booted
- Live image, such as Debian Live or Tails
- Secure hardware/firmware (Coreboot, Intel ME removed)
- Dedicated air-gapped system with no networking capabilities
This guide recommends using a bootable "live" Debian Linux image to provide such an environment, however, depending on your threat model, you may want to take fewer or more steps to secure it.
This is good advice, but in our experience adding complexity to guides makes the user more likely to completely fail to follow the instructions altogether, at worst. At best, they will succeed, but could still trip on one tiny step that makes the whole scaffolding fall apart.
A strong focus on key generation also misses the elephant in the room which is that it's basically impossible to establish a trusted cryptographic system on a compromised host. Key generation is only one part in a long chain of operations that must happen on a device for the outputs to be trusted.
The above advice could be applied to your daily computing environment and, indeed, many people use environments like Qubes OS to improve their security.
See also just disconnect the internet for more in-depth critique of the rather broad "airgapped" concept.
About ECC (elliptic curve cryptography)
In the key generation procedures, we're going to generate an Elliptic Curve (ECC) key using Curve25519. It was chosen because the curve has been supported by OpenSSH since 2014 (6.5) and GnuPG since 2021 (2.1) and is the de-facto standard since the revelations surrounding possibly the back-doored NIST curves.
Some guides insist on still using RSA instead of ECC based on this post detailing problems with ECDSA. But that post explicitly says that:
Further, Ed25519, which is EdDSA over Curve25519, is designed to overcome the side-channel attacks that have targeted ECDSA, and it is currently being standardized by NIST.
... and that "ECDSA is fragile, but it is not broken".
ECC is faster than RSA, which is particularly important if cryptographic operations are shifted away from the powerful CPU towards a security key that is inherently slower.
ECC keys are also much smaller, which makes them easier to transfer and copy around. This is especially useful if you need to type down an SSH key on some weird console (which does happen to me surprisingly regularly).
Why GnuPG
A lot of OpenPGP's bad reputation comes from the particularly byzantine implementation that has become the ad-hoc reference implementation, GnuPG.
GnuPG's implementation of the OpenPGP standard is arcane, buggy, and
sometimes downright insecure. It has bad defaults, a horrible user
interface, the API is a questionable C library running on top of a
nightmarish command-line file-descriptors based dialect, and will eat
your cat if you don't watch it carefully. (Yes, I know, {{citation needed}}, you'll have to trust me on all of those for now, but I'm
pretty sure I can generate a link for each one of those in time.)
Unfortunately, it's the only implementation that can fully support smart cards. So GnuPG it is for now.
Other OpenPGP implementations
Sequoia (Rust)
Sequoia, an alternative OpenPGP implementation written in Rust, has a much better user interface, security, and lots of promises.
It has a GnuPG backwards compatibility layer and a certificate store, but, as of June 2023, it doesn't have private key storage or smart card support.
Sequoia published (in 2022), a comparison with GnuPG that might be of interest and they maintain a comparison in the sq guide as well. They are working on both problems, see the issue 6 and openpgp-card crates.
Update (2024): the OpenPGP card work is progressing steadily. There's
now a minimalist, proof-of-concept, ssh-agent implementation. It
even supports notifying the user when a touch is required (!). The
0.10 release of the crate also supports signature generation, PIN
prompting, and "file-based private key unlocking". Interestingly, this
is actually a separate commandline interface from the sq binary in
Sequoia, although it does use Sequoia as a library.
RNP (C++)
RNP is the C++ library the Mozilla Thunderbird mail client picked to implement native OpenPGP support. It's not backwards-compatible with GnuPG's key stores.
There's a drop in replacement for RNP by the Sequoia project called octopus which allows one to share the key store with GnuPG.
PGPainless (Java)
The other major OpenPGP library is PGPainless, written in Java, and mainly used on Android implementations.
Others
The OpenPGP.org site maintains a rather good list of OpenPGP implementations.
OpenPGP backups
Some guides propose various solutions for OpenPGP private key backups. drduh's guide, for example, suggests doing a paper backup, as per the Linux Kernel maintainer PGP guide.
Some people might prefer a LUKS-encrypted USB drive hidden under their bed, but I tend to distrust inert storage since it's known to lose data in the long term, especially when unused for a long time.
Full disk encryption is also highly specific to the operating system in use. It assumes a Linux user is around to decrypt a LUKS filesystem (and knows how as well). It also introduces another secret to share or remember.
I find that this is overkill: GnuPG keyring are encrypted with a passphrase, and that should be enough for most purposes.
Another approach is to backup your key on paper. Beware that this approach is time-consuming and exposes your private key to an attacker with physical access. The hand-written approach is also possibly questionable, as you basically need to learn typography for that purpose. The author, here, basically designs their own font, essentially.