unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
From: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
To: Notmuch Mail <notmuch@notmuchmail.org>
Subject: [PATCH v2 18/21] crypto: actually stash session keys when decrypt=true
Date: Thu, 30 Nov 2017 03:59:43 -0500	[thread overview]
Message-ID: <20171130085946.11332-19-dkg@fifthhorseman.net> (raw)
In-Reply-To: <20171130085946.11332-1-dkg@fifthhorseman.net>

If you're going to store the cleartext index of an encrypted message,
in most situations you might just as well store the session key.
Doing this storage has efficiency and recoverability advantages.

Combined with a schedule of regular OpenPGP subkey rotation and
destruction, this can also offer security benefits, like "deletable
e-mail", which is the store-and-forward analog to "forward secrecy".

But wait, i hear you saying, i have a special need to store cleartext
indexes but it's really bad for me to store session keys!  Maybe
(let's imagine) i get lots of e-mails with incriminating photos
attached, and i want to be able to search for them by the text in the
e-mail, but i don't want someone with access to the index to be
actually able to see the photos themselves.

Fret not, the next patch in this series will support your wacky
uncommon use case.
---
 doc/man1/notmuch-config.rst     | 10 ++++++----
 doc/man1/notmuch-insert.rst     | 13 +++++++------
 doc/man1/notmuch-new.rst        | 21 +++++++++++----------
 doc/man1/notmuch-reindex.rst    | 10 +++++-----
 doc/man7/notmuch-properties.rst |  4 ++++
 lib/index.cc                    | 18 +++++++++++++++++-
 test/T357-index-decryption.sh   | 18 +++++++++++++++++-
 util/crypto.c                   | 16 +++++++++++++++-
 8 files changed, 82 insertions(+), 28 deletions(-)

diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 1a9e08a3..dabf269f 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -143,10 +143,12 @@ The available configuration items are described below.
         **[STORED IN DATABASE]**
         When indexing an encrypted e-mail message, if this variable is
         set to ``true``, notmuch will try to decrypt the message and
-        index the cleartext.  If ``auto``, it will try to index the
-        cleartext if a stashed session key is already known for the message,
-        but will not try to access your secret keys.  Use ``false`` to
-        avoid decrypting even when a session key is already known.
+        index the cleartext, stashing a copy of any discovered session
+        keys for the message.  If ``auto``, it will try to index the
+        cleartext if a stashed session key is already known for the message
+        (e.g. from a previous copy), but will not try to access your
+        secret keys.  Use ``false`` to avoid decrypting even when a
+        stashed session key is already present.
 
         Be aware that the notmuch index is likely sufficient to
         reconstruct the cleartext of the message itself, so please
diff --git a/doc/man1/notmuch-insert.rst b/doc/man1/notmuch-insert.rst
index b22be863..214f261b 100644
--- a/doc/man1/notmuch-insert.rst
+++ b/doc/man1/notmuch-insert.rst
@@ -54,12 +54,13 @@ Supported options for **insert** include
     ``--decrypt=(true|auto|false)``
 
         If ``true`` and the message is encrypted, try to decrypt the
-        message while indexing.  If ``auto``, and notmuch already
-        knows about a session key for the message, it will try
-        decrypting using that session key but will not try to access
-        the user's secret keys.  If decryption is successful, index
-        the cleartext itself.  Either way, the message is always
-        stored to disk in its original form (ciphertext).
+        message while indexing, storing any session keys discovered.
+        If ``auto``, and notmuch already knows about a session key for
+        the message, it will try decrypting using that session key but
+        will not try to access the user's secret keys.  If decryption
+        is successful, index the cleartext itself.  Either way, the
+        message is always stored to disk in its original form
+        (ciphertext).
 
         Be aware that the index is likely sufficient to reconstruct
         the cleartext of the message itself, so please ensure that the
diff --git a/doc/man1/notmuch-new.rst b/doc/man1/notmuch-new.rst
index 71df31d7..27676a19 100644
--- a/doc/man1/notmuch-new.rst
+++ b/doc/man1/notmuch-new.rst
@@ -46,16 +46,17 @@ Supported options for **new** include
     ``--decrypt=(true|auto|false)``
 
         If ``true``, when encountering an encrypted message, try to
-        decrypt it while indexing.  If decryption is successful, index
-        the cleartext itself.  If ``auto``, try to use any session key
-        already known to belong to this message, but do not attempt to
-        use the user's secret keys.
-
-        Be aware that the index is likely
-        sufficient to reconstruct the cleartext of the message itself,
-        so please ensure that the notmuch message index is adequately
-        protected.  DO NOT USE ``--decrypt=true`` without
-        considering the security of your index.
+        decrypt it while indexing, and store any discovered session
+        keys.  If ``auto``, try to use any session key already known
+        to belong to this message, but do not attempt to use the
+        user's secret keys.  If decryption is successful, index the
+        cleartext of the message.
+
+        Be aware that the index is likely sufficient (and the session
+        key is certainly sufficient) to reconstruct the cleartext of
+        the message itself, so please ensure that the notmuch message
+        index is adequately protected.  DO NOT USE ``--decrypt=true``
+        without considering the security of your index.
 
         See also ``index.decrypt`` in **notmuch-config(1)**.
 
diff --git a/doc/man1/notmuch-reindex.rst b/doc/man1/notmuch-reindex.rst
index e8174f39..47790871 100644
--- a/doc/man1/notmuch-reindex.rst
+++ b/doc/man1/notmuch-reindex.rst
@@ -24,11 +24,11 @@ Supported options for **reindex** include
     ``--decrypt=(true|auto|false)``
 
         If ``true``, when encountering an encrypted message, try to
-        decrypt it while reindexing.  If ``auto``, and notmuch already
-        knows about a session key for the message, it will try
-        decrypting using that session key but will not try to access
-        the user's secret keys.  If decryption is successful, index
-        the cleartext itself.
+        decrypt it while reindexing, storing any session keys
+        discovered.  If ``auto``, and notmuch already knows about a
+        session key for the message, it will try decrypting using that
+        session key but will not try to access the user's secret keys.
+        If decryption is successful, index the cleartext itself.
 
         If ``false``, notmuch reindex will also delete any stashed
         session keys for all messages matching the search terms.
diff --git a/doc/man7/notmuch-properties.rst b/doc/man7/notmuch-properties.rst
index 1bb7b1ca..b9ccc608 100644
--- a/doc/man7/notmuch-properties.rst
+++ b/doc/man7/notmuch-properties.rst
@@ -98,6 +98,10 @@ of its normal activity.
     message.  This includes attachments, cryptographic signatures, and
     other material that cannot be reconstructed from the index alone.
 
+    See ``index.decrypt`` in **notmuch-config(1)** for more
+    details about how to set notmuch's policy on when to store session
+    keys.
+
     The session key should be in the ASCII text form produced by
     GnuPG.  For OpenPGP, that consists of a decimal representation of
     the hash algorithm used (identified by number from RFC 4880,
diff --git a/lib/index.cc b/lib/index.cc
index 3914012a..0ad683fa 100644
--- a/lib/index.cc
+++ b/lib/index.cc
@@ -549,11 +549,15 @@ _index_encrypted_mime_part (notmuch_message_t *message,
     }
 #endif
     bool attempted = false;
+    GMimeDecryptResult *decrypt_result = NULL;
+    bool get_sk = (HAVE_GMIME_SESSION_KEYS && notmuch_indexopts_get_decrypt_policy (indexopts) == NOTMUCH_DECRYPT_TRUE);
     clear = _notmuch_crypto_decrypt (&attempted, notmuch_indexopts_get_decrypt_policy (indexopts),
-				     message, crypto_ctx, encrypted_data, NULL, &err);
+				     message, crypto_ctx, encrypted_data, get_sk ? &decrypt_result : NULL, &err);
     if (!attempted)
 	return;
     if (err || !clear) {
+	if (decrypt_result)
+	    g_object_unref (decrypt_result);
 	if (err) {
 	    _notmuch_database_log (notmuch, "Failed to decrypt during indexing. (%d:%d) [%s]\n",
 				   err->domain, err->code, err->message);
@@ -568,6 +572,18 @@ _index_encrypted_mime_part (notmuch_message_t *message,
 					  "property (%d)\n", status);
 	return;
     }
+    if (decrypt_result) {
+#if HAVE_GMIME_SESSION_KEYS
+	if (get_sk) {
+	    status = notmuch_message_add_property (message, "session-key",
+						   g_mime_decrypt_result_get_session_key (decrypt_result));
+	    if (status)
+		_notmuch_database_log (notmuch, "failed to add session-key "
+				       "property (%d)\n", status);
+	}
+#endif
+	g_object_unref (decrypt_result);
+    }
     _index_mime_part (message, indexopts, clear);
     g_object_unref (clear);
 
diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
index 64317c64..a3511a82 100755
--- a/test/T357-index-decryption.sh
+++ b/test/T357-index-decryption.sh
@@ -48,6 +48,14 @@ test_expect_equal \
     "$output" \
     "$expected"
 
+test_begin_subtest "show the message body of the encrypted message"
+notmuch dump wumpus
+output=$(notmuch show wumpus | awk '/^\014part}/{ f=0 }; { if (f) { print $0 } } /^\014part{ ID: 3/{ f=1 }')
+expected='This is a test encrypted message with a wumpus.'
+test_expect_equal \
+    "$output" \
+    "$expected"
+
 
 test_begin_subtest "message should go away after deletion"
 # cache the message in an env var and remove it:
@@ -129,10 +137,18 @@ test_expect_equal \
     "$output" \
     "$expected"
 
+# try a simple reindexx
+test_begin_subtest 'reindex in auto mode'
+test_expect_success 'notmuch reindex tag:encrypted and property:index.decryption=success'
+test_begin_subtest "reindexed encrypted messages, should not have changed"
+output=$(notmuch search wumpus)
+test_expect_equal \
+    "$output" \
+    "$expected"
 
 # try to remove cleartext indexing
 test_begin_subtest 'reindex without cleartext'
-test_expect_success 'notmuch reindex tag:encrypted and property:index.decryption=success'
+test_expect_success 'notmuch reindex --decrypt=false tag:encrypted and property:index.decryption=success'
 test_begin_subtest "reindexed encrypted messages, without cleartext"
 output=$(notmuch search wumpus)
 expected=''
diff --git a/util/crypto.c b/util/crypto.c
index 338f1d5d..066dea6e 100644
--- a/util/crypto.c
+++ b/util/crypto.c
@@ -197,10 +197,24 @@ _notmuch_crypto_decrypt (bool *attempted,
     if (attempted)
 	*attempted = true;
 #if (GMIME_MAJOR_VERSION < 3)
+#if HAVE_GMIME_SESSION_KEYS
+    gboolean oldgetsk = g_mime_crypto_context_get_retrieve_session_key (crypto_ctx);
+    gboolean newgetsk = (decrypt_result);
+    if (newgetsk != oldgetsk)
+	/* This could return an error, but we can't do anything about it, so ignore it */
+	g_mime_crypto_context_set_retrieve_session_key (crypto_ctx, newgetsk, NULL);
+#endif
     ret = g_mime_multipart_encrypted_decrypt(part, crypto_ctx,
 					     decrypt_result, err);
+#if HAVE_GMIME_SESSION_KEYS
+    if (newgetsk != oldgetsk)
+	g_mime_crypto_context_set_retrieve_session_key (crypto_ctx, oldgetsk, NULL);
+#endif
 #else
-    ret = g_mime_multipart_encrypted_decrypt(part, GMIME_DECRYPT_NONE, NULL,
+    GMimeDecryptFlags flags = GMIME_DECRYPT_NONE;
+    if (decrypt_result)
+	flags |= GMIME_DECRYPT_EXPORT_SESSION_KEY;
+    ret = g_mime_multipart_encrypted_decrypt(part, flags, NULL,
 					     decrypt_result, err);
 #endif
     return ret;
-- 
2.15.0

  parent reply	other threads:[~2017-11-30  9:00 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-11-30  8:59 session keys, version 2 Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 01/21] mime-node: handle decrypt_result more safely Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 02/21] crypto: add _notmuch_crypto_decrypt wrapper function Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 03/21] configure: session key handling in gmime maps to built_with("session_key") Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 04/21] crypto: use stashed session-key properties for decryption, if available Daniel Kahn Gillmor
2017-11-30 15:06   ` Daniel Kahn Gillmor
2017-12-05  1:47     ` David Bremner
2017-11-30  8:59 ` [PATCH v2 05/21] test/corpora: add an encrypted message for index decryption tests Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 06/21] crypto: Test restore of cleartext index from stashed session keys Daniel Kahn Gillmor
2017-12-05  1:59   ` David Bremner
2017-12-06  2:12     ` Daniel Kahn Gillmor
2017-12-07  8:20     ` Jameson Graef Rollins
2017-12-08  7:03       ` Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 07/21] indexing: Change from try_decrypt to decrypt Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 08/21] indexopts: change _try_decrypt to _decrypt_policy Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 09/21] lib: convert notmuch decryption policy to an enum Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 10/21] crypto: new decryption policy "auto" Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 11/21] cli/reply: use decryption policy "auto" by default Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 12/21] cli/show: " Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 13/21] cli/show, reply: document use of stashed session keys in notmuch-properties Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 14/21] cli/new, insert, reindex: update documentation for --decrypt=auto Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 15/21] crypto: record whether an actual decryption attempt happened Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 16/21] cli/new, insert, reindex: change index.decrypt to "auto" by default Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 17/21] cli/reindex: destroy stashed session keys when --decrypt=false Daniel Kahn Gillmor
2017-11-30  8:59 ` Daniel Kahn Gillmor [this message]
2017-11-30  8:59 ` [PATCH v2 19/21] crypto: add --decrypt=nostash to avoid stashing session keys Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 20/21] docs: clean up documentation about decryption policies Daniel Kahn Gillmor
2017-11-30  8:59 ` [PATCH v2 21/21] python: add decrypt_policy argument to Database.index_file() Daniel Kahn Gillmor
2017-11-30 15:05   ` Daniel Kahn Gillmor

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://notmuchmail.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20171130085946.11332-19-dkg@fifthhorseman.net \
    --to=dkg@fifthhorseman.net \
    --cc=notmuch@notmuchmail.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).