diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 801a53f3..7eabab48 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 +@node Exporting Session Keys +@subsection Exporting Session Keys +@cindex context, exporting session keys +@cindex Exporting Session Keys +@cindex exporting session keys + +@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. +@end deftypefun + +@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. +@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. + +@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 +@code{gpgme_op_decrypt_result_t} without first ensuring that +@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 aae3b606..f10db01a 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: -@deffn Command DECRYPT -@w{}-protocol=@var{name} [-@w{}-no-verify] +@deffn Command DECRYPT -@w{}-protocol=@var{name} [-@w{}-no-verify] [-@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 +@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 00e2e779..94935c80 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 a334f86f..00d256a9 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 51e42920..49c735ca 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 a8b1ac60..144b1561 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 7725a001..0e43c248 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 a815cf00..2ff353b9 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 47b7dc33..26f0d18b 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 4e513b6d..b43f683e 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 15b0b5d4..512ac19a 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 443cb768..7b14b5e9 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 2f6837da..35f43411 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 4f470a03..2a0e16e3 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 5457daa4..9a3ecb2e 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; };