summaryrefslogtreecommitdiff
path: root/src/wire/plugin_wire_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/wire/plugin_wire_test.c')
-rw-r--r--src/wire/plugin_wire_test.c393
1 files changed, 333 insertions, 60 deletions
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;