summaryrefslogtreecommitdiff
path: root/src/lib/exchange_api_handle.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/exchange_api_handle.c')
-rw-r--r--src/lib/exchange_api_handle.c2890
1 files changed, 1607 insertions, 1283 deletions
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index 7103f6d6d..fdadc8d2a 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2021 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
@@ -30,6 +30,7 @@
#include "taler_exchange_service.h"
#include "taler_auditor_service.h"
#include "taler_signatures.h"
+#include "taler_extensions.h"
#include "exchange_api_handle.h"
#include "exchange_api_curl_defaults.h"
#include "backoff.h"
@@ -39,12 +40,17 @@
* Which version of the Taler protocol is implemented
* by this library? Used to determine compatibility.
*/
-#define EXCHANGE_PROTOCOL_CURRENT 10
+#define EXCHANGE_PROTOCOL_CURRENT 19
/**
* How many versions are we backwards compatible with?
*/
-#define EXCHANGE_PROTOCOL_AGE 0
+#define EXCHANGE_PROTOCOL_AGE 2
+
+/**
+ * Set to 1 for extra debug logging.
+ */
+#define DEBUG 0
/**
* Current version for (local) JSON serialization of persisted
@@ -53,191 +59,322 @@
#define EXCHANGE_SERIALIZATION_FORMAT_VERSION 0
/**
- * Set to 1 for extra debug logging.
+ * How far off do we allow key lifetimes to be?
*/
-#define DEBUG 0
+#define LIFETIME_TOLERANCE GNUNET_TIME_UNIT_HOURS
/**
- * Log error related to CURL operations.
- *
- * @param type log level
- * @param function which function failed to run
- * @param code what was the curl error code
+ * If the "Expire" cache control header is missing, for
+ * how long do we assume the reply to be valid at least?
*/
-#define CURL_STRERROR(type, function, code) \
- GNUNET_log (type, "Curl function `%s' has failed at `%s:%d' with error: %s", \
- function, __FILE__, __LINE__, curl_easy_strerror (code));
-
+#define DEFAULT_EXPIRATION GNUNET_TIME_UNIT_HOURS
/**
- * Data for the request to get the /keys of a exchange.
+ * If the "Expire" cache control header is missing, for
+ * how long do we assume the reply to be valid at least?
*/
-struct KeysRequest;
+#define MINIMUM_EXPIRATION GNUNET_TIME_relative_multiply ( \
+ GNUNET_TIME_UNIT_MINUTES, 2)
/**
- * Entry in DLL of auditors used by an exchange.
+ * Handle for a GET /keys request.
*/
-struct TEAH_AuditorListEntry
+struct TALER_EXCHANGE_GetKeysHandle
{
- /**
- * Next pointer of DLL.
- */
- struct TEAH_AuditorListEntry *next;
/**
- * Prev pointer of DLL.
+ * The exchange base URL (i.e. "https://exchange.demo.taler.net/")
*/
- struct TEAH_AuditorListEntry *prev;
+ char *exchange_url;
/**
- * Base URL of the auditor.
+ * The url for the /keys request.
*/
- char *auditor_url;
+ char *url;
/**
- * Handle to the auditor.
+ * Previous /keys response, NULL for none.
*/
- struct TALER_AUDITOR_Handle *ah;
+ struct TALER_EXCHANGE_Keys *prev_keys;
/**
- * Head of DLL of interactions with this auditor.
+ * Entry for this request with the `struct GNUNET_CURL_Context`.
*/
- struct TEAH_AuditorInteractionEntry *ai_head;
+ struct GNUNET_CURL_Job *job;
/**
- * Tail of DLL of interactions with this auditor.
+ * Expiration time according to "Expire:" header.
+ * 0 if not provided by the server.
*/
- struct TEAH_AuditorInteractionEntry *ai_tail;
+ struct GNUNET_TIME_Timestamp expire;
/**
- * Public key of the auditor.
+ * Function to call with the exchange's certification data,
+ * NULL if this has already been done.
*/
- struct TALER_AuditorPublicKeyP auditor_pub;
+ TALER_EXCHANGE_GetKeysCallback cert_cb;
/**
- * Flag indicating that the auditor is available and that protocol
- * version compatibility is given.
+ * Closure to pass to @e cert_cb.
*/
- int is_up;
+ void *cert_cb_cls;
};
-/* ***************** Internal /keys fetching ************* */
-
/**
- * Data for the request to get the /keys of a exchange.
+ * Element in the `struct SignatureContext` array.
*/
-struct KeysRequest
+struct SignatureElement
{
+
/**
- * The connection to exchange this request handle will use
+ * Offset of the denomination in the group array,
+ * for sorting (2nd rank, ascending).
*/
- struct TALER_EXCHANGE_Handle *exchange;
+ unsigned int offset;
/**
- * The url for this handle
+ * Offset of the group in the denominations array,
+ * for sorting (2nd rank, ascending).
*/
- char *url;
+ unsigned int group_offset;
/**
- * Entry for this request with the `struct GNUNET_CURL_Context`.
+ * Pointer to actual master signature to hash over.
*/
- struct GNUNET_CURL_Job *job;
+ struct TALER_MasterSignatureP master_sig;
+};
+/**
+ * Context for collecting the array of master signatures
+ * needed to verify the exchange_sig online signature.
+ */
+struct SignatureContext
+{
/**
- * Expiration time according to "Expire:" header.
- * 0 if not provided by the server.
+ * Array of signatures to hash over.
+ */
+ struct SignatureElement *elements;
+
+ /**
+ * Write offset in the @e elements array.
*/
- struct GNUNET_TIME_Absolute expire;
+ unsigned int elements_pos;
+ /**
+ * Allocated space for @e elements.
+ */
+ unsigned int elements_size;
};
/**
- * Signature of functions called with the result from our call to the
- * auditor's /deposit-confirmation handler.
+ * Determine order to sort two elements by before
+ * we hash the master signatures. Used for
+ * sorting with qsort().
*
- * @param cls closure of type `struct TEAH_AuditorInteractionEntry *`
- * @param hr HTTP response
+ * @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.
*/
-void
-TEAH_acc_confirmation_cb (void *cls,
- const struct TALER_AUDITOR_HttpResponse *hr)
+static int
+signature_context_sort_cb (const void *a,
+ const void *b)
{
- struct TEAH_AuditorInteractionEntry *aie = cls;
- struct TEAH_AuditorListEntry *ale = aie->ale;
+ const struct SignatureElement *sa = a;
+ const struct SignatureElement *sb = b;
- if (MHD_HTTP_OK != hr->http_status)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to submit deposit confirmation to auditor `%s' with HTTP status %d (EC: %d). This is acceptable if it does not happen often.\n",
- ale->auditor_url,
- hr->http_status,
- hr->ec);
- }
- GNUNET_CONTAINER_DLL_remove (ale->ai_head,
- ale->ai_tail,
- aie);
- GNUNET_free (aie);
+ 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;
}
/**
- * Iterate over all available auditors for @a h, calling
- * @a ac and giving it a chance to start a deposit
- * confirmation interaction.
+ * Append a @a master_sig to the @a sig_ctx using the
+ * given attributes for (later) sorting.
*
- * @param h exchange to go over auditors for
- * @param ac function to call per auditor
- * @param ac_cls closure for @a ac
+ * @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
*/
-void
-TEAH_get_auditors_for_dc (struct TALER_EXCHANGE_Handle *h,
- TEAH_AuditorCallback ac,
- void *ac_cls)
+static void
+append_signature (struct SignatureContext *sig_ctx,
+ unsigned int group_offset,
+ unsigned int offset,
+ const struct TALER_MasterSignatureP *master_sig)
{
- if (NULL == h->auditors_head)
+ struct SignatureElement *element;
+ unsigned int new_size;
+
+ if (sig_ctx->elements_pos == sig_ctx->elements_size)
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "No auditor available for exchange `%s'. Not submitting deposit confirmations.\n",
- h->url);
- return;
+ 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);
}
- for (struct TEAH_AuditorListEntry *ale = h->auditors_head;
- NULL != ale;
- ale = ale->next)
+ element = &sig_ctx->elements[sig_ctx->elements_pos++];
+ element->offset = offset;
+ element->group_offset = group_offset;
+ element->master_sig = *master_sig;
+}
+
+
+/**
+ * Frees @a wfm array.
+ *
+ * @param wfm fee array to release
+ * @param wfm_len length of the @a wfm array
+ */
+static void
+free_fees (struct TALER_EXCHANGE_WireFeesByMethod *wfm,
+ unsigned int wfm_len)
+{
+ for (unsigned int i = 0; i<wfm_len; i++)
{
- struct TEAH_AuditorInteractionEntry *aie;
+ struct TALER_EXCHANGE_WireFeesByMethod *wfmi = &wfm[i];
- if (GNUNET_NO == ale->is_up)
- continue;
- aie = ac (ac_cls,
- ale->ah,
- &ale->auditor_pub);
- if (NULL != aie)
+ while (NULL != wfmi->fees_head)
{
- aie->ale = ale;
- GNUNET_CONTAINER_DLL_insert (ale->ai_head,
- ale->ai_tail,
- aie);
+ struct TALER_EXCHANGE_WireAggregateFees *fe
+ = wfmi->fees_head;
+
+ wfmi->fees_head = fe->next;
+ GNUNET_free (fe);
}
+ GNUNET_free (wfmi->method);
}
+ GNUNET_free (wfm);
}
/**
- * Release memory occupied by a keys request. Note that this does not
- * cancel the request itself.
+ * Parse wire @a fees and return array.
*
- * @param kr request to free
+ * @param master_pub master public key to use to check signatures
+ * @param currency currency amounts are expected in
+ * @param fees json AggregateTransferFee to parse
+ * @param[out] fees_len set to length of returned array
+ * @return NULL on error
*/
-static void
-free_keys_request (struct KeysRequest *kr)
+static struct TALER_EXCHANGE_WireFeesByMethod *
+parse_fees (const struct TALER_MasterPublicKeyP *master_pub,
+ const char *currency,
+ const json_t *fees,
+ unsigned int *fees_len)
{
- GNUNET_free (kr->url);
- GNUNET_free (kr);
+ struct TALER_EXCHANGE_WireFeesByMethod *fbm;
+ size_t fbml = json_object_size (fees);
+ unsigned int i = 0;
+ const char *key;
+ const json_t *fee_array;
+
+ if (UINT_MAX < fbml)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ fbm = GNUNET_new_array (fbml,
+ struct TALER_EXCHANGE_WireFeesByMethod);
+ *fees_len = (unsigned int) fbml;
+ json_object_foreach ((json_t *) fees, key, fee_array) {
+ struct TALER_EXCHANGE_WireFeesByMethod *fe = &fbm[i++];
+ size_t idx;
+ json_t *fee;
+
+ fe->method = GNUNET_strdup (key);
+ fe->fees_head = NULL;
+ json_array_foreach (fee_array, idx, fee)
+ {
+ struct TALER_EXCHANGE_WireAggregateFees *wa
+ = GNUNET_new (struct TALER_EXCHANGE_WireAggregateFees);
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("sig",
+ &wa->master_sig),
+ TALER_JSON_spec_amount ("wire_fee",
+ currency,
+ &wa->fees.wire),
+ TALER_JSON_spec_amount ("closing_fee",
+ currency,
+ &wa->fees.closing),
+ GNUNET_JSON_spec_timestamp ("start_date",
+ &wa->start_date),
+ GNUNET_JSON_spec_timestamp ("end_date",
+ &wa->end_date),
+ GNUNET_JSON_spec_end ()
+ };
+
+ wa->next = fe->fees_head;
+ fe->fees_head = wa;
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (fee,
+ spec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ free_fees (fbm,
+ i);
+ return NULL;
+ }
+ if (GNUNET_OK !=
+ TALER_exchange_offline_wire_fee_verify (
+ key,
+ wa->start_date,
+ wa->end_date,
+ &wa->fees,
+ master_pub,
+ &wa->master_sig))
+ {
+ GNUNET_break_op (0);
+ free_fees (fbm,
+ i);
+ return NULL;
+ }
+ } /* for all fees over time */
+ } /* for all methods */
+ GNUNET_assert (i == fbml);
+ return fbm;
+}
+
+
+void
+TEAH_get_auditors_for_dc (
+ struct TALER_EXCHANGE_Keys *keys,
+ TEAH_AuditorCallback ac,
+ void *ac_cls)
+{
+ if (0 == keys->num_auditors)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "No auditor available. Not submitting deposit confirmations.\n");
+ return;
+ }
+ for (unsigned int i = 0; i<keys->num_auditors; i++)
+ {
+ const struct TALER_EXCHANGE_AuditorInformation *auditor
+ = &keys->auditors[i];
+
+ ac (ac_cls,
+ auditor->auditor_url,
+ &auditor->auditor_pub);
+ }
}
@@ -252,29 +389,28 @@ free_keys_request (struct KeysRequest *kr)
*
* @param[out] sign_key where to return the result
* @param check_sigs should we check signatures?
- * @param[in] sign_key_obj json to parse
+ * @param sign_key_obj json to parse
* @param master_key master key to use to verify signature
* @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
- * invalid or the json malformed.
+ * invalid or the @a sign_key_obj is malformed.
*/
static enum GNUNET_GenericReturnValue
parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key,
- int check_sigs,
- json_t *sign_key_obj,
+ bool check_sigs,
+ const json_t *sign_key_obj,
const struct TALER_MasterPublicKeyP *master_key)
{
- struct TALER_MasterSignatureP sign_key_issue_sig;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("master_sig",
- &sign_key_issue_sig),
+ &sign_key->master_sig),
GNUNET_JSON_spec_fixed_auto ("key",
&sign_key->key),
- TALER_JSON_spec_absolute_time ("stamp_start",
- &sign_key->valid_from),
- TALER_JSON_spec_absolute_time ("stamp_expire",
- &sign_key->valid_until),
- TALER_JSON_spec_absolute_time ("stamp_end",
- &sign_key->valid_legal),
+ GNUNET_JSON_spec_timestamp ("stamp_start",
+ &sign_key->valid_from),
+ GNUNET_JSON_spec_timestamp ("stamp_expire",
+ &sign_key->valid_until),
+ GNUNET_JSON_spec_timestamp ("stamp_end",
+ &sign_key->valid_legal),
GNUNET_JSON_spec_end ()
};
@@ -286,7 +422,6 @@ parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
-
if (! check_sigs)
return GNUNET_OK;
if (GNUNET_OK !=
@@ -296,57 +431,64 @@ parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key,
sign_key->valid_until,
sign_key->valid_legal,
master_key,
- &sign_key_issue_sig))
+ &sign_key->master_sig))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
- sign_key->master_sig = sign_key_issue_sig;
return GNUNET_OK;
}
/**
- * Parse a exchange's denomination key encoded in JSON.
+ * Parse a exchange's denomination key encoded in JSON partially.
+ *
+ * Only the values for master_sig, timestamps and the cipher-specific public
+ * key are parsed. All other fields (fees, age_mask, value) MUST have been set
+ * prior to calling this function, otherwise the signature verification
+ * performed within this function will fail.
*
* @param[out] denom_key where to return the result
+ * @param cipher cipher type to parse
* @param check_sigs should we check signatures?
- * @param[in] denom_key_obj json to parse
+ * @param denom_key_obj json to parse
* @param master_key master key to use to verify signature
- * @param hash_context where to accumulate data for signature verification
+ * @param group_offset offset for the group
+ * @param index index of this denomination key in the group
+ * @param sig_ctx where to write details about encountered
+ * master signatures, NULL if not used
* @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
* invalid or the json malformed.
*/
static enum GNUNET_GenericReturnValue
-parse_json_denomkey (struct TALER_EXCHANGE_DenomPublicKey *denom_key,
- int check_sigs,
- json_t *denom_key_obj,
- struct TALER_MasterPublicKeyP *master_key,
- struct GNUNET_HashContext *hash_context)
+parse_json_denomkey_partially (
+ struct TALER_EXCHANGE_DenomPublicKey *denom_key,
+ enum GNUNET_CRYPTO_BlindSignatureAlgorithm cipher,
+ bool check_sigs,
+ const json_t *denom_key_obj,
+ struct TALER_MasterPublicKeyP *master_key,
+ unsigned int group_offset,
+ unsigned int index,
+ struct SignatureContext *sig_ctx)
{
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("master_sig",
&denom_key->master_sig),
- TALER_JSON_spec_absolute_time ("stamp_expire_deposit",
- &denom_key->expire_deposit),
- TALER_JSON_spec_absolute_time ("stamp_expire_withdraw",
- &denom_key->withdraw_valid_until),
- TALER_JSON_spec_absolute_time ("stamp_start",
- &denom_key->valid_from),
- TALER_JSON_spec_absolute_time ("stamp_expire_legal",
- &denom_key->expire_legal),
- TALER_JSON_spec_amount_any ("value",
- &denom_key->value),
- TALER_JSON_spec_amount_any ("fee_withdraw",
- &denom_key->fee_withdraw),
- TALER_JSON_spec_amount_any ("fee_deposit",
- &denom_key->fee_deposit),
- TALER_JSON_spec_amount_any ("fee_refresh",
- &denom_key->fee_refresh),
- TALER_JSON_spec_amount_any ("fee_refund",
- &denom_key->fee_refund),
- TALER_JSON_spec_denom_pub ("denom_pub",
- &denom_key->key),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
+ &denom_key->expire_deposit),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
+ &denom_key->withdraw_valid_until),
+ GNUNET_JSON_spec_timestamp ("stamp_start",
+ &denom_key->valid_from),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
+ &denom_key->expire_legal),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("lost",
+ &denom_key->lost),
+ NULL),
+ TALER_JSON_spec_denom_pub_cipher (NULL,
+ cipher,
+ &denom_key->key),
GNUNET_JSON_spec_end ()
};
@@ -358,13 +500,13 @@ parse_json_denomkey (struct TALER_EXCHANGE_DenomPublicKey *denom_key,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
-
TALER_denom_pub_hash (&denom_key->key,
&denom_key->h_key);
- if (NULL != hash_context)
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &denom_key->h_key,
- sizeof (struct GNUNET_HashCode));
+ if (NULL != sig_ctx)
+ append_signature (sig_ctx,
+ group_offset,
+ index,
+ &denom_key->master_sig);
if (! check_sigs)
return GNUNET_OK;
EXITIF (GNUNET_SYSERR ==
@@ -375,19 +517,16 @@ parse_json_denomkey (struct TALER_EXCHANGE_DenomPublicKey *denom_key,
denom_key->expire_deposit,
denom_key->expire_legal,
&denom_key->value,
- &denom_key->fee_withdraw,
- &denom_key->fee_deposit,
- &denom_key->fee_refresh,
- &denom_key->fee_refund,
+ &denom_key->fees,
master_key,
&denom_key->master_sig));
return GNUNET_OK;
EXITIF_exit:
+ GNUNET_JSON_parse_free (spec);
/* invalidate denom_key, just to be sure */
memset (denom_key,
0,
sizeof (*denom_key));
- GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
@@ -397,30 +536,29 @@ EXITIF_exit:
*
* @param[out] auditor where to return the result
* @param check_sigs should we check signatures
- * @param[in] auditor_obj json to parse
+ * @param auditor_obj json to parse
* @param key_data information about denomination keys
* @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
* invalid or the json malformed.
*/
static enum GNUNET_GenericReturnValue
parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor,
- int check_sigs,
- json_t *auditor_obj,
+ bool check_sigs,
+ const json_t *auditor_obj,
const struct TALER_EXCHANGE_Keys *key_data)
{
- json_t *keys;
+ const json_t *keys;
json_t *key;
- unsigned int len;
- unsigned int off;
- unsigned int i;
+ size_t off;
+ size_t pos;
const char *auditor_url;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("auditor_pub",
&auditor->auditor_pub),
- GNUNET_JSON_spec_string ("auditor_url",
+ TALER_JSON_spec_web_url ("auditor_url",
&auditor_url),
- GNUNET_JSON_spec_json ("denomination_keys",
- &keys),
+ GNUNET_JSON_spec_array_const ("denomination_keys",
+ &keys),
GNUNET_JSON_spec_end ()
};
@@ -438,16 +576,15 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor,
return GNUNET_SYSERR;
}
auditor->auditor_url = GNUNET_strdup (auditor_url);
- len = json_array_size (keys);
- auditor->denom_keys = GNUNET_new_array (len,
- struct
- TALER_EXCHANGE_AuditorDenominationInfo);
- off = 0;
- json_array_foreach (keys, i, key) {
+ auditor->denom_keys
+ = GNUNET_new_array (json_array_size (keys),
+ struct TALER_EXCHANGE_AuditorDenominationInfo);
+ pos = 0;
+ json_array_foreach (keys, off, key) {
struct TALER_AuditorSignatureP auditor_sig;
- struct TALER_DenominationHash denom_h;
- const struct TALER_EXCHANGE_DenomPublicKey *dk;
- unsigned int dk_off;
+ struct TALER_DenominationHashP denom_h;
+ const struct TALER_EXCHANGE_DenomPublicKey *dk = NULL;
+ unsigned int dk_off = UINT_MAX;
struct GNUNET_JSON_Specification kspec[] = {
GNUNET_JSON_spec_fixed_auto ("auditor_sig",
&auditor_sig),
@@ -464,8 +601,6 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor,
GNUNET_break_op (0);
continue;
}
- dk = NULL;
- dk_off = UINT_MAX;
for (unsigned int j = 0; j<key_data->num_denom_keys; j++)
{
if (0 == GNUNET_memcmp (&denom_h,
@@ -495,126 +630,95 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor,
dk->expire_deposit,
dk->expire_legal,
&dk->value,
- &dk->fee_withdraw,
- &dk->fee_deposit,
- &dk->fee_refresh,
- &dk->fee_refund,
+ &dk->fees,
&auditor->auditor_pub,
&auditor_sig))
{
GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
}
- auditor->denom_keys[off].denom_key_offset = dk_off;
- auditor->denom_keys[off].auditor_sig = auditor_sig;
- off++;
+ auditor->denom_keys[pos].denom_key_offset = dk_off;
+ auditor->denom_keys[pos].auditor_sig = auditor_sig;
+ pos++;
}
- auditor->num_denom_keys = off;
- GNUNET_JSON_parse_free (spec);
+ if (pos > UINT_MAX)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ auditor->num_denom_keys = (unsigned int) pos;
return GNUNET_OK;
}
/**
- * Function called with information about the auditor. Marks an
- * auditor as 'up'.
+ * Parse a exchange's global fee information encoded in JSON.
*
- * @param cls closure, a `struct TEAH_AuditorListEntry *`
- * @param hr http response from the auditor
- * @param vi basic information about the auditor
- * @param compat protocol compatibility information
+ * @param[out] gf where to return the result
+ * @param check_sigs should we check signatures
+ * @param fee_obj json to parse
+ * @param key_data already parsed information about the exchange
+ * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
+ * invalid or the json malformed.
*/
-static void
-auditor_version_cb (
- void *cls,
- const struct TALER_AUDITOR_HttpResponse *hr,
- const struct TALER_AUDITOR_VersionInformation *vi,
- enum TALER_AUDITOR_VersionCompatibility compat)
+static enum GNUNET_GenericReturnValue
+parse_global_fee (struct TALER_EXCHANGE_GlobalFee *gf,
+ bool check_sigs,
+ const json_t *fee_obj,
+ const struct TALER_EXCHANGE_Keys *key_data)
{
- struct TEAH_AuditorListEntry *ale = cls;
-
- (void) hr;
- if (NULL == vi)
- {
- /* In this case, we don't mark the auditor as 'up' */
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Auditor `%s' gave unexpected version response.\n",
- ale->auditor_url);
- return;
- }
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("start_date",
+ &gf->start_date),
+ GNUNET_JSON_spec_timestamp ("end_date",
+ &gf->end_date),
+ GNUNET_JSON_spec_relative_time ("purse_timeout",
+ &gf->purse_timeout),
+ GNUNET_JSON_spec_relative_time ("history_expiration",
+ &gf->history_expiration),
+ GNUNET_JSON_spec_uint32 ("purse_account_limit",
+ &gf->purse_account_limit),
+ TALER_JSON_SPEC_GLOBAL_FEES (key_data->currency,
+ &gf->fees),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &gf->master_sig),
+ GNUNET_JSON_spec_end ()
+ };
- if (0 != (TALER_AUDITOR_VC_INCOMPATIBLE & compat))
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (fee_obj,
+ spec,
+ NULL, NULL))
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Auditor `%s' runs incompatible protocol version!\n",
- ale->auditor_url);
- if (0 != (TALER_AUDITOR_VC_OLDER & compat))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Auditor `%s' runs outdated protocol version!\n",
- ale->auditor_url);
- }
- if (0 != (TALER_AUDITOR_VC_NEWER & compat))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Auditor `%s' runs more recent incompatible version. We should upgrade!\n",
- ale->auditor_url);
- }
- return;
+ GNUNET_break_op (0);
+#if DEBUG
+ json_dumpf (fee_obj,
+ stderr,
+ JSON_INDENT (2));
+#endif
+ return GNUNET_SYSERR;
}
- ale->is_up = GNUNET_YES;
-}
-
-
-/**
- * Recalculate our auditor list, we got /keys and it may have
- * changed.
- *
- * @param exchange exchange for which to update the list.
- */
-static void
-update_auditors (struct TALER_EXCHANGE_Handle *exchange)
-{
- struct TALER_EXCHANGE_Keys *kd = &exchange->key_data;
-
- TALER_LOG_DEBUG ("Updating auditors\n");
- for (unsigned int i = 0; i<kd->num_auditors; i++)
+ if (check_sigs)
{
- /* Compare auditor data from /keys with auditor data
- * from owned exchange structures. */
- struct TALER_EXCHANGE_AuditorInformation *auditor = &kd->auditors[i];
- struct TEAH_AuditorListEntry *ale = NULL;
-
- for (struct TEAH_AuditorListEntry *a = exchange->auditors_head;
- NULL != a;
- a = a->next)
+ if (GNUNET_OK !=
+ TALER_exchange_offline_global_fee_verify (
+ gf->start_date,
+ gf->end_date,
+ &gf->fees,
+ gf->purse_timeout,
+ gf->history_expiration,
+ gf->purse_account_limit,
+ &key_data->master_pub,
+ &gf->master_sig))
{
- if (0 == GNUNET_memcmp (&auditor->auditor_pub,
- &a->auditor_pub))
- {
- ale = a;
- break;
- }
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
}
- if (NULL != ale)
- continue; /* found, no need to add */
-
- /* new auditor, add */
- TALER_LOG_DEBUG ("Found new auditor!\n");
- ale = GNUNET_new (struct TEAH_AuditorListEntry);
- ale->auditor_pub = auditor->auditor_pub;
- ale->auditor_url = GNUNET_strdup (auditor->auditor_url);
- GNUNET_CONTAINER_DLL_insert (exchange->auditors_head,
- exchange->auditors_tail,
- ale);
-
- ale->ah = TALER_AUDITOR_connect (exchange->ctx,
- ale->auditor_url,
- &auditor_version_cb,
- ale);
}
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_OK;
}
@@ -624,7 +728,7 @@ update_auditors (struct TALER_EXCHANGE_Handle *exchange)
* @param denom1 first denomination key
* @param denom2 second denomination key
* @return 0 if the two keys are equal (not necessarily
- * the same object), 1 otherwise.
+ * the same object), non-zero otherwise.
*/
static unsigned int
denoms_cmp (const struct TALER_EXCHANGE_DenomPublicKey *denom1,
@@ -669,28 +773,21 @@ decode_keys_json (const json_t *resp_obj,
struct TALER_EXCHANGE_Keys *key_data,
enum TALER_EXCHANGE_VersionCompatibility *vc)
{
- struct TALER_ExchangeSignatureP sig;
- struct GNUNET_HashContext *hash_context;
- struct TALER_ExchangePublicKeyP pub;
- const char *currency;
- struct GNUNET_JSON_Specification mspec[] = {
- GNUNET_JSON_spec_fixed_auto ("eddsa_sig",
- &sig),
- GNUNET_JSON_spec_fixed_auto ("eddsa_pub",
- &pub),
- GNUNET_JSON_spec_fixed_auto ("master_public_key",
- &key_data->master_pub),
- TALER_JSON_spec_absolute_time ("list_issue_date",
- &key_data->list_issue_date),
- TALER_JSON_spec_relative_time ("reserve_closing_delay",
- &key_data->reserve_closing_delay),
- GNUNET_JSON_spec_string ("currency",
- &currency),
- GNUNET_JSON_spec_mark_optional (
- TALER_JSON_spec_amount_any ("wallet_balance_limit_without_kyc",
- &key_data->wallet_balance_limit_without_kyc)),
- GNUNET_JSON_spec_end ()
- };
+ struct TALER_ExchangeSignatureP exchange_sig;
+ struct TALER_ExchangePublicKeyP exchange_pub;
+ const json_t *wblwk = NULL;
+ const json_t *global_fees;
+ const json_t *sign_keys_array;
+ const json_t *denominations_by_group;
+ const json_t *auditors_array;
+ const json_t *recoup_array = NULL;
+ const json_t *manifests = NULL;
+ bool no_extensions = false;
+ bool no_signature = false;
+ const json_t *accounts;
+ const json_t *fees;
+ const json_t *wads;
+ struct SignatureContext sig_ctx = { 0 };
if (JSON_OBJECT != json_typeof (resp_obj))
{
@@ -702,16 +799,12 @@ decode_keys_json (const json_t *resp_obj,
stderr,
JSON_INDENT (2));
#endif
- /* check the version */
+ /* check the version first */
{
- const char *ver;
- unsigned int age;
- unsigned int revision;
- unsigned int current;
- char dummy;
+ struct TALER_JSON_ProtocolVersion pv;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("version",
- &ver),
+ TALER_JSON_spec_version ("version",
+ &pv),
GNUNET_JSON_spec_end ()
};
@@ -723,151 +816,378 @@ decode_keys_json (const json_t *resp_obj,
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
- if (3 != sscanf (ver,
- "%u:%u:%u%c",
- &current,
- &revision,
- &age,
- &dummy))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
*vc = TALER_EXCHANGE_VC_MATCH;
- if (EXCHANGE_PROTOCOL_CURRENT < current)
+ if (EXCHANGE_PROTOCOL_CURRENT < pv.current)
{
*vc |= TALER_EXCHANGE_VC_NEWER;
- if (EXCHANGE_PROTOCOL_CURRENT < current - age)
+ if (EXCHANGE_PROTOCOL_CURRENT < pv.current - pv.age)
*vc |= TALER_EXCHANGE_VC_INCOMPATIBLE;
}
- if (EXCHANGE_PROTOCOL_CURRENT > current)
+ if (EXCHANGE_PROTOCOL_CURRENT > pv.current)
{
*vc |= TALER_EXCHANGE_VC_OLDER;
- if (EXCHANGE_PROTOCOL_CURRENT - EXCHANGE_PROTOCOL_AGE > current)
+ if (EXCHANGE_PROTOCOL_CURRENT - EXCHANGE_PROTOCOL_AGE > pv.current)
*vc |= TALER_EXCHANGE_VC_INCOMPATIBLE;
}
- key_data->version = GNUNET_strdup (ver);
}
- hash_context = NULL;
- EXITIF (GNUNET_OK !=
+ {
+ const char *ver;
+ const char *currency;
+ const char *asset_type;
+ struct GNUNET_JSON_Specification mspec[] = {
+ GNUNET_JSON_spec_fixed_auto (
+ "exchange_sig",
+ &exchange_sig),
+ GNUNET_JSON_spec_fixed_auto (
+ "exchange_pub",
+ &exchange_pub),
+ GNUNET_JSON_spec_fixed_auto (
+ "master_public_key",
+ &key_data->master_pub),
+ GNUNET_JSON_spec_array_const ("accounts",
+ &accounts),
+ GNUNET_JSON_spec_object_const ("wire_fees",
+ &fees),
+ GNUNET_JSON_spec_array_const ("wads",
+ &wads),
+ GNUNET_JSON_spec_timestamp (
+ "list_issue_date",
+ &key_data->list_issue_date),
+ GNUNET_JSON_spec_relative_time (
+ "reserve_closing_delay",
+ &key_data->reserve_closing_delay),
+ GNUNET_JSON_spec_string (
+ "currency",
+ &currency),
+ GNUNET_JSON_spec_string (
+ "asset_type",
+ &asset_type),
+ GNUNET_JSON_spec_array_const (
+ "global_fees",
+ &global_fees),
+ GNUNET_JSON_spec_array_const (
+ "signkeys",
+ &sign_keys_array),
+ GNUNET_JSON_spec_array_const (
+ "denominations",
+ &denominations_by_group),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const (
+ "recoup",
+ &recoup_array),
+ NULL),
+ GNUNET_JSON_spec_array_const (
+ "auditors",
+ &auditors_array),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool (
+ "rewards_allowed",
+ &key_data->rewards_allowed),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_object_const ("extensions",
+ &manifests),
+ &no_extensions),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_fixed_auto (
+ "extensions_sig",
+ &key_data->extensions_sig),
+ &no_signature),
+ GNUNET_JSON_spec_string ("version",
+ &ver),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const (
+ "wallet_balance_limit_without_kyc",
+ &wblwk),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+ const char *emsg;
+ unsigned int eline;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (resp_obj,
+ (check_sig) ? mspec : &mspec[2],
+ &emsg,
+ &eline))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Parsing /keys failed for `%s' (%u)\n",
+ emsg,
+ eline);
+ EXITIF (1);
+ }
+ {
+ struct GNUNET_JSON_Specification sspec[] = {
+ TALER_JSON_spec_currency_specification (
+ "currency_specification",
+ currency,
+ &key_data->cspec),
+ TALER_JSON_spec_amount (
+ "stefan_abs",
+ currency,
+ &key_data->stefan_abs),
+ TALER_JSON_spec_amount (
+ "stefan_log",
+ currency,
+ &key_data->stefan_log),
+ GNUNET_JSON_spec_double (
+ "stefan_lin",
+ &key_data->stefan_lin),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
GNUNET_JSON_parse (resp_obj,
- (check_sig) ? mspec : &mspec[2],
- NULL, NULL));
- key_data->currency = GNUNET_strdup (currency);
+ sspec,
+ &emsg,
+ &eline))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Parsing /keys failed for `%s' (%u)\n",
+ emsg,
+ eline);
+ EXITIF (1);
+ }
+ }
+
+ key_data->currency = GNUNET_strdup (currency);
+ key_data->version = GNUNET_strdup (ver);
+ key_data->asset_type = GNUNET_strdup (asset_type);
+ if (! no_extensions)
+ key_data->extensions = json_incref ((json_t *) manifests);
+ }
- if (GNUNET_OK ==
- TALER_amount_is_valid (&key_data->wallet_balance_limit_without_kyc))
+ /* parse the global fees */
+ EXITIF (json_array_size (global_fees) > UINT_MAX);
+ key_data->num_global_fees
+ = (unsigned int) json_array_size (global_fees);
+ if (0 != key_data->num_global_fees)
{
- if (0 != strcasecmp (currency,
- key_data->wallet_balance_limit_without_kyc.currency))
+ json_t *global_fee;
+ size_t index;
+
+ key_data->global_fees
+ = GNUNET_new_array (key_data->num_global_fees,
+ struct TALER_EXCHANGE_GlobalFee);
+ json_array_foreach (global_fees, index, global_fee)
{
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
+ EXITIF (GNUNET_SYSERR ==
+ parse_global_fee (&key_data->global_fees[index],
+ check_sig,
+ global_fee,
+ key_data));
}
}
- /* parse the master public key and issue date of the response */
- if (check_sig)
- hash_context = GNUNET_CRYPTO_hash_context_start ();
/* parse the signing keys */
+ EXITIF (json_array_size (sign_keys_array) > UINT_MAX);
+ key_data->num_sign_keys
+ = (unsigned int) json_array_size (sign_keys_array);
+ if (0 != key_data->num_sign_keys)
{
- json_t *sign_keys_array;
json_t *sign_key_obj;
- unsigned int index;
+ size_t index;
- EXITIF (NULL == (sign_keys_array =
- json_object_get (resp_obj,
- "signkeys")));
- EXITIF (! json_is_array (sign_keys_array));
- if (0 != (key_data->num_sign_keys =
- json_array_size (sign_keys_array)))
+ key_data->sign_keys
+ = GNUNET_new_array (key_data->num_sign_keys,
+ struct TALER_EXCHANGE_SigningPublicKey);
+ json_array_foreach (sign_keys_array, index, sign_key_obj) {
+ EXITIF (GNUNET_SYSERR ==
+ parse_json_signkey (&key_data->sign_keys[index],
+ check_sig,
+ sign_key_obj,
+ &key_data->master_pub));
+ }
+ }
+
+ /* Parse balance limits */
+ if (NULL != wblwk)
+ {
+ EXITIF (json_array_size (wblwk) > UINT_MAX);
+ key_data->wblwk_length
+ = (unsigned int) json_array_size (wblwk);
+ key_data->wallet_balance_limit_without_kyc
+ = GNUNET_new_array (key_data->wblwk_length,
+ struct TALER_Amount);
+ for (unsigned int i = 0; i<key_data->wblwk_length; i++)
{
- key_data->sign_keys
- = GNUNET_new_array (key_data->num_sign_keys,
- struct TALER_EXCHANGE_SigningPublicKey);
- json_array_foreach (sign_keys_array, index, sign_key_obj) {
- EXITIF (GNUNET_SYSERR ==
- parse_json_signkey (&key_data->sign_keys[index],
- check_sig,
- sign_key_obj,
- &key_data->master_pub));
- }
+ struct TALER_Amount *a = &key_data->wallet_balance_limit_without_kyc[i];
+ const json_t *aj = json_array_get (wblwk,
+ i);
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount (NULL,
+ key_data->currency,
+ a),
+ GNUNET_JSON_spec_end ()
+ };
+
+ EXITIF (GNUNET_OK !=
+ GNUNET_JSON_parse (aj,
+ spec,
+ NULL, NULL));
}
}
- /* parse the denomination keys, merging with the
- possibly EXISTING array as required (/keys cherry picking) */
+ /* Parse wire accounts */
+ key_data->fees = parse_fees (&key_data->master_pub,
+ key_data->currency,
+ fees,
+ &key_data->fees_len);
+ EXITIF (NULL == key_data->fees);
+ /* parse accounts */
+ EXITIF (json_array_size (accounts) > UINT_MAX);
+ GNUNET_array_grow (key_data->accounts,
+ key_data->accounts_len,
+ json_array_size (accounts));
+ EXITIF (GNUNET_OK !=
+ TALER_EXCHANGE_parse_accounts (&key_data->master_pub,
+ accounts,
+ key_data->accounts_len,
+ key_data->accounts));
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Parsed %u wire accounts from JSON\n",
+ key_data->accounts_len);
+
+
+ /* Parse the supported extension(s): age-restriction. */
+ /* TODO: maybe lift all this into a FP in TALER_Extension ? */
+ if (! no_extensions)
{
- json_t *denom_keys_array;
- json_t *denom_key_obj;
- unsigned int index;
+ if (no_signature)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "found extensions without signature\n");
+ }
+ else
+ {
+ /* We have an extensions object. Verify its signature. */
+ EXITIF (GNUNET_OK !=
+ TALER_extensions_verify_manifests_signature (
+ manifests,
+ &key_data->extensions_sig,
+ &key_data->master_pub));
+
+ /* Parse and set the the configuration of the extensions accordingly */
+ EXITIF (GNUNET_OK !=
+ TALER_extensions_load_manifests (manifests));
+ }
- EXITIF (NULL == (denom_keys_array =
- json_object_get (resp_obj,
- "denoms")));
- EXITIF (JSON_ARRAY != json_typeof (denom_keys_array));
+ /* Assuming we might have now a new value for age_mask, set it in key_data */
+ key_data->age_mask = TALER_extensions_get_age_restriction_mask ();
+ }
- json_array_foreach (denom_keys_array, index, denom_key_obj) {
- struct TALER_EXCHANGE_DenomPublicKey dk;
- bool found = false;
+ /*
+ * Parse the denomination keys, merging with the
+ * possibly EXISTING array as required (/keys cherry picking).
+ *
+ * The denominations are grouped by common values of
+ * {cipher, value, fee, age_mask}.
+ */
+ {
+ json_t *group_obj;
+ unsigned int group_idx;
- memset (&dk,
- 0,
- sizeof (dk));
- EXITIF (GNUNET_SYSERR ==
- parse_json_denomkey (&dk,
- check_sig,
- denom_key_obj,
- &key_data->master_pub,
- hash_context));
+ json_array_foreach (denominations_by_group,
+ group_idx,
+ group_obj)
+ {
+ /* First, parse { cipher, fees, value, age_mask, hash } of the current
+ group. */
+ struct TALER_DenominationGroup group = {0};
+ const json_t *denom_keys_array;
+ struct GNUNET_JSON_Specification group_spec[] = {
+ TALER_JSON_spec_denomination_group (NULL,
+ key_data->currency,
+ &group),
+ GNUNET_JSON_spec_array_const ("denoms",
+ &denom_keys_array),
+ GNUNET_JSON_spec_end ()
+ };
+ json_t *denom_key_obj;
+ unsigned int index;
- for (unsigned int j = 0;
- j<key_data->num_denom_keys;
- j++)
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_JSON_parse (group_obj,
+ group_spec,
+ NULL,
+ NULL));
+
+ /* Now, parse the individual denominations */
+ json_array_foreach (denom_keys_array,
+ index,
+ denom_key_obj)
{
- if (0 == denoms_cmp (&dk,
- &key_data->denom_keys[j]))
+ /* Set the common fields from the group for this particular
+ denomination. Required to make the validity check inside
+ parse_json_denomkey_partially pass */
+ struct TALER_EXCHANGE_DenomPublicKey dk = {
+ .value = group.value,
+ .fees = group.fees,
+ .key.age_mask = group.age_mask
+ };
+ bool found = false;
+
+ EXITIF (GNUNET_SYSERR ==
+ parse_json_denomkey_partially (&dk,
+ group.cipher,
+ check_sig,
+ denom_key_obj,
+ &key_data->master_pub,
+ group_idx,
+ index,
+ check_sig
+ ? &sig_ctx
+ : NULL));
+ for (unsigned int j = 0;
+ j<key_data->num_denom_keys;
+ j++)
{
- found = true;
- break;
+ if (0 == denoms_cmp (&dk,
+ &key_data->denom_keys[j]))
+ {
+ found = true;
+ break;
+ }
}
- }
- if (found)
- {
- /* 0:0:0 did not support /keys cherry picking */
- TALER_LOG_DEBUG ("Skipping denomination key: already know it\n");
- TALER_denom_pub_free (&dk.key);
- continue;
- }
- if (key_data->denom_keys_size == key_data->num_denom_keys)
- GNUNET_array_grow (key_data->denom_keys,
- key_data->denom_keys_size,
- key_data->denom_keys_size * 2 + 2);
- key_data->denom_keys[key_data->num_denom_keys++] = dk;
-
- /* Update "last_denom_issue_date" */
- TALER_LOG_DEBUG ("Adding denomination key that is valid_from %s\n",
- GNUNET_STRINGS_absolute_time_to_string (dk.valid_from));
- key_data->last_denom_issue_date
- = GNUNET_TIME_absolute_max (key_data->last_denom_issue_date,
- dk.valid_from);
- };
- }
+
+ if (found)
+ {
+ /* 0:0:0 did not support /keys cherry picking */
+ TALER_LOG_DEBUG ("Skipping denomination key: already know it\n");
+ TALER_denom_pub_free (&dk.key);
+ continue;
+ }
+
+ if (key_data->denom_keys_size == key_data->num_denom_keys)
+ GNUNET_array_grow (key_data->denom_keys,
+ key_data->denom_keys_size,
+ key_data->denom_keys_size * 2 + 2);
+ GNUNET_assert (key_data->denom_keys_size >
+ key_data->num_denom_keys);
+ GNUNET_assert (key_data->num_denom_keys < UINT_MAX);
+ key_data->denom_keys[key_data->num_denom_keys++] = dk;
+
+ /* Update "last_denom_issue_date" */
+ TALER_LOG_DEBUG ("Adding denomination key that is valid_until %s\n",
+ GNUNET_TIME_timestamp2s (dk.valid_from));
+ key_data->last_denom_issue_date
+ = GNUNET_TIME_timestamp_max (key_data->last_denom_issue_date,
+ dk.valid_from);
+ }; /* end of json_array_foreach over denominations */
+ } /* end of json_array_foreach over groups of denominations */
+ } /* end of scope for group_ojb/group_idx */
/* parse the auditor information */
{
- json_t *auditors_array;
json_t *auditor_info;
unsigned int index;
- EXITIF (NULL == (auditors_array =
- json_object_get (resp_obj,
- "auditors")));
- EXITIF (JSON_ARRAY != json_typeof (auditors_array));
-
/* Merge with the existing auditor information we have (/keys cherry picking) */
- json_array_foreach (auditors_array, index, auditor_info) {
+ json_array_foreach (auditors_array, index, auditor_info)
+ {
struct TALER_EXCHANGE_AuditorInformation ai;
bool found = false;
@@ -926,173 +1246,94 @@ decode_keys_json (const json_t *resp_obj,
GNUNET_array_grow (key_data->auditors,
key_data->auditors_size,
key_data->auditors_size * 2 + 2);
+ GNUNET_assert (key_data->auditors_size >
+ key_data->num_auditors);
GNUNET_assert (NULL != ai.auditor_url);
+ GNUNET_assert (key_data->num_auditors < UINT_MAX);
key_data->auditors[key_data->num_auditors++] = ai;
};
}
/* parse the revocation/recoup information */
+ if (NULL != recoup_array)
{
- json_t *recoup_array;
json_t *recoup_info;
unsigned int index;
- if (NULL != (recoup_array =
- json_object_get (resp_obj,
- "recoup")))
+ json_array_foreach (recoup_array, index, recoup_info)
{
- EXITIF (JSON_ARRAY != json_typeof (recoup_array));
-
- json_array_foreach (recoup_array, index, recoup_info) {
- struct TALER_DenominationHash h_denom_pub;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
- &h_denom_pub),
- GNUNET_JSON_spec_end ()
- };
+ struct TALER_DenominationHashP h_denom_pub;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
+ &h_denom_pub),
+ GNUNET_JSON_spec_end ()
+ };
- EXITIF (GNUNET_OK !=
- GNUNET_JSON_parse (recoup_info,
- spec,
- NULL, NULL));
- for (unsigned int j = 0;
- j<key_data->num_denom_keys;
- j++)
+ EXITIF (GNUNET_OK !=
+ GNUNET_JSON_parse (recoup_info,
+ spec,
+ NULL, NULL));
+ for (unsigned int j = 0;
+ j<key_data->num_denom_keys;
+ j++)
+ {
+ if (0 == GNUNET_memcmp (&h_denom_pub,
+ &key_data->denom_keys[j].h_key))
{
- if (0 == GNUNET_memcmp (&h_denom_pub,
- &key_data->denom_keys[j].h_key))
- {
- key_data->denom_keys[j].revoked = GNUNET_YES;
- break;
- }
+ key_data->denom_keys[j].revoked = true;
+ break;
}
- };
+ }
}
}
if (check_sig)
{
- struct TALER_ExchangeKeySetPS ks = {
- .purpose.size = htonl (sizeof (ks)),
- .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_KEY_SET),
- .list_issue_date = GNUNET_TIME_absolute_hton (key_data->list_issue_date)
- };
+ struct GNUNET_HashContext *hash_context;
+ struct GNUNET_HashCode hc;
+ 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_array_grow (sig_ctx.elements,
+ sig_ctx.elements_size,
+ 0);
GNUNET_CRYPTO_hash_context_finish (hash_context,
- &ks.hc);
- hash_context = NULL;
+ &hc);
EXITIF (GNUNET_OK !=
TALER_EXCHANGE_test_signing_key (key_data,
- &pub));
+ &exchange_pub));
EXITIF (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_KEY_SET,
- &ks,
- &sig.eddsa_signature,
- &pub.eddsa_pub));
+ TALER_exchange_online_key_set_verify (
+ key_data->list_issue_date,
+ &hc,
+ &exchange_pub,
+ &exchange_sig));
}
return GNUNET_OK;
-EXITIF_exit:
+EXITIF_exit:
*vc = TALER_EXCHANGE_VC_PROTOCOL_ERROR;
- if (NULL != hash_context)
- GNUNET_CRYPTO_hash_context_abort (hash_context);
return GNUNET_SYSERR;
}
/**
- * Free key data object.
- *
- * @param key_data data to free (pointer itself excluded)
- */
-static void
-free_key_data (struct TALER_EXCHANGE_Keys *key_data)
-{
- GNUNET_array_grow (key_data->sign_keys,
- key_data->num_sign_keys,
- 0);
- for (unsigned int i = 0; i<key_data->num_denom_keys; i++)
- TALER_denom_pub_free (&key_data->denom_keys[i].key);
-
- GNUNET_array_grow (key_data->denom_keys,
- key_data->denom_keys_size,
- 0);
- for (unsigned int i = 0; i<key_data->num_auditors; i++)
- {
- GNUNET_array_grow (key_data->auditors[i].denom_keys,
- key_data->auditors[i].num_denom_keys,
- 0);
- GNUNET_free (key_data->auditors[i].auditor_url);
- }
- GNUNET_array_grow (key_data->auditors,
- key_data->auditors_size,
- 0);
- GNUNET_free (key_data->version);
- key_data->version = NULL;
- GNUNET_free (key_data->currency);
- key_data->currency = NULL;
-}
-
-
-/**
- * Initiate download of /keys from the exchange.
- *
- * @param cls exchange where to download /keys from
- */
-static void
-request_keys (void *cls);
-
-
-/**
- * Let the user set the last valid denomination time manually.
- *
- * @param exchange the exchange handle.
- * @param last_denom_new new last denomination time.
- */
-void
-TALER_EXCHANGE_set_last_denom (struct TALER_EXCHANGE_Handle *exchange,
- struct GNUNET_TIME_Absolute last_denom_new)
-{
- exchange->key_data.last_denom_issue_date = last_denom_new;
-}
-
-
-/**
- * Check if our current response for /keys is valid, and if
- * not trigger download.
- *
- * @param exchange exchange to check keys for
- * @param flags options controlling when to download what
- * @return until when the response is current, 0 if we are re-downloading
- */
-struct GNUNET_TIME_Absolute
-TALER_EXCHANGE_check_keys_current (struct TALER_EXCHANGE_Handle *exchange,
- enum TALER_EXCHANGE_CheckKeysFlags flags)
-{
- bool force_download = 0 != (flags & TALER_EXCHANGE_CKF_FORCE_DOWNLOAD);
- bool pull_all_keys = 0 != (flags & TALER_EXCHANGE_CKF_PULL_ALL_KEYS);
-
- if (NULL != exchange->kr)
- return GNUNET_TIME_UNIT_ZERO_ABS;
-
- if (pull_all_keys)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Forcing re-download of all exchange keys\n");
- GNUNET_break (GNUNET_YES == force_download);
- exchange->state = MHS_INIT;
- }
- if ( (! force_download) &&
- (GNUNET_TIME_absolute_is_future (exchange->key_data_expiration)) )
- return exchange->key_data_expiration;
- if (NULL == exchange->retry_task)
- exchange->retry_task = GNUNET_SCHEDULER_add_now (&request_keys,
- exchange);
- return GNUNET_TIME_UNIT_ZERO_ABS;
-}
-
-
-/**
* Callback used when downloading the reply to a /keys request
* is complete.
*
@@ -1105,118 +1346,122 @@ keys_completed_cb (void *cls,
long response_code,
const void *resp_obj)
{
- struct KeysRequest *kr = cls;
- struct TALER_EXCHANGE_Handle *exchange = kr->exchange;
- struct TALER_EXCHANGE_Keys kd;
- struct TALER_EXCHANGE_Keys kd_old;
- enum TALER_EXCHANGE_VersionCompatibility vc;
+ struct TALER_EXCHANGE_GetKeysHandle *gkh = cls;
const json_t *j = resp_obj;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .reply = j,
- .http_status = (unsigned int) response_code
+ struct TALER_EXCHANGE_Keys *kd = NULL;
+ struct TALER_EXCHANGE_KeysResponse kresp = {
+ .hr.reply = j,
+ .hr.http_status = (unsigned int) response_code,
+ .details.ok.compat = TALER_EXCHANGE_VC_PROTOCOL_ERROR,
};
+ gkh->job = NULL;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Received keys from URL `%s' with status %ld.\n",
- kr->url,
- response_code);
- kd_old = exchange->key_data;
- memset (&kd,
- 0,
- sizeof (struct TALER_EXCHANGE_Keys));
- vc = TALER_EXCHANGE_VC_PROTOCOL_ERROR;
+ "Received keys from URL `%s' with status %ld and expiration %s.\n",
+ gkh->url,
+ response_code,
+ GNUNET_TIME_timestamp2s (gkh->expire));
+ if (GNUNET_TIME_absolute_is_past (gkh->expire.abs_time))
+ {
+ if (MHD_HTTP_OK == response_code)
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Exchange failed to give expiration time, assuming in %s\n",
+ GNUNET_TIME_relative2s (DEFAULT_EXPIRATION,
+ true));
+ gkh->expire
+ = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION));
+ }
switch (response_code)
{
case 0:
- free_keys_request (kr);
- exchange->keys_error_count++;
- exchange->kr = NULL;
- GNUNET_assert (NULL == exchange->retry_task);
- exchange->retry_delay = EXCHANGE_LIB_BACKOFF (exchange->retry_delay);
- exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay,
- &request_keys,
- exchange);
- return;
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to receive /keys response from exchange %s\n",
+ gkh->exchange_url);
+ break;
case MHD_HTTP_OK:
- exchange->keys_error_count = 0;
if (NULL == j)
{
+ GNUNET_break (0);
response_code = 0;
break;
}
- /* We keep the denomination keys and auditor signatures from the
- previous iteration (/keys cherry picking) */
- kd.num_denom_keys = kd_old.num_denom_keys;
- kd.last_denom_issue_date = kd_old.last_denom_issue_date;
- GNUNET_array_grow (kd.denom_keys,
- kd.denom_keys_size,
- kd.num_denom_keys);
-
- /* First make a shallow copy, we then need another pass for the RSA key... */
- memcpy (kd.denom_keys,
- kd_old.denom_keys,
- kd_old.num_denom_keys * sizeof (struct
- TALER_EXCHANGE_DenomPublicKey));
-
- for (unsigned int i = 0; i<kd_old.num_denom_keys; i++)
- TALER_denom_pub_deep_copy (&kd.denom_keys[i].key,
- &kd_old.denom_keys[i].key);
-
- kd.num_auditors = kd_old.num_auditors;
- kd.auditors = GNUNET_new_array (kd.num_auditors,
- struct TALER_EXCHANGE_AuditorInformation);
- /* Now the necessary deep copy... */
- for (unsigned int i = 0; i<kd_old.num_auditors; i++)
+ kd = GNUNET_new (struct TALER_EXCHANGE_Keys);
+ kd->exchange_url = GNUNET_strdup (gkh->exchange_url);
+ if (NULL != gkh->prev_keys)
{
- const struct TALER_EXCHANGE_AuditorInformation *aold =
- &kd_old.auditors[i];
- struct TALER_EXCHANGE_AuditorInformation *anew = &kd.auditors[i];
-
- anew->auditor_pub = aold->auditor_pub;
- GNUNET_assert (NULL != aold->auditor_url);
- anew->auditor_url = GNUNET_strdup (aold->auditor_url);
- GNUNET_array_grow (anew->denom_keys,
- anew->num_denom_keys,
- aold->num_denom_keys);
- memcpy (anew->denom_keys,
- aold->denom_keys,
- aold->num_denom_keys
- * sizeof (struct TALER_EXCHANGE_AuditorDenominationInfo));
- }
+ const struct TALER_EXCHANGE_Keys *kd_old = gkh->prev_keys;
+
+ /* We keep the denomination keys and auditor signatures from the
+ previous iteration (/keys cherry picking) */
+ kd->num_denom_keys
+ = kd_old->num_denom_keys;
+ kd->last_denom_issue_date
+ = kd_old->last_denom_issue_date;
+ GNUNET_array_grow (kd->denom_keys,
+ kd->denom_keys_size,
+ kd->num_denom_keys);
+ /* First make a shallow copy, we then need another pass for the RSA key... */
+ GNUNET_memcpy (kd->denom_keys,
+ kd_old->denom_keys,
+ kd_old->num_denom_keys
+ * sizeof (struct TALER_EXCHANGE_DenomPublicKey));
+ for (unsigned int i = 0; i<kd_old->num_denom_keys; i++)
+ TALER_denom_pub_copy (&kd->denom_keys[i].key,
+ &kd_old->denom_keys[i].key);
+ kd->num_auditors = kd_old->num_auditors;
+ kd->auditors = GNUNET_new_array (kd->num_auditors,
+ struct TALER_EXCHANGE_AuditorInformation);
+ /* Now the necessary deep copy... */
+ for (unsigned int i = 0; i<kd_old->num_auditors; i++)
+ {
+ const struct TALER_EXCHANGE_AuditorInformation *aold =
+ &kd_old->auditors[i];
+ struct TALER_EXCHANGE_AuditorInformation *anew = &kd->auditors[i];
- /* Old auditors got just copied into new ones. */
+ anew->auditor_pub = aold->auditor_pub;
+ anew->auditor_url = GNUNET_strdup (aold->auditor_url);
+ GNUNET_array_grow (anew->denom_keys,
+ anew->num_denom_keys,
+ aold->num_denom_keys);
+ GNUNET_memcpy (
+ anew->denom_keys,
+ aold->denom_keys,
+ aold->num_denom_keys
+ * sizeof (struct TALER_EXCHANGE_AuditorDenominationInfo));
+ }
+ }
+ /* Now decode fresh /keys response */
if (GNUNET_OK !=
decode_keys_json (j,
true,
- &kd,
- &vc))
+ kd,
+ &kresp.details.ok.compat))
{
TALER_LOG_ERROR ("Could not decode /keys response\n");
- hr.http_status = 0;
- hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
- for (unsigned int i = 0; i<kd.num_auditors; i++)
- {
- struct TALER_EXCHANGE_AuditorInformation *anew = &kd.auditors[i];
-
- GNUNET_array_grow (anew->denom_keys,
- anew->num_denom_keys,
- 0);
- GNUNET_free (anew->auditor_url);
- }
- GNUNET_free (kd.auditors);
- kd.auditors = NULL;
- kd.num_auditors = 0;
- for (unsigned int i = 0; i<kd_old.num_denom_keys; i++)
- TALER_denom_pub_free (&kd.denom_keys[i].key);
- GNUNET_array_grow (kd.denom_keys,
- kd.denom_keys_size,
- 0);
- kd.num_denom_keys = 0;
+ kd->rc = 1;
+ TALER_EXCHANGE_keys_decref (kd);
+ kd = NULL;
+ kresp.hr.http_status = 0;
+ kresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
- json_decref (exchange->key_data_raw);
- exchange->key_data_raw = json_deep_copy (j);
- exchange->retry_delay = GNUNET_TIME_UNIT_ZERO;
+ kd->rc = 1;
+ kd->key_data_expiration = gkh->expire;
+ if (GNUNET_TIME_relative_cmp (
+ GNUNET_TIME_absolute_get_remaining (gkh->expire.abs_time),
+ <,
+ MINIMUM_EXPIRATION))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Exchange returned keys with expiration time below %s. Compensating.\n",
+ GNUNET_TIME_relative2s (MINIMUM_EXPIRATION,
+ true));
+ kd->key_data_expiration
+ = GNUNET_TIME_relative_to_timestamp (MINIMUM_EXPIRATION);
+ }
+
+ kresp.details.ok.keys = kd;
break;
case MHD_HTTP_BAD_REQUEST:
case MHD_HTTP_UNAUTHORIZED:
@@ -1224,121 +1469,36 @@ keys_completed_cb (void *cls,
case MHD_HTTP_NOT_FOUND:
if (NULL == j)
{
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- hr.hint = TALER_ErrorCode_get_hint (hr.ec);
+ kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec);
}
else
{
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ kresp.hr.ec = TALER_JSON_get_error_code (j);
+ kresp.hr.hint = TALER_JSON_get_error_hint (j);
}
break;
default:
- if (MHD_HTTP_GATEWAY_TIMEOUT == response_code)
- exchange->keys_error_count++;
if (NULL == j)
{
- hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- hr.hint = TALER_ErrorCode_get_hint (hr.ec);
+ kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec);
}
else
{
- hr.ec = TALER_JSON_get_error_code (j);
- hr.hint = TALER_JSON_get_error_hint (j);
+ kresp.hr.ec = TALER_JSON_get_error_code (j);
+ kresp.hr.hint = TALER_JSON_get_error_hint (j);
}
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d\n",
(unsigned int) response_code,
- (int) hr.ec);
+ (int) kresp.hr.ec);
break;
}
- exchange->key_data = kd;
- TALER_LOG_DEBUG ("Last DK issue date update to: %s\n",
- GNUNET_STRINGS_absolute_time_to_string
- (exchange->key_data.last_denom_issue_date));
-
-
- if (MHD_HTTP_OK != response_code)
- {
- exchange->kr = NULL;
- free_keys_request (kr);
- exchange->state = MHS_FAILED;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Exchange keys download failed\n");
- if (NULL != exchange->key_data_raw)
- {
- json_decref (exchange->key_data_raw);
- exchange->key_data_raw = NULL;
- }
- free_key_data (&kd_old);
- /* notify application that we failed */
- exchange->cert_cb (exchange->cert_cb_cls,
- &hr,
- NULL,
- vc);
- return;
- }
-
- exchange->kr = NULL;
- exchange->key_data_expiration = kr->expire;
- free_keys_request (kr);
- exchange->state = MHS_CERT;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Successfully downloaded exchange's keys\n");
- update_auditors (exchange);
- /* notify application about the key information */
- exchange->cert_cb (exchange->cert_cb_cls,
- &hr,
- &exchange->key_data,
- vc);
- free_key_data (&kd_old);
-}
-
-
-/* ********************* library internal API ********* */
-
-
-/**
- * Get the context of a exchange.
- *
- * @param h the exchange handle to query
- * @return ctx context to execute jobs in
- */
-struct GNUNET_CURL_Context *
-TEAH_handle_to_context (struct TALER_EXCHANGE_Handle *h)
-{
- return h->ctx;
-}
-
-
-/**
- * Check if the handle is ready to process requests.
- *
- * @param h the exchange handle to query
- * @return #GNUNET_YES if we are ready, #GNUNET_NO if not
- */
-enum GNUNET_GenericReturnValue
-TEAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h)
-{
- return (MHS_CERT == h->state) ? GNUNET_YES : GNUNET_NO;
-}
-
-
-/**
- * Obtain the URL to use for an API request.
- *
- * @param h handle for the exchange
- * @param path Taler API path (i.e. "/reserve/withdraw")
- * @return the full URL to use with cURL
- */
-char *
-TEAH_path_to_url (struct TALER_EXCHANGE_Handle *h,
- const char *path)
-{
- GNUNET_assert ('/' == path[0]);
- return TALER_url_join (h->url,
- path + 1,
- NULL);
+ gkh->cert_cb (gkh->cert_cb_cls,
+ &kresp,
+ kd);
+ TALER_EXCHANGE_get_keys_cancel (gkh);
}
@@ -1352,12 +1512,12 @@ TEAH_path_to_url (struct TALER_EXCHANGE_Handle *h,
* Parse HTTP timestamp.
*
* @param dateline header to parse header
- * @param at where to write the result
+ * @param[out] at where to write the result
* @return #GNUNET_OK on success
*/
static enum GNUNET_GenericReturnValue
parse_date_string (const char *dateline,
- struct GNUNET_TIME_Absolute *at)
+ struct GNUNET_TIME_Timestamp *at)
{
static const char *MONTHS[] =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
@@ -1439,7 +1599,7 @@ parse_date_string (const char *dateline,
}
if (t < 0)
t = 0; /* can happen due to timezone issues if date was 1.1.1970 */
- at->abs_value_us = 1000LL * 1000LL * t;
+ *at = GNUNET_TIME_timestamp_from_s (t);
return GNUNET_OK;
}
@@ -1452,7 +1612,7 @@ parse_date_string (const char *dateline,
* @param buffer header data received
* @param size size of an item in @a buffer
* @param nitems number of items in @a buffer
- * @param userdata the `struct KeysRequest`
+ * @param userdata the `struct TALER_EXCHANGE_GetKeysHandle`
* @return `size * nitems` on success (everything else aborts)
*/
static size_t
@@ -1461,7 +1621,7 @@ header_cb (char *buffer,
size_t nitems,
void *userdata)
{
- struct KeysRequest *kr = userdata;
+ struct TALER_EXCHANGE_GetKeysHandle *kr = userdata;
size_t total = size * nitems;
char *val;
@@ -1473,6 +1633,10 @@ header_cb (char *buffer,
return total;
val = GNUNET_strndup (&buffer[strlen (MHD_HTTP_HEADER_EXPIRES ": ")],
total - strlen (MHD_HTTP_HEADER_EXPIRES ": "));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Found %s header `%s'\n",
+ MHD_HTTP_HEADER_EXPIRES,
+ val);
if (GNUNET_OK !=
parse_date_string (val,
&kr->expire))
@@ -1481,205 +1645,579 @@ header_cb (char *buffer,
"Failed to parse %s-header `%s'\n",
MHD_HTTP_HEADER_EXPIRES,
val);
- kr->expire = GNUNET_TIME_UNIT_ZERO_ABS;
+ kr->expire = GNUNET_TIME_UNIT_ZERO_TS;
}
GNUNET_free (val);
return total;
}
-/* ********************* public API ******************* */
+struct TALER_EXCHANGE_GetKeysHandle *
+TALER_EXCHANGE_get_keys (
+ struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ struct TALER_EXCHANGE_Keys *last_keys,
+ TALER_EXCHANGE_GetKeysCallback cert_cb,
+ void *cert_cb_cls)
+{
+ struct TALER_EXCHANGE_GetKeysHandle *gkh;
+ CURL *eh;
+ char last_date[80] = { 0 };
+
+ TALER_LOG_DEBUG ("Connecting to the exchange (%s)\n",
+ url);
+ gkh = GNUNET_new (struct TALER_EXCHANGE_GetKeysHandle);
+ gkh->exchange_url = GNUNET_strdup (url);
+ gkh->cert_cb = cert_cb;
+ gkh->cert_cb_cls = cert_cb_cls;
+ if (NULL != last_keys)
+ {
+ gkh->prev_keys = TALER_EXCHANGE_keys_incref (last_keys);
+ TALER_LOG_DEBUG ("Last DK issue date (before GETting /keys): %s\n",
+ GNUNET_TIME_timestamp2s (
+ last_keys->last_denom_issue_date));
+ GNUNET_snprintf (last_date,
+ sizeof (last_date),
+ "%llu",
+ (unsigned long long)
+ last_keys->last_denom_issue_date.abs_time.abs_value_us
+ / 1000000LLU);
+ }
+ gkh->url = TALER_url_join (url,
+ "keys",
+ (NULL != last_keys)
+ ? "last_issue_date"
+ : NULL,
+ (NULL != last_keys)
+ ? last_date
+ : NULL,
+ NULL);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Requesting keys with URL `%s'.\n",
+ gkh->url);
+ eh = TALER_EXCHANGE_curl_easy_get_ (gkh->url);
+ if (NULL == eh)
+ {
+ GNUNET_break (0);
+ GNUNET_free (gkh->exchange_url);
+ GNUNET_free (gkh->url);
+ GNUNET_free (gkh);
+ return NULL;
+ }
+ GNUNET_break (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_VERBOSE,
+ 1));
+ GNUNET_break (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_TIMEOUT,
+ 120 /* seconds */));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_HEADERFUNCTION,
+ &header_cb));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_HEADERDATA,
+ gkh));
+ gkh->job = GNUNET_CURL_job_add_with_ct_json (ctx,
+ eh,
+ &keys_completed_cb,
+ gkh);
+ return gkh;
+}
-/**
- * Deserialize the key data and use it to bootstrap @a exchange to
- * more efficiently recover the state. Errors in @a data must be
- * tolerated (i.e. by re-downloading instead).
- *
- * @param exchange which exchange's key and wire data should be deserialized
- * @param data the data to deserialize
- */
-static void
-deserialize_data (struct TALER_EXCHANGE_Handle *exchange,
- const json_t *data)
+void
+TALER_EXCHANGE_get_keys_cancel (
+ struct TALER_EXCHANGE_GetKeysHandle *gkh)
{
- enum TALER_EXCHANGE_VersionCompatibility vc;
- json_t *keys;
+ if (NULL != gkh->job)
+ {
+ GNUNET_CURL_job_cancel (gkh->job);
+ gkh->job = NULL;
+ }
+ TALER_EXCHANGE_keys_decref (gkh->prev_keys);
+ GNUNET_free (gkh->exchange_url);
+ GNUNET_free (gkh->url);
+ GNUNET_free (gkh);
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_EXCHANGE_test_signing_key (
+ const struct TALER_EXCHANGE_Keys *keys,
+ const struct TALER_ExchangePublicKeyP *pub)
+{
+ struct GNUNET_TIME_Absolute now;
+
+ /* we will check using a tolerance of 1h for the time */
+ now = GNUNET_TIME_absolute_get ();
+ for (unsigned int i = 0; i<keys->num_sign_keys; i++)
+ if ( (GNUNET_TIME_absolute_cmp (
+ keys->sign_keys[i].valid_from.abs_time,
+ <=,
+ GNUNET_TIME_absolute_add (now,
+ LIFETIME_TOLERANCE))) &&
+ (GNUNET_TIME_absolute_cmp (
+ keys->sign_keys[i].valid_until.abs_time,
+ >,
+ GNUNET_TIME_absolute_subtract (now,
+ LIFETIME_TOLERANCE))) &&
+ (0 == GNUNET_memcmp (pub,
+ &keys->sign_keys[i].key)) )
+ return GNUNET_OK;
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Signing key not valid at time %s\n",
+ GNUNET_TIME_absolute2s (now));
+ return GNUNET_SYSERR;
+}
+
+
+const struct TALER_EXCHANGE_DenomPublicKey *
+TALER_EXCHANGE_get_denomination_key (
+ const struct TALER_EXCHANGE_Keys *keys,
+ const struct TALER_DenominationPublicKey *pk)
+{
+ for (unsigned int i = 0; i<keys->num_denom_keys; i++)
+ if (0 ==
+ TALER_denom_pub_cmp (pk,
+ &keys->denom_keys[i].key))
+ return &keys->denom_keys[i];
+ return NULL;
+}
+
+
+const struct TALER_EXCHANGE_GlobalFee *
+TALER_EXCHANGE_get_global_fee (
+ const struct TALER_EXCHANGE_Keys *keys,
+ struct GNUNET_TIME_Timestamp ts)
+{
+ for (unsigned int i = 0; i<keys->num_global_fees; i++)
+ {
+ const struct TALER_EXCHANGE_GlobalFee *gf = &keys->global_fees[i];
+
+ if (GNUNET_TIME_timestamp_cmp (ts,
+ >=,
+ gf->start_date) &&
+ GNUNET_TIME_timestamp_cmp (ts,
+ <,
+ gf->end_date))
+ return gf;
+ }
+ return NULL;
+}
+
+
+struct TALER_EXCHANGE_DenomPublicKey *
+TALER_EXCHANGE_copy_denomination_key (
+ const struct TALER_EXCHANGE_DenomPublicKey *key)
+{
+ struct TALER_EXCHANGE_DenomPublicKey *copy;
+
+ copy = GNUNET_new (struct TALER_EXCHANGE_DenomPublicKey);
+ *copy = *key;
+ TALER_denom_pub_copy (&copy->key,
+ &key->key);
+ return copy;
+}
+
+
+void
+TALER_EXCHANGE_destroy_denomination_key (
+ struct TALER_EXCHANGE_DenomPublicKey *key)
+{
+ TALER_denom_pub_free (&key->key);
+ GNUNET_free (key);
+}
+
+
+const struct TALER_EXCHANGE_DenomPublicKey *
+TALER_EXCHANGE_get_denomination_key_by_hash (
+ const struct TALER_EXCHANGE_Keys *keys,
+ const struct TALER_DenominationHashP *hc)
+{
+ for (unsigned int i = 0; i<keys->num_denom_keys; i++)
+ if (0 == GNUNET_memcmp (hc,
+ &keys->denom_keys[i].h_key))
+ return &keys->denom_keys[i];
+ return NULL;
+}
+
+
+struct TALER_EXCHANGE_Keys *
+TALER_EXCHANGE_keys_incref (struct TALER_EXCHANGE_Keys *keys)
+{
+ GNUNET_assert (keys->rc < UINT_MAX);
+ keys->rc++;
+ return keys;
+}
+
+
+void
+TALER_EXCHANGE_keys_decref (struct TALER_EXCHANGE_Keys *keys)
+{
+ if (NULL == keys)
+ return;
+ GNUNET_assert (0 < keys->rc);
+ keys->rc--;
+ if (0 != keys->rc)
+ return;
+ GNUNET_array_grow (keys->sign_keys,
+ keys->num_sign_keys,
+ 0);
+ for (unsigned int i = 0; i<keys->num_denom_keys; i++)
+ TALER_denom_pub_free (&keys->denom_keys[i].key);
+
+ GNUNET_array_grow (keys->denom_keys,
+ keys->denom_keys_size,
+ 0);
+ for (unsigned int i = 0; i<keys->num_auditors; i++)
+ {
+ GNUNET_array_grow (keys->auditors[i].denom_keys,
+ keys->auditors[i].num_denom_keys,
+ 0);
+ GNUNET_free (keys->auditors[i].auditor_url);
+ }
+ GNUNET_array_grow (keys->auditors,
+ keys->auditors_size,
+ 0);
+ TALER_EXCHANGE_free_accounts (keys->accounts_len,
+ keys->accounts);
+ GNUNET_array_grow (keys->accounts,
+ keys->accounts_len,
+ 0);
+ free_fees (keys->fees,
+ keys->fees_len);
+ json_decref (keys->extensions);
+ GNUNET_free (keys->cspec.name);
+ json_decref (keys->cspec.map_alt_unit_names);
+ GNUNET_free (keys->wallet_balance_limit_without_kyc);
+ GNUNET_free (keys->version);
+ GNUNET_free (keys->currency);
+ GNUNET_free (keys->asset_type);
+ GNUNET_free (keys->global_fees);
+ GNUNET_free (keys->exchange_url);
+ GNUNET_free (keys);
+}
+
+
+struct TALER_EXCHANGE_Keys *
+TALER_EXCHANGE_keys_from_json (const json_t *j)
+{
+ const json_t *jkeys;
const char *url;
uint32_t version;
- struct GNUNET_TIME_Absolute expire;
+ struct GNUNET_TIME_Timestamp expire
+ = GNUNET_TIME_UNIT_ZERO_TS;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint32 ("version",
&version),
- GNUNET_JSON_spec_json ("keys",
- &keys),
- GNUNET_JSON_spec_string ("exchange_url",
+ GNUNET_JSON_spec_object_const ("keys",
+ &jkeys),
+ TALER_JSON_spec_web_url ("exchange_url",
&url),
- TALER_JSON_spec_absolute_time ("expire",
- &expire),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_timestamp ("expire",
+ &expire),
+ NULL),
GNUNET_JSON_spec_end ()
};
- struct TALER_EXCHANGE_Keys key_data;
- struct TALER_EXCHANGE_HttpResponse hr = {
- .ec = TALER_EC_NONE,
- .http_status = MHD_HTTP_OK,
- .reply = data
- };
+ struct TALER_EXCHANGE_Keys *keys;
+ enum TALER_EXCHANGE_VersionCompatibility compat;
- if (NULL == data)
- return;
+ if (NULL == j)
+ return NULL;
if (GNUNET_OK !=
- GNUNET_JSON_parse (data,
+ GNUNET_JSON_parse (j,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
- return;
+ return NULL;
}
if (0 != version)
{
- GNUNET_JSON_parse_free (spec);
- return; /* unsupported version */
- }
- if (0 != strcmp (url,
- exchange->url))
- {
- GNUNET_break (0);
- GNUNET_JSON_parse_free (spec);
- return;
+ return NULL; /* unsupported version */
}
- memset (&key_data,
- 0,
- sizeof (struct TALER_EXCHANGE_Keys));
+ keys = GNUNET_new (struct TALER_EXCHANGE_Keys);
if (GNUNET_OK !=
- decode_keys_json (keys,
+ decode_keys_json (jkeys,
false,
- &key_data,
- &vc))
+ keys,
+ &compat))
{
GNUNET_break (0);
- GNUNET_JSON_parse_free (spec);
- return;
+ return NULL;
}
- /* decode successful, initialize with the result */
- GNUNET_assert (NULL == exchange->key_data_raw);
- exchange->key_data_raw = json_deep_copy (keys);
- exchange->key_data = key_data;
- exchange->key_data_expiration = expire;
- exchange->state = MHS_CERT;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Successfully loaded exchange's keys via deserialization\n");
- update_auditors (exchange);
- /* notify application about the key information */
- exchange->cert_cb (exchange->cert_cb_cls,
- &hr,
- &exchange->key_data,
- vc);
- GNUNET_JSON_parse_free (spec);
+ keys->rc = 1;
+ keys->key_data_expiration = expire;
+ keys->exchange_url = GNUNET_strdup (url);
+ return keys;
}
/**
- * Serialize the latest key data from @a
- * exchange to be persisted on disk (to be used with
- * #TALER_EXCHANGE_OPTION_DATA to more efficiently recover
- * the state).
+ * Data we track per denomination group.
+ */
+struct GroupData
+{
+ /**
+ * The json blob with the group meta-data and list of denominations
+ */
+ json_t *json;
+
+ /**
+ * Meta data for this group.
+ */
+ struct TALER_DenominationGroup meta;
+};
+
+
+/**
+ * Add denomination group represented by @a value
+ * to list of denominations in @a cls. Also frees
+ * the @a value.
*
- * @param exchange which exchange's key and wire data should be
- * serialized
- * @return NULL on error (i.e. no current data available);
- * otherwise JSON object owned by the caller
+ * @param[in,out] cls a `json_t *` with an array to build
+ * @param key unused
+ * @param value a `struct GroupData *`
+ * @return #GNUNET_OK (continue to iterate)
*/
+static enum GNUNET_GenericReturnValue
+add_grp (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *value)
+{
+ json_t *denominations_by_group = cls;
+ struct GroupData *gd = value;
+ const char *cipher;
+ json_t *ge;
+ bool age_restricted = gd->meta.age_mask.bits != 0;
+
+ (void) key;
+ switch (gd->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);
+ }
+
+ ge = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("cipher",
+ cipher),
+ GNUNET_JSON_pack_array_steal ("denoms",
+ gd->json),
+ TALER_JSON_PACK_DENOM_FEES ("fee",
+ &gd->meta.fees),
+ GNUNET_JSON_pack_allow_null (
+ age_restricted
+ ? GNUNET_JSON_pack_uint64 ("age_mask",
+ gd->meta.age_mask.bits)
+ : GNUNET_JSON_pack_string ("dummy",
+ NULL)),
+ TALER_JSON_pack_amount ("value",
+ &gd->meta.value));
+ GNUNET_assert (0 ==
+ json_array_append_new (denominations_by_group,
+ ge));
+ GNUNET_free (gd);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Convert array of account restrictions @a ars to JSON.
+ *
+ * @param ar_len length of @a ars
+ * @param ars account restrictions to convert
+ * @return JSON representation
+ */
+static json_t *
+ar_to_json (unsigned int ar_len,
+ const struct TALER_EXCHANGE_AccountRestriction ars[static ar_len])
+{
+ json_t *rval;
+
+ rval = json_array ();
+ GNUNET_assert (NULL != rval);
+ for (unsigned int i = 0; i<ar_len; i++)
+ {
+ const struct TALER_EXCHANGE_AccountRestriction *ar = &ars[i];
+
+ switch (ar->type)
+ {
+ case TALER_EXCHANGE_AR_INVALID:
+ GNUNET_break (0);
+ json_decref (rval);
+ return NULL;
+ case TALER_EXCHANGE_AR_DENY:
+ GNUNET_assert (
+ 0 ==
+ json_array_append_new (
+ rval,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("type",
+ "deny"))));
+ break;
+ case TALER_EXCHANGE_AR_REGEX:
+ GNUNET_assert (
+ 0 ==
+ json_array_append_new (
+ rval,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string (
+ "type",
+ "regex"),
+ GNUNET_JSON_pack_string (
+ "payto_regex",
+ ar->details.regex.posix_egrep),
+ GNUNET_JSON_pack_string (
+ "human_hint",
+ ar->details.regex.human_hint),
+ GNUNET_JSON_pack_object_incref (
+ "human_hint_i18n",
+ (json_t *) ar->details.regex.human_hint_i18n)
+ )));
+ break;
+ }
+ }
+ return rval;
+}
+
+
json_t *
-TALER_EXCHANGE_serialize_data (struct TALER_EXCHANGE_Handle *exchange)
+TALER_EXCHANGE_keys_to_json (const struct TALER_EXCHANGE_Keys *kd)
{
- const struct TALER_EXCHANGE_Keys *kd = &exchange->key_data;
- struct GNUNET_TIME_Absolute now;
+ struct GNUNET_TIME_Timestamp now;
json_t *keys;
json_t *signkeys;
- json_t *denoms;
+ json_t *denominations_by_group;
json_t *auditors;
+ json_t *recoup;
+ json_t *wire_fees;
+ json_t *accounts;
+ json_t *global_fees;
+ json_t *wblwk = NULL;
- now = GNUNET_TIME_absolute_get ();
+ now = GNUNET_TIME_timestamp_get ();
signkeys = json_array ();
- if (NULL == signkeys)
- {
- GNUNET_break (0);
- return NULL;
- }
+ GNUNET_assert (NULL != signkeys);
for (unsigned int i = 0; i<kd->num_sign_keys; i++)
{
const struct TALER_EXCHANGE_SigningPublicKey *sk = &kd->sign_keys[i];
json_t *signkey;
- if (now.abs_value_us > sk->valid_until.abs_value_us)
+ if (GNUNET_TIME_timestamp_cmp (now,
+ >,
+ sk->valid_until))
continue; /* skip keys that have expired */
signkey = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("key",
&sk->key),
GNUNET_JSON_pack_data_auto ("master_sig",
&sk->master_sig),
- GNUNET_JSON_pack_time_abs ("stamp_start",
- sk->valid_from),
- GNUNET_JSON_pack_time_abs ("stamp_expire",
- sk->valid_until),
- GNUNET_JSON_pack_time_abs ("stamp_end",
- sk->valid_legal));
- if (NULL == signkey)
- {
- GNUNET_break (0);
- continue;
- }
- if (0 != json_array_append_new (signkeys,
- signkey))
- {
- GNUNET_break (0);
- json_decref (signkey);
- json_decref (signkeys);
- return NULL;
- }
- }
- denoms = json_array ();
- if (NULL == denoms)
- {
- GNUNET_break (0);
- json_decref (signkeys);
- return NULL;
+ GNUNET_JSON_pack_timestamp ("stamp_start",
+ sk->valid_from),
+ GNUNET_JSON_pack_timestamp ("stamp_expire",
+ sk->valid_until),
+ GNUNET_JSON_pack_timestamp ("stamp_end",
+ sk->valid_legal));
+ GNUNET_assert (NULL != signkey);
+ GNUNET_assert (0 ==
+ json_array_append_new (signkeys,
+ signkey));
}
- for (unsigned int i = 0; i<kd->num_denom_keys; i++)
+
+ denominations_by_group = json_array ();
+ GNUNET_assert (NULL != denominations_by_group);
{
- const struct TALER_EXCHANGE_DenomPublicKey *dk = &kd->denom_keys[i];
- json_t *denom;
+ struct GNUNET_CONTAINER_MultiHashMap *dbg;
- if (now.abs_value_us > dk->expire_deposit.abs_value_us)
- continue; /* skip keys that have expired */
- denom = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_time_abs ("stamp_expire_deposit",
- dk->expire_deposit),
- GNUNET_JSON_pack_time_abs ("stamp_expire_withdraw",
- dk->withdraw_valid_until),
- GNUNET_JSON_pack_time_abs ("stamp_start",
- dk->valid_from),
- GNUNET_JSON_pack_time_abs ("stamp_expire_legal",
- dk->expire_legal),
- TALER_JSON_pack_amount ("value",
- &dk->value),
- TALER_JSON_pack_amount ("fee_withdraw",
- &dk->fee_withdraw),
- TALER_JSON_pack_amount ("fee_deposit",
- &dk->fee_deposit),
- TALER_JSON_pack_amount ("fee_refresh",
- &dk->fee_refresh),
- TALER_JSON_pack_amount ("fee_refund",
- &dk->fee_refund),
- GNUNET_JSON_pack_data_auto ("master_sig",
- &dk->master_sig),
- TALER_JSON_pack_denom_pub ("denom_pub",
- &dk->key));
- GNUNET_assert (0 ==
- json_array_append_new (denoms,
- denom));
+ dbg = GNUNET_CONTAINER_multihashmap_create (128,
+ false);
+ for (unsigned int i = 0; i<kd->num_denom_keys; i++)
+ {
+ const struct TALER_EXCHANGE_DenomPublicKey *dk = &kd->denom_keys[i];
+ struct TALER_DenominationGroup meta = {
+ .cipher = dk->key.bsign_pub_key->cipher,
+ .value = dk->value,
+ .fees = dk->fees,
+ .age_mask = dk->key.age_mask
+ };
+ struct GNUNET_HashCode key;
+ struct GroupData *gd;
+ json_t *denom;
+ struct GNUNET_JSON_PackSpec key_spec;
+
+ if (GNUNET_TIME_timestamp_cmp (now,
+ >,
+ dk->expire_deposit))
+ continue; /* skip keys that have expired */
+ TALER_denomination_group_get_key (&meta,
+ &key);
+ gd = GNUNET_CONTAINER_multihashmap_get (dbg,
+ &key);
+ if (NULL == gd)
+ {
+ gd = GNUNET_new (struct GroupData);
+ gd->meta = meta;
+ gd->json = json_array ();
+ GNUNET_assert (NULL != gd->json);
+ GNUNET_assert (
+ GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (dbg,
+ &key,
+ gd,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+
+ }
+ switch (meta.cipher)
+ {
+ case GNUNET_CRYPTO_BSA_RSA:
+ key_spec =
+ GNUNET_JSON_pack_rsa_public_key (
+ "rsa_pub",
+ dk->key.bsign_pub_key->details.rsa_public_key);
+ break;
+ case GNUNET_CRYPTO_BSA_CS:
+ key_spec =
+ GNUNET_JSON_pack_data_varsize (
+ "cs_pub",
+ &dk->key.bsign_pub_key->details.cs_public_key,
+ sizeof (dk->key.bsign_pub_key->details.cs_public_key));
+ break;
+ default:
+ GNUNET_assert (false);
+ }
+ denom = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_timestamp ("stamp_expire_deposit",
+ dk->expire_deposit),
+ GNUNET_JSON_pack_timestamp ("stamp_expire_withdraw",
+ dk->withdraw_valid_until),
+ GNUNET_JSON_pack_timestamp ("stamp_start",
+ dk->valid_from),
+ GNUNET_JSON_pack_timestamp ("stamp_expire_legal",
+ dk->expire_legal),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &dk->master_sig),
+ key_spec
+ );
+ GNUNET_assert (0 ==
+ json_array_append_new (gd->json,
+ denom));
+ }
+ GNUNET_CONTAINER_multihashmap_iterate (dbg,
+ &add_grp,
+ denominations_by_group);
+ GNUNET_CONTAINER_multihashmap_destroy (dbg);
}
+
auditors = json_array ();
GNUNET_assert (NULL != auditors);
for (unsigned int i = 0; i<kd->num_auditors; i++)
@@ -1689,14 +2227,7 @@ TALER_EXCHANGE_serialize_data (struct TALER_EXCHANGE_Handle *exchange)
json_t *adenoms;
adenoms = json_array ();
- if (NULL == adenoms)
- {
- GNUNET_break (0);
- json_decref (denoms);
- json_decref (signkeys);
- json_decref (auditors);
- return NULL;
- }
+ GNUNET_assert (NULL != adenoms);
for (unsigned int j = 0; j<ai->num_denom_keys; j++)
{
const struct TALER_EXCHANGE_AuditorDenominationInfo *adi =
@@ -1705,7 +2236,10 @@ TALER_EXCHANGE_serialize_data (struct TALER_EXCHANGE_Handle *exchange)
&kd->denom_keys[adi->denom_key_offset];
json_t *k;
- if (now.abs_value_us > dk->expire_deposit.abs_value_us)
+ GNUNET_assert (adi->denom_key_offset < kd->num_denom_keys);
+ if (GNUNET_TIME_timestamp_cmp (now,
+ >,
+ dk->expire_deposit))
continue; /* skip auditor signatures for denomination keys that have expired */
GNUNET_assert (adi->denom_key_offset < kd->num_denom_keys);
k = GNUNET_JSON_PACK (
@@ -1729,419 +2263,209 @@ TALER_EXCHANGE_serialize_data (struct TALER_EXCHANGE_Handle *exchange)
json_array_append_new (auditors,
a));
}
- keys = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("version",
- kd->version),
- GNUNET_JSON_pack_string ("currency",
- kd->currency),
- GNUNET_JSON_pack_data_auto ("master_public_key",
- &kd->master_pub),
- GNUNET_JSON_pack_time_rel ("reserve_closing_delay",
- kd->reserve_closing_delay),
- GNUNET_JSON_pack_time_abs ("list_issue_date",
- kd->list_issue_date),
- GNUNET_JSON_pack_array_steal ("signkeys",
- signkeys),
- GNUNET_JSON_pack_array_steal ("denoms",
- denoms),
- GNUNET_JSON_pack_array_steal ("auditors",
- auditors));
- return GNUNET_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("version",
- EXCHANGE_SERIALIZATION_FORMAT_VERSION),
- GNUNET_JSON_pack_time_abs ("expire",
- exchange->key_data_expiration),
- GNUNET_JSON_pack_string ("exchange_url",
- exchange->url),
- GNUNET_JSON_pack_object_steal ("keys",
- keys));
-}
-
-
-/**
- * Initialise a connection to the exchange. Will connect to the
- * exchange and obtain information about the exchange's master
- * public key and the exchange's auditor.
- * The respective information will be passed to the @a cert_cb
- * once available, and all future interactions with the exchange
- * will be checked to be signed (where appropriate) by the
- * respective master key.
- *
- * @param ctx the context
- * @param url HTTP base URL for the exchange
- * @param cert_cb function to call with the exchange's
- * certification information
- * @param cert_cb_cls closure for @a cert_cb
- * @param ... list of additional arguments,
- * terminated by #TALER_EXCHANGE_OPTION_END.
- * @return the exchange handle; NULL upon error
- */
-struct TALER_EXCHANGE_Handle *
-TALER_EXCHANGE_connect (
- struct GNUNET_CURL_Context *ctx,
- const char *url,
- TALER_EXCHANGE_CertificationCallback cert_cb,
- void *cert_cb_cls,
- ...)
-{
- struct TALER_EXCHANGE_Handle *exchange;
- va_list ap;
- enum TALER_EXCHANGE_Option opt;
- TALER_LOG_DEBUG ("Connecting to the exchange (%s)\n",
- url);
- /* Disable 100 continue processing */
- GNUNET_break (GNUNET_OK ==
- GNUNET_CURL_append_header (ctx,
- "Expect:"));
- exchange = GNUNET_new (struct TALER_EXCHANGE_Handle);
- exchange->ctx = ctx;
- exchange->url = GNUNET_strdup (url);
- exchange->cert_cb = cert_cb;
- exchange->cert_cb_cls = cert_cb_cls;
- exchange->retry_task = GNUNET_SCHEDULER_add_now (&request_keys,
- exchange);
- va_start (ap, cert_cb_cls);
- while (TALER_EXCHANGE_OPTION_END !=
- (opt = va_arg (ap, int)))
+ global_fees = json_array ();
+ GNUNET_assert (NULL != global_fees);
+ for (unsigned int i = 0; i<kd->num_global_fees; i++)
{
- switch (opt)
- {
- case TALER_EXCHANGE_OPTION_END:
- GNUNET_assert (0);
- break;
- case TALER_EXCHANGE_OPTION_DATA:
- {
- const json_t *data = va_arg (ap, const json_t *);
-
- deserialize_data (exchange,
- data);
- break;
- }
- default:
- GNUNET_assert (0);
- break;
- }
- }
- va_end (ap);
- return exchange;
-}
+ const struct TALER_EXCHANGE_GlobalFee *gf
+ = &kd->global_fees[i];
-
-/**
- * Compute the network timeout for the next request to /keys.
- *
- * @param exchange the exchange handle
- * @returns the timeout in seconds (for use by CURL)
- */
-static long
-get_keys_timeout_seconds (struct TALER_EXCHANGE_Handle *exchange)
-{
- unsigned int kec;
-
- /* if retry counter >= 8, do not bother to go further, we
- stop the exponential back-off at 128 anyway. */
- kec = GNUNET_MIN (7,
- exchange->keys_error_count);
- return GNUNET_MIN (120,
- 5 + (1L << kec));
-}
-
-
-/**
- * Initiate download of /keys from the exchange.
- *
- * @param cls exchange where to download /keys from
- */
-static void
-request_keys (void *cls)
-{
- struct TALER_EXCHANGE_Handle *exchange = cls;
- struct KeysRequest *kr;
- CURL *eh;
- char url[200] = "/keys?";
-
- exchange->retry_task = NULL;
- GNUNET_assert (NULL == exchange->kr);
- kr = GNUNET_new (struct KeysRequest);
- kr->exchange = exchange;
-
- if (GNUNET_YES == TEAH_handle_is_ready (exchange))
- {
- TALER_LOG_DEBUG ("Last DK issue date (before GETting /keys): %s\n",
- GNUNET_STRINGS_absolute_time_to_string (
- exchange->key_data.last_denom_issue_date));
- sprintf (&url[strlen (url)],
- "last_issue_date=%llu&",
- (unsigned long
- long) exchange->key_data.last_denom_issue_date.abs_value_us
- / 1000000LLU);
+ if (GNUNET_TIME_absolute_is_past (gf->end_date.abs_time))
+ continue;
+ GNUNET_assert (
+ 0 ==
+ json_array_append_new (
+ global_fees,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_timestamp ("start_date",
+ gf->start_date),
+ GNUNET_JSON_pack_timestamp ("end_date",
+ gf->end_date),
+ TALER_JSON_PACK_GLOBAL_FEES (&gf->fees),
+ GNUNET_JSON_pack_time_rel ("history_expiration",
+ gf->history_expiration),
+ GNUNET_JSON_pack_time_rel ("purse_timeout",
+ gf->purse_timeout),
+ GNUNET_JSON_pack_uint64 ("purse_account_limit",
+ gf->purse_account_limit),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &gf->master_sig))));
}
- /* Clean the last '&'/'?' sign that we optimistically put. */
- url[strlen (url) - 1] = '\0';
- kr->url = TEAH_path_to_url (exchange,
- url);
- if (NULL == kr->url)
+ accounts = json_array ();
+ GNUNET_assert (NULL != accounts);
+ for (unsigned int i = 0; i<kd->accounts_len; i++)
{
- struct TALER_EXCHANGE_HttpResponse hr = {
- .ec = TALER_EC_GENERIC_CONFIGURATION_INVALID
- };
-
- GNUNET_free (kr);
- exchange->keys_error_count++;
- exchange->state = MHS_FAILED;
- exchange->cert_cb (exchange->cert_cb_cls,
- &hr,
- NULL,
- TALER_EXCHANGE_VC_PROTOCOL_ERROR);
- return;
+ const struct TALER_EXCHANGE_WireAccount *acc
+ = &kd->accounts[i];
+ json_t *credit_restrictions;
+ json_t *debit_restrictions;
+
+ credit_restrictions
+ = ar_to_json (acc->credit_restrictions_length,
+ acc->credit_restrictions);
+ GNUNET_assert (NULL != credit_restrictions);
+ debit_restrictions
+ = ar_to_json (acc->debit_restrictions_length,
+ acc->debit_restrictions);
+ GNUNET_assert (NULL != debit_restrictions);
+ GNUNET_assert (
+ 0 ==
+ json_array_append_new (
+ accounts,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("payto_uri",
+ acc->payto_uri),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("conversion_url",
+ acc->conversion_url)),
+ GNUNET_JSON_pack_int64 ("priority",
+ acc->priority),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("bank_label",
+ acc->bank_label)),
+ GNUNET_JSON_pack_array_steal ("debit_restrictions",
+ debit_restrictions),
+ GNUNET_JSON_pack_array_steal ("credit_restrictions",
+ credit_restrictions),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &acc->master_sig))));
}
-
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Requesting keys with URL `%s'.\n",
- kr->url);
- eh = TALER_EXCHANGE_curl_easy_get_ (kr->url);
- if (NULL == eh)
- {
- GNUNET_free (kr->url);
- GNUNET_free (kr);
- exchange->retry_delay = EXCHANGE_LIB_BACKOFF (exchange->retry_delay);
- exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay,
- &request_keys,
- exchange);
- return;
- }
- GNUNET_break (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_VERBOSE,
- 0));
- GNUNET_break (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_TIMEOUT,
- get_keys_timeout_seconds (exchange)));
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_HEADERFUNCTION,
- &header_cb));
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (eh,
- CURLOPT_HEADERDATA,
- kr));
- kr->job = GNUNET_CURL_job_add_with_ct_json (exchange->ctx,
- eh,
- &keys_completed_cb,
- kr);
- exchange->kr = kr;
-}
+ "Serialized %u/%u wire accounts to JSON\n",
+ (unsigned int) json_array_size (accounts),
+ kd->accounts_len);
-
-/**
- * Disconnect from the exchange
- *
- * @param exchange the exchange handle
- */
-void
-TALER_EXCHANGE_disconnect (struct TALER_EXCHANGE_Handle *exchange)
-{
- struct TEAH_AuditorListEntry *ale;
-
- while (NULL != (ale = exchange->auditors_head))
+ wire_fees = json_object ();
+ GNUNET_assert (NULL != wire_fees);
+ for (unsigned int i = 0; i<kd->fees_len; i++)
{
- struct TEAH_AuditorInteractionEntry *aie;
-
- while (NULL != (aie = ale->ai_head))
+ const struct TALER_EXCHANGE_WireFeesByMethod *fbw
+ = &kd->fees[i];
+ json_t *wf;
+
+ wf = json_array ();
+ GNUNET_assert (NULL != wf);
+ for (struct TALER_EXCHANGE_WireAggregateFees *p = fbw->fees_head;
+ NULL != p;
+ p = p->next)
{
- GNUNET_assert (aie->ale == ale);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Not sending deposit confirmation to auditor `%s' due to exchange disconnect\n",
- ale->auditor_url);
- TALER_AUDITOR_deposit_confirmation_cancel (aie->dch);
- GNUNET_CONTAINER_DLL_remove (ale->ai_head,
- ale->ai_tail,
- aie);
- GNUNET_free (aie);
+ GNUNET_assert (
+ 0 ==
+ json_array_append_new (
+ wf,
+ GNUNET_JSON_PACK (
+ TALER_JSON_pack_amount ("wire_fee",
+ &p->fees.wire),
+ TALER_JSON_pack_amount ("closing_fee",
+ &p->fees.closing),
+ GNUNET_JSON_pack_timestamp ("start_date",
+ p->start_date),
+ GNUNET_JSON_pack_timestamp ("end_date",
+ p->end_date),
+ GNUNET_JSON_pack_data_auto ("sig",
+ &p->master_sig))));
}
- GNUNET_CONTAINER_DLL_remove (exchange->auditors_head,
- exchange->auditors_tail,
- ale);
- TALER_LOG_DEBUG ("Disconnecting the auditor `%s'\n",
- ale->auditor_url);
- TALER_AUDITOR_disconnect (ale->ah);
- GNUNET_free (ale->auditor_url);
- GNUNET_free (ale);
- }
- if (NULL != exchange->kr)
- {
- GNUNET_CURL_job_cancel (exchange->kr->job);
- free_keys_request (exchange->kr);
- exchange->kr = NULL;
- }
- free_key_data (&exchange->key_data);
- if (NULL != exchange->key_data_raw)
- {
- json_decref (exchange->key_data_raw);
- exchange->key_data_raw = NULL;
+ GNUNET_assert (0 ==
+ json_object_set_new (wire_fees,
+ fbw->method,
+ wf));
}
- if (NULL != exchange->retry_task)
+
+ recoup = json_array ();
+ GNUNET_assert (NULL != recoup);
+ for (unsigned int i = 0; i<kd->num_denom_keys; i++)
{
- GNUNET_SCHEDULER_cancel (exchange->retry_task);
- exchange->retry_task = NULL;
+ const struct TALER_EXCHANGE_DenomPublicKey *dk
+ = &kd->denom_keys[i];
+ if (! dk->revoked)
+ continue;
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ recoup,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("h_denom_pub",
+ &dk->h_key))));
}
- GNUNET_free (exchange->url);
- GNUNET_free (exchange);
-}
+ wblwk = json_array ();
+ GNUNET_assert (NULL != wblwk);
+ for (unsigned int i = 0; i<kd->wblwk_length; i++)
+ {
+ const struct TALER_Amount *a = &kd->wallet_balance_limit_without_kyc[i];
-/**
- * Test if the given @a pub is a the current signing key from the exchange
- * according to @a keys.
- *
- * @param keys the exchange's key set
- * @param pub claimed current online signing key for the exchange
- * @return #GNUNET_OK if @a pub is (according to /keys) a current signing key
- */
-enum GNUNET_GenericReturnValue
-TALER_EXCHANGE_test_signing_key (const struct TALER_EXCHANGE_Keys *keys,
- const struct TALER_ExchangePublicKeyP *pub)
-{
- struct GNUNET_TIME_Absolute now;
-
- /* we will check using a tolerance of 1h for the time */
- now = GNUNET_TIME_absolute_get ();
- for (unsigned int i = 0; i<keys->num_sign_keys; i++)
- if ( (keys->sign_keys[i].valid_from.abs_value_us <= now.abs_value_us + 60
- * 60 * 1000LL * 1000LL) &&
- (keys->sign_keys[i].valid_until.abs_value_us > now.abs_value_us - 60
- * 60 * 1000LL * 1000LL) &&
- (0 == GNUNET_memcmp (pub,
- &keys->sign_keys[i].key)) )
- return GNUNET_OK;
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Signing key not valid at time %llu\n",
- (unsigned long long) now.abs_value_us);
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Get exchange's base URL.
- *
- * @param exchange exchange handle.
- * @return the base URL from the handle.
- */
-const char *
-TALER_EXCHANGE_get_base_url (const struct TALER_EXCHANGE_Handle *exchange)
-{
- return exchange->url;
-}
-
-
-/**
- * Obtain the denomination key details from the exchange.
- *
- * @param keys the exchange's key set
- * @param pk public key of the denomination to lookup
- * @return details about the given denomination key, NULL if the key is
- * not found
- */
-const struct TALER_EXCHANGE_DenomPublicKey *
-TALER_EXCHANGE_get_denomination_key (
- const struct TALER_EXCHANGE_Keys *keys,
- const struct TALER_DenominationPublicKey *pk)
-{
- for (unsigned int i = 0; i<keys->num_denom_keys; i++)
- if (0 ==
- TALER_denom_pub_cmp (pk,
- &keys->denom_keys[i].key))
- return &keys->denom_keys[i];
- return NULL;
-}
-
-
-/**
- * Create a copy of a denomination public key.
- *
- * @param key key to copy
- * @returns a copy, must be freed with #TALER_EXCHANGE_destroy_denomination_key
- */
-struct TALER_EXCHANGE_DenomPublicKey *
-TALER_EXCHANGE_copy_denomination_key (
- const struct TALER_EXCHANGE_DenomPublicKey *key)
-{
- struct TALER_EXCHANGE_DenomPublicKey *copy;
-
- copy = GNUNET_new (struct TALER_EXCHANGE_DenomPublicKey);
- *copy = *key;
- TALER_denom_pub_deep_copy (&copy->key,
- &key->key);
- return copy;
-}
-
-
-/**
- * Destroy a denomination public key.
- * Should only be called with keys created by #TALER_EXCHANGE_copy_denomination_key.
- *
- * @param key key to destroy.
- */
-void
-TALER_EXCHANGE_destroy_denomination_key (
- struct TALER_EXCHANGE_DenomPublicKey *key)
-{
- TALER_denom_pub_free (&key->key);
- GNUNET_free (key);
-}
-
-
-/**
- * Obtain the denomination key details from the exchange.
- *
- * @param keys the exchange's key set
- * @param hc hash of the public key of the denomination to lookup
- * @return details about the given denomination key
- */
-const struct TALER_EXCHANGE_DenomPublicKey *
-TALER_EXCHANGE_get_denomination_key_by_hash (
- const struct TALER_EXCHANGE_Keys *keys,
- const struct TALER_DenominationHash *hc)
-{
- for (unsigned int i = 0; i<keys->num_denom_keys; i++)
- if (0 == GNUNET_memcmp (hc,
- &keys->denom_keys[i].h_key))
- return &keys->denom_keys[i];
- return NULL;
-}
-
-
-/**
- * Obtain the keys from the exchange.
- *
- * @param exchange the exchange handle
- * @return the exchange's key set
- */
-const struct TALER_EXCHANGE_Keys *
-TALER_EXCHANGE_get_keys (struct TALER_EXCHANGE_Handle *exchange)
-{
- (void) TALER_EXCHANGE_check_keys_current (exchange,
- TALER_EXCHANGE_CKF_NONE);
- return &exchange->key_data;
-}
-
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ wblwk,
+ TALER_JSON_from_amount (a)));
+ }
-/**
- * Obtain the keys from the exchange in the
- * raw JSON format
- *
- * @param exchange the exchange handle
- * @return the exchange's keys in raw JSON
- */
-json_t *
-TALER_EXCHANGE_get_keys_raw (struct TALER_EXCHANGE_Handle *exchange)
-{
- (void) TALER_EXCHANGE_check_keys_current (exchange,
- TALER_EXCHANGE_CKF_NONE);
- return json_deep_copy (exchange->key_data_raw);
+ keys = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("version",
+ kd->version),
+ GNUNET_JSON_pack_string ("currency",
+ kd->currency),
+ GNUNET_JSON_pack_object_steal ("currency_specification",
+ TALER_CONFIG_currency_specs_to_json (
+ &kd->cspec)),
+ TALER_JSON_pack_amount ("stefan_abs",
+ &kd->stefan_abs),
+ TALER_JSON_pack_amount ("stefan_log",
+ &kd->stefan_log),
+ GNUNET_JSON_pack_double ("stefan_lin",
+ kd->stefan_lin),
+ GNUNET_JSON_pack_string ("asset_type",
+ kd->asset_type),
+ GNUNET_JSON_pack_data_auto ("master_public_key",
+ &kd->master_pub),
+ GNUNET_JSON_pack_time_rel ("reserve_closing_delay",
+ kd->reserve_closing_delay),
+ GNUNET_JSON_pack_timestamp ("list_issue_date",
+ kd->list_issue_date),
+ GNUNET_JSON_pack_array_steal ("global_fees",
+ global_fees),
+ GNUNET_JSON_pack_array_steal ("signkeys",
+ signkeys),
+ GNUNET_JSON_pack_object_steal ("wire_fees",
+ wire_fees),
+ GNUNET_JSON_pack_array_steal ("accounts",
+ accounts),
+ GNUNET_JSON_pack_array_steal ("wads",
+ json_array ()),
+ GNUNET_JSON_pack_array_steal ("denominations",
+ denominations_by_group),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_steal ("recoup",
+ recoup)),
+ GNUNET_JSON_pack_array_steal ("auditors",
+ auditors),
+ GNUNET_JSON_pack_bool ("rewards_allowed",
+ kd->rewards_allowed),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("extensions",
+ kd->extensions)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_is_zero (&kd->extensions_sig)
+ ? GNUNET_JSON_pack_string ("dummy",
+ NULL)
+ : GNUNET_JSON_pack_data_auto ("extensions_sig",
+ &kd->extensions_sig)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_array_steal ("wallet_balance_limit_without_kyc",
+ wblwk))
+
+ );
+ return GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("version",
+ EXCHANGE_SERIALIZATION_FORMAT_VERSION),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_timestamp ("expire",
+ kd->key_data_expiration)),
+ GNUNET_JSON_pack_string ("exchange_url",
+ kd->exchange_url),
+ GNUNET_JSON_pack_object_steal ("keys",
+ keys));
}