diff options
Diffstat (limited to 'src/mint/taler-mint-httpd_keys.c')
-rw-r--r-- | src/mint/taler-mint-httpd_keys.c | 474 |
1 files changed, 27 insertions, 447 deletions
diff --git a/src/mint/taler-mint-httpd_keys.c b/src/mint/taler-mint-httpd_keys.c index 359357da..57911274 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 <gnunet/gnunet_util_lib.h> @@ -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; } |