From 8a0c8c52510d9c2d934f85159f04b666286b1786 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 17 May 2018 09:14:40 +0200 Subject: core: Always fail if an OpenPG message is not integrity protected. * src/decrypt.c (struct op_data_t): Add field not_integrity_protected. (parse_decryption_info): Set this. Also rename mode to aead_algo for clarity. (_gpgme_decrypt_status_handler): Force failure in case of a missing MDC. -- This extra check makes sure that a missing or stripped MDC in - gpg < 2.1 - or gpg 2.2 with an old cipher algorithm will lead to a decryption failure. gpg 2.3 will always fail in this case. Implementing this check here and not backporting the 2.3 change to 2.2 has the benefit that all GPGME using applications are protected but scripts relying on rfc2440 (i.e. without MDC) will only break when migrating to 2.3. Note that S/MIME has no integrity protection mechanism but gpgsm neither emits a DECRYPTION_INFO status line, so an error will not be triggered. If in the future gpgsm supports authenticated encryption it may issue a DECRYPTION_INFO line to force a failure here but it will in that case also emit a DECRYPTION_FAILED anyway. GnuPG-bug-id: 3981 Signed-off-by: Werner Koch --- NEWS | 3 +++ doc/gpgme.texi | 2 +- src/decrypt.c | 27 ++++++++++++++++++++------- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 80c01190..1eacccce 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ Noteworthy changes in version 1.11.2 (unreleased) ------------------------------------------------- + * Even for old versions of gpg a missing MDC will now lead to a + decryption failure. + Noteworthy changes in version 1.11.1 (2018-04-20) ------------------------------------------------- diff --git a/doc/gpgme.texi b/doc/gpgme.texi index c4a29d54..c745675b 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -5408,7 +5408,7 @@ or @code{gpgme_get_ctx_flag (ctx, "export-session-key")} returns true @since{1.11.0} A string with the symmetric encryption algorithm and mode using the -format ".". Note that old non-MDC encryption mode of +format ".". Note that the deprecated non-MDC encryption mode of OpenPGP is given as "PGPCFB". @end table diff --git a/src/decrypt.c b/src/decrypt.c index 0fc7019c..ecd9c144 100644 --- a/src/decrypt.c +++ b/src/decrypt.c @@ -56,6 +56,11 @@ typedef struct * message that the general DECRYPTION_FAILED. */ int any_no_seckey; + /* If the engine emits a DECRYPTION_INFO status and that does not + * indicate that an integrity proetction mode is active, this flag + * is set. */ + int not_integrity_protected; + /* A pointer to the next pointer of the last recipient in the list. This makes appending new invalid signers painless while preserving the order. */ @@ -280,7 +285,7 @@ parse_decryption_info (char *args, op_data_t opd, gpgme_protocol_t protocol) char *field[3]; int nfields; char *args2; - int mdc, mode; + int mdc, aead_algo; const char *algostr, *modestr; if (!args) @@ -296,19 +301,22 @@ parse_decryption_info (char *args, op_data_t opd, gpgme_protocol_t protocol) mdc = atoi (field[0]); algostr = _gpgme_cipher_algo_name (atoi (field[1]), protocol); - mode = nfields < 3? 0 : atoi (field[2]); - modestr = _gpgme_cipher_mode_name (mode, protocol); + aead_algo = nfields < 3? 0 : atoi (field[2]); + modestr = _gpgme_cipher_mode_name (aead_algo, protocol); free (args2); free (opd->result.symkey_algo); - if (!mode && mdc != 2) + if (!aead_algo && mdc != 2) opd->result.symkey_algo = _gpgme_strconcat (algostr, ".PGPCFB", NULL); else opd->result.symkey_algo = _gpgme_strconcat (algostr, ".", modestr, NULL); if (!opd->result.symkey_algo) return gpg_error_from_syserror (); + if (!mdc && !aead_algo) + opd->not_integrity_protected = 1; + return 0; } @@ -338,13 +346,18 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code, break; case GPGME_STATUS_EOF: - /* FIXME: These error values should probably be attributed to - the underlying crypto engine (as error source). */ + /* We force an encryption failure if we know that integrity + * protection is missing. For modern version of gpg using + * modern cipher algorithms this is not required because gpg + * will issue a failure anyway. However older gpg versions emit + * only a warning. + * Fixme: These error values should probably be attributed to + * the underlying crypto engine (as error source). */ if (opd->failed && opd->pkdecrypt_failed) return opd->pkdecrypt_failed; else if (opd->failed && opd->any_no_seckey) return gpg_error (GPG_ERR_NO_SECKEY); - else if (opd->failed) + else if (opd->failed || opd->not_integrity_protected) return gpg_error (GPG_ERR_DECRYPT_FAILED); else if (!opd->okay) return gpg_error (GPG_ERR_NO_DATA); -- cgit v1.2.3 From e04b8142df21a49e6c4a3f8234cc14bfec217222 Mon Sep 17 00:00:00 2001 From: Andre Heinecke Date: Thu, 17 May 2018 17:41:53 +0200 Subject: core, w32: Add w64 handling for regkeys * src/w32-util.c (_gpgme_get_gpg_path): Use new defines. (GNUPG_REGKEY_2): x64 aware regkey as used by GnuPG in Gpg4win 2.x (GNUPG_REGKEY_3): x64 aware regkey as used by GnuPG in Gpg4win 3.x (_gpgme_get_gpgconf_path): Use new regkeys. Add another fallback. -- This should fix more "unsupported protocol" issues if Gpg4win / GnuPG is installed in a non standard path on 64bit systems. The regkey handling is similar to that of gpgex and gpgol. GnuPG-Bug-Id: T3988 --- src/w32-util.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/w32-util.c b/src/w32-util.c index 5b02c7ea..30dd081a 100644 --- a/src/w32-util.c +++ b/src/w32-util.c @@ -72,6 +72,17 @@ # define F_OK 0 #endif +/* The Registry key used by GNUPG. */ +#ifdef _WIN64 +# define GNUPG_REGKEY_2 "Software\\Wow6432Node\\GNU\\GnuPG" +#else +# define GNUPG_REGKEY_2 "Software\\GNU\\GnuPG" +#endif +#ifdef _WIN64 +# define GNUPG_REGKEY_3 "Software\\Wow6432Node\\GnuPG" +#else +# define GNUPG_REGKEY_3 "Software\\GnuPG" +#endif DEFINE_STATIC_LOCK (get_path_lock); @@ -513,7 +524,7 @@ _gpgme_get_gpg_path (void) char *dir; dir = read_w32_registry_string ("HKEY_LOCAL_MACHINE", - "Software\\GNU\\GnuPG", + GNUPG_REGKEY_2, "Install Directory"); if (dir) { @@ -568,12 +579,12 @@ _gpgme_get_gpgconf_path (void) char *dir; dir = read_w32_registry_string (NULL, - "Software\\GNU\\GnuPG", + GNUPG_REGKEY_2, "Install Directory"); if (!dir) { char *tmp = read_w32_registry_string (NULL, - "Software\\GnuPG", + GNUPG_REGKEY_3, "Install Directory"); if (tmp) { @@ -596,6 +607,14 @@ _gpgme_get_gpgconf_path (void) gpgconf = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe"); } + /* 5. Try to find gpgconf.exe relative to us. */ + if (!gpgconf && inst_dir) + { + char *dir = _gpgme_strconcat (inst_dir, "\\..\\..\\GnuPG\\bin"); + gpgconf = find_program_in_dir (dir, name); + free (dir); + } + /* 5. Print a debug message if not found. */ if (!gpgconf) _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpgconf_path: '%s' not found",name); -- cgit v1.2.3 From 28e3778ce21069006153bc156a414de6d9347962 Mon Sep 17 00:00:00 2001 From: Andre Heinecke Date: Tue, 22 May 2018 12:08:01 +0200 Subject: cpp: Expose sessionKey and symkeyAlgo * lang/cpp/decryptionresult.cpp, lang/cpp/decryptionresult.h (DecryptionResult::symkeyAlgo, DecryptionResult::sessionKey): New. --- NEWS | 4 ++++ lang/cpp/src/decryptionresult.cpp | 11 +++++++++++ lang/cpp/src/decryptionresult.h | 4 ++++ 3 files changed, 19 insertions(+) diff --git a/NEWS b/NEWS index 1eacccce..e0adb35b 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,10 @@ Noteworthy changes in version 1.11.2 (unreleased) * Even for old versions of gpg a missing MDC will now lead to a decryption failure. + * Interface changes relative to the 1.11.1 release: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + cpp: DecryptionResult::sessionKey NEW. + cpp: DecryptionResult::symkeyAlgo NEW. Noteworthy changes in version 1.11.1 (2018-04-20) ------------------------------------------------- diff --git a/lang/cpp/src/decryptionresult.cpp b/lang/cpp/src/decryptionresult.cpp index 1e815cbe..17524db9 100644 --- a/lang/cpp/src/decryptionresult.cpp +++ b/lang/cpp/src/decryptionresult.cpp @@ -155,6 +155,16 @@ std::vector GpgME::DecryptionResult::recipie return result; } +const char *GpgME::DecryptionResult::sessionKey() const +{ + return d ? d->res.session_key : nullptr; +} + +const char *GpgME::DecryptionResult::symkeyAlgo() const +{ + return d ? d->res.symkey_algo : nullptr; +} + class GpgME::DecryptionResult::Recipient::Private : public _gpgme_recipient { public: @@ -231,6 +241,7 @@ std::ostream &GpgME::operator<<(std::ostream &os, const DecryptionResult &result << "\n unsupportedAlgorithm: " << protect(result.unsupportedAlgorithm()) << "\n isWrongKeyUsage: " << result.isWrongKeyUsage() << "\n isDeVs " << result.isDeVs() + << "\n symkeyAlgo: " << protect(result.symkeyAlgo()) << "\n recipients:\n"; const std::vector recipients = result.recipients(); std::copy(recipients.begin(), recipients.end(), diff --git a/lang/cpp/src/decryptionresult.h b/lang/cpp/src/decryptionresult.h index 57705b48..c270223d 100644 --- a/lang/cpp/src/decryptionresult.h +++ b/lang/cpp/src/decryptionresult.h @@ -77,6 +77,10 @@ public: const char *fileName() const; + const char *sessionKey() const; + + const char *symkeyAlgo() const; + class Recipient; unsigned int numRecipients() const; -- cgit v1.2.3 From 3a9e6a8e088e233097866bb0560a36cfbbc4470e Mon Sep 17 00:00:00 2001 From: Ben McGinnes Date: Wed, 23 May 2018 14:43:06 +1000 Subject: docs and examples: python howto * Updated the decryption example code in the HOWTO and the corresponding decrypt-file.py script to gracefully handle a decryption failure. This error will always be triggered when GPGME is used to try to decrypt an old, MDC-less encrypted message or file. --- lang/python/docs/GPGMEpythonHOWTOen.org | 17 ++++++++++++----- lang/python/examples/howto/decrypt-file.py | 13 ++++++++++--- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lang/python/docs/GPGMEpythonHOWTOen.org b/lang/python/docs/GPGMEpythonHOWTOen.org index cb85b61b..ef66effc 100644 --- a/lang/python/docs/GPGMEpythonHOWTOen.org +++ b/lang/python/docs/GPGMEpythonHOWTOen.org @@ -14,7 +14,7 @@ :CUSTOM_ID: intro :END: - | Version: | 0.1.0 | + | Version: | 0.1.1 | | Author: | Ben McGinnes | | Author GPG Key: | DB4724E6FA4286C92B4E55C4321E4E2373590E5D | | Language: | Australian English, British English | @@ -673,10 +673,17 @@ newfile = input("Enter path and filename of file to save decrypted data to: ") with open(ciphertext, "rb") as cfile: - plaintext, result, verify_result = gpg.Context().decrypt(cfile) - - with open(newfile, "wb") as nfile: - nfile.write(plaintext) + try: + plaintext, result, verify_result = gpg.Context().decrypt(cfile) + except gpg.errors.GPGMEError as e: + plaintext = None + print(e) + + if plaintext is not None: + with open(newfile, "wb") as nfile: + nfile.write(plaintext) + else: + pass #+end_src The data available in =plaintext= in this example is the decrypted diff --git a/lang/python/examples/howto/decrypt-file.py b/lang/python/examples/howto/decrypt-file.py index 60a050bd..b38acc79 100755 --- a/lang/python/examples/howto/decrypt-file.py +++ b/lang/python/examples/howto/decrypt-file.py @@ -38,7 +38,14 @@ else: newfile = input("Enter path and filename of file to save decrypted data to: ") with open(ciphertext, "rb") as cfile: - plaintext, result, verify_result = gpg.Context().decrypt(cfile) + try: + plaintext, result, verify_result = gpg.Context().decrypt(cfile) + except gpg.errors.GPGMEError as e: + plaintext = None + print(e) -with open(newfile, "wb") as nfile: - nfile.write(plaintext) +if plaintext is not None: + with open(newfile, "wb") as nfile: + nfile.write(plaintext) +else: + pass -- cgit v1.2.3 From fd5e14660a6f4eb1a89d69534e3e435f7fb05f8a Mon Sep 17 00:00:00 2001 From: Andre Heinecke Date: Thu, 24 May 2018 10:26:41 +0200 Subject: json: Minor typo fixes * src/gpgme-json.c: Minor typo fixes. --- src/gpgme-json.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gpgme-json.c b/src/gpgme-json.c index fb5f149b..990f4a33 100644 --- a/src/gpgme-json.c +++ b/src/gpgme-json.c @@ -490,7 +490,7 @@ _create_new_context (gpgme_protocol_t proto) /* Return a context object for protocol PROTO. This is currently a - * statuically allocated context initialized for PROTO. Termnates + * statically allocated context initialized for PROTO. Terminates * process on failure. */ static gpgme_ctx_t get_context (gpgme_protocol_t proto) @@ -597,11 +597,11 @@ data_from_base64_string (gpgme_data_t *r_data, cjson_t json) /* Create a "data" object and the "type", "base64" and "more" flags - * from DATA and append them to RESULT. Ownership if DATA is + * from DATA and append them to RESULT. Ownership of DATA is * transferred to this function. TYPE must be a fixed string. * CHUNKSIZE is the chunksize requested from the caller. If BASE64 is * -1 the need for base64 encoding is determined by the content of - * DATA, all other values are take as rtue or false. Note that + * DATA, all other values are taken as true or false. Note that * op_getmore has similar code but works on PENDING_DATA which is set * here. */ static gpg_error_t -- cgit v1.2.3 From 45036c3c4c11f7bd56a00805564108e9377b657e Mon Sep 17 00:00:00 2001 From: Andre Heinecke Date: Thu, 24 May 2018 12:34:31 +0200 Subject: json: Print signatures for decrypt/verify * gpgme-json.c (xJSON_CreateArray), (add_summary_to_object, validity_to_string): New helpers. (add_signature_to_object, add_signatures_to_object) (add_signatures_object): New. (op_decrypt): Handle verify_result. (hlp_help): Mention decrypt. --- src/gpgme-json.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) diff --git a/src/gpgme-json.c b/src/gpgme-json.c index 990f4a33..4f8e0afe 100644 --- a/src/gpgme-json.c +++ b/src/gpgme-json.c @@ -147,6 +147,16 @@ xjson_CreateObject (void) return json; } +/* Call cJSON_CreateArray but terminate in case of an error. */ +static cjson_t +xjson_CreateArray (void) +{ + cjson_t json = cJSON_CreateArray (); + if (!json) + xoutofcore ("cJSON_CreateArray"); + return json; +} + /* Wrapper around cJSON_AddStringToObject which returns an gpg-error * code instead of the NULL or the new object. */ @@ -590,6 +600,187 @@ data_from_base64_string (gpgme_data_t *r_data, cjson_t json) } +/* Helper for summary formatting */ +static void +add_summary_to_object (cjson_t result, gpgme_sigsum_t summary) +{ + cjson_t response = xjson_CreateArray (); + if ( (summary & GPGME_SIGSUM_VALID )) + cJSON_AddItemToArray (response, + cJSON_CreateString ("valid")); + if ( (summary & GPGME_SIGSUM_GREEN )) + cJSON_AddItemToArray (response, + cJSON_CreateString ("green")); + if ( (summary & GPGME_SIGSUM_RED )) + cJSON_AddItemToArray (response, + cJSON_CreateString ("red")); + if ( (summary & GPGME_SIGSUM_KEY_REVOKED)) + cJSON_AddItemToArray (response, + cJSON_CreateString ("revoked")); + if ( (summary & GPGME_SIGSUM_KEY_EXPIRED)) + cJSON_AddItemToArray (response, + cJSON_CreateString ("key-expired")); + if ( (summary & GPGME_SIGSUM_SIG_EXPIRED)) + cJSON_AddItemToArray (response, + cJSON_CreateString ("sig-expired")); + if ( (summary & GPGME_SIGSUM_KEY_MISSING)) + cJSON_AddItemToArray (response, + cJSON_CreateString ("key-missing")); + if ( (summary & GPGME_SIGSUM_CRL_MISSING)) + cJSON_AddItemToArray (response, + cJSON_CreateString ("crl-missing")); + if ( (summary & GPGME_SIGSUM_CRL_TOO_OLD)) + cJSON_AddItemToArray (response, + cJSON_CreateString ("crl-too-old")); + if ( (summary & GPGME_SIGSUM_BAD_POLICY )) + cJSON_AddItemToArray (response, + cJSON_CreateString ("bad-policy")); + if ( (summary & GPGME_SIGSUM_SYS_ERROR )) + cJSON_AddItemToArray (response, + cJSON_CreateString ("sys-error")); + + cJSON_AddItemToObject (result, "summary", response); +} + + +/* Helper for summary formatting */ +static const char * +validity_to_string (gpgme_validity_t val) +{ + switch (val) + { + case GPGME_VALIDITY_UNDEFINED:return "undefined"; + case GPGME_VALIDITY_NEVER: return "never"; + case GPGME_VALIDITY_MARGINAL: return "marginal"; + case GPGME_VALIDITY_FULL: return "full"; + case GPGME_VALIDITY_ULTIMATE: return "ultimate"; + case GPGME_VALIDITY_UNKNOWN: + default: return "unknown"; + } +} + + +/* Add a single signature to a result */ +static gpg_error_t +add_signature_to_object (cjson_t result, gpgme_signature_t sig) +{ + gpg_error_t err = 0; + + if (!cJSON_AddStringToObject (result, "status", gpgme_strerror (sig->status))) + { + err = gpg_error_from_syserror (); + goto leave; + } + + if (!cJSON_AddNumberToObject (result, "code", sig->status)) + { + err = gpg_error_from_syserror (); + goto leave; + } + + add_summary_to_object (result, sig->summary); + + if (!cJSON_AddStringToObject (result, "fingerprint", sig->fpr)) + { + err = gpg_error_from_syserror (); + goto leave; + } + + if (!cJSON_AddNumberToObject (result, "created", sig->timestamp)) + { + err = gpg_error_from_syserror (); + goto leave; + } + + if (!cJSON_AddNumberToObject (result, "expired", sig->exp_timestamp)) + { + err = gpg_error_from_syserror (); + goto leave; + } + + if (!cJSON_AddStringToObject (result, "validity", + validity_to_string (sig->validity))) + { + err = gpg_error_from_syserror (); + goto leave; + } + +leave: + return err; +} + + +/* Add multiple signatures as an array to a result */ +static gpg_error_t +add_signatures_to_object (cjson_t result, gpgme_signature_t signatures) +{ + cjson_t response = xJSON_CreateArray (); + gpg_error_t err = 0; + gpgme_signature_t sig; + + for (sig = signatures; sig; sig = sig->next) + { + cjson_t sig_obj = xjson_CreateObject (); + err = add_signature_to_object (sig_obj, sig); + if (err) + { + cJSON_Delete (sig_obj); + sig_obj = NULL; + goto leave; + } + + cJSON_AddItemToArray (response, sig_obj); + } + + if (!cJSON_AddItemToObject (result, "signatures", response)) + { + err = gpg_error_from_syserror (); + cJSON_Delete (response); + response = NULL; + return err; + } + response = NULL; + +leave: + if (err && response) + { + cJSON_Delete (response); + response = NULL; + } + return err; +} + + +/* Add an array of signature informations under the name "name". */ +static gpg_error_t +add_signatures_object (cjson_t result, const char *name, + gpgme_verify_result_t verify_result) +{ + cjson_t response = xjson_CreateObject (); + gpg_error_t err = 0; + + err = add_signatures_to_object (response, verify_result->signatures); + + if (err) + { + goto leave; + } + + if (!cJSON_AddItemToObject (result, name, response)) + { + err = gpg_error_from_syserror (); + goto leave; + } + leave: + if (err) + { + cJSON_Delete (response); + response = NULL; + } + return err; +} + + /* * Implementation of the commands. @@ -884,6 +1075,7 @@ op_decrypt (cjson_t request, cjson_t result) gpgme_data_t input = NULL; gpgme_data_t output = NULL; gpgme_decrypt_result_t decrypt_result; + gpgme_verify_result_t verify_result; if ((err = get_protocol (request, &protocol))) goto leave; @@ -955,6 +1147,24 @@ op_decrypt (cjson_t request, cjson_t result) err = make_data_object (result, output, chunksize, "plaintext", -1); output = NULL; + if (err) + { + error_object (result, "Plaintext output failed: %s", gpg_strerror (err)); + goto leave; + } + + verify_result = gpgme_op_verify_result (ctx); + if (verify_result && verify_result->signatures) + { + err = add_signatures_object (result, "info", verify_result); + } + + if (err) + { + error_object (result, "Info output failed: %s", gpg_strerror (err)); + goto leave; + } + leave: release_context (ctx); gpgme_data_release (input); @@ -1058,6 +1268,7 @@ static const char hlp_help[] = "returned. To list all operations it is allowed to leave out \"op\" in\n" "help mode. Supported values for \"op\" are:\n\n" " encrypt Encrypt data.\n" + " decrypt Decrypt data.\n" " getmore Retrieve remaining data.\n" " help Help overview."; static gpg_error_t -- cgit v1.2.3 From b344933e4cb17f2f26c4ed355217428bda8b8c40 Mon Sep 17 00:00:00 2001 From: Andre Heinecke Date: Thu, 24 May 2018 13:16:55 +0200 Subject: json: Fix invalid function call * src/gpgme-json.c (add_signatures_to_object): Fix call to xjson_CreateArray. -- That is what happens if you edit code while reviewing changes, without testing it again,.. --- src/gpgme-json.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gpgme-json.c b/src/gpgme-json.c index 4f8e0afe..f1a857d4 100644 --- a/src/gpgme-json.c +++ b/src/gpgme-json.c @@ -714,7 +714,7 @@ leave: static gpg_error_t add_signatures_to_object (cjson_t result, gpgme_signature_t signatures) { - cjson_t response = xJSON_CreateArray (); + cjson_t response = xjson_CreateArray (); gpg_error_t err = 0; gpgme_signature_t sig; -- cgit v1.2.3 From 1c0a55a60847563fecf92a383457ab3576aec5d8 Mon Sep 17 00:00:00 2001 From: Andre Heinecke Date: Thu, 24 May 2018 13:24:02 +0200 Subject: json: Add op_sign * src/gpgme-json.c (op_sign): New. --- src/gpgme-json.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/src/gpgme-json.c b/src/gpgme-json.c index f1a857d4..e8858064 100644 --- a/src/gpgme-json.c +++ b/src/gpgme-json.c @@ -1173,6 +1173,182 @@ op_decrypt (cjson_t request, cjson_t result) } + +static const char hlp_sign[] = + "op: \"sign\"\n" + "keys: Array of strings with the fingerprints of the signing key.\n" + " For a single key a String may be used instead of an array.\n" + "data: Input data. \n" + "\n" + "Optional parameters:\n" + "protocol: Either \"openpgp\" (default) or \"cms\".\n" + "chunksize: Max number of bytes in the resulting \"data\".\n" + "sender: The mail address of the sender.\n" + "mode: A string with the signing mode can be:\n" + " detached (default)\n" + " opaque\n" + " clearsign\n" + "\n" + "Optional boolean flags (default is false):\n" + "base64: Input data is base64 encoded.\n" + "armor: Request output in armored format.\n" + "\n" + "Response on success:\n" + "type: \"signature\"\n" + "data: Unless armor mode is used a Base64 encoded binary\n" + " signature. In armor mode a string with an armored\n" + " OpenPGP or a PEM message.\n" + "base64: Boolean indicating whether data is base64 encoded.\n" + "more: Optional boolean indicating that \"getmore\" is required."; +static gpg_error_t +op_sign (cjson_t request, cjson_t result) +{ + gpg_error_t err; + gpgme_ctx_t ctx = NULL; + gpgme_protocol_t protocol; + size_t chunksize; + int opt_base64; + char *keystring = NULL; + cjson_t j_input; + gpgme_data_t input = NULL; + gpgme_data_t output = NULL; + int abool; + cjson_t j_tmp; + gpgme_sig_mode_t mode = GPGME_SIG_MODE_DETACH; + gpgme_ctx_t keylist_ctx = NULL; + gpgme_key_t key = NULL; + + if ((err = get_protocol (request, &protocol))) + goto leave; + ctx = get_context (protocol); + if ((err = get_chunksize (request, &chunksize))) + goto leave; + + if ((err = get_boolean_flag (request, "base64", 0, &opt_base64))) + goto leave; + + if ((err = get_boolean_flag (request, "armor", 0, &abool))) + goto leave; + gpgme_set_armor (ctx, abool); + + j_tmp = cJSON_GetObjectItem (request, "mode"); + if (j_tmp && cjson_is_string (j_tmp)) + { + if (!strcmp (j_tmp->valuestring, "opaque")) + { + mode = GPGME_SIG_MODE_NORMAL; + } + else if (!strcmp (j_tmp->valuestring, "clearsign")) + { + mode = GPGME_SIG_MODE_CLEAR; + } + } + + j_tmp = cJSON_GetObjectItem (request, "sender"); + if (j_tmp && cjson_is_string (j_tmp)) + { + gpgme_set_sender (ctx, j_tmp->valuestring); + } + + /* Get the keys. */ + err = get_keys (request, &keystring); + if (err) + { + /* Provide a custom error response. */ + error_object (result, "Error getting keys: %s", gpg_strerror (err)); + goto leave; + } + + /* Do a keylisting and add the keys */ + if ((err = gpgme_new (&keylist_ctx))) + goto leave; + gpgme_set_protocol (keylist_ctx, protocol); + gpgme_set_keylist_mode (keylist_ctx, GPGME_KEYLIST_MODE_LOCAL); + + err = gpgme_op_keylist_start (ctx, keystring, 1); + if (err) + { + error_object (result, "Error listing keys: %s", gpg_strerror (err)); + goto leave; + } + while (!(err = gpgme_op_keylist_next (ctx, &key))) + { + if ((err = gpgme_signers_add (ctx, key))) + { + error_object (result, "Error adding signer: %s", gpg_strerror (err)); + goto leave; + } + gpgme_key_unref (key); + } + + /* Get the data. Note that INPUT is a shallow data object with the + * storage hold in REQUEST. */ + j_input = cJSON_GetObjectItem (request, "data"); + if (!j_input) + { + err = gpg_error (GPG_ERR_NO_DATA); + goto leave; + } + if (!cjson_is_string (j_input)) + { + err = gpg_error (GPG_ERR_INV_VALUE); + goto leave; + } + if (opt_base64) + { + err = data_from_base64_string (&input, j_input); + if (err) + { + error_object (result, "Error decoding Base-64 encoded 'data': %s", + gpg_strerror (err)); + goto leave; + } + } + else + { + err = gpgme_data_new_from_mem (&input, j_input->valuestring, + strlen (j_input->valuestring), 0); + if (err) + { + error_object (result, "Error getting 'data': %s", gpg_strerror (err)); + goto leave; + } + } + + /* Create an output data object. */ + err = gpgme_data_new (&output); + if (err) + { + error_object (result, "Error creating output data object: %s", + gpg_strerror (err)); + goto leave; + } + + /* Sign. */ + err = gpgme_op_sign (ctx, input, output, mode); + if (err) + { + error_object (result, "Signing failed: %s", gpg_strerror (err)); + goto leave; + } + + gpgme_data_release (input); + input = NULL; + + /* We need to base64 if armoring has not been requested. */ + err = make_data_object (result, output, chunksize, + "ciphertext", !gpgme_get_armor (ctx)); + output = NULL; + + leave: + xfree (keystring); + release_context (ctx); + release_context (keylist_ctx); + gpgme_data_release (input); + gpgme_data_release (output); + return err; +} + static const char hlp_getmore[] = "op: \"getmore\"\n" @@ -1309,6 +1485,7 @@ process_request (const char *request) } optbl[] = { { "encrypt", op_encrypt, hlp_encrypt }, { "decrypt", op_decrypt, hlp_decrypt }, + { "sign", op_sign, hlp_sign }, { "getmore", op_getmore, hlp_getmore }, { "help", op_help, hlp_help }, { NULL } -- cgit v1.2.3 From a6cd3a1197eb4efea0950394959c252f24475f67 Mon Sep 17 00:00:00 2001 From: Andre Heinecke Date: Thu, 24 May 2018 13:31:15 +0200 Subject: json: Add sign to help * src/gpgme-json.c (hlp_help): Add sign. --- src/gpgme-json.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gpgme-json.c b/src/gpgme-json.c index e8858064..9b7e867a 100644 --- a/src/gpgme-json.c +++ b/src/gpgme-json.c @@ -1445,6 +1445,7 @@ static const char hlp_help[] = "help mode. Supported values for \"op\" are:\n\n" " encrypt Encrypt data.\n" " decrypt Decrypt data.\n" + " sign Sign data.\n" " getmore Retrieve remaining data.\n" " help Help overview."; static gpg_error_t -- cgit v1.2.3 From c679ed24778c997fee72d3613babad8680855882 Mon Sep 17 00:00:00 2001 From: Andre Heinecke Date: Thu, 24 May 2018 13:36:31 +0200 Subject: json: Put signature info before data output * src/gpgme-json.c (op_decrypt): Move info before data. -- This should enable it to first parse signatures before handling very large chunks of data. --- src/gpgme-json.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/gpgme-json.c b/src/gpgme-json.c index 9b7e867a..a755500d 100644 --- a/src/gpgme-json.c +++ b/src/gpgme-json.c @@ -1144,15 +1144,6 @@ op_decrypt (cjson_t request, cjson_t result) if (decrypt_result->is_mime) xjson_AddBoolToObject (result, "mime", 1); - err = make_data_object (result, output, chunksize, "plaintext", -1); - output = NULL; - - if (err) - { - error_object (result, "Plaintext output failed: %s", gpg_strerror (err)); - goto leave; - } - verify_result = gpgme_op_verify_result (ctx); if (verify_result && verify_result->signatures) { @@ -1165,6 +1156,15 @@ op_decrypt (cjson_t request, cjson_t result) goto leave; } + err = make_data_object (result, output, chunksize, "plaintext", -1); + output = NULL; + + if (err) + { + error_object (result, "Plaintext output failed: %s", gpg_strerror (err)); + goto leave; + } + leave: release_context (ctx); gpgme_data_release (input); -- cgit v1.2.3