summaryrefslogtreecommitdiff
path: root/src/wire
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-04-15 15:00:26 +0200
committerChristian Grothoff <christian@grothoff.org>2016-04-15 15:00:26 +0200
commit74e237164ce7958c19467ad440e45576c4425570 (patch)
treee5156b7197cbb76ad0b0d617d9a04eeb2c08ea48 /src/wire
parentebf049a8c2d982e723fe67dbff64e1eea14d8247 (diff)
parent3098c0a9e0b18a436e484ef693cdebeb16d4c131 (diff)
downloadexchange-74e237164ce7958c19467ad440e45576c4425570.tar.gz
exchange-74e237164ce7958c19467ad440e45576c4425570.tar.bz2
exchange-74e237164ce7958c19467ad440e45576c4425570.zip
Merge branch 'master' of ssh://taler.net:/var/git/exchange
Diffstat (limited to 'src/wire')
-rw-r--r--src/wire/Makefile.am31
-rw-r--r--src/wire/plugin_wire_sepa.c292
-rw-r--r--src/wire/plugin_wire_template.c53
-rw-r--r--src/wire/plugin_wire_test.c393
-rw-r--r--src/wire/test_sepa_wireformat.c34
-rw-r--r--src/wire/test_wire_plugin.c196
-rw-r--r--src/wire/test_wire_plugin.conf21
-rw-r--r--src/wire/test_wire_plugin_key.priv1
-rw-r--r--src/wire/test_wire_plugin_sepa.json8
-rw-r--r--src/wire/test_wire_plugin_test.json7
-rw-r--r--src/wire/wire-sepa.conf10
-rw-r--r--src/wire/wire-test.conf16
12 files changed, 957 insertions, 105 deletions
diff --git a/src/wire/Makefile.am b/src/wire/Makefile.am
index eb2e893fa..debb27828 100644
--- a/src/wire/Makefile.am
+++ b/src/wire/Makefile.am
@@ -6,6 +6,18 @@ if USE_COVERAGE
XLIB = -lgcov
endif
+pkgcfgdir = $(prefix)/share/taler/config.d/
+
+pkgcfg_DATA = \
+ wire-sepa.conf \
+ wire-test.conf
+
+
+EXTRA_DIST = \
+ wire-sepa.conf \
+ wire-test.conf \
+ test_wire_plugin.conf
+
plugindir = $(libdir)/taler
plugin_LTLIBRARIES = \
@@ -36,7 +48,9 @@ libtaler_plugin_wire_sepa_la_LIBADD = \
$(LTLIBINTL)
libtaler_plugin_wire_sepa_la_LDFLAGS = \
$(TALER_PLUGIN_LDFLAGS) \
+ $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetjson \
-lgnunetutil $(XLIB)
@@ -60,11 +74,15 @@ libtalerwire_la_LDFLAGS = \
-export-dynamic -no-undefined
+AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
+
TESTS = \
- test_sepa_wireformat
+ test_sepa_wireformat \
+ test_wire_plugin
check_PROGRAMS= \
- test_sepa_wireformat
+ test_sepa_wireformat \
+ test_wire_plugin
@@ -76,3 +94,12 @@ test_sepa_wireformat_LDADD = \
libtalerwire.la \
$(top_builddir)/src/util/libtalerutil.la
+
+test_wire_plugin_SOURCES = \
+ test_wire_plugin.c
+test_wire_plugin_LDADD = \
+ -lgnunetjson \
+ -lgnunetutil \
+ -ljansson \
+ libtalerwire.la \
+ $(top_builddir)/src/util/libtalerutil.la
diff --git a/src/wire/plugin_wire_sepa.c b/src/wire/plugin_wire_sepa.c
index 00d19d4b0..6f01167d9 100644
--- a/src/wire/plugin_wire_sepa.c
+++ b/src/wire/plugin_wire_sepa.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2016 GNUnet e.V.
+ Copyright (C) 2016 GNUnet e.V. & Inria
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
@@ -23,6 +23,8 @@
*/
#include "platform.h"
#include "taler_wire_plugin.h"
+#include "taler_signatures.h"
+#include <gnunet/gnunet_json_lib.h>
/**
@@ -58,6 +60,8 @@ sepa_amount_round (void *cls,
struct SepaClosure *sc = cls;
uint32_t delta;
+ if (NULL == sc->currency)
+ return GNUNET_SYSERR;
if (0 != strcasecmp (amount->currency,
sc->currency))
{
@@ -348,41 +352,138 @@ validate_iban (const char *iban)
/**
+ * Compute purpose for signing.
+ *
+ * @param sepa_name name of the account holder
+ * @param iban bank account number in IBAN format
+ * @param bic bank identifier
+ * @param[out] mp purpose to be signed
+ */
+static void
+compute_purpose (const char *sepa_name,
+ const char *iban,
+ const char *bic,
+ struct TALER_MasterWireDetailsPS *wsd)
+{
+ struct GNUNET_HashContext *hc;
+
+ wsd->purpose.size = htonl (sizeof (struct TALER_MasterWireDetailsPS));
+ wsd->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SEPA_DETAILS);
+ hc = GNUNET_CRYPTO_hash_context_start ();
+ GNUNET_CRYPTO_hash_context_read (hc,
+ "sepa",
+ strlen ("sepa") + 1);
+ GNUNET_CRYPTO_hash_context_read (hc,
+ sepa_name,
+ strlen (sepa_name) + 1);
+ GNUNET_CRYPTO_hash_context_read (hc,
+ iban,
+ strlen (iban) + 1);
+ GNUNET_CRYPTO_hash_context_read (hc,
+ bic,
+ strlen (bic) + 1);
+ GNUNET_CRYPTO_hash_context_finish (hc,
+ &wsd->h_sepa_details);
+}
+
+
+/**
+ * Verify that the signature in the @a json for /wire/sepa is valid.
+ *
+ * @param json json reply with the signature
+ * @param master_pub public key of the exchange to verify against
+ * @return #GNUNET_SYSERR if @a json is invalid,
+ * #GNUNET_NO if the method is unknown,
+ * #GNUNET_OK if the json is valid
+ */
+static int
+verify_wire_sepa_signature_ok (const json_t *json,
+ const struct TALER_MasterPublicKeyP *master_pub)
+{
+ struct TALER_MasterSignatureP exchange_sig;
+ struct TALER_MasterWireDetailsPS mp;
+ const char *name;
+ const char *iban;
+ const char *bic;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("sig", &exchange_sig),
+ GNUNET_JSON_spec_string ("name", &name),
+ GNUNET_JSON_spec_string ("iban", &iban),
+ GNUNET_JSON_spec_string ("bic", &bic),
+ GNUNET_JSON_spec_end()
+ };
+
+ if (NULL == master_pub)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Skipping signature check as master public key not given\n");
+ return GNUNET_OK;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (json, spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ compute_purpose (name,
+ iban,
+ bic,
+ &mp);
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SEPA_DETAILS,
+ &mp.purpose,
+ &exchange_sig.eddsa_signature,
+ &master_pub->eddsa_pub))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_OK;
+}
+
+
+/**
* Check if the given wire format JSON object is correctly formatted
*
+ * @param cls the @e cls of this struct with the plugin-specific state
* @param wire the JSON wire format object
+ * @param master_pub public key of the exchange to verify against
* @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
*/
static int
-sepa_wire_validate (const json_t *wire)
+sepa_wire_validate (void *cls,
+ const json_t *wire,
+ const struct TALER_MasterPublicKeyP *master_pub)
{
json_error_t error;
const char *type;
const char *iban;
const char *name;
const char *bic;
- uint64_t r;
- const char *address;
if (0 != json_unpack_ex
((json_t *) wire,
- &error, JSON_STRICT,
+ &error, 0,
"{"
- "s:s," /* TYPE: sepa */
- "s:s," /* IBAN: iban */
+ "s:s," /* type: sepa */
+ "s:s," /* iban: IBAN */
"s:s," /* name: beneficiary name */
- "s:s," /* BIC: beneficiary bank's BIC */
- "s:i," /* r: random 64-bit integer nounce */
- "s:s" /* address: address of the beneficiary */
+ "s:s" /* bic: beneficiary bank's BIC */
"}",
"type", &type,
- "IBAN", &iban,
+ "iban", &iban,
"name", &name,
- "bic", &bic,
- "r", &r,
- "address", &address))
+ "bic", &bic))
{
- TALER_json_warn (error);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "JSON parsing failed at %s:%u: %s (%s)\n",
+ __FILE__, __LINE__,
+ error.text, error.source);
+ json_dumpf (wire, stderr, 0);
+ fprintf (stderr, "\n");
return GNUNET_SYSERR;
}
if (0 != strcasecmp (type,
@@ -400,11 +501,144 @@ sepa_wire_validate (const json_t *wire)
iban);
return GNUNET_NO;
}
+ /* FIXME: don't parse again, integrate properly... */
+ if (GNUNET_OK !=
+ verify_wire_sepa_signature_ok (wire,
+ master_pub))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Signature invalid\n");
+ return GNUNET_NO;
+ }
return GNUNET_YES;
}
/**
+ * Obtain wire transfer details in the plugin-specific format
+ * from the configuration.
+ *
+ * @param cls closure
+ * @param cfg configuration with details about wire accounts
+ * @param account_name which section in the configuration should we parse
+ * @return NULL if @a cfg fails to have valid wire details for @a account_name
+ */
+static json_t *
+sepa_get_wire_details (void *cls,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *account_name)
+{
+ char *sepa_wire_file;
+ json_error_t err;
+ json_t *ret;
+
+ /* Fetch reply */
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (cfg,
+ account_name,
+ "SEPA_RESPONSE_FILE",
+ &sepa_wire_file))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
+ account_name,
+ "SEPA_RESPONSE_FILE");
+ return NULL;
+ }
+ ret = json_load_file (sepa_wire_file,
+ JSON_REJECT_DUPLICATES,
+ &err);
+ if (NULL == ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse JSON in %s: %s (%s:%u)\n",
+ sepa_wire_file,
+ err.text,
+ err.source,
+ err.line);
+ GNUNET_free (sepa_wire_file);
+ return NULL;
+ }
+ if (GNUNET_YES != sepa_wire_validate (cls,
+ ret,
+ NULL))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to validate SEPA data in %s\n",
+ sepa_wire_file);
+ GNUNET_free (sepa_wire_file);
+ json_decref (ret);
+ return NULL;
+ }
+ GNUNET_free (sepa_wire_file);
+ return ret;
+}
+
+
+/**
+ * Sign wire transfer details in the plugin-specific format.
+ *
+ * @param cls closure
+ * @param in wire transfer details in JSON format
+ * @param key private signing key to use
+ * @param salt salt to add
+ * @param[out] sig where to write the signature
+ * @return #GNUNET_OK on success
+ */
+static int
+sepa_sign_wire_details (void *cls,
+ const json_t *in,
+ const struct TALER_MasterPrivateKeyP *key,
+ const struct GNUNET_HashCode *salt,
+ struct TALER_MasterSignatureP *sig)
+{
+ struct TALER_MasterWireDetailsPS wsd;
+ const char *sepa_name;
+ const char *iban;
+ const char *bic;
+ const char *type;
+ json_error_t err;
+
+ if (0 !=
+ json_unpack_ex ((json_t *) in,
+ &err,
+ 0 /* flags */,
+ "{s:s, s:s, s:s, s:s}",
+ "type", &type,
+ "name", &sepa_name,
+ "iban", &iban,
+ "bic", &bic))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to unpack JSON: %s (at %u)\n",
+ err.text,
+ err.position);
+ return GNUNET_SYSERR;
+ }
+ if (0 != strcmp (type,
+ "sepa"))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "`type' must be `sepa' for SEPA wire details\n");
+ return GNUNET_SYSERR;
+ }
+ if (1 != validate_iban (iban))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "IBAN invalid in SEPA wire details\n");
+ return GNUNET_SYSERR;
+ }
+ compute_purpose (sepa_name,
+ iban,
+ bic,
+ &wsd);
+ GNUNET_CRYPTO_eddsa_sign (&key->eddsa_priv,
+ &wsd.purpose,
+ &sig->eddsa_signature);
+ return GNUNET_OK;
+}
+
+
+/**
* Prepare for exeuction of a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
@@ -499,22 +733,26 @@ libtaler_plugin_wire_sepa_init (void *cls)
struct TALER_WIRE_Plugin *plugin;
sc = GNUNET_new (struct SepaClosure);
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "mint",
- "CURRENCY",
- &sc->currency))
+ if (NULL != cfg)
{
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "mint",
- "CURRENCY");
- GNUNET_free (sc);
- return NULL;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "exchange",
+ "CURRENCY",
+ &sc->currency))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "CURRENCY");
+ GNUNET_free (sc);
+ return NULL;
+ }
}
-
plugin = GNUNET_new (struct TALER_WIRE_Plugin);
plugin->cls = sc;
plugin->amount_round = &sepa_amount_round;
+ plugin->get_wire_details = &sepa_get_wire_details;
+ plugin->sign_wire_details = &sepa_sign_wire_details;
plugin->wire_validate = &sepa_wire_validate;
plugin->prepare_wire_transfer = &sepa_prepare_wire_transfer;
plugin->prepare_wire_transfer_cancel = &sepa_prepare_wire_transfer_cancel;
@@ -536,7 +774,7 @@ libtaler_plugin_wire_sepa_done (void *cls)
struct TALER_WIRE_Plugin *plugin = cls;
struct SepaClosure *sc = plugin->cls;
- GNUNET_free (sc->currency);
+ GNUNET_free_non_null (sc->currency);
GNUNET_free (sc);
GNUNET_free (plugin);
return NULL;
diff --git a/src/wire/plugin_wire_template.c b/src/wire/plugin_wire_template.c
index baf0ee7d5..46908c297 100644
--- a/src/wire/plugin_wire_template.c
+++ b/src/wire/plugin_wire_template.c
@@ -74,13 +74,36 @@ template_amount_round (void *cls,
/**
+ * Obtain wire transfer details in the plugin-specific format
+ * from the configuration.
+ *
+ * @param cls closure
+ * @param cfg configuration with details about wire accounts
+ * @param account_name which section in the configuration should we parse
+ * @return NULL if @a cfg fails to have valid wire details for @a account_name
+ */
+static json_t *
+template_get_wire_details (void *cls,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *account_name)
+{
+ GNUNET_break (0);
+ return NULL;
+}
+
+
+/**
* Check if the given wire format JSON object is correctly formatted
*
+ * @param cls the @e cls of this struct with the plugin-specific state
* @param wire the JSON wire format object
+ * @param master_pub public key of the exchange to verify against
* @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
*/
static int
-template_wire_validate (const json_t *wire)
+template_wire_validate (void *cls,
+ const json_t *wire,
+ const struct TALER_MasterPublicKeyP *master_pub)
{
GNUNET_break (0);
return GNUNET_SYSERR;
@@ -149,6 +172,28 @@ template_execute_wire_transfer (void *cls,
/**
+ * Sign wire transfer details in the plugin-specific format.
+ *
+ * @param cls closure
+ * @param in wire transfer details in JSON format
+ * @param key private signing key to use
+ * @param salt salt to add
+ * @param[out] sig where to write the signature
+ * @return #GNUNET_OK on success
+ */
+static int
+template_sign_wire_details (void *cls,
+ const json_t *in,
+ const struct TALER_MasterPrivateKeyP *key,
+ const struct GNUNET_HashCode *salt,
+ struct TALER_MasterSignatureP *sig)
+{
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+}
+
+
+/**
* Abort execution of a wire transfer. For example, because we are
* shutting down. Note that if an execution is aborted, it may or
* may not still succeed. The caller MUST run @e
@@ -197,12 +242,12 @@ libtaler_plugin_wire_template_init (void *cls)
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
- "mint",
+ "exchange",
"CURRENCY",
&tc->currency))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "mint",
+ "exchange",
"CURRENCY");
GNUNET_free (tc->bank_uri);
GNUNET_free (tc);
@@ -212,6 +257,8 @@ libtaler_plugin_wire_template_init (void *cls)
plugin = GNUNET_new (struct TALER_WIRE_Plugin);
plugin->cls = tc;
plugin->amount_round = &template_amount_round;
+ plugin->get_wire_details = &template_get_wire_details;
+ plugin->sign_wire_details = &template_sign_wire_details;
plugin->wire_validate = &template_wire_validate;
plugin->prepare_wire_transfer = &template_prepare_wire_transfer;
plugin->prepare_wire_transfer_cancel = &template_prepare_wire_transfer_cancel;
diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c
index 8a6f98ea2..d467b7bdd 100644
--- a/src/wire/plugin_wire_test.c
+++ b/src/wire/plugin_wire_test.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2016 GNUnet e.V.
+ Copyright (C) 2016 GNUnet e.V. & Inria
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
@@ -22,6 +22,7 @@
#include "platform.h"
#include "taler_wire_plugin.h"
#include "taler_bank_service.h"
+#include "taler_signatures.h"
/* only for HTTP status codes */
#include <microhttpd.h>
@@ -34,20 +35,31 @@ struct TestClosure
{
/**
- * Handle to the bank for sending funds to the bank.
+ * Which currency do we support?
*/
- struct TALER_BANK_Context *bank;
+ char *currency;
/**
- * Which currency do we support?
+ * URI of our bank.
*/
- char *currency;
+ char *bank_uri;
+
+ /**
+ * Handle to the bank for sending funds to the bank.
+ */
+ struct TALER_BANK_Context *bank;
/**
* Handle to the bank task, or NULL.
*/
struct GNUNET_SCHEDULER_Task *bt;
+ /**
+ * Number of the account that the exchange has at the bank for
+ * outgoing transfers.
+ */
+ unsigned long long exchange_account_outgoing_no;
+
};
@@ -124,11 +136,9 @@ struct TALER_WIRE_ExecuteHandle
* scheduler.
*
* @param cls our `struct TestClosure`
- * @param tc scheduler context (unused)
*/
static void
-context_task (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *sct)
+context_task (void *cls)
{
struct TestClosure *tc = cls;
long timeout;
@@ -171,7 +181,7 @@ context_task (void *cls,
rs,
ws,
&context_task,
- cls);
+ tc);
GNUNET_NETWORK_fdset_destroy (rs);
GNUNET_NETWORK_fdset_destroy (ws);
}
@@ -210,6 +220,13 @@ test_amount_round (void *cls,
struct TestClosure *tc = cls;
uint32_t delta;
+ if (NULL == tc->currency)
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "CURRENCY");
+ return GNUNET_SYSERR; /* not configured with currency */
+ }
if (0 != strcasecmp (amount->currency,
tc->currency))
{
@@ -226,27 +243,69 @@ test_amount_round (void *cls,
/**
+ * Compute purpose for signing.
+ *
+ * @param account number of the account
+ * @param bank_uri URI of the bank
+ * @param[out] mp purpose to be signed
+ */
+static void
+compute_purpose (uint64_t account,
+ const char *bank_uri,
+ struct TALER_MasterWireDetailsPS *wsd)
+{
+ struct GNUNET_HashContext *hc;
+ uint64_t n = GNUNET_htonll (account);
+
+ wsd->purpose.size = htonl (sizeof (struct TALER_MasterWireDetailsPS));
+ wsd->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_TEST_DETAILS);
+ hc = GNUNET_CRYPTO_hash_context_start ();
+ GNUNET_CRYPTO_hash_context_read (hc,
+ "test",
+ strlen ("test") + 1);
+ GNUNET_CRYPTO_hash_context_read (hc,
+ &n,
+ sizeof (n));
+ GNUNET_CRYPTO_hash_context_read (hc,
+ bank_uri,
+ strlen (bank_uri) + 1);
+ GNUNET_CRYPTO_hash_context_finish (hc,
+ &wsd->h_sepa_details);
+}
+
+
+/**
* Check if the given wire format JSON object is correctly formatted.
* Right now, the only thing we require is a field
* "account_number" which must contain a positive 53-bit integer.
*
+ * @param cls the @e cls of this struct with the plugin-specific state
* @param wire the JSON wire format object
+ * @param master_pub public key of the exchange to verify against
* @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
*/
static int
-test_wire_validate (const json_t *wire)
+test_wire_validate (void *cls,
+ const json_t *wire,
+ const struct TALER_MasterPublicKeyP *master_pub)
{
+ struct TestClosure *tc = cls;
json_error_t error;
json_int_t account_no;
+ const char *bank_uri;
+ const char *sig_s;
+ struct TALER_MasterWireDetailsPS wsd;
+ struct TALER_MasterSignatureP sig;
if (0 !=
json_unpack_ex ((json_t *) wire,
&error,
0,
- "{s:I}",
- "account_number", &account_no))
+ "{s:I, s:s}",
+ "account_number", &account_no,
+ "bank_uri", &bank_uri))
{
- GNUNET_break (0);
+ GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if ( (account_no < 0) ||
@@ -255,10 +314,119 @@ test_wire_validate (const json_t *wire)
GNUNET_break (0);
return GNUNET_SYSERR;
}
+ if ( (NULL != tc->bank_uri) &&
+ (0 != strcmp (bank_uri,
+ tc->bank_uri)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Wire specifies bank URI %s, but this exchange only supports %s\n",
+ bank_uri,
+ tc->bank_uri);
+ return GNUNET_NO;
+ }
+ if (NULL == master_pub)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Skipping signature check as master public key not given\n");
+ return GNUNET_OK;
+ }
+ if (0 !=
+ json_unpack_ex ((json_t *) wire,
+ &error,
+ 0,
+ "{s:s}",
+ "sig", &sig_s))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Signature check required, but signature is missing\n");
+ return GNUNET_NO;
+ }
+ compute_purpose (account_no,
+ bank_uri,
+ &wsd);
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (sig_s,
+ strlen (sig_s),
+ &sig,
+ sizeof (sig)))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_TEST_DETAILS,
+ &wsd.purpose,
+ &sig.eddsa_signature,
+ &master_pub->eddsa_pub))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
return GNUNET_YES;
}
+/**
+ * Obtain wire transfer details in the plugin-specific format
+ * from the configuration.
+ *
+ * @param cls closure
+ * @param cfg configuration with details about wire accounts
+ * @param account_name which section in the configuration should we parse
+ * @return NULL if @a cfg fails to have valid wire details for @a account_name
+ */
+static json_t *
+test_get_wire_details (void *cls,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *account_name)
+{
+ struct TestClosure *tc = cls;
+ char *test_wire_file;
+ json_error_t err;
+ json_t *ret;
+
+ /* Fetch reply */
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (cfg,
+ account_name,
+ "TEST_RESPONSE_FILE",
+ &test_wire_file))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ account_name,
+ "TEST_RESPONSE_FILE");
+ return NULL;
+ }
+ ret = json_load_file (test_wire_file,
+ JSON_REJECT_DUPLICATES,
+ &err);
+ if (NULL == ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to parse JSON in %s: %s (%s:%u)\n",
+ test_wire_file,
+ err.text,
+ err.source,
+ err.line);
+ GNUNET_free (test_wire_file);
+ return NULL;
+ }
+ if (GNUNET_YES != test_wire_validate (tc,
+ ret,
+ NULL))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to validate TEST wire data in %s\n",
+ test_wire_file);
+ GNUNET_free (test_wire_file);
+ json_decref (ret);
+ return NULL;
+ }
+ GNUNET_free (test_wire_file);
+ return ret;
+}
+
+
GNUNET_NETWORK_STRUCT_BEGIN
/**
* Format we used for serialized transaction data.
@@ -287,11 +455,9 @@ GNUNET_NETWORK_STRUCT_END
* callback with the serialized state.
*
* @param cls the `struct TALER_WIRE_PrepareHandle`
- * @param sct unused
*/
static void
-do_prepare (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *sct)
+do_prepare (void *cls)
{
struct TALER_WIRE_PrepareHandle *pth = cls;
char *wire_enc;
@@ -363,9 +529,11 @@ test_prepare_wire_transfer (void *cls,
struct TALER_WIRE_PrepareHandle *pth;
if (GNUNET_YES !=
- test_wire_validate (wire))
+ test_wire_validate (tc,
+ wire,
+ NULL))
{
- GNUNET_break (0);
+ GNUNET_break_op (0);
return NULL;
}
pth = GNUNET_new (struct TALER_WIRE_PrepareHandle);
@@ -406,27 +574,100 @@ test_prepare_wire_transfer_cancel (void *cls,
* @param cls closure with the `struct TALER_WIRE_ExecuteHandle`
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
* 0 if the bank's reply is bogus (fails to follow the protocol)
+ * @param json detailed response from the HTTPD, or NULL if reply was not JSON
*/
static void
execute_cb (void *cls,
- unsigned int http_status)
+ unsigned int http_status,
+ json_t *json)
{
struct TALER_WIRE_ExecuteHandle *eh = cls;
- char s[14];
+ json_t *reason;
+ const char *emsg;
+ char *s;
eh->aaih = NULL;
- GNUNET_snprintf (s,
- sizeof (s),
- "%u",
- http_status);
+ emsg = NULL;
+ if (NULL != json)
+ {
+ reason = json_object_get (json,
+ "reason");
+ if (NULL != reason)
+ emsg = json_string_value (reason);
+ }
+ if (NULL != emsg)
+ GNUNET_asprintf (&s,
+ "%u (%s)",
+ http_status,
+ emsg);
+ else
+ GNUNET_asprintf (&s,
+ "%u",
+ http_status);
eh->cc (eh->cc_cls,
(MHD_HTTP_OK == http_status) ? GNUNET_OK : GNUNET_SYSERR,
(MHD_HTTP_OK == http_status) ? NULL : s);
+ GNUNET_free (s);
GNUNET_free (eh);
}
/**
+ * Sign wire transfer details in the plugin-specific format.
+ *
+ * @param cls closure
+ * @param in wire transfer details in JSON format
+ * @param key private signing key to use
+ * @param salt salt to add
+ * @param[out] sig where to write the signature
+ * @return #GNUNET_OK on success
+ */
+static int
+test_sign_wire_details (void *cls,
+ const json_t *in,
+ const struct TALER_MasterPrivateKeyP *key,
+ const struct GNUNET_HashCode *salt,
+ struct TALER_MasterSignatureP *sig)
+{
+ struct TALER_MasterWireDetailsPS wsd;
+ const char *bank_uri;
+ const char *type;
+ json_int_t account;
+ json_error_t err;
+
+ if (0 !=
+ json_unpack_ex ((json_t *) in,
+ &err,
+ 0 /* flags */,
+ "{s:s, s:s, s:I}",
+ "type", &type,
+ "bank_uri", &bank_uri,
+ "account_number", &account))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to unpack JSON: %s (at %u)\n",
+ err.text,
+ err.position);
+ return GNUNET_SYSERR;
+ }
+ if (0 != strcmp (type,
+ "test"))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "`type' must be `test' for test wire details\n");
+ return GNUNET_SYSERR;
+ }
+ compute_purpose (account,
+ bank_uri,
+ &wsd);
+ GNUNET_CRYPTO_eddsa_sign (&key->eddsa_priv,
+ &wsd.purpose,
+ &sig->eddsa_signature);
+ return GNUNET_OK;
+}
+
+
+/**
* Execute a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
@@ -451,6 +692,12 @@ test_execute_wire_transfer (void *cls,
json_int_t account_no;
struct BufFormatP bf;
+ if (NULL == tc->bank)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Bank not initialized, cannot do transfers!\n");
+ return NULL; /* not initialized with configuration, cannot do transfers */
+ }
if ( (buf_size <= sizeof (struct BufFormatP)) ||
('\0' != buf[buf_size -1]) )
{
@@ -471,7 +718,9 @@ test_execute_wire_transfer (void *cls,
return NULL;
}
GNUNET_assert (GNUNET_YES ==
- test_wire_validate (wire));
+ test_wire_validate (tc,
+ wire,
+ NULL));
if (0 !=
json_unpack_ex (wire,
&error,
@@ -482,13 +731,14 @@ test_execute_wire_transfer (void *cls,
GNUNET_break (0);
return NULL;
}
-
+
eh = GNUNET_new (struct TALER_WIRE_ExecuteHandle);
eh->cc = cc;
eh->cc_cls = cc_cls;
eh->aaih = TALER_BANK_admin_add_incoming (tc->bank,
&bf.wtid,
&amount,
+ (uint64_t) tc->exchange_account_outgoing_no,
(uint64_t) account_no,
&execute_cb,
eh);
@@ -537,45 +787,63 @@ libtaler_plugin_wire_test_init (void *cls)
struct GNUNET_CONFIGURATION_Handle *cfg = cls;
struct TestClosure *tc;
struct TALER_WIRE_Plugin *plugin;
- char *uri;
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "wire-test",
- "bank_uri",
- &uri))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "wire-test",
- "bank_uri");
- return NULL;
- }
tc = GNUNET_new (struct TestClosure);
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "mint",
- "CURRENCY",
- &tc->currency))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "mint",
- "CURRENCY");
- GNUNET_free (uri);
- GNUNET_free (tc);
- return NULL;
- }
- tc->bank = TALER_BANK_init (uri);
- if (NULL == tc->bank)
+ if (NULL != cfg)
{
- GNUNET_break (0);
- GNUNET_free (tc->currency);
- GNUNET_free (tc);
- return NULL;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "wire-outgoing-test",
+ "BANK_URI",
+ &tc->bank_uri))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "wire-outgoing-test",
+ "BANK_URI");
+ GNUNET_free (tc);
+ return NULL;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg,
+ "wire-outgoing-test",
+ "EXCHANGE_ACCOUNT_NUMBER",
+ &tc->exchange_account_outgoing_no))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "wire-outgoing-test",
+ "EXCHANGE_ACCOUNT_NUMBER");
+ GNUNET_free (tc->bank_uri);
+ GNUNET_free (tc);
+ return NULL;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "exchange",
+ "CURRENCY",
+ &tc->currency))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "CURRENCY");
+ GNUNET_free (tc->bank_uri);
+ GNUNET_free (tc);
+ return NULL;
+ }
+ tc->bank = TALER_BANK_init (tc->bank_uri);
+ if (NULL == tc->bank)
+ {
+ GNUNET_break (0);
+ GNUNET_free (tc->currency);
+ GNUNET_free (tc->bank_uri);
+ GNUNET_free (tc);
+ return NULL;
+ }
}
-
plugin = GNUNET_new (struct TALER_WIRE_Plugin);
plugin->cls = tc;
plugin->amount_round = &test_amount_round;
+ plugin->get_wire_details = &test_get_wire_details;
+ plugin->sign_wire_details = &test_sign_wire_details;
plugin->wire_validate = &test_wire_validate;
plugin->prepare_wire_transfer = &test_prepare_wire_transfer;
plugin->prepare_wire_transfer_cancel = &test_prepare_wire_transfer_cancel;
@@ -602,8 +870,13 @@ libtaler_plugin_wire_test_done (void *cls)
GNUNET_SCHEDULER_cancel (tc->bt);
tc->bt = NULL;
}
- TALER_BANK_fini (tc->bank);
- GNUNET_free (tc->currency);
+ if (NULL != tc->bank)
+ {
+ TALER_BANK_fini (tc->bank);
+ tc->bank = NULL;
+ }
+ GNUNET_free_non_null (tc->currency);
+ GNUNET_free_non_null (tc->bank_uri);
GNUNET_free (tc);
GNUNET_free (plugin);
return NULL;
diff --git a/src/wire/test_sepa_wireformat.c b/src/wire/test_sepa_wireformat.c
index edbe5bc45..cd31a971c 100644
--- a/src/wire/test_sepa_wireformat.c
+++ b/src/wire/test_sepa_wireformat.c
@@ -28,37 +28,37 @@
/* Valid SEPA data */
static const char * const valid_wire_str =
"{ \"type\":\"SEPA\", \
-\"IBAN\":\"DE67830654080004822650\", \
+\"iban\":\"DE67830654080004822650\", \
\"name\":\"GNUnet e.V.\", \
\"bic\":\"GENODEF1SLR\", \
-\"r\":123456789, \
+\"salt\":\"123456789\", \
\"address\": \"foobar\"}";
/* IBAN has wrong country code */
static const char * const invalid_wire_str =
"{ \"type\":\"SEPA\", \
-\"IBAN\":\"XX67830654080004822650\", \
+\"iban\":\"XX67830654080004822650\", \
\"name\":\"GNUnet e.V.\", \
\"bic\":\"GENODEF1SLR\", \
-\"r\":123456789, \
+\"salt\":\"123456789\", \
\"address\": \"foobar\"}";
/* IBAN has wrong checksum */
static const char * const invalid_wire_str2 =
"{ \"type\":\"SEPA\", \
-\"IBAN\":\"DE67830654080004822651\", \
+\"iban\":\"DE67830654080004822651\", \
\"name\":\"GNUnet e.V.\", \
\"bic\":\"GENODEF1SLR\", \
-\"r\":123456789, \
+\"salt\":\"123456789\", \
\"address\": \"foobar\"}";
/* Unsupported wireformat type */
static const char * const unsupported_wire_str =
"{ \"type\":\"unsupported\", \
-\"IBAN\":\"DE67830654080004822650\", \
+\"iban\":\"DE67830654080004822650\", \
\"name\":\"GNUnet e.V.\", \
\"bic\":\"GENODEF1SLR\", \
-\"r\":123456789, \
+\"salt\":\"123456789\", \
\"address\": \"foobar\"}";
@@ -77,7 +77,7 @@ main(int argc,
NULL);
cfg = GNUNET_CONFIGURATION_create ();
GNUNET_CONFIGURATION_set_value_string (cfg,
- "mint",
+ "exchange",
"currency",
"EUR");
plugin = TALER_WIRE_plugin_load (cfg,
@@ -85,16 +85,24 @@ main(int argc,
GNUNET_assert (NULL != plugin);
(void) memset(&error, 0, sizeof(error));
GNUNET_assert (NULL != (wire = json_loads (unsupported_wire_str, 0, NULL)));
- GNUNET_assert (GNUNET_YES != plugin->wire_validate (wire));
+ GNUNET_assert (GNUNET_YES != plugin->wire_validate (NULL,
+ wire,
+ NULL));
json_decref (wire);
GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str, 0, NULL)));
- GNUNET_assert (GNUNET_NO == plugin->wire_validate (wire));
+ GNUNET_assert (GNUNET_NO == plugin->wire_validate (NULL,
+ wire,
+ NULL));
json_decref (wire);
GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str2, 0, NULL)));
- GNUNET_assert (GNUNET_NO == plugin->wire_validate (wire));
+ GNUNET_assert (GNUNET_NO == plugin->wire_validate (NULL,
+ wire,
+ NULL));
json_decref (wire);
GNUNET_assert (NULL != (wire = json_loads (valid_wire_str, 0, &error)));
- ret = plugin->wire_validate (wire);
+ ret = plugin->wire_validate (NULL,
+ wire,
+ NULL);
json_decref (wire);
TALER_WIRE_plugin_unload (plugin);
GNUNET_CONFIGURATION_destroy (cfg);
diff --git a/src/wire/test_wire_plugin.c b/src/wire/test_wire_plugin.c
new file mode 100644
index 000000000..27b4366c1
--- /dev/null
+++ b/src/wire/test_wire_plugin.c
@@ -0,0 +1,196 @@
+/*
+ This file is part of TALER
+ (C) 2015, 2016 GNUnet e.V. and Inria
+
+ 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
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file wire/test_wire_plugin.c
+ * @brief Tests for wire plugins
+ * @author Christian Grothoff
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_wire_lib.h"
+#include "taler_wire_plugin.h"
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+
+
+/**
+ * Definitions for a test with a plugin.
+ */
+struct TestBlock {
+
+ /**
+ * Name of the plugin to test.
+ */
+ const char *plugin_name;
+
+ /**
+ * JSON template expected by the plugin for an account definition.
+ */
+ const char *json_proto;
+
+};
+
+
+/**
+ * List of plugins and (unsigned) JSON account definitions
+ * to use for the tests.
+ */
+static struct TestBlock tests[] = {
+ { "sepa", "{ \"type\":\"sepa\", \"iban\":\"DE67830654080004822650\", \"name\":\"GNUnet e.V.\", \"bic\":\"GENODEF1SLR\" }" },
+ { "test", "{ \"type\":\"test\", \"bank_uri\":\"http://localhost/\", \"account_number\":42 }" },
+ { NULL, NULL }
+};
+
+
+/**
+ * Private key used to sign wire details.
+ */
+static struct TALER_MasterPrivateKeyP priv_key;
+
+/**
+ * Public key matching #priv_key.
+ */
+static struct TALER_MasterPublicKeyP pub_key;
+
+/**
+ * Our configuration.
+ */
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+
+/**
+ * Run the test.
+ *
+ * @param name of the test
+ * @param plugin plugin to test
+ * @param wire wire details for testing
+ * @return #GNUNET_OK on success
+ */
+static int
+run_test (const char *name,
+ struct TALER_WIRE_Plugin *plugin,
+ json_t *wire)
+{
+ struct GNUNET_HashCode salt;
+ struct TALER_MasterSignatureP sig;
+ json_t *lwire;
+
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
+ &salt,
+ sizeof (salt));
+ if (GNUNET_OK !=
+ plugin->sign_wire_details (plugin->cls,
+ wire,
+ &priv_key,
+ &salt,
+ &sig))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ json_object_set_new (wire,
+ "salt",
+ GNUNET_JSON_from_data (&salt,
+ sizeof (salt)));
+ json_object_set_new (wire,
+ "sig",
+ GNUNET_JSON_from_data (&sig,
+ sizeof (sig)));
+ if (GNUNET_OK !=
+ plugin->wire_validate (plugin->cls,
+ wire,
+ &pub_key))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ /* load wire details from file */
+ lwire = plugin->get_wire_details (plugin->cls,
+ cfg,
+ name);
+ if (NULL == lwire)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ plugin->wire_validate (plugin->cls,
+ lwire,
+ &pub_key))
+ {
+ GNUNET_break (0);
+ json_decref (lwire);
+ return GNUNET_SYSERR;
+ }
+ json_decref (lwire);
+ return GNUNET_OK;
+}
+
+
+int
+main (int argc,
+ const char *const argv[])
+{
+ json_t *wire;
+ int ret;
+ struct TALER_WIRE_Plugin *plugin;
+ const struct TestBlock *test;
+ unsigned int i;
+ struct GNUNET_CRYPTO_EddsaPrivateKey *pk;
+
+ GNUNET_log_setup ("test-wire-plugin",
+ "WARNING",
+ NULL);
+ cfg = GNUNET_CONFIGURATION_create ();
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONFIGURATION_load (cfg,
+ "test_wire_plugin.conf"));
+ pk = GNUNET_CRYPTO_eddsa_key_create_from_file ("test_wire_plugin_key.priv");
+ priv_key.eddsa_priv = *pk;
+ GNUNET_free (pk);
+ GNUNET_CRYPTO_eddsa_key_get_public (&priv_key.eddsa_priv,
+ &pub_key.eddsa_pub);
+ ret = GNUNET_OK;
+ for (i=0;NULL != (test = &tests[i])->plugin_name;i++)
+ {
+ plugin = TALER_WIRE_plugin_load (cfg,
+ test->plugin_name);
+ GNUNET_assert (NULL != plugin);
+ wire = json_loads (test->json_proto, 0, NULL);
+ GNUNET_assert (NULL != wire);
+ ret = run_test (test->plugin_name, plugin, wire);
+ json_decref (wire);
+ TALER_WIRE_plugin_unload (plugin);
+ if (GNUNET_OK != ret)
+ {
+ fprintf (stdout,
+ "%s FAILED\n",
+ test->plugin_name);
+ break;
+ }
+ else
+ {
+ fprintf (stdout,
+ "%s PASS\n",
+ test->plugin_name);
+ }
+ }
+ GNUNET_CONFIGURATION_destroy (cfg);
+ if (GNUNET_NO == ret)
+ return 1;
+ return 0;
+}
diff --git a/src/wire/test_wire_plugin.conf b/src/wire/test_wire_plugin.conf
new file mode 100644
index 000000000..ece816954
--- /dev/null
+++ b/src/wire/test_wire_plugin.conf
@@ -0,0 +1,21 @@
+# This file is in the public domain.
+#
+[test]
+# This is the response we give out for the /wire request. It provides
+# wallets with the bank information for transfers to the exchange.
+TEST_RESPONSE_FILE = test_wire_plugin_test.json
+
+[sepa]
+# This is the response we give out for the /wire request. It provides
+# wallets with the bank information for transfers to the exchange.
+SEPA_RESPONSE_FILE = test_wire_plugin_sepa.json
+
+
+[wire-outgoing-test]
+# For transfers made by the exchange, we need to know
+# the URI of the bank (where the /admin/add/incoming API
+# is avaialble).
+BANK_URI = http://localhost/
+
+[exchange]
+CURRENCY = "EUR"
diff --git a/src/wire/test_wire_plugin_key.priv b/src/wire/test_wire_plugin_key.priv
new file mode 100644
index 000000000..26b4f26f6
--- /dev/null
+++ b/src/wire/test_wire_plugin_key.priv
@@ -0,0 +1 @@
+?Sgb@Js; %aKȉs_Hў \ No newline at end of file
diff --git a/src/wire/test_wire_plugin_sepa.json b/src/wire/test_wire_plugin_sepa.json
new file mode 100644
index 000000000..175345f0c
--- /dev/null
+++ b/src/wire/test_wire_plugin_sepa.json
@@ -0,0 +1,8 @@
+{
+ "salt": "32V01R7K4T02S74PZZMVXRQ1K7FR948RBNB9BJ5Z101HEQFH7CW7J82006GY3BPTGQ4FM775PSSRD3K9MY97HSNVVCGEVBPVSAQ2710",
+ "type": "sepa",
+ "iban": "DE67830654080004822650",
+ "sig": "K48GPPM715ZXX0DC597WESD5ECT3R0B3TAFQMB68SBF4K5CZ5KCE9NESN1JX412SPZ82PSV7JAPVJFXDDTZ63YV4295S5RC28E4221G",
+ "name": "GNUnet e.V.",
+ "bic": "GENODEF1SLR"
+} \ No newline at end of file
diff --git a/src/wire/test_wire_plugin_test.json b/src/wire/test_wire_plugin_test.json
new file mode 100644
index 000000000..6fe6b2359
--- /dev/null
+++ b/src/wire/test_wire_plugin_test.json
@@ -0,0 +1,7 @@
+{
+ "type": "test",
+ "bank_uri": "http://localhost/",
+ "sig": "KX1CMHNFH1WE10244AEF07AXHJCF9PZDZVNZBC9P4EJEQ1MH1Y3C2TWF08VTQMK4N5TCV0V1VTGWSV0WB8TB9YQRZW87F5A6KCEZ81R",
+ "account_number": 42,
+ "salt": "EZV905MQPVAZEMGC6SEZQF2Z75P6ZKTN8TX00JHN11S7J81DQ78G8Z551K6TGR9WHPP0JW1X9J9X9CVRY48JTHBCP6Q4XKJ6R2G18G0"
+} \ No newline at end of file
diff --git a/src/wire/wire-sepa.conf b/src/wire/wire-sepa.conf
new file mode 100644
index 000000000..7321a2be6
--- /dev/null
+++ b/src/wire/wire-sepa.conf
@@ -0,0 +1,10 @@
+# Configuration for SEPA wire plugin.
+
+[wire-incoming-sepa]
+# This is the response we give out for the /wire request. It provides
+# wallets with the bank information for transfers to the exchange.
+SEPA_RESPONSE_FILE = ${TALER_CONFIG_HOME}/sepa.json
+
+[wire-outgoing-sepa]
+# This section should contain the options required for making outgoing
+# SEPA transfers. Not yet supported (need libebics).
diff --git a/src/wire/wire-test.conf b/src/wire/wire-test.conf
new file mode 100644
index 000000000..98e486acb
--- /dev/null
+++ b/src/wire/wire-test.conf
@@ -0,0 +1,16 @@
+# This file is in the public domain.
+#
+[wire-incoming-test]
+# This is the response we give out for the /wire request. It provides
+# wallets with the bank information for transfers to the exchange.
+TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json
+
+[wire-outgoing-test]
+# For outgoing transfers, we need to know the exchange's
+# account number at the bank.
+EXCHANGE_ACCOUNT_NUMBER = 2
+
+# For transfers made by the exchange, we need to know
+# the URI of the bank (where the /admin/add/incoming API
+# is avaialble).
+# BANK_URI = https://bank.demo.taler.net/