aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS7
-rw-r--r--doc/gpgme.texi2
-rw-r--r--lang/cpp/src/decryptionresult.cpp11
-rw-r--r--lang/cpp/src/decryptionresult.h4
-rw-r--r--lang/python/docs/GPGMEpythonHOWTOen.org17
-rwxr-xr-xlang/python/examples/howto/decrypt-file.py13
-rw-r--r--src/decrypt.c27
-rw-r--r--src/gpgme-json.c395
-rw-r--r--src/w32-util.c25
9 files changed, 479 insertions, 22 deletions
diff --git a/NEWS b/NEWS
index 80c01190..e0adb35b 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,13 @@
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/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 "<algo>.<mode>". Note that old non-MDC encryption mode of
+format "<algo>.<mode>". Note that the deprecated non-MDC encryption mode of
OpenPGP is given as "PGPCFB".
@end table
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::Recipient> 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<DecryptionResult::Recipient> 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;
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 <[email protected]> |
| 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
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);
diff --git a/src/gpgme-json.c b/src/gpgme-json.c
index fb5f149b..a755500d 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. */
@@ -490,7 +500,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)
@@ -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.
@@ -597,11 +788,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
@@ -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;
@@ -952,9 +1144,27 @@ op_decrypt (cjson_t request, cjson_t result)
if (decrypt_result->is_mime)
xjson_AddBoolToObject (result, "mime", 1);
+ 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;
+ }
+
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);
@@ -964,6 +1174,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"
"\n"
@@ -1058,6 +1444,8 @@ 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"
+ " sign Sign data.\n"
" getmore Retrieve remaining data.\n"
" help Help overview.";
static gpg_error_t
@@ -1098,6 +1486,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 }
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);