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.c2726
1 files changed, 1529 insertions, 1197 deletions
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index ac0e0584f..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
@@ -40,12 +40,17 @@
* Which version of the Taler protocol is implemented
* by this library? Used to determine compatibility.
*/
-#define EXCHANGE_PROTOCOL_CURRENT 12
+#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
@@ -54,7 +59,7 @@
#define EXCHANGE_SERIALIZATION_FORMAT_VERSION 0
/**
- * How far off do we allow key liftimes to be?
+ * How far off do we allow key lifetimes to be?
*/
#define LIFETIME_TOLERANCE GNUNET_TIME_UNIT_HOURS
@@ -65,175 +70,311 @@
#define DEFAULT_EXPIRATION GNUNET_TIME_UNIT_HOURS
/**
- * Set to 1 for extra debug logging.
- */
-#define DEBUG 0
-
-/**
- * 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
- */
-#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));
-
-
-/**
- * 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 GNUNET_TIME_Timestamp expire;
+ struct SignatureElement *elements;
+
+ /**
+ * Write offset in the @e elements array.
+ */
+ unsigned int elements_pos;
+ /**
+ * Allocated space for @e elements.
+ */
+ unsigned int elements_size;
};
-void
-TEAH_acc_confirmation_cb (void *cls,
- const struct TALER_AUDITOR_HttpResponse *hr)
+/**
+ * Determine order to sort two elements by before
+ * we hash the master signatures. Used for
+ * sorting with qsort().
+ *
+ * @param a pointer to a `struct SignatureElement`
+ * @param b pointer to a `struct SignatureElement`
+ * @return 0 if equal, -1 if a < b, 1 if a > b.
+ */
+static int
+signature_context_sort_cb (const void *a,
+ const void *b)
{
- 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;
}
-void
-TEAH_get_auditors_for_dc (struct TALER_EXCHANGE_Handle *h,
- TEAH_AuditorCallback ac,
- void *ac_cls)
+/**
+ * Append a @a master_sig to the @a sig_ctx using the
+ * given attributes for (later) sorting.
+ *
+ * @param[in,out] sig_ctx signature context to update
+ * @param group_offset offset for the group
+ * @param offset offset for the entry
+ * @param master_sig master signature for the entry
+ */
+static void
+append_signature (struct SignatureContext *sig_ctx,
+ unsigned int group_offset,
+ unsigned int offset,
+ const struct TALER_MasterSignatureP *master_sig)
{
- 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);
+ }
}
@@ -248,21 +389,20 @@ 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),
GNUNET_JSON_spec_timestamp ("stamp_start",
@@ -282,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 !=
@@ -292,33 +431,45 @@ 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",
@@ -331,22 +482,16 @@ parse_json_denomkey (struct TALER_EXCHANGE_DenomPublicKey *denom_key,
&denom_key->valid_from),
GNUNET_JSON_spec_timestamp ("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_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 ()
};
-
if (GNUNET_OK !=
GNUNET_JSON_parse (denom_key_obj,
spec,
@@ -355,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 ==
@@ -372,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;
}
@@ -394,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 ()
};
@@ -435,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),
@@ -461,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,
@@ -492,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;
+ 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 ()
+ };
- (void) hr;
- if (NULL == vi)
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (fee_obj,
+ spec,
+ NULL, NULL))
{
- /* 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;
+ GNUNET_break_op (0);
+#if DEBUG
+ json_dumpf (fee_obj,
+ stderr,
+ JSON_INDENT (2));
+#endif
+ return GNUNET_SYSERR;
}
-
- if (0 != (TALER_AUDITOR_VC_INCOMPATIBLE & compat))
+ if (check_sigs)
{
- 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;
- }
- 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++)
- {
- /* 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;
}
@@ -621,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,
@@ -666,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),
- 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_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))
{
@@ -699,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 ()
};
@@ -720,184 +816,331 @@ 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));
- }
+ 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 the supported extension(s): age-restriction. */
- /* TODO: maybe lift this into a FP in TALER_Extension ? */
+ /* Parse balance limits */
+ if (NULL != wblwk)
{
- json_t *age_restriction = json_object_get (resp_obj,
- "age_restriction");
-
- if (NULL != age_restriction)
+ 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++)
{
- bool critical;
- const char *version;
- const char *age_groups;
+ 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[] = {
- GNUNET_JSON_spec_bool ("critical",
- &critical),
- GNUNET_JSON_spec_string ("version",
- &version),
- GNUNET_JSON_spec_string ("age_groups",
- &age_groups),
+ TALER_JSON_spec_amount (NULL,
+ key_data->currency,
+ a),
GNUNET_JSON_spec_end ()
};
- if (GNUNET_OK !=
- GNUNET_JSON_parse (age_restriction,
- spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
+ EXITIF (GNUNET_OK !=
+ GNUNET_JSON_parse (aj,
+ spec,
+ NULL, NULL));
+ }
+ }
- if (critical || // do we care?
- 0 != strncmp (version, "1", 1) ) /* TODO: better compatibility check */
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
+ /* 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));
- if (TALER_Extension_OK !=
- TALER_parse_age_group_string (age_groups,
- &key_data->age_mask))
- {
- // TODO: print more specific error?
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
+ 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)
+ {
+ 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));
+ }
+
+ /* 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 ();
}
- /* parse the denomination keys, merging with the
- possibly EXISTING array as required (/keys cherry picking) */
+ /*
+ * 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}.
+ */
{
- /* The denominations can be in "denoms" and (optionally) in
- * "age_restricted_denoms"
- */
- struct
- { char *name;
- bool is_optional_age_restriction;} hive[2] = {
- { "denoms", false },
- { "age_restricted_denoms", true },
- };
+ json_t *group_obj;
+ unsigned int group_idx;
- for (size_t s = 0; s < sizeof(hive) / sizeof(hive[0]); s++)
+ json_array_foreach (denominations_by_group,
+ group_idx,
+ group_obj)
{
- json_t *denom_keys_array;
+ /* 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;
- denom_keys_array = json_object_get (resp_obj,
- hive[s].name);
-
- EXITIF (NULL == denom_keys_array &&
- ! hive[s].is_optional_age_restriction);
-
- if (NULL == denom_keys_array &&
- hive[s].is_optional_age_restriction)
- continue;
-
- /* if "age_restricted_denoms" exists, age-restriction better be enabled
- * (that is: mask non-zero) */
- EXITIF (NULL != denom_keys_array &&
- hive[s].is_optional_age_restriction &&
- 0 == key_data->age_mask.mask);
-
- EXITIF (JSON_ARRAY != json_typeof (denom_keys_array));
-
- json_array_foreach (denom_keys_array, index, denom_key_obj) {
- struct TALER_EXCHANGE_DenomPublicKey dk;
+ 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)
+ {
+ /* 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;
- memset (&dk,
- 0,
- sizeof (dk));
EXITIF (GNUNET_SYSERR ==
- parse_json_denomkey (&dk,
- check_sig,
- denom_key_obj,
- &key_data->master_pub,
- hash_context));
-
- /* Mark age restriction according where we got this denomination from,
- * "denoms" or "age_restricted_denoms" */
- if (hive[s].is_optional_age_restriction)
- dk.age_restricted = true;
-
+ 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++)
@@ -909,6 +1152,7 @@ decode_keys_json (const json_t *resp_obj,
break;
}
}
+
if (found)
{
/* 0:0:0 did not support /keys cherry picking */
@@ -916,10 +1160,14 @@ decode_keys_json (const json_t *resp_obj,
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" */
@@ -928,23 +1176,18 @@ decode_keys_json (const json_t *resp_obj,
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;
@@ -1003,163 +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_timestamp_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);
-
-
-void
-TALER_EXCHANGE_set_last_denom (struct TALER_EXCHANGE_Handle *exchange,
- struct GNUNET_TIME_Timestamp last_denom_new)
-{
- TALER_LOG_DEBUG (
- "Application explicitly set last denomination validity to %s\n",
- GNUNET_TIME_timestamp2s (last_denom_new));
- exchange->key_data.last_denom_issue_date = last_denom_new;
-}
-
-
-struct GNUNET_TIME_Timestamp
-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_TS;
-
- 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.abs_time)) )
- 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_TS;
-}
-
-
-/**
* Callback used when downloading the reply to a /keys request
* is complete.
*
@@ -1172,132 +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 and expiration %s.\n",
- kr->url,
+ gkh->url,
response_code,
- GNUNET_TIME_timestamp2s (kr->expire));
- if (GNUNET_TIME_absolute_is_past (kr->expire.abs_time))
+ GNUNET_TIME_timestamp2s (gkh->expire));
+ if (GNUNET_TIME_absolute_is_past (gkh->expire.abs_time))
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Exchange failed to give expiration time, assuming in %s\n",
- GNUNET_TIME_relative2s (DEFAULT_EXPIRATION,
- true));
- kr->expire
+ 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));
}
- kd_old = exchange->key_data;
- memset (&kd,
- 0,
- sizeof (struct TALER_EXCHANGE_Keys));
- vc = TALER_EXCHANGE_VC_PROTOCOL_ERROR;
switch (response_code)
{
case 0:
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to receive /keys response from exchange %s\n",
- exchange->url);
- 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;
+ 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:
@@ -1305,108 +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;
- if (GNUNET_TIME_absolute_is_past (
- exchange->key_data.last_denom_issue_date.abs_time))
- TALER_LOG_WARNING ("Last DK issue date from exchange is in the past: %s\n",
- GNUNET_TIME_timestamp2s (
- exchange->key_data.last_denom_issue_date));
- else
- TALER_LOG_DEBUG ("Last DK issue date updated to: %s\n",
- GNUNET_TIME_timestamp2s (
- 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 ********* */
-
-
-struct GNUNET_CURL_Context *
-TEAH_handle_to_context (struct TALER_EXCHANGE_Handle *h)
-{
- return h->ctx;
-}
-
-
-enum GNUNET_GenericReturnValue
-TEAH_handle_is_ready (struct TALER_EXCHANGE_Handle *h)
-{
- return (MHS_CERT == h->state) ? GNUNET_YES : GNUNET_NO;
-}
-
-
-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);
}
@@ -1420,7 +1512,7 @@ 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
@@ -1520,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
@@ -1529,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;
@@ -1541,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))
@@ -1556,114 +1652,462 @@ header_cb (char *buffer,
}
-/* ********************* 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_Timestamp 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),
- GNUNET_JSON_spec_timestamp ("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;
+}
+
+
+/**
+ * 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[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_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_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];
@@ -1684,63 +2128,96 @@ TALER_EXCHANGE_serialize_data (struct TALER_EXCHANGE_Handle *exchange)
sk->valid_until),
GNUNET_JSON_pack_timestamp ("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_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 (GNUNET_TIME_timestamp_cmp (now,
- >,
- dk->expire_deposit))
- continue; /* skip keys that have expired */
- 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),
- 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++)
@@ -1750,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 =
@@ -1766,6 +2236,7 @@ TALER_EXCHANGE_serialize_data (struct TALER_EXCHANGE_Handle *exchange)
&kd->denom_keys[adi->denom_key_offset];
json_t *k;
+ GNUNET_assert (adi->denom_key_offset < kd->num_denom_keys);
if (GNUNET_TIME_timestamp_cmp (now,
>,
dk->expire_deposit))
@@ -1792,349 +2263,210 @@ TALER_EXCHANGE_serialize_data (struct TALER_EXCHANGE_Handle *exchange)
json_array_append_new (auditors,
a));
}
+
+ global_fees = json_array ();
+ GNUNET_assert (NULL != global_fees);
+ for (unsigned int i = 0; i<kd->num_global_fees; i++)
+ {
+ const struct TALER_EXCHANGE_GlobalFee *gf
+ = &kd->global_fees[i];
+
+ 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))));
+ }
+
+ accounts = json_array ();
+ GNUNET_assert (NULL != accounts);
+ for (unsigned int i = 0; i<kd->accounts_len; i++)
+ {
+ 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,
+ "Serialized %u/%u wire accounts to JSON\n",
+ (unsigned int) json_array_size (accounts),
+ kd->accounts_len);
+
+ wire_fees = json_object ();
+ GNUNET_assert (NULL != wire_fees);
+ for (unsigned int i = 0; i<kd->fees_len; i++)
+ {
+ 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 (
+ 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_assert (0 ==
+ json_object_set_new (wire_fees,
+ fbw->method,
+ wf));
+ }
+
+ recoup = json_array ();
+ GNUNET_assert (NULL != recoup);
+ for (unsigned int i = 0; i<kd->num_denom_keys; i++)
+ {
+ 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))));
+ }
+
+ 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];
+
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ wblwk,
+ TALER_JSON_from_amount (a)));
+ }
+
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_array_steal ("denoms",
- denoms),
+ 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));
+ 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_timestamp ("expire",
- exchange->key_data_expiration),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_timestamp ("expire",
+ kd->key_data_expiration)),
GNUNET_JSON_pack_string ("exchange_url",
- exchange->url),
+ kd->exchange_url),
GNUNET_JSON_pack_object_steal ("keys",
keys));
}
-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,
- MHD_HTTP_HEADER_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)))
- {
- 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;
-}
-
-
-/**
- * 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_TIME_timestamp2s (
- 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_time.abs_value_us
- / 1000000LLU);
- }
-
- /* 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)
- {
- 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;
- }
-
- 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;
-}
-
-
-void
-TALER_EXCHANGE_disconnect (struct TALER_EXCHANGE_Handle *exchange)
-{
- struct TEAH_AuditorListEntry *ale;
-
- while (NULL != (ale = exchange->auditors_head))
- {
- struct TEAH_AuditorInteractionEntry *aie;
-
- while (NULL != (aie = ale->ai_head))
- {
- 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_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;
- }
- if (NULL != exchange->retry_task)
- {
- GNUNET_SCHEDULER_cancel (exchange->retry_task);
- exchange->retry_task = NULL;
- }
- GNUNET_free (exchange->url);
- GNUNET_free (exchange);
-}
-
-
-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 char *
-TALER_EXCHANGE_get_base_url (const struct TALER_EXCHANGE_Handle *exchange)
-{
- return exchange->url;
-}
-
-
-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;
-}
-
-
-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;
-}
-
-
-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_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;
-}
-
-
-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;
-}
-
-
-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);
-}
-
-
/* end of exchange_api_handle.c */