From 962990c7ceb405639f6e5284234abef473bc07b3 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 7 Mar 2020 12:58:40 +0100 Subject: [PATCH] share master private key verification logic between taler-exchange-wire and taler-exchange-keyup --- src/exchange-tools/Makefile.am | 3 +- src/exchange-tools/key-helper.c | 129 ++++++++++++++++++ src/exchange-tools/taler-exchange-keycheck.c | 21 +-- src/exchange-tools/taler-exchange-keyup.c | 112 ++-------------- src/exchange-tools/taler-exchange-wire.c | 130 ++++++------------- 5 files changed, 194 insertions(+), 201 deletions(-) create mode 100644 src/exchange-tools/key-helper.c diff --git a/src/exchange-tools/Makefile.am b/src/exchange-tools/Makefile.am index c86faf587..9509e20db 100644 --- a/src/exchange-tools/Makefile.am +++ b/src/exchange-tools/Makefile.am @@ -68,4 +68,5 @@ taler_exchange_dbinit_CPPFLAGS = \ EXTRA_DIST = \ - $(pkgcfg_DATA) + $(pkgcfg_DATA) \ + key-helper.c diff --git a/src/exchange-tools/key-helper.c b/src/exchange-tools/key-helper.c new file mode 100644 index 000000000..281202bd7 --- /dev/null +++ b/src/exchange-tools/key-helper.c @@ -0,0 +1,129 @@ +/* + This file is part of TALER + Copyright (C) 2015-2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file key-helper.c + * @brief shared logic between tools that deal with the master private key + * @author Christian Grothoff + */ + +/** + * Extract the @a master_priv from the @a cfg or @a masterkeyfile and + * verify that it matches the master public key given in @a cfg. + * + * @param cfg configuration to use + * @param masterkeyfile master private key filename, can be NULL to use from @a cfg + * @param[out] master_priv where to store the master private key on success + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failures + */ +static int +get_and_check_master_key (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *masterkeyfile, + struct TALER_MasterPrivateKeyP *master_priv) +{ + struct GNUNET_CRYPTO_EddsaPublicKey mpub; + struct GNUNET_CRYPTO_EddsaPublicKey mpub_cfg; + char *fn; + + if (NULL != masterkeyfile) + { + fn = GNUNET_strdup (masterkeyfile); + } + else + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + "exchange", + "MASTER_PRIV_FILE", + &fn)) + { + fprintf (stderr, + "Master private key file given neither in configuration nor on command-line\n"); + return GNUNET_SYSERR; + } + } + if (GNUNET_YES != + GNUNET_DISK_file_test (fn)) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Exchange master private key `%s' does not exist yet, creating it!\n", + fn); + { + struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv; + + eddsa_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (fn); + if (NULL == eddsa_priv) + { + fprintf (stderr, + "Failed to initialize master key from file `%s'\n", + fn); + GNUNET_free (fn); + return GNUNET_SYSERR; + } + master_priv->eddsa_priv = *eddsa_priv; + GNUNET_CRYPTO_eddsa_key_get_public (eddsa_priv, + &mpub); + GNUNET_free (eddsa_priv); + } + + /* Check our key matches that in the configuration */ + { + char *masters; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "exchange", + "MASTER_PUBLIC_KEY", + &masters)) + { + /* Help user by telling them precisely what to fix */ + masters = GNUNET_STRINGS_data_to_string_alloc (&mpub, + sizeof (mpub)); + fprintf (stderr, + "You must set MASTER_PUBLIC_KEY to `%s' in the [exchange] section of the configuration before proceeding.\n", + masters); + GNUNET_free (masters); + GNUNET_free (fn); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (masters, + strlen (masters), + &mpub_cfg, + sizeof (mpub_cfg))) + { + fprintf (stderr, + "MASTER_PUBLIC_KEY value `%s' specified in section [exchange] of the configuration is a valid public key\n", + masters); + GNUNET_free (masters); + GNUNET_free (fn); + return GNUNET_SYSERR; + } + if (0 != GNUNET_memcmp (&mpub, + &mpub_cfg)) + { + fprintf (stderr, + "MASTER_PUBLIC_KEY value `%s' specified in section [exchange] of the configuration does not match our master private key. You can use `gnunet-ecc -p \"%s\"' to determine the correct value.\n", + masters, + fn); + GNUNET_free (masters); + GNUNET_free (fn); + return GNUNET_SYSERR; + } + GNUNET_free (masters); + } + GNUNET_free (fn); + + return GNUNET_OK; +} diff --git a/src/exchange-tools/taler-exchange-keycheck.c b/src/exchange-tools/taler-exchange-keycheck.c index 5094f60c9..7c0785808 100644 --- a/src/exchange-tools/taler-exchange-keycheck.c +++ b/src/exchange-tools/taler-exchange-keycheck.c @@ -95,8 +95,9 @@ signkeys_iter (void *cls, filename); return GNUNET_SYSERR; } - printf ("Signing key `%s' valid\n", - filename); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Signing key `%s' valid\n", + filename); return GNUNET_OK; } @@ -182,10 +183,10 @@ denomkeys_iter (void *cls, alias); return GNUNET_SYSERR; } - printf ("Denomination key `%s' (%s) is valid\n", - alias, - GNUNET_h2s (&hc)); - + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Denomination key `%s' (%s) is valid\n", + alias, + GNUNET_h2s (&hc)); return GNUNET_OK; } @@ -212,7 +213,6 @@ exchange_denomkeys_check () GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "exchange", "master_public_key"); - global_ret = 1; return GNUNET_NO; } if (0 > TALER_EXCHANGEDB_denomination_keys_iterate (exchange_directory, @@ -271,12 +271,17 @@ run (void *cls, * @return 0 ok, 1 on error */ int -main (int argc, char *const *argv) +main (int argc, + char *const *argv) { const struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_GETOPT_OPTION_END }; + /* force linker to link against libtalerutil; if we do + not do this, the linker may "optimize" libtalerutil + away and skip #TALER_OS_init(), which we do need */ + (void) TALER_project_data_default (); GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-exchange-keycheck", "WARNING", diff --git a/src/exchange-tools/taler-exchange-keyup.c b/src/exchange-tools/taler-exchange-keyup.c index 9a845d4e2..75f7b83d1 100644 --- a/src/exchange-tools/taler-exchange-keyup.c +++ b/src/exchange-tools/taler-exchange-keyup.c @@ -242,6 +242,8 @@ static struct GNUNET_HashCode revoke_dkh; static int global_ret; +#include "key-helper.c" + /** * Hash the data defining the coin type. Exclude information that may * not be the same for all instances of the coin type (i.e. the @@ -1166,7 +1168,6 @@ run (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) { struct GNUNET_TIME_Relative lookahead_sign; - struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv; (void) cls; (void) args; @@ -1187,11 +1188,10 @@ run (void *cls, } if (now.abs_value_us != now_tmp.abs_value_us) { - /* The user gave "--now", use it */ + /* The user gave "--now", use it! */ now = now_tmp; } - /* The user _might_ have given "--now" but it matched - * exactly the normal now, so no change required. */ + GNUNET_TIME_round_abs (&now); if (NULL == feedir) { @@ -1216,19 +1216,6 @@ run (void *cls, global_ret = 1; return; } - GNUNET_TIME_round_abs (&now); - if ( (NULL == masterkeyfile) && - (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (kcfg, - "exchange", - "MASTER_PRIV_FILE", - &masterkeyfile)) ) - { - fprintf (stderr, - "Master key file not given in neither configuration nor command-line\n"); - global_ret = 1; - return; - } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (kcfg, "exchange", @@ -1241,66 +1228,16 @@ run (void *cls, global_ret = 1; return; } - if (GNUNET_YES != GNUNET_DISK_file_test (masterkeyfile)) - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Exchange master private key `%s' does not exist yet, creating it!\n", - masterkeyfile); - eddsa_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile); - if (NULL == eddsa_priv) + + if (GNUNET_OK != + get_and_check_master_key (kcfg, + masterkeyfile, + &master_priv)) { - fprintf (stderr, - "Failed to initialize master key from file `%s'\n", - masterkeyfile); global_ret = 1; return; } - master_priv.eddsa_priv = *eddsa_priv; - GNUNET_free (eddsa_priv); - GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv, - &master_public_key.eddsa_pub); - /* Check master public key in configuration matches our - master private key */ - { - char *masters; - struct TALER_MasterPublicKeyP mpub_cfg; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange", - "MASTER_PUBLIC_KEY", - &masters)) - { - fprintf (stderr, - "Master public key option missing in configuration\n"); - global_ret = 1; - return; - } - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (masters, - strlen (masters), - &mpub_cfg, - sizeof (mpub_cfg))) - { - fprintf (stderr, - "Master public key `%s' in configuration is not a valid key\n", - masters); - GNUNET_free (masters); - global_ret = 1; - return; - } - if (0 != GNUNET_memcmp (&master_public_key, - &mpub_cfg)) - { - fprintf (stderr, - "Master public key `%s' in configuration does not match our master private key!\n", - masters); - GNUNET_free (masters); - global_ret = 1; - return; - } - GNUNET_free (masters); - } if (NULL != auditorrequestfile) { auditor_output_file = fopen (auditorrequestfile, @@ -1316,37 +1253,6 @@ run (void *cls, } } - /* check if key from file matches the one from the configuration */ - { - struct TALER_MasterPublicKeyP master_public_key_from_cfg; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_data (kcfg, - "exchange", - "master_public_key", - &master_public_key_from_cfg, - sizeof (struct - GNUNET_CRYPTO_EddsaPublicKey))) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "master_public_key"); - global_ret = 1; - return; - } - if (0 != - GNUNET_memcmp (&master_public_key, - &master_public_key_from_cfg)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "exchange", - "master_public_key", - _ ("does not match with private key")); - global_ret = 1; - return; - } - } - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "exchange", diff --git a/src/exchange-tools/taler-exchange-wire.c b/src/exchange-tools/taler-exchange-wire.c index d69f61ed2..2f6b4ad73 100644 --- a/src/exchange-tools/taler-exchange-wire.c +++ b/src/exchange-tools/taler-exchange-wire.c @@ -44,6 +44,9 @@ static struct TALER_MasterPrivateKeyP master_priv; static int global_ret; +#include "key-helper.c" + + /** * Function called with information about a wire account. Signs * the account's wire details and writes out the JSON file to disk. @@ -55,7 +58,6 @@ static void sign_account_data (void *cls, const struct TALER_EXCHANGEDB_AccountInfo *ai) { - json_t *wire; char *json_out; FILE *out; int ret; @@ -72,20 +74,24 @@ sign_account_data (void *cls, return; } - wire = TALER_JSON_exchange_wire_signature_make (ai->payto_uri, - &master_priv); - if (NULL == wire) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not sign wire account `%s'. Is the URI well-formed?\n", - ai->payto_uri); - global_ret = 1; - return; + json_t *wire; + + wire = TALER_JSON_exchange_wire_signature_make (ai->payto_uri, + &master_priv); + if (NULL == wire) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not sign wire account `%s'. Is the URI well-formed?\n", + ai->payto_uri); + global_ret = 1; + return; + } + GNUNET_assert (NULL != wire); + json_out = json_dumps (wire, + JSON_INDENT (2)); + json_decref (wire); } - GNUNET_assert (NULL != wire); - json_out = json_dumps (wire, - JSON_INDENT (2)); - json_decref (wire); GNUNET_assert (NULL != json_out); if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (ai->wire_response_filename)) @@ -99,11 +105,11 @@ sign_account_data (void *cls, } out = fopen (ai->wire_response_filename, - "w+"); + "w+"); /* create, if exists, truncate */ if (NULL == out) { GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "fopen", + "fopen(w+)", ai->wire_response_filename); global_ret = 1; free (json_out); @@ -112,16 +118,25 @@ sign_account_data (void *cls, ret = fprintf (out, "%s", json_out); - if ( (0 == fclose (out)) && - (-1 != ret) ) - fprintf (stdout, - "Created wire account file `%s'\n", - ai->wire_response_filename); - else + if ( (0 != fclose (out)) || + (-1 == ret) ) + { fprintf (stderr, "Failure creating wire account file `%s': %s\n", ai->wire_response_filename, strerror (errno)); + /* attempt to remove malformed file */ + if (0 != unlink (ai->wire_response_filename)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "unlink", + ai->wire_response_filename); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Created wire account file `%s'\n", + ai->wire_response_filename); + } free (json_out); } @@ -140,86 +155,23 @@ run (void *cls, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { - struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv; - char *masters; - struct GNUNET_CRYPTO_EddsaPublicKey mpub; - struct GNUNET_CRYPTO_EddsaPublicKey mpub_cfg; - (void) cls; (void) args; (void) cfgfile; - if ( (NULL == masterkeyfile) && - (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - "exchange", - "MASTER_PRIV_FILE", - &masterkeyfile)) ) - { - fprintf (stderr, - "Master key file not given in neither configuration nor command-line\n"); - global_ret = 1; - return; - } - if (GNUNET_YES != - GNUNET_DISK_file_test (masterkeyfile)) - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Exchange master private key `%s' does not exist yet, creating it!\n", - masterkeyfile); - eddsa_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile); - if (NULL == eddsa_priv) - { - fprintf (stderr, - "Failed to initialize master key from file `%s'\n", - masterkeyfile); - global_ret = 1; - return; - } - master_priv.eddsa_priv = *eddsa_priv; + if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "exchange", - "MASTER_PUBLIC_KEY", - &masters)) + get_and_check_master_key (cfg, + masterkeyfile, + &master_priv)) { - fprintf (stderr, - "Master public key option missing in configuration\n"); - global_ret = 1; - return; - } - GNUNET_CRYPTO_eddsa_key_get_public (eddsa_priv, - &mpub); - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (masters, - strlen (masters), - &mpub_cfg, - sizeof (mpub_cfg))) - { - fprintf (stderr, - "Master public key `%s' in configuration is not a valid key\n", - masters); - GNUNET_free (masters); - global_ret = 1; - return; - } - if (0 != GNUNET_memcmp (&mpub, - &mpub_cfg)) - { - fprintf (stderr, - "Master public key `%s' in configuration does not match our master private key from `%s'!\n", - masters, - masterkeyfile); - GNUNET_free (masters); global_ret = 1; return; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Signing /wire response with private key matching public key `%s'\n", - masters); - GNUNET_free (masters); + "Signing /wire responses\n"); TALER_EXCHANGEDB_find_accounts (cfg, &sign_account_data, NULL); - GNUNET_free (eddsa_priv); }