diff options
Diffstat (limited to 'src/exchange/taler-exchange-httpd_management_post_keys.c')
-rw-r--r-- | src/exchange/taler-exchange-httpd_management_post_keys.c | 343 |
1 files changed, 183 insertions, 160 deletions
diff --git a/src/exchange/taler-exchange-httpd_management_post_keys.c b/src/exchange/taler-exchange-httpd_management_post_keys.c index 311fff781..f91f24c41 100644 --- a/src/exchange/taler-exchange-httpd_management_post_keys.c +++ b/src/exchange/taler-exchange-httpd_management_post_keys.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020 Taler Systems SA + Copyright (C) 2020-2023 Taler Systems SA 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 @@ -40,13 +40,23 @@ struct DenomSig /** * Hash of a denomination public key. */ - struct GNUNET_HashCode h_denom_pub; + struct TALER_DenominationHashP h_denom_pub; /** * Master signature for the @e h_denom_pub. */ struct TALER_MasterSignatureP master_sig; + /** + * Fee structure for this key, as per our configuration. + */ + struct TALER_EXCHANGEDB_DenominationKeyMetaData meta; + + /** + * The full public key. + */ + struct TALER_DenominationPublicKey denom_pub; + }; @@ -65,6 +75,11 @@ struct SigningSig */ struct TALER_MasterSignatureP master_sig; + /** + * Our meta data on this key. + */ + struct TALER_EXCHANGEDB_SignkeyMetaData meta; + }; @@ -85,6 +100,11 @@ struct AddKeysContext struct SigningSig *s_sigs; /** + * Our key state. + */ + struct TEH_KeyStateHandle *ksh; + + /** * Length of the d_sigs array. */ unsigned int nd_sigs; @@ -121,15 +141,14 @@ add_keys (void *cls, /* activate all denomination keys */ for (unsigned int i = 0; i<akc->nd_sigs; i++) { + struct DenomSig *d = &akc->d_sigs[i]; enum GNUNET_DB_QueryStatus qs; - bool is_active = false; struct TALER_EXCHANGEDB_DenominationKeyMetaData meta; - struct TALER_DenominationPublicKey denom_pub; /* For idempotency, check if the key is already active */ qs = TEH_plugin->lookup_denomination_key ( TEH_plugin->cls, - &akc->d_sigs[i].h_denom_pub, + &d->h_denom_pub, &meta); if (qs < 0) { @@ -142,73 +161,21 @@ add_keys (void *cls, "lookup denomination key"); return qs; } - if (0 == qs) - { - enum GNUNET_GenericReturnValue rv; - - rv = TEH_keys_load_fees (&akc->d_sigs[i].h_denom_pub, - &denom_pub, - &meta); - switch (rv) - { - case GNUNET_SYSERR: - *mhd_ret = TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, - GNUNET_h2s (&akc->d_sigs[i].h_denom_pub)); - return GNUNET_DB_STATUS_HARD_ERROR; - case GNUNET_NO: - *mhd_ret = TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN, - GNUNET_h2s (&akc->d_sigs[i].h_denom_pub)); - return GNUNET_DB_STATUS_HARD_ERROR; - case GNUNET_OK: - break; - } - } - else + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) { - is_active = true; + /* FIXME: assert meta === d->meta might be good */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Denomination key %s already active, skipping\n", + GNUNET_h2s (&d->h_denom_pub.hash)); + continue; /* skip, already known */ } - /* check signature is valid */ - { - if (GNUNET_OK != - TALER_exchange_offline_denom_validity_verify ( - &akc->d_sigs[i].h_denom_pub, - meta.start, - meta.expire_withdraw, - meta.expire_deposit, - meta.expire_legal, - &meta.value, - &meta.fee_withdraw, - &meta.fee_deposit, - &meta.fee_refresh, - &meta.fee_refund, - &TEH_master_public_key, - &akc->d_sigs[i].master_sig)) - { - GNUNET_break_op (0); - *mhd_ret = TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_MANAGEMENT_KEYS_DENOMKEY_ADD_SIGNATURE_INVALID, - GNUNET_h2s (&akc->d_sigs[i].h_denom_pub)); - return GNUNET_DB_STATUS_HARD_ERROR; - } - } - if (is_active) - continue; /* skip, already known */ qs = TEH_plugin->add_denomination_key ( TEH_plugin->cls, - &akc->d_sigs[i].h_denom_pub, - &denom_pub, - &meta, - &akc->d_sigs[i].master_sig); - GNUNET_CRYPTO_rsa_public_key_free (denom_pub.rsa_public_key); + &d->h_denom_pub, + &d->denom_pub, + &d->meta, + &d->master_sig); if (qs < 0) { if (GNUNET_DB_STATUS_SOFT_ERROR == qs) @@ -222,20 +189,19 @@ add_keys (void *cls, } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Added offline signature for denomination `%s'\n", - GNUNET_h2s (&akc->d_sigs[i].h_denom_pub)); + GNUNET_h2s (&d->h_denom_pub.hash)); GNUNET_assert (0 != qs); } - for (unsigned int i = 0; i<akc->ns_sigs; i++) { + struct SigningSig *s = &akc->s_sigs[i]; enum GNUNET_DB_QueryStatus qs; - bool is_active = false; struct TALER_EXCHANGEDB_SignkeyMetaData meta; qs = TEH_plugin->lookup_signing_key ( TEH_plugin->cls, - &akc->s_sigs[i].exchange_pub, + &s->exchange_pub, &meta); if (qs < 0) { @@ -248,53 +214,19 @@ add_keys (void *cls, "lookup signing key"); return qs; } - if (0 == qs) - { - if (GNUNET_OK != - TEH_keys_get_timing (&akc->s_sigs[i].exchange_pub, - &meta)) - { - /* For idempotency, check if the key is already active */ - *mhd_ret = TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_UNKNOWN, - TALER_B2S (&akc->s_sigs[i].exchange_pub)); - return GNUNET_DB_STATUS_HARD_ERROR; - } - } - else - { - is_active = true; /* if we pass, it's active! */ - } - - /* check signature is valid */ + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) { - if (GNUNET_OK != - TALER_exchange_offline_signkey_validity_verify ( - &akc->s_sigs[i].exchange_pub, - meta.start, - meta.expire_sign, - meta.expire_legal, - &TEH_master_public_key, - &akc->s_sigs[i].master_sig)) - { - GNUNET_break_op (0); - *mhd_ret = TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_ADD_SIGNATURE_INVALID, - GNUNET_h2s (&akc->d_sigs[i].h_denom_pub)); - return GNUNET_DB_STATUS_HARD_ERROR; - } + /* FIXME: assert meta === d->meta might be good */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Signing key %s already active, skipping\n", + TALER_B2S (&s->exchange_pub)); + continue; /* skip, already known */ } - if (is_active) - continue; /* skip, already known */ qs = TEH_plugin->activate_signing_key ( TEH_plugin->cls, - &akc->s_sigs[i].exchange_pub, - &meta, - &akc->s_sigs[i].master_sig); + &s->exchange_pub, + &s->meta, + &s->master_sig); if (qs < 0) { if (GNUNET_DB_STATUS_SOFT_ERROR == qs) @@ -308,29 +240,47 @@ add_keys (void *cls, } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Added offline signature for signing key `%s'\n", - TALER_B2S (&akc->s_sigs[i].exchange_pub)); + TALER_B2S (&s->exchange_pub)); GNUNET_assert (0 != qs); } return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */ } +/** + * Clean up state in @a akc, but do not free @a akc itself + * + * @param[in,out] akc state to clean up + */ +static void +cleanup_akc (struct AddKeysContext *akc) +{ + for (unsigned int i = 0; i<akc->nd_sigs; i++) + { + struct DenomSig *d = &akc->d_sigs[i]; + + TALER_denom_pub_free (&d->denom_pub); + } + GNUNET_free (akc->d_sigs); + GNUNET_free (akc->s_sigs); +} + + MHD_RESULT TEH_handler_management_post_keys ( struct MHD_Connection *connection, const json_t *root) { - struct AddKeysContext akc; - json_t *denom_sigs; - json_t *signkey_sigs; + struct AddKeysContext akc = { 0 }; + const json_t *denom_sigs; + const json_t *signkey_sigs; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("denom_sigs", - &denom_sigs), - GNUNET_JSON_spec_json ("signkey_sigs", - &signkey_sigs), + GNUNET_JSON_spec_array_const ("denom_sigs", + &denom_sigs), + GNUNET_JSON_spec_array_const ("signkey_sigs", + &signkey_sigs), GNUNET_JSON_spec_end () }; - bool ok; MHD_RESULT ret; { @@ -344,21 +294,23 @@ TEH_handler_management_post_keys ( if (GNUNET_NO == res) return MHD_YES; /* failure */ } - if (! (json_is_array (denom_sigs) && - json_is_array (signkey_sigs)) ) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received POST /management/keys request\n"); + + akc.ksh = TEH_keys_get_state_for_management_only (); /* may start its own transaction, thus must be done here, before we run ours! */ + if (NULL == akc.ksh) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error ( connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "array expected for denom_sigs and signkey_sigs"); + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, + "no key state (not even for management)"); } + akc.nd_sigs = json_array_size (denom_sigs); akc.d_sigs = GNUNET_new_array (akc.nd_sigs, struct DenomSig); - ok = true; for (unsigned int i = 0; i<akc.nd_sigs; i++) { struct DenomSig *d = &akc.d_sigs[i]; @@ -375,25 +327,64 @@ TEH_handler_management_post_keys ( json_array_get (denom_sigs, i), ispec); - if (GNUNET_SYSERR == res) + if (GNUNET_OK != res) { - ret = MHD_NO; /* hard failure */ - ok = false; - break; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failure to handle /management/keys\n"); + cleanup_akc (&akc); + return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } - if (GNUNET_NO == res) + + res = TEH_keys_load_fees (akc.ksh, + &d->h_denom_pub, + &d->denom_pub, + &d->meta); + switch (res) { - ret = MHD_YES; - ok = false; + case GNUNET_SYSERR: + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, + GNUNET_h2s (&d->h_denom_pub.hash)); + cleanup_akc (&akc); + return ret; + case GNUNET_NO: + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN, + GNUNET_h2s (&d->h_denom_pub.hash)); + cleanup_akc (&akc); + return ret; + case GNUNET_OK: break; } + /* check signature is valid */ + TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; + if (GNUNET_OK != + TALER_exchange_offline_denom_validity_verify ( + &d->h_denom_pub, + d->meta.start, + d->meta.expire_withdraw, + d->meta.expire_deposit, + d->meta.expire_legal, + &d->meta.value, + &d->meta.fees, + &TEH_master_public_key, + &d->master_sig)) + { + GNUNET_break_op (0); + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_MANAGEMENT_KEYS_DENOMKEY_ADD_SIGNATURE_INVALID, + GNUNET_h2s (&d->h_denom_pub.hash)); + cleanup_akc (&akc); + return ret; + } } - if (! ok) - { - GNUNET_free (akc.d_sigs); - GNUNET_JSON_parse_free (spec); - return ret; - } + akc.ns_sigs = json_array_size (signkey_sigs); akc.s_sigs = GNUNET_new_array (akc.ns_sigs, struct SigningSig); @@ -413,25 +404,58 @@ TEH_handler_management_post_keys ( json_array_get (signkey_sigs, i), ispec); - if (GNUNET_SYSERR == res) + if (GNUNET_OK != res) { - ret = MHD_NO; /* hard failure */ - ok = false; - break; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failure to handle /management/keys\n"); + cleanup_akc (&akc); + return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } - if (GNUNET_NO == res) + res = TEH_keys_get_timing (&s->exchange_pub, + &s->meta); + switch (res) { - ret = MHD_YES; - ok = false; + case GNUNET_SYSERR: + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, + TALER_B2S (&s->exchange_pub)); + cleanup_akc (&akc); + return ret; + case GNUNET_NO: + /* For idempotency, check if the key is already active */ + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_UNKNOWN, + TALER_B2S (&s->exchange_pub)); + cleanup_akc (&akc); + return ret; + case GNUNET_OK: break; } - } - if (! ok) - { - GNUNET_free (akc.d_sigs); - GNUNET_free (akc.s_sigs); - GNUNET_JSON_parse_free (spec); - return ret; + + /* check signature is valid */ + TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; + if (GNUNET_OK != + TALER_exchange_offline_signkey_validity_verify ( + &s->exchange_pub, + s->meta.start, + s->meta.expire_sign, + s->meta.expire_legal, + &TEH_master_public_key, + &s->master_sig)) + { + GNUNET_break_op (0); + ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_MANAGEMENT_KEYS_SIGNKEY_ADD_SIGNATURE_INVALID, + TALER_B2S (&s->exchange_pub)); + cleanup_akc (&akc); + return ret; + } } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received %u denomination and %u signing key signatures\n", @@ -442,12 +466,11 @@ TEH_handler_management_post_keys ( res = TEH_DB_run_transaction (connection, "add keys", + TEH_MT_REQUEST_OTHER, &ret, &add_keys, &akc); - GNUNET_free (akc.d_sigs); - GNUNET_free (akc.s_sigs); - GNUNET_JSON_parse_free (spec); + cleanup_akc (&akc); if (GNUNET_SYSERR == res) return ret; } |