GnuPG and other creatures.

GnuPG is a tricky and finicky piece of software. While learning it, I had to find out a lot of its peculiarities.

1. Notes

1.1. Gnupg is very stateful.

It heavily depends on the gnupg directory, where it keeps the address book.

1.2. Gnupg is very picky about options, parameters, whatever it has.

Example:

gpg2 --yes  --batch --trust-model always --armor --recipient 'security@slackware.com'

The --yes --batch --trust-model always is needed for demo purposes, so that no questions are asked. --armor is needed, because output is normally binary, and we need to encode it.

1.3. Address book is very important for gnupg.

As opposed to whatever you might read in the manuals, cryptography is not really about algorithms as it is about people.

When working with gnupg, you will be dealing with people all the time, looking at them, trusting or dis-trusing them, looking at their connections, et cetera.

1.4. Keys are identified by fingerprints.

Keys themselves are huge (4096 bytes are, like 2 pages of text), you don’t want to manage them yourself. But each of them has a fingerprint, which is used to identify it more or less uniquely.

1.5. Key-ID is the last 16 numbers of a fingerprint.

It is not documented anywhere, but now I am telling you.

  1. ’6A4463C040102233’ is the Key-ID for Slackware Linux.
  2. ’0368EF579C7BA3B6’ is the Key-ID for SlackBuilds Project.
gpg2 --no-verbose -q --recv-keys '6A4463C040102233'  2>&1
echo
gpg2 --no-verbose -q --recv-keys '0368EF579C7BA3B6'  2>&1
gpg: enabled debug flags: memstat
gpg: data source: http://pgp.surf.nl:11371
gpg: armor header: Comment: Hostname: pgp.surf.nl
gpg: armor header: Version: Hockeypuck 2.1.2
gpg: key 6A4463C040102233: number of dropped non-self-signatures: 231
gpg: pub  dsa1024/6A4463C040102233 2003-02-26  Slackware Linux Project <security@slackware.com>
gpg: removing signature from key 6A4463C040102233 on user ID "Slackware Linux Project <security@slackware.com>": signature superseded
gpg: removing signature from key 6A4463C040102233 on user ID "Slackware Linux Project <security@slackware.com>": signature superseded
gpg: key 6A4463C040102233: 1 duplicate signature removed
gpg: key 6A4463C040102233: "Slackware Linux Project <security@slackware.com>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1
gpg: keydb: handles=2 locks=1 parse=0 get=2
gpg:        build=0 update=0 insert=0 delete=0
gpg:        reset=0 found=2 not=0 cache=0 not=0
gpg: kid_not_found_cache: count=0 peak=0 flushes=0
gpg: sig_cache: total=20 cached=14 good=14 bad=0
gpg: random usage: poolsize=600 mixed=0 polls=0/0 added=0/0
              outmix=0 getlvl1=0/0 getlvl2=0/0
gpg: rndjent stat: collector=0x0000000000000000 calls=0 bytes=0
gpg: secmem usage: 0/32768 bytes in 0 blocks

gpg: enabled debug flags: memstat
gpg: data source: http://pgp.surf.nl:11371
gpg: armor header: Comment: Hostname: pgp.surf.nl
gpg: armor header: Version: Hockeypuck 2.1.2
gpg: key 0368EF579C7BA3B6: number of dropped non-self-signatures: 14
gpg: pub  dsa1024/0368EF579C7BA3B6 2007-01-27  SlackBuilds.org Development Team <slackbuilds-devel@slackbuilds.org>
gpg: key 0368EF579C7BA3B6: "SlackBuilds.org Development Team <slackbuilds-devel@slackbuilds.org>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1
gpg: keydb: handles=2 locks=1 parse=0 get=2
gpg:        build=0 update=0 insert=0 delete=0
gpg:        reset=0 found=2 not=0 cache=0 not=0
gpg: kid_not_found_cache: count=0 peak=0 flushes=0
gpg: sig_cache: total=9 cached=7 good=7 bad=0
gpg: random usage: poolsize=600 mixed=0 polls=0/0 added=0/0
              outmix=0 getlvl1=0/0 getlvl2=0/0
gpg: rndjent stat: collector=0x0000000000000000 calls=0 bytes=0
gpg: secmem usage: 0/32768 bytes in 0 blocks

1.6. GnuPG does not preserve message length.

This is not obvious, but this is true.

Test message 0003 +++.

Encrypt with one key:

  1. Slackware Linux Project <security@slackware.com> (not certified, OpenPGP, created: 27/02/2003)
gpg2 --yes  --batch  --armor --trust-model always --no-default-recipient --recipient 'security@slackware.com'  --encrypt
-----BEGIN PGP MESSAGE-----

hQEOA3aHN/lOUjVpEAP+NsqQ/tY2+EwNeYX2TV1RHMnQB06uEbI24uKIAqyb6x7M
v328C+z42Fbbyf82/yb7SuJvwnjLJZ0PcskTWQmo7009yi7OBnddkfpCAzSN67zD
ijps3oTtBn2gXHJhgdGK+x9hpTDAMAPC5UDm6CJIfH5/GbATxNOn5InWKk4TWOwD
/iivsu+Cw7/qiS5f2CY2PXRtdINHsNew/sCkRAtQPeFraNS2BCF4unuIPBeUM0F9
U5XBASzq1L+leFzv2FTrc4xk/4lThUFEBo7+XPR3A85rnpqAMqDiN5OVfHnrNVRt
dUfyi2zPddienWPuMNEI37eY4YejmWoH4CNyR3LQDZ9w0jsBb5oOBwc6Mi9pWGxz
lRs27RScTFqoQg/WkHVuoFTMkc3DBgj7TeUvViEf45Q1mRJaYgavMuXXuhB8EQ==
=biKf
-----END PGP MESSAGE-----

There is an empty line at the top, but not at the bottom? WTF?

wc -c
wc -c <<EOF
nil
EOF
0
517

Size hugely differs!

1.7. GnuPG encrypts the same cleartext DIFFERENTLY EACH TIME

HUGELY UNOBVIOUS

The following example has the same cleartext, but the cipertext will be different.

sleep 1;  gpg2 --yes  --batch  --armor --trust-model always --recipient 'security@slackware.com' --encrypt
-----BEGIN PGP MESSAGE-----

hQEOA3aHN/lOUjVpEAP/RUxAlhkW8PrJ4ULAeM9Po9x9vQtKUfdpbktt7Fd5CvQx
1PpXNLSPQcu3RmMWExlSOdAc89u4oeJsleR18jQuwUTfuqC5k/zzbsxHitEeoWSW
NVL2g+g0qeh20XOPe/otVPA4Ho/NjX4WwNy9NVgZzMPw90R4/cAhCcikvPHzEhEE
AL1cKFklENuPegkFmIe+3FifaokEAe8t2Mkk9jd5uBMVaTmnlXzjhloXopscpCsD
yKAcEdfsP2rvvIXhssxAvPAqauWyYpgULe64AdxeniutjyTyRQxvExmWw1HQRmZZ
XEgptlTFpL53ZHOvoaN9TxfqGP5BP8ylDZdplxBUCVZw0lIB48yndY7xChfeebbB
ed1Nmtd9TI5KOZHUz018M/+adXzu8agHHRoxmI1rwMi8akzPVLl36af1DN3lzfCT
6RF17GXgpewPHqbiK6miiB/7AaSb
=nbTL
-----END PGP MESSAGE-----
diff --report-identical-files --side-by-side <(cat <<EOF
nil
EOF
)  <(cat <<EOF
nil
EOF
)
exit 0
-----BEGIN PGP MESSAGE-----					-----BEGIN PGP MESSAGE-----

hQEOA3aHN/lOUjVpEAP/d9lUngV9EVFh9yGRmijaY0xgbiRxC96FVw11yosiO |	hQEOA3aHN/lOUjVpEAQAkzjNExyYNSqrM152b7npJzo20WXbBKDYEZ8kvoBwu
+LUFcJoKVl7at+OA7YFQepNoGlrjaMmM64+1mPZjpVau6ydfSHPR41TOPoJVV |	jmoaiJmFPLU2krlDzT8tr5nrNYU9TXM2wx0A1geM59Xp6T7HzSVjlehaGLTga
8dG2eM+cBY+disXlYHHD6KX9R1hzKa0bhrzwmbqbRLxRTitb7Z9MurUJcXVDj |	0bo6L7/m3guF86kd7+NTsbLeogQ6Ra6ckOLrtW5wbR25kfEub2QkA6tJli7E4
/2t0oSFJ5V4Btz7rUIHi8bwa9mlVeZgkwVIAnanvYGAgjaw0JJinqtyTe+Cb/ |	/iQe101Tuaq93roc2Ar1QijUSNEDZgv0m/7xQ6lWnT0APegGC843LPiIhXfEe
k1bjgfPVhKmztr3msZrOy8xUJlFekZVW5KNGD4vgwTZSqEW37Q0bQ3MYiR4sI |	ozGi23quj3oNJpHSMeQvzxGw5+K+CkRuWnqT43nfpGOVMAsfed/DRbVjQuQs5
RB2XZHoXijDzJNHRx+tfCk5nhZ9zLgfWkDOQxneSF+MB0lIBbNw6fp9QcTZSb |	vjoMG+Sw8md35lsvWsTv+dErBv/UtldMwxLNzTXX+Vwi0lIBo32DixsV7wx4f
RC1orzZAABW5rzzwLpipT7uQ5ISGGxtBtZhFr6uZX8GHl8Gqk0pLpMEQkbBP4 |	B/Rj7VoWYsOgLLR0xXO48L7eQVWgYy3sOygBZwgcfDs3a1ihIlrBYXqMre55F
n1BWvFbe2G7t/9PRyCLAbiqnVHDm				      |	a7W3U2gV4YnjS8CTaOjzB09ZCsAw
=Cjf8							      |	=iXjK
-----END PGP MESSAGE-----					-----END PGP MESSAGE-----

They are similar up to the character 18, but not further.

1.8. GnuPG by default also includes metadata. (HUGELY unclear)

read -d '' cleartext
diff -y \
<(printf "%s" "$cleartext" | gpg2 --yes  --batch  --armor --trust-model always --hidden-recipient 'security@slackware.com' --encrypt) \
<(printf "%s" "$cleartext" | gpg2 --yes  --batch  --armor --trust-model always --recipient 'security@slackware.com' --encrypt)
printf "\nVisible recpipent\n"
printf "%s" "$cleartext" | gpg2 --yes  --batch  --armor --trust-model always --no-default-recipient --recipient 'security@slackware.com' --encrypt | gpg2 --pinentry-mode cancel --list-packets 2>&1 | grep -F 'encrypted with'
printf "\nHidden recpipent\n"
printf "%s" "$cleartext" | gpg2 --yes  --batch  --armor --trust-model always --no-default-recipient --hidden-recipient 'security@slackware.com' --encrypt | gpg2 --pinentry-mode cancel --list-packets 2>&1 | grep -F 'encrypted with'
-----BEGIN PGP MESSAGE-----					-----BEGIN PGP MESSAGE-----

hQEOAwAAAAAAAAAAEAQAx07fFUO7g5qBUJthUCyfa3L1PguSLxGAe8AlBhZbe |	hQEOA3aHN/lOUjVpEAP8COSescSPFwZHrmYNyMy6XjIa8vN0bbBj50QzhWGLC
PqQ6E8/sTFwd2ix6elUPolsJ9fFQ7rU0x11rBJHH7i6uBDWzasdkp8Rn3w/Ss |	OtBLMcGjitCrwRiiEsDvb70YkC1W4ujv9PcKnMPlk/GZeuCiJqXXivMaNvRV1
NL77DExogIFe9HqMzJzsELiheYMVN76XAlkr7kCS7H2Fd7TqC4ZXqlo9Eg+r4 |	okIFYgvy845eZ1VV//CJXTDTUm7YKbxisRptAWJISb1QydiuYrmh0BEs2cUgH
ALPL/30E+GXF+poTXOCXI95cdgHGiRWZyxQWFQeduiHKNOkBfe53YPCwO7yHl |	/2Qr9kqo0iBrGF8TUGZKK0rbiAQSis1yC/dGNfCkI/nmcHWzeEE/fsOg+IZaJ
NS/wDQH9lE3zta+9uYYG2U3/ktjRye2JOQ92tPC5TjXpq9fKA9+PbWP5K4KoL |	NGWPi4H8AKvvStJrvnNctvFd5eRkDihzNWjbN15QQhCz/3qY12GxRxhTuYv9+
c7bqUM16d5FQlFSH/yimAGb9gRMQFE8SpawSv92XrY1P0lEBBsZ8z9ZtsigbZ |	X35LzHc72yVt3Yu74XgK8QZZP4vwsE6Lapm6yEmJX1Ex0lEBXvvffz3N433Hc
gM129ABQc1gHRzspmm4LCbFzJdLgpq5sXZ5UygZcRfD3wmdgmeuJB6xylbe7r |	w2aTs6+cYiQHMCX6DaFACpNuIj9CGNSJ3/Yyw56vCpMYG5aHVs6gH1lHFokla
803nE4RSPuR52I8B+UZMh+2F240=				      |	77J8ny9fW1MkOyi7MxdZone667k=
=iREK							      |	=xg18
-----END PGP MESSAGE-----					-----END PGP MESSAGE-----

Visible recpipent
gpg: encrypted with 1024-bit ELG key, ID 768737F94E523569, created 2003-02-26

Hidden recpipent
gpg: encrypted with ELG key, ID 0000000000000000

HIDING RECIPIENT IS NOT (!!!) THE DEFAULT SETTING. The interceptor WILL KNOW who you are writing to by default.

1.9. Different recipients

Now try with different recipients:

  1. Slackware Linux Project <security@slackware.com> (not certified, OpenPGP, created: 27/02/2003)
  2. SlackBuilds.org Development Team <slackbuilds-devel@slackbuilds.org> (not certified, OpenPGP, created: 28/01/2007)
   read -d '' cleartext
   diff -y \
  <(printf "%s" "$cleartext" | gpg2 --yes  --batch  --armor --trust-model always --recipient 'security@slackware.com' --encrypt) \
  <(printf "%s" "$cleartext" | gpg2 --yes  --batch  --armor --trust-model always --recipient 'security@slackware.com' --recipient 'slackbuilds-devel@slackbuilds.org' --encrypt)
printf "%s" "$cleartext" | gpg2 --yes  --batch  --armor --trust-model always --recipient 'security@slackware.com' --encrypt | wc -c
printf "%s" "$cleartext" | gpg2 --yes  --batch  --armor --trust-model always --recipient 'slackbuilds-devel@slackbuilds.org' --encrypt | wc -c
printf "%s" "$cleartext" | gpg2 --yes  --batch  --armor --trust-model always --recipient 'security@slackware.com' --recipient 'slackbuilds-devel@slackbuilds.org' --encrypt | wc -c
printf "\nAlso metadata for multiple recipients:\n"
printf "%s" "$cleartext" | gpg2 --yes  --batch  --armor --trust-model always --recipient 'security@slackware.com' --recipient 'slackbuilds-devel@slackbuilds.org' --encrypt | gpg2 --list-packets 2>&1 | grep -F 'encrypted with'
-----BEGIN PGP MESSAGE-----                                     -----BEGIN PGP MESSAGE-----

hQEOA3aHN/lOUjVpEAQAwSYL8+1VxzbgDuH4O+3APLww1/YRPwVT1fMwcJaJn | hQEOA3aHN/lOUjVpEAP+NOgmHMV3+7lJNqBPzau3Jk36HQAMTToo8UUaEoIsK
ATZQzEGEPhG30ffWXPhC+fYHIbVLo0dEtkD+2K8Q1YYpvLWf8gRJPs5aFal3G | neHzSnrQruLmfQDKDrcx3L7gA40ML7V4lYJ8Gr+xBKgkInk5veHyEQEO28cct
JB8DDO0otSLFpBa4qIkKBajwwxnQy5lsyz5+nHhynWsAqgYGbjpXuryPnAKvY | WVrde9CaRQAU7rQLrx+yBclHTtjFo5Zz0bkk6G5kbAtb9GVdV2M2LYNHElzdt
+waeFOL4gykwxQrcJMk5bcsTntXErVRR+lDFhklv0d3/M62hbG3RyvL4yq/Ir | AMnS0UDUSc6OiJXJFvc2wTeUmBIW4zaWDDHfPbZ9HOMK4FlDnLwZ8E5q3QA5c
HdjAlcrGyUnKN7f/sFjZghRCRV7B/GugruC5vD45Nc030De5o1yKK30uSqQ85 | AYGs2qba/FhCddbJlQkrppxijR/KW6OB2He6QqlpIT5rMtTmff9SumPetztJx
8iqfThVldyWiYJl+iyIwFikhv3VSKXIoMBJRDzqLR5qE0jsBmnKyfX+iOOYS5 | NTUO2unAEfl2sR4qD2hdgMxKo7PLM37PuUyBgn+Yx7Q3hQIOA5HkZF/FddSbE
+u6WQqtxwsYoRFM11AwgaWN7cpnWkIlSFgyPxa6xN3zt3q4x8nZPmNNeHIhKd | XiVVNr6L5j1+Zo7HaBjh6RYAJ2Cx6zzFsP0dVyrZ3Z+2U600nE7Ttt2Bkwgjf
=8HI6                                                         | 8QNDglJewjR4nd0LKg7A55lVNpGWM4l7tZ54WPagn8UOJtFgxNVliupjH5G3Y
                                                              > qrFmOGxoQoIuwPKQsZm5gekFXJraHLuOWKjH4+I5FeeJErnkJGKu3m2Ay+Jo+
                                                              > 2gi81VmAYwfpJmXlankXkATAFRMk/i9mfkQPFkrE+VVei/vyxT99alInghOQm
                                                              > lW+1CaJwhsrWLx6qXTceAe5aNE9d3XPfDu90pEHxENWkQGffuK4BQyYGy8l7X
                                                              > Kx8fS25cS22vsriYpV6hxAf/arg5Qv7Hy6GCh9y+bNBfzGX214Jh8yjVLUtu/
                                                              > 6Ivh/NvOjpf5QVlebmsur36KxZy/n/y/OQkjTslB47gdm5twdzty81O3wYUNO
                                                              > YzhOCzCldClUTVygU89ekB3BvRzi17lVoNOstIz6CGVSPIEUWiYpHOwNdjBoo
                                                              > mftI6SVxFYiJPeiVCL3mweE/YKx9zyVhJYKHSz4L2fKfDF5vLTymsV0zyCsUG
                                                              > 6azAXXn3O4LsAofz+sjPJvbN3vzk0PSOYAtYSdyJOUIFt9Q/Xu7xuwr8hX4a5
                                                              > uYPRymgFNtbmNAxrgQD/NoYJvYd0CEpebv91NBaYrae9SNI7AbhlkXwc12bcF
                                                              > uMq1d1MhktDJrt7zC5Cd0ATLEJ4JfUFsznudgaUEafAHrltnMSS9jYoKhynzf
                                                              > =yTHX
-----END PGP MESSAGE-----                                       -----END PGP MESSAGE-----
516
862
1231

Also metadata for multiple recipients:
gpg: encrypted with 2048-bit ELG key, ID 0x91E4645FC575D49B, created 2007-01-27
gpg: encrypted with 1024-bit ELG key, ID 0x768737F94E523569, created 2003-02-26

Two-recipient message is NOT two ciphertexts concatenated. Why?

1.10. trust level

sig {,1,2,3} is trust level:

gpg2 --list-sigs 'slackbuilds-devel@slackbuilds.org'
pub   dsa1024/0x0368EF579C7BA3B6 2007-01-27 [SC]
      Key fingerprint = D307 6BC3 E783 EE74 7F09  B8B7 0368 EF57 9C7B A3B6
      Keygrip = F9231F44B9E53FAC422A0B8D69FAC7D94F824BB1
uid                   [ unknown] SlackBuilds.org Development Team <slackbuilds-devel@slackbuilds.org>
sig          0xF1D5979976B20C2C 2007-01-27  [User ID not found]
sig          0x5E56AAAFA75CBDA0 2007-01-27  Eric Hameleers <alien@slackware.com>
sig          0x57DB2CB7EABADD7B 2007-01-27  [User ID not found]
sig 3        0xED03EF40D0E52F04 2007-01-27  [User ID not found]
sig 3        0x8D01BA7CBD9A880E 2007-01-27  [User ID not found]
sig 3        0x0368EF579C7BA3B6 2007-01-27  SlackBuilds.org Development Team <slackbuilds-devel@slackbuilds.org>
sig          0x151BC8BDF48D71EA 2007-02-08  [User ID not found]
sig          0xB44A343FA8F23B66 2008-11-22  [User ID not found]
sig          0x72C395892C5402BF 2009-01-05  [User ID not found]
sig          0x6A4463C040102233 2013-03-12  Slackware Linux Project <security@slackware.com>
sig          0xE8D8E103E906E998 2017-04-06  [User ID not found]
sig          0x883EC63B769EE011 2016-11-10  [User ID not found]
sig       X  0x78C2DF2D1A170CC6 2016-07-12  [User ID not found]
sub   elg2048/0x91E4645FC575D49B 2007-01-27 [E]
      Key fingerprint = 2415 DC27 B7A0 F5D6 5806  E4C4 91E4 645F C575 D49B
      Keygrip = A67D366751302FE14888E348BB2634D409EC71F4
sig          0x0368EF579C7BA3B6 2007-01-27  SlackBuilds.org Development Team <slackbuilds-devel@slackbuilds.org>

1.11. Importing a key to Emacs

https://rollenspiel.social/@ArneBab/110294093784538500

C-s key tab C-space C-<arrow-down> M-x epa-import-keys-region

PGP with mu4e just works.

Sadly it’s not the case for every GnuPG client.

1.12. What are you defending against? How do you find out that enemies don’t send emails in your name?

You don’t!

Enemies MAY send emails pretending to be your.name@gmail.com, and you cannot do anything against it, if your friends don’t verify signatures. (And they do not.)

1.13. How do you prevent people from publicly disclosing that they know you by signing your key and uploading it to a keyserver?

You can’t!

Moreover, a terrorist can ceritify your key and upload it to the server, and somebody can claim that you talked to a terrorist!

So, the protection against this is only social. You MUST memorise the phrase “anybody can certify my key and upload it to the server”.

Moreover, most people keep their contacts in Google Contacts anyway, so Google does know who you know. Also, most people won’t bother uploading your key anywhere.

1.14. How do you verify messages in console mu?

The command below should work if you have at least one signed message.

Depending on your settings (auto-key-locate mechanisms, auto-key-import, and auto-key-retrieve), it will either verify the signature as true or fake, or complain that it has no key.

mu verify --verbose "$(mu find -f l flag:signed   | tail -n 1)"

1.15. What are (auto-key-locate mechanisms, auto-key-import, and auto-key-retrieve)?

This is very confusing.

So, locate is for encryption, and on sending.

I suggest refraining from encryption until you are fully comfortable with signing, because you risk permanently losing your messages.

If you are fine with using some centralised way to find your friend’s pubkey, you can set that locate to some method, just make sure that you understand what it does.

import is for the cases when a key is attached to the message (yes, you can do that, see --include-key-block).

retrieve is for the cases when the key is not attached to the message (yes, you can do that). Then gnupg will look at the server for the available key.

1.16. How do I verify messages in mu4e?

(setf mm-verify-option 'always)
(cl-pushnew "multipart/signed" gnus-buttonized-mime-types)

For details, see gnus#Security

mm is the gnus message viewer, reused by mu4e

1.17. mm-verify-option is always unsure about the signature

Because you don’t have a key?

You may want to set gnupg to fetch the key automatically (see above) 1.15 .

You may also want to import a key manually. I have not yet found a way to do it with mm/gnus (probably there is a way), but you can use an improvised function.

(defun mu4e-view-snarf-pgp-key (&optional msg)
  "Snarf the pgp key for the specified message."
  (interactive)
  (let* ((msg (or msg (mu4e-message-at-point)))
          (path (mu4e-message-field msg :path))
          (cmd (format "%s verify --verbose %s"
                 mu4e-mu-binary
                 (shell-quote-argument path)))
          (output (shell-command-to-string cmd)))
    (message "mu4e-view-snarf-pgp-key" ":msg=" msg ":path=" path ":cmd=" cmd ":output=" output)
    (let ((case-fold-search nil)
          (index 0))
      (while (string-match "finger-print[[:space:]]*: \\([A-F0-9]+\\)" output index)
        (let* ((cmd (format "%s --recv %s"
                            epg-gpg-program (match-string 1 output)))
               (output (shell-command-to-string cmd)))
          (setf index (match-end 0))
          (message output))))))

1.18. What does verification actually tell you? If verification fails, what does it mean?

(Malory=active, Eve=passive/evesdropping)

If an evil Malory steals your Friend’s key Gmail password, but not key password, they won’t send you a broken signature, they will just send you an unsigned message.

Gmail works over TLS, so an evil Eve won’t be able to MITM with the message over Friend-Gmail link without the Friend noticing.

If the Friend is stupid, they will neglect the TLS warning, and Malory may mangle the message, and the signature will be broken.

Gmail might deliver your Friend’s message over unsafe link, or Malory might have cracked Gmail, and the signature will be broken.

You are probably also not an idiot, and only connect to your mail server over TLS, so Eve cannot do anything, and you are also not an idiot and care about TLS warnings, so only if Malory has hacked LetsEncrypt or your server, the signature will be broken.

So really the only place where the signature can be realistically broken without TLS being broken is between mail servers.

This means that all of your email is probably garbage, and you need to call your friend and clarify what is going on, ideally via several means. (The best way is probably SIP operators from neutral countries, small enough so that nobody cares about them.)

1.19. How do I sign messages by default in mu?

You don’t, as mu itself does not send messages.

1.20. How do I sign messages by default in mu4e?

:hook
(mu4e-compose-mode . (lambda () (mml-secure-sign)))
:config
(setf mm-sign-option 'guided)

This will add a marker which will tell mm/mml/gnus to sign automatically. Delete it if you do not actually want to sign the message.

If you do want to sign the message, try sending it, and mm will ask you for the key.

For me this signs with pgp/mime, and I am fine with that.

1.21. How do I use Gnupg with Gmail?

There is (2024) an extension called “Mailvelope” (https://www.mailvelope.com/). It is open-source, up to the GMail api key.

They claim that their key management is fully local (unless you choose to upload your pubkeys).

It can connect to gnupg, although this requires some gymnastics.

  1. https://github.com/mailvelope/mailvelope/wiki/Mailvelope-GnuPG-integration
  2. https://github.com/mailvelope/mailvelope/wiki/Creating-the-app-manifest-file-on-macOS-and-Linux
  3. Mailvelope (min. version 5.0) is installed in the browser
  4. GnuPG (min. version 2.4.0) is installed on the system
  5. gpgme (min. version 1.19.0) is installed on the system

1.22. How do I use Gnupg with Gmail Android?

You cannot use it with a native Gmail app, well, unless you are some kind of Android disassembly ninja.

There are two methods, both bad.

1.22.1. K-9Mail/Fairemail + OpenKeychain + synchronise keys

This is not too bad if you can use K-9 Mail with Gmail.

Creates nice pgp/mime messages and encrypts attachments, but works over IMAP, so your battery will suffer, and all the nice features of Gmail labels will be lost. You will also be able to verify signatures.

1.22.2. Sign/Encrypt with OpenKeychain and paste

Annoying to copy/paste, for both writing and reading. Does not encrypt/sign attachments unless you are a base64 ninja.

But keeps the original Gmail app and all its niceties.

1.23. How do I encrypt my password file?

gpg2 --sign --encrypt --recipient <my-key-id> ~/passwords.txt

This will create ~/passwords.txt.gpg

There are different pinentries, you probably want to rebuild pinentry with –enable-inside-emacs, but if you are running a GUI, and a window appears, don’t be surprised.

There are pinentries in gtk2, gtk3, and such.

1.24. What if I do not use --sign? I can decrypt a file means that it is fine, right?

No! Or, rather, it only guarantees that Malory has not seen your passwords. But Malory may have replaced the file, and you would still see it as encrypted, and be able to decrypt it.

1.25. How do I decrypt my password file?

Emacs will decrypt it automatically, potentially asking you for a passphrase with one of the pinentry.

If you need to use a password from that file in some other software, encrypt that password alone, and use PassCmd, for example:

PassCmd "gpg2 --quiet --for-your-eyes-only --no-tty --decrypt ~/.password-store/mbsync/gmail.gpg"

Same will work in dumb console.

1.26. How to import data from old gnupg installations?

From those which have no pubring.gpg (new ones):

gpg2 --keyring path/to/pubring.kbx --export  | gpg2 --import

From older ones, which have pubring.gpg:

gpg2 --import path/to/pubring.gpg

1.27. How do I find someone’s key to encrypt to or check the senders signature?

Okay, a keyserver is essentially a phonebook, what in computing is called a “directory”. The problem with a keyserver is that everyone can upload all kind of crap there, and the Web of Trust is not very reliable, so you want a more robust way of finding signatures.

I will use bernhard.reiter@intevation.de, because it is used in a lot of examples on the Internet.

1.27.1. General method: --locate-keys

This will query not just keyservers, but other methods too. nodefault there is important, replace wkd with another method of your preference.

gpg2 --auto-key-locate clear,nodefault,wkd  --locate-keys bernhard.reiter@intevation.de 2>&1
gpg: key 0x2B7BA3BF9BC3A554: "Bernhard Reiter <bernhard@intevation.de>" not changed
gpg: Total number processed: 1
gpg:              unchanged: 1
pub   rsa3072/0x2B7BA3BF9BC3A554 2020-06-11 [SC] [expires: 2030-06-09]
      Key fingerprint = BDD9 57F9 C4FE 0FDC 583D  CD6D 2B7B A3BF 9BC3 A554
      Keygrip = 3B4CC1377231CE855D26B45CFF672B9331D11F7B
uid                   [ unknown] Bernhard Reiter <bernhard@intevation.de>
uid                   [ unknown] Bernhard Reiter <bernhard@fsfe.org>
uid                   [ unknown] Bernhard E. Reiter <bernhard.reiter@intevation.de>
sub   rsa3072/0x5B7528174D2908F8 2022-06-01 [E] [expires: 2024-06-12]
      Key fingerprint = 39B1 E207 2EB8 8FB6 4DAC  4F5C 5B75 2817 4D29 08F8
      Keygrip = 3CC1BF56D4F7EBC76285DE6291077B14E960E335

1.27.2. Method 1 (HTTPS): WKD

If you put a key on an HTTPS webserver, you can be sure that it is as trustworthy as LetsEncrypt. Let us check this:

/usr/libexec/gpg-wks-client --verbose --check 'bernhard.reiter@intevation.de' 2>&1
gpg-wks-client: public key for 'bernhard.reiter@intevation.de' found via WKD
gpg-wks-client: gpg2: gpg: Total number processed: 1
gpg-wks-client: fingerprint: BDD957F9C4FE0FDC583DCD6D2B7BA3BF9BC3A554
gpg-wks-client:     user-id: Bernhard Reiter <bernhard@intevation.de>
gpg-wks-client:     created: Thu 11 Jun 2020 16:24:59 CST
gpg-wks-client:   addr-spec: bernhard@intevation.de
gpg-wks-client:     user-id: Bernhard Reiter <bernhard@fsfe.org>
gpg-wks-client:     created: Thu 11 Jun 2020 16:04:48 CST
gpg-wks-client:   addr-spec: bernhard@fsfe.org
gpg-wks-client:     user-id: Bernhard E. Reiter <bernhard.reiter@intevation.de>
gpg-wks-client:     created: Thu 11 Jun 2020 16:03:07 CST
gpg-wks-client:   addr-spec: bernhard.reiter@intevation.de

You can put your key on your own web server, so only people who know your server will be able to query your keys. GMail obviously won’t put your keys on their WKD, because they don’t like in-band security.

How to publish it?

  1. https://www.uriports.com/blog/setting-up-openpgp-web-key-directory/
  2. https://brandonrozek.com/blog/decentralized-pgp-keys-wkd/

Get the tricky url part:

gpg2 --with-wkd-hash -k bernhard.reiter@intevation.de | tail -n 3
gpg2 --with-wkd-hash -k bernhard.reiter@intevation.de > hacabazoakmnagxwmkjerb9yehuwehbm
cp /var/www/htdoc/intevation.de/.well-known/openpgpkey/policy/hu/hacabazoakmnagxwmkjerb9yehuwehbm

1.27.3. Method 2 (DNS): CERT, PKA, DANE, IPGP

CERT and PKA are two kinds of DNS records used for placing a key there.

This is like WKD, only you put the pubkey on DNS, not on HTTPS.

The nice part about it is that:

  1. You need no HTTPS server.
  2. You can use these methods when you don’t control the mail server itself. I.e. it works with GMail with a personal domain name.

The bad part about it is that DNS is insecure. It is possible to sign DNS records with DNSSEC, but the DNSSEC does not encrypt the payload, only signs, and is very often misconfigured. And if you use DNS-over-HTTPS or DNS-over-TLS, you still end up trusting LetsEncrypt.

If you are interested, see here: https://www.gushi.org/make-dns-cert/howto.html

Practically speaking, since most people do not have their own email server, this is not really useful.

1.29. How does that Subkey machinery work in practice?

Okay, so idea is the following.

Firstly, gpg really expects you to share your encryption key among your devices. You can, in principle, define different IDs for different devices, and encrypt for them independently, but it is not convenient at all.

So, if you need to revoke your encryption key, you are to revoke it everywhere, and update all your devices.

What subkeys are about, is about signature keys being fairly independent. This is how you do it:

key_id='Test (Test 001) <invalid>'
gpg2 --quick-generate-key "$key_id" "ed25519/cert+cv25519/encr"
cfpr=$(gpg2 --list-secret-keys "$key_id" | grep 'Key fingerprint' | head -n 1 | cut -d '=' -f 2 | cut -c 2-)
gpg2 --quick-add-key "$cfpr" cv25519 encr 1y
efpr=$(gpg2 --list-secret-keys "$key_id" | grep 'Key fingerprint' | tail -n 1 | cut -d '=' -f 2 | cut -c 2-)
gpg2 --quick-add-key "$cfpr" ed25519 sign 1y
sfpr_laptop=$(gpg2 --list-secret-keys "$key_id" | grep 'Key fingerprint' | tail -n 1 | cut -d '=' -f 2 | cut -c 2-)
gpg2 --quick-add-key "$cfpr" ed25519 sign 1y
sfpr_mailvelope=$(gpg2 --list-secret-keys "$key_id" | grep 'Key fingerprint' | tail -n 1 | cut -d '=' -f 2 | cut -c 2-)
gpg2 --quick-add-key "$cfpr" ed25519 sign 1y
sfpr_phone=$(gpg2 --list-secret-keys "$key_id" | grep 'Key fingerprint' | tail -n 1 | cut -d '=' -f 2 | cut -c 2-)
mkdir ~/.gnupg/secrtificate-backup/
gpg2 --output ~/.gnupg/secrtificate-backup/"$(date --iso-local).${key_id}.gpg.asc" --armor --export-secret-keys "$key_id"
mkdir ~/.gnupg/secrtificate-backup/"$(date --iso-local).${key_id}.gpg.asc"
gpg2 --delete-secret-keys "$key_id" # yes to first, and no to the rest
gpg2 --armor --output ~/.gnupg/secrtificate-backup/"$(date --iso-local).${key_id}.gpg.asc"/ec-2024-laptop.gpg.asc --export-secret-subkeys "${efpr}!" "${sfpr_laptop}!"
gpg2 --armor --output ~/.gnupg/secrtificate-backup/"$(date --iso-local).${key_id}.gpg.asc"/ec-2024-mailvelope.gpg.asc --export-secret-subkeys "${efpr}!" "${sfpr_mailvelope}!"
gpg2 --armor --output ~/.gnupg/secrtificate-backup/"$(date --iso-local).${key_id}.gpg.asc"/ec-2024-phone.gpg.asc --export-secret-subkeys "${efpr}!" "${sfpr_phone}!"
gpg2 --delete-secret-keys "$key_id" # yes to all
gpg2 --import ~/.gnupg/secrtificate-backup/"$(date --iso-local).${key_id}.gpg.asc"/ec-2024-laptop.gpg.asc

Now you have your master key backed up, and one sign+encrypt pair on your laptop. Import the mailvelope and phone subcerts into mailvelope and phone (openkeychain) respectively.

You can backup the master key and delete file with it.

2. Open Questions

2.1. TODO Is it better to synchronise keys over different devices, or have different ones?

You most probably do not want to synchronise the master key.

If you only care about signing, you may have different keys everywhere, and if a device is stolen, revoke the key by a master key.

But for encryption… I don’t know.

Probably, you need to synchronise the key.

3. References

  1. Michael W Lucas “PGP & GnuPG”
  2. Neal H. Walfield “An Advanced Introduction to GnuPG”
  3. https://wiki.debian.org/Subkeys
  4. https://www.gushi.org/make-dns-cert/howto.html