summaryrefslogtreecommitdiff
path: root/src/exchange-lib/exchange_api_wire.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2018-04-02 14:24:45 +0200
committerChristian Grothoff <christian@grothoff.org>2018-04-02 14:29:44 +0200
commitcb55c1a3af9f56a6da38e5589e72df0b70d355b1 (patch)
tree5f9a3af7d9073249f77ce56c690844a6cb27c3e7 /src/exchange-lib/exchange_api_wire.c
parent7a20062bafed42f937c5388aed09042aad7014c0 (diff)
downloadexchange-cb55c1a3af9f56a6da38e5589e72df0b70d355b1.tar.gz
exchange-cb55c1a3af9f56a6da38e5589e72df0b70d355b1.tar.bz2
exchange-cb55c1a3af9f56a6da38e5589e72df0b70d355b1.zip
Changing configuration structure to enable multiple accounts.
This change enables using multiple wire plugins at the same time. Also, we now distinguish between the wire plugin (i.e. EBICS or taler_bank) and the wire method (i.e. SEPA or x-taler-bank) that the wire plugin is implementing. The "taler-bank" wire method was renamed from "test" to "x-taler-bank". This also changes the format of the /wire response of the exchange, as we now need to return multiple accounts. Note that wire fees are specified per wire method, not per wire account. taler-exchange-keyup now automatically signs all of the /wire responses in the location specified by the configuration. Account identification in wire plugins was changed to use payto://-URLs instead of method-specific JSON fields. Signing and validation of /wire responses was moved from each wire plugin to a generic validation method in libtalerutil (crypto) or libtalerjson (for JSON-formatted inputs). Convenience methods were added to generate JSON for wire accounts (salting, signing). Various section and option names were adjusted to streamline the configuration and make it more consistent overall. Documentation was updated as well.
Diffstat (limited to 'src/exchange-lib/exchange_api_wire.c')
-rw-r--r--src/exchange-lib/exchange_api_wire.c475
1 files changed, 228 insertions, 247 deletions
diff --git a/src/exchange-lib/exchange_api_wire.c b/src/exchange-lib/exchange_api_wire.c
index f1056fdd2..12789b2bc 100644
--- a/src/exchange-lib/exchange_api_wire.c
+++ b/src/exchange-lib/exchange_api_wire.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
+ Copyright (C) 2014-2018 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 by the Free Software
@@ -27,6 +27,7 @@
#include <gnunet/gnunet_curl_lib.h>
#include "taler_exchange_service.h"
#include "taler_json_lib.h"
+#include "taler_wire_lib.h"
#include "taler_signatures.h"
#include "taler_wire_plugin.h"
#include "exchange_api_handle.h"
@@ -63,66 +64,133 @@ struct TALER_EXCHANGE_WireHandle
*/
void *cb_cls;
+};
+
+
+/**
+ * List of wire fees by method.
+ */
+struct FeeMap
+{
/**
- * Set to the "methods" JSON array returned by the
- * /wire request.
+ * Next entry in list.
*/
- json_t *methods;
+ struct FeeMap *next;
/**
- * Current iteration offset in the @e methods array.
+ * Wire method this fee structure is for.
*/
- unsigned int methods_off;
+ char *method;
+ /**
+ * Array of wire fees, also linked list, but allocated
+ * only once.
+ */
+ struct TALER_EXCHANGE_WireAggregateFees *fee_list;
};
/**
- * Verify that the signature on the "200 OK" response
- * for /wire/METHOD from the exchange is valid.
+ * Frees @a fm.
*
- * @param wh wire handle with key material
- * @param method method to verify the reply for
- * @param json json reply with the signature
- * @return #GNUNET_SYSERR if @a json is invalid,
- * #GNUNET_NO if the method is unknown,
- * #GNUNET_OK if the json is valid
+ * @param fm memory to release
*/
-static int
-verify_wire_method_signature_ok (const struct TALER_EXCHANGE_WireHandle *wh,
- const char *method,
- const json_t *json)
+static void
+free_fees (struct FeeMap *fm)
{
- const struct TALER_EXCHANGE_Keys *key_state;
- struct TALER_WIRE_Plugin *plugin;
- char *lib_name;
- char *emsg;
- enum TALER_ErrorCode ec;
-
- key_state = TALER_EXCHANGE_get_keys (wh->exchange);
- (void) GNUNET_asprintf (&lib_name,
- "libtaler_plugin_wire_%s",
- method);
- plugin = GNUNET_PLUGIN_load (lib_name,
- NULL);
- if (NULL == plugin)
+ while (NULL != fm)
{
- GNUNET_free (lib_name);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Wire transfer method `%s' not supported\n",
- method);
- return GNUNET_NO;
+ struct FeeMap *fe = fm->next;
+
+ GNUNET_free (fm->fee_list);
+ GNUNET_free (fm->method);
+ GNUNET_free (fm);
+ fm = fe;
+ }
+}
+
+
+/**
+ * Parse wire @a fees and return map.
+ *
+ * @param fees json AggregateTransferFee to parse
+ * @return NULL on error
+ */
+static struct FeeMap *
+parse_fees (json_t *fees)
+{
+ struct FeeMap *fm = NULL;
+ const char *key;
+ json_t *fee_array;
+
+ json_object_foreach (fees, key, fee_array) {
+ struct FeeMap *fe = GNUNET_new (struct FeeMap);
+ int len;
+ unsigned int idx;
+ json_t *fee;
+
+ if (0 == (len = json_array_size (fee_array)))
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (fe);
+ continue; /* skip */
+ }
+ fe->method = GNUNET_strdup (key);
+ fe->next = fm;
+ fe->fee_list = GNUNET_new_array (len,
+ struct TALER_EXCHANGE_WireAggregateFees);
+ fm = fe;
+ json_array_foreach (fee_array, idx, fee)
+ {
+ struct TALER_EXCHANGE_WireAggregateFees *wa = &fe->fee_list[idx];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("sig",
+ &wa->master_sig),
+ TALER_JSON_spec_amount ("wire_fee",
+ &wa->wire_fee),
+ TALER_JSON_spec_amount ("closing_fee",
+ &wa->closing_fee),
+ GNUNET_JSON_spec_absolute_time ("start_date",
+ &wa->start_date),
+ GNUNET_JSON_spec_absolute_time ("end_date",
+ &wa->end_date),
+ GNUNET_JSON_spec_end()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (fee,
+ spec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ free_fees (fm);
+ return NULL;
+ }
+ if (idx < len)
+ wa->next = &fe->fee_list[idx + 1];
+ }
}
- plugin->library_name = lib_name;
- ec = plugin->wire_validate (plugin->cls,
- json,
- &key_state->master_pub,
- &emsg);
- GNUNET_free_non_null (emsg);
- GNUNET_PLUGIN_unload (lib_name,
- plugin);
- GNUNET_free (lib_name);
- return (TALER_EC_NONE == ec) ? GNUNET_OK : GNUNET_SYSERR;
+ return fm;
+}
+
+
+/**
+ * Find fee by @a method.
+ *
+ * @param fm map to look in
+ * @param method key to look for
+ * @return NULL if fee is not specified in @a fm
+ */
+static const struct TALER_EXCHANGE_WireAggregateFees *
+lookup_fee (const struct FeeMap *fm,
+ const char *method)
+{
+ for (;NULL != fm; fm = fm->next)
+ if (0 == strcasecmp (fm->method,
+ method))
+ return fm->fee_list;
+ return NULL;
}
@@ -140,70 +208,131 @@ handle_wire_finished (void *cls,
const json_t *json)
{
struct TALER_EXCHANGE_WireHandle *wh = cls;
- json_t *keep = NULL;
+ enum TALER_ErrorCode ec;
wh->job = NULL;
+ ec = TALER_EC_NONE;
switch (response_code)
{
case 0:
break;
case MHD_HTTP_OK:
{
- const struct TALER_EXCHANGE_Keys *keys;
- const struct TALER_MasterPublicKeyP *master_pub;
- const char *key;
- json_t *method;
- int ret;
-
- /* We 'keep' methods that we support and that are well-formed;
- we fail (by setting response_code=0) if any method that we do
- support fails to verify. */
- keep = json_object ();
- json_object_foreach ((json_t *) json, key, method) {
- ret = verify_wire_method_signature_ok (wh,
- key,
- method);
- if (GNUNET_SYSERR == ret)
- {
- /* bogus reply */
- GNUNET_break_op (0);
- response_code = 0;
- }
- /* GNUNET_NO: not understood by us, simply skip! */
- if (GNUNET_OK == ret)
- {
- /* supported and valid, keep! */
- json_object_set (keep,
- key,
- method);
- }
- }
- /* check fees */
- keys = TALER_EXCHANGE_get_keys (wh->exchange);
- if (NULL == keys)
- master_pub = NULL;
- else
- master_pub = &keys->master_pub;
+ json_t *accounts;
+ json_t *fees;
+ int num_accounts;
+ struct FeeMap *fm;
+ const struct TALER_EXCHANGE_Keys *key_state;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_json ("accounts", &accounts),
+ GNUNET_JSON_spec_json ("fees", &fees),
+ GNUNET_JSON_spec_end()
+ };
+
if (GNUNET_OK !=
- TALER_EXCHANGE_wire_get_fees (master_pub,
- keep,
- NULL,
- NULL))
+ GNUNET_JSON_parse (json,
+ spec,
+ NULL, NULL))
{
/* bogus reply */
GNUNET_break_op (0);
response_code = 0;
+ ec = TALER_EC_SERVER_JSON_INVALID;
+ break;
}
- if (0 != response_code)
+ if (0 == (num_accounts = json_array_size (accounts)))
{
- /* all supported methods were valid, use 'keep' for 'json' */
+ /* bogus reply */
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ response_code = 0;
+ ec = TALER_EC_SERVER_JSON_INVALID;
break;
}
- /* some supported methods were invalid, release 'keep', preserve
- full 'json' for application-level error handling. */
- json_decref (keep);
- keep = NULL;
- }
+ if (NULL == (fm = parse_fees (fees)))
+ {
+ /* bogus reply */
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ response_code = 0;
+ ec = TALER_EC_SERVER_JSON_INVALID;
+ break;
+ }
+
+ key_state = TALER_EXCHANGE_get_keys (wh->exchange);
+ /* parse accounts */
+ {
+ struct TALER_EXCHANGE_WireAccount was[num_accounts];
+
+ for (unsigned int i=0;i<num_accounts;i++)
+ {
+ struct TALER_EXCHANGE_WireAccount *wa = &was[i];
+ json_t *account;
+ struct GNUNET_JSON_Specification spec_account[] = {
+ GNUNET_JSON_spec_string ("url", &wa->url),
+ GNUNET_JSON_spec_string ("salt", &wa->salt),
+ GNUNET_JSON_spec_fixed_auto ("master_sig", &wa->master_sig),
+ GNUNET_JSON_spec_end()
+ };
+ char *method;
+
+ account = json_array_get (accounts,
+ i);
+ if (GNUNET_OK !=
+ TALER_JSON_wire_signature_check (account,
+ &key_state->master_pub))
+ {
+ /* bogus reply */
+ GNUNET_break_op (0);
+ response_code = 0;
+ ec = TALER_EC_SERVER_SIGNATURE_INVALID;
+ break;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (account,
+ spec_account,
+ NULL, NULL))
+ {
+ /* bogus reply */
+ GNUNET_break_op (0);
+ response_code = 0;
+ ec = TALER_EC_SERVER_JSON_INVALID;
+ break;
+ }
+ if (NULL == (method = TALER_WIRE_payto_get_method (wa->url)))
+ {
+ /* bogus reply */
+ GNUNET_break_op (0);
+ response_code = 0;
+ ec = TALER_EC_SERVER_JSON_INVALID;
+ break;
+ }
+ if (NULL == (wa->fees = lookup_fee (fm,
+ method)))
+ {
+ /* bogus reply */
+ GNUNET_break_op (0);
+ response_code = 0;
+ ec = TALER_EC_SERVER_JSON_INVALID;
+ GNUNET_free (method);
+ break;
+ }
+ GNUNET_free (method);
+ } /* end 'for all accounts */
+ if ( (0 != response_code) &&
+ (NULL != wh->cb) )
+ {
+ wh->cb (wh->cb_cls,
+ response_code,
+ TALER_EC_NONE,
+ num_accounts,
+ was);
+ wh->cb = NULL;
+ }
+ } /* end of 'parse accounts */
+ free_fees (fm);
+ GNUNET_JSON_parse_free (spec);
+ } /* end of MHD_HTTP_OK */
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy
@@ -226,12 +355,12 @@ handle_wire_finished (void *cls,
response_code = 0;
break;
}
- wh->cb (wh->cb_cls,
- response_code,
- TALER_JSON_get_error_code (json),
- (NULL != keep) ? keep : json);
- if (NULL != keep)
- json_decref (keep);
+ if (NULL != wh->cb)
+ wh->cb (wh->cb_cls,
+ response_code,
+ (0 == response_code) ? ec : TALER_JSON_get_error_code (json),
+ 0,
+ NULL);
TALER_EXCHANGE_wire_cancel (wh);
}
@@ -306,157 +435,9 @@ TALER_EXCHANGE_wire_cancel (struct TALER_EXCHANGE_WireHandle *wh)
GNUNET_CURL_job_cancel (wh->job);
wh->job = NULL;
}
- if (NULL != wh->methods)
- {
- json_decref (wh->methods);
- wh->methods = NULL;
- }
GNUNET_free (wh->url);
GNUNET_free (wh);
}
-/**
- * Parse wire @a fee and store the result in @a af.
- *
- * @param[out] af where to write the result
- * @param fee json AggregateTransferFee to parse
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
- */
-static int
-parse_json_fees (struct TALER_EXCHANGE_WireAggregateFees *af,
- json_t *fee)
-{
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("sig",
- &af->master_sig),
- TALER_JSON_spec_amount ("wire_fee",
- &af->wire_fee),
- TALER_JSON_spec_amount ("closing_fee",
- &af->closing_fee),
- GNUNET_JSON_spec_absolute_time ("start_date",
- &af->start_date),
- GNUNET_JSON_spec_absolute_time ("end_date",
- &af->end_date),
- GNUNET_JSON_spec_end()
- };
-
- if (GNUNET_OK !=
- GNUNET_JSON_parse (fee,
- spec,
- NULL,
- NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Check the #TALER_SIGNATURE_MASTER_WIRE_FEES signature.
- *
- * @param af record to check
- * @param wire_method wire method to check against
- * @param master_pub expected signing key
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
- */
-static int
-check_sig (const struct TALER_EXCHANGE_WireAggregateFees *af,
- const char *wire_method,
- const struct TALER_MasterPublicKeyP *master_pub)
-{
- struct TALER_MasterWireFeePS wp;
-
- wp.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_FEES);
- wp.purpose.size = htonl (sizeof (wp));
- GNUNET_CRYPTO_hash (wire_method,
- strlen (wire_method) + 1,
- &wp.h_wire_method);
- wp.start_date = GNUNET_TIME_absolute_hton (af->start_date);
- wp.end_date = GNUNET_TIME_absolute_hton (af->end_date);
- TALER_amount_hton (&wp.wire_fee,
- &af->wire_fee);
- TALER_amount_hton (&wp.closing_fee,
- &af->closing_fee);
- return GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_FEES,
- &wp.purpose,
- &af->master_sig.eddsa_signature,
- &master_pub->eddsa_pub);
-}
-
-
-/**
- * Obtain information about wire fees encoded in @a obj
- * by wire method.
- *
- * @param master_pub public key to use to verify signatures, NULL to not verify
- * @param obj wire information as encoded in the #TALER_EXCHANGE_WireResultCallback
- * @param cb callback to invoke for the fees
- * @param cb_cls closure for @a cb
- * @return #GNUNET_OK in success, #GNUNET_SYSERR if @a obj is ill-formed
- */
-int
-TALER_EXCHANGE_wire_get_fees (const struct TALER_MasterPublicKeyP *master_pub,
- const json_t *obj,
- TALER_EXCHANGE_WireFeeCallback cb,
- void *cb_cls)
-{
- const char *wire_method;
- json_t *value;
-
- json_object_foreach (((json_t *) obj), wire_method, value)
- {
- json_t *fees;
- size_t num_fees;
-
- fees = json_object_get (value, "fees");
- if ( (NULL == fees) ||
- (! json_is_array (fees)) )
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- num_fees = json_array_size (fees);
- if (num_fees > 1024)
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- {
- struct TALER_EXCHANGE_WireAggregateFees af[num_fees + 1];
-
- for (size_t i=0;i<num_fees;i++)
- {
- af[i].next = &af[i+1];
- if (GNUNET_OK !=
- parse_json_fees (&af[i],
- json_array_get (fees,
- i)))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- if ( (NULL != master_pub) &&
- (GNUNET_OK !=
- check_sig (&af[i],
- wire_method,
- master_pub)) )
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- }
- af[num_fees - 1].next = NULL;
- if (NULL != cb)
- cb (cb_cls,
- wire_method,
- &af[0]);
- }
- }
- return GNUNET_OK;
-}
-
-
/* end of exchange_api_wire.c */