[Pkg-gnupg-commit] [gpgme] 41/62: core: Enable extraction of session keys.

Daniel Kahn Gillmor dkg at fifthhorseman.net
Sat Nov 19 04:03:35 UTC 2016


This is an automated email from the git hooks/post-receive script.

dkg pushed a commit to branch experimental
in repository gpgme.

commit cad1210fb8a7402cb29e607f8f9680005314120d
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date:   Fri Nov 11 16:49:28 2016 +0900

    core: Enable extraction of session keys.
    
    * src/gpgme.c (gpgme_set_export_session_keys): New function.
    (gpgme_get_export_session_keys): New function.
    * src/gpgme.h.in (struct _gpgme_op_decrypt_result): Add session_key
    member.
    (gpgme_{set,get}_export_session_keys): Declare new functions.
    * src/libgpgme.vers, src/gpgme.def: Export new functions in shared
    object.
    * src/engine.h: (_gpgme_engine_op_decrypt) Add export_session_key
    parameter.
    (_gpgme_engine_op_decrypt_verify): Add export_session_key parameter.
    * src/engine-backend.h: (struct engine_ops): Change function
    pointer declarations to match.
    * src/context.h (struct gpgme_context): Add export_session_keys member.
    * src/decrypt.c (release_op_data): Free result.session_key.
    (_gpgme_decrypt_status_handler): Store a copy of the exported session
    key.
    (decrypt_start): Pass export_session_keys from the context.
    * src/decrypt-verify.c (decrypt_verify_start): Pass
    export_session_keys from context.
    * src/engine.c (_gpgme_engine_op_decrypt): Pass through
    export_session_key flag.
    (_gpgme_engine_op_decrypt_verify): Pass through export_session_key
    flag.
    * src/engine-gpg.c (gpg_decrypt): If export_session_key is set, add
    --export-session-key to argument list.
    * src/engine-gpgsm.c (gpgsm_decrypt): Ignore export_session_key for
    now, since gpgsm offers no such mechanism.
    * src/engine-uiserver.c (_uiserver_decrypt): If export_session_key is
    set, add --export-session-key flag to cmd.
    * doc/gpgme.texi: Document new functions and session_key member of
    decrypt_result_t.
    * doc/uiserver.texi: Add --export-session-key flag to DECRYPT command.
    
    --
    
    gpg(1) documents session key export as useful for key escrow, and is
    rightly dubious of that use case.  However, session key export is also
    useful in other use cases.  Two examples from MUA development (where
    this functionality would be specifically useful to me right now):
    
     * If the MUA stores a local copy of the session key upon decrypting
       the message, it can re-decrypt the message without expensive
       asymmetric operations.  When rendering a thread with dozens of
       encrypted messages, this can represent a significant speedup.
    
     * A user may have expired encryption-capable secret key material,
       along with many messages encrypted to that material.  If she stores
       the session keys for those messages she wants to keep, she can
       destroy her secret key material and make any messages she has
       deleted completely unrecoverable, even to an attacker who gets her
       remaining secret keys in the future.
    
    This patchset makes a two specific implementation decisions that could
    have gone in different ways.  I welcome feedback on preferred outcomes.
    
     0) session key representation: we currently represent the session key
        as an opaque textual string, rather than trying to provide any
        sort of in-memory structure.  While it wouldn't be hard to parse
        the data produced by gpg's --export-session-key, I chose to use
        the opaque string rather than lock in a particular data format.
    
     1) API/ABI: i've added a member to gpgme_op_decrypt_result_t.  This
        has the potential to cause an out-of-bound memory access if
        someone uses code compiled against the newer verision, but linked
        at runtime against an older version.  I've attempted to limit that
        risk by documenting that users must verify
        gpgme_get_export_session_keys() before accessing this new struct
        member -- this means that code expecting this capability will
        require the symbol at link-time, and will refuse to link against
        older versions.
    
        Another approach to solving this problem would be to avoid
        modifying gpgme_op_decrypt_result_t, and to introduce instead a
        new function gpgme_op_session_key(), which could be called in the
        same places as gpgme_op_decrypt_result().  Depending on the
        representation of the session key, this might introduce new
        memory-management burdens on the user of the library, and the
        session key is certainly part of a decryption result, so it seemed
        simpler to go with what i have here.
    
    If anyone has strong preferences that these choices should be solved
    in a different way, i'm happy to hear them.
    
    Additionally, I note that i'm also still pretty unclear about how the
    "UI Server" fits into this whole ecosystem. In particular, I don't
    know whether it's kosher to just add an --export-session-key flag to
    the DECRYPT operation without actually having implemented it anywhere,
    but i don't see where i would actually implement it either :/
    
    If this patch (or some variant) is adopted, i will supply another
    patch that permits offering a session key during decryption (e.g. "gpg
    --override-session-key"), but I wanted to get these implementation
    choices ironed out first.
    
    Gnupg-Bug-Id: 2754
    Signed-off-by: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
    
    On the concern of adding a new field to a structure: It may not be
    clearly documented but we don't expect that a user ever allocates such
    a structure - those result structure may only be created bu gpgme and
    are read-only for the user.  Adding a new member constitutes a
    compatible ABI change and thus an older SO may not be used by code
    compiled with a header for the newer API.  Unless someone tinkers with
    the build system, this should never happen.  We have added new fields
    to result structure may times and I can't remember any problems.
    
     - wk
---
 doc/gpgme.texi        | 38 ++++++++++++++++++++++++++++++++++++++
 doc/uiserver.texi     | 10 ++++++----
 src/context.h         |  3 +++
 src/decrypt-verify.c  |  2 +-
 src/decrypt.c         | 11 ++++++++++-
 src/engine-backend.h  |  4 ++--
 src/engine-gpg.c      |  5 ++++-
 src/engine-gpgsm.c    |  5 ++++-
 src/engine-uiserver.c | 16 +++++++++-------
 src/engine.c          |  8 ++++----
 src/engine.h          |  6 ++++--
 src/gpgme.c           | 24 ++++++++++++++++++++++++
 src/gpgme.def         |  2 ++
 src/gpgme.h.in        | 11 +++++++++++
 src/libgpgme.vers     |  3 +++
 15 files changed, 125 insertions(+), 23 deletions(-)

diff --git a/doc/gpgme.texi b/doc/gpgme.texi
index 801a53f..7eabab4 100644
--- a/doc/gpgme.texi
+++ b/doc/gpgme.texi
@@ -191,6 +191,7 @@ Context Attributes
 * Text Mode::                     Choosing canonical text mode.
 * Offline Mode::                  Choosing offline mode.
 * Included Certificates::         Including a number of certificates.
+* Exporting Session Keys::        Requesting session keys upon decryption.
 * Key Listing Mode::              Selecting key listing mode.
 * Passphrase Callback::           Getting the passphrase from the user.
 * Progress Meter Callback::       Being informed about the progress.
@@ -2351,6 +2352,7 @@ started.  In fact, these references are accessed through the
 * Offline Mode::                  Choosing offline mode.
 * Pinentry Mode::                 Choosing the pinentry mode.
 * Included Certificates::         Including a number of certificates.
+* Exporting Session Keys::        Requesting session keys upon decryption.
 * Key Listing Mode::              Selecting key listing mode.
 * Passphrase Callback::           Getting the passphrase from the user.
 * Progress Meter Callback::       Being informed about the progress.
@@ -2641,6 +2643,29 @@ certificates to include into an S/MIME signed message.
 @end deftypefun
 
 
+ at node Exporting Session Keys
+ at subsection Exporting Session Keys
+ at cindex context, exporting session keys
+ at cindex Exporting Session Keys
+ at cindex exporting session keys
+
+ at deftypefun void gpgme_set_export_session_keys (@w{gpgme_ctx_t @var{ctx}}, @w{int @var{yes}})
+The function @code{gpgme_set_export_session_keys} specifies whether
+the context should try to export the symmetric session key when
+decrypting data.  By default, session keys are not exported.
+
+Session keys are not exported if @var{yes} is zero, and
+enabled otherwise.
+ at end deftypefun
+
+ at deftypefun int gpgme_get_export_session_keys (@w{gpgme_ctx_t @var{ctx}})
+The function @code{gpgme_get_export_session_keys} returns @code{1} if
+the context will try to export the symmetric session key when
+decrypting, and @code{0} if not, or if @var{ctx} is not a valid
+pointer.
+ at end deftypefun
+
+
 @node Key Listing Mode
 @subsection Key Listing Mode
 @cindex key listing mode
@@ -4777,6 +4802,19 @@ This is a linked list of recipients to which this message was encrypted.
 @item char *file_name
 This is the filename of the original plaintext message file if it is
 known, otherwise this is a null pointer.
+
+ at item char *session_key
+A textual representation (null-terminated string) of the session key
+used in symmetric encryption of the message, if the context has been
+set to export session keys (see @code{gpgme_get_export_session_keys}
+and @code{gpgme_set_export_session_keys}), and a session key was
+available for the most recent decryption operation.  Otherwise, this
+is a null pointer.
+
+You should never access this member of a
+ at code{gpgme_op_decrypt_result_t} without first ensuring that
+ at code{gpgme_get_export_session_keys} returns non-zero for the
+reporting context.
 @end table
 @end deftp
 
diff --git a/doc/uiserver.texi b/doc/uiserver.texi
index aae3b60..f10db01 100644
--- a/doc/uiserver.texi
+++ b/doc/uiserver.texi
@@ -260,12 +260,14 @@ encoded. For details on the file descriptor, see the description of
 @noindent
 The decryption is started with the command:
 
- at deffn Command DECRYPT - at w{}-protocol=@var{name} [- at w{}-no-verify]
+ at deffn Command DECRYPT - at w{}-protocol=@var{name} [- at w{}-no-verify] [- at w{}-export-session-key]
 @var{name} is the encryption protocol used for the message. For a
 description of the allowed protocols see the @code{ENCRYPT} command.
-This argument is mandatory.  If the option @option{--no-verify} is given,
-the server should not try to verify a signature, in case the input data
-is an OpenPGP combined message.
+This argument is mandatory.  If the option @option{--no-verify} is
+given, the server should not try to verify a signature, in case the
+input data is an OpenPGP combined message. If the option
+ at option{--export-session-key} is given and the underlying engine knows
+how to export the session key, it will appear on a status line
 @end deffn
 
 
diff --git a/src/context.h b/src/context.h
index 00e2e77..94935c8 100644
--- a/src/context.h
+++ b/src/context.h
@@ -111,6 +111,9 @@ struct gpgme_context
    * unmodified string, as received form gpg, will be returned.  */
   unsigned int raw_description : 1;
 
+  /* True if session keys should be exported upon decryption.  */
+  unsigned int export_session_keys : 1;
+
   /* Flags for keylist mode.  */
   gpgme_keylist_mode_t keylist_mode;
 
diff --git a/src/decrypt-verify.c b/src/decrypt-verify.c
index a334f86..00d256a 100644
--- a/src/decrypt-verify.c
+++ b/src/decrypt-verify.c
@@ -77,7 +77,7 @@ decrypt_verify_start (gpgme_ctx_t ctx, int synchronous,
   _gpgme_engine_set_status_handler (ctx->engine,
 				    decrypt_verify_status_handler, ctx);
 
-  return _gpgme_engine_op_decrypt_verify (ctx->engine, cipher, plain);
+  return _gpgme_engine_op_decrypt_verify (ctx->engine, cipher, plain, ctx->export_session_keys);
 }
 
 
diff --git a/src/decrypt.c b/src/decrypt.c
index 51e4292..49c735c 100644
--- a/src/decrypt.c
+++ b/src/decrypt.c
@@ -63,6 +63,9 @@ release_op_data (void *hook)
   if (opd->result.file_name)
     free (opd->result.file_name);
 
+  if (opd->result.session_key)
+    free (opd->result.session_key);
+
   while (recipient)
     {
       gpgme_recipient_t next = recipient->next;
@@ -277,6 +280,12 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
       opd->last_recipient_p = &(*opd->last_recipient_p)->next;
       break;
 
+    case GPGME_STATUS_SESSION_KEY:
+      if (opd->result.session_key)
+        free (opd->result.session_key);
+      opd->result.session_key = strdup(args);
+      break;
+
     case GPGME_STATUS_NO_SECKEY:
       {
 	gpgme_recipient_t rec = opd->result.recipients;
@@ -381,7 +390,7 @@ decrypt_start (gpgme_ctx_t ctx, int synchronous,
 
   _gpgme_engine_set_status_handler (ctx->engine, decrypt_status_handler, ctx);
 
-  return _gpgme_engine_op_decrypt (ctx->engine, cipher, plain);
+  return _gpgme_engine_op_decrypt (ctx->engine, cipher, plain, ctx->export_session_keys);
 }
 
 
diff --git a/src/engine-backend.h b/src/engine-backend.h
index a8b1ac6..144b156 100644
--- a/src/engine-backend.h
+++ b/src/engine-backend.h
@@ -62,9 +62,9 @@ struct engine_ops
   gpgme_error_t (*set_locale) (void *engine, int category, const char *value);
   gpgme_error_t (*set_protocol) (void *engine, gpgme_protocol_t protocol);
   gpgme_error_t (*decrypt) (void *engine, gpgme_data_t ciph,
-			    gpgme_data_t plain);
+			    gpgme_data_t plain, int export_session_key);
   gpgme_error_t (*decrypt_verify) (void *engine, gpgme_data_t ciph,
-				   gpgme_data_t plain);
+				   gpgme_data_t plain, int export_session_key);
   gpgme_error_t (*delete) (void *engine, gpgme_key_t key, int allow_secret);
   gpgme_error_t (*edit) (void *engine, int type, gpgme_key_t key,
 			 gpgme_data_t out, gpgme_ctx_t ctx /* FIXME */);
diff --git a/src/engine-gpg.c b/src/engine-gpg.c
index 7725a00..0e43c24 100644
--- a/src/engine-gpg.c
+++ b/src/engine-gpg.c
@@ -1550,13 +1550,16 @@ add_input_size_hint (engine_gpg_t gpg, gpgme_data_t data)
 
 
 static gpgme_error_t
-gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
+gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain, int export_session_key)
 {
   engine_gpg_t gpg = engine;
   gpgme_error_t err;
 
   err = add_arg (gpg, "--decrypt");
 
+  if (!err && export_session_key)
+    err = add_arg (gpg, "--show-session-key");
+
   /* Tell the gpg object about the data.  */
   if (!err)
     err = add_arg (gpg, "--output");
diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c
index a815cf0..2ff353b 100644
--- a/src/engine-gpgsm.c
+++ b/src/engine-gpgsm.c
@@ -1120,10 +1120,13 @@ gpgsm_reset (void *engine)
 
 
 static gpgme_error_t
-gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
+gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain, int export_session_key)
 {
   engine_gpgsm_t gpgsm = engine;
   gpgme_error_t err;
+  /* gpgsm is not capable of exporting session keys right now, so we
+   * will ignore this if requested. */
+  (void)export_session_key;
 
   if (!gpgsm)
     return gpg_error (GPG_ERR_INV_VALUE);
diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c
index 47b7dc3..26f0d18 100644
--- a/src/engine-uiserver.c
+++ b/src/engine-uiserver.c
@@ -960,7 +960,8 @@ uiserver_reset (void *engine)
 
 static gpgme_error_t
 _uiserver_decrypt (void *engine, int verify,
-		   gpgme_data_t ciph, gpgme_data_t plain)
+		   gpgme_data_t ciph, gpgme_data_t plain,
+                   int export_session_key)
 {
   engine_uiserver_t uiserver = engine;
   gpgme_error_t err;
@@ -978,8 +979,9 @@ _uiserver_decrypt (void *engine, int verify,
   else
     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
 
-  if (asprintf (&cmd, "DECRYPT%s%s", protocol,
-		verify ? "" : " --no-verify") < 0)
+  if (asprintf (&cmd, "DECRYPT%s%s%s", protocol,
+		verify ? "" : " --no-verify",
+                export_session_key ? " --export-session-key" : "") < 0)
     return gpg_error_from_syserror ();
 
   uiserver->input_cb.data = ciph;
@@ -1006,16 +1008,16 @@ _uiserver_decrypt (void *engine, int verify,
 
 
 static gpgme_error_t
-uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
+uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain, int export_session_key)
 {
-  return _uiserver_decrypt (engine, 0, ciph, plain);
+  return _uiserver_decrypt (engine, 0, ciph, plain, export_session_key);
 }
 
 
 static gpgme_error_t
-uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
+uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain, int export_session_key)
 {
-  return _uiserver_decrypt (engine, 1, ciph, plain);
+  return _uiserver_decrypt (engine, 1, ciph, plain, export_session_key);
 }
 
 
diff --git a/src/engine.c b/src/engine.c
index 4e513b6..b43f683 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -653,7 +653,7 @@ _gpgme_engine_set_protocol (engine_t engine, gpgme_protocol_t protocol)
 
 gpgme_error_t
 _gpgme_engine_op_decrypt (engine_t engine, gpgme_data_t ciph,
-			  gpgme_data_t plain)
+			  gpgme_data_t plain, int export_session_key)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -661,13 +661,13 @@ _gpgme_engine_op_decrypt (engine_t engine, gpgme_data_t ciph,
   if (!engine->ops->decrypt)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
-  return (*engine->ops->decrypt) (engine->engine, ciph, plain);
+  return (*engine->ops->decrypt) (engine->engine, ciph, plain, export_session_key);
 }
 
 
 gpgme_error_t
 _gpgme_engine_op_decrypt_verify (engine_t engine, gpgme_data_t ciph,
-				 gpgme_data_t plain)
+				 gpgme_data_t plain, int export_session_key)
 {
   if (!engine)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -675,7 +675,7 @@ _gpgme_engine_op_decrypt_verify (engine_t engine, gpgme_data_t ciph,
   if (!engine->ops->decrypt_verify)
     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
 
-  return (*engine->ops->decrypt_verify) (engine->engine, ciph, plain);
+  return (*engine->ops->decrypt_verify) (engine->engine, ciph, plain, export_session_key);
 }
 
 
diff --git a/src/engine.h b/src/engine.h
index 15b0b5d..512ac19 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -83,10 +83,12 @@ _gpgme_engine_set_colon_line_handler (engine_t engine,
 				      engine_colon_line_handler_t fnc,
 				      void *fnc_value);
 gpgme_error_t _gpgme_engine_op_decrypt (engine_t engine, gpgme_data_t ciph,
-					gpgme_data_t plain);
+					gpgme_data_t plain,
+                                        int export_session_key);
 gpgme_error_t _gpgme_engine_op_decrypt_verify (engine_t engine,
 					       gpgme_data_t ciph,
-					       gpgme_data_t plain);
+					       gpgme_data_t plain,
+                                               int export_session_key);
 gpgme_error_t _gpgme_engine_op_delete (engine_t engine, gpgme_key_t key,
 				       int allow_secret);
 gpgme_error_t _gpgme_engine_op_edit (engine_t engine, int type,
diff --git a/src/gpgme.c b/src/gpgme.c
index 443cb76..7b14b5e 100644
--- a/src/gpgme.c
+++ b/src/gpgme.c
@@ -518,6 +518,30 @@ gpgme_get_armor (gpgme_ctx_t ctx)
 }
 
 
+/* Enable or disable the exporting session keys upon decryption.  */
+void
+gpgme_set_export_session_keys (gpgme_ctx_t ctx, int export_session_keys)
+{
+  TRACE2 (DEBUG_CTX, "gpgme_set_export_session_keys", ctx, "export_session_keys=%i (%s)",
+	  export_session_keys, export_session_keys ? "yes" : "no");
+
+  if (!ctx)
+    return;
+
+  ctx->export_session_keys = !!export_session_keys;
+}
+
+
+/* Return whether this context will export session keys upon decryption.  */
+int
+gpgme_get_export_session_keys (gpgme_ctx_t ctx)
+{
+  TRACE2 (DEBUG_CTX, "gpgme_get_export_session_keys", ctx, "ctx->export_session_keys=%i (%s)",
+	  ctx->export_session_keys, ctx->export_session_keys ? "yes" : "no");
+  return ctx->export_session_keys;
+}
+
+
 /* Enable or disable the use of the special textmode.  Textmode is for
   example used for the RFC2015 signatures; note that the updated RFC
   3156 mandates that the MUA does some preparations so that textmode
diff --git a/src/gpgme.def b/src/gpgme.def
index 2f6837d..35f4341 100644
--- a/src/gpgme.def
+++ b/src/gpgme.def
@@ -252,5 +252,7 @@ EXPORTS
     gpgme_op_query_swdb                   @189
     gpgme_op_query_swdb_result            @190
 
+    gpgme_set_export_session_keys         @191
+    gpgme_get_export_session_keys         @192
 ; END
 
diff --git a/src/gpgme.h.in b/src/gpgme.h.in
index 4f470a0..2a0e16e 100644
--- a/src/gpgme.h.in
+++ b/src/gpgme.h.in
@@ -1037,6 +1037,13 @@ void gpgme_set_offline (gpgme_ctx_t ctx, int yes);
 /* Return non-zero if offline mode is set in CTX.  */
 int gpgme_get_offline (gpgme_ctx_t ctx);
 
+/* If YES is non-zero, try to return session keys during decryption,
+   do not otherwise.  */
+void gpgme_set_export_session_keys (gpgme_ctx_t ctx, int yes);
+
+/* Return non-zero if export_session_keys is set in CTX.  */
+int gpgme_get_export_session_keys (gpgme_ctx_t ctx);
+
 /* Use whatever the default of the backend crypto engine is.  */
 #define GPGME_INCLUDE_CERTS_DEFAULT	-256
 
@@ -1527,6 +1534,10 @@ struct _gpgme_op_decrypt_result
   /* The original file name of the plaintext message, if
      available.  */
   char *file_name;
+
+  /* A textual representation of the session key used to decrypt the
+   * message, if available */
+  char *session_key;
 };
 typedef struct _gpgme_op_decrypt_result *gpgme_decrypt_result_t;
 
diff --git a/src/libgpgme.vers b/src/libgpgme.vers
index 5457daa..9a3ecb2 100644
--- a/src/libgpgme.vers
+++ b/src/libgpgme.vers
@@ -125,6 +125,9 @@ GPGME_1.1 {
 
     gpgme_op_query_swdb;
     gpgme_op_query_swdb_result;
+
+    gpgme_set_export_session_keys;
+    gpgme_get_export_session_keys;
 };
 
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-gnupg/gpgme.git



More information about the Pkg-gnupg-commit mailing list