summaryrefslogtreecommitdiff
path: root/src/exchange/taler-exchange-httpd_keys.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchange/taler-exchange-httpd_keys.c')
-rw-r--r--src/exchange/taler-exchange-httpd_keys.c2789
1 files changed, 2285 insertions, 504 deletions
diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c
index 705900206..0ec28e950 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -1,31 +1,35 @@
/*
- This file is part of TALER
- Copyright (C) 2020, 2021 Taler Systems SA
+ This file is part of TALER
+ Copyright (C) 2020-2023 Taler Systems SA
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
+ 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 taler-exchange-httpd_keys.c
* @brief management of our various keys
* @author Christian Grothoff
+ * @author Özgür Kesim
*/
#include "platform.h"
#include "taler_json_lib.h"
#include "taler_mhd_lib.h"
+#include "taler_kyclogic_lib.h"
#include "taler_dbevents.h"
#include "taler-exchange-httpd.h"
+#include "taler-exchange-httpd_config.h"
#include "taler-exchange-httpd_keys.h"
#include "taler-exchange-httpd_responses.h"
#include "taler_exchangedb_plugin.h"
+#include "taler_extensions.h"
/**
@@ -36,21 +40,9 @@
/**
- * Taler protocol version in the format CURRENT:REVISION:AGE
- * as used by GNU libtool. See
- * https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
- *
- * Please be very careful when updating and follow
- * https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
- * precisely. Note that this version has NOTHING to do with the
- * release version, and the format is NOT the same that semantic
- * versioning uses either.
- *
- * When changing this version, you likely want to also update
- * #TALER_PROTOCOL_CURRENT and #TALER_PROTOCOL_AGE in
- * exchange_api_handle.c!
+ * When do we forcefully timeout a /keys request?
*/
-#define EXCHANGE_PROTOCOL_VERSION "9:0:0"
+#define KEYS_TIMEOUT GNUNET_TIME_UNIT_MINUTES
/**
@@ -62,7 +54,7 @@ struct HelperDenomination
/**
* When will the helper start to use this key for signing?
*/
- struct GNUNET_TIME_Absolute start_time;
+ struct GNUNET_TIME_Timestamp start_time;
/**
* For how long will the helper allow signing? 0 if
@@ -71,9 +63,9 @@ struct HelperDenomination
struct GNUNET_TIME_Relative validity_duration;
/**
- * Hash of the denomination key.
+ * Hash of the full denomination key.
*/
- struct GNUNET_HashCode h_denom_pub;
+ struct TALER_DenominationHashP h_denom_pub;
/**
* Signature over this key from the security module's key.
@@ -86,10 +78,29 @@ struct HelperDenomination
struct TALER_DenominationPublicKey denom_pub;
/**
+ * Details depend on the @e denom_pub.cipher type.
+ */
+ union
+ {
+
+ /**
+ * Hash of the RSA key.
+ */
+ struct TALER_RsaPubHashP h_rsa;
+
+ /**
+ * Hash of the CS key.
+ */
+ struct TALER_CsPubHashP h_cs;
+
+ } h_details;
+
+ /**
* Name in configuration section for this denomination type.
*/
char *section_name;
+
};
@@ -129,7 +140,7 @@ struct HelperSignkey
/**
* When will the helper start to use this key for signing?
*/
- struct GNUNET_TIME_Absolute start_time;
+ struct GNUNET_TIME_Timestamp start_time;
/**
* For how long will the helper allow signing? 0 if
@@ -151,10 +162,9 @@ struct HelperSignkey
/**
- * State associated with the crypto helpers / security modules.
- * Created per-thread, but NOT updated when the #key_generation
- * is updated (instead constantly kept in sync whenever
- * #TEH_keys_get_state() is called).
+ * State associated with the crypto helpers / security modules. NOT updated
+ * when the #key_generation is updated (instead constantly kept in sync
+ * whenever #TEH_keys_get_state() is called).
*/
struct HelperState
{
@@ -167,7 +177,12 @@ struct HelperState
/**
* Handle for the denom/RSA helper.
*/
- struct TALER_CRYPTO_DenominationHelper *dh;
+ struct TALER_CRYPTO_RsaDenominationHelper *rsadh;
+
+ /**
+ * Handle for the denom/CS helper.
+ */
+ struct TALER_CRYPTO_CsDenominationHelper *csdh;
/**
* Map from H(denom_pub) to `struct HelperDenomination` entries.
@@ -175,6 +190,16 @@ struct HelperState
struct GNUNET_CONTAINER_MultiHashMap *denom_keys;
/**
+ * 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;
+
+ /**
* Map from `struct TALER_ExchangePublicKey` to `struct HelperSignkey`
* entries. Based on the fact that a `struct GNUNET_PeerIdentity` is also
* an EdDSA public key.
@@ -203,12 +228,17 @@ struct KeysResponseData
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_Absolute cherry_pick_date;
+ struct GNUNET_TIME_Timestamp cherry_pick_date;
};
@@ -238,7 +268,6 @@ struct SigningKey
};
-
struct TEH_KeyStateHandle
{
@@ -256,12 +285,28 @@ struct TEH_KeyStateHandle
struct GNUNET_CONTAINER_MultiPeerMap *signkey_map;
/**
+ * Head of DLL of our global fees.
+ */
+ struct TEH_GlobalFee *gf_head;
+
+ /**
+ * Tail of DLL of our global fees.
+ */
+ struct TEH_GlobalFee *gf_tail;
+
+ /**
* json array with the auditors of this exchange. Contains exactly
* the information needed for the "auditors" field of the /keys response.
*/
json_t *auditors;
/**
+ * json array with the global fees of this exchange. Contains exactly
+ * the information needed for the "global_fees" field of the /keys response.
+ */
+ json_t *global_fees;
+
+ /**
* Sorted array of responses to /keys (MUST be sorted by cherry-picking date) of
* length @e krd_array_length;
*/
@@ -293,18 +338,19 @@ struct TEH_KeyStateHandle
/**
* When did we initiate the key reloading?
*/
- struct GNUNET_TIME_Absolute reload_time;
+ struct GNUNET_TIME_Timestamp reload_time;
/**
- * When is the next key invalid and we expect to have a different reply?
+ * What is the period at which we rotate keys
+ * (signing or denomination keys)?
*/
- struct GNUNET_TIME_Absolute next_reload;
+ struct GNUNET_TIME_Relative rekey_frequency;
/**
* When does our online signing key expire and we
* thus need to re-generate this response?
*/
- struct GNUNET_TIME_Absolute signature_expires;
+ struct GNUNET_TIME_Timestamp signature_expires;
/**
* True if #finish_keys_response() was not yet run and this key state
@@ -335,17 +381,128 @@ struct SuspendedKeysRequests
* The suspended connection.
*/
struct MHD_Connection *connection;
+
+ /**
+ * When does this request timeout?
+ */
+ struct GNUNET_TIME_Absolute timeout;
+};
+
+
+/**
+ * Information we track about wire fees.
+ */
+struct WireFeeSet
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct WireFeeSet *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct WireFeeSet *prev;
+
+ /**
+ * Actual fees.
+ */
+ struct TALER_WireFeeSet fees;
+
+ /**
+ * Start date of fee validity (inclusive).
+ */
+ struct GNUNET_TIME_Timestamp start_date;
+
+ /**
+ * End date of fee validity (exclusive).
+ */
+ struct GNUNET_TIME_Timestamp end_date;
+
+ /**
+ * Wire method the fees apply to.
+ */
+ char *method;
+};
+
+
+/**
+ * State we keep per thread to cache the /wire response.
+ */
+struct WireStateHandle
+{
+
+ /**
+ * JSON reply for /wire response.
+ */
+ json_t *json_reply;
+
+ /**
+ * ETag for this response (if any).
+ */
+ char *etag;
+
+ /**
+ * head of DLL of wire fees.
+ */
+ struct WireFeeSet *wfs_head;
+
+ /**
+ * Tail of DLL of wire fees.
+ */
+ struct WireFeeSet *wfs_tail;
+
+ /**
+ * Earliest timestamp of all the wire methods when we have no more fees.
+ */
+ struct GNUNET_TIME_Absolute cache_expiration;
+
+ /**
+ * @e cache_expiration time, formatted.
+ */
+ char dat[128];
+
+ /**
+ * For which (global) wire_generation was this data structure created?
+ * Used to check when we are outdated and need to be re-generated.
+ */
+ uint64_t wire_generation;
+
+ /**
+ * Is the wire data ready?
+ */
+ bool ready;
+
};
/**
+ * Stores the latest generation of our wire response.
+ */
+static struct WireStateHandle *wire_state;
+
+/**
+ * Handler listening for wire updates by other exchange
+ * services.
+ */
+static struct GNUNET_DB_EventHandler *wire_eh;
+
+/**
+ * Counter incremented whenever we have a reason to re-build the #wire_state
+ * because something external changed.
+ */
+static uint64_t wire_generation;
+
+
+/**
* Stores the latest generation of our key state.
*/
static struct TEH_KeyStateHandle *key_state;
/**
* Counter incremented whenever we have a reason to re-build the keys because
- * something external changed (in another thread). See #TEH_keys_get_state() and
+ * something external changed. See #TEH_keys_get_state() and
* #TEH_keys_update_states() for uses of this variable.
*/
static uint64_t key_generation;
@@ -379,15 +536,30 @@ static unsigned int skr_size;
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 denom_sm_pub;
+static struct TALER_SecurityModulePublicKeyP denom_rsa_sm_pub;
+
+/**
+ * CS security module public key, all zero if not known.
+ */
+static struct TALER_SecurityModulePublicKeyP denom_cs_sm_pub;
/**
* EdDSA security module public key, all zero if not known.
@@ -399,6 +571,483 @@ static struct TALER_SecurityModulePublicKeyP esign_sm_pub;
*/
static bool terminating;
+
+/**
+ * Free memory associated with @a wsh
+ *
+ * @param[in] wsh wire state to destroy
+ */
+static void
+destroy_wire_state (struct WireStateHandle *wsh)
+{
+ struct WireFeeSet *wfs;
+
+ while (NULL != (wfs = wsh->wfs_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (wsh->wfs_head,
+ wsh->wfs_tail,
+ wfs);
+ GNUNET_free (wfs->method);
+ GNUNET_free (wfs);
+ }
+ json_decref (wsh->json_reply);
+ GNUNET_free (wsh->etag);
+ GNUNET_free (wsh);
+}
+
+
+/**
+ * Function called whenever another exchange process has updated
+ * the wire data in the database.
+ *
+ * @param cls NULL
+ * @param extra unused
+ * @param extra_size number of bytes in @a extra unused
+ */
+static void
+wire_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 /wire update event\n");
+ TEH_check_invariants ();
+ wire_generation++;
+ key_generation++;
+ TEH_resume_keys_requests (false);
+}
+
+
+enum GNUNET_GenericReturnValue
+TEH_wire_init ()
+{
+ struct GNUNET_DB_EventHeaderP es = {
+ .size = htons (sizeof (es)),
+ .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED),
+ };
+
+ wire_eh = TEH_plugin->event_listen (TEH_plugin->cls,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &es,
+ &wire_update_event_cb,
+ NULL);
+ if (NULL == wire_eh)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+void
+TEH_wire_done ()
+{
+ if (NULL != wire_state)
+ {
+ destroy_wire_state (wire_state);
+ wire_state = NULL;
+ }
+ if (NULL != wire_eh)
+ {
+ TEH_plugin->event_listen_cancel (TEH_plugin->cls,
+ wire_eh);
+ wire_eh = NULL;
+ }
+}
+
+
+/**
+ * Add information about a wire account to @a cls.
+ *
+ * @param cls a `json_t *` object to expand with wire account details
+ * @param payto_uri the exchange bank account URI to add
+ * @param conversion_url URL of a conversion service, NULL if there is no conversion
+ * @param debit_restrictions JSON array with debit restrictions on the account
+ * @param credit_restrictions JSON array with credit restrictions on the account
+ * @param master_sig master key signature affirming that this is a bank
+ * account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS)
+ * @param bank_label label the wallet should use to display the account, can be NULL
+ * @param priority priority for ordering bank account labels
+ */
+static void
+add_wire_account (void *cls,
+ const char *payto_uri,
+ const char *conversion_url,
+ const json_t *debit_restrictions,
+ const json_t *credit_restrictions,
+ const struct TALER_MasterSignatureP *master_sig,
+ const char *bank_label,
+ int64_t priority)
+{
+ json_t *a = cls;
+
+ if (GNUNET_OK !=
+ TALER_exchange_wire_signature_check (
+ payto_uri,
+ conversion_url,
+ debit_restrictions,
+ credit_restrictions,
+ &TEH_master_public_key,
+ master_sig))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Database has wire account with invalid signature. Skipping entry. Did the exchange offline public key change?\n");
+ return;
+ }
+ if (0 !=
+ json_array_append_new (
+ a,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("payto_uri",
+ payto_uri),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("conversion_url",
+ conversion_url)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("bank_label",
+ bank_label)),
+ GNUNET_JSON_pack_int64 ("priority",
+ priority),
+ GNUNET_JSON_pack_array_incref ("debit_restrictions",
+ (json_t *) debit_restrictions),
+ GNUNET_JSON_pack_array_incref ("credit_restrictions",
+ (json_t *) credit_restrictions),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ master_sig))))
+ {
+ GNUNET_break (0); /* out of memory!? */
+ return;
+ }
+}
+
+
+/**
+ * Closure for #add_wire_fee().
+ */
+struct AddContext
+{
+ /**
+ * Wire method the fees are for.
+ */
+ char *wire_method;
+
+ /**
+ * Wire state we are building.
+ */
+ struct WireStateHandle *wsh;
+
+ /**
+ * Array to append the fee to.
+ */
+ json_t *a;
+
+ /**
+ * Set to the maximum end-date seen.
+ */
+ struct GNUNET_TIME_Absolute max_seen;
+};
+
+
+/**
+ * Add information about a wire account to @a cls.
+ *
+ * @param cls a `struct AddContext`
+ * @param fees the wire fees we charge
+ * @param start_date from when are these fees valid (start date)
+ * @param end_date until when are these fees valid (end date, exclusive)
+ * @param master_sig master key signature affirming that this is the correct
+ * fee (of purpose #TALER_SIGNATURE_MASTER_WIRE_FEES)
+ */
+static void
+add_wire_fee (void *cls,
+ const struct TALER_WireFeeSet *fees,
+ struct GNUNET_TIME_Timestamp start_date,
+ struct GNUNET_TIME_Timestamp end_date,
+ const struct TALER_MasterSignatureP *master_sig)
+{
+ struct AddContext *ac = cls;
+ struct WireFeeSet *wfs;
+
+ if (GNUNET_OK !=
+ TALER_exchange_offline_wire_fee_verify (
+ ac->wire_method,
+ start_date,
+ end_date,
+ fees,
+ &TEH_master_public_key,
+ master_sig))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Database has wire fee with invalid signature. Skipping entry. Did the exchange offline public key change?\n");
+ return;
+ }
+ ac->max_seen = GNUNET_TIME_absolute_max (ac->max_seen,
+ end_date.abs_time);
+ wfs = GNUNET_new (struct WireFeeSet);
+ wfs->start_date = start_date;
+ wfs->end_date = end_date;
+ wfs->fees = *fees;
+ wfs->method = GNUNET_strdup (ac->wire_method);
+ GNUNET_CONTAINER_DLL_insert (ac->wsh->wfs_head,
+ ac->wsh->wfs_tail,
+ wfs);
+ if (0 !=
+ json_array_append_new (
+ ac->a,
+ GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("wire_fee",
+ &fees->wire),
+ TALER_JSON_pack_amount ("closing_fee",
+ &fees->closing),
+ GNUNET_JSON_pack_timestamp ("start_date",
+ start_date),
+ GNUNET_JSON_pack_timestamp ("end_date",
+ end_date),
+ GNUNET_JSON_pack_data_auto ("sig",
+ master_sig))))
+ {
+ GNUNET_break (0); /* out of memory!? */
+ return;
+ }
+}
+
+
+/**
+ * Create the /wire response from our database state.
+ *
+ * @return NULL on error
+ */
+static struct WireStateHandle *
+build_wire_state (void)
+{
+ json_t *wire_accounts_array;
+ json_t *wire_fee_object;
+ uint64_t wg = wire_generation; /* must be obtained FIRST */
+ enum GNUNET_DB_QueryStatus qs;
+ struct WireStateHandle *wsh;
+ json_t *wads;
+
+ wsh = GNUNET_new (struct WireStateHandle);
+ wsh->wire_generation = wg;
+ wire_accounts_array = json_array ();
+ GNUNET_assert (NULL != wire_accounts_array);
+ qs = TEH_plugin->get_wire_accounts (TEH_plugin->cls,
+ &add_wire_account,
+ wire_accounts_array);
+ if (0 > qs)
+ {
+ GNUNET_break (0);
+ json_decref (wire_accounts_array);
+ wsh->ready = false;
+ return wsh;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Build /wire data with %u accounts\n",
+ (unsigned int) json_array_size (wire_accounts_array));
+ wire_fee_object = json_object ();
+ GNUNET_assert (NULL != wire_fee_object);
+ wsh->cache_expiration = GNUNET_TIME_UNIT_FOREVER_ABS;
+ {
+ json_t *account;
+ size_t index;
+
+ json_array_foreach (wire_accounts_array,
+ index,
+ account)
+ {
+ char *wire_method;
+ const char *payto_uri = json_string_value (json_object_get (account,
+ "payto_uri"));
+
+ GNUNET_assert (NULL != payto_uri);
+ wire_method = TALER_payto_get_method (payto_uri);
+ if (NULL == wire_method)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No wire method in `%s'\n",
+ payto_uri);
+ wsh->ready = false;
+ json_decref (wire_accounts_array);
+ json_decref (wire_fee_object);
+ return wsh;
+ }
+ if (NULL == json_object_get (wire_fee_object,
+ wire_method))
+ {
+ struct AddContext ac = {
+ .wire_method = wire_method,
+ .wsh = wsh,
+ .a = json_array ()
+ };
+
+ GNUNET_assert (NULL != ac.a);
+ qs = TEH_plugin->get_wire_fees (TEH_plugin->cls,
+ wire_method,
+ &add_wire_fee,
+ &ac);
+ if (0 > qs)
+ {
+ GNUNET_break (0);
+ json_decref (ac.a);
+ json_decref (wire_fee_object);
+ json_decref (wire_accounts_array);
+ GNUNET_free (wire_method);
+ wsh->ready = false;
+ return wsh;
+ }
+ if (0 != json_array_size (ac.a))
+ {
+ wsh->cache_expiration
+ = GNUNET_TIME_absolute_min (ac.max_seen,
+ wsh->cache_expiration);
+ GNUNET_assert (0 ==
+ json_object_set_new (wire_fee_object,
+ wire_method,
+ ac.a));
+ }
+ else
+ {
+ json_decref (ac.a);
+ }
+ }
+ GNUNET_free (wire_method);
+ }
+ }
+
+ wads = json_array (); /* #7271 */
+ GNUNET_assert (NULL != wads);
+ wsh->json_reply = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("accounts",
+ wire_accounts_array),
+ GNUNET_JSON_pack_array_steal ("wads",
+ wads),
+ GNUNET_JSON_pack_object_steal ("fees",
+ wire_fee_object));
+ wsh->ready = true;
+ return wsh;
+}
+
+
+void
+TEH_wire_update_state (void)
+{
+ struct GNUNET_DB_EventHeaderP es = {
+ .size = htons (sizeof (es)),
+ .type = htons (TALER_DBEVENT_EXCHANGE_WIRE_UPDATED),
+ };
+
+ TEH_plugin->event_notify (TEH_plugin->cls,
+ &es,
+ NULL,
+ 0);
+ wire_generation++;
+ key_generation++;
+}
+
+
+/**
+ * Return the current key state for this thread. Possibly
+ * re-builds the key state if we have reason to believe
+ * that something changed.
+ *
+ * @return NULL on error
+ */
+struct WireStateHandle *
+get_wire_state (void)
+{
+ struct WireStateHandle *old_wsh;
+
+ old_wsh = wire_state;
+ if ( (NULL == old_wsh) ||
+ (old_wsh->wire_generation < wire_generation) )
+ {
+ struct WireStateHandle *wsh;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Rebuilding /wire, generation upgrade from %llu to %llu\n",
+ (unsigned long long) (NULL == old_wsh) ? 0LL :
+ old_wsh->wire_generation,
+ (unsigned long long) wire_generation);
+ TEH_check_invariants ();
+ wsh = build_wire_state ();
+ wire_state = wsh;
+ if (NULL != old_wsh)
+ destroy_wire_state (old_wsh);
+ TEH_check_invariants ();
+ return wsh;
+ }
+ return old_wsh;
+}
+
+
+const struct TALER_WireFeeSet *
+TEH_wire_fees_by_time (
+ struct GNUNET_TIME_Timestamp ts,
+ const char *method)
+{
+ struct WireStateHandle *wsh = get_wire_state ();
+
+ for (struct WireFeeSet *wfs = wsh->wfs_head;
+ NULL != wfs;
+ wfs = wfs->next)
+ {
+ if (0 != strcmp (method,
+ wfs->method))
+ continue;
+ if ( (GNUNET_TIME_timestamp_cmp (wfs->start_date,
+ >,
+ ts)) ||
+ (GNUNET_TIME_timestamp_cmp (ts,
+ >=,
+ wfs->end_date)) )
+ continue;
+ return &wfs->fees;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "No wire fees for method `%s' at %s configured\n",
+ method,
+ GNUNET_TIME_timestamp2s (ts));
+ return NULL;
+}
+
+
+/**
+ * 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.
@@ -425,6 +1074,13 @@ suspend_request (struct MHD_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)
{
@@ -442,6 +1098,56 @@ suspend_request (struct MHD_Connection *connection)
}
+/**
+ * Called on each denomination key. Checks that the key still works.
+ *
+ * @param cls NULL
+ * @param hc denomination hash (unused)
+ * @param value a `struct TEH_DenominationKey`
+ * @return #GNUNET_OK
+ */
+static enum GNUNET_GenericReturnValue
+check_dk (void *cls,
+ const struct GNUNET_HashCode *hc,
+ void *value)
+{
+ struct TEH_DenominationKey *dk = value;
+
+ (void) cls;
+ (void) hc;
+ switch (dk->denom_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->denom_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
+TEH_check_invariants ()
+{
+ struct TEH_KeyStateHandle *ksh;
+
+ if (0 == TEH_check_invariants_flag)
+ return;
+ ksh = TEH_keys_get_state ();
+ if (NULL == ksh)
+ return;
+ GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map,
+ &check_dk,
+ NULL);
+}
+
+
void
TEH_resume_keys_requests (bool do_shutdown)
{
@@ -476,6 +1182,7 @@ clear_response_cache (struct TEH_KeyStateHandle *ksh)
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,
@@ -490,19 +1197,43 @@ clear_response_cache (struct TEH_KeyStateHandle *ksh)
* @param sm_pub RSA security module public key to check
*/
static void
-check_denom_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub)
+check_denom_rsa_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub)
{
if (0 !=
GNUNET_memcmp (sm_pub,
- &denom_sm_pub))
+ &denom_rsa_sm_pub))
{
- if (! GNUNET_is_zero (&denom_sm_pub))
+ if (! GNUNET_is_zero (&denom_rsa_sm_pub))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Our RSA security module changed its key. This must not happen.\n");
GNUNET_assert (0);
}
- denom_sm_pub = *sm_pub; /* TOFU ;-) */
+ denom_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_denom_cs_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub)
+{
+ if (0 !=
+ GNUNET_memcmp (sm_pub,
+ &denom_cs_sm_pub))
+ {
+ if (! GNUNET_is_zero (&denom_cs_sm_pub))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Our CS security module changed its key. This must not happen.\n");
+ GNUNET_assert (0);
+ }
+ denom_cs_sm_pub = *sm_pub; /* TOFU ;-) */
}
}
@@ -540,7 +1271,7 @@ check_esign_sm_pub (const struct TALER_SecurityModulePublicKeyP *sm_pub)
* @param value the `struct HelperDenomination` to release
* @return #GNUNET_OK (continue to iterate)
*/
-static int
+static enum GNUNET_GenericReturnValue
free_denom_cb (void *cls,
const struct GNUNET_HashCode *h_denom_pub,
void *value)
@@ -549,7 +1280,7 @@ free_denom_cb (void *cls,
(void) cls;
(void) h_denom_pub;
- GNUNET_CRYPTO_rsa_public_key_free (hd->denom_pub.rsa_public_key);
+ TALER_denom_pub_free (&hd->denom_pub);
GNUNET_free (hd->section_name);
GNUNET_free (hd);
return GNUNET_OK;
@@ -565,7 +1296,7 @@ free_denom_cb (void *cls,
* @param value the `struct HelperSignkey` to release
* @return #GNUNET_OK (continue to iterate)
*/
-static int
+static enum GNUNET_GenericReturnValue
free_esign_cb (void *cls,
const struct GNUNET_PeerIdentity *pid,
void *value)
@@ -591,6 +1322,10 @@ 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,
@@ -598,10 +1333,15 @@ destroy_key_helpers (struct HelperState *hs)
hs);
GNUNET_CONTAINER_multipeermap_destroy (hs->esign_keys);
hs->esign_keys = NULL;
- if (NULL != hs->dh)
+ if (NULL != hs->rsadh)
+ {
+ TALER_CRYPTO_helper_rsa_disconnect (hs->rsadh);
+ hs->rsadh = NULL;
+ }
+ if (NULL != hs->csdh)
{
- TALER_CRYPTO_helper_denom_disconnect (hs->dh);
- hs->dh = NULL;
+ TALER_CRYPTO_helper_cs_disconnect (hs->csdh);
+ hs->csdh = NULL;
}
if (NULL != hs->esh)
{
@@ -612,6 +1352,51 @@ destroy_key_helpers (struct HelperState *hs)
/**
+ * Looks up the AGE_RESTRICTED setting for a denomination in the config and
+ * returns the age restriction (mask) accordingly.
+ *
+ * @param section_name Section in the configuration for the particular
+ * denomination.
+ */
+static struct TALER_AgeMask
+load_age_mask (const char *section_name)
+{
+ static const struct TALER_AgeMask null_mask = {0};
+ enum GNUNET_GenericReturnValue ret;
+
+ if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
+ TEH_cfg,
+ section_name,
+ "AGE_RESTRICTED")))
+ return null_mask;
+
+ if (GNUNET_SYSERR ==
+ (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
+ section_name,
+ "AGE_RESTRICTED")))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section_name,
+ "AGE_RESTRICTED",
+ "Value must be YES or NO\n");
+ return null_mask;
+ }
+
+ if (GNUNET_OK == ret)
+ {
+ if (! TEH_age_restriction_enabled)
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "age restriction set in section %s, yet, age restriction is not enabled\n",
+ section_name);
+ return TEH_age_restriction_config.mask;
+ }
+
+
+ return null_mask;
+}
+
+
+/**
* 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.
@@ -623,20 +1408,20 @@ destroy_key_helpers (struct HelperState *hs)
* 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_denom_pub hash of the @a denom_pub that is available (or was purged)
- * @param denom_pub the public key itself, NULL if the key was revoked or purged
+ * @param h_rsa hash of the @a denom_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_denom_cb (
+helper_rsa_cb (
void *cls,
const char *section_name,
- struct GNUNET_TIME_Absolute start_time,
+ struct GNUNET_TIME_Timestamp start_time,
struct GNUNET_TIME_Relative validity_duration,
- const struct GNUNET_HashCode *h_denom_pub,
- const struct TALER_DenominationPublicKey *denom_pub,
+ const struct TALER_RsaPubHashP *h_rsa,
+ struct GNUNET_CRYPTO_BlindSignPublicKey *bs_pub,
const struct TALER_SecurityModulePublicKeyP *sm_pub,
const struct TALER_SecurityModuleSignatureP *sm_sig)
{
@@ -645,14 +1430,94 @@ helper_denom_cb (
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"RSA helper announces key %s for denomination type %s with validity %s\n",
- GNUNET_h2s (h_denom_pub),
+ GNUNET_h2s (&h_rsa->hash),
section_name,
GNUNET_STRINGS_relative_time_to_string (validity_duration,
GNUNET_NO));
key_generation++;
TEH_resume_keys_requests (false);
- hd = GNUNET_CONTAINER_multihashmap_get (hs->denom_keys,
- h_denom_pub);
+ 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_denom_rsa_sm_pub (sm_pub);
+ hd = GNUNET_new (struct HelperDenomination);
+ 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->denom_pub.bsign_pub_key =
+ GNUNET_CRYPTO_bsign_pub_incref (bs_pub);
+ /* load the age mask for the denomination, if applicable */
+ hd->denom_pub.age_mask = load_age_mask (section_name);
+ TALER_denom_pub_hash (&hd->denom_pub,
+ &hd->h_denom_pub);
+ hd->section_name = GNUNET_strdup (section_name);
+ GNUNET_assert (
+ GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (
+ hs->denom_keys,
+ &hd->h_denom_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 denomination 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 denom_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 HelperDenomination *hd;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "CS helper announces key %s for denomination 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++;
+ TEH_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 */
@@ -660,20 +1525,32 @@ helper_denom_cb (
return;
}
GNUNET_assert (NULL != sm_pub);
- check_denom_sm_pub (sm_pub);
+ check_denom_cs_sm_pub (sm_pub);
hd = GNUNET_new (struct HelperDenomination);
hd->start_time = start_time;
hd->validity_duration = validity_duration;
- hd->h_denom_pub = *h_denom_pub;
+ hd->h_details.h_cs = *h_cs;
hd->sm_sig = *sm_sig;
- hd->denom_pub.rsa_public_key
- = GNUNET_CRYPTO_rsa_public_key_dup (denom_pub->rsa_public_key);
+ GNUNET_assert (GNUNET_CRYPTO_BSA_CS == bs_pub->cipher);
+ hd->denom_pub.bsign_pub_key
+ = GNUNET_CRYPTO_bsign_pub_incref (bs_pub);
+ /* load the age mask for the denomination, if applicable */
+ hd->denom_pub.age_mask = load_age_mask (section_name);
+ TALER_denom_pub_hash (&hd->denom_pub,
+ &hd->h_denom_pub);
hd->section_name = GNUNET_strdup (section_name);
GNUNET_assert (
GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (
hs->denom_keys,
- &hd->h_denom_pub,
+ &hd->h_denom_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));
}
@@ -697,7 +1574,7 @@ helper_denom_cb (
static void
helper_esign_cb (
void *cls,
- struct GNUNET_TIME_Absolute start_time,
+ struct GNUNET_TIME_Timestamp start_time,
struct GNUNET_TIME_Relative validity_duration,
const struct TALER_ExchangePublicKeyP *exchange_pub,
const struct TALER_SecurityModulePublicKeyP *sm_pub,
@@ -721,7 +1598,6 @@ helper_esign_cb (
{
/* should be just an update (revocation!), so update existing entry */
hsk->validity_duration = validity_duration;
- GNUNET_break (0 == start_time.abs_value_us);
return;
}
GNUNET_assert (NULL != sm_pub);
@@ -753,18 +1629,35 @@ 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->dh = TALER_CRYPTO_helper_denom_connect (TEH_cfg,
- &helper_denom_cb,
- hs);
- if (NULL == hs->dh)
+ hs->rsadh = TALER_CRYPTO_helper_rsa_connect (TEH_cfg,
+ "taler-exchange",
+ &helper_rsa_cb,
+ hs);
+ if (NULL == hs->rsadh)
+ {
+ destroy_key_helpers (hs);
+ return GNUNET_SYSERR;
+ }
+ hs->csdh = TALER_CRYPTO_helper_cs_connect (TEH_cfg,
+ "taler-exchange",
+ &helper_cs_cb,
+ hs);
+ if (NULL == hs->csdh)
{
destroy_key_helpers (hs);
return GNUNET_SYSERR;
}
hs->esh = TALER_CRYPTO_helper_esign_connect (TEH_cfg,
+ "taler-exchange",
&helper_esign_cb,
hs);
if (NULL == hs->esh)
@@ -784,7 +1677,8 @@ setup_key_helpers (struct HelperState *hs)
static void
sync_key_helpers (struct HelperState *hs)
{
- TALER_CRYPTO_helper_denom_poll (hs->dh);
+ TALER_CRYPTO_helper_rsa_poll (hs->rsadh);
+ TALER_CRYPTO_helper_cs_poll (hs->csdh);
TALER_CRYPTO_helper_esign_poll (hs->esh);
}
@@ -797,7 +1691,7 @@ sync_key_helpers (struct HelperState *hs)
* @param value a `struct TEH_DenominationKey` to free
* @return #GNUNET_OK (continue to iterate)
*/
-static int
+static enum GNUNET_GenericReturnValue
clear_denomination_cb (void *cls,
const struct GNUNET_HashCode *h_denom_pub,
void *value)
@@ -807,7 +1701,7 @@ clear_denomination_cb (void *cls,
(void) cls;
(void) h_denom_pub;
- GNUNET_CRYPTO_rsa_public_key_free (dk->denom_pub.rsa_public_key);
+ TALER_denom_pub_free (&dk->denom_pub);
while (NULL != (as = dk->as_head))
{
GNUNET_CONTAINER_DLL_remove (dk->as_head,
@@ -828,7 +1722,7 @@ clear_denomination_cb (void *cls,
* @param value a `struct SigningKey` to free
* @return #GNUNET_OK (continue to iterate)
*/
-static int
+static enum GNUNET_GenericReturnValue
clear_signkey_cb (void *cls,
const struct GNUNET_PeerIdentity *pid,
void *value)
@@ -853,7 +1747,16 @@ static void
destroy_key_state (struct TEH_KeyStateHandle *ksh,
bool free_helper)
{
+ struct TEH_GlobalFee *gf;
+
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_denomination_cb,
ksh);
@@ -864,6 +1767,8 @@ destroy_key_state (struct TEH_KeyStateHandle *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)
{
destroy_key_helpers (ksh->helpers);
@@ -894,8 +1799,12 @@ keys_update_event_cb (void *cls,
(void) cls;
(void) extra;
(void) extra_size;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Received /keys update event\n");
+ TEH_check_invariants ();
key_generation++;
TEH_resume_keys_requests (false);
+ TEH_check_invariants ();
}
@@ -918,6 +1827,17 @@ TEH_keys_init ()
"SIGNKEY_LEGAL_DURATION");
return GNUNET_SYSERR;
}
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
+ "exchange",
+ "ASSET_TYPE",
+ &asset_type))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
+ "exchange",
+ "ASSET_TYPE");
+ asset_type = GNUNET_strdup ("fiat");
+ }
keys_eh = TEH_plugin->event_listen (TEH_plugin->cls,
GNUNET_TIME_UNIT_FOREVER_REL,
&es,
@@ -938,6 +1858,11 @@ TEH_keys_init ()
void
TEH_keys_finished ()
{
+ if (NULL != keys_tt)
+ {
+ GNUNET_SCHEDULER_cancel (keys_tt);
+ keys_tt = NULL;
+ }
if (NULL != key_state)
destroy_key_state (key_state,
true);
@@ -965,7 +1890,7 @@ static void
denomination_info_cb (
void *cls,
const struct TALER_DenominationPublicKey *denom_pub,
- const struct GNUNET_HashCode *h_denom_pub,
+ const struct TALER_DenominationHashP *h_denom_pub,
const struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta,
const struct TALER_MasterSignatureP *master_sig,
bool recoup_possible)
@@ -973,27 +1898,48 @@ denomination_info_cb (
struct TEH_KeyStateHandle *ksh = cls;
struct TEH_DenominationKey *dk;
- if ( (0 == meta->start.abs_value_us) ||
- (0 == meta->expire_withdraw.abs_value_us) ||
- (0 == meta->expire_deposit.abs_value_us) ||
- (0 == meta->expire_legal.abs_value_us) )
+ if (GNUNET_OK !=
+ TALER_exchange_offline_denom_validity_verify (
+ h_denom_pub,
+ meta->start,
+ meta->expire_withdraw,
+ meta->expire_deposit,
+ meta->expire_legal,
+ &meta->value,
+ &meta->fees,
+ &TEH_master_public_key,
+ master_sig))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Database has denomination with invalid signature. Skipping entry. Did the exchange offline public key change?\n");
+ return;
+ }
+
+ GNUNET_assert (GNUNET_CRYPTO_BSA_INVALID !=
+ denom_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 denomination key %s\n",
- GNUNET_h2s (h_denom_pub));
+ GNUNET_h2s (&h_denom_pub->hash));
return;
}
dk = GNUNET_new (struct TEH_DenominationKey);
- dk->denom_pub.rsa_public_key
- = GNUNET_CRYPTO_rsa_public_key_dup (denom_pub->rsa_public_key);
+ TALER_denom_pub_copy (&dk->denom_pub,
+ denom_pub);
dk->h_denom_pub = *h_denom_pub;
dk->meta = *meta;
dk->master_sig = *master_sig;
dk->recoup_possible = recoup_possible;
+ dk->denom_pub.age_mask = meta->age_mask;
+
GNUNET_assert (
GNUNET_OK ==
GNUNET_CONTAINER_multihashmap_put (ksh->denomkey_map,
- &dk->h_denom_pub,
+ &dk->h_denom_pub.hash,
dk,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
@@ -1018,6 +1964,19 @@ signkey_info_cb (
struct SigningKey *sk;
struct GNUNET_PeerIdentity pid;
+ if (GNUNET_OK !=
+ TALER_exchange_offline_signkey_validity_verify (
+ exchange_pub,
+ meta->start,
+ meta->expire_sign,
+ meta->expire_legal,
+ &TEH_master_public_key,
+ master_sig))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Database has signing key with invalid signature. Skipping entry. Did the exchange offline public key change?\n");
+ return;
+ }
sk = GNUNET_new (struct SigningKey);
sk->exchange_pub = *exchange_pub;
sk->meta = *meta;
@@ -1058,7 +2017,7 @@ struct GetAuditorSigsContext
* @param value a `struct TEH_DenominationKey`
* @return #GNUNET_OK (continue to iterate)
*/
-static int
+static enum GNUNET_GenericReturnValue
get_auditor_sigs (void *cls,
const struct GNUNET_HashCode *h_denom_pub,
void *value)
@@ -1139,7 +2098,7 @@ static void
auditor_denom_cb (
void *cls,
const struct TALER_AuditorPublicKeyP *auditor_pub,
- const struct GNUNET_HashCode *h_denom_pub,
+ const struct TALER_DenominationHashP *h_denom_pub,
const struct TALER_AuditorSignatureP *auditor_sig)
{
struct TEH_KeyStateHandle *ksh = cls;
@@ -1147,7 +2106,7 @@ auditor_denom_cb (
struct TEH_AuditorSignature *as;
dk = GNUNET_CONTAINER_multihashmap_get (ksh->denomkey_map,
- h_denom_pub);
+ &h_denom_pub->hash);
if (NULL == dk)
{
/* Odd, this should be impossible as per foreign key
@@ -1171,9 +2130,9 @@ auditor_denom_cb (
struct SignKeyCtx
{
/**
- * When does the next signing key expire. Updated.
+ * What is the current rotation frequency for signing keys. Updated.
*/
- struct GNUNET_TIME_Absolute next_sk_expire;
+ struct GNUNET_TIME_Relative min_sk_frequency;
/**
* JSON array of signing keys (being created).
@@ -1191,7 +2150,7 @@ struct SignKeyCtx
* @param value a `struct SigningKey`
* @return #GNUNET_OK (continue to iterate)
*/
-static int
+static enum GNUNET_GenericReturnValue
add_sign_key_cb (void *cls,
const struct GNUNET_PeerIdentity *pid,
void *value)
@@ -1199,21 +2158,26 @@ add_sign_key_cb (void *cls,
struct SignKeyCtx *ctx = cls;
struct SigningKey *sk = value;
- ctx->next_sk_expire =
- GNUNET_TIME_absolute_min (ctx->next_sk_expire,
- sk->meta.expire_sign);
-
+ (void) pid;
+ 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_time_abs ("stamp_start",
- sk->meta.start),
- GNUNET_JSON_pack_time_abs ("stamp_expire",
- sk->meta.expire_sign),
- GNUNET_JSON_pack_time_abs ("stamp_end",
- sk->meta.expire_legal),
+ 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",
@@ -1238,10 +2202,10 @@ struct DenomKeyCtx
json_t *recoup;
/**
- * When does the next denomination key expire. Updated.
+ * What is the minimum key rotation frequency of
+ * valid denomination keys?
*/
- struct GNUNET_TIME_Absolute next_dk_expire;
-
+ struct GNUNET_TIME_Relative min_dk_frequency;
};
@@ -1255,7 +2219,7 @@ struct DenomKeyCtx
* @param value a `struct TEH_DenominationKey`
* @return #GNUNET_OK (continue to iterate)
*/
-static int
+static enum GNUNET_GenericReturnValue
add_denom_key_cb (void *cls,
const struct GNUNET_HashCode *h_denom_pub,
void *value)
@@ -1275,80 +2239,33 @@ add_denom_key_cb (void *cls,
}
else
{
- dkc->next_dk_expire =
- GNUNET_TIME_absolute_min (dkc->next_dk_expire,
- dk->meta.expire_withdraw);
+ 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_value_us);
+ dk->meta.start.abs_time.abs_value_us);
}
return GNUNET_OK;
}
/**
- * Produce HTTP "Date:" header.
- *
- * @param at time to write to @a date
- * @param[out] date where to write the header, with
- * at least 128 bytes available space.
- */
-static void
-get_date_string (struct GNUNET_TIME_Absolute at,
- char date[128])
-{
- static const char *const days[] =
- { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
- static const char *const mons[] =
- { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
- "Nov", "Dec"};
- struct tm now;
- time_t t;
-#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
- ! defined(HAVE_GMTIME_R)
- struct tm*pNow;
-#endif
-
- date[0] = 0;
- t = (time_t) (at.abs_value_us / 1000LL / 1000LL);
-#if defined(HAVE_C11_GMTIME_S)
- if (NULL == gmtime_s (&t, &now))
- return;
-#elif defined(HAVE_W32_GMTIME_S)
- if (0 != gmtime_s (&now, &t))
- return;
-#elif defined(HAVE_GMTIME_R)
- if (NULL == gmtime_r (&t, &now))
- return;
-#else
- pNow = gmtime (&t);
- if (NULL == pNow)
- return;
- now = *pNow;
-#endif
- sprintf (date,
- "%3s, %02u %3s %04u %02u:%02u:%02u GMT",
- days[now.tm_wday % 7],
- (unsigned int) now.tm_mday,
- mons[now.tm_mon % 12],
- (unsigned int) (1900 + now.tm_year),
- (unsigned int) now.tm_hour,
- (unsigned int) now.tm_min,
- (unsigned int) now.tm_sec);
-}
-
-
-/**
* Add the headers we want to set for every /keys response.
*
- * @param ksh the key state to use
+ * @param cls the key state to use
* @param[in,out] response the response to modify
- * @return #GNUNET_OK on success
*/
-static enum GNUNET_GenericReturnValue
-setup_general_response_headers (const struct TEH_KeyStateHandle *ksh,
+static void
+setup_general_response_headers (void *cls,
struct MHD_Response *response)
{
+ struct TEH_KeyStateHandle *ksh = cls;
char dat[128];
TALER_MHD_add_global_headers (response);
@@ -1356,30 +2273,72 @@ setup_general_response_headers (const struct TEH_KeyStateHandle *ksh,
MHD_add_response_header (response,
MHD_HTTP_HEADER_CONTENT_TYPE,
"application/json"));
- get_date_string (ksh->reload_time,
- dat);
GNUNET_break (MHD_YES ==
MHD_add_response_header (response,
- MHD_HTTP_HEADER_LAST_MODIFIED,
- dat));
- if (0 != ksh->next_reload.abs_value_us)
+ MHD_HTTP_HEADER_CACHE_CONTROL,
+ "public,must-revalidate,max-age=86400"));
+ if (! GNUNET_TIME_relative_is_zero (ksh->rekey_frequency))
{
- struct GNUNET_TIME_Absolute m;
-
- m = GNUNET_TIME_relative_to_absolute (TEH_max_keys_caching);
- m = GNUNET_TIME_absolute_min (m,
- ksh->next_reload);
- get_date_string (m,
- dat);
+ struct GNUNET_TIME_Relative r;
+ struct GNUNET_TIME_Absolute a;
+ struct GNUNET_TIME_Timestamp km;
+ struct GNUNET_TIME_Timestamp m;
+ struct GNUNET_TIME_Timestamp we;
+
+ r = GNUNET_TIME_relative_min (TEH_max_keys_caching,
+ ksh->rekey_frequency);
+ a = GNUNET_TIME_relative_to_absolute (r);
+ /* Round up to next full day to ensure the expiration
+ time does not become a fingerprint! */
+ a = GNUNET_TIME_absolute_round_down (a,
+ GNUNET_TIME_UNIT_DAYS);
+ a = GNUNET_TIME_absolute_add (a,
+ GNUNET_TIME_UNIT_DAYS);
+ km = GNUNET_TIME_absolute_to_timestamp (a);
+ we = GNUNET_TIME_absolute_to_timestamp (wire_state->cache_expiration);
+ m = GNUNET_TIME_timestamp_min (we,
+ km);
+ TALER_MHD_get_date_string (m.abs_time,
+ dat);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Setting /keys 'Expires' header to '%s'\n",
- dat);
+ "Setting /keys 'Expires' header to '%s' (rekey frequency is %s)\n",
+ dat,
+ GNUNET_TIME_relative2s (ksh->rekey_frequency,
+ false));
GNUNET_break (MHD_YES ==
MHD_add_response_header (response,
MHD_HTTP_HEADER_EXPIRES,
dat));
+ ksh->signature_expires
+ = GNUNET_TIME_timestamp_min (m,
+ ksh->signature_expires);
}
- return GNUNET_OK;
+ /* Set cache control headers: our response varies depending on these headers */
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_VARY,
+ MHD_HTTP_HEADER_ACCEPT_ENCODING));
+}
+
+
+/**
+ * Function called with wallet balance thresholds.
+ *
+ * @param[in,out] cls a `json **` where to put the array of json amounts discovered
+ * @param threshold another threshold amount to add
+ */
+static void
+wallet_threshold_cb (void *cls,
+ const struct TALER_Amount *threshold)
+{
+ json_t **ret = cls;
+
+ if (NULL == *ret)
+ *ret = json_array ();
+ GNUNET_assert (0 ==
+ json_array_append_new (*ret,
+ TALER_JSON_from_amount (
+ threshold)));
}
@@ -1388,50 +2347,58 @@ setup_general_response_headers (const struct TEH_KeyStateHandle *ksh,
* @a recoup and @a denoms.
*
* @param[in,out] ksh key state handle we build @a krd for
- * @param[in] denom_keys_hash hash over all the denominatoin keys in @a denoms
- * @param last_cpd timestamp to use
- * @param signkeys list of sign keys to return
- * @param recoup list of revoked keys to return
- * @param denoms list of denominations to return
+ * @param[in] denom_keys_hash hash over all the denomination keys in @a denoms
+ * @param last_cherry_pick_date timestamp to use
+ * @param[in,out] signkeys list of sign keys to return
+ * @param[in,out] recoup list of revoked keys to return
+ * @param[in,out] grouped_denominations list of grouped denominations to return
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
create_krd (struct TEH_KeyStateHandle *ksh,
const struct GNUNET_HashCode *denom_keys_hash,
- struct GNUNET_TIME_Absolute last_cpd,
+ struct GNUNET_TIME_Timestamp last_cherry_pick_date,
json_t *signkeys,
json_t *recoup,
- json_t *denoms)
+ json_t *grouped_denominations)
{
struct KeysResponseData krd;
struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangeSignatureP exchange_sig;
+ struct WireStateHandle *wsh;
json_t *keys;
- GNUNET_assert (0 != last_cpd.abs_value_us);
+ wsh = get_wire_state ();
+ if (! wsh->ready)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_assert (! GNUNET_TIME_absolute_is_zero (
+ last_cherry_pick_date.abs_time));
GNUNET_assert (NULL != signkeys);
GNUNET_assert (NULL != recoup);
- GNUNET_assert (NULL != denoms);
+ GNUNET_assert (NULL != grouped_denominations);
GNUNET_assert (NULL != ksh->auditors);
GNUNET_assert (NULL != TEH_currency);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Creating /keys at cherry pick date %s\n",
- GNUNET_STRINGS_absolute_time_to_string (last_cpd));
- /* Sign hash over denomination keys */
- {
- struct TALER_ExchangeKeySetPS ks = {
- .purpose.size = htonl (sizeof (ks)),
- .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_KEY_SET),
- .list_issue_date = GNUNET_TIME_absolute_hton (last_cpd),
- .hc = *denom_keys_hash
- };
+ GNUNET_TIME_timestamp2s (last_cherry_pick_date));
+
+ /* Sign hash over master signatures of all denomination keys until this time
+ (in reverse order). */
+ {
enum TALER_ErrorCode ec;
if (TALER_EC_NONE !=
- (ec = TEH_keys_exchange_sign2 (ksh,
- &ks,
- &exchange_pub,
- &exchange_sig)))
+ (ec =
+ TALER_exchange_online_key_set_sign (
+ &TEH_keys_exchange_sign2_,
+ ksh,
+ last_cherry_pick_date,
+ denom_keys_hash,
+ &exchange_pub,
+ &exchange_sig)))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Could not create key response data: cannot sign (%s)\n",
@@ -1439,21 +2406,44 @@ create_krd (struct TEH_KeyStateHandle *ksh,
return GNUNET_SYSERR;
}
}
+
{
const struct SigningKey *sk;
sk = GNUNET_CONTAINER_multipeermap_get (
ksh->signkey_map,
(const struct GNUNET_PeerIdentity *) &exchange_pub);
- ksh->signature_expires = GNUNET_TIME_absolute_min (sk->meta.expire_sign,
- ksh->signature_expires);
+ ksh->signature_expires = GNUNET_TIME_timestamp_min (sk->meta.expire_sign,
+ ksh->signature_expires);
}
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Build /keys data with %u wire accounts\n",
+ (unsigned int) json_array_size (
+ json_object_get (wsh->json_reply,
+ "accounts")));
+
keys = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("version",
EXCHANGE_PROTOCOL_VERSION),
+ GNUNET_JSON_pack_string ("base_url",
+ TEH_base_url),
GNUNET_JSON_pack_string ("currency",
TEH_currency),
+ GNUNET_JSON_pack_object_steal (
+ "currency_specification",
+ TALER_CONFIG_currency_specs_to_json (TEH_cspec)),
+ TALER_JSON_pack_amount ("stefan_abs",
+ &TEH_stefan_abs),
+ TALER_JSON_pack_amount ("stefan_log",
+ &TEH_stefan_log),
+ GNUNET_JSON_pack_double ("stefan_lin",
+ (double) TEH_stefan_lin),
+ GNUNET_JSON_pack_string ("asset_type",
+ asset_type),
+ GNUNET_JSON_pack_bool ("rewards_allowed",
+ GNUNET_YES ==
+ TEH_enable_rewards),
GNUNET_JSON_pack_data_auto ("master_public_key",
&TEH_master_public_key),
GNUNET_JSON_pack_time_rel ("reserve_closing_delay",
@@ -1462,35 +2452,115 @@ create_krd (struct TEH_KeyStateHandle *ksh,
signkeys),
GNUNET_JSON_pack_array_incref ("recoup",
recoup),
- GNUNET_JSON_pack_array_incref ("denoms",
- denoms),
+ GNUNET_JSON_pack_array_incref ("wads",
+ json_object_get (wsh->json_reply,
+ "wads")),
+ GNUNET_JSON_pack_array_incref ("accounts",
+ json_object_get (wsh->json_reply,
+ "accounts")),
+ GNUNET_JSON_pack_object_incref ("wire_fees",
+ json_object_get (wsh->json_reply,
+ "fees")),
+ GNUNET_JSON_pack_array_incref ("denominations",
+ grouped_denominations),
GNUNET_JSON_pack_array_incref ("auditors",
ksh->auditors),
- GNUNET_JSON_pack_time_abs ("list_issue_date",
- last_cpd),
- GNUNET_JSON_pack_data_auto ("eddsa_pub",
+ GNUNET_JSON_pack_array_incref ("global_fees",
+ ksh->global_fees),
+ GNUNET_JSON_pack_timestamp ("list_issue_date",
+ last_cherry_pick_date),
+ GNUNET_JSON_pack_data_auto ("exchange_pub",
&exchange_pub),
- GNUNET_JSON_pack_data_auto ("eddsa_sig",
+ GNUNET_JSON_pack_data_auto ("exchange_sig",
&exchange_sig));
GNUNET_assert (NULL != keys);
- if ( (TEH_KYC_NONE != TEH_kyc_config.mode) &&
- (GNUNET_OK ==
- TALER_amount_is_valid (&TEH_kyc_config.wallet_balance_limit)) )
+
+ /* Set wallet limit if KYC is configured */
{
- GNUNET_assert (
- 0 ==
- json_object_set_new (
+ json_t *wblwk = NULL;
+
+ TALER_KYCLOGIC_kyc_iterate_thresholds (
+ TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE,
+ &wallet_threshold_cb,
+ &wblwk);
+ if (NULL != wblwk)
+ GNUNET_assert (
+ 0 ==
+ json_object_set_new (
+ keys,
+ "wallet_balance_limit_without_kyc",
+ wblwk));
+ }
+
+ /* Signal support for the configured, enabled extensions. */
+ {
+ json_t *extensions = json_object ();
+ bool has_extensions = false;
+
+ GNUNET_assert (NULL != extensions);
+ /* Fill in the configurations of the enabled extensions */
+ for (const struct TALER_Extensions *iter = TALER_extensions_get_head ();
+ NULL != iter && NULL != iter->extension;
+ iter = iter->next)
+ {
+ const struct TALER_Extension *extension = iter->extension;
+ json_t *manifest;
+ int r;
+
+ /* skip if not enabled */
+ if (! extension->enabled)
+ continue;
+
+ /* flag our findings so far */
+ has_extensions = true;
+
+
+ manifest = extension->manifest (extension);
+ GNUNET_assert (manifest);
+
+ r = json_object_set_new (
+ extensions,
+ extension->name,
+ manifest);
+ GNUNET_assert (0 == r);
+ }
+
+ /* Update the keys object with the extensions and its signature */
+ if (has_extensions)
+ {
+ json_t *sig;
+ int r;
+
+ r = json_object_set_new (
keys,
- "wallet_balance_limit_without_kyc",
- TALER_JSON_from_amount (
- &TEH_kyc_config.wallet_balance_limit)));
+ "extensions",
+ extensions);
+ GNUNET_assert (0 == r);
+
+ /* Add the signature of the extensions, if it is not zero */
+ if (TEH_extensions_signed)
+ {
+ sig = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("extensions_sig",
+ &TEH_extensions_sig));
+
+ r = json_object_update (keys, sig);
+ GNUNET_assert (0 == r);
+ }
+ }
+ else
+ {
+ json_decref (extensions);
+ }
}
+
{
char *keys_json;
void *keys_jsonz;
size_t keys_jsonz_size;
int comp;
+ char etag[sizeof (struct GNUNET_HashCode) * 2];
/* Convert /keys response to UTF8-String */
keys_json = json_dumps (keys,
@@ -1502,15 +2572,34 @@ create_krd (struct TEH_KeyStateHandle *ksh,
keys_jsonz = GNUNET_strdup (keys_json);
keys_jsonz_size = strlen (keys_json);
+ /* hash to compute etag */
+ {
+ struct GNUNET_HashCode ehash;
+ char *end;
+
+ GNUNET_CRYPTO_hash (keys_jsonz,
+ keys_jsonz_size,
+ &ehash);
+ end = GNUNET_STRINGS_data_to_string (&ehash,
+ sizeof (ehash),
+ etag,
+ sizeof (etag));
+ *end = '\0';
+ }
+
/* Create uncompressed response */
krd.response_uncompressed
= MHD_create_response_from_buffer (keys_jsonz_size,
keys_json,
MHD_RESPMEM_MUST_FREE);
GNUNET_assert (NULL != krd.response_uncompressed);
- GNUNET_assert (GNUNET_OK ==
- setup_general_response_headers (ksh,
- krd.response_uncompressed));
+ setup_general_response_headers (ksh,
+ krd.response_uncompressed);
+ /* Information is always public, revalidate after 1 day */
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (krd.response_uncompressed,
+ MHD_HTTP_HEADER_ETAG,
+ etag));
/* Also compute compressed version of /keys response */
comp = TALER_MHD_body_compress (&keys_jsonz,
&keys_jsonz_size);
@@ -1526,11 +2615,16 @@ create_krd (struct TEH_KeyStateHandle *ksh,
MHD_add_response_header (krd.response_compressed,
MHD_HTTP_HEADER_CONTENT_ENCODING,
"deflate")) );
- GNUNET_assert (GNUNET_OK ==
- setup_general_response_headers (ksh,
- krd.response_compressed));
+ setup_general_response_headers (ksh,
+ krd.response_compressed);
+ /* Information is always public, revalidate after 1 day */
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (krd.response_compressed,
+ MHD_HTTP_HEADER_ETAG,
+ etag));
+ krd.etag = GNUNET_strdup (etag);
}
- krd.cherry_pick_date = last_cpd;
+ krd.cherry_pick_date = last_cherry_pick_date;
GNUNET_array_append (ksh->krd_array,
ksh->krd_array_length,
krd);
@@ -1539,6 +2633,194 @@ create_krd (struct TEH_KeyStateHandle *ksh,
/**
+ * Element in the `struct SignatureContext` array.
+ */
+struct SignatureElement
+{
+
+ /**
+ * Offset of the denomination in the group array,
+ * for sorting (2nd rank, ascending).
+ */
+ unsigned int offset;
+
+ /**
+ * Offset of the group in the denominations array,
+ * for sorting (2nd rank, ascending).
+ */
+ unsigned int group_offset;
+
+ /**
+ * Pointer to actual master signature to hash over.
+ */
+ struct TALER_MasterSignatureP master_sig;
+};
+
+/**
+ * Context for collecting the array of master signatures
+ * needed to verify the exchange_sig online signature.
+ */
+struct SignatureContext
+{
+ /**
+ * Array of signatures to hash over.
+ */
+ struct SignatureElement *elements;
+
+ /**
+ * Write offset in the @e elements array.
+ */
+ unsigned int elements_pos;
+
+ /**
+ * Allocated space for @e elements.
+ */
+ unsigned int elements_size;
+};
+
+
+/**
+ * Determine order to sort two elements by before
+ * we hash the master signatures. Used for
+ * sorting with qsort().
+ *
+ * @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 int
+signature_context_sort_cb (const void *a,
+ const void *b)
+{
+ const struct SignatureElement *sa = a;
+ const struct SignatureElement *sb = b;
+
+ 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;
+}
+
+
+/**
+ * Append a @a master_sig to the @a sig_ctx using the
+ * given attributes for (later) sorting.
+ *
+ * @param[in,out] sig_ctx signature context to update
+ * @param group_offset offset for the group
+ * @param offset offset for the entry
+ * @param master_sig master signature for the entry
+ */
+static void
+append_signature (struct SignatureContext *sig_ctx,
+ unsigned int group_offset,
+ unsigned int offset,
+ const struct TALER_MasterSignatureP *master_sig)
+{
+ struct SignatureElement *element;
+ unsigned int new_size;
+
+ if (sig_ctx->elements_pos == sig_ctx->elements_size)
+ {
+ if (0 == sig_ctx->elements_size)
+ new_size = 1024;
+ else
+ new_size = sig_ctx->elements_size * 2;
+ GNUNET_array_grow (sig_ctx->elements,
+ sig_ctx->elements_size,
+ new_size);
+ }
+ element = &sig_ctx->elements[sig_ctx->elements_pos++];
+ element->offset = offset;
+ element->group_offset = group_offset;
+ element->master_sig = *master_sig;
+}
+
+
+/**
+ *GroupData is the value we store for each group meta-data */
+struct GroupData
+{
+ /**
+ * The json blob with the group meta-data and list of denominations
+ */
+ json_t *json;
+
+ /**
+ * List of denominations 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;
+
+};
+
+
+/**
+ * Helper function called to clean up the group data
+ * in the denominations_by_group below.
+ *
+ * @param cls unused
+ * @param key unused
+ * @param value a `struct GroupData` to free
+ * @return #GNUNET_OK
+ */
+static int
+free_group (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *value)
+{
+ struct GroupData *gd = value;
+
+ (void) cls;
+ (void) key;
+ GNUNET_free (gd);
+ return GNUNET_OK;
+}
+
+
+static void
+compute_msig_hash (struct SignatureContext *sig_ctx,
+ struct GNUNET_HashCode *hc)
+{
+ struct GNUNET_HashContext *hash_context;
+
+ hash_context = GNUNET_CRYPTO_hash_context_start ();
+ qsort (sig_ctx->elements,
+ sig_ctx->elements_pos,
+ sizeof (struct SignatureElement),
+ &signature_context_sort_cb);
+ for (unsigned int i = 0; i<sig_ctx->elements_pos; i++)
+ {
+ struct SignatureElement *element = &sig_ctx->elements[i];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding %u,%u,%s\n",
+ element->group_offset,
+ element->offset,
+ TALER_B2S (&element->master_sig));
+ GNUNET_CRYPTO_hash_context_read (hash_context,
+ &element->master_sig,
+ sizeof (element->master_sig));
+ }
+ GNUNET_CRYPTO_hash_context_finish (hash_context,
+ hc);
+}
+
+
+/**
* Update the "/keys" responses in @a ksh, computing the detailed replies.
*
* This function is to recompute all (including cherry-picked) responses we
@@ -1550,144 +2832,399 @@ create_krd (struct TEH_KeyStateHandle *ksh,
static enum GNUNET_GenericReturnValue
finish_keys_response (struct TEH_KeyStateHandle *ksh)
{
+ enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
json_t *recoup;
- struct SignKeyCtx sctx;
- json_t *denoms;
- struct GNUNET_TIME_Absolute last_cpd;
+ struct SignKeyCtx sctx = {
+ .min_sk_frequency = GNUNET_TIME_UNIT_FOREVER_REL
+ };
+ json_t *grouped_denominations = NULL;
+ struct GNUNET_TIME_Timestamp last_cherry_pick_date;
struct GNUNET_CONTAINER_Heap *heap;
- struct GNUNET_HashContext *hash_context;
+ struct SignatureContext sig_ctx = { 0 };
+ /* Remember if we have any denomination with age restriction */
+ bool has_age_restricted_denomination = false;
+ struct WireStateHandle *wsh;
+ wsh = get_wire_state ();
+ if (! wsh->ready)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (0 ==
+ json_array_size (json_object_get (wsh->json_reply,
+ "accounts")) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "No wire accounts available. Refusing to generate /keys response.\n");
+ return GNUNET_NO;
+ }
sctx.signkeys = json_array ();
GNUNET_assert (NULL != sctx.signkeys);
- sctx.next_sk_expire = GNUNET_TIME_UNIT_FOREVER_ABS;
+ recoup = json_array ();
+ GNUNET_assert (NULL != recoup);
+ grouped_denominations = json_array ();
+ GNUNET_assert (NULL != grouped_denominations);
+
GNUNET_CONTAINER_multipeermap_iterate (ksh->signkey_map,
&add_sign_key_cb,
&sctx);
- recoup = json_array ();
- GNUNET_assert (NULL != recoup);
+ if (0 == json_array_size (sctx.signkeys))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "No online signing keys available. Refusing to generate /keys response.\n");
+ ret = GNUNET_NO;
+ goto CLEANUP;
+ }
heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX);
{
struct DenomKeyCtx dkc = {
.recoup = recoup,
.heap = heap,
- .next_dk_expire = GNUNET_TIME_UNIT_FOREVER_ABS,
+ .min_dk_frequency = GNUNET_TIME_UNIT_FOREVER_REL,
};
GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map,
&add_denom_key_cb,
&dkc);
- ksh->next_reload
- = GNUNET_TIME_absolute_min (dkc.next_dk_expire,
- sctx.next_sk_expire);
+ ksh->rekey_frequency
+ = GNUNET_TIME_relative_min (dkc.min_dk_frequency,
+ sctx.min_sk_frequency);
}
- denoms = json_array ();
- GNUNET_assert (NULL != denoms);
- last_cpd = GNUNET_TIME_UNIT_ZERO_ABS;
- hash_context = GNUNET_CRYPTO_hash_context_start ();
+
+ last_cherry_pick_date = GNUNET_TIME_UNIT_ZERO_TS;
+
{
struct TEH_DenominationKey *dk;
+ struct GNUNET_CONTAINER_MultiHashMap *denominations_by_group;
- /* heap = min heap, sorted by start time */
+ denominations_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 ( (last_cpd.abs_value_us != dk->meta.start.abs_value_us) &&
- (0 != last_cpd.abs_value_us) )
+ 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;
- GNUNET_CRYPTO_hash_context_finish (
- GNUNET_CRYPTO_hash_context_copy (hash_context),
- &hc);
+ compute_msig_hash (&sig_ctx,
+ &hc);
if (GNUNET_OK !=
create_krd (ksh,
&hc,
- last_cpd,
+ last_cherry_pick_date,
sctx.signkeys,
recoup,
- denoms))
+ grouped_denominations))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to generate key response data for %s\n",
- GNUNET_STRINGS_absolute_time_to_string (last_cpd));
- GNUNET_CRYPTO_hash_context_abort (hash_context);
+ 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);
- json_decref (denoms);
- json_decref (sctx.signkeys);
- json_decref (recoup);
- return GNUNET_SYSERR;
+ goto CLEANUP;
}
}
- last_cpd = dk->meta.start;
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &dk->h_denom_pub,
- sizeof (struct GNUNET_HashCode));
- GNUNET_assert (
- 0 ==
- json_array_append_new (
- denoms,
- GNUNET_JSON_PACK (
+
+ last_cherry_pick_date = dk->meta.start;
+ /*
+ * Group the denominations by {cipher, value, fees, age_mask}.
+ *
+ * For each group we save the group meta-data and the list of
+ * denominations in this group as a json-blob in the multihashmap
+ * denominations_by_group.
+ */
+ {
+ struct GroupData *group;
+ json_t *entry;
+ struct GNUNET_HashCode key;
+ struct TALER_DenominationGroup meta = {
+ .cipher = dk->denom_pub.bsign_pub_key->cipher,
+ .value = dk->meta.value,
+ .fees = dk->meta.fees,
+ .age_mask = dk->meta.age_mask,
+ };
+
+ /* Search the group/JSON-blob for the key */
+ TALER_denomination_group_get_key (&meta,
+ &key);
+ group = GNUNET_CONTAINER_multihashmap_get (
+ denominations_by_group,
+ &key);
+ if (NULL == group)
+ {
+ /* There is no group for this meta-data yet, so we create a new group */
+ bool age_restricted = meta.age_mask.bits != 0;
+ const char *cipher;
+
+ group = GNUNET_new (struct GroupData);
+ switch (meta.cipher)
+ {
+ case GNUNET_CRYPTO_BSA_RSA:
+ cipher = age_restricted ? "RSA+age_restricted" : "RSA";
+ break;
+ case GNUNET_CRYPTO_BSA_CS:
+ cipher = age_restricted ? "CS+age_restricted" : "CS";
+ break;
+ default:
+ GNUNET_assert (false);
+ }
+ /* Create a new array for the denominations 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 ("denoms",
+ group->list),
+ TALER_JSON_PACK_DENOM_FEES ("fee",
+ &meta.fees),
+ TALER_JSON_pack_amount ("value",
+ &meta.value));
+ GNUNET_assert (NULL != group->json);
+ if (age_restricted)
+ {
+ GNUNET_assert (
+ 0 ==
+ json_object_set_new (group->json,
+ "age_mask",
+ json_integer (
+ meta.age_mask.bits)));
+ /* Remember that we have found at least _one_ age restricted denomination */
+ has_age_restricted_denomination = true;
+ }
+ group->group_off
+ = json_array_size (grouped_denominations);
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ grouped_denominations,
+ group->json));
+ GNUNET_assert (
+ GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (denominations_by_group,
+ &key,
+ group,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ }
+
+ /* Now that we have found/created the right group, add the
+ denomination to the list */
+ {
+ struct HelperDenomination *hd;
+ struct GNUNET_JSON_PackSpec key_spec;
+ bool private_key_lost;
+
+ hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys,
+ &dk->h_denom_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->denom_pub.bsign_pub_key->details.rsa_public_key);
+ break;
+ case GNUNET_CRYPTO_BSA_CS:
+ key_spec =
+ GNUNET_JSON_pack_data_varsize (
+ "cs_pub",
+ &dk->denom_pub.bsign_pub_key->details.cs_public_key,
+ sizeof (dk->denom_pub.bsign_pub_key->details.cs_public_key));
+ break;
+ default:
+ GNUNET_assert (false);
+ }
+
+ entry = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("master_sig",
&dk->master_sig),
- GNUNET_JSON_pack_time_abs ("stamp_start",
- dk->meta.start),
- GNUNET_JSON_pack_time_abs ("stamp_expire_withdraw",
- dk->meta.expire_withdraw),
- GNUNET_JSON_pack_time_abs ("stamp_expire_deposit",
- dk->meta.expire_deposit),
- GNUNET_JSON_pack_time_abs ("stamp_expire_legal",
- dk->meta.expire_legal),
- GNUNET_JSON_pack_rsa_public_key ("denom_pub",
- dk->denom_pub.rsa_public_key),
- TALER_JSON_pack_amount ("value",
- &dk->meta.value),
- TALER_JSON_pack_amount ("fee_withdraw",
- &dk->meta.fee_withdraw),
- TALER_JSON_pack_amount ("fee_deposit",
- &dk->meta.fee_deposit),
- TALER_JSON_pack_amount ("fee_refresh",
- &dk->meta.fee_refresh),
- TALER_JSON_pack_amount ("fee_refund",
- &dk->meta.fee_refund))));
- }
+ 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);
+ }
+
+ /* Build up the running hash of all master signatures of the
+ denominations */
+ append_signature (&sig_ctx,
+ group->group_off,
+ (unsigned int) json_array_size (group->list),
+ &dk->master_sig);
+ /* Finally, add the denomination to the list of denominations in this
+ group */
+ GNUNET_assert (json_is_array (group->list));
+ GNUNET_assert (0 ==
+ json_array_append_new (group->list,
+ entry));
+ }
+ } /* loop over heap ends */
+
+ GNUNET_CONTAINER_multihashmap_iterate (denominations_by_group,
+ &free_group,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group);
}
GNUNET_CONTAINER_heap_destroy (heap);
- if (0 != last_cpd.abs_value_us)
+
+ if (! GNUNET_TIME_absolute_is_zero (last_cherry_pick_date.abs_time))
{
struct GNUNET_HashCode hc;
- GNUNET_CRYPTO_hash_context_finish (hash_context,
- &hc);
+ compute_msig_hash (&sig_ctx,
+ &hc);
if (GNUNET_OK !=
create_krd (ksh,
&hc,
- last_cpd,
+ last_cherry_pick_date,
sctx.signkeys,
recoup,
- denoms))
+ grouped_denominations))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to generate key response data for %s\n",
- GNUNET_STRINGS_absolute_time_to_string (last_cpd));
- json_decref (denoms);
- json_decref (sctx.signkeys);
- json_decref (recoup);
- return GNUNET_SYSERR;
+ GNUNET_TIME_timestamp2s (last_cherry_pick_date));
+ goto CLEANUP;
}
ksh->management_only = false;
+
+ /* Sanity check: Make sure that age restriction is enabled IFF at least
+ * one age restricted denomination exist */
+ if (! has_age_restricted_denomination && TEH_age_restriction_enabled)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Age restriction is enabled, but NO denominations with age restriction found!\n");
+ goto CLEANUP;
+ }
+ else if (has_age_restricted_denomination && ! TEH_age_restriction_enabled)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Age restriction is NOT enabled, but denominations with age restriction found!\n");
+ goto CLEANUP;
+ }
}
else
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"No denomination keys available. Refusing to generate /keys response.\n");
- GNUNET_CRYPTO_hash_context_abort (hash_context);
}
- json_decref (sctx.signkeys);
+ ret = GNUNET_OK;
+
+CLEANUP:
+ GNUNET_array_grow (sig_ctx.elements,
+ sig_ctx.elements_size,
+ 0);
+ json_decref (grouped_denominations);
+ if (NULL != sctx.signkeys)
+ json_decref (sctx.signkeys);
json_decref (recoup);
- json_decref (denoms);
- return GNUNET_OK;
+ return ret;
+}
+
+
+/**
+ * Called with information about global fees.
+ *
+ * @param cls `struct TEH_KeyStateHandle *` we are building
+ * @param fees the global fees we charge
+ * @param purse_timeout when do purses time out
+ * @param history_expiration how long are account histories preserved
+ * @param purse_account_limit how many purses are free per account
+ * @param start_date from when are these fees valid (start date)
+ * @param end_date until when are these fees valid (end date, exclusive)
+ * @param master_sig master key signature affirming that this is the correct
+ * fee (of purpose #TALER_SIGNATURE_MASTER_GLOBAL_FEES)
+ */
+static void
+global_fee_info_cb (
+ void *cls,
+ const struct TALER_GlobalFeeSet *fees,
+ struct GNUNET_TIME_Relative purse_timeout,
+ struct GNUNET_TIME_Relative history_expiration,
+ uint32_t purse_account_limit,
+ struct GNUNET_TIME_Timestamp start_date,
+ struct GNUNET_TIME_Timestamp end_date,
+ const struct TALER_MasterSignatureP *master_sig)
+{
+ struct TEH_KeyStateHandle *ksh = cls;
+ struct TEH_GlobalFee *gf;
+
+ if (GNUNET_OK !=
+ TALER_exchange_offline_global_fee_verify (
+ start_date,
+ end_date,
+ fees,
+ purse_timeout,
+ history_expiration,
+ purse_account_limit,
+ &TEH_master_public_key,
+ master_sig))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Database has global fee with invalid signature. Skipping entry. Did the exchange offline public key change?\n");
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Found global fees with %u purses\n",
+ purse_account_limit);
+ gf = GNUNET_new (struct TEH_GlobalFee);
+ gf->start_date = start_date;
+ gf->end_date = end_date;
+ gf->fees = *fees;
+ gf->purse_timeout = purse_timeout;
+ gf->history_expiration = history_expiration;
+ gf->purse_account_limit = purse_account_limit;
+ gf->master_sig = *master_sig;
+ GNUNET_CONTAINER_DLL_insert (ksh->gf_head,
+ ksh->gf_tail,
+ gf);
+ GNUNET_assert (
+ 0 ==
+ json_array_append_new (
+ ksh->global_fees,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_timestamp ("start_date",
+ start_date),
+ GNUNET_JSON_pack_timestamp ("end_date",
+ end_date),
+ TALER_JSON_PACK_GLOBAL_FEES (fees),
+ GNUNET_JSON_pack_time_rel ("history_expiration",
+ history_expiration),
+ GNUNET_JSON_pack_time_rel ("purse_timeout",
+ purse_timeout),
+ GNUNET_JSON_pack_uint64 ("purse_account_limit",
+ purse_account_limit),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ master_sig))));
}
@@ -1707,9 +3244,8 @@ build_key_state (struct HelperState *hs,
enum GNUNET_DB_QueryStatus qs;
ksh = GNUNET_new (struct TEH_KeyStateHandle);
- ksh->signature_expires = GNUNET_TIME_UNIT_FOREVER_ABS;
- ksh->reload_time = GNUNET_TIME_absolute_get ();
- GNUNET_TIME_round_abs (&ksh->reload_time);
+ ksh->signature_expires = GNUNET_TIME_UNIT_FOREVER_TS;
+ ksh->reload_time = GNUNET_TIME_timestamp_get ();
/* We must use the key_generation from when we STARTED the process! */
ksh->key_generation = key_generation;
if (NULL == hs)
@@ -1729,12 +3265,31 @@ build_key_state (struct HelperState *hs,
ksh->helpers = hs;
}
ksh->denomkey_map = GNUNET_CONTAINER_multihashmap_create (1024,
- GNUNET_YES);
+ true);
ksh->signkey_map = GNUNET_CONTAINER_multipeermap_create (32,
- GNUNET_NO /* MUST be NO! */);
+ false /* MUST be false! */);
ksh->auditors = json_array ();
GNUNET_assert (NULL != ksh->auditors);
/* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */
+ GNUNET_break (GNUNET_OK ==
+ TEH_plugin->preflight (TEH_plugin->cls));
+ if (NULL != ksh->global_fees)
+ json_decref (ksh->global_fees);
+ ksh->global_fees = json_array ();
+ qs = TEH_plugin->get_global_fees (TEH_plugin->cls,
+ &global_fee_info_cb,
+ ksh);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Loading global fees from DB: %d\n",
+ qs);
+ 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 = TEH_plugin->iterate_denominations (TEH_plugin->cls,
&denomination_info_cb,
ksh);
@@ -1777,20 +3332,23 @@ build_key_state (struct HelperState *hs,
true);
return NULL;
}
+
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;
}
+
return ksh;
}
@@ -1812,16 +3370,8 @@ TEH_keys_update_states ()
}
-/**
- * Obtain the key state for the current thread. Should ONLY be used
- * directly if @a management_only is true. Otherwise use #TEH_keys_get_state().
- *
- * @param management_only if we should NOT run 'finish_keys_response()'
- * because we only need the state for the /management/keys API
- * @return NULL on error
- */
static struct TEH_KeyStateHandle *
-get_key_state (bool management_only)
+keys_get_state (bool management_only)
{
struct TEH_KeyStateHandle *old_ksh;
struct TEH_KeyStateHandle *ksh;
@@ -1837,9 +3387,9 @@ get_key_state (bool management_only)
return ksh;
}
if ( (old_ksh->key_generation < key_generation) ||
- (GNUNET_TIME_absolute_is_past (old_ksh->signature_expires)) )
+ (GNUNET_TIME_absolute_is_past (old_ksh->signature_expires.abs_time)) )
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Rebuilding /keys, generation upgrade from %llu to %llu\n",
(unsigned long long) old_ksh->key_generation,
(unsigned long long) key_generation);
@@ -1857,27 +3407,58 @@ get_key_state (bool management_only)
struct TEH_KeyStateHandle *
+TEH_keys_get_state_for_management_only (void)
+{
+ return keys_get_state (true);
+}
+
+
+struct TEH_KeyStateHandle *
TEH_keys_get_state (void)
{
struct TEH_KeyStateHandle *ksh;
- ksh = get_key_state (false);
+ ksh = keys_get_state (false);
if (NULL == ksh)
return NULL;
+
if (ksh->management_only)
{
if (GNUNET_OK !=
finish_keys_response (ksh))
return NULL;
}
+
return ksh;
}
+const struct TEH_GlobalFee *
+TEH_keys_global_fee_by_time (
+ struct TEH_KeyStateHandle *ksh,
+ struct GNUNET_TIME_Timestamp ts)
+{
+ for (const struct TEH_GlobalFee *gf = ksh->gf_head;
+ NULL != gf;
+ gf = gf->next)
+ {
+ if (GNUNET_TIME_timestamp_cmp (ts,
+ >=,
+ gf->start_date) &&
+ GNUNET_TIME_timestamp_cmp (ts,
+ <,
+ gf->end_date))
+ return gf;
+ }
+ return NULL;
+}
+
+
struct TEH_DenominationKey *
-TEH_keys_denomination_by_hash (const struct GNUNET_HashCode *h_denom_pub,
- struct MHD_Connection *conn,
- MHD_RESULT *mret)
+TEH_keys_denomination_by_hash (
+ const struct TALER_DenominationHashP *h_denom_pub,
+ struct MHD_Connection *conn,
+ MHD_RESULT *mret)
{
struct TEH_KeyStateHandle *ksh;
@@ -1890,25 +3471,29 @@ TEH_keys_denomination_by_hash (const struct GNUNET_HashCode *h_denom_pub,
NULL);
return NULL;
}
- return TEH_keys_denomination_by_hash2 (ksh,
- h_denom_pub,
- conn,
- mret);
+
+ return TEH_keys_denomination_by_hash_from_state (ksh,
+ h_denom_pub,
+ conn,
+ mret);
}
struct TEH_DenominationKey *
-TEH_keys_denomination_by_hash2 (struct TEH_KeyStateHandle *ksh,
- const struct GNUNET_HashCode *h_denom_pub,
- struct MHD_Connection *conn,
- MHD_RESULT *mret)
+TEH_keys_denomination_by_hash_from_state (
+ const struct TEH_KeyStateHandle *ksh,
+ const struct TALER_DenominationHashP *h_denom_pub,
+ struct MHD_Connection *conn,
+ MHD_RESULT *mret)
{
struct TEH_DenominationKey *dk;
dk = GNUNET_CONTAINER_multihashmap_get (ksh->denomkey_map,
- h_denom_pub);
+ &h_denom_pub->hash);
if (NULL == dk)
{
+ if (NULL == conn)
+ return NULL;
*mret = TEH_RESPONSE_reply_unknown_denom_pub_hash (conn,
h_denom_pub);
return NULL;
@@ -1917,33 +3502,220 @@ TEH_keys_denomination_by_hash2 (struct TEH_KeyStateHandle *ksh,
}
-struct TALER_DenominationSignature
-TEH_keys_denomination_sign (const struct GNUNET_HashCode *h_denom_pub,
- const void *msg,
- size_t msg_size,
- enum TALER_ErrorCode *ec)
+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])
+{
+ struct TEH_KeyStateHandle *ksh;
+ struct HelperDenomination *hd;
+ struct TALER_CRYPTO_RsaSignRequest rsrs[csds_length];
+ struct TALER_CRYPTO_CsSignRequest csrs[csds_length];
+ struct TALER_BlindedDenominationSignature rs[csds_length];
+ struct TALER_BlindedDenominationSignature cs[csds_length];
+ unsigned int rsrs_pos = 0;
+ unsigned int csrs_pos = 0;
+ enum TALER_ErrorCode ec;
+
+ ksh = TEH_keys_get_state ();
+ if (NULL == ksh)
+ return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
+ for (unsigned int i = 0; i<csds_length; i++)
+ {
+ const struct TALER_DenominationHashP *h_denom_pub = csds[i].h_denom_pub;
+ const struct TALER_BlindedPlanchet *bp = csds[i].bp;
+
+ hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys,
+ &h_denom_pub->hash);
+ if (NULL == hd)
+ return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
+ if (bp->blinded_message->cipher !=
+ hd->denom_pub.bsign_pub_key->cipher)
+ return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+ switch (hd->denom_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_planchet
+ = &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,
+ for_melt,
+ (0 == rsrs_pos) ? bss : cs);
+ if (TALER_EC_NONE != ec)
+ {
+ for (unsigned int i = 0; i<csrs_pos; i++)
+ TALER_blinded_denom_sig_free (&cs[i]);
+ return ec;
+ }
+ TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_CS] += csrs_pos;
+ }
+ 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++)
+ TALER_blinded_denom_sig_free (&cs[i]);
+ for (unsigned int i = 0; i<rsrs_pos; i++)
+ TALER_blinded_denom_sig_free (&rs[i]);
+ return ec;
+ }
+ TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_RSA] += rsrs_pos;
+ }
+
+ if ( (0 != csrs_pos) &&
+ (0 != rsrs_pos) )
+ {
+ rsrs_pos = 0;
+ csrs_pos = 0;
+ for (unsigned int i = 0; i<csds_length; i++)
+ {
+ const struct TALER_BlindedPlanchet *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;
+}
+
+
+enum TALER_ErrorCode
+TEH_keys_denomination_cs_r_pub (
+ const struct TEH_CsDeriveData *cdd,
+ bool for_melt,
+ struct GNUNET_CRYPTO_CSPublicRPairP *r_pub)
{
+ const struct TALER_DenominationHashP *h_denom_pub = cdd->h_denom_pub;
+ const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdd->nonce;
struct TEH_KeyStateHandle *ksh;
- struct TALER_DenominationSignature none = { NULL };
+ struct HelperDenomination *hd;
ksh = TEH_keys_get_state ();
if (NULL == ksh)
{
- *ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
- return none;
+ return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
+ }
+ hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys,
+ &h_denom_pub->hash);
+ if (NULL == hd)
+ {
+ return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
+ }
+ if (GNUNET_CRYPTO_BSA_CS !=
+ hd->denom_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,
+ for_melt,
+ r_pub);
+ }
+}
+
+
+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])
+{
+ struct TEH_KeyStateHandle *ksh;
+ struct HelperDenomination *hd;
+ struct TALER_CRYPTO_CsDeriveRequest cdrs[cdds_length];
+
+ ksh = TEH_keys_get_state ();
+ if (NULL == ksh)
+ {
+ return TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
+ }
+ for (unsigned int i = 0; i<cdds_length; i++)
+ {
+ const struct TALER_DenominationHashP *h_denom_pub = cdds[i].h_denom_pub;
+ const struct GNUNET_CRYPTO_CsSessionNonce *nonce = cdds[i].nonce;
+
+ hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys,
+ &h_denom_pub->hash);
+ if (NULL == hd)
+ {
+ return TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
+ }
+ if (GNUNET_CRYPTO_BSA_CS !=
+ hd->denom_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_denom_sign (ksh->helpers->dh,
- h_denom_pub,
- msg,
- msg_size,
- ec);
+
+ return TALER_CRYPTO_helper_cs_r_batch_derive (ksh->helpers->csdh,
+ cdds_length,
+ cdrs,
+ for_melt,
+ r_pubs);
}
void
-TEH_keys_denomination_revoke (const struct GNUNET_HashCode *h_denom_pub)
+TEH_keys_denomination_revoke (const struct TALER_DenominationHashP *h_denom_pub)
{
struct TEH_KeyStateHandle *ksh;
+ struct HelperDenomination *hd;
ksh = TEH_keys_get_state ();
if (NULL == ksh)
@@ -1951,9 +3723,30 @@ TEH_keys_denomination_revoke (const struct GNUNET_HashCode *h_denom_pub)
GNUNET_break (0);
return;
}
- TALER_CRYPTO_helper_denom_revoke (ksh->helpers->dh,
- h_denom_pub);
- TEH_keys_update_states ();
+ hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys,
+ &h_denom_pub->hash);
+ if (NULL == hd)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ switch (hd->denom_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);
+ TEH_keys_update_states ();
+ return;
+ case GNUNET_CRYPTO_BSA_CS:
+ TALER_CRYPTO_helper_cs_revoke (ksh->helpers->csdh,
+ &hd->h_details.h_cs);
+ TEH_keys_update_states ();
+ return;
+ }
+ GNUNET_break (0);
+ return;
}
@@ -1983,13 +3776,15 @@ TEH_keys_exchange_sign_ (
enum TALER_ErrorCode
TEH_keys_exchange_sign2_ (
- struct TEH_KeyStateHandle *ksh,
+ void *cls,
const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
struct TALER_ExchangePublicKeyP *pub,
struct TALER_ExchangeSignatureP *sig)
{
+ struct TEH_KeyStateHandle *ksh = cls;
enum TALER_ErrorCode ec;
+ TEH_METRICS_num_signatures[TEH_MT_SIGNATURE_EDDSA]++;
ec = TALER_CRYPTO_helper_esign_sign_ (ksh->helpers->esh,
purpose,
pub,
@@ -2044,7 +3839,7 @@ TEH_keys_exchange_revoke (const struct TALER_ExchangePublicKeyP *exchange_pub)
* Comparator used for a binary search by cherry_pick_date for @a key in the
* `struct KeysResponseData` array. See libc's qsort() and bsearch() functions.
*
- * @param key pointer to a `struct GNUNET_TIME_Absolute`
+ * @param key pointer to a `struct GNUNET_TIME_Timestamp`
* @param value pointer to a `struct KeysResponseData` array entry
* @return 0 if time matches, -1 if key is smaller, 1 if key is larger
*/
@@ -2052,12 +3847,16 @@ static int
krd_search_comparator (const void *key,
const void *value)
{
- const struct GNUNET_TIME_Absolute *kd = key;
+ const struct GNUNET_TIME_Timestamp *kd = key;
const struct KeysResponseData *krd = value;
- if (kd->abs_value_us > krd->cherry_pick_date.abs_value_us)
+ if (GNUNET_TIME_timestamp_cmp (*kd,
+ >,
+ krd->cherry_pick_date))
return -1;
- if (kd->abs_value_us < krd->cherry_pick_date.abs_value_us)
+ if (GNUNET_TIME_timestamp_cmp (*kd,
+ <,
+ krd->cherry_pick_date))
return 1;
return 0;
}
@@ -2067,8 +3866,12 @@ MHD_RESULT
TEH_keys_get_handler (struct TEH_RequestContext *rc,
const char *const args[])
{
- struct GNUNET_TIME_Absolute last_issue_date;
+ struct GNUNET_TIME_Timestamp last_issue_date;
+ const char *etag;
+ etag = MHD_lookup_connection_value (rc->connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_IF_NONE_MATCH);
(void) args;
{
const char *have_cherrypick;
@@ -2095,11 +3898,11 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc,
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_absolute_from_s (cherrypickn);
+ last_issue_date = GNUNET_TIME_timestamp_from_s (cherrypickn);
}
else
{
- last_issue_date.abs_value_us = 0LLU;
+ last_issue_date = GNUNET_TIME_UNIT_ZERO_TS;
}
}
@@ -2108,15 +3911,20 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc,
const struct KeysResponseData *krd;
ksh = TEH_keys_get_state ();
- if (NULL == ksh)
+ if ( (NULL == ksh) ||
+ (0 == ksh->krd_array_length) )
{
- if ( (SKR_LIMIT == skr_size) &&
- (rc->connection == skr_connection) )
+ if ( ( (SKR_LIMIT == skr_size) &&
+ (rc->connection == skr_connection) ) ||
+ TEH_suicide)
{
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
- "too many connections suspended on /keys");
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_SERVICE_UNAVAILABLE,
+ TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+ TEH_suicide
+ ? "server terminating"
+ : "too many connections suspended waiting on /keys");
}
return suspend_request (rc->connection);
}
@@ -2127,16 +3935,16 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc,
&krd_search_comparator);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Filtering /keys by cherry pick date %s found entry %u/%u\n",
- GNUNET_STRINGS_absolute_time_to_string (last_issue_date),
+ GNUNET_TIME_timestamp2s (last_issue_date),
(unsigned int) (krd - ksh->krd_array),
ksh->krd_array_length);
if ( (NULL == krd) &&
(ksh->krd_array_length > 0) )
{
- if (0 != last_issue_date.abs_value_us)
+ if (! GNUNET_TIME_absolute_is_zero (last_issue_date.abs_time))
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Client provided invalid cherry picking timestamp %s, returning full response\n",
- GNUNET_STRINGS_absolute_time_to_string (last_issue_date));
+ GNUNET_TIME_timestamp2s (last_issue_date));
krd = &ksh->krd_array[ksh->krd_array_length - 1];
}
if (NULL == krd)
@@ -2145,6 +3953,14 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc,
Wait until they are. */
return suspend_request (rc->connection);
}
+ if ( (NULL != etag) &&
+ (0 == strcmp (etag,
+ krd->etag)) )
+ return TEH_RESPONSE_reply_not_modified (rc->connection,
+ krd->etag,
+ &setup_general_response_headers,
+ ksh);
+
return MHD_queue_response (rc->connection,
MHD_HTTP_OK,
(MHD_YES ==
@@ -2156,22 +3972,23 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc,
/**
- * Load fees and expiration times (!) for the denomination type configured in
- * section @a section_name. Before calling this function, the `start` and
- * `validity_duration` times must already be initialized in @a meta.
+ * Load extension data, like fees, expiration times (!) and age restriction
+ * flags for the denomination type configured in section @a section_name.
+ * Before calling this function, the `start` and `validity_duration` times must
+ * already be initialized in @a meta.
*
* @param section_name section in the configuration to use
* @param[in,out] meta denomination type data to complete
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
-load_fees (const char *section_name,
- struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta)
+load_extension_data (const char *section_name,
+ struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta)
{
struct GNUNET_TIME_Relative deposit_duration;
struct GNUNET_TIME_Relative legal_duration;
- GNUNET_assert (0 != meta->start.abs_value_us); /* caller bug */
+ GNUNET_assert (! GNUNET_TIME_absolute_is_zero (meta->start.abs_time)); /* caller bug */
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
section_name,
@@ -2194,14 +4011,13 @@ load_fees (const char *section_name,
"DURATION_LEGAL");
return GNUNET_SYSERR;
}
- /* NOTE: this is a change from the 0.8 semantics of the configuration:
- before duration_spend was relative to 'start', not to 'expire_withdraw'.
- But doing it this way avoids the error case where previously
- duration_spend < duration_withdraw was not allowed. */
- meta->expire_deposit = GNUNET_TIME_absolute_add (meta->expire_withdraw,
- deposit_duration);
- meta->expire_legal = GNUNET_TIME_absolute_add (meta->expire_deposit,
- legal_duration);
+ meta->expire_deposit
+ = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (meta->expire_withdraw.abs_time,
+ deposit_duration));
+ meta->expire_legal = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (meta->expire_deposit.abs_time,
+ legal_duration));
if (GNUNET_OK !=
TALER_config_get_amount (TEH_cfg,
section_name,
@@ -2214,115 +4030,71 @@ load_fees (const char *section_name,
section_name);
return GNUNET_SYSERR;
}
- if (GNUNET_OK !=
- TALER_config_get_amount (TEH_cfg,
- section_name,
- "FEE_WITHDRAW",
- &meta->fee_withdraw))
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- "Need amount for option `%s' in section `%s'\n",
- "FEE_WITHDRAW",
- section_name);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_config_get_amount (TEH_cfg,
- section_name,
- "FEE_DEPOSIT",
- &meta->fee_deposit))
+ if (0 != strcasecmp (TEH_currency,
+ meta->value.currency))
{
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- "Need amount for option `%s' in section `%s'\n",
- "FEE_DEPOSIT",
- section_name);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_config_get_amount (TEH_cfg,
- section_name,
- "FEE_REFRESH",
- &meta->fee_refresh))
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- "Need amount for option `%s' in section `%s'\n",
- "FEE_REFRESH",
- section_name);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Need denomination value in section `%s' to use currency `%s'\n",
+ section_name,
+ TEH_currency);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
- TALER_config_get_amount (TEH_cfg,
- section_name,
- "FEE_REFUND",
- &meta->fee_refund))
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- "Need amount for option `%s' in section `%s'\n",
- "FEE_REFUND",
- section_name);
- return GNUNET_SYSERR;
- }
- if ( (0 != strcasecmp (TEH_currency,
- meta->value.currency)) ||
- (0 != strcasecmp (TEH_currency,
- meta->fee_withdraw.currency)) ||
- (0 != strcasecmp (TEH_currency,
- meta->fee_deposit.currency)) ||
- (0 != strcasecmp (TEH_currency,
- meta->fee_refresh.currency)) ||
- (0 != strcasecmp (TEH_currency,
- meta->fee_refund.currency)) )
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- "Need amounts in section `%s' to use currency `%s'\n",
- section_name,
- TEH_currency);
+ TALER_config_get_denom_fees (TEH_cfg,
+ TEH_currency,
+ section_name,
+ &meta->fees))
return GNUNET_SYSERR;
- }
+ meta->age_mask = load_age_mask (section_name);
return GNUNET_OK;
}
enum GNUNET_GenericReturnValue
-TEH_keys_load_fees (const struct GNUNET_HashCode *h_denom_pub,
+TEH_keys_load_fees (struct TEH_KeyStateHandle *ksh,
+ const struct TALER_DenominationHashP *h_denom_pub,
struct TALER_DenominationPublicKey *denom_pub,
struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta)
{
- struct TEH_KeyStateHandle *ksh;
struct HelperDenomination *hd;
enum GNUNET_GenericReturnValue ok;
- ksh = get_key_state (true);
- if (NULL == ksh)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
-
hd = GNUNET_CONTAINER_multihashmap_get (ksh->helpers->denom_keys,
- h_denom_pub);
+ &h_denom_pub->hash);
if (NULL == hd)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Denomination %s not known\n",
- GNUNET_h2s (h_denom_pub));
+ GNUNET_h2s (&h_denom_pub->hash));
return GNUNET_NO;
}
meta->start = hd->start_time;
- meta->expire_withdraw = GNUNET_TIME_absolute_add (meta->start,
- hd->validity_duration);
- ok = load_fees (hd->section_name,
- meta);
+ meta->expire_withdraw = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (meta->start.abs_time,
+ hd->validity_duration));
+ ok = load_extension_data (hd->section_name,
+ meta);
if (GNUNET_OK == ok)
- denom_pub->rsa_public_key
- = GNUNET_CRYPTO_rsa_public_key_dup (hd->denom_pub.rsa_public_key);
+ {
+ GNUNET_assert (GNUNET_CRYPTO_BSA_INVALID !=
+ hd->denom_pub.bsign_pub_key->cipher);
+ TALER_denom_pub_copy (denom_pub,
+ &hd->denom_pub);
+ }
else
- denom_pub->rsa_public_key = NULL;
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "No fees for `%s', voiding key\n",
+ hd->section_name);
+ memset (denom_pub,
+ 0,
+ sizeof (*denom_pub));
+ }
return ok;
}
-int
+enum GNUNET_GenericReturnValue
TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub,
struct TALER_EXCHANGEDB_SignkeyMetaData *meta)
{
@@ -2330,7 +4102,7 @@ TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub,
struct HelperSignkey *hsk;
struct GNUNET_PeerIdentity pid;
- ksh = get_key_state (true);
+ ksh = TEH_keys_get_state_for_management_only ();
if (NULL == ksh)
{
GNUNET_break (0);
@@ -2340,11 +4112,19 @@ TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub,
pid.public_key = exchange_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_add (meta->start,
- hsk->validity_duration);
- meta->expire_legal = GNUNET_TIME_absolute_add (meta->expire_sign,
- signkey_legal_duration);
+
+ 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;
}
@@ -2383,7 +4163,7 @@ struct FutureBuilderContext
* @param value a `struct HelperDenomination`
* @return #GNUNET_OK (continue to iterate)
*/
-static int
+static enum GNUNET_GenericReturnValue
add_future_denomkey_cb (void *cls,
const struct GNUNET_HashCode *h_denom_pub,
void *value)
@@ -2391,7 +4171,7 @@ add_future_denomkey_cb (void *cls,
struct FutureBuilderContext *fbc = cls;
struct HelperDenomination *hd = value;
struct TEH_DenominationKey *dk;
- struct TALER_EXCHANGEDB_DenominationKeyMetaData meta;
+ struct TALER_EXCHANGEDB_DenominationKeyMetaData meta = {0};
dk = GNUNET_CONTAINER_multihashmap_get (fbc->ksh->denomkey_map,
h_denom_pub);
@@ -2400,11 +4180,12 @@ add_future_denomkey_cb (void *cls,
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_add (meta.start,
- hd->validity_duration);
+ meta.expire_withdraw = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_absolute_add (meta.start.abs_time,
+ hd->validity_duration));
if (GNUNET_OK !=
- load_fees (hd->section_name,
- &meta))
+ load_extension_data (hd->section_name,
+ &meta))
{
/* Woops, couldn't determine fee structure!? */
return GNUNET_OK;
@@ -2416,24 +4197,18 @@ add_future_denomkey_cb (void *cls,
GNUNET_JSON_PACK (
TALER_JSON_pack_amount ("value",
&meta.value),
- GNUNET_JSON_pack_time_abs ("stamp_start",
- meta.start),
- GNUNET_JSON_pack_time_abs ("stamp_expire_withdraw",
- meta.expire_withdraw),
- GNUNET_JSON_pack_time_abs ("stamp_expire_deposit",
- meta.expire_deposit),
- GNUNET_JSON_pack_time_abs ("stamp_expire_legal",
- meta.expire_legal),
- GNUNET_JSON_pack_rsa_public_key ("denom_pub",
- hd->denom_pub.rsa_public_key),
- TALER_JSON_pack_amount ("fee_withdraw",
- &meta.fee_withdraw),
- TALER_JSON_pack_amount ("fee_deposit",
- &meta.fee_deposit),
- TALER_JSON_pack_amount ("fee_refresh",
- &meta.fee_refresh),
- TALER_JSON_pack_amount ("fee_refund",
- &meta.fee_refund),
+ 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),
+ TALER_JSON_pack_denom_pub ("denom_pub",
+ &hd->denom_pub),
+ TALER_JSON_PACK_DENOM_FEES ("fee",
+ &meta.fees),
GNUNET_JSON_pack_data_auto ("denom_secmod_sig",
&hd->sm_sig),
GNUNET_JSON_pack_string ("section_name",
@@ -2453,7 +4228,7 @@ add_future_denomkey_cb (void *cls,
* @param value a `struct HelperDenomination`
* @return #GNUNET_OK (continue to iterate)
*/
-static int
+static enum GNUNET_GenericReturnValue
add_future_signkey_cb (void *cls,
const struct GNUNET_PeerIdentity *pid,
void *value)
@@ -2461,8 +4236,8 @@ add_future_signkey_cb (void *cls,
struct FutureBuilderContext *fbc = cls;
struct HelperSignkey *hsk = value;
struct SigningKey *sk;
- struct GNUNET_TIME_Absolute stamp_expire;
- struct GNUNET_TIME_Absolute legal_end;
+ struct GNUNET_TIME_Timestamp stamp_expire;
+ struct GNUNET_TIME_Timestamp legal_end;
sk = GNUNET_CONTAINER_multipeermap_get (fbc->ksh->signkey_map,
pid);
@@ -2470,22 +4245,24 @@ add_future_signkey_cb (void *cls,
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_add (hsk->start_time,
- hsk->validity_duration);
- legal_end = GNUNET_TIME_absolute_add (stamp_expire,
- signkey_legal_duration);
+ 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->exchange_pub),
- GNUNET_JSON_pack_time_abs ("stamp_start",
- hsk->start_time),
- GNUNET_JSON_pack_time_abs ("stamp_expire",
- stamp_expire),
- GNUNET_JSON_pack_time_abs ("stamp_end",
- legal_end),
+ 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;
@@ -2499,11 +4276,12 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh,
struct TEH_KeyStateHandle *ksh;
json_t *reply;
- ksh = get_key_state (true);
+ (void) rh;
+ ksh = TEH_keys_get_state_for_management_only ();
if (NULL == ksh)
{
return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
+ MHD_HTTP_SERVICE_UNAVAILABLE,
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
"no key state");
}
@@ -2516,8 +4294,10 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh,
.signkeys = json_array ()
};
- if (GNUNET_is_zero (&denom_sm_pub))
+ if ( (GNUNET_is_zero (&denom_rsa_sm_pub)) &&
+ (GNUNET_is_zero (&denom_cs_sm_pub)) )
{
+ /* Either IPC failed, or neither helper had any denominations configured. */
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_GATEWAY,
TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE,
@@ -2530,7 +4310,6 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh,
TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE,
NULL);
}
- // then a secmod helper is not yet running and we should return an MHD_HTTP_BAD_GATEWAY!
GNUNET_assert (NULL != fbc.denoms);
GNUNET_assert (NULL != fbc.signkeys);
GNUNET_CONTAINER_multihashmap_iterate (ksh->helpers->denom_keys,
@@ -2547,7 +4326,9 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh,
GNUNET_JSON_pack_data_auto ("master_pub",
&TEH_master_public_key),
GNUNET_JSON_pack_data_auto ("denom_secmod_public_key",
- &denom_sm_pub),
+ &denom_rsa_sm_pub),
+ GNUNET_JSON_pack_data_auto ("denom_secmod_cs_public_key",
+ &denom_cs_sm_pub),
GNUNET_JSON_pack_data_auto ("signkey_secmod_public_key",
&esign_sm_pub));
GNUNET_log (GNUNET_ERROR_TYPE_INFO,