donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

commit f49c763dba881e5b90fb46c01e7036779cd26a4b
parent 995ba60e993586dd42d97de4407f301f0cc7c20b
Author: Pius Loosli <loosp2@bfh.ch>
Date:   Sat,  6 Jan 2024 23:14:09 +0100

[donau] first iteration on /keys: adapt structs and includes from exchange get /keys as well

Diffstat:
Msrc/donau/donau-httpd.c | 45++++++++++-----------------------------------
Msrc/donau/donau-httpd_keys.c | 667++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/donau/donau-httpd_keys.h | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
3 files changed, 618 insertions(+), 215 deletions(-)

diff --git a/src/donau/donau-httpd.c b/src/donau/donau-httpd.c @@ -176,15 +176,15 @@ typedef MHD_RESULT * @param connection where to send the reply on * @param details details for the error message, can be NULL */ -static MHD_RESULT -r404 (struct MHD_Connection *connection, - const char *details) -{ - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, - details); -} +// static MHD_RESULT +// r404 (struct MHD_Connection *connection, +// const char *details) +// { +// return TALER_MHD_reply_with_error (connection, +// MHD_HTTP_NOT_FOUND, +// TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, +// details); +// } /** @@ -387,31 +387,6 @@ proceed_with_handler (struct DH_RequestContext *rc, /** - * Handle a GET "/management" request. - * - * @param rc request context - * @param args array of additional options (must be [0] == "keys") - * @return MHD result code - */ -static MHD_RESULT -handle_get_keys (struct DH_RequestContext *rc, - const char *const args[2]) -{ - if ( (NULL != args[0]) && - (0 == strcmp (args[0], - "keys")) && - (NULL == args[1]) ) - { - return DH_get_keys_handler (rc->rh, - rc->connection); - } - GNUNET_break_op (0); - return r404 (rc->connection, - "/management/*"); -} - - -/** * Handle incoming HTTP request. * * @param cls closure for MHD daemon (unused) @@ -454,7 +429,7 @@ handle_mhd_request (void *cls, { .url = "keys", .method = MHD_HTTP_METHOD_GET, - .handler.get = &handle_get_keys, + .handler.get = &DH_keys_get_handler, .nargs = 1 }, /** diff --git a/src/donau/donau-httpd_keys.c b/src/donau/donau-httpd_keys.c @@ -27,7 +27,6 @@ #include "donau-httpd_config.h" #include "donaudb_plugin.h" - /** * How many /keys request do we hold in suspension at * most at any time? @@ -35,6 +34,153 @@ #define SKR_LIMIT 32 /** + * When do we forcefully timeout a /keys request? + */ +#define KEYS_TIMEOUT GNUNET_TIME_UNIT_MINUTES + +/** + * Number of entries in the @e skr_head DLL. + */ +static unsigned int skr_size; + +/** + * Handle to a connection that should be force-resumed + * with a hard error due to @a skr_size hitting + * #SKR_LIMIT. + */ +static struct MHD_Connection *skr_connection; + +/** + * Entry of /keys requests that are currently suspended because we are + * waiting for /keys to become ready. + */ +struct SuspendedKeysRequests +{ + /** + * Kept in a DLL. + */ + struct SuspendedKeysRequests *next; + + /** + * Kept in a DLL. + */ + struct SuspendedKeysRequests *prev; + + /** + * The suspended connection. + */ + struct MHD_Connection *connection; + + /** + * When does this request timeout? + */ + struct GNUNET_TIME_Absolute timeout; +}; + +/** + * Head of DLL of suspended /keys requests. + */ +static struct SuspendedKeysRequests *skr_head; + +/** + * Tail of DLL of suspended /keys requests. + */ +static struct SuspendedKeysRequests *skr_tail; + +/** + * Task to force timeouts on /keys requests. + */ +static struct GNUNET_SCHEDULER_Task *keys_tt; + +/** + * Are we shutting down? + */ +static bool terminating; + +/** + * Function called to forcefully resume suspended keys requests. + * + * @param cls unused, NULL + */ +static void +keys_timeout_cb (void *cls) +{ + struct SuspendedKeysRequests *skr; + + (void) cls; + keys_tt = NULL; + while (NULL != (skr = skr_head)) + { + if (GNUNET_TIME_absolute_is_future (skr->timeout)) + break; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Resuming /keys request due to timeout\n"); + GNUNET_CONTAINER_DLL_remove (skr_head, + skr_tail, + skr); + MHD_resume_connection (skr->connection); + TALER_MHD_daemon_trigger (); + GNUNET_free (skr); + } + if (NULL == skr) + return; + keys_tt = GNUNET_SCHEDULER_add_at (skr->timeout, + &keys_timeout_cb, + NULL); +} + + +/** + * Suspend /keys request while we (hopefully) are waiting to be + * provisioned with key material. + * + * @param[in] connection to suspend + */ +static MHD_RESULT +suspend_request (struct MHD_Connection *connection) +{ + struct SuspendedKeysRequests *skr; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Suspending /keys request until key material changes\n"); + if (terminating) + { + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, + "Exchange terminating"); + } + skr = GNUNET_new (struct SuspendedKeysRequests); + skr->connection = connection; + MHD_suspend_connection (connection); + GNUNET_CONTAINER_DLL_insert (skr_head, + skr_tail, + skr); + skr->timeout = GNUNET_TIME_relative_to_absolute (KEYS_TIMEOUT); + if (NULL == keys_tt) + { + keys_tt = GNUNET_SCHEDULER_add_at (skr->timeout, + &keys_timeout_cb, + NULL); + } + skr_size++; + if (skr_size > SKR_LIMIT) + { + skr = skr_tail; + GNUNET_CONTAINER_DLL_remove (skr_head, + skr_tail, + skr); + skr_size--; + skr_connection = skr->connection; + MHD_resume_connection (skr->connection); + TALER_MHD_daemon_trigger (); + GNUNET_free (skr); + } + return MHD_YES; +} + + +/** * Information about a donation unit on offer by the donation unit helper. */ struct HelperDonationUnit @@ -115,17 +261,17 @@ static uint64_t key_generation; /** * RSA security module public key, all zero if not known. */ -static struct TALER_SecurityModulePublicKeyP donation_unit_rsa_sm_pub; +// static struct TALER_SecurityModulePublicKeyP donation_unit_rsa_sm_pub; /** * CS security module public key, all zero if not known. */ -static struct TALER_SecurityModulePublicKeyP donation_unit_cs_sm_pub; +// static struct TALER_SecurityModulePublicKeyP donation_unit_cs_sm_pub; /** * EdDSA security module public key, all zero if not known. */ -static struct TALER_SecurityModulePublicKeyP esign_sm_pub; +// static struct TALER_SecurityModulePublicKeyP esign_sm_pub; /** @@ -302,9 +448,9 @@ struct HelperState }; /** - * Closure for #add_denom_key_cb. + * Closure for #add_donation_unit_key_cb. */ -struct DenomKeyCtx +struct DonationUnitKeyCtx { /** * Heap for sorting active donation unit keys by start time. @@ -447,11 +593,10 @@ finish_keys_response (struct DH_KeyStateHandle *ksh) GNUNET_assert (NULL != recoup); heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX); { - struct DenomKeyCtx dkc = { - .recoup = recoup, - .heap = heap, - .min_dk_frequency = GNUNET_TIME_UNIT_FOREVER_REL, - }; + // struct DonationUnitKeyCtx dkc = { + // .heap = heap, + // .min_dk_frequency = GNUNET_TIME_UNIT_FOREVER_REL, + // }; // GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map, // &add_denom_key_cb, @@ -500,7 +645,7 @@ finish_keys_response (struct DH_KeyStateHandle *ksh) ret = GNUNET_OK; -CLEANUP: +// CLEANUP: json_decref (grouped_donation_units); json_decref (sctx.signkeys); json_decref (recoup); @@ -735,11 +880,11 @@ setup_key_helpers (struct HelperState *hs) * @return NULL on error (i.e. failed to access database) */ static struct DH_KeyStateHandle * -build_key_state (struct HelperState *hs, - bool management_only) +build_key_state (struct HelperState *hs /*, + bool management_only*/) { struct DH_KeyStateHandle *ksh; - enum GNUNET_DB_QueryStatus qs; + // enum GNUNET_DB_QueryStatus qs; ksh = GNUNET_new (struct DH_KeyStateHandle); ksh->signature_expires = GNUNET_TIME_UNIT_FOREVER_TS; @@ -801,11 +946,11 @@ build_key_state (struct HelperState *hs, // return NULL; // } - if (management_only) - { - ksh->management_only = true; - return ksh; - } + // if (management_only) + // { + // ksh->management_only = true; + // return ksh; + // } if (GNUNET_OK != finish_keys_response (ksh)) @@ -839,14 +984,14 @@ DH_keys_update_states () static struct DH_KeyStateHandle * -keys_get_state (bool management_only) +DH_keys_get_state (/*bool management_only*/) { struct DH_KeyStateHandle *old_ksh; struct DH_KeyStateHandle *ksh; old_ksh = key_state; if (NULL == old_ksh) { - ksh = build_key_state (NULL, management_only); + ksh = build_key_state (NULL /*, management_only*/); ksh = NULL; if (NULL == ksh) return NULL; @@ -860,8 +1005,8 @@ keys_get_state (bool management_only) "Rebuilding /keys, generation upgrade from %llu to %llu\n", (unsigned long long) old_ksh->key_generation, (unsigned long long) key_generation); - ksh = build_key_state (old_ksh->helpers, - management_only); + ksh = build_key_state (old_ksh->helpers /*, + management_only*/); key_state = ksh; old_ksh->helpers = NULL; destroy_key_state (old_ksh, @@ -895,179 +1040,353 @@ struct FutureBuilderContext }; +// /** +// * Function called on all of our current and future donation unit keys +// * known to the helper process. Filters out those that are current +// * and adds the remaining donation unit keys (with their configuration +// * data) to the JSON array. +// * +// * @param cls the `struct FutureBuilderContext *` +// * @param h_donation_unit_pub hash of the donation unit public key +// * @param value a `struct HelperDenomination` +// * @return #GNUNET_OK (continue to iterate) +// */ +// static enum GNUNET_GenericReturnValue +// add_donation_unitkey_cb (void *cls, +// const struct GNUNET_HashCode *h_donation_unit_pub, +// void *value) +// { +// struct FutureBuilderContext *fbc = cls; +// struct HelperDonationUnit *helper_donation_unit = value; +// struct DH_DonationUnitKey *donation_unit_key; +// struct DONAUDB_DonationUnitKeyMetaData meta = {0}; + +// donation_unit_key = GNUNET_CONTAINER_multihashmap_get (fbc->ksh->denomkey_map, +// h_donation_unit_pub); +// if (NULL != donation_unit_key) +// return GNUNET_OK; /* skip: this key is already active! */ +// // if (GNUNET_TIME_relative_is_zero (hd->validity_duration)) +// // return GNUNET_OK; /* this key already expired! */ + +// GNUNET_assert ( +// 0 == +// json_array_append_new ( +// fbc->donation_units, +// GNUNET_JSON_PACK ( +// TALER_JSON_pack_amount ("value", +// &meta.value), +// GNUNET_JSON_pack_uint64 ("year", +// meta.validity_year), +// GNUNET_JSON_pack_data_auto ("donation_unit_pub", +// &helper_donation_unit->donation_unit_pub) +// // GNUNET_JSON_pack_string ("section_name", +// // helper_donation_unit->section_name) +// ))); +// return GNUNET_OK; +// } + + +// /** +// * Function called on all of our current and future exchange signing keys +// * known to the helper process. Filters out those that are current +// * and adds the remaining signing keys (with their configuration +// * data) to the JSON array. +// * +// * @param cls the `struct FutureBuilderContext *` +// * @param pid actually the exchange public key (type disguised) +// * @param value a `struct HelperDenomination` +// * @return #GNUNET_OK (continue to iterate) +// */ +// static enum GNUNET_GenericReturnValue +// add_signkey_cb (void *cls, +// const struct GNUNET_PeerIdentity *pid, +// void *value) +// { +// struct FutureBuilderContext *fbc = cls; +// struct HelperSignkey *hsk = value; +// struct SigningKey *sk; +// // struct GNUNET_TIME_Timestamp stamp_expire; +// // struct GNUNET_TIME_Timestamp legal_end; + +// sk = GNUNET_CONTAINER_multipeermap_get (fbc->ksh->signkey_map, +// pid); +// if (NULL != sk) +// return GNUNET_OK; /* skip: this key is already active */ +// // if (GNUNET_TIME_relative_is_zero (hsk->validity_duration)) +// // return GNUNET_OK; /* this key already expired! */ +// // stamp_expire = GNUNET_TIME_absolute_to_timestamp ( +// // GNUNET_TIME_absolute_add (hsk->start_time.abs_time, +// // hsk->validity_duration)); +// // legal_end = GNUNET_TIME_absolute_to_timestamp ( +// // GNUNET_TIME_absolute_add (stamp_expire.abs_time, +// // signkey_legal_duration)); +// GNUNET_assert (0 == +// json_array_append_new ( +// fbc->signkeys, +// GNUNET_JSON_PACK ( +// GNUNET_JSON_pack_data_auto ("key", +// &hsk->donau_pub), +// // GNUNET_JSON_pack_timestamp ("stamp_end", +// // legal_end), +// GNUNET_JSON_pack_data_auto ("year", +// &hsk->year) +// // GNUNET_JSON_pack_data_auto ("signkey_secmod_sig", +// // &hsk->sm_sig) +// ))); +// return GNUNET_OK; +// } + + +// MHD_RESULT +// DH_get_keys_handler (const struct DH_RequestHandler *rh, +// struct MHD_Connection *connection) +// { +// struct DH_KeyStateHandle *ksh; +// json_t *reply; + +// (void) rh; +// ksh = keys_get_state (true); +// if (NULL == ksh) +// { +// return TALER_MHD_reply_with_error (connection, +// MHD_HTTP_SERVICE_UNAVAILABLE, +// TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, +// "no key state"); +// } +// sync_key_helpers (ksh->helpers); +// if (NULL == ksh->management_keys_reply) +// { +// struct FutureBuilderContext fbc = { +// .ksh = ksh, +// .donation_units = json_array (), +// .signkeys = json_array () +// }; +// if ( (GNUNET_is_zero (&donation_unit_rsa_sm_pub)) && +// (GNUNET_is_zero (&donation_unit_cs_sm_pub)) ) +// { +// /* Either IPC failed, or neither helper had any donation_unit configured. */ +// return TALER_MHD_reply_with_error (connection, +// MHD_HTTP_BAD_GATEWAY, +// TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE, +// NULL); +// } +// if (GNUNET_is_zero (&esign_sm_pub)) +// { +// return TALER_MHD_reply_with_error (connection, +// MHD_HTTP_BAD_GATEWAY, +// TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE, +// NULL); +// } +// GNUNET_assert (NULL != fbc.donation_units); +// GNUNET_assert (NULL != fbc.signkeys); +// GNUNET_CONTAINER_multihashmap_iterate (ksh->helpers->donation_unit_keys, +// &add_donation_unitkey_cb, +// &fbc); +// GNUNET_CONTAINER_multipeermap_iterate (ksh->helpers->esign_keys, +// &add_signkey_cb, +// &fbc); +// reply = GNUNET_JSON_PACK ( +// GNUNET_JSON_pack_array_steal ("future_donation_units", +// fbc.donation_units), +// GNUNET_JSON_pack_array_steal ("future_signkeys", +// fbc.signkeys), +// GNUNET_JSON_pack_data_auto ("donation_unit_secmod_public_key", +// &donation_unit_rsa_sm_pub), +// GNUNET_JSON_pack_data_auto ("donation_unit_secmod_cs_public_key", +// &donation_unit_cs_sm_pub), +// GNUNET_JSON_pack_data_auto ("signkey_secmod_public_key", +// &esign_sm_pub)); +// GNUNET_log (GNUNET_ERROR_TYPE_INFO, +// "Returning GET /keys response:\n"); +// if (NULL == reply) +// return TALER_MHD_reply_with_error (connection, +// MHD_HTTP_INTERNAL_SERVER_ERROR, +// TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE, +// NULL); +// GNUNET_assert (NULL == ksh->management_keys_reply); +// ksh->management_keys_reply = reply; +// } +// else +// { +// reply = ksh->management_keys_reply; +// } +// return TALER_MHD_reply_json (connection, +// reply, +// MHD_HTTP_OK); +// } + /** - * Function called on all of our current and future donation unit keys - * known to the helper process. Filters out those that are current - * and adds the remaining donation unit keys (with their configuration - * data) to the JSON array. + * Comparator used for a binary search by cherry_pick_date for @a key in the + * `struct KeysResponseData` array. See libc's qsort() and bsearch() functions. * - * @param cls the `struct FutureBuilderContext *` - * @param h_donation_unit_pub hash of the donation unit public key - * @param value a `struct HelperDenomination` - * @return #GNUNET_OK (continue to iterate) + * @param key pointer to a `struct GNUNET_TIME_Timestamp` + * @param value pointer to a `struct KeysResponseData` array entry + * @return 0 if time matches, -1 if key is smaller, 1 if key is larger */ -static enum GNUNET_GenericReturnValue -add_donation_unitkey_cb (void *cls, - const struct GNUNET_HashCode *h_donation_unit_pub, - void *value) +static int +krd_search_comparator (const void *key, + const void *value) { - struct FutureBuilderContext *fbc = cls; - struct HelperDonationUnit *helper_donation_unit = value; - struct DH_DonationUnitKey *donation_unit_key; - struct DONAUDB_DonationUnitKeyMetaData meta = {0}; - - donation_unit_key = GNUNET_CONTAINER_multihashmap_get (fbc->ksh->denomkey_map, - h_donation_unit_pub); - if (NULL != donation_unit_key) - return GNUNET_OK; /* skip: this key is already active! */ - // if (GNUNET_TIME_relative_is_zero (hd->validity_duration)) - // return GNUNET_OK; /* this key already expired! */ - - GNUNET_assert ( - 0 == - json_array_append_new ( - fbc->donation_units, - GNUNET_JSON_PACK ( - TALER_JSON_pack_amount ("value", - &meta.value), - GNUNET_JSON_pack_uint64 ("year", - meta.validity_year), - GNUNET_JSON_pack_data_auto ("donation_unit_pub", - &helper_donation_unit->donation_unit_pub) - // GNUNET_JSON_pack_string ("section_name", - // helper_donation_unit->section_name) - ))); - return GNUNET_OK; + const struct GNUNET_TIME_Timestamp *kd = key; + const struct KeysResponseData *krd = value; + + if (GNUNET_TIME_timestamp_cmp (*kd, + >, + krd->cherry_pick_date)) + return -1; + if (GNUNET_TIME_timestamp_cmp (*kd, + <, + krd->cherry_pick_date)) + return 1; + return 0; } /** - * Function called on all of our current and future exchange signing keys - * known to the helper process. Filters out those that are current - * and adds the remaining signing keys (with their configuration - * data) to the JSON array. + * Callback used to set headers in a response. * - * @param cls the `struct FutureBuilderContext *` - * @param pid actually the exchange public key (type disguised) - * @param value a `struct HelperDenomination` - * @return #GNUNET_OK (continue to iterate) + * @param cls closure + * @param[in,out] resp response to modify */ -static enum GNUNET_GenericReturnValue -add_signkey_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) +typedef void +(*TEH_RESPONSE_SetHeaders)(void *cls, + struct MHD_Response *resp); + + +MHD_RESULT +DH_RESPONSE_reply_not_modified ( + struct MHD_Connection *connection, + const char *etags, + TEH_RESPONSE_SetHeaders cb, + void *cb_cls) { - struct FutureBuilderContext *fbc = cls; - struct HelperSignkey *hsk = value; - struct SigningKey *sk; - // struct GNUNET_TIME_Timestamp stamp_expire; - // struct GNUNET_TIME_Timestamp legal_end; - - sk = GNUNET_CONTAINER_multipeermap_get (fbc->ksh->signkey_map, - pid); - if (NULL != sk) - return GNUNET_OK; /* skip: this key is already active */ - // if (GNUNET_TIME_relative_is_zero (hsk->validity_duration)) - // return GNUNET_OK; /* this key already expired! */ - // stamp_expire = GNUNET_TIME_absolute_to_timestamp ( - // GNUNET_TIME_absolute_add (hsk->start_time.abs_time, - // hsk->validity_duration)); - // legal_end = GNUNET_TIME_absolute_to_timestamp ( - // GNUNET_TIME_absolute_add (stamp_expire.abs_time, - // signkey_legal_duration)); - GNUNET_assert (0 == - json_array_append_new ( - fbc->signkeys, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("key", - &hsk->donau_pub), - // GNUNET_JSON_pack_timestamp ("stamp_end", - // legal_end), - GNUNET_JSON_pack_data_auto ("year", - &hsk->year) - // GNUNET_JSON_pack_data_auto ("signkey_secmod_sig", - // &hsk->sm_sig) - ))); - return GNUNET_OK; + MHD_RESULT ret; + struct MHD_Response *resp; + + resp = MHD_create_response_from_buffer (0, + NULL, + MHD_RESPMEM_PERSISTENT); + cb (cb_cls, + resp); + GNUNET_break (MHD_YES == + MHD_add_response_header (resp, + MHD_HTTP_HEADER_ETAG, + etags)); + ret = MHD_queue_response (connection, + MHD_HTTP_NOT_MODIFIED, + resp); + GNUNET_break (MHD_YES == ret); + MHD_destroy_response (resp); + return ret; } MHD_RESULT -DH_get_keys_handler (const struct DH_RequestHandler *rh, - struct MHD_Connection *connection) +DH_keys_get_handler (struct DH_RequestContext *rc, + const char *const args[]) { - struct DH_KeyStateHandle *ksh; - json_t *reply; + struct GNUNET_TIME_Timestamp last_issue_date; + const char *etag; + + etag = MHD_lookup_connection_value (rc->connection, + MHD_HEADER_KIND, + MHD_HTTP_HEADER_IF_NONE_MATCH); + (void) args; + // { + // const char *have_cherrypick; + + // have_cherrypick = MHD_lookup_connection_value (rc->connection, + // MHD_GET_ARGUMENT_KIND, + // "last_issue_date"); + // if (NULL != have_cherrypick) + // { + // unsigned long long cherrypickn; + + // if (1 != + // sscanf (have_cherrypick, + // "%llu", + // &cherrypickn)) + // { + // GNUNET_break_op (0); + // return TALER_MHD_reply_with_error (rc->connection, + // MHD_HTTP_BAD_REQUEST, + // TALER_EC_GENERIC_PARAMETER_MALFORMED, + // have_cherrypick); + // } + // /* The following multiplication may overflow; but this should not really + // be a problem, as giving back 'older' data than what the client asks for + // (given that the client asks for data in the distant future) is not + // problematic */ + // last_issue_date = GNUNET_TIME_timestamp_from_s (cherrypickn); + // } + // else + // { + // last_issue_date = GNUNET_TIME_UNIT_ZERO_TS; + // } + // } - (void) rh; - ksh = keys_get_state (true); - if (NULL == ksh) - { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_SERVICE_UNAVAILABLE, - TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, - "no key state"); - } - sync_key_helpers (ksh->helpers); - if (NULL == ksh->management_keys_reply) { - struct FutureBuilderContext fbc = { - .ksh = ksh, - .donation_units = json_array (), - .signkeys = json_array () - }; - if ( (GNUNET_is_zero (&donation_unit_rsa_sm_pub)) && - (GNUNET_is_zero (&donation_unit_cs_sm_pub)) ) + struct DH_KeyStateHandle *ksh; + const struct KeysResponseData *krd; + + ksh = DH_keys_get_state (); + if ( (NULL == ksh) || + (0 == ksh->krd_array_length) ) { - /* Either IPC failed, or neither helper had any donation_unit configured. */ - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_GATEWAY, - TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE, - NULL); + if ( ( (SKR_LIMIT == skr_size) && + (rc->connection == skr_connection) ) || + DH_suicide) + { + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_SERVICE_UNAVAILABLE, + TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, + DH_suicide + ? "server terminating" + : "too many connections suspended waiting on /keys"); + } + return suspend_request (rc->connection); } - if (GNUNET_is_zero (&esign_sm_pub)) + krd = bsearch (&last_issue_date, + ksh->krd_array, + ksh->krd_array_length, + sizeof (struct KeysResponseData), + &krd_search_comparator); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Filtering /keys by cherry pick date %s found entry %u/%u\n", + GNUNET_TIME_timestamp2s (last_issue_date), + (unsigned int) (krd - ksh->krd_array), + ksh->krd_array_length); + if ( (NULL == krd) && + (ksh->krd_array_length > 0) ) { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_GATEWAY, - TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE, - NULL); + if (! GNUNET_TIME_absolute_is_zero (last_issue_date.abs_time)) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Client provided invalid cherry picking timestamp %s, returning full response\n", + GNUNET_TIME_timestamp2s (last_issue_date)); + krd = &ksh->krd_array[ksh->krd_array_length - 1]; } - GNUNET_assert (NULL != fbc.donation_units); - GNUNET_assert (NULL != fbc.signkeys); - GNUNET_CONTAINER_multihashmap_iterate (ksh->helpers->donation_unit_keys, - &add_donation_unitkey_cb, - &fbc); - GNUNET_CONTAINER_multipeermap_iterate (ksh->helpers->esign_keys, - &add_signkey_cb, - &fbc); - reply = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ("future_donation_units", - fbc.donation_units), - GNUNET_JSON_pack_array_steal ("future_signkeys", - fbc.signkeys), - GNUNET_JSON_pack_data_auto ("donation_unit_secmod_public_key", - &donation_unit_rsa_sm_pub), - GNUNET_JSON_pack_data_auto ("donation_unit_secmod_cs_public_key", - &donation_unit_cs_sm_pub), - GNUNET_JSON_pack_data_auto ("signkey_secmod_public_key", - &esign_sm_pub)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Returning GET /keys response:\n"); - if (NULL == reply) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE, - NULL); - GNUNET_assert (NULL == ksh->management_keys_reply); - ksh->management_keys_reply = reply; - } - else - { - reply = ksh->management_keys_reply; + if (NULL == krd) + { + /* Likely keys not ready *yet*. + Wait until they are. */ + return suspend_request (rc->connection); + } + if ( (NULL != etag) && + (0 == strcmp (etag, + krd->etag)) ) + return DH_RESPONSE_reply_not_modified (rc->connection, + krd->etag, + &setup_general_response_headers, + ksh); + + return MHD_queue_response (rc->connection, + MHD_HTTP_OK, + (MHD_YES == + TALER_MHD_can_compress (rc->connection)) + ? krd->response_compressed + : krd->response_uncompressed); } - return TALER_MHD_reply_json (connection, - reply, - MHD_HTTP_OK); } diff --git a/src/donau/donau-httpd_keys.h b/src/donau/donau-httpd_keys.h @@ -57,16 +57,125 @@ struct DH_DonationUnitKey }; /** - * Function to call to handle requests to "/management/keys" by sending - * back our future key material. + * Information needed to create a blind signature. + */ +// struct DH_CoinSignData +// { +/** + * Hash of key to sign with. + */ +// const struct TALER_DenominationHashP *h_denom_pub; + +/** + * Blinded planchet to sign over. + */ +// const struct TALER_BlindedPlanchet *bp; +// }; + + +// /** +// * Request to sign @a csds. +// * +// * @param csds array with data to blindly sign (and keys to sign with) +// * @param csds_length length of @a csds array +// * @param for_melt true if this is for a melt operation +// * @param[out] bss array set to the blind signature on success; must be of length @a csds_length +// * @return #TALER_EC_NONE on success +// */ +// enum TALER_ErrorCode +// TEH_keys_denomination_batch_sign ( +// unsigned int csds_length, +// const struct TEH_CoinSignData csds[static csds_length], +// bool for_melt, +// struct TALER_BlindedDenominationSignature bss[static csds_length]); + + +// /** +// * Information needed to derive the CS r_pub. +// */ +// struct TEH_CsDeriveData +// { +// /** +// * Hash of key to sign with. +// */ +// const struct TALER_DenominationHashP *h_denom_pub; + +// /** +// * Nonce to use. +// */ +// const struct GNUNET_CRYPTO_CsSessionNonce *nonce; +// }; + + +// /** +// * Request to derive CS @a r_pub using the denomination and nonce from @a cdd. +// * +// * @param cdd data to compute @a r_pub from +// * @param for_melt true if this is for a melt operation +// * @param[out] r_pub where to write the result +// * @return #TALER_EC_NONE on success +// */ +// enum TALER_ErrorCode +// TEH_keys_denomination_cs_r_pub ( +// const struct TEH_CsDeriveData *cdd, +// bool for_melt, +// struct GNUNET_CRYPTO_CSPublicRPairP *r_pub); + +// /** +// * Request to derive a bunch of CS @a r_pubs using the +// * denominations and nonces from @a cdds. +// * +// * @param cdds array to compute @a r_pubs from +// * @param cdds_length length of the @a cdds array +// * @param for_melt true if this is for a melt operation +// * @param[out] r_pubs array where to write the result; must be of length @a cdds_length +// * @return #TALER_EC_NONE on success +// */ +// enum TALER_ErrorCode +// TEH_keys_denomination_cs_batch_r_pub ( +// unsigned int cdds_length, +// const struct TEH_CsDeriveData cdds[static cdds_length], +// bool for_melt, +// struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[static cdds_length]); + + +/** + * Fully clean up keys subsystem. + */ +void +TEH_keys_finished (void); + + +/** + * Resumes all suspended /keys requests, we may now have key material + * (or are shutting down). * - * @param rh context of the handler - * @param connection the MHD connection to handle + * @param do_shutdown are we shutting down? + */ +void +TEH_resume_keys_requests (bool do_shutdown); + + +/** + * Function to call to handle requests to "/keys" by sending + * back our current key material. + * + * @param rc request context + * @param args array of additional options (must be empty for this function) * @return MHD result code */ MHD_RESULT -DH_get_keys_handler (const struct DH_RequestHandler *rh, - struct MHD_Connection *connection); +DH_keys_get_handler (struct DH_RequestContext *rc, + const char *const args[]); + + +/** + * Initialize keys subsystem. + * + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +TEH_keys_init (void); #endif