exchange/src/mint/taler-mint-keyup.c

952 lines
29 KiB
C
Raw Normal View History

2015-01-08 18:37:20 +01:00
/*
This file is part of TALER
2015-03-17 11:37:21 +01:00
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
2015-01-08 18:37:20 +01:00
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, 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
2015-03-17 11:37:21 +01:00
* @author Christian Grothoff
2015-01-08 18:37:20 +01:00
*/
#include <platform.h>
#include <gnunet/gnunet_util_lib.h>
#include "taler_util.h"
#include "key_io.h"
2015-01-08 18:37:20 +01:00
2015-01-27 14:55:05 +01:00
/**
2015-03-17 11:37:21 +01:00
* 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.
*/
2015-01-08 18:37:20 +01:00
#define HASH_CUTOFF 20
/**
* Macro to round microseconds to seconds in GNUNET_TIME_* structs.
2015-03-17 11:37:21 +01:00
*
* @param name value to round
* @param field rel_value_us or abs_value_us
2015-01-08 18:37:20 +01:00
*/
#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.
*/
2015-01-08 18:37:20 +01:00
struct CoinTypeNBO
{
/**
* How long can the coin be spend?
*/
2015-01-08 18:37:20 +01:00
struct GNUNET_TIME_RelativeNBO duration_spend;
/**
* How long can the coin be withdrawn (generated)?
*/
2015-01-08 18:37:20 +01:00
struct GNUNET_TIME_RelativeNBO duration_withdraw;
/**
* What is the value of the coin?
*/
2015-01-08 18:37:20 +01:00
struct TALER_AmountNBO value;
/**
* What is the fee charged for withdrawl?
*/
2015-01-08 18:37:20 +01:00
struct TALER_AmountNBO fee_withdraw;
/**
* What is the fee charged for deposits?
*/
2015-01-08 18:37:20 +01:00
struct TALER_AmountNBO fee_deposit;
/**
* What is the fee charged for melting?
*/
2015-01-08 18:37:20 +01:00
struct TALER_AmountNBO fee_refresh;
2015-03-17 11:37:21 +01:00
/**
* Key size in NBO.
*/
uint32_t rsa_keysize;
2015-01-08 18:37:20 +01:00
};
GNUNET_NETWORK_STRUCT_END
/**
2015-03-17 11:37:21 +01:00
* Set of all of the parameters that chracterize a coin.
*/
2015-01-08 18:37:20 +01:00
struct CoinTypeParams
{
/**
2015-03-17 11:37:21 +01:00
* How long can the coin be spend? Should be significantly
* larger than @e duration_withdraw (i.e. years).
*/
2015-01-08 18:37:20 +01:00
struct GNUNET_TIME_Relative duration_spend;
/**
2015-03-17 11:37:21 +01:00
* 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.
*/
2015-01-08 18:37:20 +01:00
struct GNUNET_TIME_Relative duration_withdraw;
/**
2015-03-17 11:37:21 +01:00
* 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.
*/
2015-01-08 18:37:20 +01:00
struct GNUNET_TIME_Relative duration_overlap;
/**
2015-03-17 11:37:21 +01:00
* What is the value of the coin?
*/
2015-01-08 18:37:20 +01:00
struct TALER_Amount value;
/**
2015-03-17 11:37:21 +01:00
* What is the fee charged for withdrawl?
*/
2015-01-08 18:37:20 +01:00
struct TALER_Amount fee_withdraw;
/**
2015-03-17 11:37:21 +01:00
* What is the fee charged for deposits?
*/
2015-01-08 18:37:20 +01:00
struct TALER_Amount fee_deposit;
/**
2015-03-17 11:37:21 +01:00
* What is the fee charged for melting?
*/
2015-01-08 18:37:20 +01:00
struct TALER_Amount fee_refresh;
/**
2015-03-17 11:37:21 +01:00
* Time at which this coin is supposed to become valid.
*/
2015-01-08 18:37:20 +01:00
struct GNUNET_TIME_Absolute anchor;
2015-03-17 11:37:21 +01:00
/**
* Length of the RSA key in bits.
*/
uint32_t rsa_keysize;
2015-01-08 18:37:20 +01:00
};
/**
* Filename of the master private key.
*/
static char *masterkeyfile;
/**
* Director of the mint, containing the keys.
*/
static char *mintdir;
/**
* Time to pretend when the key update is executed.
*/
static char *pretend_time_str;
/**
* Handle to the mint's configuration
*/
static struct GNUNET_CONFIGURATION_Handle *kcfg;
/**
* Time when the key update is executed. Either the actual current time, or a
* pretended time.
*/
static struct GNUNET_TIME_Absolute now;
/**
* Master private key of the mint.
*/
static struct GNUNET_CRYPTO_EddsaPrivateKey *master_priv;
/**
* Master public key of the mint.
*/
static struct GNUNET_CRYPTO_EddsaPublicKey *master_pub;
/**
* Until what time do we provide keys?
*/
static struct GNUNET_TIME_Absolute lookahead_sign_stamp;
/**
* 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 *
2015-01-08 18:37:20 +01:00
get_signkey_file (struct GNUNET_TIME_Absolute start)
{
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);
2015-01-08 18:37:20 +01:00
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).
*
* @param p coin parameters to convert to a hash
* @param hash[OUT] set to the hash matching @a p
2015-01-08 18:37:20 +01:00
*/
static void
hash_coin_type (const struct CoinTypeParams *p,
struct GNUNET_HashCode *hash)
2015-01-08 18:37:20 +01:00
{
struct CoinTypeNBO p_nbo;
memset (&p_nbo,
0,
sizeof (struct CoinTypeNBO));
2015-01-08 18:37:20 +01:00
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);
2015-03-17 11:37:21 +01:00
p_nbo.rsa_keysize = htonl (p->rsa_keysize);
GNUNET_CRYPTO_hash (&p_nbo,
sizeof (struct CoinTypeNBO),
hash);
2015-01-08 18:37:20 +01:00
}
/**
* 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)
*/
2015-01-08 18:37:20 +01:00
static const char *
get_cointype_dir (const struct CoinTypeParams *p)
{
static char dir[4096];
struct GNUNET_HashCode hash;
char *hash_str;
char *val_str;
2015-03-17 11:37:21 +01:00
size_t i;
2015-01-08 18:37:20 +01:00
hash_coin_type (p, &hash);
2015-01-28 20:31:28 +01:00
hash_str = GNUNET_STRINGS_data_to_string_alloc (&hash,
sizeof (struct GNUNET_HashCode));
2015-01-08 18:37:20 +01:00
GNUNET_assert (NULL != hash_str);
2015-03-17 11:37:21 +01:00
GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1);
2015-01-08 18:37:20 +01:00
hash_str[HASH_CUTOFF] = 0;
val_str = TALER_amount_to_string (p->value);
for (i = 0; i < strlen (val_str); i++)
2015-03-17 11:37:21 +01:00
if ( (':' == val_str[i]) ||
('.' == val_str[i]) )
2015-01-08 18:37:20 +01:00
val_str[i] = '_';
GNUNET_snprintf (dir,
sizeof (dir),
"%s" DIR_SEPARATOR_STR DIR_DENOMKEYS DIR_SEPARATOR_STR "%s-%s",
mintdir,
val_str,
hash_str);
2015-01-08 18:37:20 +01:00
GNUNET_free (hash_str);
2015-03-17 11:37:21 +01:00
GNUNET_free (val_str);
2015-01-08 18:37:20 +01:00
return dir;
}
/**
2015-03-17 11:37:21 +01:00
* 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
*
2015-03-17 11:37:21 +01:00
* @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)
*/
2015-01-08 18:37:20 +01:00
static const char *
2015-03-17 11:37:21 +01:00
get_cointype_file (const struct CoinTypeParams *p,
2015-01-08 18:37:20 +01:00
struct GNUNET_TIME_Absolute start)
{
static char filename[4096];
2015-03-17 11:37:21 +01:00
const char *dir;
2015-01-08 18:37:20 +01:00
dir = get_cointype_dir (p);
GNUNET_snprintf (filename,
sizeof (filename),
"%s" DIR_SEPARATOR_STR "%llu",
dir,
(unsigned long long) start.abs_value_us);
2015-01-08 18:37:20 +01:00
return filename;
}
/**
2015-03-17 11:37:21 +01:00
* 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.
2015-01-08 18:37:20 +01:00
*
2015-03-17 11:37:21 +01:00
* @param cls closure, a `struct GNUNET_TIME_Absolute *`, updated
* to contain the highest timestamp (below #now)
* that was found
2015-01-08 18:37:20 +01:00
* @param filename complete filename (absolute path)
2015-03-17 11:37:21 +01:00
* @return #GNUNET_OK (to continue to iterate)
2015-01-08 18:37:20 +01:00
*/
static int
get_anchor_iter (void *cls,
const char *filename)
{
struct GNUNET_TIME_Absolute *anchor = cls;
struct GNUNET_TIME_Absolute stamp;
2015-01-08 18:37:20 +01:00
const char *base;
char *end = NULL;
base = GNUNET_STRINGS_get_short_name (filename);
stamp.abs_value_us = strtol (base,
&end,
10);
2015-01-08 18:37:20 +01:00
if ((NULL == end) || (0 != *end))
{
fprintf(stderr,
2015-03-17 11:37:21 +01:00
"Ignoring unexpected file `%s'.\n",
filename);
2015-01-08 18:37:20 +01:00
return GNUNET_OK;
}
2015-03-17 11:37:21 +01:00
// TODO: check if it's actually a valid key file?
if (stamp.abs_value_us <= now.abs_value_us)
*anchor = GNUNET_TIME_absolute_max (stamp,
*anchor);
2015-01-08 18:37:20 +01:00
return GNUNET_OK;
}
/**
* Get the timestamp where the first new key should be generated.
2015-03-17 11:37:21 +01:00
* Relies on correctly named key files (as we do not parse them,
* but just look at the filenames to "guess" at their contents).
2015-01-08 18:37:20 +01:00
*
2015-03-17 11:37:21 +01:00
* @param dir directory that should contain the existing keys
* @param duration how long is one key valid (for signing)?
2015-01-08 18:37:20 +01:00
* @param overlap what's the overlap between the keys validity period?
* @param[out] anchor the timestamp where the first new key should be generated
*/
static void
2015-01-08 18:37:20 +01:00
get_anchor (const char *dir,
struct GNUNET_TIME_Relative duration,
struct GNUNET_TIME_Relative overlap,
struct GNUNET_TIME_Absolute *anchor)
{
GNUNET_assert (0 == duration.rel_value_us % 1000000);
GNUNET_assert (0 == overlap.rel_value_us % 1000000);
if (GNUNET_YES !=
2015-03-17 11:37:21 +01:00
GNUNET_DISK_directory_test (dir,
GNUNET_YES))
2015-01-08 18:37:20 +01:00
{
*anchor = now;
2015-03-17 11:37:21 +01:00
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"No existing keys found, starting with fresh key set.\n");
2015-01-08 18:37:20 +01:00
return;
}
*anchor = GNUNET_TIME_UNIT_ZERO_ABS;
if (-1 ==
GNUNET_DISK_directory_scan (dir,
&get_anchor_iter,
anchor))
2015-01-08 18:37:20 +01:00
{
*anchor = now;
2015-03-17 11:37:21 +01:00
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"No existing keys found, starting with fresh key set.\n");
2015-01-08 18:37:20 +01:00
return;
}
2015-03-17 11:37:21 +01:00
/* FIXME: this check is a bit dubious, as 'now'
may be way into the future if we want to generate
many keys... */
if ((GNUNET_TIME_absolute_add (*anchor,
duration)).abs_value_us < now.abs_value_us)
2015-01-08 18:37:20 +01:00
{
2015-03-17 11:37:21 +01:00
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Existing keys are way too old, starting with fresh key set.\n");
2015-01-08 18:37:20 +01:00
*anchor = now;
}
2015-03-17 11:37:21 +01:00
else if (anchor->abs_value_us != now.abs_value_us) // Also odd...
2015-01-08 18:37:20 +01:00
{
2015-03-17 11:37:21 +01:00
/* Real starting time is the last start time + duration - overlap */
*anchor = GNUNET_TIME_absolute_add (*anchor,
duration);
*anchor = GNUNET_TIME_absolute_subtract (*anchor,
overlap);
2015-01-08 18:37:20 +01:00
}
2015-03-17 11:37:21 +01:00
/* anchor is now the stamp where we need to create a new key */
2015-01-08 18:37:20 +01:00
}
/**
2015-03-17 12:17:42 +01:00
* Create a mint signing key (for signing mint messages, not for coins)
* and assert its correctness by signing it with the master key.
*
2015-03-17 12:17:42 +01:00
* @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
*/
2015-01-08 18:37:20 +01:00
static void
create_signkey_issue_priv (struct GNUNET_TIME_Absolute start,
struct GNUNET_TIME_Relative duration,
struct TALER_MINT_SignKeyIssuePriv *pi)
2015-01-08 18:37:20 +01:00
{
struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
struct TALER_MINT_SignKeyIssue *issue = &pi->issue;
2015-01-08 18:37:20 +01:00
priv = GNUNET_CRYPTO_eddsa_key_create ();
pi->signkey_priv = *priv;
2015-01-08 18:37:20 +01:00
GNUNET_free (priv);
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);
2015-01-08 18:37:20 +01:00
issue->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNKEY);
issue->purpose.size = htonl (sizeof (struct TALER_MINT_SignKeyIssue) -
offsetof (struct TALER_MINT_SignKeyIssue,
purpose));
2015-01-08 18:37:20 +01:00
2015-03-09 11:24:35 +01:00
GNUNET_assert (GNUNET_OK ==
GNUNET_CRYPTO_eddsa_sign (master_priv,
&issue->purpose,
&issue->signature));
2015-01-08 18:37:20 +01:00
}
/**
*
*
2015-03-17 11:37:21 +01:00
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/
static int
2015-01-08 18:37:20 +01:00
mint_keys_update_signkeys ()
{
struct GNUNET_TIME_Relative signkey_duration;
struct GNUNET_TIME_Absolute anchor;
char *signkey_dir;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (kcfg,
"mint_keys",
"signkey_duration",
&signkey_duration))
2015-01-08 18:37:20 +01:00
{
2015-03-17 12:17:42 +01:00
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"mint_keys",
"signkey_duration");
2015-01-08 18:37:20 +01:00
return GNUNET_SYSERR;
}
ROUND_TO_SECS (signkey_duration,
rel_value_us);
GNUNET_asprintf (&signkey_dir,
"%s" DIR_SEPARATOR_STR DIR_SIGNKEYS,
mintdir);
2015-01-08 18:37:20 +01:00
// make sure the directory exists
if (GNUNET_OK !=
GNUNET_DISK_directory_create (signkey_dir))
2015-01-08 18:37:20 +01:00
{
fprintf (stderr,
"Failed to create signing key directory\n");
2015-01-08 18:37:20 +01:00
return GNUNET_SYSERR;
}
get_anchor (signkey_dir,
signkey_duration,
GNUNET_TIME_UNIT_ZERO,
&anchor);
2015-01-08 18:37:20 +01:00
while (anchor.abs_value_us < lookahead_sign_stamp.abs_value_us)
{
const char *skf;
2015-01-08 18:37:20 +01:00
skf = get_signkey_file (anchor);
if (GNUNET_YES !=
GNUNET_DISK_file_test (skf))
2015-01-08 18:37:20 +01:00
{
struct TALER_MINT_SignKeyIssuePriv signkey_issue;
2015-01-08 18:37:20 +01:00
ssize_t nwrite;
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);
2015-01-08 18:37:20 +01:00
if (nwrite != sizeof (struct TALER_MINT_SignKeyIssue))
{
fprintf (stderr,
"Failed to write to file `%s': %s\n",
skf,
STRERROR (errno));
2015-01-08 18:37:20 +01:00
return GNUNET_SYSERR;
}
}
2015-03-17 11:37:21 +01:00
anchor = GNUNET_TIME_absolute_add (anchor,
signkey_duration);
2015-01-08 18:37:20 +01:00
}
return GNUNET_OK;
}
/**
2015-03-17 11:37:21 +01:00
* Parse configuration for coin type parameters. Also determines
* our anchor by looking at the existing coins of the same type.
*
2015-03-17 11:37:21 +01:00
* @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)
2015-01-08 18:37:20 +01:00
{
const char *dir;
2015-03-17 11:37:21 +01:00
unsigned long long rsa_keysize;
2015-03-17 12:17:42 +01:00
/* FIXME: is 'ct' option or section name? */
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (kcfg,
"mint_denom_duration_withdraw",
ct,
&params->duration_withdraw))
2015-01-08 18:37:20 +01:00
{
2015-03-17 12:17:42 +01:00
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
ct,
"mint_denom_duration_withdraw");
2015-01-08 18:37:20 +01:00
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,
&params->duration_spend))
2015-01-08 18:37:20 +01:00
{
2015-03-17 12:17:42 +01:00
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
ct,
"mint_denom_duration_spend");
2015-01-08 18:37:20 +01:00
return GNUNET_SYSERR;
}
2015-03-17 11:37:21 +01:00
ROUND_TO_SECS (params->duration_spend,
rel_value_us);
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (kcfg,
"mint_denom_duration_overlap",
ct,
&params->duration_overlap))
2015-01-08 18:37:20 +01:00
{
2015-03-17 12:17:42 +01:00
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
ct,
"mint_denom_duration_overlap");
2015-01-08 18:37:20 +01:00
return GNUNET_SYSERR;
}
ROUND_TO_SECS (params->duration_overlap,
rel_value_us);
2015-03-17 11:37:21 +01:00
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (kcfg,
"mint_denom_rsa_keysize",
ct,
&rsa_keysize))
{
2015-03-17 12:17:42 +01:00
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
ct,
"mint_denom_rsa_keysize");
2015-03-17 11:37:21 +01:00
return GNUNET_SYSERR;
}
if ( (rsa_keysize > 4 * 2048) ||
(rsa_keysize < 1024) )
{
fprintf (stderr,
"Given RSA keysize %llu outside of permitted range\n",
rsa_keysize);
return GNUNET_SYSERR;
}
params->rsa_keysize = (unsigned int) rsa_keysize;
if (GNUNET_OK !=
TALER_config_get_denom (kcfg,
"mint_denom_value",
ct,
&params->value))
2015-01-08 18:37:20 +01:00
{
2015-03-17 12:17:42 +01:00
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
ct,
"mint_denom_value");
2015-01-08 18:37:20 +01:00
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_config_get_denom (kcfg,
"mint_denom_fee_withdraw",
ct,
&params->fee_withdraw))
2015-01-08 18:37:20 +01:00
{
2015-03-17 12:17:42 +01:00
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
ct,
"mint_denom_fee_withdraw");
2015-01-08 18:37:20 +01:00
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_config_get_denom (kcfg,
"mint_denom_fee_deposit",
ct,
&params->fee_deposit))
2015-01-08 18:37:20 +01:00
{
2015-03-17 12:17:42 +01:00
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
ct,
"mint_denom_fee_deposit");
2015-01-08 18:37:20 +01:00
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_config_get_denom (kcfg,
"mint_denom_fee_refresh",
ct,
&params->fee_refresh))
2015-01-08 18:37:20 +01:00
{
2015-03-17 12:17:42 +01:00
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
ct,
"mint_denom_fee_refresh");
2015-01-08 18:37:20 +01:00
return GNUNET_SYSERR;
}
dir = get_cointype_dir (params);
2015-03-17 11:37:21 +01:00
get_anchor (dir,
params->duration_spend,
params->duration_overlap,
&params->anchor);
2015-01-08 18:37:20 +01:00
return GNUNET_OK;
}
/**
2015-03-17 11:37:21 +01:00
* 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.
*
2015-03-17 11:37:21 +01:00
* @param params parameters used to initialize the @a dki
* @param dki[OUT] initialized according to @a params
*/
2015-01-08 18:37:20 +01:00
static void
2015-03-17 11:37:21 +01:00
create_denomkey_issue (const struct CoinTypeParams *params,
struct TALER_MINT_DenomKeyIssuePriv *dki)
2015-01-08 18:37:20 +01:00
{
2015-03-17 11:37:21 +01:00
GNUNET_assert (NULL !=
(dki->denom_priv = GNUNET_CRYPTO_rsa_private_key_create (params->rsa_keysize)));
dki->denom_pub = GNUNET_CRYPTO_rsa_private_key_get_public (dki->denom_priv);
GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub,
&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,
2015-01-08 18:37:20 +01:00
params->duration_withdraw));
dki->issue.expire_spend =
GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
2015-01-08 18:37:20 +01:00
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);
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));
2015-03-09 11:24:35 +01:00
GNUNET_assert (GNUNET_OK ==
GNUNET_CRYPTO_eddsa_sign (master_priv,
&dki->issue.purpose,
&dki->issue.signature));
2015-01-08 18:37:20 +01:00
}
/**
2015-03-17 11:37:21 +01:00
* Generate new coin signing keys for the coin type of the given @a
* coin_alias.
*
2015-03-17 11:37:21 +01:00
* @param coin_alias name of the coin's section in the configuration
* @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
*/
static int
2015-01-08 18:37:20 +01:00
mint_keys_update_cointype (const char *coin_alias)
{
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)
{
2015-01-08 18:37:20 +01:00
const char *dkf;
2015-01-08 18:37:20 +01:00
dkf = get_cointype_file (&p, p.anchor);
if (GNUNET_YES != GNUNET_DISK_file_test (dkf))
{
struct TALER_MINT_DenomKeyIssuePriv denomkey_issue;
2015-01-08 18:37:20 +01:00
int ret;
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);
ret = TALER_MINT_write_denom_key (dkf,
&denomkey_issue);
GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv);
2015-01-08 18:37:20 +01:00
if (GNUNET_OK != ret)
{
fprintf (stderr,
"Failed to write to file `%s'\n",
dkf);
2015-01-08 18:37:20 +01:00
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);
}
return GNUNET_OK;
}
/**
*
*
2015-03-17 12:17:42 +01:00
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/
static int
2015-01-08 18:37:20 +01:00
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))
2015-01-08 18:37:20 +01:00
{
2015-03-17 12:17:42 +01:00
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"mint_keys",
"coin_types");
2015-01-08 18:37:20 +01:00
return GNUNET_SYSERR;
}
for (ct = strtok_r (coin_types, " ", &tok_ctx);
ct != NULL;
ct = strtok_r (NULL, " ", &tok_ctx))
{
if (GNUNET_OK != mint_keys_update_cointype (ct))
{
GNUNET_free (coin_types);
return GNUNET_SYSERR;
}
}
GNUNET_free (coin_types);
return GNUNET_OK;
}
/**
* The main function of the keyup tool
*
* @param argc number of arguments from the command line
* @param argv command line arguments
* @return 0 ok, 1 on error
*/
int
2015-03-17 12:27:26 +01:00
main (int argc,
char *const *argv)
2015-01-08 18:37:20 +01:00
{
static const struct GNUNET_GETOPT_CommandLineOption options[] = {
GNUNET_GETOPT_OPTION_HELP ("gnunet-mint-keyup OPTIONS"),
{'m', "master-key", "FILE",
"master key file (private key)", 1,
&GNUNET_GETOPT_set_filename, &masterkeyfile},
{'d', "mint-dir", "DIR",
"mint directory with keys to update", 1,
&GNUNET_GETOPT_set_filename, &mintdir},
{'t', "time", "TIMESTAMP",
"pretend it is a different time for the update", 0,
&GNUNET_GETOPT_set_string, &pretend_time_str},
GNUNET_GETOPT_OPTION_END
};
2015-03-17 12:27:26 +01:00
struct GNUNET_TIME_Relative lookahead_sign;
2015-01-08 18:37:20 +01:00
GNUNET_assert (GNUNET_OK ==
GNUNET_log_setup ("taler-mint-keyup",
"WARNING",
NULL));
2015-01-08 18:37:20 +01:00
if (GNUNET_GETOPT_run ("taler-mint-keyup",
options,
argc, argv) < 0)
2015-01-08 18:37:20 +01:00
return 1;
if (NULL == mintdir)
{
fprintf (stderr,
2015-03-17 12:27:26 +01:00
"Mint directory not given\n");
2015-01-08 18:37:20 +01:00
return 1;
}
if (NULL != pretend_time_str)
{
if (GNUNET_OK !=
GNUNET_STRINGS_fancy_time_to_absolute (pretend_time_str,
&now))
2015-01-08 18:37:20 +01:00
{
fprintf (stderr,
2015-03-17 12:27:26 +01:00
"timestamp `%s' invalid\n",
pretend_time_str);
2015-01-08 18:37:20 +01:00
return 1;
}
}
else
{
now = GNUNET_TIME_absolute_get ();
}
ROUND_TO_SECS (now, abs_value_us);
kcfg = TALER_config_load (mintdir);
2015-01-08 18:37:20 +01:00
if (NULL == kcfg)
{
fprintf (stderr,
"Failed to load mint configuration\n");
2015-01-08 18:37:20 +01:00
return 1;
}
if (NULL == masterkeyfile)
{
fprintf (stderr,
2015-03-17 12:27:26 +01:00
"Master key file not given\n");
2015-01-08 18:37:20 +01:00
return 1;
}
master_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile);
if (NULL == master_priv)
{
fprintf (stderr,
"Failed to initialize master key from file `%s'\n",
masterkeyfile);
2015-01-08 18:37:20 +01:00
return 1;
}
master_pub = GNUNET_new (struct GNUNET_CRYPTO_EddsaPublicKey);
GNUNET_CRYPTO_eddsa_key_get_public (master_priv,
master_pub);
2015-01-08 18:37:20 +01:00
2015-03-17 12:27:26 +01:00
/* check if key from file matches the one from the configuration */
2015-01-08 18:37:20 +01:00
{
struct GNUNET_CRYPTO_EddsaPublicKey master_pub_from_cfg;
2015-01-27 18:35:17 +01:00
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_data (kcfg,
"mint",
"master_pub",
2015-01-27 18:35:17 +01:00
&master_pub_from_cfg,
sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
2015-01-08 18:37:20 +01:00
{
2015-03-17 12:17:42 +01:00
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"mint",
"master_pub");
2015-01-08 18:37:20 +01:00
return 1;
}
if (0 !=
memcmp (master_pub,
&master_pub_from_cfg,
sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
2015-01-08 18:37:20 +01:00
{
2015-03-17 12:17:42 +01:00
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
"mint",
"master_pub",
_("does not match with private key"));
2015-01-08 18:37:20 +01:00
return 1;
}
}
2015-03-17 12:27:26 +01:00
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 ())
2015-01-08 18:37:20 +01:00
return 1;
return 0;
}
/* end of taler-mint-keyup.c */