diff options
Diffstat (limited to 'src/mint/taler-mint-keyup.c')
-rw-r--r-- | src/mint/taler-mint-keyup.c | 861 |
1 files changed, 574 insertions, 287 deletions
diff --git a/src/mint/taler-mint-keyup.c b/src/mint/taler-mint-keyup.c index c4e153476..603154f65 100644 --- a/src/mint/taler-mint-keyup.c +++ b/src/mint/taler-mint-keyup.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014 Christian Grothoff (and other contributing authors) + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) 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 @@ -13,58 +13,140 @@ You should have received a copy of the GNU General Public License along with TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/> */ - /** * @file taler-mint-keyup.c * @brief Update the mint's keys for coins and signatures, * using the mint's offline master key. * @author Florian Dold * @author Benedikt Mueller + * @author Christian Grothoff */ - #include <platform.h> #include <gnunet/gnunet_util_lib.h> #include "taler_util.h" -#include "taler_signatures.h" #include "key_io.h" /** - * FIXME: allow user to specify (within reason). + * When generating filenames from a cryptographic hash, we do not use + * all 512 bits but cut off after this number of characters (in + * base32-encoding). Base32 is 5 bit per character, and given that we + * have very few coin types we hash, at 100 bits the chance of + * collision (by accident over tiny set -- birthday paradox does not + * apply here!) is negligible. */ -#define RSA_KEYSIZE 2048 - #define HASH_CUTOFF 20 /** * Macro to round microseconds to seconds in GNUNET_TIME_* structs. + * + * @param name value to round + * @param field rel_value_us or abs_value_us */ #define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000); GNUNET_NETWORK_STRUCT_BEGIN +/** + * Struct with all of the key information for a kind of coin. Hashed + * to generate a unique directory name per coin type. + */ struct CoinTypeNBO { + /** + * How long can the coin be spend? + */ struct GNUNET_TIME_RelativeNBO duration_spend; + + /** + * How long can the coin be withdrawn (generated)? + */ struct GNUNET_TIME_RelativeNBO duration_withdraw; + + /** + * What is the value of the coin? + */ struct TALER_AmountNBO value; + + /** + * What is the fee charged for withdrawl? + */ struct TALER_AmountNBO fee_withdraw; + + /** + * What is the fee charged for deposits? + */ struct TALER_AmountNBO fee_deposit; + + /** + * What is the fee charged for melting? + */ struct TALER_AmountNBO fee_refresh; + + /** + * Key size in NBO. + */ + uint32_t rsa_keysize; }; GNUNET_NETWORK_STRUCT_END +/** + * Set of all of the parameters that chracterize a coin. + */ struct CoinTypeParams { + + /** + * How long can the coin be spend? Should be significantly + * larger than @e duration_withdraw (i.e. years). + */ struct GNUNET_TIME_Relative duration_spend; + + /** + * How long can the coin be withdrawn (generated)? Should be small + * enough to limit how many coins will be signed into existence with + * the same key, but large enough to still provide a reasonable + * anonymity set. + */ struct GNUNET_TIME_Relative duration_withdraw; + + /** + * How much should coin creation (@e duration_withdraw) duration + * overlap with the next coin? Basically, the starting time of two + * coins is always @e duration_withdraw - @e duration_overlap apart. + */ struct GNUNET_TIME_Relative duration_overlap; + + /** + * What is the value of the coin? + */ struct TALER_Amount value; + + /** + * What is the fee charged for withdrawl? + */ struct TALER_Amount fee_withdraw; + + /** + * What is the fee charged for deposits? + */ struct TALER_Amount fee_deposit; + + /** + * What is the fee charged for melting? + */ struct TALER_Amount fee_refresh; + + /** + * Time at which this coin is supposed to become valid. + */ struct GNUNET_TIME_Absolute anchor; + + /** + * Length of the RSA key in bits. + */ + uint32_t rsa_keysize; }; @@ -97,12 +179,12 @@ static struct GNUNET_TIME_Absolute now; /** * Master private key of the mint. */ -static struct GNUNET_CRYPTO_EddsaPrivateKey *master_priv; +static struct TALER_MasterPrivateKey master_priv; /** * Master public key of the mint. */ -static struct GNUNET_CRYPTO_EddsaPublicKey *master_pub; +static struct TALER_MasterPublicKey master_pub; /** * Until what time do we provide keys? @@ -110,152 +192,183 @@ static struct GNUNET_CRYPTO_EddsaPublicKey *master_pub; static struct GNUNET_TIME_Absolute lookahead_sign_stamp; -static int -config_get_denom (const char *section, const char *option, struct TALER_Amount *denom) -{ - char *str; - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg, section, option, &str)) - return GNUNET_NO; - if (GNUNET_OK != TALER_string_to_amount (str, denom)) - return GNUNET_SYSERR; - return GNUNET_OK; -} - - -static char * -get_signkey_dir () -{ - char *dir; - size_t len; - len = GNUNET_asprintf (&dir, ("%s" DIR_SEPARATOR_STR DIR_SIGNKEYS), mintdir); - GNUNET_assert (len > 0); - return dir; -} - - -static char * +/** + * Obtain the name of the directory we use to store signing + * keys created at time @a start. + * + * @param start time at which we create the signing key + * @return name of the directory we should use, basically "$MINTDIR/$TIME/"; + * (valid until next call to this function) + */ +static const char * get_signkey_file (struct GNUNET_TIME_Absolute start) { - char *dir; - size_t len; - len = GNUNET_asprintf (&dir, ("%s" DIR_SEPARATOR_STR DIR_SIGNKEYS DIR_SEPARATOR_STR "%llu"), - mintdir, (long long) start.abs_value_us); - GNUNET_assert (len > 0); + static char dir[4096]; + + GNUNET_snprintf (dir, + sizeof (dir), + "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS DIR_SEPARATOR_STR "%llu", + mintdir, + (unsigned long long) start.abs_value_us); return dir; } /** - * 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 anchor, overlap). + * 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 + * anchor, overlap). + * + * @param p coin parameters to convert to a hash + * @param hash[OUT] set to the hash matching @a p */ static void -hash_coin_type (const struct CoinTypeParams *p, struct GNUNET_HashCode *hash) +hash_coin_type (const struct CoinTypeParams *p, + struct GNUNET_HashCode *hash) { struct CoinTypeNBO p_nbo; - memset (&p_nbo, 0, sizeof (struct CoinTypeNBO)); - + memset (&p_nbo, + 0, + sizeof (struct CoinTypeNBO)); p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend); p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw); - p_nbo.value = TALER_amount_hton (p->value); - p_nbo.fee_withdraw = TALER_amount_hton (p->fee_withdraw); - p_nbo.fee_deposit = TALER_amount_hton (p->fee_deposit); - p_nbo.fee_refresh = TALER_amount_hton (p->fee_refresh); - - GNUNET_CRYPTO_hash (&p_nbo, sizeof (struct CoinTypeNBO), hash); + TALER_amount_hton (&p_nbo.value, + &p->value); + TALER_amount_hton (&p_nbo.fee_withdraw, + &p->fee_withdraw); + TALER_amount_hton (&p_nbo.fee_deposit, + &p->fee_deposit); + TALER_amount_hton (&p_nbo.fee_refresh, + &p->fee_refresh); + p_nbo.rsa_keysize = htonl (p->rsa_keysize); + GNUNET_CRYPTO_hash (&p_nbo, + sizeof (struct CoinTypeNBO), + hash); } +/** + * Obtain the name of the directory we should use to store coins of + * the given type. The directory name has the format + * "$MINTDIR/$VALUE/$HASH/" where "$VALUE" represents the value of the + * coin and "$HASH" encodes all of the coin's parameters, generating a + * unique string for each type of coin. Note that the "$HASH" + * includes neither the absolute creation time nor the key of the + * coin, thus the files in the subdirectory really just refer to the + * same type of coins, not the same coin. + * + * @param p coin parameters to convert to a directory name + * @return directory name (valid until next call to this function) + */ static const char * get_cointype_dir (const struct CoinTypeParams *p) { static char dir[4096]; - size_t len; struct GNUNET_HashCode hash; char *hash_str; char *val_str; - unsigned int i; + size_t i; hash_coin_type (p, &hash); hash_str = GNUNET_STRINGS_data_to_string_alloc (&hash, sizeof (struct GNUNET_HashCode)); - GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1); GNUNET_assert (NULL != hash_str); + GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1); hash_str[HASH_CUTOFF] = 0; - val_str = TALER_amount_to_string (p->value); + val_str = TALER_amount_to_string (&p->value); for (i = 0; i < strlen (val_str); i++) - if (':' == val_str[i] || '.' == val_str[i]) + if ( (':' == val_str[i]) || + ('.' == val_str[i]) ) val_str[i] = '_'; - len = GNUNET_snprintf (dir, sizeof (dir), - ("%s" DIR_SEPARATOR_STR DIR_DENOMKEYS DIR_SEPARATOR_STR "%s-%s"), - mintdir, val_str, hash_str); - GNUNET_assert (len > 0); + GNUNET_snprintf (dir, + sizeof (dir), + "%s" DIR_SEPARATOR_STR DIR_DENOMKEYS DIR_SEPARATOR_STR "%s-%s", + mintdir, + val_str, + hash_str); GNUNET_free (hash_str); + GNUNET_free (val_str); return dir; } +/** + * Obtain the name of the file we would use to store the key + * information for a coin of the given type @a p and validity + * start time @a start + * + * @param p parameters for the coin + * @param start when would the coin begin to be issued + * @return name of the file to use for this coin + * (valid until next call to this function) + */ static const char * -get_cointype_file (struct CoinTypeParams *p, +get_cointype_file (const struct CoinTypeParams *p, struct GNUNET_TIME_Absolute start) { - const char *dir; static char filename[4096]; - size_t len; + const char *dir; + dir = get_cointype_dir (p); - len = GNUNET_snprintf (filename, sizeof (filename), ("%s" DIR_SEPARATOR_STR "%llu"), - dir, (unsigned long long) start.abs_value_us); - GNUNET_assert (len > 0); + GNUNET_snprintf (filename, + sizeof (filename), + "%s" DIR_SEPARATOR_STR "%llu", + dir, + (unsigned long long) start.abs_value_us); return filename; } /** - * Get the latest key file from the past. + * Get the latest key file from a past run of the key generation + * tool. Used to calculate the starting time for the keys we + * generate during this invocation. This function is used to + * handle both signing keys and coin keys, as in both cases + * the filenames correspond to the timestamps we need. * - * @param cls closure + * @param cls closure, a `struct GNUNET_TIME_Absolute *`, updated + * to contain the highest timestamp (below #now) + * that was found * @param filename complete filename (absolute path) - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to stop iteration with no error, - * #GNUNET_SYSERR to abort iteration with error! + * @return #GNUNET_OK (to continue to iterate) */ static int get_anchor_iter (void *cls, const char *filename) { - struct GNUNET_TIME_Absolute stamp; struct GNUNET_TIME_Absolute *anchor = cls; + struct GNUNET_TIME_Absolute stamp; const char *base; char *end = NULL; base = GNUNET_STRINGS_get_short_name (filename); - stamp.abs_value_us = strtol (base, &end, 10); - + stamp.abs_value_us = strtol (base, + &end, + 10); if ((NULL == end) || (0 != *end)) { - fprintf(stderr, "Ignoring unexpected file '%s'.\n", filename); + fprintf(stderr, + "Ignoring unexpected file `%s'.\n", + filename); return GNUNET_OK; } - - // TODO: check if it's actually a valid key file - - if ((stamp.abs_value_us <= now.abs_value_us) && (stamp.abs_value_us > anchor->abs_value_us)) - *anchor = stamp; - + if (stamp.abs_value_us <= now.abs_value_us) + *anchor = GNUNET_TIME_absolute_max (stamp, + *anchor); return GNUNET_OK; } /** * Get the timestamp where the first new key should be generated. - * Relies on correctly named key files. + * Relies on correctly named key files (as we do not parse them, + * but just look at the filenames to "guess" at their contents). * - * @param dir directory with the signed stuff - * @param duration how long is one key valid? + * @param dir directory that should contain the existing keys + * @param duration how long is one key valid (for signing)? * @param overlap what's the overlap between the keys validity period? * @param[out] anchor the timestamp where the first new key should be generated */ @@ -267,36 +380,57 @@ get_anchor (const char *dir, { GNUNET_assert (0 == duration.rel_value_us % 1000000); GNUNET_assert (0 == overlap.rel_value_us % 1000000); - if (GNUNET_YES != GNUNET_DISK_directory_test (dir, GNUNET_YES)) + if (GNUNET_YES != + GNUNET_DISK_directory_test (dir, + GNUNET_YES)) { *anchor = now; - printf ("Can't look for anchor (%s)\n", dir); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "No existing keys found, starting with fresh key set.\n"); return; } - *anchor = GNUNET_TIME_UNIT_ZERO_ABS; - if (-1 == GNUNET_DISK_directory_scan (dir, &get_anchor_iter, anchor)) + if (-1 == + GNUNET_DISK_directory_scan (dir, + &get_anchor_iter, + anchor)) { *anchor = now; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "No existing keys found, starting with fresh key set.\n"); return; } - if ((GNUNET_TIME_absolute_add (*anchor, duration)).abs_value_us < now.abs_value_us) + /* FIXME: this check is a bit dubious, as 'now' + may be way into the future if we want to generate + many keys... #3727*/ + if ((GNUNET_TIME_absolute_add (*anchor, + duration)).abs_value_us < now.abs_value_us) { - // there's no good anchor, start from now - // (existing keys are too old) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Existing keys are way too old, starting with fresh key set.\n"); *anchor = now; } - else if (anchor->abs_value_us != now.abs_value_us) + else if (anchor->abs_value_us != now.abs_value_us) // Also odd... { - // we have a good anchor - *anchor = GNUNET_TIME_absolute_add (*anchor, duration); - *anchor = GNUNET_TIME_absolute_subtract (*anchor, overlap); + /* Real starting time is the last start time + duration - overlap */ + *anchor = GNUNET_TIME_absolute_add (*anchor, + duration); + *anchor = GNUNET_TIME_absolute_subtract (*anchor, + overlap); } - // anchor is now the stamp where we need to create a new key + /* anchor is now the stamp where we need to create a new key */ } +/** + * Create a mint signing key (for signing mint messages, not for coins) + * and assert its correctness by signing it with the master key. + * + * @param start start time of the validity period for the key + * @param duration how long should the key be valid + * @param pi[OUT] set to the signing key information + */ static void create_signkey_issue_priv (struct GNUNET_TIME_Absolute start, struct GNUNET_TIME_Relative duration, @@ -306,33 +440,32 @@ create_signkey_issue_priv (struct GNUNET_TIME_Absolute start, struct TALER_MINT_SignKeyIssue *issue = &pi->issue; priv = GNUNET_CRYPTO_eddsa_key_create (); - GNUNET_assert (NULL != priv); - pi->signkey_priv = *priv; + pi->signkey_priv.eddsa_priv = *priv; GNUNET_free (priv); - issue->master_pub = *master_pub; + issue->master_pub = master_pub; issue->start = GNUNET_TIME_absolute_hton (start); - issue->expire = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (start, duration)); - - GNUNET_CRYPTO_eddsa_key_get_public (&pi->signkey_priv, &issue->signkey_pub); - + issue->expire = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (start, + duration)); + GNUNET_CRYPTO_eddsa_key_get_public (&pi->signkey_priv.eddsa_priv, + &issue->signkey_pub.eddsa_pub); issue->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNKEY); - issue->purpose.size = htonl (sizeof (struct TALER_MINT_SignKeyIssue) - offsetof (struct TALER_MINT_SignKeyIssue, purpose)); + issue->purpose.size = htonl (sizeof (struct TALER_MINT_SignKeyIssue) - + offsetof (struct TALER_MINT_SignKeyIssue, + purpose)); GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_sign (master_priv, + GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv, &issue->purpose, - &issue->signature)); -} - - -static int -check_signkey_valid (const char *signkey_filename) -{ - // FIXME: do real checks - return GNUNET_OK; + &issue->signature.eddsa_signature)); } +/** + * Generate signing keys starting from the last key found to + * the lookahead time. + * + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ static int mint_keys_update_signkeys () { @@ -340,110 +473,219 @@ mint_keys_update_signkeys () struct GNUNET_TIME_Absolute anchor; char *signkey_dir; - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_keys", "signkey_duration", &signkey_duration)) + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (kcfg, + "mint_keys", + "signkey_duration", + &signkey_duration)) { - fprintf (stderr, "Can't read config value mint_keys.signkey_duration\n"); + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint_keys", + "signkey_duration"); return GNUNET_SYSERR; } - ROUND_TO_SECS (signkey_duration, rel_value_us); - signkey_dir = get_signkey_dir (); - // make sure the directory exists - if (GNUNET_OK != GNUNET_DISK_directory_create (signkey_dir)) + ROUND_TO_SECS (signkey_duration, + rel_value_us); + GNUNET_asprintf (&signkey_dir, + "%s" DIR_SEPARATOR_STR DIR_SIGNKEYS, + mintdir); + /* make sure the directory exists */ + if (GNUNET_OK != + GNUNET_DISK_directory_create (signkey_dir)) { - fprintf (stderr, "Cant create signkey dir\n"); + fprintf (stderr, + "Failed to create signing key directory\n"); return GNUNET_SYSERR; } - get_anchor (signkey_dir, signkey_duration, GNUNET_TIME_UNIT_ZERO, &anchor); + get_anchor (signkey_dir, + signkey_duration, + GNUNET_TIME_UNIT_ZERO /* no overlap for signing keys */, + &anchor); + + while (anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) + { + const char *skf; + struct TALER_MINT_SignKeyIssuePriv signkey_issue; + ssize_t nwrite; - while (anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) { - char *skf; skf = get_signkey_file (anchor); - if (GNUNET_YES != GNUNET_DISK_file_test (skf)) - { - struct TALER_MINT_SignKeyIssuePriv signkey_issue; - ssize_t nwrite; - printf ("Generating signing key for %s.\n", - GNUNET_STRINGS_absolute_time_to_string (anchor)); - create_signkey_issue_priv (anchor, signkey_duration, &signkey_issue); - nwrite = GNUNET_DISK_fn_write (skf, &signkey_issue, sizeof (struct TALER_MINT_SignKeyIssue), - (GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_USER_READ)); - if (nwrite != sizeof (struct TALER_MINT_SignKeyIssue)) - { - fprintf (stderr, "Can't write to file '%s'\n", skf); - return GNUNET_SYSERR; - } - } - else if (GNUNET_OK != check_signkey_valid (skf)) + GNUNET_break (GNUNET_YES != + GNUNET_DISK_file_test (skf)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Generating signing key for %s.\n", + GNUNET_STRINGS_absolute_time_to_string (anchor)); + create_signkey_issue_priv (anchor, + signkey_duration, + &signkey_issue); + nwrite = GNUNET_DISK_fn_write (skf, + &signkey_issue, + sizeof (struct TALER_MINT_SignKeyIssue), + GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_USER_READ); + if (nwrite != sizeof (struct TALER_MINT_SignKeyIssue)) { + fprintf (stderr, + "Failed to write to file `%s': %s\n", + skf, + STRERROR (errno)); return GNUNET_SYSERR; } - anchor = GNUNET_TIME_absolute_add (anchor, signkey_duration); + anchor = GNUNET_TIME_absolute_add (anchor, + signkey_duration); } return GNUNET_OK; } +/** + * Parse configuration for coin type parameters. Also determines + * our anchor by looking at the existing coins of the same type. + * + * @param ct section in the configuration file giving the coin type parameters + * @param params[OUT] set to the coin parameters from the configuration + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid + */ static int -get_cointype_params (const char *ct, struct CoinTypeParams *params) +get_cointype_params (const char *ct, + struct CoinTypeParams *params) { const char *dir; - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_withdraw", ct, ¶ms->duration_withdraw)) + unsigned long long rsa_keysize; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (kcfg, + ct, + "duration_withdraw", + ¶ms->duration_withdraw)) { - fprintf (stderr, "Withdraw duration not given for coin type '%s'\n", ct); + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "duration_withdraw"); return GNUNET_SYSERR; } - ROUND_TO_SECS (params->duration_withdraw, rel_value_us); - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_spend", ct, ¶ms->duration_spend)) + ROUND_TO_SECS (params->duration_withdraw, + rel_value_us); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (kcfg, + ct, + "duration_spend", + ¶ms->duration_spend)) { - fprintf (stderr, "Spend duration not given for coin type '%s'\n", ct); + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "duration_spend"); return GNUNET_SYSERR; } - ROUND_TO_SECS (params->duration_spend, rel_value_us); - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_denom_duration_overlap", ct, ¶ms->duration_overlap)) + ROUND_TO_SECS (params->duration_spend, + rel_value_us); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (kcfg, + ct, + "duration_overlap", + ¶ms->duration_overlap)) { - fprintf (stderr, "Overlap duration not given for coin type '%s'\n", ct); + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "mint_denom_duration_overlap"); return GNUNET_SYSERR; } - ROUND_TO_SECS (params->duration_overlap, rel_value_us); - - if (GNUNET_OK != config_get_denom ("mint_denom_value", ct, ¶ms->value)) + ROUND_TO_SECS (params->duration_overlap, + rel_value_us); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (kcfg, + ct, + "rsa_keysize", + &rsa_keysize)) { - fprintf (stderr, "Value not given for coin type '%s'\n", ct); + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "rsa_keysize"); return GNUNET_SYSERR; } - - if (GNUNET_OK != config_get_denom ("mint_denom_fee_withdraw", ct, ¶ms->fee_withdraw)) + if ( (rsa_keysize > 4 * 2048) || + (rsa_keysize < 1024) ) { - fprintf (stderr, "Withdraw fee not given for coin type '%s'\n", ct); + fprintf (stderr, + "Given RSA keysize %llu outside of permitted range\n", + rsa_keysize); return GNUNET_SYSERR; } - - if (GNUNET_OK != config_get_denom ("mint_denom_fee_deposit", ct, ¶ms->fee_deposit)) + params->rsa_keysize = (unsigned int) rsa_keysize; + if (GNUNET_OK != + TALER_config_get_denom (kcfg, + ct, + "value", + ¶ms->value)) { - fprintf (stderr, "Deposit fee not given for coin type '%s'\n", ct); + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "value"); return GNUNET_SYSERR; } - - if (GNUNET_OK != config_get_denom ("mint_denom_fee_refresh", ct, ¶ms->fee_refresh)) + if (GNUNET_OK != + TALER_config_get_denom (kcfg, + ct, + "fee_withdraw", + ¶ms->fee_withdraw)) { - fprintf (stderr, "Deposit fee not given for coin type '%s'\n", ct); + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "fee_withdraw"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_config_get_denom (kcfg, + ct, + "fee_deposit", + ¶ms->fee_deposit)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "fee_deposit"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_config_get_denom (kcfg, + ct, + "fee_refresh", + ¶ms->fee_refresh)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + ct, + "fee_refresh"); return GNUNET_SYSERR; } dir = get_cointype_dir (params); - get_anchor (dir, params->duration_spend, params->duration_overlap, ¶ms->anchor); + get_anchor (dir, + params->duration_spend, + params->duration_overlap, + ¶ms->anchor); return GNUNET_OK; } +/** + * Initialize the private and public key information structure for + * signing coins into existence. Generates the private signing key + * and signes it together with the coin's meta data using the master + * signing key. + * + * @param params parameters used to initialize the @a dki + * @param dki[OUT] initialized according to @a params + */ static void -create_denomkey_issue (struct CoinTypeParams *params, +create_denomkey_issue (const struct CoinTypeParams *params, struct TALER_MINT_DenomKeyIssuePriv *dki) { - GNUNET_assert (NULL != (dki->denom_priv = GNUNET_CRYPTO_rsa_private_key_create (RSA_KEYSIZE))); - dki->issue.denom_pub = GNUNET_CRYPTO_rsa_private_key_get_public (dki->denom_priv); - dki->issue.master = *master_pub; + dki->denom_priv.rsa_private_key + = GNUNET_CRYPTO_rsa_private_key_create (params->rsa_keysize); + GNUNET_assert (NULL != dki->denom_priv.rsa_private_key); + dki->denom_pub.rsa_public_key + = GNUNET_CRYPTO_rsa_private_key_get_public (dki->denom_priv.rsa_private_key); + GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key, + &dki->issue.denom_hash); + dki->issue.master = master_pub; dki->issue.start = GNUNET_TIME_absolute_hton (params->anchor); dki->issue.expire_withdraw = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor, @@ -451,137 +693,125 @@ create_denomkey_issue (struct CoinTypeParams *params, dki->issue.expire_spend = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor, params->duration_spend)); - dki->issue.value = TALER_amount_hton (params->value); - dki->issue.fee_withdraw = TALER_amount_hton (params->fee_withdraw); - dki->issue.fee_deposit = TALER_amount_hton (params->fee_deposit); - dki->issue.fee_refresh = TALER_amount_hton (params->fee_refresh); - + TALER_amount_hton (&dki->issue.value, + ¶ms->value); + TALER_amount_hton (&dki->issue.fee_withdraw, + ¶ms->fee_withdraw); + TALER_amount_hton (&dki->issue.fee_deposit, + ¶ms->fee_deposit); + TALER_amount_hton (&dki->issue.fee_refresh, + ¶ms->fee_refresh); dki->issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOM); - dki->issue.purpose.size = htonl (sizeof (struct TALER_MINT_DenomKeyIssuePriv) - offsetof (struct TALER_MINT_DenomKeyIssuePriv, issue.purpose)); - + dki->issue.purpose.size = htonl (sizeof (struct TALER_MINT_DenomKeyIssuePriv) - + offsetof (struct TALER_MINT_DenomKeyIssuePriv, + issue.purpose)); GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_sign (master_priv, + GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv, &dki->issue.purpose, - &dki->issue.signature)); -} - - -static int -check_cointype_valid (const char *filename, struct CoinTypeParams *params) -{ - // FIXME: add real checks - return GNUNET_OK; + &dki->issue.signature.eddsa_signature)); } -static int -mint_keys_update_cointype (const char *coin_alias) +/** + * Generate new coin signing keys for the coin type of the given @a + * coin_alias. + * + * @param cls a `int *`, to be set to #GNUNET_SYSERR on failure + * @param coin_alias name of the coin's section in the configuration + */ +static void +mint_keys_update_cointype (void *cls, + const char *coin_alias) { + int *ret = cls; struct CoinTypeParams p; - const char *cointype_dir; - - if (GNUNET_OK != get_cointype_params (coin_alias, &p)) - return GNUNET_SYSERR; - - cointype_dir = get_cointype_dir (&p); - if (GNUNET_OK != GNUNET_DISK_directory_create (cointype_dir)) - return GNUNET_SYSERR; - - while (p.anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) { - const char *dkf; - dkf = get_cointype_file (&p, p.anchor); - - if (GNUNET_YES != GNUNET_DISK_file_test (dkf)) - { - struct TALER_MINT_DenomKeyIssuePriv denomkey_issue; - int ret; - printf ("Generating denomination key for type '%s', start %s.\n", - coin_alias, - GNUNET_STRINGS_absolute_time_to_string (p.anchor)); - printf ("Target path: %s\n", dkf); - create_denomkey_issue (&p, &denomkey_issue); - ret = TALER_MINT_write_denom_key (dkf, &denomkey_issue); - GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv); - if (GNUNET_OK != ret) - { - fprintf (stderr, "Can't write to file '%s'\n", dkf); - return GNUNET_SYSERR; - } - } - else if (GNUNET_OK != check_cointype_valid (dkf, &p)) - { - return GNUNET_SYSERR; - } - p.anchor = GNUNET_TIME_absolute_add (p.anchor, p.duration_spend); - p.anchor = GNUNET_TIME_absolute_subtract (p.anchor, p.duration_overlap); + const char *dkf; + struct TALER_MINT_DenomKeyIssuePriv denomkey_issue; + + if (0 != strncasecmp (coin_alias, + "coin_", + strlen ("coin_"))) + return; /* not a coin definition */ + if (GNUNET_OK != + get_cointype_params (coin_alias, + &p)) + { + *ret = GNUNET_SYSERR; + return; } - return GNUNET_OK; -} - - -static int -mint_keys_update_denomkeys () -{ - char *coin_types; - char *ct; - char *tok_ctx; - - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (kcfg, "mint_keys", "coin_types", &coin_types)) + if (GNUNET_OK != + GNUNET_DISK_directory_create (get_cointype_dir (&p))) { - fprintf (stderr, "mint_keys.coin_types not in configuration\n"); - return GNUNET_SYSERR; + *ret = GNUNET_SYSERR; + return; } - for (ct = strtok_r (coin_types, " ", &tok_ctx); - ct != NULL; - ct = strtok_r (NULL, " ", &tok_ctx)) + while (p.anchor.abs_value_us < lookahead_sign_stamp.abs_value_us) { - if (GNUNET_OK != mint_keys_update_cointype (ct)) + dkf = get_cointype_file (&p, + p.anchor); + GNUNET_break (GNUNET_YES != GNUNET_DISK_file_test (dkf)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Generating denomination key for type `%s', start %s at %s\n", + coin_alias, + GNUNET_STRINGS_absolute_time_to_string (p.anchor), + dkf); + create_denomkey_issue (&p, + &denomkey_issue); + if (GNUNET_OK != + TALER_MINT_write_denom_key (dkf, + &denomkey_issue)) { - GNUNET_free (coin_types); - return GNUNET_SYSERR; + fprintf (stderr, + "Failed to write denomination key information to file `%s'.\n", + dkf); + *ret = GNUNET_SYSERR; + GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key); + return; } + GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key); + p.anchor = GNUNET_TIME_absolute_add (p.anchor, + p.duration_spend); + p.anchor = GNUNET_TIME_absolute_subtract (p.anchor, + p.duration_overlap); } - GNUNET_free (coin_types); - return GNUNET_OK; } +/** + * Update all of the denomination keys of the mint. + * + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ static int -mint_keys_update () +mint_keys_update_denomkeys () { - int ret; - struct GNUNET_TIME_Relative lookahead_sign; - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (kcfg, "mint_keys", "lookahead_sign", &lookahead_sign)) - { - fprintf (stderr, "mint_keys.lookahead_sign not found\n"); - return GNUNET_SYSERR; - } - if (lookahead_sign.rel_value_us == 0) - { - fprintf (stderr, "mint_keys.lookahead_sign must not be zero\n"); - return GNUNET_SYSERR; - } - ROUND_TO_SECS (lookahead_sign, rel_value_us); - lookahead_sign_stamp = GNUNET_TIME_absolute_add (now, lookahead_sign); + int ok; - ret = mint_keys_update_signkeys (); - if (GNUNET_OK != ret) - return GNUNET_SYSERR; - - return mint_keys_update_denomkeys (); + ok = GNUNET_OK; + GNUNET_CONFIGURATION_iterate_sections (kcfg, + &mint_keys_update_cointype, + &ok); + return ok; } /** - * The main function of the keyup tool + * The main function of the taler-mint-keyup tool. This tool is used + * to create the signing and denomination keys for the mint. It uses + * the long-term offline private key and writes the (additional) key + * files to the respective mint directory (from where they can then be + * copied to the online server). Note that we need (at least) the + * most recent generated previous keys so as to align the validity + * periods. * * @param argc number of arguments from the command line * @param argv command line arguments * @return 0 ok, 1 on error */ int -main (int argc, char *const *argv) +main (int argc, + char *const *argv) { static const struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"), @@ -596,22 +826,33 @@ main (int argc, char *const *argv) &GNUNET_GETOPT_set_string, &pretend_time_str}, GNUNET_GETOPT_OPTION_END }; + struct GNUNET_TIME_Relative lookahead_sign; + struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv; - GNUNET_assert (GNUNET_OK == GNUNET_log_setup ("taler-mint-keyup", "WARNING", NULL)); + GNUNET_assert (GNUNET_OK == + GNUNET_log_setup ("taler-mint-keyup", + "WARNING", + NULL)); - if (GNUNET_GETOPT_run ("taler-mint-keyup", options, argc, argv) < 0) + if (GNUNET_GETOPT_run ("taler-mint-keyup", + options, + argc, argv) < 0) return 1; if (NULL == mintdir) { - fprintf (stderr, "mint directory not given\n"); + fprintf (stderr, + "Mint directory not given\n"); return 1; } - if (NULL != pretend_time_str) { - if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_absolute (pretend_time_str, &now)) + if (GNUNET_OK != + GNUNET_STRINGS_fancy_time_to_absolute (pretend_time_str, + &now)) { - fprintf (stderr, "timestamp invalid\n"); + fprintf (stderr, + "timestamp `%s' invalid\n", + pretend_time_str); return 1; } } @@ -624,44 +865,90 @@ main (int argc, char *const *argv) kcfg = TALER_config_load (mintdir); if (NULL == kcfg) { - fprintf (stderr, "can't load mint configuration\n"); + fprintf (stderr, + "Failed to load mint configuration\n"); return 1; } - if (NULL == masterkeyfile) { - fprintf (stderr, "master key file not given\n"); + fprintf (stderr, + "Master key file not given\n"); return 1; } - master_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile); - if (NULL == master_priv) + eddsa_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile); + if (NULL == eddsa_priv) { - fprintf (stderr, "master key invalid\n"); + fprintf (stderr, + "Failed to initialize master key from file `%s'\n", + masterkeyfile); return 1; } + master_priv.eddsa_priv = *eddsa_priv; + GNUNET_free (eddsa_priv); + GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv, + &master_pub.eddsa_pub); - master_pub = GNUNET_new (struct GNUNET_CRYPTO_EddsaPublicKey); - GNUNET_CRYPTO_eddsa_key_get_public (master_priv, master_pub); - - // check if key from file matches the one from the configuration + /* check if key from file matches the one from the configuration */ { struct GNUNET_CRYPTO_EddsaPublicKey master_pub_from_cfg; + if (GNUNET_OK != - GNUNET_CONFIGURATION_get_data (kcfg, "mint", "master_pub", + GNUNET_CONFIGURATION_get_data (kcfg, + "mint", + "master_pub", &master_pub_from_cfg, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) { - fprintf (stderr, "master key missing in configuration (mint.master_pub)\n"); + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint", + "master_pub"); return 1; } - if (0 != memcmp (master_pub, &master_pub_from_cfg, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) + if (0 != + memcmp (&master_pub, + &master_pub_from_cfg, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) { - fprintf (stderr, "Mismatch between key from mint configuration and master private key file from command line.\n"); + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "mint", + "master_pub", + _("does not match with private key")); return 1; } } - if (GNUNET_OK != mint_keys_update ()) + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (kcfg, + "mint_keys", + "lookahead_sign", + &lookahead_sign)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint_keys", + "lookahead_sign"); + return GNUNET_SYSERR; + } + if (0 == lookahead_sign.rel_value_us) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "mint_keys", + "lookahead_sign", + _("must not be zero")); + return GNUNET_SYSERR; + } + ROUND_TO_SECS (lookahead_sign, + rel_value_us); + lookahead_sign_stamp = GNUNET_TIME_absolute_add (now, + lookahead_sign); + + + /* finally, do actual work */ + if (GNUNET_OK != mint_keys_update_signkeys ()) + return 1; + + if (GNUNET_OK != mint_keys_update_denomkeys ()) return 1; return 0; } + +/* end of taler-mint-keyup.c */ |