donau

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

commit 4fc8dc919a1b0758080549367d581143cf373ca2
parent 745f406d372048ff75d64d79075647b39875fa54
Author: Casaburi Johannes <johannes.casaburi@students.bfh.ch>
Date:   Thu, 11 Jan 2024 02:02:31 +0100

reimported httpd keys files

Diffstat:
Msrc/donau/donau-httpd_keys.c | 3007+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/donau/donau-httpd_keys.h | 416++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
2 files changed, 2507 insertions(+), 916 deletions(-)

diff --git a/src/donau/donau-httpd_keys.c b/src/donau/donau-httpd_keys.c @@ -1,13 +1,13 @@ /* This file is part of TALER - Copyright (C) 2020-2024 Taler Systems SA + Copyright (C) 2024 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 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 CHARITYABILITY or FITNESS FOR + 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 @@ -16,16 +16,20 @@ /** * @file donau-httpd_keys.c * @brief management of our various keys - * @author Christian Grothoff - * @author Özgür Kesim + * @author Johannes Casaburi */ #include "taler/platform.h" #include "taler/taler_json_lib.h" #include "taler/taler_mhd_lib.h" +//#include "taler/taler_kyclogic_lib.h" +#include "taler/taler_dbevents.h" #include "donau-httpd.h" -#include "donau-httpd_keys.h" #include "donau-httpd_config.h" +#include "donau-httpd_keys.h" +//#include "donau-httpd_responses.h" #include "donaudb_plugin.h" +//#include "taler/taler_extensions.h" + /** * How many /keys request do we hold in suspension at @@ -33,155 +37,15 @@ */ #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. + * Information about a donation_unit on offer by the donation_unit helper. */ struct HelperDonationUnit { @@ -198,11 +62,16 @@ struct HelperDonationUnit struct GNUNET_TIME_Relative validity_duration; /** - * Hash of the full donation unit key. + * Hash of the full donation_unit key. */ struct DONAU_DonationUnitHashP h_donation_unit_pub; /** + * Signature over this key from the security module's key. + */ + struct TALER_SecurityModuleSignatureP sm_sig; + + /** * The (full) public key. */ struct DONAU_DonationUnitPublicKey donation_unit_pub; @@ -226,73 +95,134 @@ struct HelperDonationUnit } h_details; /** - * Name in configuration section for this donation unit type. + * Name in configuration section for this donation_unit type. */ char *section_name; }; + /** - * Information about a signing key on offer by the sign helper. + * Information about a signing key on offer by the esign helper. */ struct HelperSignkey { /** * When will the helper start to use this key for signing? */ - // struct GNUNET_TIME_Timestamp start_time; - int year; + struct GNUNET_TIME_Timestamp start_time; + + /** + * For how long will the helper allow signing? 0 if + * the key was revoked or purged. + */ + struct GNUNET_TIME_Relative validity_duration; /** * The public key. */ struct DONAU_DonauPublicKeyP donau_pub; + /** + * Signature over this key from the security module's key. + */ + struct TALER_SecurityModuleSignatureP sm_sig; + }; -/** - * Counter incremented whenever we have a reason to re-build the keys because - * something external changed. See #DH_keys_get_state() and - * #DH_keys_update_states() for uses of this variable. - */ -static uint64_t key_generation; /** - * RSA security module public key, all zero if not known. + * State associated with the crypto helpers / security modules. NOT updated + * when the #key_generation is updated (instead constantly kept in sync + * whenever #DH_keys_get_state() is called). */ -// static struct TALER_SecurityModulePublicKeyP donation_unit_rsa_sm_pub; +struct HelperState +{ -/** - * CS security module public key, all zero if not known. - */ -// static struct TALER_SecurityModulePublicKeyP donation_unit_cs_sm_pub; + /** + * Handle for the esign/EdDSA helper. + */ + struct TALER_CRYPTO_DonauSignHelper *esh; -/** - * EdDSA security module public key, all zero if not known. - */ -// static struct TALER_SecurityModulePublicKeyP esign_sm_pub; + /** + * Handle for the donation_unit/RSA helper. + */ + struct TALER_CRYPTO_RsaDonationUnitHelper *rsadh; + /** + * Handle for the donation_unit/CS helper. + */ + struct TALER_CRYPTO_CsDonationUnitHelper *csdh; + + /** + * Map from H(donation_unit_pub) to `struct HelperDonationUnit` entries. + */ + struct GNUNET_CONTAINER_MultiHashMap *donation_unit_keys; + + /** + * Map from H(rsa_pub) to `struct HelperDonationUnit` entries. + */ + struct GNUNET_CONTAINER_MultiHashMap *rsa_keys; + + /** + * Map from H(cs_pub) to `struct HelperDonationUnit` entries. + */ + struct GNUNET_CONTAINER_MultiHashMap *cs_keys; + + /** + * Map from `struct DONAU_DonauPublicKey` to `struct HelperSignkey` + * entries. Based on the fact that a `struct GNUNET_PeerIdentity` is also + * an EdDSA public key. + */ + struct GNUNET_CONTAINER_MultiPeerMap *esign_keys; + +}; -/** - * When do we forcefully timeout a /keys request? - */ -#define KEYS_TIMEOUT GNUNET_TIME_UNIT_MINUTES /** - * Stores the latest generation of our key state. + * Entry in (sorted) array with possible pre-build responses for /keys. + * We keep pre-build responses for the various (valid) cherry-picking + * values around. */ -static struct DH_KeyStateHandle *key_state; +struct KeysResponseData +{ + + /** + * Response to return if the client supports (deflate) compression. + */ + struct MHD_Response *response_compressed; + + /** + * Response to return if the client does not support compression. + */ + struct MHD_Response *response_uncompressed; + + /** + * ETag for these responses. + */ + char *etag; + + /** + * Cherry-picking timestamp the client must have set for this + * response to be valid. 0 if this is the "full" response. + * The client's request must include this date or a higher one + * for this response to be applicable. + */ + struct GNUNET_TIME_Timestamp cherry_pick_date; + +}; + /** - * @brief All information about an donau online signing key (which is used to - * sign messages from the donau). + * @brief All information about an exchange online signing key (which is used to + * sign messages from the exchange). */ struct SigningKey { /** - * The donau's (online signing) public key. + * The exchange's (online signing) public key. */ struct DONAU_DonauPublicKeyP donau_pub; @@ -301,16 +231,17 @@ struct SigningKey */ struct DONAUDB_SignkeyMetaData meta; + }; struct DH_KeyStateHandle { /** - * Mapping from donation unit keys to donation unit key issue struct. + * Mapping from donation_unit keys to donation_unit key issue struct. * Used to lookup the key by hash. */ - struct GNUNET_CONTAINER_MultiHashMap *denomkey_map; + struct GNUNET_CONTAINER_MultiHashMap *donation_unitkey_map; /** * Map from `struct DONAU_DonauPublicKey` to `struct SigningKey` @@ -354,6 +285,12 @@ struct DH_KeyStateHandle struct GNUNET_TIME_Timestamp reload_time; /** + * What is the period at which we rotate keys + * (signing or donation_unit keys)? + */ + struct GNUNET_TIME_Relative rekey_frequency; + + /** * When does our online signing key expire and we * thus need to re-generate this response? */ @@ -367,102 +304,981 @@ struct DH_KeyStateHandle }; + /** - * Entry in (sorted) array with possible pre-build responses for /keys. - * We keep pre-build responses for the various (valid) cherry-picking - * values around. + * Entry of /keys requests that are currently suspended because we are + * waiting for /keys to become ready. */ -struct KeysResponseData +struct SuspendedKeysRequests { - /** - * Response to return if the client supports (deflate) compression. + * Kept in a DLL. */ - struct MHD_Response *response_compressed; + struct SuspendedKeysRequests *next; /** - * Response to return if the client does not support compression. + * Kept in a DLL. */ - struct MHD_Response *response_uncompressed; + struct SuspendedKeysRequests *prev; /** - * ETag for these responses. + * The suspended connection. */ - char *etag; + struct MHD_Connection *connection; /** - * Cherry-picking timestamp the client must have set for this - * response to be valid. 0 if this is the "full" response. - * The client's request must include this date or a higher one - * for this response to be applicable. + * When does this request timeout? */ - struct GNUNET_TIME_Timestamp cherry_pick_date; - + struct GNUNET_TIME_Absolute timeout; }; + /** - * State associated with the crypto helpers / security modules. NOT updated - * when the #key_generation is updated (instead constantly kept in sync - * whenever #DH_keys_get_state() is called). + * Stores the latest generation of our key state. */ -struct HelperState +static struct DH_KeyStateHandle *key_state; + +/** + * Counter incremented whenever we have a reason to re-build the keys because + * something external changed. See #DH_keys_get_state() and + * #DH_keys_update_states() for uses of this variable. + */ +static uint64_t key_generation; + +/** + * Handler listening for wire updates by other exchange + * services. + */ +static struct GNUNET_DB_EventHandler *keys_eh; + +/** + * Head of DLL of suspended /keys requests. + */ +static struct SuspendedKeysRequests *skr_head; + +/** + * Tail of DLL of suspended /keys requests. + */ +static struct SuspendedKeysRequests *skr_tail; + +/** + * 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; + +/** + * Task to force timeouts on /keys requests. + */ +static struct GNUNET_SCHEDULER_Task *keys_tt; + +/** + * For how long should a signing key be legally retained? + * Configuration value. + */ +static struct GNUNET_TIME_Relative signkey_legal_duration; + +/** + * What type of asset are we dealing with here? + */ +static char *asset_type; + +/** + * RSA security module public key, all zero if not known. + */ +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; + +/** + * EdDSA security module public key, all zero if not known. + */ +static struct TALER_SecurityModulePublicKeyP esign_sm_pub; + +/** + * 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; - /** - * Handle for the esign/EdDSA helper. - */ - struct TALER_CRYPTO_ExchangeSignHelper *esh; + (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); +} - /** - * Handle for the donation_unit/RSA helper. - */ - struct TALER_CRYPTO_RsaDenominationHelper *rsadh; - /** - * Handle for the donation_unit/CS helper. - */ - struct TALER_CRYPTO_CsDenominationHelper *csdh; +/** + * 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; - /** - * Map from H(donation_unit_pub) to `struct HelperDenomination` entries. - */ - struct GNUNET_CONTAINER_MultiHashMap *donation_unit_keys; + 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, + "Donau 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; +} - /** - * Map from H(rsa_pub) to `struct HelperDenomination` entries. - */ - struct GNUNET_CONTAINER_MultiHashMap *rsa_keys; - /** - * Map from H(cs_pub) to `struct HelperDenomination` entries. - */ - struct GNUNET_CONTAINER_MultiHashMap *cs_keys; +/** + * Called on each donation_unit key. Checks that the key still works. + * + * @param cls NULL + * @param hc donation_unit hash (unused) + * @param value a `struct DH_DonationUnitKey` + * @return #GNUNET_OK + */ +static enum GNUNET_GenericReturnValue +check_dk (void *cls, + const struct GNUNET_HashCode *hc, + void *value) +{ + struct DH_DonationUnitKey *dk = value; + + (void) cls; + (void) hc; + switch (dk->donation_unit_pub.bsign_pub_key->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + GNUNET_assert (GNUNET_CRYPTO_rsa_public_key_check ( + dk->donation_unit_pub.bsign_pub_key->details.rsa_public_key)); + return GNUNET_OK; + case GNUNET_CRYPTO_BSA_CS: + /* nothing to do for GNUNET_CRYPTO_BSA_CS */ + return GNUNET_OK; + } + GNUNET_assert (0); + return GNUNET_SYSERR; +} + + +void +DH_check_invariants () +{ + struct DH_KeyStateHandle *ksh; + + //if (0 == DH_check_invariants_flag) + // return; + ksh = DH_keys_get_state (); + if (NULL == ksh) + return; + GNUNET_CONTAINER_multihashmap_iterate (ksh->donation_unitkey_map, + &check_dk, + NULL); +} + + +void +DH_resume_keys_requests (bool do_shutdown) +{ + struct SuspendedKeysRequests *skr; + + if (do_shutdown) + terminating = true; + while (NULL != (skr = skr_head)) + { + GNUNET_CONTAINER_DLL_remove (skr_head, + skr_tail, + skr); + skr_size--; + MHD_resume_connection (skr->connection); + TALER_MHD_daemon_trigger (); + GNUNET_free (skr); + } +} + + +/** + * Clear memory for responses to "/keys" in @a ksh. + * + * @param[in,out] ksh key state to update + */ +static void +clear_response_cache (struct DH_KeyStateHandle *ksh) +{ + for (unsigned int i = 0; i<ksh->krd_array_length; i++) + { + struct KeysResponseData *krd = &ksh->krd_array[i]; + + MHD_destroy_response (krd->response_compressed); + MHD_destroy_response (krd->response_uncompressed); + GNUNET_free (krd->etag); + } + GNUNET_array_grow (ksh->krd_array, + ksh->krd_array_length, + 0); +} + + +/** + * Check that the given RSA security module's public key is the one + * we have pinned. If it does not match, we die hard. + * + * @param sm_pub RSA security module public key to check + */ +static void +check_donation_unit_rsa_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub) +{ + if (0 != + GNUNET_memcmp (sm_pub, + &donation_unit_rsa_sm_pub)) + { + if (! GNUNET_is_zero (&donation_unit_rsa_sm_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Our RSA security module changed its key. This must not happen.\n"); + GNUNET_assert (0); + } + donation_unit_rsa_sm_pub = *sm_pub; /* TOFU ;-) */ + } +} + + +/** + * Check that the given CS security module's public key is the one + * we have pinned. If it does not match, we die hard. + * + * @param sm_pub RSA security module public key to check + */ +static void +check_donation_unit_cs_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub) +{ + if (0 != + GNUNET_memcmp (sm_pub, + &donation_unit_cs_sm_pub)) + { + if (! GNUNET_is_zero (&donation_unit_cs_sm_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Our CS security module changed its key. This must not happen.\n"); + GNUNET_assert (0); + } + donation_unit_cs_sm_pub = *sm_pub; /* TOFU ;-) */ + } +} + + +/** + * Check that the given EdDSA security module's public key is the one + * we have pinned. If it does not match, we die hard. + * + * @param sm_pub EdDSA security module public key to check + */ +static void +check_esign_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub) +{ + if (0 != + GNUNET_memcmp (sm_pub, + &esign_sm_pub)) + { + if (! GNUNET_is_zero (&esign_sm_pub)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Our EdDSA security module changed its key. This must not happen.\n"); + GNUNET_assert (0); + } + esign_sm_pub = *sm_pub; /* TOFU ;-) */ + } +} + + +/** + * Helper function for #destroy_key_helpers to free all entries + * in the `donation_unit_keys` map. + * + * @param cls the `struct HelperDonationUnit` + * @param h_donation_unit_pub hash of the donation_unit public key + * @param value the `struct HelperDonationUnit` to release + * @return #GNUNET_OK (continue to iterate) + */ +static enum GNUNET_GenericReturnValue +free_donation_unit_cb (void *cls, + const struct GNUNET_HashCode *h_donation_unit_pub, + void *value) +{ + struct HelperDonationUnit *hd = value; + + (void) cls; + (void) h_donation_unit_pub; + //DONAU_donation_unit_pub_free (&hd->donation_unit_pub); + GNUNET_free (hd->section_name); + GNUNET_free (hd); + return GNUNET_OK; +} + + +/** + * Helper function for #destroy_key_helpers to free all entries + * in the `esign_keys` map. + * + * @param cls the `struct HelperSignkey` + * @param pid unused, matches the exchange public key + * @param value the `struct HelperSignkey` to release + * @return #GNUNET_OK (continue to iterate) + */ +static enum GNUNET_GenericReturnValue +free_esign_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct HelperSignkey *hsk = value; + + (void) cls; + (void) pid; + GNUNET_free (hsk); + return GNUNET_OK; +} + + +/** + * Destroy helper state. Does NOT call free() on @a hs, as that + * state is not separately allocated! Dual to #setup_key_helpers(). + * + * @param[in] hs helper state to free, but NOT the @a hs pointer itself! + */ +static void +destroy_key_helpers (struct HelperState *hs) +{ + GNUNET_CONTAINER_multihashmap_iterate (hs->donation_unit_keys, + &free_donation_unit_cb, + hs); + GNUNET_CONTAINER_multihashmap_destroy (hs->rsa_keys); + hs->rsa_keys = NULL; + GNUNET_CONTAINER_multihashmap_destroy (hs->cs_keys); + hs->cs_keys = NULL; + GNUNET_CONTAINER_multihashmap_destroy (hs->donation_unit_keys); + hs->donation_unit_keys = NULL; + GNUNET_CONTAINER_multipeermap_iterate (hs->esign_keys, + &free_esign_cb, + hs); + GNUNET_CONTAINER_multipeermap_destroy (hs->esign_keys); + hs->esign_keys = NULL; + if (NULL != hs->rsadh) + { + TALER_CRYPTO_helper_rsa_disconnect (hs->rsadh); + hs->rsadh = NULL; + } + if (NULL != hs->csdh) + { + TALER_CRYPTO_helper_cs_disconnect (hs->csdh); + hs->csdh = NULL; + } + if (NULL != hs->esh) + { + TALER_CRYPTO_helper_esign_disconnect (hs->esh); + hs->esh = NULL; + } +} + + +/** + * Function called with information about available keys for signing. Usually + * only called once per key upon connect. Also called again in case a key is + * being revoked, in that case with an @a end_time of zero. + * + * @param cls closure with the `struct HelperState *` + * @param section_name name of the donation_unit type in the configuration; + * NULL if the key has been revoked or purged + * @param start_time when does the key become available for signing; + * zero if the key has been revoked or purged + * @param validity_duration how long does the key remain available for signing; + * zero if the key has been revoked or purged + * @param h_rsa hash of the @a donation_unit_pub that is available (or was purged) + * @param bs_pub the public key itself, NULL if the key was revoked or purged + * @param sm_pub public key of the security module, NULL if the key was revoked or purged + * @param sm_sig signature from the security module, NULL if the key was revoked or purged + * The signature was already verified against @a sm_pub. + */ +static void +helper_rsa_cb ( + void *cls, + const char *section_name, + struct GNUNET_TIME_Timestamp start_time, + struct GNUNET_TIME_Relative validity_duration, + const struct TALER_RsaPubHashP *h_rsa, + struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, + const struct TALER_SecurityModulePublicKeyP *sm_pub, + const struct TALER_SecurityModuleSignatureP *sm_sig) +{ + struct HelperState *hs = cls; + struct HelperDonationUnit *hd; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "RSA helper announces key %s for donation_unit type %s with validity %s\n", + GNUNET_h2s (&h_rsa->hash), + section_name, + GNUNET_STRINGS_relative_time_to_string (validity_duration, + GNUNET_NO)); + key_generation++; + DH_resume_keys_requests (false); + hd = GNUNET_CONTAINER_multihashmap_get (hs->rsa_keys, + &h_rsa->hash); + if (NULL != hd) + { + /* should be just an update (revocation!), so update existing entry */ + hd->validity_duration = validity_duration; + return; + } + GNUNET_assert (NULL != sm_pub); + check_donation_unit_rsa_sm_pub (sm_pub); + hd = GNUNET_new (struct HelperDonationUnit); + hd->start_time = start_time; + hd->validity_duration = validity_duration; + hd->h_details.h_rsa = *h_rsa; + hd->sm_sig = *sm_sig; + GNUNET_assert (GNUNET_CRYPTO_BSA_RSA == bs_pub->cipher); + hd->donation_unit_pub.bsign_pub_key = + GNUNET_CRYPTO_bsign_pub_incref (bs_pub); + //DONAU_donation_unit_pub_hash (&hd->donation_unit_pub, + // &hd->h_donation_unit_pub); + hd->section_name = GNUNET_strdup (section_name); + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + hs->donation_unit_keys, + &hd->h_donation_unit_pub.hash, + hd, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + hs->rsa_keys, + &hd->h_details.h_rsa.hash, + hd, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); +} + + +/** + * Function called with information about available CS keys for signing. Usually + * only called once per key upon connect. Also called again in case a key is + * being revoked, in that case with an @a end_time of zero. + * + * @param cls closure with the `struct HelperState *` + * @param section_name name of the donation_unit type in the configuration; + * NULL if the key has been revoked or purged + * @param start_time when does the key become available for signing; + * zero if the key has been revoked or purged + * @param validity_duration how long does the key remain available for signing; + * zero if the key has been revoked or purged + * @param h_cs hash of the @a donation_unit_pub that is available (or was purged) + * @param bs_pub the public key itself, NULL if the key was revoked or purged + * @param sm_pub public key of the security module, NULL if the key was revoked or purged + * @param sm_sig signature from the security module, NULL if the key was revoked or purged + * The signature was already verified against @a sm_pub. + */ +static void +helper_cs_cb ( + void *cls, + const char *section_name, + struct GNUNET_TIME_Timestamp start_time, + struct GNUNET_TIME_Relative validity_duration, + const struct TALER_CsPubHashP *h_cs, + struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub, + const struct TALER_SecurityModulePublicKeyP *sm_pub, + const struct TALER_SecurityModuleSignatureP *sm_sig) +{ + struct HelperState *hs = cls; + struct HelperDonationUnit *hd; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "CS helper announces key %s for donation_unit type %s with validity %s\n", + GNUNET_h2s (&h_cs->hash), + section_name, + GNUNET_STRINGS_relative_time_to_string (validity_duration, + GNUNET_NO)); + key_generation++; + DH_resume_keys_requests (false); + hd = GNUNET_CONTAINER_multihashmap_get (hs->cs_keys, + &h_cs->hash); + if (NULL != hd) + { + /* should be just an update (revocation!), so update existing entry */ + hd->validity_duration = validity_duration; + return; + } + GNUNET_assert (NULL != sm_pub); + check_donation_unit_cs_sm_pub (sm_pub); + hd = GNUNET_new (struct HelperDonationUnit); + hd->start_time = start_time; + hd->validity_duration = validity_duration; + hd->h_details.h_cs = *h_cs; + hd->sm_sig = *sm_sig; + GNUNET_assert (GNUNET_CRYPTO_BSA_CS == bs_pub->cipher); + hd->donation_unit_pub.bsign_pub_key + = GNUNET_CRYPTO_bsign_pub_incref (bs_pub); + //DONAU_donation_unit_pub_hash (&hd->donation_unit_pub, + // &hd->h_donation_unit_pub); + hd->section_name = GNUNET_strdup (section_name); + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + hs->donation_unit_keys, + &hd->h_donation_unit_pub.hash, + hd, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + hs->cs_keys, + &hd->h_details.h_cs.hash, + hd, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); +} + + +/** + * Function called with information about available keys for signing. Usually + * only called once per key upon connect. Also called again in case a key is + * being revoked, in that case with an @a end_time of zero. + * + * @param cls closure with the `struct HelperState *` + * @param start_time when does the key become available for signing; + * zero if the key has been revoked or purged + * @param validity_duration how long does the key remain available for signing; + * zero if the key has been revoked or purged + * @param donau_pub the public key itself, NULL if the key was revoked or purged + * @param sm_pub public key of the security module, NULL if the key was revoked or purged + * @param sm_sig signature from the security module, NULL if the key was revoked or purged + * The signature was already verified against @a sm_pub. + */ +static void +helper_esign_cb ( + void *cls, + struct GNUNET_TIME_Timestamp start_time, + struct GNUNET_TIME_Relative validity_duration, + const struct DONAU_DonauPublicKeyP *donau_pub, + const struct TALER_SecurityModulePublicKeyP *sm_pub, + const struct TALER_SecurityModuleSignatureP *sm_sig) +{ + struct HelperState *hs = cls; + struct HelperSignkey *hsk; + struct GNUNET_PeerIdentity pid; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "EdDSA helper announces signing key %s with validity %s\n", + TALER_B2S (donau_pub), + GNUNET_STRINGS_relative_time_to_string (validity_duration, + GNUNET_NO)); + key_generation++; + DH_resume_keys_requests (false); + pid.public_key = donau_pub->eddsa_pub; + hsk = GNUNET_CONTAINER_multipeermap_get (hs->esign_keys, + &pid); + if (NULL != hsk) + { + /* should be just an update (revocation!), so update existing entry */ + hsk->validity_duration = validity_duration; + return; + } + GNUNET_assert (NULL != sm_pub); + check_esign_sm_pub (sm_pub); + hsk = GNUNET_new (struct HelperSignkey); + hsk->start_time = start_time; + hsk->validity_duration = validity_duration; + hsk->donau_pub = *donau_pub; + hsk->sm_sig = *sm_sig; + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multipeermap_put ( + hs->esign_keys, + &pid, + hsk, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); +} + + +/** + * Setup helper state. + * + * @param[out] hs helper state to initialize + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +setup_key_helpers (struct HelperState *hs) +{ + hs->donation_unit_keys + = GNUNET_CONTAINER_multihashmap_create (1024, + GNUNET_YES); + hs->rsa_keys + = GNUNET_CONTAINER_multihashmap_create (1024, + GNUNET_YES); + hs->cs_keys + = GNUNET_CONTAINER_multihashmap_create (1024, + GNUNET_YES); + hs->esign_keys + = GNUNET_CONTAINER_multipeermap_create (32, + GNUNET_NO /* MUST BE NO! */); + hs->rsadh = TALER_CRYPTO_helper_rsa_connect (DH_cfg, + "taler", + &helper_rsa_cb, + hs); + if (NULL == hs->rsadh) + { + destroy_key_helpers (hs); + return GNUNET_SYSERR; + } + hs->csdh = TALER_CRYPTO_helper_cs_connect (DH_cfg, + "taler", + &helper_cs_cb, + hs); + if (NULL == hs->csdh) + { + destroy_key_helpers (hs); + return GNUNET_SYSERR; + } + hs->esh = TALER_CRYPTO_helper_esign_connect (DH_cfg, + "taler", + &helper_esign_cb, + hs); + if (NULL == hs->esh) + { + destroy_key_helpers (hs); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Synchronize helper state. Polls the key helper for updates. + * + * @param[in,out] hs helper state to synchronize + */ +static void +sync_key_helpers (struct HelperState *hs) +{ + TALER_CRYPTO_helper_rsa_poll (hs->rsadh); + TALER_CRYPTO_helper_cs_poll (hs->csdh); + TALER_CRYPTO_helper_esign_poll (hs->esh); +} + + +/** + * Free donation_unit key data. + * + * @param cls a `struct DH_KeyStateHandle`, unused + * @param h_donation_unit_pub hash of the donation_unit public key, unused + * @param value a `struct DH_DonationUnitKey` to free + * @return #GNUNET_OK (continue to iterate) + */ +static enum GNUNET_GenericReturnValue +clear_donation_unit_cb (void *cls, + const struct GNUNET_HashCode *h_donation_unit_pub, + void *value) +{ + struct DH_DonationUnitKey *dk = value; + + (void) cls; + (void) h_donation_unit_pub; + //DONAU_donation_unit_pub_free (&dk->donation_unit_pub); + + GNUNET_free (dk); + return GNUNET_OK; +} + + +/** + * Free donation_unit key data. + * + * @param cls a `struct DH_KeyStateHandle`, unused + * @param pid the online signing key (type-disguised), unused + * @param value a `struct SigningKey` to free + * @return #GNUNET_OK (continue to iterate) + */ +static enum GNUNET_GenericReturnValue +clear_signkey_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct SigningKey *sk = value; + + (void) cls; + (void) pid; + GNUNET_free (sk); + return GNUNET_OK; +} + + +/** + * Free resources associated with @a cls, possibly excluding + * the helper data. + * + * @param[in] ksh key state to release + * @param free_helper true to also release the helper state + */ +static void +destroy_key_state (struct DH_KeyStateHandle *ksh, + bool free_helper) +{ + + clear_response_cache (ksh); + + GNUNET_CONTAINER_multihashmap_iterate (ksh->donation_unitkey_map, + &clear_donation_unit_cb, + ksh); + GNUNET_CONTAINER_multihashmap_destroy (ksh->donation_unitkey_map); + GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map, + &clear_signkey_cb, + ksh); + GNUNET_CONTAINER_multipeermap_destroy (ksh->signkey_map); + if (free_helper) + { + destroy_key_helpers (ksh->helpers); + GNUNET_free (ksh->helpers); + } + if (NULL != ksh->management_keys_reply) + { + json_decref (ksh->management_keys_reply); + ksh->management_keys_reply = NULL; + } + GNUNET_free (ksh); +} + + +/** + * Function called whenever another exchange process has updated + * the keys data in the database. + * + * @param cls NULL + * @param extra unused + * @param extra_size number of bytes in @a extra unused + */ +static void +keys_update_event_cb (void *cls, + const void *extra, + size_t extra_size) +{ + (void) cls; + (void) extra; + (void) extra_size; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received /keys update event\n"); + DH_check_invariants (); + key_generation++; + DH_resume_keys_requests (false); + DH_check_invariants (); +} + + +enum GNUNET_GenericReturnValue +DH_keys_init () +{ + struct GNUNET_DB_EventHeaderP es = { + .size = htons (sizeof (es)), + .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED), + }; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (DH_cfg, + "exchange", + "SIGNKEY_LEGAL_DURATION", + &signkey_legal_duration)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "SIGNKEY_LEGAL_DURATION"); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (DH_cfg, + "exchange", + "ASSET_TYPE", + &asset_type)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + "exchange", + "ASSET_TYPE"); + asset_type = GNUNET_strdup ("fiat"); + } + keys_eh = DH_plugin->event_listen (DH_plugin->cls, + GNUNET_TIME_UNIT_FOREVER_REL, + &es, + &keys_update_event_cb, + NULL); + if (NULL == keys_eh) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Fully clean up our state. + */ +void +DH_keys_finished () +{ + if (NULL != keys_tt) + { + GNUNET_SCHEDULER_cancel (keys_tt); + keys_tt = NULL; + } + if (NULL != key_state) + destroy_key_state (key_state, + true); + if (NULL != keys_eh) + { + DH_plugin->event_listen_cancel (DH_plugin->cls, + keys_eh); + keys_eh = NULL; + } +} + - /** - * Map from `struct TALER_ExchangePublicKey` to `struct HelperSignkey` - * entries. Based on the fact that a `struct GNUNET_PeerIdentity` is also - * an EdDSA public key. - */ - struct GNUNET_CONTAINER_MultiPeerMap *esign_keys; +/** + * Function called with information about the exchange's donation_unit keys. + * + * @param cls closure with a `struct DH_KeyStateHandle *` + * @param donation_unit_pub public key of the donation_unit + * @param h_donation_unit_pub hash of @a donation_unit_pub + * @param meta meta data information about the donation_unit type (value, expirations, fees) + * coins of this donation_unit + */ +static void +donation_unit_info_cb ( + void *cls, + const struct DONAU_DonationUnitPublicKey *donation_unit_pub, + const struct DONAU_DonationUnitHashP *h_donation_unit_pub, + const struct DONAUDB_DonationUnitKeyMetaData *meta) +{ + struct DH_KeyStateHandle *ksh = cls; + struct DH_DonationUnitKey *dk; + + GNUNET_assert (GNUNET_CRYPTO_BSA_INVALID != + donation_unit_pub->bsign_pub_key->cipher); + //if (GNUNET_TIME_absolute_is_zero (meta->start.abs_time) || + // GNUNET_TIME_absolute_is_zero (meta->expire_withdraw.abs_time) || + // GNUNET_TIME_absolute_is_zero (meta->expire_deposit.abs_time) || + // GNUNET_TIME_absolute_is_zero (meta->expire_legal.abs_time) ) + //{ + // GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + // "Database contains invalid donation_unit key %s\n", + // GNUNET_h2s (&h_donation_unit_pub->hash)); + // return; + //} + dk = GNUNET_new (struct DH_DonationUnitKey); + DONAU_donation_unit_pub_deep_copy (&dk->donation_unit_pub, + donation_unit_pub); + dk->h_donation_unit_pub = *h_donation_unit_pub; + //dk->meta = *meta; + + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (ksh->donation_unitkey_map, + &dk->h_donation_unit_pub.hash, + dk, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); +} -}; /** - * Closure for #add_donation_unit_key_cb. + * Function called with information about the exchange's online signing keys. + * + * @param cls closure with a `struct DH_KeyStateHandle *` + * @param donau_pub the public key + * @param meta meta data information about the donation_unit type (expirations) */ -struct DonationUnitKeyCtx +static void +signkey_info_cb ( + void *cls, + const struct DONAU_DonauPublicKeyP *donau_pub, + const struct DONAUDB_SignkeyMetaData *meta) { - /** - * Heap for sorting active donation unit keys by start time. - */ - struct GNUNET_CONTAINER_Heap *heap; + struct DH_KeyStateHandle *ksh = cls; + struct SigningKey *sk; + struct GNUNET_PeerIdentity pid; + + sk = GNUNET_new (struct SigningKey); + sk->donau_pub = *donau_pub; + sk->meta = *meta; + pid.public_key = donau_pub->eddsa_pub; + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multipeermap_put (ksh->signkey_map, + &pid, + sk, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); +} - /** - * What is the minimum key rotation frequency of - * valid donation unit keys? - */ - struct GNUNET_TIME_Relative min_dk_frequency; -}; /** * Closure for #add_sign_key_cb. @@ -480,6 +1296,7 @@ struct SignKeyCtx json_t *signkeys; }; + /** * Function called for all signing keys, used to build up the * respective JSON response. @@ -498,29 +1315,91 @@ add_sign_key_cb (void *cls, struct SigningKey *sk = value; (void) pid; - // if (GNUNET_TIME_absolute_is_future (sk->meta.expire_sign.abs_time)) - // { + //if (GNUNET_TIME_absolute_is_future (sk->meta.expire_sign.abs_time)) + //{ // ctx->min_sk_frequency = // GNUNET_TIME_relative_min (ctx->min_sk_frequency, // GNUNET_TIME_absolute_get_difference ( // sk->meta.start.abs_time, // sk->meta.expire_sign.abs_time)); - // } - GNUNET_assert ( - 0 == - json_array_append_new ( - ctx->signkeys, - GNUNET_JSON_PACK ( - // GNUNET_JSON_pack_timestamp ("stamp_start", - // sk->meta.start), - // GNUNET_JSON_pack_timestamp ("stamp_expire", - // sk->meta.expire_sign), - // GNUNET_JSON_pack_timestamp ("stamp_end", - // sk->meta.expire_legal), - // GNUNET_JSON_pack_data_auto ("master_sig", - // &sk->master_sig), - GNUNET_JSON_pack_data_auto ("key", - &sk->donau_pub)))); + //} + //GNUNET_assert ( + // 0 == + // json_array_append_new ( + // ctx->signkeys, + // GNUNET_JSON_PACK ( + // GNUNET_JSON_pack_timestamp ("stamp_start", + // sk->meta.start), + // GNUNET_JSON_pack_timestamp ("stamp_expire", + // sk->meta.expire_sign), + // GNUNET_JSON_pack_timestamp ("stamp_end", + // sk->meta.expire_legal), + // GNUNET_JSON_pack_data_auto ("key", + // &sk->donau_pub)))); + return GNUNET_OK; +} + + +/** + * Closure for #add_donation_unit_key_cb. + */ +struct DenomKeyCtx +{ + /** + * Heap for sorting active donation_unit keys by start time. + */ + struct GNUNET_CONTAINER_Heap *heap; + + /** + * What is the minimum key rotation frequency of + * valid donation_unit keys? + */ + struct GNUNET_TIME_Relative min_dk_frequency; +}; + + +/** + * Function called for all donation_unit keys, used to build up the + * JSON list of *revoked* donation_unit keys and the + * heap of non-revoked donation_unit keys by timeout. + * + * @param cls a `struct DenomKeyCtx` + * @param h_donation_unit_pub hash of the donation_unit key + * @param value a `struct DH_DonationUnitKey` + * @return #GNUNET_OK (continue to iterate) + */ +static enum GNUNET_GenericReturnValue +add_donation_unit_key_cb (void *cls, + const struct GNUNET_HashCode *h_donation_unit_pub, + void *value) +{ + struct DenomKeyCtx *dkc = cls; + struct DH_DonationUnitKey *dk = value; + + //if (dk->recoup_possible) + //{ + // GNUNET_assert ( + // 0 == + // json_array_append_new ( + // dkc->recoup, + // GNUNET_JSON_PACK ( + // GNUNET_JSON_pack_data_auto ("h_donation_unit_pub", + // h_donation_unit_pub)))); + //} + //else + //{ + // if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time)) + // { + // dkc->min_dk_frequency = + // GNUNET_TIME_relative_min (dkc->min_dk_frequency, + // GNUNET_TIME_absolute_get_difference ( + // dk->meta.start.abs_time, + // dk->meta.expire_withdraw.abs_time)); + // } + // (void) GNUNET_CONTAINER_heap_insert (dkc->heap, + // dk, + // dk->meta.start.abs_time.abs_value_us); + //} return GNUNET_OK; } @@ -549,6 +1428,7 @@ setup_general_response_headers (void *cls, MHD_add_response_header (response, MHD_HTTP_HEADER_LAST_MODIFIED, dat)); + /* Set cache control headers: our response varies depending on these headers */ GNUNET_break (MHD_YES == MHD_add_response_header (response, @@ -563,311 +1443,379 @@ setup_general_response_headers (void *cls, /** - * Update the "/keys" responses in @a ksh, computing the detailed replies. - * - * This function is to recompute all (including cherry-picked) responses we - * might want to return, based on the state already in @a ksh. - * - * @param[in,out] ksh state handle to update - * @return #GNUNET_OK on success + * Element in the `struct SignatureContext` array. */ -static enum GNUNET_GenericReturnValue -finish_keys_response (struct DH_KeyStateHandle *ksh) +struct SignatureElement { - enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; - json_t *recoup; - struct SignKeyCtx sctx; - json_t *grouped_donation_units = NULL; - struct GNUNET_TIME_Timestamp last_cherry_pick_date; - struct GNUNET_CONTAINER_Heap *heap; - struct GNUNET_HashContext *hash_context = NULL; - // struct GNUNET_HashCode grouped_hash_xor = {0}; - - sctx.signkeys = json_array (); - GNUNET_assert (NULL != sctx.signkeys); - sctx.min_sk_frequency = GNUNET_TIME_UNIT_FOREVER_REL; - GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map, - &add_sign_key_cb, - &sctx); - recoup = json_array (); - GNUNET_assert (NULL != recoup); - heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX); - { - // struct DonationUnitKeyCtx dkc = { - // .heap = heap, - // .min_dk_frequency = GNUNET_TIME_UNIT_FOREVER_REL, - // }; - - // GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map, - // &add_denom_key_cb, - // &dkc); - // ksh->rekey_frequency - // = GNUNET_TIME_relative_min (dkc.min_dk_frequency, - // sctx.min_sk_frequency); - } - hash_context = GNUNET_CRYPTO_hash_context_start (); - - grouped_donation_units = json_array (); - GNUNET_assert (NULL != grouped_donation_units); + /** + * Offset of the donation_unit in the group array, + * for sorting (2nd rank, ascending). + */ + unsigned int offset; - last_cherry_pick_date = GNUNET_TIME_UNIT_ZERO_TS; + /** + * Offset of the group in the donation_units array, + * for sorting (2nd rank, ascending). + */ + unsigned int group_offset; - GNUNET_CONTAINER_heap_destroy (heap); - if (! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time)) - { - struct GNUNET_HashCode hc; +}; - GNUNET_CRYPTO_hash_context_finish (hash_context, - &hc); - // if (GNUNET_OK != - // create_krd (ksh, - // &hc, - // last_cherry_pick_date, - // sctx.signkeys, - // recoup, - // grouped_denominations, - // &grouped_hash_xor)) - // { - // GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - // "Failed to generate key response data for %s\n", - // GNUNET_TIME_timestamp2s (last_cherry_pick_date)); - // goto CLEANUP; - // } - ksh->management_only = false; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "No donation unit keys available. Refusing to generate /keys response.\n"); - GNUNET_CRYPTO_hash_context_abort (hash_context); - } +/** + * Context for collecting the array of master signatures + * needed to verify the donau_sig online signature. + */ +struct SignatureContext +{ + /** + * Array of signatures to hash over. + */ + struct SignatureElement *elements; - ret = GNUNET_OK; + /** + * Write offset in the @e elements array. + */ + unsigned int elements_pos; -// CLEANUP: - json_decref (grouped_donation_units); - json_decref (sctx.signkeys); - json_decref (recoup); - return ret; -} + /** + * Allocated space for @e elements. + */ + unsigned int elements_size; +}; /** - * Free donation unit key data. + * Determine order to sort two elements by before + * we hash the master signatures. Used for + * sorting with qsort(). * - * @param cls a `struct DH_KeyStateHandle`, unused - * @param h_donation_unit_pub hash of the donation unit public key, unused - * @param value a `struct DH_DonationUnitKey` to free - * @return #GNUNET_OK (continue to iterate) + * @param a pointer to a `struct SignatureElement` + * @param b pointer to a `struct SignatureElement` + * @return 0 if equal, -1 if a < b, 1 if a > b. */ -static enum GNUNET_GenericReturnValue -clear_donation_unit_cb (void *cls, - const struct GNUNET_HashCode *h_donation_unit_pub, - void *value) +static int +signature_context_sort_cb (const void *a, + const void *b) { - struct DH_DonationUnitKey *dk = value; + const struct SignatureElement *sa = a; + const struct SignatureElement *sb = b; - (void) cls; - (void) h_donation_unit_pub; - // TALER_denom_pub_free (&dk->denom_pub); - GNUNET_free (dk); - return GNUNET_OK; + if (sa->group_offset < sb->group_offset) + return -1; + if (sa->group_offset > sb->group_offset) + return 1; + if (sa->offset < sb->offset) + return -1; + if (sa->offset > sb->offset) + return 1; + /* We should never have two disjoint elements + with same time and offset */ + GNUNET_assert (sa == sb); + return 0; } /** - * Free donation unit key data. - * - * @param cls a `struct DH_KeyStateHandle`, unused - * @param pid the online signing key (type-disguised), unused - * @param value a `struct SigningKey` to free - * @return #GNUNET_OK (continue to iterate) - */ -static enum GNUNET_GenericReturnValue -clear_signkey_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) + *GroupData is the value we store for each group meta-data */ +struct GroupData { - struct SigningKey *sk = value; + /** + * The json blob with the group meta-data and list of donation_units + */ + json_t *json; - (void) cls; - (void) pid; - GNUNET_free (sk); - return GNUNET_OK; -} + /** + * List of donation_units for the group, + * included in @e json, do not free separately! + */ + json_t *list; + + /** + * Offset of the group in the final array. + */ + unsigned int group_off; + +}; /** - * Clear memory for responses to "/keys" in @a ksh. + * Helper function called to clean up the group data + * in the donation_units_by_group below. * - * @param[in,out] ksh key state to update + * @param cls unused + * @param key unused + * @param value a `struct GroupData` to free + * @return #GNUNET_OK */ -static void -clear_response_cache (struct DH_KeyStateHandle *ksh) +static int +free_group (void *cls, + const struct GNUNET_HashCode *key, + void *value) { - for (unsigned int i = 0; i<ksh->krd_array_length; i++) - { - struct KeysResponseData *krd = &ksh->krd_array[i]; + struct GroupData *gd = value; - MHD_destroy_response (krd->response_compressed); - MHD_destroy_response (krd->response_uncompressed); - GNUNET_free (krd->etag); - } - GNUNET_array_grow (ksh->krd_array, - ksh->krd_array_length, - 0); + (void) cls; + (void) key; + GNUNET_free (gd); + return GNUNET_OK; } /** - * Synchronize helper state. Polls the key helper for updates. + * Update the "/keys" responses in @a ksh, computing the detailed replies. * - * @param[in,out] hs helper state to synchronize + * This function is to recompute all (including cherry-picked) responses we + * might want to return, based on the state already in @a ksh. + * + * @param[in,out] ksh state handle to update + * @return #GNUNET_OK on success */ -static void -sync_key_helpers (struct HelperState *hs) +static enum GNUNET_GenericReturnValue +finish_keys_response (struct DH_KeyStateHandle *ksh) { - TALER_CRYPTO_helper_rsa_poll (hs->rsadh); - TALER_CRYPTO_helper_cs_poll (hs->csdh); - TALER_CRYPTO_helper_esign_poll (hs->esh); -} + enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; + struct SignKeyCtx sctx = { + .min_sk_frequency = GNUNET_TIME_UNIT_FOREVER_REL + }; + json_t *grouped_donation_units = NULL; + struct GNUNET_TIME_Timestamp last_cherry_pick_date; + struct GNUNET_CONTAINER_Heap *heap; + struct SignatureContext sig_ctx = { 0 }; + sctx.signkeys = json_array (); + GNUNET_assert (NULL != sctx.signkeys); + grouped_donation_units = json_array (); + GNUNET_assert (NULL != grouped_donation_units); -/** - * Destroy helper state. Does NOT call free() on @a hs, as that - * state is not separately allocated! Dual to #setup_key_helpers(). - * - * @param[in] hs helper state to free, but NOT the @a hs pointer itself! - */ -static void -destroy_key_helpers (struct HelperState *hs) -{ - // GNUNET_CONTAINER_multihashmap_iterate (hs->denom_keys, - // &free_denom_cb, - // hs); - // GNUNET_CONTAINER_multihashmap_destroy (hs->rsa_keys); - // hs->rsa_keys = NULL; - // GNUNET_CONTAINER_multihashmap_destroy (hs->cs_keys); - // hs->cs_keys = NULL; - // GNUNET_CONTAINER_multihashmap_destroy (hs->denom_keys); - // hs->denom_keys = NULL; - // GNUNET_CONTAINER_multipeermap_iterate (hs->esign_keys, - // &free_esign_cb, - // hs); - // GNUNET_CONTAINER_multipeermap_destroy (hs->esign_keys); - hs->esign_keys = NULL; - if (NULL != hs->rsadh) - { - TALER_CRYPTO_helper_rsa_disconnect (hs->rsadh); - hs->rsadh = NULL; - } - if (NULL != hs->csdh) + GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map, + &add_sign_key_cb, + &sctx); + if (0 == json_array_size (sctx.signkeys)) { - TALER_CRYPTO_helper_cs_disconnect (hs->csdh); - hs->csdh = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No online signing keys available. Refusing to generate /keys response.\n"); + ret = GNUNET_NO; + goto CLEANUP; } - if (NULL != hs->esh) + heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX); { - TALER_CRYPTO_helper_esign_disconnect (hs->esh); - hs->esh = NULL; + struct DenomKeyCtx dkc = { + .heap = heap, + .min_dk_frequency = GNUNET_TIME_UNIT_FOREVER_REL, + }; + + GNUNET_CONTAINER_multihashmap_iterate (ksh->donation_unitkey_map, + &add_donation_unit_key_cb, + &dkc); + ksh->rekey_frequency + = GNUNET_TIME_relative_min (dkc.min_dk_frequency, + sctx.min_sk_frequency); } -} + last_cherry_pick_date = GNUNET_TIME_UNIT_ZERO_TS; + + { + struct DH_DonationUnitKey *dk; + struct GNUNET_CONTAINER_MultiHashMap *donation_units_by_group; + + donation_units_by_group = + GNUNET_CONTAINER_multihashmap_create (1024, + GNUNET_NO /* NO, because keys are only on the stack */); + /* heap = max heap, sorted by start time */ + while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap))) + { + //if (GNUNET_TIME_timestamp_cmp (last_cherry_pick_date, + // !=, + // dk->meta.start) && + // (! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time)) ) + //{ + // /* + // * This is not the first entry in the heap (because last_cherry_pick_date != + // * GNUNET_TIME_UNIT_ZERO_TS) and the previous entry had a different + // * start time. Therefore, we create a new entry in ksh. + // */ + // struct GNUNET_HashCode hc; +// + // if (GNUNET_OK != + // create_krd (ksh, + // &hc, + // last_cherry_pick_date, + // sctx.signkeys, + // recoup, + // grouped_donation_units)) + // { + // GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + // "Failed to generate key response data for %s\n", + // GNUNET_TIME_timestamp2s (last_cherry_pick_date)); + // /* drain heap before destroying it */ + // while (NULL != (dk = GNUNET_CONTAINER_heap_remove_root (heap))) + // /* intentionally empty */; + // GNUNET_CONTAINER_heap_destroy (heap); + // goto CLEANUP; + // } + //} + + //last_cherry_pick_date = dk->meta.start; + /* + * Group the donation_units by {cipher, value, fees, age_mask}. + * + * For each group we save the group meta-data and the list of + * donation_units in this group as a json-blob in the multihashmap + * donation_units_by_group. + */ + { + struct GroupData *group; + json_t *entry; + struct GNUNET_HashCode key; + struct DONAU_DonationUnitGroup meta = { + .cipher = dk->donation_unit_pub.bsign_pub_key->cipher, + // .value = dk->meta.value, + }; + + /* Search the group/JSON-blob for the key */ + //DONAU_donation_unit_group_get_key (&meta, + // &key); + group = GNUNET_CONTAINER_multihashmap_get ( + donation_units_by_group, + &key); + if (NULL == group) + { + /* There is no group for this meta-data yet, so we create a new group */ + const char *cipher; + + group = GNUNET_new (struct GroupData); + + /* Create a new array for the donation_units in this group */ + group->list = json_array (); + GNUNET_assert (NULL != group->list); + group->json = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("cipher", + cipher), + GNUNET_JSON_pack_array_steal ("donation_units", + group->list)); + //DONAU_JSON_pack_amount ("value", + // &meta.value)); + GNUNET_assert (NULL != group->json); + + group->group_off + = json_array_size (grouped_donation_units); + GNUNET_assert (0 == + json_array_append_new ( + grouped_donation_units, + group->json)); + GNUNET_assert ( + GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (donation_units_by_group, + &key, + group, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + } + + /* Now that we have found/created the right group, add the + donation_unit to the list */ + { + struct HelperDonationUnit *hd; + struct GNUNET_JSON_PackSpec key_spec; + bool private_key_lost; + + hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->donation_unit_keys, + &dk->h_donation_unit_pub.hash); + private_key_lost + = (NULL == hd) || + GNUNET_TIME_absolute_is_past ( + GNUNET_TIME_absolute_add ( + hd->start_time.abs_time, + hd->validity_duration)); + //switch (meta.cipher) + //{ + //case GNUNET_CRYPTO_BSA_RSA: + // key_spec = + // GNUNET_JSON_pack_rsa_public_key ( + // "rsa_pub", + // dk->donation_unit_pub.bsign_pub_key->details.rsa_public_key); + // break; + //case GNUNET_CRYPTO_BSA_CS: + // key_spec = + // GNUNET_JSON_pack_data_varsize ( + // "cs_pub", + // &dk->donation_unit_pub.bsign_pub_key->details.cs_public_key, + // sizeof (dk->donation_unit_pub.bsign_pub_key->details.cs_public_key)); + // break; + //default: + // GNUNET_assert (false); + //} + + entry = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_allow_null ( + private_key_lost + ? GNUNET_JSON_pack_bool ("lost", + true) + : GNUNET_JSON_pack_string ("dummy", + NULL)), + //GNUNET_JSON_pack_timestamp ("stamp_start", + // dk->meta.start), + //GNUNET_JSON_pack_timestamp ("stamp_expire_withdraw", + // dk->meta.expire_withdraw), + //GNUNET_JSON_pack_timestamp ("stamp_expire_deposit", + // dk->meta.expire_deposit), + //GNUNET_JSON_pack_timestamp ("stamp_expire_legal", + // dk->meta.expire_legal), + key_spec + ); + GNUNET_assert (NULL != entry); + } + + /* Finally, add the donation_unit to the list of donation_units in this + group */ + GNUNET_assert (json_is_array (group->list)); + GNUNET_assert (0 == + json_array_append_new (group->list, + entry)); + } + } /* loop over heap ends */ -/** - * Free resources associated with @a cls, possibly excluding - * the helper data. - * - * @param[in] ksh key state to release - * @param free_helper true to also release the helper state - */ -static void -destroy_key_state (struct DH_KeyStateHandle *ksh, - bool free_helper) -{ - // struct DH_GlobalFee *gf; + GNUNET_CONTAINER_multihashmap_iterate (donation_units_by_group, + &free_group, + NULL); + GNUNET_CONTAINER_multihashmap_destroy (donation_units_by_group); + } + GNUNET_CONTAINER_heap_destroy (heap); - clear_response_cache (ksh); - // while (NULL != (gf = ksh->gf_head)) - // { - // GNUNET_CONTAINER_DLL_remove (ksh->gf_head, - // ksh->gf_tail, - // gf); - // GNUNET_free (gf); - // } - GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map, - &clear_donation_unit_cb, - ksh); - GNUNET_CONTAINER_multihashmap_destroy (ksh->denomkey_map); - GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map, - &clear_signkey_cb, - ksh); - GNUNET_CONTAINER_multipeermap_destroy (ksh->signkey_map); - // json_decref (ksh->auditors); - // ksh->auditors = NULL; - // json_decref (ksh->global_fees); - // ksh->global_fees = NULL; - if (free_helper) + if (! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time)) { - destroy_key_helpers (ksh->helpers); - GNUNET_free (ksh->helpers); + struct GNUNET_HashCode hc; + + //if (GNUNET_OK != + // create_krd (ksh, + // &hc, + // last_cherry_pick_date, + // sctx.signkeys, + // grouped_donation_units)) + //{ + // GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + // "Failed to generate key response data for %s\n", + // GNUNET_TIME_timestamp2s (last_cherry_pick_date)); + // goto CLEANUP; + //} + ksh->management_only = false; + } - if (NULL != ksh->management_keys_reply) + else { - json_decref (ksh->management_keys_reply); - ksh->management_keys_reply = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No donation_unit keys available. Refusing to generate /keys response.\n"); } - GNUNET_free (ksh); -} - + ret = GNUNET_OK; -/** - * Setup helper state. - * - * @param[out] hs helper state to initialize - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -setup_key_helpers (struct HelperState *hs) -{ - // hs->denom_keys - // = GNUNET_CONTAINER_multihashmap_create (1024, - // GNUNET_YES); - hs->rsa_keys - = GNUNET_CONTAINER_multihashmap_create (1024, - GNUNET_YES); - hs->cs_keys - = GNUNET_CONTAINER_multihashmap_create (1024, - GNUNET_YES); - hs->esign_keys - = GNUNET_CONTAINER_multipeermap_create (32, - GNUNET_NO /* MUST BE NO! */); - // hs->rsadh = TALER_CRYPTO_helper_rsa_connect (DH_cfg, - // &helper_rsa_cb, - // hs); - // if (NULL == hs->rsadh) - // { - // destroy_key_helpers (hs); - // return GNUNET_SYSERR; - // } - // hs->csdh = TALER_CRYPTO_helper_cs_connect (DH_cfg, - // &helper_cs_cb, - // hs); - // if (NULL == hs->csdh) - // { - // destroy_key_helpers (hs); - // return GNUNET_SYSERR; - // } - // hs->esh = TALER_CRYPTO_helper_esign_connect (DH_cfg, - // &helper_esign_cb, - // hs); - // if (NULL == hs->esh) - // { - // destroy_key_helpers (hs); - // return GNUNET_SYSERR; - // } - return GNUNET_OK; +CLEANUP: + GNUNET_array_grow (sig_ctx.elements, + sig_ctx.elements_size, + 0); + json_decref (grouped_donation_units); + if (NULL != sctx.signkeys) + json_decref (sctx.signkeys); + return ret; } @@ -880,11 +1828,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; @@ -907,56 +1855,48 @@ build_key_state (struct HelperState *hs /*, { ksh->helpers = hs; } - ksh->denomkey_map = GNUNET_CONTAINER_multihashmap_create (1024, + ksh->donation_unitkey_map = GNUNET_CONTAINER_multihashmap_create (1024, true); ksh->signkey_map = GNUNET_CONTAINER_multipeermap_create (32, - false /* MUST be false! */ - ); + false /* MUST be false! */); /* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */ - // GNUNET_break (GNUNET_OK == - // DH_plugin->preflight (DH_plugin->cls)); - // if (qs < 0) - // { + GNUNET_break (GNUNET_OK == + DH_plugin->preflight (DH_plugin->cls)); + + //qs = DH_plugin->iterate_donation_units (DH_plugin->cls, + // &donation_unit_info_cb, + // ksh); + //if (qs < 0) + //{ // GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); // GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); // destroy_key_state (ksh, // true); // return NULL; - // } - // qs = DH_plugin->iterate_denominations (DH_plugin->cls, - // &denomination_info_cb, - // ksh); - // if (qs < 0) - // { - // GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); - // GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); - // destroy_key_state (ksh, - // true); - // return NULL; - // } - /* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */ - // qs = DH_plugin->iterate_active_signkeys (DH_plugin->cls, - // &signkey_info_cb, - // ksh); - // if (qs < 0) - // { + //} + ///* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */ + //qs = DH_plugin->iterate_active_signkeys (DH_plugin->cls, + // &signkey_info_cb, + // ksh); + //if (qs < 0) + //{ // GNUNET_break (0); // destroy_key_state (ksh, // true); // 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)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Could not finish /keys response (likely no signing keys available yet)\n"); + "Could not finish /keys response (required data not configured yet)\n"); destroy_key_state (ksh, true); return NULL; @@ -971,28 +1911,29 @@ DH_keys_update_states () { struct GNUNET_DB_EventHeaderP es = { .size = htons (sizeof (es)), - // .type = htons (TALER_DBEVENT_DONAU_KEYS_UPDATED), + .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED), }; DH_plugin->event_notify (DH_plugin->cls, - &es, - NULL, - 0); + &es, + NULL, + 0); key_generation++; - // DH_resume_keys_requests (false); + DH_resume_keys_requests (false); } static struct DH_KeyStateHandle * -DH_keys_get_state (/*bool management_only*/) +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 = NULL; + ksh = build_key_state (NULL, + management_only); if (NULL == ksh) return NULL; key_state = ksh; @@ -1005,8 +1946,8 @@ DH_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, @@ -1018,202 +1959,404 @@ DH_keys_get_state (/*bool management_only*/) } -/** - * Closure for #add_future_donation_unit_cb and #add_future_signkey_cb. - */ -struct FutureBuilderContext +struct DH_KeyStateHandle * +DH_keys_get_state_for_management_only (void) +{ + return keys_get_state (true); +} + + +struct DH_KeyStateHandle * +DH_keys_get_state (void) { - /** - * Our key state. - */ struct DH_KeyStateHandle *ksh; - /** - * Array of donation unit keys. - */ - json_t *donation_units; + ksh = keys_get_state (false); + if (NULL == ksh) + return NULL; - /** - * Array of signing keys. - */ - json_t *signkeys; + if (ksh->management_only) + { + if (GNUNET_OK != + finish_keys_response (ksh)) + return NULL; + } + + return ksh; +} + + +struct DH_DonationUnitKey * +DH_keys_donation_unit_by_hash ( + const struct DONAU_DonationUnitHashP *h_donation_unit_pub, + struct MHD_Connection *conn, + MHD_RESULT *mret) +{ + struct DH_KeyStateHandle *ksh; + + ksh = DH_keys_get_state (); + if (NULL == ksh) + { + *mret = TALER_MHD_reply_with_error (conn, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, + NULL); + return NULL; + } + + return DH_keys_donation_unit_by_hash_from_state (ksh, + h_donation_unit_pub, + conn, + mret); +} + + +struct DH_DonationUnitKey * +DH_keys_donation_unit_by_hash_from_state ( + const struct DH_KeyStateHandle *ksh, + const struct DONAU_DonationUnitHashP *h_donation_unit_pub, + struct MHD_Connection *conn, + MHD_RESULT *mret) +{ + struct DH_DonationUnitKey *dk; + + dk = GNUNET_CONTAINER_multihashmap_get (ksh->donation_unitkey_map, + &h_donation_unit_pub->hash); + if (NULL == dk) + { + if (NULL == conn) + return NULL; + //*mret = DH_RESPONSE_reply_unknown_donation_unit_pub_hash (conn, + // h_donation_unit_pub); + //return NULL; + } + return dk; +} + + +enum TALER_ErrorCode +DH_keys_donation_unit_batch_sign ( + unsigned int csds_length, + const struct DH_CoinSignData csds[static csds_length], + struct DONAU_BlindedDonationUnitSignature bss[static csds_length]) +{ + struct DH_KeyStateHandle *ksh; + struct HelperDonationUnit *hd; + struct TALER_CRYPTO_RsaSignRequest rsrs[csds_length]; + struct TALER_CRYPTO_CsSignRequest csrs[csds_length]; + struct DONAU_BlindedDonationUnitSignature rs[csds_length]; + struct DONAU_BlindedDonationUnitSignature cs[csds_length]; + unsigned int rsrs_pos = 0; + unsigned int csrs_pos = 0; + enum TALER_ErrorCode ec; + + ksh = DH_keys_get_state (); + if (NULL == ksh) + return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; + for (unsigned int i = 0; i<csds_length; i++) + { + const struct DONAU_DonationUnitHashP *h_donation_unit_pub = csds[i].h_donation_unit_pub; + const struct DONAU_BlindedDonationUnit *bp = csds[i].bp; + + hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->donation_unit_keys, + &h_donation_unit_pub->hash); + if (NULL == hd) + return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; + //if (bp->blinded_message->cipher != + // hd->donation_unit_pub.bsign_pub_key->cipher) + // return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + //switch (hd->donation_unit_pub.bsign_pub_key->cipher) + //{ + //case GNUNET_CRYPTO_BSA_RSA: + // rsrs[rsrs_pos].h_rsa = &hd->h_details.h_rsa; + // rsrs[rsrs_pos].msg + // = bp->blinded_message->details.rsa_blinded_message.blinded_msg; + // rsrs[rsrs_pos].msg_size + // = bp->blinded_message->details.rsa_blinded_message.blinded_msg_size; + // rsrs_pos++; + // break; + //case GNUNET_CRYPTO_BSA_CS: + // csrs[csrs_pos].h_cs = &hd->h_details.h_cs; + // csrs[csrs_pos].blinded_donation_unit + // = &bp->blinded_message->details.cs_blinded_message; + // csrs_pos++; + // break; + //default: + // return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + //} + } + + if ( (0 != csrs_pos) && + (0 != rsrs_pos) ) + { + memset (rs, + 0, + sizeof (rs)); + memset (cs, + 0, + sizeof (cs)); + } + ec = TALER_EC_NONE; + //if (0 != csrs_pos) + //{ + // ec = TALER_CRYPTO_helper_cs_batch_sign ( + // ksh->helpers->csdh, + // csrs_pos, + // csrs, + // (0 == rsrs_pos) ? bss : cs); + // if (TALER_EC_NONE != ec) + // { + // for (unsigned int i = 0; i<csrs_pos; i++) + // DONAU_blinded_donation_unit_sig_free (&cs[i]); + // return ec; + // } + //} + if (0 != rsrs_pos) + { + ec = TALER_CRYPTO_helper_rsa_batch_sign ( + ksh->helpers->rsadh, + rsrs_pos, + rsrs, + (0 == csrs_pos) ? bss : rs); + if (TALER_EC_NONE != ec) + { + for (unsigned int i = 0; i<csrs_pos; i++) + DONAU_blinded_donation_unit_sig_free (&cs[i]); + for (unsigned int i = 0; i<rsrs_pos; i++) + DONAU_blinded_donation_unit_sig_free (&rs[i]); + return ec; + } + } + + if ( (0 != csrs_pos) && + (0 != rsrs_pos) ) + { + rsrs_pos = 0; + csrs_pos = 0; + for (unsigned int i = 0; i<csds_length; i++) + { + const struct DONAU_BlindedDonationUnit *bp = csds[i].bp; + + //switch (bp->blinded_message->cipher) + //{ + //case GNUNET_CRYPTO_BSA_RSA: + // bss[i] = rs[rsrs_pos++]; + // break; + //case GNUNET_CRYPTO_BSA_CS: + // bss[i] = cs[csrs_pos++]; + // break; + //default: + // GNUNET_assert (0); + //} + } + } + return TALER_EC_NONE; +} -}; -// /** -// * 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); -// } +//enum TALER_ErrorCode +//DH_keys_donation_unit_cs_r_pub ( +// const struct DH_CsDeriveData *cdd, +// struct GNUNET_CRYPTO_CSPublicRPairP *r_pub) +//{ +// const struct DONAU_DonationUnitHashP *h_donation_unit_pub = cdd->h_donation_unit_pub; +// const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdd->nonce; +// struct DH_KeyStateHandle *ksh; +// struct HelperDonationUnit *hd; +// +// ksh = DH_keys_get_state (); +// if (NULL == ksh) +// { +// return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; +// } +// hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->donation_unit_keys, +// &h_donation_unit_pub->hash); +// if (NULL == hd) +// { +// return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; +// } +// if (GNUNET_CRYPTO_BSA_CS != +// hd->donation_unit_pub.bsign_pub_key->cipher) +// { +// return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; +// } +// +// { +// struct TALER_CRYPTO_CsDeriveRequest cdr = { +// .h_cs = &hd->h_details.h_cs, +// .nonce = nonce +// }; +// return TALER_CRYPTO_helper_cs_r_derive (ksh->helpers->csdh, +// &cdr, +// r_pub); +// } +//} + + +//enum TALER_ErrorCode +//DH_keys_donation_unit_cs_batch_r_pub ( +// unsigned int cdds_length, +// const struct DH_CsDeriveData cdds[static cdds_length], +// struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[static cdds_length]) +//{ +// struct DH_KeyStateHandle *ksh; +// struct HelperDonationUnit *hd; +// struct TALER_CRYPTO_CsDeriveRequest cdrs[cdds_length]; +// +// ksh = DH_keys_get_state (); +// if (NULL == ksh) +// { +// return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; +// } +// for (unsigned int i = 0; i<cdds_length; i++) +// { +// const struct DONAU_DonationUnitHashP *h_donation_unit_pub = cdds[i].h_donation_unit_pub; +// const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdds[i].nonce; +// +// hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->donation_unit_keys, +// &h_donation_unit_pub->hash); +// if (NULL == hd) +// { +// return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; +// } +// if (GNUNET_CRYPTO_BSA_CS != +// hd->donation_unit_pub.bsign_pub_key->cipher) +// { +// return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; +// } +// cdrs[i].h_cs = &hd->h_details.h_cs; +// cdrs[i].nonce = nonce; +// } +// +// return TALER_CRYPTO_helper_cs_r_batch_derive (ksh->helpers->csdh, +// cdds_length, +// cdrs, +// r_pubs); +//} + + +void +DH_keys_donation_unit_revoke (const struct DONAU_DonationUnitHashP *h_donation_unit_pub) +{ + struct DH_KeyStateHandle *ksh; + struct HelperDonationUnit *hd; + + ksh = DH_keys_get_state (); + if (NULL == ksh) + { + GNUNET_break (0); + return; + } + hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->donation_unit_keys, + &h_donation_unit_pub->hash); + if (NULL == hd) + { + GNUNET_break (0); + return; + } + switch (hd->donation_unit_pub.bsign_pub_key->cipher) + { + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + TALER_CRYPTO_helper_rsa_revoke (ksh->helpers->rsadh, + &hd->h_details.h_rsa); + DH_keys_update_states (); + return; + case GNUNET_CRYPTO_BSA_CS: + TALER_CRYPTO_helper_cs_revoke (ksh->helpers->csdh, + &hd->h_details.h_cs); + DH_keys_update_states (); + return; + } + GNUNET_break (0); + return; +} + + +//enum TALER_ErrorCode +//DH_keys_donau_sign_ ( +// const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, +// struct DONAU_DonauPublicKeyP *pub, +// struct DONAU_DonauSignatureP *sig) +//{ +// struct DH_KeyStateHandle *ksh; +// +// ksh = DH_keys_get_state (); +// if (NULL == ksh) +// { +// /* This *can* happen if the exchange's crypto helper is not running +// or had some bad error. */ +// GNUNET_log (GNUNET_ERROR_TYPE_ERROR, +// "Cannot sign request, no valid signing keys available.\n"); +// return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING; +// } +// return DH_keys_donau_sign2_ (ksh, +// purpose, +// pub, +// sig); +//} +// +// +//enum TALER_ErrorCode +//DH_keys_donau_sign2_ ( +// void *cls, +// const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, +// struct DONAU_DonauPublicKeyP *pub, +// struct DONAU_DonauSignatureP *sig) +//{ +// struct DH_KeyStateHandle *ksh = cls; +// enum TALER_ErrorCode ec; +// +// ec = TALER_CRYPTO_helper_esign_sign_ (ksh->helpers->esh, +// purpose, +// pub, +// sig); +// if (TALER_EC_NONE != ec) +// return ec; +// { +// /* Here we check here that 'pub' is set to an exchange public key that is +// actually signed by the master key! Otherwise, we happily continue to +// use key material even if the offline signatures have not been made +// yet! */ +// struct GNUNET_PeerIdentity pid; +// struct SigningKey *sk; +// +// pid.public_key = pub->eddsa_pub; +// sk = GNUNET_CONTAINER_multipeermap_get (ksh->signkey_map, +// &pid); +// if (NULL == sk) +// { +// /* just to be safe, zero out the (valid) signature, as the key +// should not or no longer be used */ +// GNUNET_log (GNUNET_ERROR_TYPE_WARNING, +// "Cannot sign, offline key signatures are missing!\n"); +// memset (sig, +// 0, +// sizeof (*sig)); +// return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG; +// } +// } +// return ec; +//} + + +void +DH_keys_donau_revoke (const struct DONAU_DonauPublicKeyP *donau_pub) +{ + struct DH_KeyStateHandle *ksh; + + ksh = DH_keys_get_state (); + if (NULL == ksh) + { + GNUNET_break (0); + return; + } + TALER_CRYPTO_helper_esign_revoke (ksh->helpers->esh, + donau_pub); + DH_keys_update_states (); +} + /** * Comparator used for a binary search by cherry_pick_date for @a key in the @@ -1242,48 +2385,9 @@ krd_search_comparator (const void *key, } -/** - * Callback used to set headers in a response. - * - * @param cls closure - * @param[in,out] resp response to modify - */ -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) -{ - 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_keys_get_handler (struct DH_RequestContext *rc, - const char *const args[]) + const char *const args[]) { struct GNUNET_TIME_Timestamp last_issue_date; const char *etag; @@ -1292,38 +2396,38 @@ DH_keys_get_handler (struct DH_RequestContext *rc, 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; - // } - // } + { + 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; + } + } { struct DH_KeyStateHandle *ksh; @@ -1375,10 +2479,10 @@ DH_keys_get_handler (struct DH_RequestContext *rc, if ( (NULL != etag) && (0 == strcmp (etag, krd->etag)) ) - return DH_RESPONSE_reply_not_modified (rc->connection, - krd->etag, - &setup_general_response_headers, - ksh); + //return DH_RESPONSE_reply_not_modified (rc->connection, + // krd->etag, + // &setup_general_response_headers, + // ksh); return MHD_queue_response (rc->connection, MHD_HTTP_OK, @@ -1390,4 +2494,251 @@ DH_keys_get_handler (struct DH_RequestContext *rc, } +enum GNUNET_GenericReturnValue +DH_keys_get_timing (const struct DONAU_DonauPublicKeyP *donau_pub, + struct DONAUDB_SignkeyMetaData *meta) +{ + struct DH_KeyStateHandle *ksh; + struct HelperSignkey *hsk; + struct GNUNET_PeerIdentity pid; + + ksh = DH_keys_get_state_for_management_only (); + if (NULL == ksh) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + pid.public_key = donau_pub->eddsa_pub; + hsk = GNUNET_CONTAINER_multipeermap_get (ksh->helpers->esign_keys, + &pid); + if (NULL == hsk) + { + GNUNET_break (0); + return GNUNET_NO; + } + meta->start = hsk->start_time; + + //meta->expire_sign = GNUNET_TIME_absolute_to_timestamp ( + // GNUNET_TIME_absolute_add (meta->start.abs_time, + // hsk->validity_duration)); + //meta->expire_legal = GNUNET_TIME_absolute_to_timestamp ( + // GNUNET_TIME_absolute_add (meta->expire_sign.abs_time, + // signkey_legal_duration)); + return GNUNET_OK; +} + + +/** + * Closure for #add_future_donation_unitkey_cb and #add_future_signkey_cb. + */ +struct FutureBuilderContext +{ + /** + * Our key state. + */ + struct DH_KeyStateHandle *ksh; + + /** + * Array of donation_unit keys. + */ + json_t *donation_units; + + /** + * Array of signing keys. + */ + json_t *signkeys; + +}; + + +/** + * 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 HelperDonationUnit` + * @return #GNUNET_OK (continue to iterate) + */ +static enum GNUNET_GenericReturnValue +add_future_donation_unitkey_cb (void *cls, + const struct GNUNET_HashCode *h_donation_unit_pub, + void *value) +{ + struct FutureBuilderContext *fbc = cls; + struct HelperDonationUnit *hd = value; + struct DH_DonationUnitKey *dk; + struct DONAUDB_DonationUnitKeyMetaData meta = {0}; + + dk = GNUNET_CONTAINER_multihashmap_get (fbc->ksh->donation_unitkey_map, + h_donation_unit_pub); + if (NULL != dk) + 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! */ + //meta.start = hd->start_time; + //meta.expire_withdraw = GNUNET_TIME_absolute_to_timestamp ( + // GNUNET_TIME_absolute_add (meta.start.abs_time, + // hd->validity_duration)); + + //GNUNET_assert ( + // 0 == + // json_array_append_new ( + // fbc->donation_units, + // GNUNET_JSON_PACK ( + // //DONAU_JSON_pack_amount ("value", + // // &meta.value), + // //GNUNET_JSON_pack_timestamp ("stamp_start", + // // meta.start), + // //GNUNET_JSON_pack_timestamp ("stamp_expire_withdraw", + // // meta.expire_withdraw), + // //GNUNET_JSON_pack_timestamp ("stamp_expire_deposit", + // // meta.expire_deposit), + // //GNUNET_JSON_pack_timestamp ("stamp_expire_legal", + // // meta.expire_legal), + // DONAU_JSON_pack_donation_unit_pub ("donation_unit_pub", + // &hd->donation_unit_pub), + // GNUNET_JSON_pack_data_auto ("donation_unit_secmod_sig", + // &hd->sm_sig), + // GNUNET_JSON_pack_string ("section_name", + // hd->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 HelperDonationUnit` + * @return #GNUNET_OK (continue to iterate) + */ +static enum GNUNET_GenericReturnValue +add_future_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_start", + hsk->start_time), + GNUNET_JSON_pack_timestamp ("stamp_expire", + stamp_expire), + GNUNET_JSON_pack_timestamp ("stamp_end", + legal_end), + GNUNET_JSON_pack_data_auto ("signkey_secmod_sig", + &hsk->sm_sig)))); + return GNUNET_OK; +} + + +MHD_RESULT +DH_keys_management_get_keys_handler (const struct DH_RequestHandler *rh, + struct MHD_Connection *connection) +{ + struct DH_KeyStateHandle *ksh; + json_t *reply; + + (void) rh; + ksh = DH_keys_get_state_for_management_only (); + 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_units 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_future_donation_unitkey_cb, + &fbc); + GNUNET_CONTAINER_multipeermap_iterate (ksh->helpers->esign_keys, + &add_future_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 /management/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); +} + + /* end of donau-httpd_keys.c */ diff --git a/src/donau/donau-httpd_keys.h b/src/donau/donau-httpd_keys.h @@ -1,150 +1,262 @@ /* This file is part of TALER - Copyright (C) 2020-2023 Taler Systems SA + Copyright (C) 2020-2022 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 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 CHARITYABILITY or FITNESS FOR + 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, see <http://www.gnu.org/licenses/> */ /** - * @file donau-httpd_keys.h + * @file taler-donau-httpd_keys.h * @brief management of our various keys * @author Christian Grothoff */ #include "taler/platform.h" #include "taler/taler_json_lib.h" #include "taler/taler_mhd_lib.h" -// #include "donau-httpd_responses.h" +//#include "taler/taler-donau-httpd_responses.h" #include "donau_util.h" #include "donaudb_plugin.h" #include "donau-httpd.h" - #ifndef DONAU_HTTPD_KEYS_H #define DONAU_HTTPD_KEYS_H /** - * @brief All information about a donation unit key (which is used to - * sign donation receipts into existence). + * @brief All information about a donation_unit key (which is used to + * sign coins into existence). */ struct DH_DonationUnitKey { /** - * Decoded donation unit public key (the hash of it is in + * Decoded donation_unit public key (the hash of it is in * @e issue, but we sometimes need the full public key as well). */ struct DONAU_DonationUnitPublicKey donation_unit_pub; /** - * Hash code of the donation unit public key. + * Hash code of the donation_unit public key. */ struct DONAU_DonationUnitHashP h_donation_unit_pub; /** - * Meta data about the type of the donation unit, containing the validity - * year and the value of the donation unit. + * Meta data about the type of the donation_unit, such as fees and validity + * periods. */ struct DONAUDB_DonationUnitKeyMetaData meta; }; /** - * Information needed to create a blind signature. + * Snapshot of the (coin and signing) keys (including private keys) of + * the donau. There can be multiple instances of this struct, as it is + * reference counted and only destroyed once the last user is done + * with it. The current instance is acquired using + * #DH_KS_acquire(). Using this function increases the + * reference count. The contents of this structure (except for the + * reference counter) should be considered READ-ONLY until it is + * ultimately destroyed (as there can be many concurrent users). + */ +struct DH_KeyStateHandle; + + +/** + * Run internal invariant checks. For debugging. + */ +void +DH_check_invariants (void); + +/** + * Clean up wire subsystem. */ -// struct DH_CoinSignData -// { +void +DH_wire_done (void); + /** + * Initialize wire subsystem. + * + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +DH_wire_init (void); + + +/** + * Something changed in the database. Rebuild the wire replies. This function + * should be called if the donau learns about a new signature from our + * master key. + * + * (We do not do so immediately, but merely signal to all threads that they + * need to rebuild their wire state upon the next call to + * #DH_keys_get_state()). + */ +void +DH_wire_update_state (void); + + +/** + * Return the current key state for this thread. Possibly re-builds the key + * state if we have reason to believe that something changed. + * + * The result is ONLY valid until the next call to + * #DH_keys_donation_unit_by_hash() or #DH_keys_get_state() + * or #DH_keys_donau_sign(). + * + * @return NULL on error + */ +struct DH_KeyStateHandle * +DH_keys_get_state (void); + +/** + * Obtain the key state if we should NOT run finish_keys_response() because we + * only need the state for the /management/keys API + */ +struct DH_KeyStateHandle * +DH_keys_get_state_for_management_only (void); + +/** + * Something changed in the database. Rebuild all key states. This function + * should be called if the donau learns about a new signature from an + * auditor or our master key. + * + * (We do not do so immediately, but merely signal to all threads that they + * need to rebuild their key state upon the next call to + * #DH_keys_get_state()). + */ +void +DH_keys_update_states (void); + + +/** + * Look up the issue for a denom public key. Note that the result + * must only be used in this thread and only until another key or + * key state is resolved. + * + * @param h_donation_unit_pub hash of donation_unit public key + * @param[in,out] conn used to return status message if NULL is returned + * @param[out] mret set to the MHD status if NULL is returned + * @return the donation_unit key issue, + * or NULL if @a h_donation_unit_pub could not be found + */ +struct DH_DonationUnitKey * +DH_keys_donation_unit_by_hash ( + const struct DONAU_DonationUnitHashP *h_donation_unit_pub, + struct MHD_Connection *conn, + MHD_RESULT *mret); + + +/** + * Look up the issue for a denom public key using a given @a ksh. This allows + * requesting multiple donation_units with the same @a ksh which thus will + * remain valid until the next call to #DH_keys_donation_unit_by_hash() or + * #DH_keys_get_state() or #DH_keys_donau_sign(). + * + * @param ksh key state state to look in + * @param h_donation_unit_pub hash of donation_unit public key + * @param[in,out] conn connection used to return status message if NULL is returned + * @param[out] mret set to the MHD status if NULL is returned + * @return the donation_unit key issue, + * or NULL if @a h_donation_unit_pub could not be found + */ +struct DH_DonationUnitKey * +DH_keys_donation_unit_by_hash_from_state ( + const struct DH_KeyStateHandle *ksh, + const struct DONAU_DonationUnitHashP *h_donation_unit_pub, + struct MHD_Connection *conn, + MHD_RESULT *mret); + +/** + * Information needed to create a blind signature. + */ +struct DH_CoinSignData +{ + /** * Hash of key to sign with. */ -// const struct TALER_DenominationHashP *h_denom_pub; + const struct DONAU_DonationUnitHashP *h_donation_unit_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]); + const struct DONAU_BlindedDonationUnit *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[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 +DH_keys_donation_unit_batch_sign ( + unsigned int csds_length, + const struct DH_CoinSignData csds[static csds_length], + struct DONAU_BlindedDonationUnitSignature bss[static csds_length]); + + +/** + * Information needed to derive the CS r_pub. + */ +struct DH_CsDeriveData +{ + /** + * Hash of key to sign with. + */ + const struct DONAU_DonationUnitHashP *h_donation_unit_pub; + + /** + * Nonce to use. + */ + const struct GNUNET_CRYPTO_CsSessionNonce *nonce; +}; + + +/** + * Request to derive CS @a r_pub using the donation_unit and nonce from @a cdd. + * + * @param cdd data to compute @a r_pub from + * @param[out] r_pub where to write the result + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +DH_keys_donation_unit_cs_r_pub ( + const struct DH_CsDeriveData *cdd, + struct GNUNET_CRYPTO_CSPublicRPairP *r_pub); + + +/** + * Request to derive a bunch of CS @a r_pubs using the + * donation_units and nonces from @a cdds. + * + * @param cdds array to compute @a r_pubs from + * @param cdds_length length of the @a cdds array + * @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 +DH_keys_donation_unit_cs_batch_r_pub ( + unsigned int cdds_length, + const struct DH_CsDeriveData cdds[static cdds_length], + struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[static cdds_length]); /** * Fully clean up keys subsystem. */ void -TEH_keys_finished (void); +DH_keys_finished (void); /** @@ -154,7 +266,110 @@ TEH_keys_finished (void); * @param do_shutdown are we shutting down? */ void -TEH_resume_keys_requests (bool do_shutdown); +DH_resume_keys_requests (bool do_shutdown); + + +/** + * Sign the message in @a purpose with the donau's signing key. + * + * The @a purpose data is the beginning of the data of which the signature is + * to be created. The `size` field in @a purpose must correctly indicate the + * number of bytes of the data structure, including its header. Use + * #DH_keys_donau_sign() instead of calling this function directly! + * + * @param purpose the message to sign + * @param[out] pub set to the current public signing key of the donau + * @param[out] sig signature over purpose using current signing key + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +DH_keys_donau_sign_ ( + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct DONAU_DonauPublicKeyP *pub, + struct DONAU_DonauSignatureP *sig); + + +/** + * Sign the message in @a purpose with the donau's signing key. + * + * The @a purpose data is the beginning of the data of which the signature is + * to be created. The `size` field in @a purpose must correctly indicate the + * number of bytes of the data structure, including its header. Use + * #DH_keys_donau_sign() instead of calling this function directly! + * + * @param cls key state state to look in + * @param purpose the message to sign + * @param[out] pub set to the current public signing key of the donau + * @param[out] sig signature over purpose using current signing key + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +DH_keys_donau_sign2_ ( + void *cls, + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct DONAU_DonauPublicKeyP *pub, + struct DONAU_DonauSignatureP *sig); + + +/** + * @ingroup crypto + * @brief EdDSA sign a given block. + * + * The @a ps data must be a fixed-size struct for which the signature is to be + * created. The `size` field in @a ps->purpose must correctly indicate the + * number of bytes of the data structure, including its header. + * + * @param ps packed struct with what to sign, MUST begin with a purpose + * @param[out] pub where to store the public key to use for the signing + * @param[out] sig where to write the signature + * @return #TALER_EC_NONE on success + */ +#define DH_keys_donau_sign(ps,pub,sig) \ + ({ \ + /* check size is set correctly */ \ + GNUNET_assert (htonl ((ps)->purpose.size) == \ + sizeof (*ps)); \ + /* check 'ps' begins with the purpose */ \ + GNUNET_static_assert (((void*) (ps)) == \ + ((void*) &(ps)->purpose)); \ + DH_keys_donau_sign_ (&(ps)->purpose, \ + pub, \ + sig); \ + }) + + +/** + * @ingroup crypto + * @brief EdDSA sign a given block. + * + * The @a ps data must be a fixed-size struct for which the signature is to be + * created. The `size` field in @a ps->purpose must correctly indicate the + * number of bytes of the data structure, including its header. + * + * This allows requesting multiple donation_units with the same @a ksh which + * thus will remain valid until the next call to + * #DH_keys_donation_unit_by_hash() or #DH_keys_get_state() or + * #DH_keys_donau_sign(). + * + * @param ksh key state to use + * @param ps packed struct with what to sign, MUST begin with a purpose + * @param[out] pub where to store the public key to use for the signing + * @param[out] sig where to write the signature + * @return #TALER_EC_NONE on success + */ +#define DH_keys_donau_sign2(ksh,ps,pub,sig) \ + ({ \ + /* check size is set correctly */ \ + GNUNET_assert (htonl ((ps)->purpose.size) == \ + sizeof (*ps)); \ + /* check 'ps' begins with the purpose */ \ + GNUNET_static_assert (((void*) (ps)) == \ + ((void*) &(ps)->purpose)); \ + DH_keys_donau_sign2_ (ksh, \ + &(ps)->purpose, \ + pub, \ + sig); \ + }) /** @@ -167,7 +382,32 @@ TEH_resume_keys_requests (bool do_shutdown); */ MHD_RESULT DH_keys_get_handler (struct DH_RequestContext *rc, - const char *const args[]); + const char *const args[]); + + +/** + * Function to call to handle requests to "/management/keys" by sending + * back our future key material. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @return MHD result code + */ +MHD_RESULT +DH_keys_management_get_keys_handler (const struct DH_RequestHandler *rh, + struct MHD_Connection *connection); + + +/** + * Load expiration times for the given onling signing key. + * + * @param donau_pub the online signing key + * @param[out] meta set to meta data about the key + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +DH_keys_get_timing (const struct DONAU_DonauPublicKeyP *donau_pub, + struct DONAUDB_SignkeyMetaData *meta); /** @@ -176,7 +416,7 @@ DH_keys_get_handler (struct DH_RequestContext *rc, * @return #GNUNET_OK on success */ enum GNUNET_GenericReturnValue -TEH_keys_init (void); +DH_keys_init (void); #endif