[gmime-devel] [PATCH v2 2/2] Support extraction of session keys during decryption.



Some message stores can be optimized by caching session keys of
encrypted messages, rather than incurring asymmetric crypto operations
on each subsequent decryption.

This patch allows gmime to perform extraction of session keys during
message decryption to support that use case.

See the discussion starting here for more detail on the API/design
choices:
https://mail.gnome.org/archives/gmime-devel-list/2016-July/msg00005.html
---
 gmime/gmime-crypto-context.c | 41 ++++++++++++++++++++++++++++++++++++
 gmime/gmime-crypto-context.h |  4 ++++
 gmime/gmime-gpg-context.c    | 50 +++++++++++++++++++++++++++++++++++++++++++-
 gmime/gmime-gpg-context.h    |  4 ++++
 tests/test-pgp.c             |  5 +++++
 tests/test-pgpmime.c         |  9 +++++++-
 6 files changed, 111 insertions(+), 2 deletions(-)

diff --git a/gmime/gmime-crypto-context.c b/gmime/gmime-crypto-context.c
index 5269440..90fa390 100644
--- a/gmime/gmime-crypto-context.c
+++ b/gmime/gmime-crypto-context.c
@@ -573,6 +573,7 @@ g_mime_decrypt_result_init (GMimeDecryptResult *result, GMimeDecryptResultClass
        result->mdc = GMIME_DIGEST_ALGO_DEFAULT;
        result->recipients = NULL;
        result->signatures = NULL;
+       result->session_key = NULL;
 }
 
 static void
@@ -586,6 +587,8 @@ g_mime_decrypt_result_finalize (GObject *object)
        if (result->signatures)
                g_object_unref (result->signatures);
        
+       g_free (result->session_key);
+       
        G_OBJECT_CLASS (result_parent_class)->finalize (object);
 }
 
@@ -755,3 +758,41 @@ g_mime_decryption_result_get_mdc (GMimeDecryptResult *result)
        
        return result->mdc;
 }
+
+
+/**
+ * g_mime_decrypt_result_set_session_key:
+ * @result: a #GMimeDecryptResult
+ * @session_key: a pointer to a null-terminated string representing the session key
+ *
+ * Set the session_key to be returned by this decryption result.
+ **/
+void
+g_mime_decrypt_result_set_session_key (GMimeDecryptResult *result, const char *session_key)
+{
+       g_return_if_fail (GMIME_IS_DECRYPT_RESULT (result));
+
+       g_free (result->session_key);
+       result->session_key = g_strdup(session_key);
+}
+
+
+/**
+ * g_mime_decrypt_result_get_session_key:
+ * @result: a #GMimeDecryptResult
+ *
+ * Get the session_key used for this decryption, if the underlying
+ * crypto context is capable of and (configured to) retrieve session
+ * keys during decryption.  See, for example,
+ * g_mime_gpg_context_set_retrieve_session_key().
+ *
+ * Returns: the session_key digest algorithm used, or NULL if no
+ * session key was requested or found.
+ **/
+const char*
+g_mime_decryption_result_get_session_key (GMimeDecryptResult *result)
+{
+       g_return_val_if_fail (GMIME_IS_DECRYPT_RESULT (result), GMIME_DIGEST_ALGO_DEFAULT);
+       
+       return result->session_key;
+}
diff --git a/gmime/gmime-crypto-context.h b/gmime/gmime-crypto-context.h
index cd38760..72573ed 100644
--- a/gmime/gmime-crypto-context.h
+++ b/gmime/gmime-crypto-context.h
@@ -206,6 +206,7 @@ struct _GMimeDecryptResult {
        GMimeSignatureList *signatures;
        GMimeCipherAlgo cipher;
        GMimeDigestAlgo mdc;
+       char *session_key;
 };
 
 struct _GMimeDecryptResultClass {
@@ -229,6 +230,9 @@ GMimeCipherAlgo g_mime_decrypt_result_get_cipher (GMimeDecryptResult *result);
 void g_mime_decrypt_result_set_mdc (GMimeDecryptResult *result, GMimeDigestAlgo mdc);
 GMimeDigestAlgo g_mime_decrypt_result_get_mdc (GMimeDecryptResult *result);
 
+void g_mime_decrypt_result_set_session_key (GMimeDecryptResult *result, const char *session_key);
+const char *g_mime_decrypt_result_get_session_key (GMimeDecryptResult *result);
+
 G_END_DECLS
 
 #endif /* __GMIME_CRYPTO_CONTEXT_H__ */
diff --git a/gmime/gmime-gpg-context.c b/gmime/gmime-gpg-context.c
index afc1d52..ac12c41 100644
--- a/gmime/gmime-gpg-context.c
+++ b/gmime/gmime-gpg-context.c
@@ -172,6 +172,7 @@ g_mime_gpg_context_init (GMimeGpgContext *ctx, GMimeGpgContextClass *klass)
        ctx->always_trust = FALSE;
        ctx->use_agent = FALSE;
        ctx->path = NULL;
+       ctx->retrieve_session_key = FALSE;
 }
 
 static void
@@ -311,6 +312,7 @@ struct _GpgCtx {
        GMimeStream *diagnostics;
        
        GMimeCertificateList *encrypted_to;  /* full list of encrypted-to recipients */
+       char *session_key;
        GMimeSignatureList *signatures;
        GMimeSignature *signature;
        
@@ -375,6 +377,7 @@ gpg_ctx_new (GMimeGpgContext *ctx)
        gpg->need_id = NULL;
        
        gpg->encrypted_to = NULL;
+       gpg->session_key = NULL;
        gpg->signatures = NULL;
        gpg->signature = NULL;
        
@@ -555,6 +558,8 @@ gpg_ctx_free (struct _GpgCtx *gpg)
        if (gpg->encrypted_to)
                g_object_unref (gpg->encrypted_to);
        
+       g_free (gpg->session_key);
+       
        if (gpg->signatures)
                g_object_unref (gpg->signatures);
        
@@ -691,6 +696,9 @@ gpg_ctx_get_argv (struct _GpgCtx *gpg, int status_fd, int secret_fd, char ***str
                if (gpg->use_agent)
                        g_ptr_array_add (args, "--use-agent");
                
+               if (gpg->ctx->retrieve_session_key)
+                       g_ptr_array_add (args, "--show-session-key");
+               
                g_ptr_array_add (args, "--decrypt");
                g_ptr_array_add (args, "--output");
                g_ptr_array_add (args, "-");
@@ -1326,6 +1334,8 @@ gpg_ctx_parse_status (struct _GpgCtx *gpg, GError **err)
                                /* nothing to do... we'll grab the MDC used in DECRYPTION_INFO */
                        } else if (!strncmp (status, "BADMDC", 6)) {
                                /* nothing to do, this will only be sent after DECRYPTION_FAILED */
+                       } else if (gpg->ctx->retrieve_session_key && !strncmp (status, "SESSION_KEY", 11)) {
+                               status = next_token (status, &gpg->session_key);
                        } else {
                                gpg_ctx_parse_signer_info (gpg, status);
                        }
@@ -2059,10 +2069,12 @@ gpg_decrypt (GMimeCryptoContext *context, GMimeStream *istream,
        result = g_mime_decrypt_result_new ();
        result->recipients = gpg->encrypted_to;
        result->signatures = gpg->signatures;
+       result->session_key = gpg->session_key;
        result->cipher = gpg->cipher;
        result->mdc = gpg->digest;
        gpg->encrypted_to = NULL;
        gpg->signatures = NULL;
+       gpg->session_key = NULL;
        
        gpg_ctx_free (gpg);
        
@@ -2305,7 +2317,7 @@ g_mime_gpg_context_get_use_agent (GMimeGpgContext *ctx)
 /**
  * g_mime_gpg_context_set_use_agent:
  * @ctx: a #GMimeGpgContext
- * @use_agent: always trust flag
+ * @use_agent: use agent flag
  *
  * Sets the @use_agent flag on the gpg context, which indicates that
  * GnuPG should attempt to use gpg-agent for credentials.
@@ -2317,3 +2329,39 @@ g_mime_gpg_context_set_use_agent (GMimeGpgContext *ctx, gboolean use_agent)
        
        ctx->use_agent = use_agent;
 }
+
+/**
+ * g_mime_gpg_context_get_retrieve_session_key:
+ * @ctx: a #GMimeGpgContext
+ *
+ * Gets the retrieve_session_key flag on the gpg context.
+ *
+ * Returns: the retrieve_session_key flag on the gpg context, which
+ * indicates that GnuPG should attempt to retrieve the session key for
+ * any encrypted message.
+ **/
+gboolean
+g_mime_gpg_context_get_retrieve_session_key (GMimeGpgContext *ctx)
+{
+       g_return_val_if_fail (GMIME_IS_GPG_CONTEXT (ctx), FALSE);
+       
+       return ctx->retrieve_session_key;
+}
+
+
+/**
+ * g_mime_gpg_context_set_retrieve_session_key:
+ * @ctx: a #GMimeGpgContext
+ * @retrieve_session_key: retrieve session key flag
+ *
+ * Sets the @retrieve_session_key flag on the gpg context, which
+ * indicates that GnuPG should attempt to retrieve the session key for
+ * any encrypted message.
+ **/
+void
+g_mime_gpg_context_set_retrieve_session_key (GMimeGpgContext *ctx, gboolean retrieve_session_key)
+{
+       g_return_if_fail (GMIME_IS_GPG_CONTEXT (ctx));
+       
+       ctx->retrieve_session_key = retrieve_session_key;
+}
diff --git a/gmime/gmime-gpg-context.h b/gmime/gmime-gpg-context.h
index a088e90..4f17fe0 100644
--- a/gmime/gmime-gpg-context.h
+++ b/gmime/gmime-gpg-context.h
@@ -51,6 +51,7 @@ struct _GMimeGpgContext {
        gboolean always_trust;
        gboolean use_agent;
        char *path;
+       gboolean retrieve_session_key;
 };
 
 struct _GMimeGpgContextClass {
@@ -73,6 +74,9 @@ void g_mime_gpg_context_set_always_trust (GMimeGpgContext *ctx, gboolean always_
 gboolean g_mime_gpg_context_get_use_agent (GMimeGpgContext *ctx);
 void g_mime_gpg_context_set_use_agent (GMimeGpgContext *ctx, gboolean use_agent);
 
+gboolean g_mime_gpg_context_get_retrieve_session_key (GMimeGpgContext *ctx);
+void g_mime_gpg_context_set_retrieve_session_key (GMimeGpgContext *ctx, gboolean retrieve_session_key);
+
 G_END_DECLS
 
 #endif /* __GMIME_GPG_CONTEXT_H__ */
diff --git a/tests/test-pgp.c b/tests/test-pgp.c
index 928f7f1..81a4b9d 100644
--- a/tests/test-pgp.c
+++ b/tests/test-pgp.c
@@ -163,6 +163,11 @@ test_decrypt (GMimeCryptoContext *ctx, gboolean sign, GMimeStream *cleartext, GM
                        ex = exception_new ("unexpected signature");
        }
        
+       /* Did not ask for session_key -- it should not be present.
+          We test asking for session_key over in test-pgpmime.c */
+       if (ex == NULL && result->session_key)
+               ex = exception_new ("got session_key when not requested");
+       
        g_object_unref (result);
        
        if (ex != NULL) {
diff --git a/tests/test-pgpmime.c b/tests/test-pgpmime.c
index cdb83e4..27e6b6b 100644
--- a/tests/test-pgpmime.c
+++ b/tests/test-pgpmime.c
@@ -351,9 +351,15 @@ test_multipart_encrypted (GMimeCryptoContext *ctx, gboolean sign)
                throw (ex);
        }
        
+       if (!result->session_key) {
+               ex = exception_new ("No session key returned!");
+               g_object_unref (cleartext);
+               throw (ex);
+       }
+       
        if (result->signatures)
                v(print_verify_results (result->signatures));
-       
+
        if (sign) {
                if (!result->signatures || get_sig_status (result->signatures) != GMIME_SIGNATURE_STATUS_GOOD)
                        ex = exception_new ("signature status expected to be GOOD");
@@ -436,6 +442,7 @@ int main (int argc, char *argv[])
        
        ctx = g_mime_gpg_context_new (request_passwd, NULL);
        g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) ctx, TRUE);
+       g_mime_gpg_context_set_retrieve_session_key ((GMimeGpgContext *) ctx, TRUE);
        
        testsuite_check ("GMimeGpgContext::import");
        try {
-- 
2.10.2



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]