From 172505bd0f16f5be18d64e86c4ad7ed01e920c1d Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 28 Jan 2015 20:10:59 +0100 Subject: splitting up /keys and keystate handling --- src/mint/Makefile.am | 3 +- src/mint/taler-mint-httpd_keys.c | 474 ++--------------------------------- src/mint/taler-mint-httpd_keys.h | 107 +------- src/mint/taler-mint-httpd_keystate.c | 459 +++++++++++++++++++++++++++++++++ src/mint/taler-mint-httpd_keystate.h | 129 ++++++++++ 5 files changed, 626 insertions(+), 546 deletions(-) create mode 100644 src/mint/taler-mint-httpd_keystate.c create mode 100644 src/mint/taler-mint-httpd_keystate.h (limited to 'src') diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am index 17b82dc51..e2c44d21f 100644 --- a/src/mint/Makefile.am +++ b/src/mint/Makefile.am @@ -61,9 +61,10 @@ taler_mint_reservemod_LDFLAGS = \ taler_mint_httpd_SOURCES = \ taler-mint-httpd.c \ + taler-mint-httpd_keystate.c taler-mint-httpd_keystate.h \ + taler-mint-httpd_db.c taler-mint-httpd_db.h \ taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \ taler-mint-httpd_responses.c taler-mint-httpd_responses.h \ - taler-mint-httpd_db.c taler-mint-httpd_db.h \ taler-mint-httpd_mhd.c \ taler-mint-httpd_keys.c \ taler-mint-httpd_deposit.c \ diff --git a/src/mint/taler-mint-httpd_keys.c b/src/mint/taler-mint-httpd_keys.c index 359357da9..579112749 100644 --- a/src/mint/taler-mint-httpd_keys.c +++ b/src/mint/taler-mint-httpd_keys.c @@ -19,10 +19,6 @@ * @author Florian Dold * @author Benedikt Mueller * @author Christian Grothoff - * - * TODO: - * - separate key management into a file separate from - * /keys handling (to fit other handlers) */ #include "platform.h" #include @@ -35,373 +31,7 @@ #include "taler_json_lib.h" #include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_keys.h" - - - -/** - * Mint key state. Never use directly, instead access via - * #TALER_MINT_key_state_acquire and #TALER_MINT_key_state_release. - */ -static struct MintKeyState *internal_key_state; - -/** - * Mutex protecting access to #internal_key_state. - */ -static pthread_mutex_t internal_key_state_mutex = PTHREAD_MUTEX_INITIALIZER; - -/** - * Pipe used for signaling reloading of our key state. - */ -static int reload_pipe[2]; - - -/** - * Convert the public part of a denomination key - * issue to a JSON object. - * - * @param dki the denomination key issue - * @return a JSON object describing the denomination key isue (public part) - */ -static json_t * -denom_key_issue_to_json (const struct TALER_MINT_DenomKeyIssue *dki) -{ - char *buf; - size_t buf_len; - json_t *dk_json = json_object (); - - json_object_set_new (dk_json, "master_sig", - TALER_JSON_from_data (&dki->signature, sizeof (struct GNUNET_CRYPTO_EddsaSignature))); - json_object_set_new (dk_json, "stamp_start", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->start))); - json_object_set_new (dk_json, "stamp_expire_withdraw", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_withdraw))); - json_object_set_new (dk_json, "stamp_expire_deposit", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_spend))); - - - buf_len = GNUNET_CRYPTO_rsa_public_key_encode (dki->denom_pub, - &buf); - json_object_set_new (dk_json, "denom_pub", - TALER_JSON_from_data (buf, - buf_len)); - GNUNET_free (buf); - json_object_set_new (dk_json, "value", - TALER_JSON_from_amount (TALER_amount_ntoh (dki->value))); - json_object_set_new (dk_json, - "fee_withdraw", - TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_withdraw))); - json_object_set_new (dk_json, - "fee_deposit", - TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_deposit))); - json_object_set_new (dk_json, - "fee_refresh", - TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_refresh))); - return dk_json; -} - - -/** - * Convert the public part of a sign key - * issue to a JSON object. - * - * @param ski the sign key issue - * @return a JSON object describing the sign key isue (public part) - */ -static json_t * -sign_key_issue_to_json (const struct TALER_MINT_SignKeyIssue *ski) -{ - json_t *sk_json = json_object (); - json_object_set_new (sk_json, "stamp_start", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->start))); - json_object_set_new (sk_json, "stamp_expire", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->expire))); - json_object_set_new (sk_json, "master_sig", - TALER_JSON_from_data (&ski->signature, sizeof (struct GNUNET_CRYPTO_EddsaSignature))); - json_object_set_new (sk_json, "key", - TALER_JSON_from_data (&ski->signkey_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))); - return sk_json; -} - - -/** - * Get the relative time value that describes how - * far in the future do we want to provide coin keys. - * - * @return the provide duration - */ -static struct GNUNET_TIME_Relative -TALER_MINT_conf_duration_provide () -{ - struct GNUNET_TIME_Relative rel; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (cfg, - "mint_keys", - "lookahead_provide", - &rel)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "mint_keys.lookahead_provide not valid or not given\n"); - GNUNET_abort (); - } - return rel; -} - - -/** - * Iterator for denomination keys. - * - * @param cls closure - * @param dki the denomination key issue - * @param alias coin alias - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to stop iteration with no error, - * #GNUNET_SYSERR to abort iteration with error! - */ -static int -reload_keys_denom_iter (void *cls, - const char *alias, - const struct TALER_MINT_DenomKeyIssuePriv *dki) -{ - struct MintKeyState *ctx = cls; - struct GNUNET_TIME_Absolute stamp_provide; - struct GNUNET_HashCode denom_key_hash; - int res; - - stamp_provide = GNUNET_TIME_absolute_add (ctx->reload_time, - TALER_MINT_conf_duration_provide ()); - - if (GNUNET_TIME_absolute_ntoh (dki->issue.expire_spend).abs_value_us < ctx->reload_time.abs_value_us) - { - // this key is expired - return GNUNET_OK; - } - if (GNUNET_TIME_absolute_ntoh (dki->issue.start).abs_value_us > stamp_provide.abs_value_us) - { - // we are to early for this key - return GNUNET_OK; - } - - GNUNET_CRYPTO_hash (&dki->issue.denom_pub, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey), - &denom_key_hash); - - res = GNUNET_CONTAINER_multihashmap_put (ctx->denomkey_map, - &denom_key_hash, - GNUNET_memdup (dki, sizeof (struct TALER_MINT_DenomKeyIssuePriv)), - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); - if (GNUNET_OK != res) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Duplicate denomination key\n"); - - json_array_append_new (ctx->denom_keys_array, - denom_key_issue_to_json (&dki->issue)); - - return GNUNET_OK; -} - - -/** - * Iterator for sign keys. - * - * @param cls closure - * @param ski the sign key issue - * @return #GNUNET_OK to continue to iterate, - * #GNUNET_NO to stop iteration with no error, - * #GNUNET_SYSERR to abort iteration with error! - */ -static int -reload_keys_sign_iter (void *cls, - const struct TALER_MINT_SignKeyIssuePriv *ski) -{ - struct MintKeyState *ctx = cls; - struct GNUNET_TIME_Absolute stamp_provide; - - stamp_provide = GNUNET_TIME_absolute_add (ctx->reload_time, TALER_MINT_conf_duration_provide (cfg)); - - if (GNUNET_TIME_absolute_ntoh (ski->issue.expire).abs_value_us < ctx->reload_time.abs_value_us) - { - // this key is expired - return GNUNET_OK; - } - - if (GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us > stamp_provide.abs_value_us) - { - // we are to early for this key - return GNUNET_OK; - } - - // the signkey is valid for now, check - // if it's more recent than the current one! - if (GNUNET_TIME_absolute_ntoh (ctx->current_sign_key_issue.issue.start).abs_value_us > - GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us) - ctx->current_sign_key_issue = *ski; - - - ctx->next_reload = GNUNET_TIME_absolute_min (ctx->next_reload, - GNUNET_TIME_absolute_ntoh (ski->issue.expire)); - - json_array_append_new (ctx->sign_keys_array, - sign_key_issue_to_json (&ski->issue)); - - return GNUNET_OK; -} - - -/** - * Load the mint's key state from disk. - * - * @return fresh key state (with reference count 1) - */ -static struct MintKeyState * -reload_keys () -{ - struct MintKeyState *key_state; - json_t *keys; - - key_state = GNUNET_new (struct MintKeyState); - key_state->refcnt = 1; - - key_state->next_reload = GNUNET_TIME_UNIT_FOREVER_ABS; - - key_state->denom_keys_array = json_array (); - GNUNET_assert (NULL != key_state->denom_keys_array); - - key_state->sign_keys_array = json_array (); - GNUNET_assert (NULL != key_state->sign_keys_array); - - key_state->denomkey_map = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); - GNUNET_assert (NULL != key_state->denomkey_map); - - key_state->reload_time = GNUNET_TIME_absolute_get (); - - TALER_MINT_denomkeys_iterate (mintdir, &reload_keys_denom_iter, key_state); - TALER_MINT_signkeys_iterate (mintdir, &reload_keys_sign_iter, key_state); - - keys = json_pack ("{s:o, s:o, s:o, s:o}", - "master_pub", TALER_JSON_from_data (&master_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)), - "signkeys", key_state->sign_keys_array, - "denoms", key_state->denom_keys_array, - "list_issue_date", TALER_JSON_from_abs (key_state->reload_time)); - - key_state->keys_json = json_dumps (keys, JSON_INDENT(2)); - - return key_state; -} - - -/** - * Release key state, free if necessary (if reference count gets to zero). - * - * @param key_state the key state to release - */ -void -TALER_MINT_key_state_release (struct MintKeyState *key_state) -{ - GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex)); - GNUNET_assert (0 != key_state->refcnt); - key_state->refcnt += 1; - if (key_state->refcnt == 0) { - GNUNET_free (key_state); - } - GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); -} - - -/** - * Acquire the key state of the mint. Updates keys if necessary. - * For every call to #TALER_MINT_key_state_acquire, a matching call - * to #TALER_MINT_key_state_release must be made. - * - * @return the key state - */ -struct MintKeyState * -TALER_MINT_key_state_acquire (void) -{ - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - struct MintKeyState *key_state; - - // FIXME: the locking we have is very coarse-grained, - // using multiple locks might be nicer ... - - GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex)); - if (NULL == internal_key_state) - { - internal_key_state = reload_keys (); - } - else if (internal_key_state->next_reload.abs_value_us <= now.abs_value_us) - { - GNUNET_assert (0 != internal_key_state->refcnt); - internal_key_state->refcnt--; - if (0 == internal_key_state->refcnt) - GNUNET_free (internal_key_state); - internal_key_state = reload_keys (); - } - key_state = internal_key_state; - key_state->refcnt += 1; - GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); - - return key_state; -} - - -/** - * Look up the issue for a denom public key. - * - * @param key state to look in - * @param denom_pub denomination public key - * @return the denomination key issue, - * or NULL if denom_pub could not be found - */ -struct TALER_MINT_DenomKeyIssuePriv * -TALER_MINT_get_denom_key (const struct MintKeyState *key_state, - const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub) -{ - struct TALER_MINT_DenomKeyIssuePriv *issue; - struct GNUNET_HashCode hash; - char *buf; - size_t buf_len; - - buf_len = GNUNET_CRYPTO_rsa_public_key_encode (denom_pub, - &buf); - GNUNET_CRYPTO_hash (buf, - buf_len, - &hash); - GNUNET_free (buf); - issue = GNUNET_CONTAINER_multihashmap_get (key_state->denomkey_map, &hash); - return issue; -} - - -/** - * Check if a coin is valid; that is, whether the denomination key exists, - * is not expired, and the signature is correct. - * - * @param key_state the key state to use for checking the coin's validity - * @param coin_public_info the coin public info to check for validity - * @return #GNUNET_YES if the coin is valid, - * #GNUNET_NO if it is invalid - * #GNUNET_SYSERROR if an internal error occured - */ -int -TALER_MINT_test_coin_valid (const struct MintKeyState *key_state, - const struct TALER_CoinPublicInfo *coin_public_info) -{ - struct TALER_MINT_DenomKeyIssuePriv *dki; - struct GNUNET_HashCode c_hash; - - dki = TALER_MINT_get_denom_key (key_state, coin_public_info->denom_pub); - if (NULL == dki) - return GNUNET_NO; - /* FIXME: we had envisioned a more complex scheme... */ - GNUNET_CRYPTO_hash (&coin_public_info->coin_pub, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), - &c_hash); - if (GNUNET_OK != - GNUNET_CRYPTO_rsa_verify (&c_hash, - coin_public_info->denom_sig, - dki->issue.denom_pub)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "coin signature is invalid\n"); - return GNUNET_NO; - } - return GNUNET_YES; -} +#include "taler-mint-httpd_keystate.h" /** @@ -448,89 +78,39 @@ TALER_MINT_handler_keys (struct RequestHandler *rh, /** - * Handle a signal, writing relevant signal numbers - * (currently just SIGUSR1) to a pipe. + * Check if a coin is valid; that is, whether the denomination key exists, + * is not expired, and the signature is correct. * - * @param signal_number the signal number - */ -static void -handle_signal (int signal_number) -{ - size_t res; - char c = signal_number; - - if (SIGUSR1 == signal_number) - { - errno = 0; - res = write (reload_pipe[1], &c, 1); - if ((res < 0) && (EINTR != errno)) - { - GNUNET_break (0); - return; - } - if (0 == res) - { - GNUNET_break (0); - return; - } - } -} - - -/** - * Read signals from a pipe in a loop, and reload keys from disk if - * SIGUSR1 is read from the pipe. + * @param key_state the key state to use for checking the coin's validity + * @param coin_public_info the coin public info to check for validity + * @return #GNUNET_YES if the coin is valid, + * #GNUNET_NO if it is invalid + * #GNUNET_SYSERROR if an internal error occured */ int -TALER_MINT_key_reload_loop (void) +TALER_MINT_test_coin_valid (const struct MintKeyState *key_state, + const struct TALER_CoinPublicInfo *coin_public_info) { - struct sigaction act; - - if (0 != pipe (reload_pipe)) - { - fprintf (stderr, - "Failed to create pipe.\n"); - return GNUNET_SYSERR; - } - memset (&act, 0, sizeof (struct sigaction)); - act.sa_handler = &handle_signal; - - if (0 != sigaction (SIGUSR1, &act, NULL)) - { - fprintf (stderr, - "Failed to set signal handler.\n"); - return GNUNET_SYSERR; - } + struct TALER_MINT_DenomKeyIssuePriv *dki; + struct GNUNET_HashCode c_hash; - while (1) + dki = TALER_MINT_get_denom_key (key_state, coin_public_info->denom_pub); + if (NULL == dki) + return GNUNET_NO; + /* FIXME: we had envisioned a more complex scheme... */ + GNUNET_CRYPTO_hash (&coin_public_info->coin_pub, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), + &c_hash); + if (GNUNET_OK != + GNUNET_CRYPTO_rsa_verify (&c_hash, + coin_public_info->denom_sig, + dki->issue.denom_pub)) { - char c; - ssize_t res; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "(re-)loading keys\n"); - GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex)); - if (NULL != internal_key_state) - { - GNUNET_assert (0 != internal_key_state->refcnt); - internal_key_state->refcnt -= 1; - if (0 == internal_key_state->refcnt) - GNUNET_free (internal_key_state); - } - internal_key_state = reload_keys (); - GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); -read_again: - errno = 0; - res = read (reload_pipe[0], &c, 1); - if ((res < 0) && (EINTR != errno)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (EINTR == errno) - goto read_again; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "coin signature is invalid\n"); + return GNUNET_NO; } - return GNUNET_OK; + return GNUNET_YES; } diff --git a/src/mint/taler-mint-httpd_keys.h b/src/mint/taler-mint-httpd_keys.h index eeeb21ef3..7dc5bc0eb 100644 --- a/src/mint/taler-mint-httpd_keys.h +++ b/src/mint/taler-mint-httpd_keys.h @@ -29,88 +29,7 @@ #include "taler-mint-httpd.h" #include "mint.h" #include "key_io.h" - - -/** - * Snapshot of the (coin and signing) - * keys (including private keys) of the mint. - */ -struct MintKeyState -{ - /** - * When did we initiate the key reloading? - */ - struct GNUNET_TIME_Absolute reload_time; - - /** - * JSON array with denomination keys. - */ - json_t *denom_keys_array; - - /** - * JSON array with signing keys. - */ - json_t *sign_keys_array; - - /** - * Mapping from denomination keys to denomination key issue struct. - */ - struct GNUNET_CONTAINER_MultiHashMap *denomkey_map; - - /** - * When is the next key invalid and we have to reload? - */ - struct GNUNET_TIME_Absolute next_reload; - - /** - * Mint signing key that should be used currently. - */ - struct TALER_MINT_SignKeyIssuePriv current_sign_key_issue; - - /** - * Cached JSON text that the mint will send for - * a /keys request. - */ - char *keys_json; - - /** - * Reference count. - */ - unsigned int refcnt; -}; - - -/** - * Release key state, free if necessary (if reference count gets to zero). - * - * @param key_state the key state to release - */ -void -TALER_MINT_key_state_release (struct MintKeyState *key_state); - - -/** - * Acquire the key state of the mint. Updates keys if necessary. - * For every call to #TALER_MINT_key_state_acquire, a matching call - * to #TALER_MINT_key_state_release must be made. - * - * @return the key state - */ -struct MintKeyState * -TALER_MINT_key_state_acquire (void); - - -/** - * Look up the issue for a denom public key. - * - * @param key state to look in - * @param denom_pub denomination public key - * @return the denomination key issue, - * or NULL if denom_pub could not be found - */ -struct TALER_MINT_DenomKeyIssuePriv * -TALER_MINT_get_denom_key (const struct MintKeyState *key_state, - const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub); +#include "taler-mint-httpd_keystate.h" /** @@ -129,13 +48,16 @@ TALER_MINT_test_coin_valid (const struct MintKeyState *key_state, /** - * Read signals from a pipe in a loop, and reload keys from disk if - * SIGUSR1 is read from the pipe. + * Sign the message in @a purpose with the mint's signing + * key. * - * @return #GNUNET_OK if we terminated normally, #GNUNET_SYSERR on error + * @param purpose the message to sign + * @param[OUT] sig signature over purpose using current signing key */ -int -TALER_MINT_key_reload_loop (void); +void +TALER_MINT_keys_sign (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct GNUNET_CRYPTO_EddsaSignature *sig); + /** @@ -156,16 +78,5 @@ TALER_MINT_handler_keys (struct RequestHandler *rh, size_t *upload_data_size); -/** - * Sign the message in @a purpose with the mint's signing - * key. - * - * @param purpose the message to sign - * @param[OUT] sig signature over purpose using current signing key - */ -void -TALER_MINT_keys_sign (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, - struct GNUNET_CRYPTO_EddsaSignature *sig); - #endif diff --git a/src/mint/taler-mint-httpd_keystate.c b/src/mint/taler-mint-httpd_keystate.c new file mode 100644 index 000000000..dd1855889 --- /dev/null +++ b/src/mint/taler-mint-httpd_keystate.c @@ -0,0 +1,459 @@ +/* + This file is part of TALER + (C) 2014 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file taler-mint-httpd_keystate.c + * @brief management of our coin signing keys + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "mint.h" +#include "taler_signatures.h" +#include "taler-mint-httpd_keystate.h" +#include "taler_json_lib.h" +#include "taler-mint-httpd_parsing.h" + + +/** + * Mint key state. Never use directly, instead access via + * #TALER_MINT_key_state_acquire and #TALER_MINT_key_state_release. + */ +static struct MintKeyState *internal_key_state; + +/** + * Mutex protecting access to #internal_key_state. + */ +static pthread_mutex_t internal_key_state_mutex = PTHREAD_MUTEX_INITIALIZER; + +/** + * Pipe used for signaling reloading of our key state. + */ +static int reload_pipe[2]; + + +/** + * Convert the public part of a denomination key + * issue to a JSON object. + * + * @param dki the denomination key issue + * @return a JSON object describing the denomination key isue (public part) + */ +static json_t * +denom_key_issue_to_json (const struct TALER_MINT_DenomKeyIssue *dki) +{ + char *buf; + size_t buf_len; + json_t *dk_json = json_object (); + + json_object_set_new (dk_json, "master_sig", + TALER_JSON_from_data (&dki->signature, sizeof (struct GNUNET_CRYPTO_EddsaSignature))); + json_object_set_new (dk_json, "stamp_start", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->start))); + json_object_set_new (dk_json, "stamp_expire_withdraw", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_withdraw))); + json_object_set_new (dk_json, "stamp_expire_deposit", TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (dki->expire_spend))); + + + buf_len = GNUNET_CRYPTO_rsa_public_key_encode (dki->denom_pub, + &buf); + json_object_set_new (dk_json, "denom_pub", + TALER_JSON_from_data (buf, + buf_len)); + GNUNET_free (buf); + json_object_set_new (dk_json, "value", + TALER_JSON_from_amount (TALER_amount_ntoh (dki->value))); + json_object_set_new (dk_json, + "fee_withdraw", + TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_withdraw))); + json_object_set_new (dk_json, + "fee_deposit", + TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_deposit))); + json_object_set_new (dk_json, + "fee_refresh", + TALER_JSON_from_amount(TALER_amount_ntoh (dki->fee_refresh))); + return dk_json; +} + + +/** + * Convert the public part of a sign key + * issue to a JSON object. + * + * @param ski the sign key issue + * @return a JSON object describing the sign key isue (public part) + */ +static json_t * +sign_key_issue_to_json (const struct TALER_MINT_SignKeyIssue *ski) +{ + json_t *sk_json = json_object (); + + json_object_set_new (sk_json, + "stamp_start", + TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->start))); + json_object_set_new (sk_json, + "stamp_expire", + TALER_JSON_from_abs (GNUNET_TIME_absolute_ntoh (ski->expire))); + json_object_set_new (sk_json, + "master_sig", + TALER_JSON_from_data (&ski->signature, + sizeof (struct GNUNET_CRYPTO_EddsaSignature))); + json_object_set_new (sk_json, "key", + TALER_JSON_from_data (&ski->signkey_pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))); + return sk_json; +} + + +/** + * Get the relative time value that describes how + * far in the future do we want to provide coin keys. + * + * @return the provide duration + */ +static struct GNUNET_TIME_Relative +TALER_MINT_conf_duration_provide () +{ + struct GNUNET_TIME_Relative rel; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (cfg, + "mint_keys", + "lookahead_provide", + &rel)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "mint_keys.lookahead_provide not valid or not given\n"); + GNUNET_abort (); + } + return rel; +} + + +/** + * Iterator for denomination keys. + * + * @param cls closure + * @param dki the denomination key issue + * @param alias coin alias + * @return #GNUNET_OK to continue to iterate, + * #GNUNET_NO to stop iteration with no error, + * #GNUNET_SYSERR to abort iteration with error! + */ +static int +reload_keys_denom_iter (void *cls, + const char *alias, + const struct TALER_MINT_DenomKeyIssuePriv *dki) +{ + struct MintKeyState *ctx = cls; + struct GNUNET_TIME_Absolute stamp_provide; + struct GNUNET_HashCode denom_key_hash; + int res; + + stamp_provide = GNUNET_TIME_absolute_add (ctx->reload_time, + TALER_MINT_conf_duration_provide ()); + + if (GNUNET_TIME_absolute_ntoh (dki->issue.expire_spend).abs_value_us < ctx->reload_time.abs_value_us) + { + // this key is expired + return GNUNET_OK; + } + if (GNUNET_TIME_absolute_ntoh (dki->issue.start).abs_value_us > stamp_provide.abs_value_us) + { + // we are to early for this key + return GNUNET_OK; + } + + GNUNET_CRYPTO_hash (&dki->issue.denom_pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey), + &denom_key_hash); + + res = GNUNET_CONTAINER_multihashmap_put (ctx->denomkey_map, + &denom_key_hash, + GNUNET_memdup (dki, sizeof (struct TALER_MINT_DenomKeyIssuePriv)), + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + if (GNUNET_OK != res) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Duplicate denomination key\n"); + + json_array_append_new (ctx->denom_keys_array, + denom_key_issue_to_json (&dki->issue)); + + return GNUNET_OK; +} + + +/** + * Iterator for sign keys. + * + * @param cls closure + * @param ski the sign key issue + * @return #GNUNET_OK to continue to iterate, + * #GNUNET_NO to stop iteration with no error, + * #GNUNET_SYSERR to abort iteration with error! + */ +static int +reload_keys_sign_iter (void *cls, + const struct TALER_MINT_SignKeyIssuePriv *ski) +{ + struct MintKeyState *ctx = cls; + struct GNUNET_TIME_Absolute stamp_provide; + + stamp_provide = GNUNET_TIME_absolute_add (ctx->reload_time, + TALER_MINT_conf_duration_provide (cfg)); + + if (GNUNET_TIME_absolute_ntoh (ski->issue.expire).abs_value_us < ctx->reload_time.abs_value_us) + { + // this key is expired + return GNUNET_OK; + } + + if (GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us > stamp_provide.abs_value_us) + { + // we are to early for this key + return GNUNET_OK; + } + + // the signkey is valid for now, check + // if it's more recent than the current one! + if (GNUNET_TIME_absolute_ntoh (ctx->current_sign_key_issue.issue.start).abs_value_us > + GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us) + ctx->current_sign_key_issue = *ski; + + + ctx->next_reload = GNUNET_TIME_absolute_min (ctx->next_reload, + GNUNET_TIME_absolute_ntoh (ski->issue.expire)); + + json_array_append_new (ctx->sign_keys_array, + sign_key_issue_to_json (&ski->issue)); + + return GNUNET_OK; +} + + +/** + * Load the mint's key state from disk. + * + * @return fresh key state (with reference count 1) + */ +static struct MintKeyState * +reload_keys () +{ + struct MintKeyState *key_state; + json_t *keys; + + key_state = GNUNET_new (struct MintKeyState); + key_state->refcnt = 1; + + key_state->next_reload = GNUNET_TIME_UNIT_FOREVER_ABS; + + key_state->denom_keys_array = json_array (); + GNUNET_assert (NULL != key_state->denom_keys_array); + + key_state->sign_keys_array = json_array (); + GNUNET_assert (NULL != key_state->sign_keys_array); + + key_state->denomkey_map = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + GNUNET_assert (NULL != key_state->denomkey_map); + + key_state->reload_time = GNUNET_TIME_absolute_get (); + + TALER_MINT_denomkeys_iterate (mintdir, &reload_keys_denom_iter, key_state); + TALER_MINT_signkeys_iterate (mintdir, &reload_keys_sign_iter, key_state); + + keys = json_pack ("{s:o, s:o, s:o, s:o}", + "master_pub", TALER_JSON_from_data (&master_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)), + "signkeys", key_state->sign_keys_array, + "denoms", key_state->denom_keys_array, + "list_issue_date", TALER_JSON_from_abs (key_state->reload_time)); + + key_state->keys_json = json_dumps (keys, JSON_INDENT(2)); + + return key_state; +} + + +/** + * Release key state, free if necessary (if reference count gets to zero). + * + * @param key_state the key state to release + */ +void +TALER_MINT_key_state_release (struct MintKeyState *key_state) +{ + GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex)); + GNUNET_assert (0 != key_state->refcnt); + key_state->refcnt += 1; + if (key_state->refcnt == 0) { + GNUNET_free (key_state); + } + GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); +} + + +/** + * Acquire the key state of the mint. Updates keys if necessary. + * For every call to #TALER_MINT_key_state_acquire, a matching call + * to #TALER_MINT_key_state_release must be made. + * + * @return the key state + */ +struct MintKeyState * +TALER_MINT_key_state_acquire (void) +{ + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); + struct MintKeyState *key_state; + + // FIXME: the locking we have is very coarse-grained, + // using multiple locks might be nicer ... + + GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex)); + if (NULL == internal_key_state) + { + internal_key_state = reload_keys (); + } + else if (internal_key_state->next_reload.abs_value_us <= now.abs_value_us) + { + GNUNET_assert (0 != internal_key_state->refcnt); + internal_key_state->refcnt--; + if (0 == internal_key_state->refcnt) + GNUNET_free (internal_key_state); + internal_key_state = reload_keys (); + } + key_state = internal_key_state; + key_state->refcnt += 1; + GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); + + return key_state; +} + + +/** + * Look up the issue for a denom public key. + * + * @param key state to look in + * @param denom_pub denomination public key + * @return the denomination key issue, + * or NULL if denom_pub could not be found + */ +struct TALER_MINT_DenomKeyIssuePriv * +TALER_MINT_get_denom_key (const struct MintKeyState *key_state, + const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub) +{ + struct TALER_MINT_DenomKeyIssuePriv *issue; + struct GNUNET_HashCode hash; + char *buf; + size_t buf_len; + + buf_len = GNUNET_CRYPTO_rsa_public_key_encode (denom_pub, + &buf); + GNUNET_CRYPTO_hash (buf, + buf_len, + &hash); + GNUNET_free (buf); + issue = GNUNET_CONTAINER_multihashmap_get (key_state->denomkey_map, &hash); + return issue; +} + + +/** + * Handle a signal, writing relevant signal numbers + * (currently just SIGUSR1) to a pipe. + * + * @param signal_number the signal number + */ +static void +handle_signal (int signal_number) +{ + size_t res; + char c = signal_number; + + if (SIGUSR1 == signal_number) + { + errno = 0; + res = write (reload_pipe[1], &c, 1); + if ((res < 0) && (EINTR != errno)) + { + GNUNET_break (0); + return; + } + if (0 == res) + { + GNUNET_break (0); + return; + } + } +} + + +/** + * Read signals from a pipe in a loop, and reload keys from disk if + * SIGUSR1 is read from the pipe. + */ +int +TALER_MINT_key_reload_loop (void) +{ + struct sigaction act; + + if (0 != pipe (reload_pipe)) + { + fprintf (stderr, + "Failed to create pipe.\n"); + return GNUNET_SYSERR; + } + memset (&act, 0, sizeof (struct sigaction)); + act.sa_handler = &handle_signal; + + if (0 != sigaction (SIGUSR1, &act, NULL)) + { + fprintf (stderr, + "Failed to set signal handler.\n"); + return GNUNET_SYSERR; + } + + while (1) + { + char c; + ssize_t res; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "(re-)loading keys\n"); + GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex)); + if (NULL != internal_key_state) + { + GNUNET_assert (0 != internal_key_state->refcnt); + internal_key_state->refcnt -= 1; + if (0 == internal_key_state->refcnt) + GNUNET_free (internal_key_state); + } + internal_key_state = reload_keys (); + GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex)); +read_again: + errno = 0; + res = read (reload_pipe[0], &c, 1); + if ((res < 0) && (EINTR != errno)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (EINTR == errno) + goto read_again; + } + return GNUNET_OK; +} + + +/* end of taler-mint-httpd_keystate.c */ diff --git a/src/mint/taler-mint-httpd_keystate.h b/src/mint/taler-mint-httpd_keystate.h new file mode 100644 index 000000000..6f83b0861 --- /dev/null +++ b/src/mint/taler-mint-httpd_keystate.h @@ -0,0 +1,129 @@ +/* + This file is part of TALER + (C) 2014 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file taler-mint-httpd_keystate.h + * @brief management of our private signing keys (denomination keys) + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#ifndef TALER_MINT_HTTPD_KEYSTATE_H +#define TALER_MINT_HTTPD_KEYSTATE_H + + +#include +#include +#include +#include "taler-mint-httpd.h" +#include "mint.h" +#include "key_io.h" + + +/** + * Snapshot of the (coin and signing) + * keys (including private keys) of the mint. + */ +struct MintKeyState +{ + /** + * When did we initiate the key reloading? + */ + struct GNUNET_TIME_Absolute reload_time; + + /** + * JSON array with denomination keys. + */ + json_t *denom_keys_array; + + /** + * JSON array with signing keys. + */ + json_t *sign_keys_array; + + /** + * Mapping from denomination keys to denomination key issue struct. + */ + struct GNUNET_CONTAINER_MultiHashMap *denomkey_map; + + /** + * When is the next key invalid and we have to reload? + */ + struct GNUNET_TIME_Absolute next_reload; + + /** + * Mint signing key that should be used currently. + */ + struct TALER_MINT_SignKeyIssuePriv current_sign_key_issue; + + /** + * Cached JSON text that the mint will send for + * a /keys request. + */ + char *keys_json; + + /** + * Reference count. + */ + unsigned int refcnt; +}; + + +/** + * Acquire the key state of the mint. Updates keys if necessary. + * For every call to #TALER_MINT_key_state_acquire(), a matching call + * to #TALER_MINT_key_state_release() must be made. + * + * @return the key state + */ +struct MintKeyState * +TALER_MINT_key_state_acquire (void); + + +/** + * Release key state, free if necessary (if reference count gets to zero). + * + * @param key_state the key state to release + */ +void +TALER_MINT_key_state_release (struct MintKeyState *key_state); + + +/** + * Look up the issue for a denom public key. + * + * @param key state to look in + * @param denom_pub denomination public key + * @return the denomination key issue, + * or NULL if denom_pub could not be found + */ +struct TALER_MINT_DenomKeyIssuePriv * +TALER_MINT_get_denom_key (const struct MintKeyState *key_state, + const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub); + + +/** + * Read signals from a pipe in a loop, and reload keys from disk if + * SIGUSR1 is read from the pipe. + * + * @return #GNUNET_OK if we terminated normally, #GNUNET_SYSERR on error + */ +int +TALER_MINT_key_reload_loop (void); + + + + +#endif -- cgit v1.2.3