Subject: bug#23915: 24.5; editing *.gpg file through emacs presents an unclean (and unsafe) round trip

From: Daniel Kahn Gillmor <[email-masked]>
Date: Thu, 07 Jul 2016 19:56:24 -0400

If i edit a file whose name matches the glob *.gpg in emacs, gpg
decrypts it (i'm prompted by the gpg-agent for my passphrase) and i am
presented with the cleartext version of the file to edit.

when i save, it re-encrypts the file.

This is a sensible workflow in general, but there are several strange
properties that make it not a clean round-trip:

 a) the original file may or may not have been ascii-armored.  The saved
    file is always raw (not ascii-armored).

 b) the original file may have had an OpenPGP signature inside the
    encryption.  the saved file never has a signature.

 c) the original file may have been encrypted to multiple recipients (in
    OpenPGP terms, there are multiple PKESKs, one for each recipient).
    The saved file will be encrypted to every recipient whose public key
    (as identified by the key ID in the PKESKs) are present in the
    editor's keyring.  (if the file also was passphrase-encrypted, the
    SKESK is dropped)

I think the right approach to resolve these would be:

 A) remember whether the file was ASCII-armored initially or not, and
    use that value when saving.

 B) If an OpenPGP signature was present in the document when opening,
    warn (with e.g. *Messages* ? prompting for confirmation?) when
    trying to save that the resulting file will destroy the signature.

 C) if more than a single PKESK or SKESK is present when opening, warn
    (again, with *Messages* ? prompting for confirmation?) when trying
    to save that all other PKESKs or SKESKs will be dropped for the
    re-saved file.

The resolution (C) is unsatisfying, but there is no safe/complete answer
given the OpenPGP data structure:

On the one hand, we can't guarantee replication of the full set of
recipients PKESKs, because the editor may not have the associated public
keys in her keyring.

On the other hand, the PKESKs are not cryptographically-authenticated at
all.  So if we re-encrypt to all, an attack presents itself:

 * Mallory knows that Alice and Bob are planning something;

 * Mallory knows the secret key according to some encryption-capable
   public key X in Alice's public keyring;

 * Mallory intercepts an encrypted document D sent from Bob to Alice.

 * Mallory prepends D with a phony PKESK with the key ID of X, creating
   new document D'

 * Mallory replaces D with D' in Bob's message to Alice.

 * Alice edits the document, creating new document E, and sends E back
   to Bob.

 * Mallory intercepts E, decrypts it with X, strips the extra
   PKESK creating E', and forwards E' on to Bob.

Hope this makes sense!  Happy to clarify if you have any questions.

     --dkg

In GNU Emacs 24.5.1 (x86_64-pc-linux-gnu, GTK+ Version 3.18.9)
 of 2016-04-08 on binet, modified by Debian
Windowing system distributor `The X.Org Foundation', version 11.0.11803000
System Description:	Debian GNU/Linux testing/unstable

Configured using:
 `configure --build x86_64-linux-gnu --prefix=/usr
 --sharedstatedir=/var/lib --libexecdir=/usr/lib
 --localstatedir=/var/lib --infodir=/usr/share/info
 --mandir=/usr/share/man --with-pop=yes
 --enable-locallisppath=/etc/emacs24:/etc/emacs:/usr/local/share/emacs/24.5/site-lisp:/usr/local/share/emacs/site-lisp:/usr/share/emacs/24.5/site-lisp:/usr/share/emacs/site-lisp
 --build x86_64-linux-gnu --prefix=/usr --sharedstatedir=/var/lib
 --libexecdir=/usr/lib --localstatedir=/var/lib
 --infodir=/usr/share/info --mandir=/usr/share/man --with-pop=yes
 --enable-locallisppath=/etc/emacs24:/etc/emacs:/usr/local/share/emacs/24.5/site-lisp:/usr/local/share/emacs/site-lisp:/usr/share/emacs/24.5/site-lisp:/usr/share/emacs/site-lisp
 --with-x=yes --with-x-toolkit=gtk3 --with-toolkit-scroll-bars
 'CFLAGS=-g -O2 -fstack-protector-strong -Wformat
 -Werror=format-security -Wall' 'CPPFLAGS=-Wdate-time
 -D_FORTIFY_SOURCE=2' LDFLAGS=-Wl,-z,relro'

Important settings:
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Fundamental

Minor modes in effect:
  diff-auto-refine-mode: t
  savehist-mode: t
  display-time-mode: t
  tooltip-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  line-number-mode: t
  transient-mark-mode: t

Recent messages:
Loading /etc/emacs/site-start.d/51debian-el.el (source)...done
No desktop file.
For information about GNU Emacs and the GNU system, type C-h C-a.
Decrypting /home/dkg/tmp/foo.gpg...done
End of buffer
Saving file /home/dkg/tmp/foo.gpg...
Buffer foo.gpg does not end in newline.  Add one? (y or n) y
Untrusted key XXXXXXXXXXXXXXXX REDACTED_NAME <REDACTED_EMAIL_ADDRESS>.  Use anyway? (y or n) y
Encrypting /home/dkg/tmp/foo.gpg... [2 times]
Wrote /home/dkg/tmp/foo.gpg [2 times]

Load-path shadows:
/usr/share/emacs24/site-lisp/cmake-data/cmake-mode hides /usr/share/emacs/site-lisp/cmake-mode
/usr/share/emacs/24.5/site-lisp/debian-startup hides /usr/share/emacs/site-lisp/debian-startup
/usr/share/emacs/site-lisp/rst hides /usr/share/emacs/24.5/lisp/textmodes/rst

Features:
(shadow sort gnus-util mail-extr emacsbug epa-file epa derived epg
package epg-config notmuch hl-line notmuch-maildir-fcc notmuch-hello
wid-edit notmuch-tree notmuch-show notmuch-message notmuch-print
notmuch-crypto notmuch-mua notmuch-address notmuch-company
notmuch-parser notmuch-wash diff-mode coolj notmuch-query goto-addr
thingatpt icalendar diary-lib diary-loaddefs cal-menu calendar
cal-loaddefs notmuch-tag crm notmuch-lib advice notmuch-version cl gv
message sendmail format-spec rfc822 mailabbrev mail-utils gmm-utils
mailheader mm-view mml-smime smime password-cache dig mailcap mml
easymenu mml-sec mm-decode mm-bodies mm-encode mail-parse rfc2231
rfc2047 rfc2045 ietf-drums mm-util help-fns mail-prsvr savehist time
desktop frameset cl-loaddefs cl-lib debian-el debian-el-loaddefs
haskell-mode-autoloads emacs-goodies-el emacs-goodies-custom
emacs-goodies-loaddefs easy-mmode dpkg-dev-el dpkg-dev-el-loaddefs
bbdb-autoloads time-date tooltip electric uniquify ediff-hook vc-hooks
lisp-float-type mwheel x-win x-dnd tool-bar dnd fontset image regexp-opt
fringe tabulated-list newcomment lisp-mode prog-mode register page
menu-bar rfn-eshadow timer select scroll-bar mouse jit-lock font-lock
syntax facemenu font-core frame cham georgian utf-8-lang misc-lang
vietnamese tibetan thai tai-viet lao korean japanese hebrew greek
romanian slovak czech european ethiopic indian cyrillic chinese
case-table epa-hook jka-cmpr-hook help simple abbrev minibuffer nadvice
loaddefs button faces cus-face macroexp files text-properties overlay
sha1 md5 base64 format env code-pages mule custom widget
hashtable-print-readable backquote make-network-process dbusbind
gfilenotify dynamic-setting system-font-setting font-render-setting
move-toolbar gtk x-toolkit x multi-tty emacs)

Memory information:
((conses 16 113554 6541)
 (symbols 48 22919 0)
 (miscs 40 43 86)
 (strings 32 25862 4332)
 (string-bytes 1 791709)
 (vectors 16 14367)
 (vector-slots 8 431934 2841)
 (floats 8 79 326)
 (intervals 56 269 9)
 (buffers 960 12)
 (heap 1024 37164 997))