summaryrefslogtreecommitdiff
path: root/src/exchange-tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchange-tools')
-rw-r--r--src/exchange-tools/.gitignore6
-rw-r--r--src/exchange-tools/Makefile.am67
-rw-r--r--src/exchange-tools/exchange-offline.conf15
-rw-r--r--src/exchange-tools/key-helper.c132
-rw-r--r--src/exchange-tools/taler-auditor-offline.c1496
-rw-r--r--src/exchange-tools/taler-exchange-dbinit.c132
-rw-r--r--src/exchange-tools/taler-exchange-keycheck.c336
-rw-r--r--src/exchange-tools/taler-exchange-keyup.c1513
-rw-r--r--src/exchange-tools/taler-exchange-offline.c5497
-rw-r--r--src/exchange-tools/taler-exchange-wire.c220
-rw-r--r--src/exchange-tools/test_taler_exchange_httpd.conf130
-rw-r--r--src/exchange-tools/test_taler_exchange_httpd_home/.config/taler/test.json8
-rw-r--r--src/exchange-tools/test_taler_exchange_httpd_home/.local/share/taler/exchange/offline-keys/master.priv1
-rwxr-xr-xsrc/exchange-tools/test_taler_exchange_keyup.sh142
14 files changed, 7142 insertions, 2553 deletions
diff --git a/src/exchange-tools/.gitignore b/src/exchange-tools/.gitignore
index 6e9e12faf..69279d792 100644
--- a/src/exchange-tools/.gitignore
+++ b/src/exchange-tools/.gitignore
@@ -1,3 +1,3 @@
-test_taler_exchange_httpd_home/.local/share/taler/exchange/live-keys/
-test_taler_exchange_httpd_home/.local/share/taler/exchange/wirefees/
-test_taler_exchange_httpd_home/.config/taler/account-1.json
+taler-exchange-offline
+taler-auditor-offline
+taler-crypto-worker
diff --git a/src/exchange-tools/Makefile.am b/src/exchange-tools/Makefile.am
index 2327d8111..955544564 100644
--- a/src/exchange-tools/Makefile.am
+++ b/src/exchange-tools/Makefile.am
@@ -4,7 +4,8 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/include
pkgcfgdir = $(prefix)/share/taler/config.d/
pkgcfg_DATA = \
- coins.conf
+ coins.conf \
+ exchange-offline.conf
if USE_COVERAGE
AM_CFLAGS = --coverage -O0
@@ -12,44 +13,37 @@ if USE_COVERAGE
endif
bin_PROGRAMS = \
- taler-exchange-keyup \
- taler-exchange-keycheck \
- taler-exchange-wire \
+ taler-auditor-offline \
+ taler-exchange-offline \
taler-exchange-dbinit
-taler_exchange_keyup_SOURCES = \
- taler-exchange-keyup.c
-taler_exchange_keyup_LDADD = \
- $(LIBGCRYPT_LIBS) \
- $(top_builddir)/src/util/libtalerutil.la \
- $(top_builddir)/src/pq/libtalerpq.la \
- $(top_builddir)/src/bank-lib/libtalerbank.la \
- $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
- -lgnunetutil $(XLIB)
-taler_exchange_keyup_LDFLAGS = $(POSTGRESQL_LDFLAGS)
-
-
-taler_exchange_wire_SOURCES = \
- taler-exchange-wire.c
-taler_exchange_wire_LDADD = \
+taler_exchange_offline_SOURCES = \
+ taler-exchange-offline.c
+taler_exchange_offline_LDADD = \
$(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/lib/libtalerexchange.la \
$(top_builddir)/src/json/libtalerjson.la \
- $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
- $(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/extensions/libtalerextensions.la \
-lgnunetjson \
+ -lgnunetcurl \
+ -ljansson \
-lgnunetutil \
- -ljansson $(XLIB)
-taler_exchange_wire_LDFLAGS = $(POSTGRESQL_LDFLAGS)
+ $(XLIB)
-taler_exchange_keycheck_SOURCES = \
- taler-exchange-keycheck.c
-taler_exchange_keycheck_LDADD = \
+taler_auditor_offline_SOURCES = \
+ taler-auditor-offline.c
+taler_auditor_offline_LDADD = \
$(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/lib/libtalerexchange.la \
+ $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
- $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
- -lgnunetutil $(XLIB)
-taler_exchange_keycheck_LDFLAGS = $(POSTGRESQL_LDFLAGS)
+ $(top_builddir)/src/extensions/libtalerextensions.la \
+ -lgnunetjson \
+ -lgnunetcurl \
+ -ljansson \
+ -lgnunetutil \
+ $(XLIB)
taler_exchange_dbinit_SOURCES = \
taler-exchange-dbinit.c
@@ -58,9 +52,8 @@ taler_exchange_dbinit_LDADD = \
$(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/pq/libtalerpq.la \
$(top_builddir)/src/exchangedb/libtalerexchangedb.la \
- -lgnunetutil $(XLIB)
-taler_exchange_dbinit_LDFLAGS = \
- $(POSTGRESQL_LDFLAGS)
+ -lgnunetutil \
+ $(XLIB)
taler_exchange_dbinit_CPPFLAGS = \
-I$(top_srcdir)/src/include \
-I$(top_srcdir)/src/pq/ \
@@ -71,17 +64,7 @@ taler_exchange_dbinit_CPPFLAGS = \
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
-check_SCRIPTS = \
- test_taler_exchange_keyup.sh
-
-TESTS = \
- $(check_SCRIPTS)
-
# Distribution
EXTRA_DIST = \
- test_taler_exchange_httpd_home/.local/share/taler/exchange/offline-keys/master.priv \
- test_taler_exchange_httpd.conf \
- key-helper.c \
- $(check_SCRIPTS) \
$(pkgcfg_DATA)
diff --git a/src/exchange-tools/exchange-offline.conf b/src/exchange-tools/exchange-offline.conf
new file mode 100644
index 000000000..020eb34ba
--- /dev/null
+++ b/src/exchange-tools/exchange-offline.conf
@@ -0,0 +1,15 @@
+# This file is in the public domain.
+#
+[exchange-offline]
+
+# Where do we store the offline master private key of the exchange?
+MASTER_PRIV_FILE = ${TALER_DATA_HOME}exchange-offline/master.priv
+
+# Where do we store the TOFU key material?
+SECM_TOFU_FILE = ${TALER_DATA_HOME}exchange-offline/secm_tofus.pub
+
+# Base32-encoded public key of the RSA helper.
+# SECM_DENOM_PUBKEY =
+
+# Base32-encoded public key of the EdDSA helper.
+# SECM_ESIGN_PUBKEY =
diff --git a/src/exchange-tools/key-helper.c b/src/exchange-tools/key-helper.c
deleted file mode 100644
index f711a1c28..000000000
--- a/src/exchange-tools/key-helper.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2015-2020 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
- 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, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file key-helper.c
- * @brief shared logic between tools that deal with the master private key
- * @author Christian Grothoff
- */
-
-/**
- * Extract the @a master_priv from the @a cfg or @a masterkeyfile and
- * verify that it matches the master public key given in @a cfg.
- *
- * @param cfg configuration to use
- * @param masterkeyfile master private key filename, can be NULL to use from @a cfg
- * @param[out] master_priv where to store the master private key on success
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on failures
- */
-static int
-get_and_check_master_key (const struct GNUNET_CONFIGURATION_Handle *cfg,
- const char *masterkeyfile,
- struct TALER_MasterPrivateKeyP *master_priv)
-{
- struct GNUNET_CRYPTO_EddsaPublicKey mpub;
- struct GNUNET_CRYPTO_EddsaPublicKey mpub_cfg;
- char *fn;
-
- if (NULL != masterkeyfile)
- {
- fn = GNUNET_strdup (masterkeyfile);
- }
- else
- {
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_filename (cfg,
- "exchange",
- "MASTER_PRIV_FILE",
- &fn))
- {
- fprintf (stderr,
- "Master private key file given neither in configuration nor on command-line\n");
- return GNUNET_SYSERR;
- }
- }
- if (GNUNET_YES !=
- GNUNET_DISK_file_test (fn))
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Exchange master private key `%s' does not exist yet, creating it!\n",
- fn);
- {
- int ret;
-
- ret = GNUNET_CRYPTO_eddsa_key_from_file (fn,
- GNUNET_YES,
- &master_priv->eddsa_priv);
- if (GNUNET_SYSERR == ret)
- {
- fprintf (stderr,
- "Failed to initialize master key from file `%s': %s\n",
- fn,
- (GNUNET_NO == ret)
- ? "file exists"
- : "could not create file");
- GNUNET_free (fn);
- return GNUNET_SYSERR;
- }
- GNUNET_CRYPTO_eddsa_key_get_public (&master_priv->eddsa_priv,
- &mpub);
- }
-
- /* Check our key matches that in the configuration */
- {
- char *masters;
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "exchange",
- "MASTER_PUBLIC_KEY",
- &masters))
- {
- /* Help user by telling them precisely what to fix */
- masters = GNUNET_STRINGS_data_to_string_alloc (&mpub,
- sizeof (mpub));
- fprintf (stderr,
- "You must set MASTER_PUBLIC_KEY to `%s' in the [exchange] section of the configuration before proceeding.\n",
- masters);
- GNUNET_free (masters);
- GNUNET_free (fn);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (masters,
- strlen (masters),
- &mpub_cfg,
- sizeof (mpub_cfg)))
- {
- fprintf (stderr,
- "MASTER_PUBLIC_KEY value `%s' specified in section [exchange] of the configuration is a valid public key\n",
- masters);
- GNUNET_free (masters);
- GNUNET_free (fn);
- return GNUNET_SYSERR;
- }
- if (0 != GNUNET_memcmp (&mpub,
- &mpub_cfg))
- {
- fprintf (stderr,
- "MASTER_PUBLIC_KEY value `%s' specified in section [exchange] of the configuration does not match our master private key. You can use `gnunet-ecc -p \"%s\"' to determine the correct value.\n",
- masters,
- fn);
- GNUNET_free (masters);
- GNUNET_free (fn);
- return GNUNET_SYSERR;
- }
- GNUNET_free (masters);
- }
- GNUNET_free (fn);
-
- return GNUNET_OK;
-}
diff --git a/src/exchange-tools/taler-auditor-offline.c b/src/exchange-tools/taler-auditor-offline.c
new file mode 100644
index 000000000..8c280d46b
--- /dev/null
+++ b/src/exchange-tools/taler-auditor-offline.c
@@ -0,0 +1,1496 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-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 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, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-auditor-offline.c
+ * @brief Support for operations involving the auditor's (offline) key.
+ * @author Christian Grothoff
+ */
+#include <platform.h>
+#include <gnunet/gnunet_json_lib.h>
+#include "taler_json_lib.h"
+#include "taler_exchange_service.h"
+
+/**
+ * Name of the input of a denomination key signature for the 'upload' operation.
+ * The "auditor-" prefix ensures that there is no ambiguity between
+ * taler-exchange-offline and taler-auditor-offline JSON formats.
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_SIGN_DENOMINATION "auditor-sign-denomination-0"
+
+/**
+ * Name of the input for the 'sign' and 'show' operations.
+ * The "auditor-" prefix ensures that there is no ambiguity between
+ * taler-exchange-offline and taler-auditor-offline JSON formats.
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_INPUT_KEYS "auditor-keys-0"
+
+/**
+ * Show the offline signing key.
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_SETUP "auditor-setup-0"
+
+/**
+ * Our private key, initialized in #load_offline_key().
+ */
+static struct TALER_AuditorPrivateKeyP auditor_priv;
+
+/**
+ * Our private key, initialized in #load_offline_key().
+ */
+static struct TALER_AuditorPublicKeyP auditor_pub;
+
+/**
+ * Base URL of this auditor's REST endpoint.
+ */
+static char *auditor_url;
+
+/**
+ * Exchange's master public key.
+ */
+static struct TALER_MasterPublicKeyP master_pub;
+
+/**
+ * Our context for making HTTP requests.
+ */
+static struct GNUNET_CURL_Context *ctx;
+
+/**
+ * Reschedule context for #ctx.
+ */
+static struct GNUNET_CURL_RescheduleContext *rc;
+
+/**
+ * Handle to the exchange's configuration
+ */
+static const struct GNUNET_CONFIGURATION_Handle *kcfg;
+
+/**
+ * Return value from main().
+ */
+static int global_ret;
+
+/**
+ * Input to consume.
+ */
+static json_t *in;
+
+/**
+ * Array of actions to perform.
+ */
+static json_t *out;
+
+/**
+ * Currency supported by this auditor.
+ */
+static char *currency;
+
+
+/**
+ * A subcommand supported by this program.
+ */
+struct SubCommand
+{
+ /**
+ * Name of the command.
+ */
+ const char *name;
+
+ /**
+ * Help text for the command.
+ */
+ const char *help;
+
+ /**
+ * Function implementing the command.
+ *
+ * @param args subsequent command line arguments (char **)
+ */
+ void (*cb)(char *const *args);
+};
+
+
+/**
+ * Data structure for wire add requests.
+ */
+struct DenominationAddRequest
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct DenominationAddRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct DenominationAddRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_AuditorAddDenominationHandle *h;
+
+ /**
+ * Array index of the associated command.
+ */
+ size_t idx;
+};
+
+
+/**
+ * Next work item to perform.
+ */
+static struct GNUNET_SCHEDULER_Task *nxt;
+
+/**
+ * Active denomination add requests.
+ */
+static struct DenominationAddRequest *dar_head;
+
+/**
+ * Active denomination add requests.
+ */
+static struct DenominationAddRequest *dar_tail;
+
+/**
+ * Handle to the exchange, used to request /keys.
+ */
+static struct TALER_EXCHANGE_GetKeysHandle *exchange;
+
+
+/**
+ * Shutdown task. Invoked when the application is being terminated.
+ *
+ * @param cls NULL
+ */
+static void
+do_shutdown (void *cls)
+{
+ (void) cls;
+
+ {
+ struct DenominationAddRequest *dar;
+
+ while (NULL != (dar = dar_head))
+ {
+ fprintf (stderr,
+ "Aborting incomplete wire add #%u\n",
+ (unsigned int) dar->idx);
+ TALER_EXCHANGE_add_auditor_denomination_cancel (dar->h);
+ GNUNET_CONTAINER_DLL_remove (dar_head,
+ dar_tail,
+ dar);
+ GNUNET_free (dar);
+ }
+ }
+ if (NULL != out)
+ {
+ json_dumpf (out,
+ stdout,
+ JSON_INDENT (2));
+ json_decref (out);
+ out = NULL;
+ }
+ if (NULL != in)
+ {
+ fprintf (stderr,
+ "Darning: input not consumed!\n");
+ json_decref (in);
+ in = NULL;
+ }
+ if (NULL != exchange)
+ {
+ TALER_EXCHANGE_get_keys_cancel (exchange);
+ exchange = NULL;
+ }
+ if (NULL != nxt)
+ {
+ GNUNET_SCHEDULER_cancel (nxt);
+ nxt = NULL;
+ }
+ if (NULL != ctx)
+ {
+ GNUNET_CURL_fini (ctx);
+ ctx = NULL;
+ }
+ if (NULL != rc)
+ {
+ GNUNET_CURL_gnunet_rc_destroy (rc);
+ rc = NULL;
+ }
+}
+
+
+/**
+ * Test if we should shut down because all tasks are done.
+ */
+static void
+test_shutdown (void)
+{
+ if ( (NULL == dar_head) &&
+ (NULL == exchange) &&
+ (NULL == nxt) )
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Function to continue processing the next command.
+ *
+ * @param cls must be a `char *const*` with the array of
+ * command-line arguments to process next
+ */
+static void
+work (void *cls);
+
+
+/**
+ * Function to schedule job to process the next command.
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+next (char *const *args)
+{
+ GNUNET_assert (NULL == nxt);
+ if (NULL == args[0])
+ {
+ test_shutdown ();
+ return;
+ }
+ nxt = GNUNET_SCHEDULER_add_now (&work,
+ (void *) args);
+}
+
+
+/**
+ * Add an operation to the #out JSON array for processing later.
+ *
+ * @param op_name name of the operation
+ * @param op_value values for the operation (consumed)
+ */
+static void
+output_operation (const char *op_name,
+ json_t *op_value)
+{
+ json_t *action;
+
+ GNUNET_assert (NULL != out);
+ action = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("operation",
+ op_name),
+ GNUNET_JSON_pack_object_steal ("arguments",
+ op_value));
+ GNUNET_break (0 ==
+ json_array_append_new (out,
+ action));
+}
+
+
+/**
+ * Information about a subroutine for an upload.
+ */
+struct UploadHandler
+{
+ /**
+ * Key to trigger this subroutine.
+ */
+ const char *key;
+
+ /**
+ * Function implementing an upload.
+ *
+ * @param exchange_url URL of the exchange
+ * @param idx index of the operation we are performing
+ * @param value arguments to drive the upload.
+ */
+ void (*cb)(const char *exchange_url,
+ size_t idx,
+ const json_t *value);
+
+};
+
+
+/**
+ * Load the offline key (if not yet done). Triggers shutdown on failure.
+ *
+ * @param do_create #GNUNET_YES if the key may be created
+ * @return #GNUNET_OK on success
+ */
+static int
+load_offline_key (int do_create)
+{
+ static bool done;
+ int ret;
+ char *fn;
+
+ if (done)
+ return GNUNET_OK;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (kcfg,
+ "auditor",
+ "AUDITOR_PRIV_FILE",
+ &fn))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "auditor",
+ "AUDITOR_PRIV_FILE");
+ test_shutdown ();
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_YES !=
+ GNUNET_DISK_file_test (fn))
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Auditor private key `%s' does not exist yet, creating it!\n",
+ fn);
+ ret = GNUNET_CRYPTO_eddsa_key_from_file (fn,
+ do_create,
+ &auditor_priv.eddsa_priv);
+ if (GNUNET_SYSERR == ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to initialize auditor key from file `%s': %s\n",
+ fn,
+ "could not create file");
+ GNUNET_free (fn);
+ test_shutdown ();
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (fn);
+ GNUNET_CRYPTO_eddsa_key_get_public (&auditor_priv.eddsa_priv,
+ &auditor_pub.eddsa_pub);
+ done = true;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called with information about the post denomination (signature)
+ * add operation result.
+ *
+ * @param cls closure with a `struct DenominationAddRequest`
+ * @param adr response data
+ */
+static void
+denomination_add_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_AuditorAddDenominationResponse *adr)
+{
+ struct DenominationAddRequest *dar = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &adr->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ fprintf (stderr,
+ "Upload failed for command #%u with status %u: %s (%s)\n",
+ (unsigned int) dar->idx,
+ hr->http_status,
+ TALER_ErrorCode_get_hint (hr->ec),
+ NULL != hr->hint
+ ? hr->hint
+ : "no hint provided");
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (dar_head,
+ dar_tail,
+ dar);
+ GNUNET_free (dar);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload denomination add data.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for denomination revocation
+ */
+static void
+upload_denomination_add (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ struct TALER_AuditorSignatureP auditor_sig;
+ struct TALER_DenominationHashP h_denom_pub;
+ struct DenominationAddRequest *dar;
+ const char *err_name;
+ unsigned int err_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
+ &h_denom_pub),
+ GNUNET_JSON_spec_fixed_auto ("auditor_sig",
+ &auditor_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ fprintf (stderr,
+ "Invalid input for adding denomination: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ return;
+ }
+ dar = GNUNET_new (struct DenominationAddRequest);
+ dar->idx = idx;
+ dar->h =
+ TALER_EXCHANGE_add_auditor_denomination (ctx,
+ exchange_url,
+ &h_denom_pub,
+ &auditor_pub,
+ &auditor_sig,
+ &denomination_add_cb,
+ dar);
+ GNUNET_CONTAINER_DLL_insert (dar_head,
+ dar_tail,
+ dar);
+}
+
+
+/**
+ * Perform uploads based on the JSON in #out.
+ *
+ * @param exchange_url base URL of the exchange to use
+ */
+static void
+trigger_upload (const char *exchange_url)
+{
+ struct UploadHandler uhs[] = {
+ {
+ .key = OP_SIGN_DENOMINATION,
+ .cb = &upload_denomination_add
+ },
+ /* array termination */
+ {
+ .key = NULL
+ }
+ };
+ size_t index;
+ json_t *obj;
+
+ json_array_foreach (out, index, obj) {
+ bool found = false;
+ const char *key;
+ const json_t *value;
+
+ key = json_string_value (json_object_get (obj, "operation"));
+ value = json_object_get (obj, "arguments");
+ if (NULL == key)
+ {
+ fprintf (stderr,
+ "Malformed JSON input\n");
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ return;
+ }
+ /* block of code that uses key and value */
+ for (unsigned int i = 0; NULL != uhs[i].key; i++)
+ {
+ if (0 == strcasecmp (key,
+ uhs[i].key))
+ {
+ found = true;
+ uhs[i].cb (exchange_url,
+ index,
+ value);
+ break;
+ }
+ }
+ if (! found)
+ {
+ fprintf (stderr,
+ "Upload does not know how to handle `%s'\n",
+ key);
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ return;
+ }
+ }
+ /* test here, in case no upload was triggered (i.e. empty input) */
+ test_shutdown ();
+}
+
+
+/**
+ * Upload operation result (signatures) to exchange.
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+do_upload (char *const *args)
+{
+ char *exchange_url;
+
+ (void) args;
+ if (GNUNET_YES == GNUNET_is_zero (&auditor_pub))
+ {
+ /* private key not available, try configuration for public key */
+ char *auditor_public_key_str;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (kcfg,
+ "auditor",
+ "PUBLIC_KEY",
+ &auditor_public_key_str))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "auditor",
+ "PUBLIC_KEY");
+ global_ret = EXIT_NOTCONFIGURED;
+ test_shutdown ();
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_public_key_from_string (
+ auditor_public_key_str,
+ strlen (auditor_public_key_str),
+ &auditor_pub.eddsa_pub))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "auditor",
+ "PUBLIC_KEY",
+ "invalid key");
+ GNUNET_free (auditor_public_key_str);
+ global_ret = EXIT_NOTCONFIGURED;
+ test_shutdown ();
+ return;
+ }
+ GNUNET_free (auditor_public_key_str);
+ }
+ if (NULL != in)
+ {
+ fprintf (stderr,
+ "Downloaded data was not consumed, refusing upload\n");
+ test_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if (NULL == out)
+ {
+ json_error_t err;
+
+ out = json_loadf (stdin,
+ JSON_REJECT_DUPLICATES,
+ &err);
+ if (NULL == out)
+ {
+ fprintf (stderr,
+ "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
+ err.text,
+ err.line,
+ err.source,
+ err.position);
+ test_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ }
+ if (! json_is_array (out))
+ {
+ fprintf (stderr,
+ "Error: expected JSON array for `upload` command\n");
+ test_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (kcfg,
+ "exchange",
+ "BASE_URL",
+ &exchange_url))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "BASE_URL");
+ global_ret = EXIT_NOTCONFIGURED;
+ test_shutdown ();
+ return;
+ }
+ trigger_upload (exchange_url);
+ json_decref (out);
+ out = NULL;
+ GNUNET_free (exchange_url);
+}
+
+
+/**
+ * Function called with information about who is auditing
+ * a particular exchange and what keys the exchange is using.
+ *
+ * @param cls closure with the `char **` remaining args
+ * @param kr response data
+ * @param keys key data from the exchange
+ */
+static void
+keys_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_KeysResponse *kr,
+ struct TALER_EXCHANGE_Keys *keys)
+{
+ char *const *args = cls;
+
+ exchange = NULL;
+ switch (kr->hr.http_status)
+ {
+ case MHD_HTTP_OK:
+ if (NULL == kr->hr.reply)
+ {
+ GNUNET_break (0);
+ test_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ break;
+ default:
+ fprintf (stderr,
+ "Failed to download keys: %s (HTTP status: %u/%u)\n",
+ kr->hr.hint,
+ kr->hr.http_status,
+ (unsigned int) kr->hr.ec);
+ test_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ in = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("operation",
+ OP_INPUT_KEYS),
+ GNUNET_JSON_pack_object_incref ("arguments",
+ (json_t *) kr->hr.reply));
+ if (NULL == args[0])
+ {
+ json_dumpf (in,
+ stdout,
+ JSON_INDENT (2));
+ json_decref (in);
+ in = NULL;
+ }
+ next (args);
+ TALER_EXCHANGE_keys_decref (keys);
+}
+
+
+/**
+ * Download future keys.
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+do_download (char *const *args)
+{
+ char *exchange_url;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (kcfg,
+ "exchange",
+ "BASE_URL",
+ &exchange_url))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "BASE_URL");
+ test_shutdown ();
+ global_ret = EXIT_NOTCONFIGURED;
+ return;
+ }
+ exchange = TALER_EXCHANGE_get_keys (ctx,
+ exchange_url,
+ NULL,
+ &keys_cb,
+ (void *) args);
+ GNUNET_free (exchange_url);
+}
+
+
+/**
+ * Output @a denomkeys for human consumption.
+ *
+ * @param denomkeys keys to output
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+show_denomkeys (const json_t *denomkeys)
+{
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (denomkeys, index, value) {
+ struct TALER_DenominationGroup group;
+ const json_t *denoms;
+ const char *err_name;
+ unsigned int err_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_denomination_group (NULL,
+ currency,
+ &group),
+ GNUNET_JSON_spec_array_const ("denoms",
+ &denoms),
+ GNUNET_JSON_spec_end ()
+ };
+ size_t index2;
+ json_t *value2;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ fprintf (stderr,
+ "Invalid input for denomination key to 'show': %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) index);
+ GNUNET_JSON_parse_free (spec);
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ return GNUNET_SYSERR;
+ }
+ json_array_foreach (denoms, index2, value2) {
+ struct GNUNET_TIME_Timestamp stamp_start;
+ struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
+ struct GNUNET_TIME_Timestamp stamp_expire_deposit;
+ struct GNUNET_TIME_Timestamp stamp_expire_legal;
+ struct TALER_DenominationPublicKey denom_pub;
+ struct TALER_MasterSignatureP master_sig;
+ struct GNUNET_JSON_Specification ispec[] = {
+ TALER_JSON_spec_denom_pub_cipher (NULL,
+ group.cipher,
+ &denom_pub),
+ GNUNET_JSON_spec_timestamp ("stamp_start",
+ &stamp_start),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
+ &stamp_expire_withdraw),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
+ &stamp_expire_deposit),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
+ &stamp_expire_legal),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+ struct GNUNET_TIME_Relative duration;
+ struct TALER_DenominationHashP h_denom_pub;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value2,
+ ispec,
+ &err_name,
+ &err_line))
+ {
+ fprintf (stderr,
+ "Invalid input for denomination key to 'show': %s#%u at %u/%u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) index,
+ (unsigned int) index2);
+ GNUNET_JSON_parse_free (spec);
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ return GNUNET_SYSERR;
+ }
+ duration = GNUNET_TIME_absolute_get_difference (
+ stamp_start.abs_time,
+ stamp_expire_withdraw.abs_time);
+ TALER_denom_pub_hash (&denom_pub,
+ &h_denom_pub);
+ if (GNUNET_OK !=
+ TALER_exchange_offline_denom_validity_verify (
+ &h_denom_pub,
+ stamp_start,
+ stamp_expire_withdraw,
+ stamp_expire_deposit,
+ stamp_expire_legal,
+ &group.value,
+ &group.fees,
+ &master_pub,
+ &master_sig))
+ {
+ fprintf (stderr,
+ "Invalid master signature for key %s (aborting)\n",
+ TALER_B2S (&h_denom_pub));
+ global_ret = EXIT_FAILURE;
+ GNUNET_JSON_parse_free (ispec);
+ GNUNET_JSON_parse_free (spec);
+ test_shutdown ();
+ return GNUNET_SYSERR;
+ }
+
+ {
+ char *withdraw_fee_s;
+ char *deposit_fee_s;
+ char *refresh_fee_s;
+ char *refund_fee_s;
+ char *deposit_s;
+ char *legal_s;
+
+ withdraw_fee_s = TALER_amount_to_string (&group.fees.withdraw);
+ deposit_fee_s = TALER_amount_to_string (&group.fees.deposit);
+ refresh_fee_s = TALER_amount_to_string (&group.fees.refresh);
+ refund_fee_s = TALER_amount_to_string (&group.fees.refund);
+ deposit_s = GNUNET_strdup (
+ GNUNET_TIME_timestamp2s (stamp_expire_deposit));
+ legal_s = GNUNET_strdup (
+ GNUNET_TIME_timestamp2s (stamp_expire_legal));
+
+ printf (
+ "DENOMINATION-KEY %s of value %s starting at %s "
+ "(used for: %s, deposit until: %s legal end: %s) with fees %s/%s/%s/%s\n",
+ TALER_B2S (&h_denom_pub),
+ TALER_amount2s (&group.value),
+ GNUNET_TIME_timestamp2s (stamp_start),
+ GNUNET_TIME_relative2s (duration,
+ false),
+ deposit_s,
+ legal_s,
+ withdraw_fee_s,
+ deposit_fee_s,
+ refresh_fee_s,
+ refund_fee_s);
+ GNUNET_free (withdraw_fee_s);
+ GNUNET_free (deposit_fee_s);
+ GNUNET_free (refresh_fee_s);
+ GNUNET_free (refund_fee_s);
+ GNUNET_free (deposit_s);
+ GNUNET_free (legal_s);
+ }
+ GNUNET_JSON_parse_free (ispec);
+ }
+ GNUNET_JSON_parse_free (spec);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Parse the '/keys' input for operation called @a command_name.
+ *
+ * @param command_name name of the command, for logging errors
+ * @return NULL if the input is malformed
+ */
+static json_t *
+parse_keys (const char *command_name)
+{
+ json_t *keys;
+ const char *op_str;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_json ("arguments",
+ &keys),
+ GNUNET_JSON_spec_string ("operation",
+ &op_str),
+ GNUNET_JSON_spec_end ()
+ };
+ const char *err_name;
+ unsigned int err_line;
+
+ if (NULL == in)
+ {
+ json_error_t err;
+
+ in = json_loadf (stdin,
+ JSON_REJECT_DUPLICATES,
+ &err);
+ if (NULL == in)
+ {
+ fprintf (stderr,
+ "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
+ err.text,
+ err.line,
+ err.source,
+ err.position);
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ return NULL;
+ }
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (in,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to '%s': %s#%u (skipping)\n",
+ command_name,
+ err_name,
+ err_line);
+ json_dumpf (in,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ return NULL;
+ }
+ if (0 != strcmp (op_str,
+ OP_INPUT_KEYS))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to '%s' : operation is `%s', expected `%s'\n",
+ command_name,
+ op_str,
+ OP_INPUT_KEYS);
+ GNUNET_JSON_parse_free (spec);
+ return NULL;
+ }
+ json_decref (in);
+ in = NULL;
+ return keys;
+}
+
+
+/**
+ * Show exchange denomination keys.
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+do_show (char *const *args)
+{
+ json_t *keys;
+ const char *err_name;
+ unsigned int err_line;
+ const json_t *denomkeys;
+ struct TALER_MasterPublicKeyP mpub;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("denominations",
+ &denomkeys),
+ GNUNET_JSON_spec_fixed_auto ("master_public_key",
+ &mpub),
+ GNUNET_JSON_spec_end ()
+ };
+
+ keys = parse_keys ("show");
+ if (NULL == keys)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Showing failed: no valid input\n");
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (keys,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ fprintf (stderr,
+ "Invalid input to 'show': %s#%u (skipping)\n",
+ err_name,
+ err_line);
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ json_decref (keys);
+ return;
+ }
+ if (0 !=
+ GNUNET_memcmp (&mpub,
+ &master_pub))
+ {
+ fprintf (stderr,
+ "Exchange master public key does not match key we have configured (aborting)\n");
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ json_decref (keys);
+ return;
+ }
+ if (GNUNET_OK !=
+ show_denomkeys (denomkeys))
+ {
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ json_decref (keys);
+ return;
+ }
+ json_decref (keys);
+ /* do NOT consume input if next argument is '-' */
+ if ( (NULL != args[0]) &&
+ (0 == strcmp ("-",
+ args[0])) )
+ {
+ next (args + 1);
+ return;
+ }
+ next (args);
+}
+
+
+/**
+ * Sign @a denomkeys with offline key.
+ *
+ * @param denomkeys keys to output
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+sign_denomkeys (const json_t *denomkeys)
+{
+ size_t group_idx;
+ json_t *value;
+
+ json_array_foreach (denomkeys, group_idx, value) {
+ struct TALER_DenominationGroup group = { 0 };
+ const json_t *denom_keys_array;
+ const char *err_name;
+ unsigned int err_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_denomination_group (NULL,
+ currency,
+ &group),
+ GNUNET_JSON_spec_array_const ("denoms",
+ &denom_keys_array),
+ GNUNET_JSON_spec_end ()
+ };
+ size_t index;
+ json_t *denom_key_obj;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ fprintf (stderr,
+ "Invalid input for denomination key to 'sign': %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) group_idx);
+ GNUNET_JSON_parse_free (spec);
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ return GNUNET_SYSERR;
+ }
+ json_array_foreach (denom_keys_array, index, denom_key_obj) {
+ struct GNUNET_TIME_Timestamp stamp_start;
+ struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
+ struct GNUNET_TIME_Timestamp stamp_expire_deposit;
+ struct GNUNET_TIME_Timestamp stamp_expire_legal;
+ struct TALER_DenominationPublicKey denom_pub = {
+ .age_mask = group.age_mask
+ };
+ struct TALER_MasterSignatureP master_sig;
+ struct GNUNET_JSON_Specification ispec[] = {
+ TALER_JSON_spec_denom_pub_cipher (NULL,
+ group.cipher,
+ &denom_pub),
+ GNUNET_JSON_spec_timestamp ("stamp_start",
+ &stamp_start),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
+ &stamp_expire_withdraw),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
+ &stamp_expire_deposit),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
+ &stamp_expire_legal),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+ struct TALER_DenominationHashP h_denom_pub;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (denom_key_obj,
+ ispec,
+ &err_name,
+ &err_line))
+ {
+ fprintf (stderr,
+ "Invalid input for denomination key to 'show': %s#%u at %u/%u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) group_idx,
+ (unsigned int) index);
+ GNUNET_JSON_parse_free (spec);
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ return GNUNET_SYSERR;
+ }
+ TALER_denom_pub_hash (&denom_pub,
+ &h_denom_pub);
+ if (GNUNET_OK !=
+ TALER_exchange_offline_denom_validity_verify (
+ &h_denom_pub,
+ stamp_start,
+ stamp_expire_withdraw,
+ stamp_expire_deposit,
+ stamp_expire_legal,
+ &group.value,
+ &group.fees,
+ &master_pub,
+ &master_sig))
+ {
+ fprintf (stderr,
+ "Invalid master signature for key %s (aborting)\n",
+ TALER_B2S (&h_denom_pub));
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ return GNUNET_SYSERR;
+ }
+
+ {
+ struct TALER_AuditorSignatureP auditor_sig;
+
+ TALER_auditor_denom_validity_sign (auditor_url,
+ &h_denom_pub,
+ &master_pub,
+ stamp_start,
+ stamp_expire_withdraw,
+ stamp_expire_deposit,
+ stamp_expire_legal,
+ &group.value,
+ &group.fees,
+ &auditor_priv,
+ &auditor_sig);
+ output_operation (OP_SIGN_DENOMINATION,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("h_denom_pub",
+ &h_denom_pub),
+ GNUNET_JSON_pack_data_auto ("auditor_sig",
+ &auditor_sig)));
+ }
+ GNUNET_JSON_parse_free (ispec);
+ }
+ GNUNET_JSON_parse_free (spec);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Sign denomination keys.
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+do_sign (char *const *args)
+{
+ json_t *keys;
+ const char *err_name;
+ unsigned int err_line;
+ struct TALER_MasterPublicKeyP mpub;
+ const json_t *denomkeys;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("denominations",
+ &denomkeys),
+ GNUNET_JSON_spec_fixed_auto ("master_public_key",
+ &mpub),
+ GNUNET_JSON_spec_end ()
+ };
+
+ keys = parse_keys ("sign");
+ if (NULL == keys)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Signing failed: no valid input\n");
+ return;
+ }
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ {
+ json_decref (keys);
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (keys,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ fprintf (stderr,
+ "Invalid input to 'sign': %s#%u (skipping)\n",
+ err_name,
+ err_line);
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ json_decref (keys);
+ return;
+ }
+ if (0 !=
+ GNUNET_memcmp (&mpub,
+ &master_pub))
+ {
+ fprintf (stderr,
+ "Exchange master public key does not match key we have configured (aborting)\n");
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ json_decref (keys);
+ return;
+ }
+ if (NULL == out)
+ {
+ out = json_array ();
+ GNUNET_assert (NULL != out);
+ }
+ if (GNUNET_OK !=
+ sign_denomkeys (denomkeys))
+ {
+ global_ret = EXIT_FAILURE;
+ test_shutdown ();
+ json_decref (keys);
+ return;
+ }
+ json_decref (keys);
+ next (args);
+}
+
+
+/**
+ * Setup and output offline signing key.
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+do_setup (char *const *args)
+{
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_YES))
+ {
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if (NULL != *args)
+ {
+ if (NULL == out)
+ {
+ out = json_array ();
+ GNUNET_assert (NULL != out);
+ }
+ output_operation (OP_SETUP,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("auditor_pub",
+ &auditor_pub)));
+ }
+
+ else
+ {
+ char *pub_s;
+
+ pub_s = GNUNET_STRINGS_data_to_string_alloc (&auditor_pub,
+ sizeof (auditor_pub));
+ fprintf (stdout,
+ "%s\n",
+ pub_s);
+ GNUNET_free (pub_s);
+ }
+ if ( (NULL != *args) &&
+ (0 == strcmp (*args,
+ "-")) )
+ args++;
+ next (args);
+}
+
+
+static void
+work (void *cls)
+{
+ char *const *args = cls;
+ struct SubCommand cmds[] = {
+ {
+ .name = "setup",
+ .help =
+ "setup auditor offline private key and show the public key",
+ .cb = &do_setup
+ },
+ {
+ .name = "download",
+ .help =
+ "obtain keys from exchange (to be performed online!)",
+ .cb = &do_download
+ },
+ {
+ .name = "show",
+ .help =
+ "display keys from exchange for human review (pass '-' as argument to disable consuming input)",
+ .cb = &do_show
+ },
+ {
+ .name = "sign",
+ .help =
+ "sing all denomination keys from the input",
+ .cb = &do_sign
+ },
+ {
+ .name = "upload",
+ .help =
+ "upload operation result to exchange (to be performed online!)",
+ .cb = &do_upload
+ },
+ /* list terminator */
+ {
+ .name = NULL,
+ }
+ };
+ (void) cls;
+
+ nxt = NULL;
+ for (unsigned int i = 0; NULL != cmds[i].name; i++)
+ {
+ if (0 == strcasecmp (cmds[i].name,
+ args[0]))
+ {
+ cmds[i].cb (&args[1]);
+ return;
+ }
+ }
+
+ if (0 != strcasecmp ("help",
+ args[0]))
+ {
+ fprintf (stderr,
+ "Unexpected command `%s'\n",
+ args[0]);
+ global_ret = EXIT_INVALIDARGUMENT;
+ }
+ fprintf (stderr,
+ "Supported subcommands:\n");
+ for (unsigned int i = 0; NULL != cmds[i].name; i++)
+ {
+ fprintf (stderr,
+ "\t%s - %s\n",
+ cmds[i].name,
+ cmds[i].help);
+ }
+}
+
+
+/**
+ * Main function that will be run.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param cfg configuration
+ */
+static void
+run (void *cls,
+ char *const *args,
+ const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ (void) cls;
+ (void) cfgfile;
+ kcfg = cfg;
+ if (GNUNET_OK !=
+ TALER_config_get_currency (kcfg,
+ &currency))
+ {
+ global_ret = EXIT_NOTCONFIGURED;
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (kcfg,
+ "auditor",
+ "BASE_URL",
+ &auditor_url))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "auditor",
+ "BASE_URL");
+ global_ret = EXIT_NOTCONFIGURED;
+ return;
+ }
+ {
+ char *master_public_key_str;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "exchange",
+ "MASTER_PUBLIC_KEY",
+ &master_public_key_str))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "MASTER_PUBLIC_KEY");
+ global_ret = EXIT_NOTCONFIGURED;
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_public_key_from_string (
+ master_public_key_str,
+ strlen (master_public_key_str),
+ &master_pub.eddsa_pub))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid master public key given in exchange configuration.");
+ GNUNET_free (master_public_key_str);
+ global_ret = EXIT_NOTCONFIGURED;
+ return;
+ }
+ GNUNET_free (master_public_key_str);
+ }
+ ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
+ &rc);
+ rc = GNUNET_CURL_gnunet_rc_create (ctx);
+ GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
+ NULL);
+ next (args);
+}
+
+
+/**
+ * The main function of the taler-auditor-offline tool. This tool is used to
+ * sign denomination keys with the auditor's key. It uses the long-term
+ * offline private key of the auditor and generates signatures with it. It
+ * also supports online operations with the exchange to download its input
+ * data and to upload its results. Those online operations should be performed
+ * on another machine in production!
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc,
+ char *const *argv)
+{
+ struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_END
+ };
+ enum GNUNET_GenericReturnValue ret;
+
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_get_utf8_args (argc, argv,
+ &argc, &argv))
+ return EXIT_INVALIDARGUMENT;
+ /* force linker to link against libtalerutil; if we do
+ not do this, the linker may "optimize" libtalerutil
+ away and skip #TALER_OS_init(), which we do need */
+ TALER_OS_init ();
+ ret = GNUNET_PROGRAM_run (
+ argc, argv,
+ "taler-auditor-offline",
+ gettext_noop ("Operations for offline signing for a Taler exchange"),
+ options,
+ &run, NULL);
+ GNUNET_free_nz ((void *) argv);
+ if (GNUNET_SYSERR == ret)
+ return EXIT_INVALIDARGUMENT;
+ if (GNUNET_NO == ret)
+ return EXIT_SUCCESS;
+ return global_ret;
+}
+
+
+/* end of taler-auditor-offline.c */
diff --git a/src/exchange-tools/taler-exchange-dbinit.c b/src/exchange-tools/taler-exchange-dbinit.c
index 67632faca..41ff2ab24 100644
--- a/src/exchange-tools/taler-exchange-dbinit.c
+++ b/src/exchange-tools/taler-exchange-dbinit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015 Taler Systems SA
+ Copyright (C) 2014-2024 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
@@ -17,6 +17,7 @@
* @file exchange-tools/taler-exchange-dbinit.c
* @brief Create tables for the exchange database.
* @author Florian Dold
+ * @author Christian Grothoff
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
@@ -29,16 +30,36 @@
static int global_ret;
/**
+ * -a option: inject auditor triggers
+ */
+static int inject_auditor;
+
+/**
* -r option: do full DB reset
*/
static int reset_db;
/**
+ * -s option: clear revolving shard locks
+ */
+static int clear_shards;
+
+/**
* -g option: garbage collect DB reset
*/
static int gc_db;
/**
+ * -P option: setup a partitioned database
+ */
+static uint32_t num_partitions;
+
+/**
+ * -f option: force partitions to be created when there is only one
+ */
+static int force_create_partitions;
+
+/**
* Main function that will be run.
*
* @param cls closure
@@ -57,40 +78,77 @@ run (void *cls,
(void) cls;
(void) args;
(void) cfgfile;
+
if (NULL ==
(plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
{
fprintf (stderr,
"Failed to initialize database plugin.\n");
- global_ret = 1;
+ global_ret = EXIT_NOTINSTALLED;
return;
}
if (reset_db)
{
- if (GNUNET_OK != plugin->drop_tables (plugin->cls))
+ if (GNUNET_OK !=
+ plugin->drop_tables (plugin->cls))
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Could not drop tables as requested. Either database was not yet initialized, or permission denied. Consult the logs. Will still try to create new tables.\n");
}
}
if (GNUNET_OK !=
- plugin->create_tables (plugin->cls))
+ plugin->create_tables (plugin->cls,
+ force_create_partitions || num_partitions > 0,
+ num_partitions))
{
fprintf (stderr,
"Failed to initialize database.\n");
TALER_EXCHANGEDB_plugin_unload (plugin);
- global_ret = 1;
+ plugin = NULL;
+ global_ret = EXIT_NOPERMISSION;
return;
}
- if (gc_db)
+ if (gc_db || clear_shards)
+ {
+ if (GNUNET_OK !=
+ plugin->preflight (plugin->cls))
+ {
+ fprintf (stderr,
+ "Failed to prepare database.\n");
+ TALER_EXCHANGEDB_plugin_unload (plugin);
+ plugin = NULL;
+ global_ret = EXIT_NOPERMISSION;
+ return;
+ }
+ if (clear_shards)
+ {
+ if (GNUNET_OK !=
+ plugin->delete_shard_locks (plugin->cls))
+ {
+ fprintf (stderr,
+ "Clearing revolving shards failed!\n");
+ }
+ }
+ if (gc_db)
+ {
+ if (GNUNET_SYSERR == plugin->gc (plugin->cls))
+ {
+ fprintf (stderr,
+ "Garbage collection failed!\n");
+ }
+ }
+ }
+ if (inject_auditor)
{
- if (GNUNET_SYSERR == plugin->gc (plugin->cls))
+ if (GNUNET_SYSERR == plugin->inject_auditor_triggers (plugin->cls))
{
fprintf (stderr,
- "Garbage collection failed!\n");
+ "Injecting auditor triggers failed!\n");
+ global_ret = EXIT_FAILURE;
}
}
TALER_EXCHANGEDB_plugin_unload (plugin);
+ plugin = NULL;
}
@@ -100,39 +158,61 @@ run (void *cls,
*
* @param argc number of arguments from the command line
* @param argv command line arguments
- * @return 0 ok, 1 on error
+ * @return 0 ok, non-zero on error
*/
int
main (int argc,
char *const *argv)
{
const struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_option_flag ('r',
- "reset",
- "reset database (DANGEROUS: all existing data is lost!)",
- &reset_db),
+ GNUNET_GETOPT_option_flag ('a',
+ "inject-auditor",
+ "inject auditor triggers",
+ &inject_auditor),
GNUNET_GETOPT_option_flag ('g',
"gc",
"garbage collect database",
&gc_db),
+ GNUNET_GETOPT_option_flag ('r',
+ "reset",
+ "reset database (DANGEROUS: all existing data is lost!)",
+ &reset_db),
+ GNUNET_GETOPT_option_flag ('s',
+ "shardunlock",
+ "unlock all revolving shard locks (use after system crash or shard size change while services are not running)",
+ &clear_shards),
+ GNUNET_GETOPT_option_uint ('P',
+ "partition",
+ "NUMBER",
+ "Setup a partitioned database where each table which can be partitioned holds NUMBER partitions on a single DB node",
+ &num_partitions),
+ GNUNET_GETOPT_option_flag ('f',
+ "force",
+ "Force partitions to be created if there is only one partition",
+ &force_create_partitions),
GNUNET_GETOPT_OPTION_END
};
+ enum GNUNET_GenericReturnValue ret;
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_get_utf8_args (argc, argv,
+ &argc, &argv))
+ return EXIT_INVALIDARGUMENT;
/* force linker to link against libtalerutil; if we do
not do this, the linker may "optimize" libtalerutil
away and skip #TALER_OS_init(), which we do need */
- (void) TALER_project_data_default ();
- GNUNET_assert (GNUNET_OK ==
- GNUNET_log_setup ("taler-exchange-dbinit",
- "INFO",
- NULL));
- if (GNUNET_OK !=
- GNUNET_PROGRAM_run (argc, argv,
- "taler-exchange-dbinit",
- "Initialize Taler exchange database",
- options,
- &run, NULL))
- return 1;
+ TALER_OS_init ();
+ ret = GNUNET_PROGRAM_run (
+ argc, argv,
+ "taler-exchange-dbinit",
+ gettext_noop ("Initialize Taler exchange database"),
+ options,
+ &run, NULL);
+ GNUNET_free_nz ((void *) argv);
+ if (GNUNET_SYSERR == ret)
+ return EXIT_INVALIDARGUMENT;
+ if (GNUNET_NO == ret)
+ return EXIT_SUCCESS;
return global_ret;
}
diff --git a/src/exchange-tools/taler-exchange-keycheck.c b/src/exchange-tools/taler-exchange-keycheck.c
deleted file mode 100644
index 917ca879f..000000000
--- a/src/exchange-tools/taler-exchange-keycheck.c
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014, 2015, 2016 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
- 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, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-keycheck.c
- * @brief Check exchange keys for validity. Reads the signing and denomination
- * keys from the exchange directory and checks to make sure they are
- * well-formed. This is purely a diagnostic tool.
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#include <platform.h>
-#include <gnunet/gnunet_util_lib.h>
-#include "taler_exchangedb_lib.h"
-
-/**
- * Exchange directory with the keys.
- */
-static char *exchange_directory;
-
-/**
- * Our configuration.
- */
-static const struct GNUNET_CONFIGURATION_Handle *kcfg;
-
-/**
- * Return value from main().
- */
-static int global_ret;
-
-/**
- * Option -i used to print full denomination key hashes for
- * denominations of certain amounts.
- */
-static struct TALER_Amount print_dk_amount;
-
-
-/**
- * Function called on each signing key.
- *
- * @param cls closure (NULL)
- * @param filename name of the file the key came from
- * @param ski the sign key
- * @return #GNUNET_OK to continue to iterate,
- * #GNUNET_NO to stop iteration with no error,
- * #GNUNET_SYSERR to abort iteration with error!
- */
-static int
-signkeys_iter (void *cls,
- const char *filename,
- const struct TALER_EXCHANGEDB_PrivateSigningKeyInformationP *ski)
-{
- (void) cls;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Iterating over key `%s' for start time %s\n",
- filename,
- GNUNET_STRINGS_absolute_time_to_string
- (GNUNET_TIME_absolute_ntoh (ski->issue.start)));
-
- if (ntohl (ski->issue.purpose.size) !=
- (sizeof (struct TALER_ExchangeSigningKeyValidityPS)))
- {
- fprintf (stderr,
- "Signing key `%s' has invalid purpose size\n",
- filename);
- return GNUNET_SYSERR;
- }
- if ( (0 != GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us
- % 1000000) ||
- (0 != GNUNET_TIME_absolute_ntoh (ski->issue.expire).abs_value_us
- % 1000000) ||
- (0 != GNUNET_TIME_absolute_ntoh (ski->issue.end).abs_value_us
- % 1000000) )
- {
- fprintf (stderr,
- "Timestamps are not multiples of a round second\n");
- return GNUNET_SYSERR;
- }
-
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY,
- &ski->issue,
- &ski->master_sig.eddsa_signature,
- &ski->issue.master_public_key.eddsa_pub))
- {
- fprintf (stderr,
- "Signing key `%s' has invalid signature\n",
- filename);
- return GNUNET_SYSERR;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Signing key `%s' valid\n",
- filename);
- return GNUNET_OK;
-}
-
-
-/**
- * Check signing keys.
- *
- * @return #GNUNET_OK if the keys are OK
- * #GNUNET_NO if not
- */
-static int
-exchange_signkeys_check ()
-{
- if (0 > TALER_EXCHANGEDB_signing_keys_iterate (exchange_directory,
- &signkeys_iter,
- NULL))
- return GNUNET_NO;
- return GNUNET_OK;
-}
-
-
-/**
- * Function called on each denomination key.
- *
- * @param cls closure (NULL)
- * @param dki the denomination key
- * @param alias coin alias
- * @return #GNUNET_OK to continue to iterate,
- * #GNUNET_NO to stop iteration with no error,
- * #GNUNET_SYSERR to abort iteration with error!
- */
-static int
-denomkeys_iter (void *cls,
- const char *alias,
- const struct
- TALER_EXCHANGEDB_DenominationKey *dki)
-{
- struct GNUNET_HashCode hc;
- struct TALER_Amount value;
-
- (void) cls;
- if (ntohl (dki->issue.properties.purpose.size) !=
- sizeof (struct TALER_DenominationKeyValidityPS))
- {
- fprintf (stderr,
- "Denomination key for `%s' has invalid purpose size\n",
- alias);
- return GNUNET_SYSERR;
- }
-
- if ( (0 != GNUNET_TIME_absolute_ntoh (
- dki->issue.properties.start).abs_value_us % 1000000) ||
- (0 != GNUNET_TIME_absolute_ntoh (
- dki->issue.properties.expire_withdraw).abs_value_us % 1000000) ||
- (0 != GNUNET_TIME_absolute_ntoh (
- dki->issue.properties.expire_legal).abs_value_us % 1000000) ||
- (0 != GNUNET_TIME_absolute_ntoh (
- dki->issue.properties.expire_deposit).abs_value_us % 1000000) )
- {
- fprintf (stderr,
- "Timestamps are not multiples of a round second\n");
- return GNUNET_SYSERR;
- }
-
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (
- TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY,
- &dki->issue.properties,
- &dki->issue.signature.eddsa_signature,
- &dki->issue.properties.master.eddsa_pub))
- {
- fprintf (stderr,
- "Denomination key for `%s' has invalid signature\n",
- alias);
- return GNUNET_SYSERR;
- }
- GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key,
- &hc);
- if (0 != GNUNET_memcmp (&hc,
- &dki->issue.properties.denom_hash))
- {
- fprintf (stderr,
- "Public key for `%s' does not match signature\n",
- alias);
- return GNUNET_SYSERR;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Denomination key `%s' (%s) is valid\n",
- alias,
- GNUNET_h2s (&hc));
- TALER_amount_ntoh (&value,
- &dki->issue.properties.value);
- if ( (GNUNET_OK ==
- TALER_amount_cmp_currency (&print_dk_amount,
- &value)) &&
- (0 ==
- TALER_amount_cmp (&print_dk_amount,
- &value)) )
- {
- char *dh;
- struct GNUNET_TIME_Absolute start;
-
- start = GNUNET_TIME_absolute_ntoh (dki->issue.properties.start);
- dh = GNUNET_STRINGS_data_to_string_alloc (&dki->issue.properties.denom_hash,
- sizeof (struct GNUNET_HashCode));
- /* output start time first for easy numeric sorting, then
- the denomination hash, and finally the human-readable start time */
- printf ("%020llu %s %s\n",
- (unsigned long long) start.abs_value_us,
- dh,
- GNUNET_STRINGS_absolute_time_to_string (start));
- GNUNET_free (dh);
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Check denomination keys.
- *
- * @return #GNUNET_OK if the keys are OK
- * #GNUNET_NO if not
- */
-static int
-exchange_denomkeys_check ()
-{
- struct TALER_MasterPublicKeyP master_public_key_from_cfg;
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_data (kcfg,
- "exchange",
- "master_public_key",
- &master_public_key_from_cfg,
- sizeof (struct
- GNUNET_CRYPTO_EddsaPublicKey)))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "exchange",
- "master_public_key");
- return GNUNET_NO;
- }
- if (0 > TALER_EXCHANGEDB_denomination_keys_iterate (exchange_directory,
- &denomkeys_iter,
- NULL))
- return GNUNET_NO;
- return GNUNET_OK;
-}
-
-
-/**
- * Main function that will be run.
- *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be NULL!)
- * @param cfg configuration
- */
-static void
-run (void *cls,
- char *const *args,
- const char *cfgfile,
- const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- (void) cls;
- (void) args;
- (void) cfgfile;
- kcfg = cfg;
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_filename (kcfg,
- "exchange",
- "KEYDIR",
- &exchange_directory))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "exchange",
- "KEYDIR");
- global_ret = 1;
- return;
- }
-
- if ( (GNUNET_OK != exchange_signkeys_check ()) ||
- (GNUNET_OK != exchange_denomkeys_check ()) )
- {
- global_ret = 1;
- return;
- }
-}
-
-
-/**
- * The main function of the keyup tool
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
-int
-main (int argc,
- char *const *argv)
-{
- const struct GNUNET_GETOPT_CommandLineOption options[] = {
- TALER_getopt_get_amount ('i',
- "denomination-info-hash",
- "AMOUNT",
- "print full denomination hashes of all denominations with the given AMOUNT value",
- &print_dk_amount),
- GNUNET_GETOPT_OPTION_END
- };
-
- /* force linker to link against libtalerutil; if we do
- not do this, the linker may "optimize" libtalerutil
- away and skip #TALER_OS_init(), which we do need */
- (void) TALER_project_data_default ();
- GNUNET_assert (GNUNET_OK ==
- GNUNET_log_setup ("taler-exchange-keycheck",
- "WARNING",
- NULL));
- if (GNUNET_OK !=
- GNUNET_PROGRAM_run (argc, argv,
- "taler-exchange-keycheck",
- "Check keys of the exchange for validity",
- options,
- &run, NULL))
- return 1;
- return global_ret;
-
-}
-
-
-/* end of taler-exchange-keycheck.c */
diff --git a/src/exchange-tools/taler-exchange-keyup.c b/src/exchange-tools/taler-exchange-keyup.c
deleted file mode 100644
index 0f5f66a8d..000000000
--- a/src/exchange-tools/taler-exchange-keyup.c
+++ /dev/null
@@ -1,1513 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2014-2020 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
- 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, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-keyup.c
- * @brief Update the exchange's keys for coins and online signing keys,
- * using the exchange's offline master key.
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#include <platform.h>
-#include "taler_exchangedb_lib.h"
-
-
-/**
- * When generating filenames from a cryptographic hash, we do not use all 512
- * bits but cut off after this number of characters (in base32-encoding).
- * Base32 is 5 bit per character, and given that we have very few coin types,
- * at 100 bits the chance of collision (by accident over such a tiny set) is
- * negligible. (Also, some file-systems do not support very long file names.)
- */
-#define HASH_CUTOFF 20
-
-
-GNUNET_NETWORK_STRUCT_BEGIN
-
-/**
- * Struct with all of the meta data about a denomination. Hashed
- * to generate a unique directory name per coin type.
- */
-struct DenominationNBOP
-{
- /**
- * How long are the signatures legally valid?
- */
- struct GNUNET_TIME_RelativeNBO duration_legal;
-
- /**
- * How long can the coins be spend?
- */
- struct GNUNET_TIME_RelativeNBO duration_spend;
-
- /**
- * How long can coins be withdrawn (generated)?
- */
- struct GNUNET_TIME_RelativeNBO duration_withdraw;
-
- /**
- * What is the value of each coin?
- */
- struct TALER_AmountNBO value;
-
- /**
- * What is the fee charged for withdrawal?
- */
- struct TALER_AmountNBO fee_withdraw;
-
- /**
- * What is the fee charged for deposits?
- */
- struct TALER_AmountNBO fee_deposit;
-
- /**
- * What is the fee charged for melting?
- */
- struct TALER_AmountNBO fee_refresh;
-
- /**
- * What is the fee charged for refunds?
- */
- struct TALER_AmountNBO fee_refund;
-
- /**
- * Key size (in NBO).
- */
- uint32_t rsa_keysize;
-};
-
-GNUNET_NETWORK_STRUCT_END
-
-/**
- * Set of all of the parameters that characterize a denomination.
- */
-struct DenominationParameters
-{
-
- /**
- * How long are the signatures legally valid? Should be
- * significantly larger than @e duration_spend (i.e. years).
- */
- struct GNUNET_TIME_Relative duration_legal;
-
- /**
- * How long can the coins be spend? Should be significantly
- * larger than @e duration_withdraw (i.e. years).
- */
- struct GNUNET_TIME_Relative duration_spend;
-
- /**
- * How long can coins be withdrawn (generated)? Should be small
- * enough to limit how many coins will be signed into existence with
- * the same key, but large enough to still provide a reasonable
- * anonymity set.
- */
- struct GNUNET_TIME_Relative duration_withdraw;
-
- /**
- * What is the value of each coin?
- */
- struct TALER_Amount value;
-
- /**
- * What is the fee charged for withdrawal?
- */
- struct TALER_Amount fee_withdraw;
-
- /**
- * What is the fee charged for deposits?
- */
- struct TALER_Amount fee_deposit;
-
- /**
- * What is the fee charged for melting?
- */
- struct TALER_Amount fee_refresh;
-
- /**
- * What is the fee charged for refunds?
- */
- struct TALER_Amount fee_refund;
-
- /**
- * Time at which this coin is supposed to become valid.
- */
- struct GNUNET_TIME_Absolute anchor;
-
- /**
- * Length of the RSA key (in bits).
- */
- uint32_t rsa_keysize;
-};
-
-
-/**
- * How much should coin creation (@e duration_withdraw) duration overlap
- * with the next denomination? Basically, the starting time of two
- * denominations is always @e duration_withdraw - #duration_overlap apart.
- */
-static struct GNUNET_TIME_Relative duration_overlap;
-
-/**
- * The configured currency.
- */
-static char *currency;
-
-/**
- * Filename of the master private key.
- */
-static char *masterkeyfile;
-
-/**
- * Filename where to write denomination key signing
- * requests for the auditor (optional, can be NULL).
- */
-static char *auditorrequestfile;
-
-/**
- * Handle for writing the output for the auditor.
- */
-static FILE *auditor_output_file;
-
-/**
- * Director of the exchange, containing the keys.
- */
-static char *exchange_directory;
-
-/**
- * Directory where we should write the wire transfer fee structure.
- */
-static char *feedir;
-
-/**
- * Handle to the exchange's configuration
- */
-static const struct GNUNET_CONFIGURATION_Handle *kcfg;
-
-/**
- * Time when the key update is executed.
- * Either the actual current time, or a pretended time.
- */
-static struct GNUNET_TIME_Absolute now;
-
-/**
- * The time for the key update, as passed by the user
- * on the command line.
- */
-static struct GNUNET_TIME_Absolute now_tmp;
-
-/**
- * Master private key of the exchange.
- */
-static struct TALER_MasterPrivateKeyP master_priv;
-
-/**
- * Master public key of the exchange.
- */
-static struct TALER_MasterPublicKeyP master_public_key;
-
-/**
- * Until what time do we provide keys?
- */
-static struct GNUNET_TIME_Absolute lookahead_sign_stamp;
-
-/**
- * Largest duration for spending of any key.
- */
-static struct GNUNET_TIME_Relative max_duration_spend;
-
-/**
- * Revoke denomination key identified by this hash (if non-zero).
- */
-static struct GNUNET_HashCode revoke_dkh;
-
-/**
- * Which RSA key size should we use for replacement keys after revocation?
- * (Useful because maybe that's the one option one might usefully want to
- * change when replacing a key.)
- */
-static unsigned int replacement_key_size = 2048;
-
-/**
- * Return value from main().
- */
-static int global_ret;
-
-
-#include "key-helper.c"
-
-/**
- * Hash the data defining a denomination type. Exclude information that may
- * not be the same for all instances of the denomination's type (i.e. the
- * anchor, overlap).
- *
- * @param p denomination parameters to convert to a hash
- * @param[out] hash set to the hash matching @a p
- */
-static void
-hash_denomination_parameters (const struct DenominationParameters *p,
- struct GNUNET_HashCode *hash)
-{
- struct DenominationNBOP p_nbo;
-
- memset (&p_nbo,
- 0,
- sizeof (struct DenominationNBOP));
- p_nbo.duration_spend = GNUNET_TIME_relative_hton (p->duration_spend);
- p_nbo.duration_legal = GNUNET_TIME_relative_hton (p->duration_legal);
- p_nbo.duration_withdraw = GNUNET_TIME_relative_hton (p->duration_withdraw);
- TALER_amount_hton (&p_nbo.value,
- &p->value);
- TALER_amount_hton (&p_nbo.fee_withdraw,
- &p->fee_withdraw);
- TALER_amount_hton (&p_nbo.fee_deposit,
- &p->fee_deposit);
- TALER_amount_hton (&p_nbo.fee_refresh,
- &p->fee_refresh);
- TALER_amount_hton (&p_nbo.fee_refund,
- &p->fee_refund);
- p_nbo.rsa_keysize = htonl (p->rsa_keysize);
- GNUNET_CRYPTO_hash (&p_nbo,
- sizeof (struct DenominationNBOP),
- hash);
-}
-
-
-/**
- * Obtain the name of the directory we should use to store denominations of
- * the given type. The directory name has the format
- * "$EXCHANGEDIR/$VALUE/$HASH/" where "$VALUE" represents the value of the
- * coins and "$HASH" encodes all of the denomination's parameters, generating
- * a unique string for each type of denomination. Note that the "$HASH"
- * includes neither the absolute creation time nor the key of the
- * denomination, thus the files in the subdirectory really just refer to the
- * same type of denominations, not the same denomination.
- *
- * @param p denomination parameters to convert to a directory name
- * @return directory name (valid until next call to this function)
- */
-static const char *
-get_denomination_dir (const struct DenominationParameters *p)
-{
- static char dir[4096];
- struct GNUNET_HashCode hash;
- char *hash_str;
- char *val_str;
-
- hash_denomination_parameters (p,
- &hash);
- hash_str = GNUNET_STRINGS_data_to_string_alloc (&hash,
- sizeof (struct
- GNUNET_HashCode));
- GNUNET_assert (NULL != hash_str);
- GNUNET_assert (HASH_CUTOFF <= strlen (hash_str) + 1);
- hash_str[HASH_CUTOFF] = 0;
-
- val_str = TALER_amount_to_string (&p->value);
- GNUNET_assert (NULL != val_str);
- for (size_t i = 0; i < strlen (val_str); i++)
- if ( (':' == val_str[i]) ||
- ('.' == val_str[i]) )
- val_str[i] = '_';
-
- GNUNET_snprintf (dir,
- sizeof (dir),
- "%s" DIR_SEPARATOR_STR TALER_EXCHANGEDB_DIR_DENOMINATION_KEYS
- DIR_SEPARATOR_STR "%s-%s",
- exchange_directory,
- val_str,
- hash_str);
- GNUNET_free (hash_str);
- GNUNET_free (val_str);
- return dir;
-}
-
-
-/**
- * Obtain the name of the file we would use to store the key
- * information for a denomination of the given type @a p and validity
- * start time @a start
- *
- * @param p parameters for the denomination
- * @param start when would the denomination begin to be issued
- * @return name of the file to use for this denomination
- * (valid until next call to this function)
- */
-static const char *
-get_denomination_type_file (const struct DenominationParameters *p,
- struct GNUNET_TIME_Absolute start)
-{
- static char filename[4096];
- const char *dir;
-
- dir = get_denomination_dir (p);
- GNUNET_snprintf (filename,
- sizeof (filename),
- "%s" DIR_SEPARATOR_STR "%llu",
- dir,
- (unsigned long long) start.abs_value_us);
- return filename;
-}
-
-
-/**
- * Get the latest key file from a past run of the key generation
- * tool. Used to calculate the starting time for the keys we
- * generate during this invocation. This function is used to
- * handle both signing keys and denomination keys, as in both cases
- * the filenames correspond to the timestamps we need.
- *
- * @param cls closure, a `struct GNUNET_TIME_Absolute *`, updated
- * to contain the highest timestamp (below #now)
- * that was found
- * @param filename complete filename (absolute path)
- * @return #GNUNET_OK (to continue to iterate)
- */
-static int
-get_anchor_iter (void *cls,
- const char *filename)
-{
- struct GNUNET_TIME_Absolute *anchor = cls;
- struct GNUNET_TIME_Absolute stamp;
- const char *base;
- char *end = NULL;
- long long int bval;
-
- base = GNUNET_STRINGS_get_short_name (filename);
- bval = strtoll (base,
- &end,
- 10);
- if ( (NULL == end) ||
- (0 != *end) ||
- (0 > bval) )
- {
- fprintf (stderr,
- "Ignoring unexpected file `%s'.\n",
- filename);
- return GNUNET_OK;
- }
- stamp.abs_value_us = (uint64_t) bval;
- *anchor = GNUNET_TIME_absolute_max (stamp,
- *anchor);
- return GNUNET_OK;
-}
-
-
-/**
- * Get the timestamp where the first new key should be generated.
- * Relies on correctly named key files (as we do not parse them,
- * but just look at the filenames to "guess" at their contents).
- *
- * @param dir directory that should contain the existing keys
- * @param duration how long is one key valid (for signing)?
- * @param overlap what's the overlap between the keys validity period?
- * @param[out] anchor the timestamp where the first new key should be generated
- */
-static void
-get_anchor (const char *dir,
- struct GNUNET_TIME_Relative duration,
- struct GNUNET_TIME_Relative overlap,
- struct GNUNET_TIME_Absolute *anchor)
-{
- GNUNET_assert (0 == duration.rel_value_us % 1000000);
- GNUNET_assert (0 == overlap.rel_value_us % 1000000);
- if (GNUNET_YES !=
- GNUNET_DISK_directory_test (dir,
- GNUNET_YES))
- {
- *anchor = now;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "No existing keys found, starting with fresh key set.\n");
- return;
- }
- *anchor = GNUNET_TIME_UNIT_ZERO_ABS;
- if (-1 ==
- GNUNET_DISK_directory_scan (dir,
- &get_anchor_iter,
- anchor))
- {
- *anchor = now;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "No existing keys found, starting with fresh key set.\n");
- return;
- }
-
- if ((GNUNET_TIME_absolute_add (*anchor,
- duration)).abs_value_us < now.abs_value_us)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Existing keys are way too old, starting with fresh key set.\n");
- *anchor = now;
- }
- else if (anchor->abs_value_us != now.abs_value_us)
- {
- *anchor = GNUNET_TIME_absolute_add (*anchor,
- duration);
- *anchor = GNUNET_TIME_absolute_subtract (*anchor,
- overlap);
- }
-
- /* anchor is now the stamp where we need to create a new key */
-}
-
-
-/**
- * Create a exchange signing key (for signing exchange messages, not for
- * signing coins) and assert its correctness by signing it with the master
- * key.
- *
- * @param start start time of the validity period for the key
- * @param duration how long should the key be valid
- * @param end when do all signatures by this key expire
- * @param[out] pi set to the signing key information
- */
-static void
-create_signkey_issue_priv (
- struct GNUNET_TIME_Absolute start,
- struct GNUNET_TIME_Relative duration,
- struct GNUNET_TIME_Absolute end,
- struct TALER_EXCHANGEDB_PrivateSigningKeyInformationP *pi)
-{
- struct TALER_ExchangeSigningKeyValidityPS *issue = &pi->issue;
-
- GNUNET_CRYPTO_eddsa_key_create (&pi->signkey_priv.eddsa_priv);
- issue->master_public_key = master_public_key;
- issue->start = GNUNET_TIME_absolute_hton (start);
- issue->expire = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (start,
- duration));
- issue->end = GNUNET_TIME_absolute_hton (end);
- GNUNET_CRYPTO_eddsa_key_get_public (&pi->signkey_priv.eddsa_priv,
- &issue->signkey_pub.eddsa_pub);
- issue->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY);
- issue->purpose.size = htonl (sizeof (struct
- TALER_ExchangeSigningKeyValidityPS));
- GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv,
- issue,
- &pi->master_sig.eddsa_signature);
-}
-
-
-/**
- * Generate signing keys starting from the last key found to
- * the lookahead time.
- *
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
- */
-static int
-exchange_keys_update_signkeys (void)
-{
- struct GNUNET_TIME_Relative signkey_duration;
- struct GNUNET_TIME_Relative legal_duration;
- struct GNUNET_TIME_Absolute anchor;
- char *signkey_dir;
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (kcfg,
- "exchange",
- "SIGNKEY_DURATION",
- &signkey_duration))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "exchange",
- "SIGNKEY_DURATION");
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (kcfg,
- "exchange",
- "LEGAL_DURATION",
- &legal_duration))
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- "exchange",
- "LEGAL_DURATION",
- "fails to specify valid timeframe");
- return GNUNET_SYSERR;
- }
- if (signkey_duration.rel_value_us > legal_duration.rel_value_us)
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- "exchange",
- "LEGAL_DURATION",
- "Value given for LEGAL_DURATION must be longer than value for SIGNKEY_DURATION");
- return GNUNET_SYSERR;
- }
- GNUNET_TIME_round_rel (&signkey_duration);
- GNUNET_asprintf (&signkey_dir,
- "%s" DIR_SEPARATOR_STR TALER_EXCHANGEDB_DIR_SIGNING_KEYS,
- exchange_directory);
- /* make sure the directory exists */
- if (GNUNET_OK !=
- GNUNET_DISK_directory_create (signkey_dir))
- {
- fprintf (stderr,
- "Failed to create signing key directory\n");
- GNUNET_free (signkey_dir);
- return GNUNET_SYSERR;
- }
-
- get_anchor (signkey_dir,
- signkey_duration,
- GNUNET_TIME_UNIT_ZERO /* no overlap for signing keys */,
- &anchor);
- GNUNET_free (signkey_dir);
-
- while (anchor.abs_value_us < lookahead_sign_stamp.abs_value_us)
- {
- struct TALER_EXCHANGEDB_PrivateSigningKeyInformationP signkey_issue;
- struct GNUNET_TIME_Absolute end;
-
- end = GNUNET_TIME_absolute_add (anchor,
- legal_duration);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Generating signing key for %s.\n",
- GNUNET_STRINGS_absolute_time_to_string (anchor));
- create_signkey_issue_priv (anchor,
- signkey_duration,
- end,
- &signkey_issue);
- if (GNUNET_OK !=
- TALER_EXCHANGEDB_signing_key_write (exchange_directory,
- anchor,
- &signkey_issue))
- return GNUNET_SYSERR;
- anchor = GNUNET_TIME_absolute_add (anchor,
- signkey_duration);
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Parse configuration for denomination type parameters. Also determines
- * our anchor by looking at the existing denominations of the same type.
- *
- * @param ct section in the configuration file giving the denomination type parameters
- * @param[out] params set to the denomination parameters from the configuration
- * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid
- */
-static int
-get_denomination_type_params (const char *ct,
- struct DenominationParameters *params)
-{
- const char *dir;
- unsigned long long rsa_keysize;
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (kcfg,
- ct,
- "DURATION_WITHDRAW",
- &params->duration_withdraw))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "DURATION_WITHDRAW");
- return GNUNET_SYSERR;
- }
- GNUNET_TIME_round_rel (&params->duration_withdraw);
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (kcfg,
- ct,
- "DURATION_SPEND",
- &params->duration_spend))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "DURATION_SPEND");
- return GNUNET_SYSERR;
- }
- GNUNET_TIME_round_rel (&params->duration_spend);
- max_duration_spend = GNUNET_TIME_relative_max (max_duration_spend,
- params->duration_spend);
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (kcfg,
- ct,
- "DURATION_LEGAL",
- &params->duration_legal))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "DURATION_LEGAL");
- return GNUNET_SYSERR;
- }
- GNUNET_TIME_round_rel (&params->duration_legal);
- if (duration_overlap.rel_value_us >=
- params->duration_withdraw.rel_value_us)
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- "exchangedb",
- "DURATION_OVERLAP",
- "Value given for DURATION_OVERLAP must be smaller than value for DURATION_WITHDRAW!");
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_number (kcfg,
- ct,
- "rsa_keysize",
- &rsa_keysize))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "rsa_keysize");
- return GNUNET_SYSERR;
- }
- if ( (rsa_keysize > 4 * 2048) ||
- (rsa_keysize < 1024) )
- {
- fprintf (stderr,
- "Given RSA keysize %llu outside of permitted range\n",
- rsa_keysize);
- return GNUNET_SYSERR;
- }
- params->rsa_keysize = (unsigned int) rsa_keysize;
- if (GNUNET_OK !=
- TALER_config_get_amount (kcfg,
- ct,
- "VALUE",
- &params->value))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "VALUE");
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_config_get_amount (kcfg,
- ct,
- "FEE_WITHDRAW",
- &params->fee_withdraw))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "FEE_WITHDRAW");
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_config_get_amount (kcfg,
- ct,
- "FEE_DEPOSIT",
- &params->fee_deposit))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "FEE_DEPOSIT");
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_config_get_amount (kcfg,
- ct,
- "FEE_REFRESH",
- &params->fee_refresh))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "FEE_REFRESH");
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_config_get_amount (kcfg,
- ct,
- "fee_refund",
- &params->fee_refund))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ct,
- "FEE_REFUND");
- return GNUNET_SYSERR;
- }
-
- dir = get_denomination_dir (params);
- get_anchor (dir,
- params->duration_withdraw,
- duration_overlap,
- &params->anchor);
-
- /**
- * The "anchor" is merely the latest denom key filename
- * converted to a GNUnet absolute time.
- */
-
- return GNUNET_OK;
-}
-
-
-/**
- * Initialize the private and public key information structure for
- * signing coins into existence. Generates the private signing key
- * and signes it together with the denomination's meta data using the master
- * signing key.
- *
- * @param params parameters used to initialize the @a dki
- * @param[out] dki initialized according to @a params
- */
-static void
-create_denomkey_issue (
- const struct DenominationParameters *params,
- struct TALER_EXCHANGEDB_DenominationKey *dki)
-{
- dki->denom_priv.rsa_private_key
- = GNUNET_CRYPTO_rsa_private_key_create (params->rsa_keysize);
- GNUNET_assert (NULL != dki->denom_priv.rsa_private_key);
- dki->denom_pub.rsa_public_key
- = GNUNET_CRYPTO_rsa_private_key_get_public (
- dki->denom_priv.rsa_private_key);
- GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key,
- &dki->issue.properties.denom_hash);
- dki->issue.properties.master = master_public_key;
- dki->issue.properties.start = GNUNET_TIME_absolute_hton (params->anchor);
- dki->issue.properties.expire_withdraw =
- GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
- params->
- duration_withdraw));
- dki->issue.properties.expire_deposit =
- GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
- params->duration_spend));
- dki->issue.properties.expire_legal =
- GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_add (params->anchor,
- params->duration_legal));
- TALER_amount_hton (&dki->issue.properties.value,
- &params->value);
- TALER_amount_hton (&dki->issue.properties.fee_withdraw,
- &params->fee_withdraw);
- TALER_amount_hton (&dki->issue.properties.fee_deposit,
- &params->fee_deposit);
- TALER_amount_hton (&dki->issue.properties.fee_refresh,
- &params->fee_refresh);
- TALER_amount_hton (&dki->issue.properties.fee_refund,
- &params->fee_refund);
- dki->issue.properties.purpose.purpose
- = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
- dki->issue.properties.purpose.size
- = htonl (sizeof (struct TALER_DenominationKeyValidityPS));
- GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv,
- &dki->issue.properties,
- &dki->issue.signature.eddsa_signature);
-}
-
-
-/**
- * Write the @a denomkey_issue to file @a dkf and also (if applicable)
- * dump the properties to the #auditor_output_file.
- *
- * @param dkf where to write the @a denomkey_issue
- * @param denomkey_issue data to write
- * @return #GNUNET_OK on success
- */
-static int
-write_denomkey_issue (
- const char *dkf,
- const struct TALER_EXCHANGEDB_DenominationKey *denomkey_issue)
-{
- if (GNUNET_OK !=
- TALER_EXCHANGEDB_denomination_key_write (dkf,
- denomkey_issue))
- {
- fprintf (stderr,
- "Failed to write denomination key information to file `%s'.\n",
- dkf);
- return GNUNET_SYSERR;
- }
- if ( (NULL != auditor_output_file) &&
- (1 !=
- fwrite (&denomkey_issue->issue.properties,
- sizeof (struct TALER_DenominationKeyValidityPS),
- 1,
- auditor_output_file)) )
- {
- fprintf (stderr,
- "Failed to write denomination key information to %s: %s\n",
- auditorrequestfile,
- strerror (errno));
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Generate new denomination signing keys for the denomination type of the given @a
- * denomination_alias.
- *
- * @param cls a `int *`, to be set to #GNUNET_SYSERR on failure
- * @param denomination_alias name of the denomination's section in the configuration
- */
-static void
-exchange_keys_update_denominationtype (void *cls,
- const char *denomination_alias)
-{
- int *ret = cls;
- struct DenominationParameters p;
- const char *dkf;
- struct TALER_EXCHANGEDB_DenominationKey denomkey_issue;
-
- if (0 != strncasecmp (denomination_alias,
- "coin_",
- strlen ("coin_")))
- return; /* not a denomination type definition */
- if (GNUNET_OK !=
- get_denomination_type_params (denomination_alias,
- &p))
- {
- *ret = GNUNET_SYSERR;
- return;
- }
- /* p has the right anchor now = latest denom filename converted to time. */
- if (GNUNET_OK !=
- GNUNET_DISK_directory_create (get_denomination_dir (&p)))
- {
- *ret = GNUNET_SYSERR;
- return;
- }
-
- while (p.anchor.abs_value_us < lookahead_sign_stamp.abs_value_us)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Future time not covered yet for type `%s': %s\n",
- denomination_alias,
- GNUNET_STRINGS_relative_time_to_string
- (GNUNET_TIME_absolute_get_difference (p.anchor,
- lookahead_sign_stamp),
- GNUNET_NO));
- dkf = get_denomination_type_file (&p,
- p.anchor);
- GNUNET_break (GNUNET_YES !=
- GNUNET_DISK_file_test (dkf));
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Generating denomination key for type `%s', start %s at %s\n",
- denomination_alias,
- GNUNET_STRINGS_absolute_time_to_string (p.anchor),
- dkf);
- create_denomkey_issue (&p,
- &denomkey_issue);
- *ret = write_denomkey_issue (dkf,
- &denomkey_issue);
- GNUNET_CRYPTO_rsa_private_key_free (
- denomkey_issue.denom_priv.rsa_private_key);
- GNUNET_CRYPTO_rsa_public_key_free (denomkey_issue.denom_pub.rsa_public_key);
- if (GNUNET_OK != *ret)
- return; /* stop loop, hard error */
- p.anchor = GNUNET_TIME_absolute_add (p.anchor,
- p.duration_withdraw);
- p.anchor = GNUNET_TIME_absolute_subtract (p.anchor,
- duration_overlap);
- }
-}
-
-
-/**
- * Update all of the denomination keys of the exchange.
- *
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
- */
-static int
-exchange_keys_update_denomkeys (void)
-{
- int ok;
-
- ok = GNUNET_OK;
- GNUNET_CONFIGURATION_iterate_sections (kcfg,
- &exchange_keys_update_denominationtype,
- &ok);
- return ok;
-}
-
-
-/**
- * Sign @a af with @a priv
- *
- * @param[in,out] af fee structure to sign
- * @param method name of the wire method for which we sign
- * @param priv private key to use for signing
- */
-static void
-sign_af (struct TALER_EXCHANGEDB_AggregateFees *af,
- const char *method,
- const struct GNUNET_CRYPTO_EddsaPrivateKey *priv)
-{
- struct TALER_MasterWireFeePS wf;
-
- TALER_EXCHANGEDB_fees_2_wf (method,
- af,
- &wf);
- GNUNET_CRYPTO_eddsa_sign (priv,
- &wf,
- &af->master_sig.eddsa_signature);
-}
-
-
-/**
- * Output the wire fee structure. Must be run after #max_duration_spend
- * was initialized.
- *
- * @param cls pointer to `int`, set to #GNUNET_SYSERR on error
- * @param wiremethod method to write fees for
- */
-static void
-create_wire_fee_for_method (void *cls,
- const char *wiremethod)
-{
- int *ret = cls;
- struct TALER_EXCHANGEDB_AggregateFees *af_head;
- struct TALER_EXCHANGEDB_AggregateFees *af_tail;
- unsigned int year;
- struct GNUNET_TIME_Absolute last_date;
- struct GNUNET_TIME_Absolute start_date;
- struct GNUNET_TIME_Absolute end_date;
- char yearstr[12];
- char *fn;
- char *section;
-
- if (GNUNET_OK != *ret)
- return;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Setting up wire fees for `%s'\n",
- wiremethod);
- last_date = GNUNET_TIME_absolute_add (lookahead_sign_stamp,
- max_duration_spend);
- GNUNET_asprintf (&section,
- "fees-%s",
- wiremethod);
- GNUNET_asprintf (&fn,
- "%s/%s.fee",
- feedir,
- wiremethod);
- af_head = NULL;
- af_tail = NULL;
- year = GNUNET_TIME_get_current_year ();
- start_date = GNUNET_TIME_year_to_time (year);
- while (start_date.abs_value_us < last_date.abs_value_us)
- {
- struct TALER_EXCHANGEDB_AggregateFees *af;
- char *opt;
-
- GNUNET_snprintf (yearstr,
- sizeof (yearstr),
- "%u",
- year);
- end_date = GNUNET_TIME_year_to_time (year + 1);
- af = GNUNET_new (struct TALER_EXCHANGEDB_AggregateFees);
- af->start_date = start_date;
- af->end_date = end_date;
-
- /* handle wire fee */
- GNUNET_asprintf (&opt,
- "wire-fee-%u",
- year);
- if ( (GNUNET_OK !=
- TALER_config_get_amount (kcfg,
- section,
- opt,
- &af->wire_fee)) ||
- (0 != strcasecmp (currency,
- af->wire_fee.currency)) )
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid or missing amount in `%s' under `%s'\n",
- wiremethod,
- opt);
- *ret = GNUNET_SYSERR;
- GNUNET_free (opt);
- break;
- }
- GNUNET_free (opt);
-
- /* handle closing fee */
- GNUNET_asprintf (&opt,
- "closing-fee-%u",
- year);
- if ( (GNUNET_OK !=
- TALER_config_get_amount (kcfg,
- section,
- opt,
- &af->closing_fee)) ||
- (0 != strcasecmp (currency,
- af->closing_fee.currency)) )
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid or missing amount in `%s' under `%s'\n",
- wiremethod,
- opt);
- *ret = GNUNET_SYSERR;
- GNUNET_free (opt);
- break;
- }
-
- GNUNET_free (opt);
- sign_af (af,
- wiremethod,
- &master_priv.eddsa_priv);
- if (NULL == af_tail)
- af_head = af;
- else
- af_tail->next = af;
- af_tail = af;
- start_date = end_date;
- year++;
- }
- if ( (GNUNET_OK == *ret) &&
- (GNUNET_OK !=
- TALER_EXCHANGEDB_fees_write (fn,
- wiremethod,
- af_head)) )
- *ret = GNUNET_SYSERR;
- GNUNET_free (section);
- GNUNET_free (fn);
- TALER_EXCHANGEDB_fees_free (af_head);
-}
-
-
-/**
- * Output the wire fee structure. Must be run after #max_duration_spend
- * was initialized.
- *
- * @param cls pointer to `int`, set to #GNUNET_SYSERR on error
- * @param ai information about enabled accounts
- */
-static void
-create_wire_fee_by_account (void *cls,
- const struct TALER_EXCHANGEDB_AccountInfo *ai)
-{
- int *ret = cls;
-
- if (GNUNET_NO == ai->credit_enabled)
- return;
- /* We may call this function repeatedly for the same method
- if there are multiple accounts with plugins using the
- same method, but except for some minor performance loss,
- this is harmless. */
- create_wire_fee_for_method (ret,
- ai->method);
-}
-
-
-/**
- * Output the wire fee structure. Must be run after #max_duration_spend
- * was initialized.
- *
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
- */
-static int
-create_wire_fees (void)
-{
- int ret;
-
- ret = GNUNET_OK;
- TALER_EXCHANGEDB_find_accounts (kcfg,
- &create_wire_fee_by_account,
- &ret);
- return ret;
-}
-
-
-/**
- * Check if the denomination that we just revoked is currently active,
- * and if so, generate a replacement key.
- *
- * @param cls closure with the revoked denomination key hash, a `struct GNUNET_HashCode *`
- * @param alias coin alias
- * @param dki the denomination key
- * @return #GNUNET_OK to continue to iterate,
- * #GNUNET_NO to stop iteration with no error,
- * #GNUNET_SYSERR to abort iteration with error!
- */
-static int
-check_revocation_regeneration (
- void *cls,
- const char *alias,
- const struct TALER_EXCHANGEDB_DenominationKey *dki)
-{
- const struct GNUNET_HashCode *denom_hash = cls;
- struct GNUNET_TIME_Absolute now;
- struct GNUNET_TIME_Absolute withdraw_end;
-
- (void) alias;
- if (0 !=
- GNUNET_memcmp (denom_hash,
- &dki->issue.properties.denom_hash))
- return GNUNET_OK; /* does not match */
- now = GNUNET_TIME_absolute_get ();
- withdraw_end = GNUNET_TIME_absolute_ntoh (
- dki->issue.properties.expire_withdraw);
- if (now.abs_value_us >= withdraw_end.abs_value_us)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Revoked denomination key has expired, no need to create a replacement\n");
- return GNUNET_NO;
- }
-
- {
- struct GNUNET_TIME_Absolute anchor
- = GNUNET_TIME_absolute_ntoh (dki->issue.properties.start);
- struct TALER_EXCHANGEDB_DenominationKey dki_new;
- const char *dkf;
- int ret;
- struct DenominationParameters dp = {
- .duration_legal
- = GNUNET_TIME_absolute_get_difference
- (anchor,
- GNUNET_TIME_absolute_ntoh (dki->issue.properties.expire_legal)),
- .duration_spend
- = GNUNET_TIME_absolute_get_difference
- (anchor,
- GNUNET_TIME_absolute_ntoh (dki->issue.properties.expire_deposit)),
- .duration_withdraw
- = GNUNET_TIME_absolute_get_difference
- (anchor,
- GNUNET_TIME_absolute_ntoh (dki->issue.properties.expire_withdraw)),
- .anchor = anchor,
- .rsa_keysize = replacement_key_size
- };
- char *dkfi;
-
- TALER_amount_ntoh (&dp.value,
- &dki->issue.properties.value);
- TALER_amount_ntoh (&dp.fee_withdraw,
- &dki->issue.properties.fee_withdraw);
- TALER_amount_ntoh (&dp.fee_deposit,
- &dki->issue.properties.fee_deposit);
- TALER_amount_ntoh (&dp.fee_refresh,
- &dki->issue.properties.fee_refresh);
- TALER_amount_ntoh (&dp.fee_refund,
- &dki->issue.properties.fee_refund);
-
- /* find unused file name for revocation file by appending -%u */
- dkf = get_denomination_type_file (&dp,
- dp.anchor);
- for (unsigned int i = 1;; i++)
- {
- GNUNET_asprintf (&dkfi,
- "%s-%u",
- dkf,
- i);
- if (GNUNET_YES != GNUNET_DISK_file_test (dkfi))
- break;
- GNUNET_free (dkfi);
- }
-
- create_denomkey_issue (&dp,
- &dki_new);
- ret = write_denomkey_issue (dkfi,
- &dki_new);
- GNUNET_free (dkfi);
- GNUNET_CRYPTO_rsa_private_key_free (dki_new.denom_priv.rsa_private_key);
- GNUNET_CRYPTO_rsa_public_key_free (dki_new.denom_pub.rsa_public_key);
- if (GNUNET_OK != ret)
- return GNUNET_SYSERR;
- }
-
- return GNUNET_NO;
-}
-
-
-/**
- * Revoke the denomination key matching @a hc and request /recoup to be
- * initiated.
- *
- * @param hc denomination key hash to revoke
- * @return #GNUNET_OK on success,
- * #GNUNET_NO if @a hc was not found
- * #GNUNET_SYSERR on error
- */
-static int
-revoke_denomination (const struct GNUNET_HashCode *hc)
-{
- {
- char *basedir;
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_filename (kcfg,
- "exchange",
- "REVOCATION_DIR",
- &basedir))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "exchange",
- "REVOCATION_DIR");
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_EXCHANGEDB_denomination_key_revoke (basedir,
- hc,
- &master_priv))
- {
- GNUNET_free (basedir);
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- GNUNET_free (basedir);
- }
-
- if (GNUNET_SYSERR ==
- TALER_EXCHANGEDB_denomination_keys_iterate (exchange_directory,
- &check_revocation_regeneration,
- (void *) hc))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Might have failed to generate replacement for revoked denomination key!\n");
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Main function that will be run.
- *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be NULL!)
- * @param cfg configuration
- */
-static void
-run (void *cls,
- char *const *args,
- const char *cfgfile,
- const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- struct GNUNET_TIME_Relative lookahead_sign;
-
- (void) cls;
- (void) args;
- (void) cfgfile;
- kcfg = cfg;
- if (GNUNET_OK !=
- TALER_config_get_currency (cfg,
- &currency))
- {
- global_ret = 1;
- return;
- }
- if (now.abs_value_us != now_tmp.abs_value_us)
- {
- /* The user gave "--now", use it! */
- now = now_tmp;
- }
- GNUNET_TIME_round_abs (&now);
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (kcfg,
- "exchangedb",
- "DURATION_OVERLAP",
- &duration_overlap))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "exchangedb",
- "DURATION_OVERLAP");
- global_ret = 1;
- return;
- }
- GNUNET_TIME_round_rel (&duration_overlap);
-
- if (NULL == feedir)
- {
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_filename (kcfg,
- "exchangedb",
- "WIREFEE_BASE_DIR",
- &feedir))
- {
- fprintf (stderr,
- "Wire fee directory given neither in configuration nor on command-line\n");
- global_ret = 1;
- return;
- }
- }
- if (GNUNET_OK !=
- GNUNET_DISK_directory_create (feedir))
- {
- GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
- "mkdir",
- feedir);
- global_ret = 1;
- return;
- }
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_filename (kcfg,
- "exchange",
- "KEYDIR",
- &exchange_directory))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "exchange",
- "KEYDIR");
- global_ret = 1;
- return;
- }
-
- if (GNUNET_OK !=
- get_and_check_master_key (kcfg,
- masterkeyfile,
- &master_priv))
- {
- global_ret = 1;
- return;
- }
- GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv,
- &master_public_key.eddsa_pub);
-
- if (NULL != auditorrequestfile)
- {
- auditor_output_file = fopen (auditorrequestfile,
- "w");
- if (NULL == auditor_output_file)
- {
- GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
- "open (w)",
- auditorrequestfile);
- global_ret = 1;
- return;
- }
- }
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (kcfg,
- "exchange",
- "LOOKAHEAD_SIGN",
- &lookahead_sign))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "exchange",
- "LOOKAHEAD_SIGN");
- global_ret = 1;
- return;
- }
- if (0 == lookahead_sign.rel_value_us)
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- "exchange",
- "LOOKAHEAD_SIGN",
- "must not be zero");
- global_ret = 1;
- return;
- }
- GNUNET_TIME_round_rel (&lookahead_sign);
- lookahead_sign_stamp = GNUNET_TIME_absolute_add (now,
- lookahead_sign);
-
-
- /* finally, do actual work */
- if (0 != GNUNET_is_zero (&revoke_dkh))
- {
- if (GNUNET_OK != revoke_denomination (&revoke_dkh))
- {
- global_ret = 1;
- return;
- }
- /* if we were invoked to revoke a key, let's not also generate
- new keys, as that might not be desired. */
- return;
- }
-
- if (NULL == auditor_output_file)
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Option `-o' missing. Hence, you will NOT be able to use an auditor with the generated keys!\n");
-
- if (GNUNET_OK != exchange_keys_update_signkeys ())
- {
- global_ret = 1;
- return;
- }
- if (GNUNET_OK != exchange_keys_update_denomkeys ())
- {
- global_ret = 1;
- return;
- }
- if (GNUNET_OK != create_wire_fees ())
- {
- global_ret = 1;
- return;
- }
-}
-
-
-/**
- * The main function of the taler-exchange-keyup tool. This tool is used to
- * create the signing and denomination keys for the exchange. It uses the
- * long-term offline private key and writes the (additional) key files to the
- * respective exchange directory (from where they can then be copied to the
- * online server). Note that we need (at least) the most recent generated
- * previous keys to align the validity periods.
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
-int
-main (int argc,
- char *const *argv)
-{
- struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_option_filename ('m',
- "master-key",
- "FILENAME",
- "master key file (private key)",
- &masterkeyfile),
- GNUNET_GETOPT_option_filename ('f',
- "feedir",
- "DIRNAME",
- "directory where to write wire transfer fee structure",
- &feedir),
- GNUNET_GETOPT_option_uint ('k',
- "replacement-keysize",
- "BITS",
- "when creating a replacement key in a revocation operation, which key size should be used for the new denomination key",
- &replacement_key_size),
- GNUNET_GETOPT_option_filename ('o',
- "output",
- "FILENAME",
- "auditor denomination key signing request file to create",
- &auditorrequestfile),
- GNUNET_GETOPT_option_base32_auto ('r',
- "revoke",
- "DKH",
- "revoke denomination key hash (DKH) and request wallets to initiate recoup",
- &revoke_dkh),
- GNUNET_GETOPT_option_timetravel ('T',
- "timetravel"),
- GNUNET_GETOPT_option_absolute_time ('t',
- "time",
- "TIMESTAMP",
- "pretend it is a different time for the update",
- &now_tmp),
- GNUNET_GETOPT_OPTION_END
- };
-
- /* force linker to link against libtalerutil; if we do
- not do this, the linker may "optimize" libtalerutil
- away and skip #TALER_OS_init(), which we do need */
- (void) TALER_project_data_default ();
- GNUNET_assert (GNUNET_OK ==
- GNUNET_log_setup ("taler-exchange-keyup",
- "WARNING",
- NULL));
- now = now_tmp = GNUNET_TIME_absolute_get ();
- if (GNUNET_OK !=
- GNUNET_PROGRAM_run (argc, argv,
- "taler-exchange-keyup",
- "Setup signing and denomination keys for a Taler exchange",
- options,
- &run, NULL))
- return 1;
- if (NULL != auditor_output_file)
- {
- GNUNET_assert (0 == fclose (auditor_output_file));
- auditor_output_file = NULL;
- }
- return global_ret;
-}
-
-
-/* end of taler-exchange-keyup.c */
diff --git a/src/exchange-tools/taler-exchange-offline.c b/src/exchange-tools/taler-exchange-offline.c
new file mode 100644
index 000000000..1f10c55e3
--- /dev/null
+++ b/src/exchange-tools/taler-exchange-offline.c
@@ -0,0 +1,5497 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020-2024 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
+ 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, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file taler-exchange-offline.c
+ * @brief Support for operations involving the exchange's offline master key.
+ * @author Christian Grothoff
+ */
+#include <platform.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_json_lib.h"
+#include "taler_exchange_service.h"
+#include "taler_extensions.h"
+#include <regex.h>
+
+
+/**
+ * Name of the input for the 'sign' and 'show' operation.
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_INPUT_KEYS "exchange-input-keys-0"
+
+/**
+ * Name of the operation to 'disable auditor'
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_DISABLE_AUDITOR "exchange-disable-auditor-0"
+
+/**
+ * Name of the operation to 'enable auditor'
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_ENABLE_AUDITOR "exchange-enable-auditor-0"
+
+/**
+ * Name of the operation to 'enable wire'
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_ENABLE_WIRE "exchange-enable-wire-0"
+
+/**
+ * Name of the operation to 'disable wire'
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_DISABLE_WIRE "exchange-disable-wire-0"
+
+/**
+ * Name of the operation to set a 'wire-fee'
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_SET_WIRE_FEE "exchange-set-wire-fee-0"
+
+/**
+ * Name of the operation to set a 'global-fee'
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_SET_GLOBAL_FEE "exchange-set-global-fee-0"
+
+/**
+ * Name of the operation to 'upload' key signatures
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_UPLOAD_SIGS "exchange-upload-sigs-0"
+
+/**
+ * Name of the operation to 'revoke-denomination' key
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_REVOKE_DENOMINATION "exchange-revoke-denomination-0"
+
+/**
+ * Name of the operation to 'revoke-signkey'
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_REVOKE_SIGNKEY "exchange-revoke-signkey-0"
+
+/**
+ * Show the offline signing key.
+ * The last component --by convention-- identifies the protocol version
+ * and should be incremented whenever the JSON format of the 'argument' changes.
+ */
+#define OP_SETUP "exchange-setup-0"
+
+/**
+ * sign the enabled and configured extensions.
+ */
+#define OP_EXTENSIONS "exchange-extensions-0"
+
+/**
+ * Generate message to drain profits.
+ */
+#define OP_DRAIN_PROFITS "exchange-drain-profits-0"
+
+/**
+ * Setup AML staff.
+ */
+#define OP_UPDATE_AML_STAFF "exchange-add-aml-staff-0"
+
+/**
+ * Setup partner exchange for wad transfers.
+ */
+#define OP_ADD_PARTNER "exchange-add-partner-0"
+
+/**
+ * Our private key, initialized in #load_offline_key().
+ */
+static struct TALER_MasterPrivateKeyP master_priv;
+
+/**
+ * Our private key, initialized in #load_offline_key().
+ */
+static struct TALER_MasterPublicKeyP master_pub;
+
+/**
+ * Our context for making HTTP requests.
+ */
+static struct GNUNET_CURL_Context *ctx;
+
+/**
+ * Reschedule context for #ctx.
+ */
+static struct GNUNET_CURL_RescheduleContext *rc;
+
+/**
+ * Handle to the exchange's configuration
+ */
+static const struct GNUNET_CONFIGURATION_Handle *kcfg;
+
+/**
+ * Age restriction configuration
+ */
+static bool ar_enabled = false;
+static struct TALER_AgeRestrictionConfig ar_config = {0};
+
+/**
+ * Return value from main().
+ */
+static int global_ret;
+
+/**
+ * Input to consume.
+ */
+static json_t *in;
+
+/**
+ * Array of actions to perform.
+ */
+static json_t *out;
+
+/**
+ * Currency we have configured.
+ */
+static char *currency;
+
+/**
+ * URL of the exchange we are interacting with
+ * as per our configuration.
+ */
+static char *CFG_exchange_url;
+
+/**
+ * A subcommand supported by this program.
+ */
+struct SubCommand
+{
+ /**
+ * Name of the command.
+ */
+ const char *name;
+
+ /**
+ * Help text for the command.
+ */
+ const char *help;
+
+ /**
+ * Function implementing the command.
+ *
+ * @param args subsequent command line arguments (char **)
+ */
+ void (*cb)(char *const *args);
+};
+
+
+/**
+ * Data structure for denomination revocation requests.
+ */
+struct DenomRevocationRequest
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct DenomRevocationRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct DenomRevocationRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle *h;
+
+ /**
+ * Array index of the associated command.
+ */
+ size_t idx;
+};
+
+
+/**
+ * Data structure for signkey revocation requests.
+ */
+struct SignkeyRevocationRequest
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct SignkeyRevocationRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct SignkeyRevocationRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle *h;
+
+ /**
+ * Array index of the associated command.
+ */
+ size_t idx;
+};
+
+
+/**
+ * Data structure for auditor add requests.
+ */
+struct AuditorAddRequest
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct AuditorAddRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct AuditorAddRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementAuditorEnableHandle *h;
+
+ /**
+ * Array index of the associated command.
+ */
+ size_t idx;
+};
+
+
+/**
+ * Data structure for auditor del requests.
+ */
+struct AuditorDelRequest
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct AuditorDelRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct AuditorDelRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementAuditorDisableHandle *h;
+
+ /**
+ * Array index of the associated command.
+ */
+ size_t idx;
+};
+
+
+/**
+ * Data structure for wire add requests.
+ */
+struct WireAddRequest
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct WireAddRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct WireAddRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementWireEnableHandle *h;
+
+ /**
+ * Array index of the associated command.
+ */
+ size_t idx;
+};
+
+
+/**
+ * Data structure for wire del requests.
+ */
+struct WireDelRequest
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct WireDelRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct WireDelRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementWireDisableHandle *h;
+
+ /**
+ * Array index of the associated command.
+ */
+ size_t idx;
+};
+
+
+/**
+ * Data structure for announcing wire fees.
+ */
+struct WireFeeRequest
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct WireFeeRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct WireFeeRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementSetWireFeeHandle *h;
+
+ /**
+ * Array index of the associated command.
+ */
+ size_t idx;
+};
+
+
+/**
+ * Data structure for draining profits.
+ */
+struct DrainProfitsRequest
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct DrainProfitsRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct DrainProfitsRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementDrainProfitsHandle *h;
+
+ /**
+ * Array index of the associated command.
+ */
+ size_t idx;
+};
+
+
+/**
+ * Data structure for announcing global fees.
+ */
+struct GlobalFeeRequest
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct GlobalFeeRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct GlobalFeeRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle *h;
+
+ /**
+ * Array index of the associated command.
+ */
+ size_t idx;
+};
+
+
+/**
+ * Ongoing /keys request.
+ */
+struct UploadKeysRequest
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct UploadKeysRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct UploadKeysRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementPostKeysHandle *h;
+
+ /**
+ * Operation index.
+ */
+ size_t idx;
+};
+
+/**
+ * Ongoing /management/extensions request.
+ */
+struct UploadExtensionsRequest
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct UploadExtensionsRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct UploadExtensionsRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementPostExtensionsHandle *h;
+
+ /**
+ * Operation index.
+ */
+ size_t idx;
+};
+
+
+/**
+ * Data structure for AML staff requests.
+ */
+struct AmlStaffRequest
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct AmlStaffRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct AmlStaffRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *h;
+
+ /**
+ * Array index of the associated command.
+ */
+ size_t idx;
+};
+
+
+/**
+ * Data structure for partner add requests.
+ */
+struct PartnerAddRequest
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct PartnerAddRequest *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct PartnerAddRequest *prev;
+
+ /**
+ * Operation handle.
+ */
+ struct TALER_EXCHANGE_ManagementAddPartner *h;
+
+ /**
+ * Array index of the associated command.
+ */
+ size_t idx;
+};
+
+
+/**
+ * Next work item to perform.
+ */
+static struct GNUNET_SCHEDULER_Task *nxt;
+
+/**
+ * Handle for #do_download.
+ */
+static struct TALER_EXCHANGE_ManagementGetKeysHandle *mgkh;
+
+
+/**
+ * Active AML staff change requests.
+ */
+static struct AmlStaffRequest *asr_head;
+
+/**
+ * Active AML staff change requests.
+ */
+static struct AmlStaffRequest *asr_tail;
+
+/**
+ * Active partner add requests.
+ */
+static struct PartnerAddRequest *par_head;
+
+/**
+ * Active partner add requests.
+ */
+static struct PartnerAddRequest *par_tail;
+
+/**
+ * Active denomiantion revocation requests.
+ */
+static struct DenomRevocationRequest *drr_head;
+
+/**
+ * Active denomiantion revocation requests.
+ */
+static struct DenomRevocationRequest *drr_tail;
+
+/**
+ * Active signkey revocation requests.
+ */
+static struct SignkeyRevocationRequest *srr_head;
+
+/**
+ * Active signkey revocation requests.
+ */
+static struct SignkeyRevocationRequest *srr_tail;
+
+/**
+ * Active auditor add requests.
+ */
+static struct AuditorAddRequest *aar_head;
+
+/**
+ * Active auditor add requests.
+ */
+static struct AuditorAddRequest *aar_tail;
+
+/**
+ * Active auditor del requests.
+ */
+static struct AuditorDelRequest *adr_head;
+
+/**
+ * Active auditor del requests.
+ */
+static struct AuditorDelRequest *adr_tail;
+
+/**
+ * Active wire add requests.
+ */
+static struct WireAddRequest *war_head;
+
+/**
+ * Active wire add requests.
+ */
+static struct WireAddRequest *war_tail;
+
+/**
+ * Active wire del requests.
+ */
+static struct WireDelRequest *wdr_head;
+
+/**
+ * Active wire del requests.
+ */
+static struct WireDelRequest *wdr_tail;
+
+/**
+ * Active wire fee requests.
+ */
+static struct WireFeeRequest *wfr_head;
+
+/**
+ * Active wire fee requests.
+ */
+static struct WireFeeRequest *wfr_tail;
+
+/**
+ * Active global fee requests.
+ */
+static struct GlobalFeeRequest *gfr_head;
+
+/**
+ * Active global fee requests.
+ */
+static struct GlobalFeeRequest *gfr_tail;
+
+/**
+ * Active keys upload requests.
+ */
+static struct UploadKeysRequest *ukr_head;
+
+/**
+ * Active keys upload requests.
+ */
+static struct UploadKeysRequest *ukr_tail;
+
+/**
+ * Active extensions upload requests.
+ */
+static struct UploadExtensionsRequest *uer_head;
+
+/**
+ * Active extensions upload requests.
+ */
+static struct UploadExtensionsRequest *uer_tail;
+
+/**
+ * Active drain profits requests.
+ */
+struct DrainProfitsRequest *dpr_head;
+
+/**
+ * Active drain profits requests.
+ */
+static struct DrainProfitsRequest *dpr_tail;
+
+
+/**
+ * Shutdown task. Invoked when the application is being terminated.
+ *
+ * @param cls NULL
+ */
+static void
+do_shutdown (void *cls)
+{
+ (void) cls;
+
+ {
+ struct AmlStaffRequest *asr;
+
+ while (NULL != (asr = asr_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete AML staff update #%u\n",
+ (unsigned int) asr->idx);
+ TALER_EXCHANGE_management_update_aml_officer_cancel (asr->h);
+ GNUNET_CONTAINER_DLL_remove (asr_head,
+ asr_tail,
+ asr);
+ GNUNET_free (asr);
+ }
+ }
+ {
+ struct PartnerAddRequest *par;
+
+ while (NULL != (par = par_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete partner add request #%u\n",
+ (unsigned int) par->idx);
+ TALER_EXCHANGE_management_add_partner_cancel (par->h);
+ GNUNET_CONTAINER_DLL_remove (par_head,
+ par_tail,
+ par);
+ GNUNET_free (par);
+ }
+ }
+ {
+ struct DenomRevocationRequest *drr;
+
+ while (NULL != (drr = drr_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete denomination revocation #%u\n",
+ (unsigned int) drr->idx);
+ TALER_EXCHANGE_management_revoke_denomination_key_cancel (drr->h);
+ GNUNET_CONTAINER_DLL_remove (drr_head,
+ drr_tail,
+ drr);
+ GNUNET_free (drr);
+ }
+ }
+ {
+ struct SignkeyRevocationRequest *srr;
+
+ while (NULL != (srr = srr_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete signkey revocation #%u\n",
+ (unsigned int) srr->idx);
+ TALER_EXCHANGE_management_revoke_signing_key_cancel (srr->h);
+ GNUNET_CONTAINER_DLL_remove (srr_head,
+ srr_tail,
+ srr);
+ GNUNET_free (srr);
+ }
+ }
+
+ {
+ struct AuditorAddRequest *aar;
+
+ while (NULL != (aar = aar_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete auditor add #%u\n",
+ (unsigned int) aar->idx);
+ TALER_EXCHANGE_management_enable_auditor_cancel (aar->h);
+ GNUNET_CONTAINER_DLL_remove (aar_head,
+ aar_tail,
+ aar);
+ GNUNET_free (aar);
+ }
+ }
+ {
+ struct AuditorDelRequest *adr;
+
+ while (NULL != (adr = adr_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete auditor del #%u\n",
+ (unsigned int) adr->idx);
+ TALER_EXCHANGE_management_disable_auditor_cancel (adr->h);
+ GNUNET_CONTAINER_DLL_remove (adr_head,
+ adr_tail,
+ adr);
+ GNUNET_free (adr);
+ }
+ }
+ {
+ struct WireAddRequest *war;
+
+ while (NULL != (war = war_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete wire add #%u\n",
+ (unsigned int) war->idx);
+ TALER_EXCHANGE_management_enable_wire_cancel (war->h);
+ GNUNET_CONTAINER_DLL_remove (war_head,
+ war_tail,
+ war);
+ GNUNET_free (war);
+ }
+ }
+ {
+ struct WireDelRequest *wdr;
+
+ while (NULL != (wdr = wdr_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete wire del #%u\n",
+ (unsigned int) wdr->idx);
+ TALER_EXCHANGE_management_disable_wire_cancel (wdr->h);
+ GNUNET_CONTAINER_DLL_remove (wdr_head,
+ wdr_tail,
+ wdr);
+ GNUNET_free (wdr);
+ }
+ }
+ {
+ struct WireFeeRequest *wfr;
+
+ while (NULL != (wfr = wfr_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete wire fee #%u\n",
+ (unsigned int) wfr->idx);
+ TALER_EXCHANGE_management_set_wire_fees_cancel (wfr->h);
+ GNUNET_CONTAINER_DLL_remove (wfr_head,
+ wfr_tail,
+ wfr);
+ GNUNET_free (wfr);
+ }
+ }
+ {
+ struct GlobalFeeRequest *gfr;
+
+ while (NULL != (gfr = gfr_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete global fee #%u\n",
+ (unsigned int) gfr->idx);
+ TALER_EXCHANGE_management_set_global_fees_cancel (gfr->h);
+ GNUNET_CONTAINER_DLL_remove (gfr_head,
+ gfr_tail,
+ gfr);
+ GNUNET_free (gfr);
+ }
+ }
+ {
+ struct UploadKeysRequest *ukr;
+
+ while (NULL != (ukr = ukr_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete key signature upload #%u\n",
+ (unsigned int) ukr->idx);
+ TALER_EXCHANGE_post_management_keys_cancel (ukr->h);
+ GNUNET_CONTAINER_DLL_remove (ukr_head,
+ ukr_tail,
+ ukr);
+ GNUNET_free (ukr);
+ }
+ }
+ {
+ struct UploadExtensionsRequest *uer;
+
+ while (NULL != (uer = uer_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete extensions signature upload #%u\n",
+ (unsigned int) uer->idx);
+ TALER_EXCHANGE_management_post_extensions_cancel (uer->h);
+ GNUNET_CONTAINER_DLL_remove (uer_head,
+ uer_tail,
+ uer);
+ GNUNET_free (uer);
+ }
+ }
+
+ {
+ struct DrainProfitsRequest *dpr;
+
+ while (NULL != (dpr = dpr_head))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Aborting incomplete drain profits request #%u\n",
+ (unsigned int) dpr->idx);
+ TALER_EXCHANGE_management_drain_profits_cancel (dpr->h);
+ GNUNET_CONTAINER_DLL_remove (dpr_head,
+ dpr_tail,
+ dpr);
+ GNUNET_free (dpr);
+ }
+ }
+
+ if (NULL != out)
+ {
+ if (EXIT_SUCCESS == global_ret)
+ json_dumpf (out,
+ stdout,
+ JSON_INDENT (2));
+ json_decref (out);
+ out = NULL;
+ }
+ if (NULL != in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Input not consumed!\n");
+ json_decref (in);
+ in = NULL;
+ }
+ if (NULL != nxt)
+ {
+ GNUNET_SCHEDULER_cancel (nxt);
+ nxt = NULL;
+ }
+ if (NULL != mgkh)
+ {
+ TALER_EXCHANGE_get_management_keys_cancel (mgkh);
+ mgkh = NULL;
+ }
+ if (NULL != ctx)
+ {
+ GNUNET_CURL_fini (ctx);
+ ctx = NULL;
+ }
+ if (NULL != rc)
+ {
+ GNUNET_CURL_gnunet_rc_destroy (rc);
+ rc = NULL;
+ }
+}
+
+
+/**
+ * Test if we should shut down because all tasks are done.
+ */
+static void
+test_shutdown (void)
+{
+ if ( (NULL == drr_head) &&
+ (NULL == par_head) &&
+ (NULL == asr_head) &&
+ (NULL == srr_head) &&
+ (NULL == aar_head) &&
+ (NULL == adr_head) &&
+ (NULL == war_head) &&
+ (NULL == wdr_head) &&
+ (NULL == wfr_head) &&
+ (NULL == gfr_head) &&
+ (NULL == ukr_head) &&
+ (NULL == uer_head) &&
+ (NULL == dpr_head) &&
+ (NULL == mgkh) &&
+ (NULL == nxt) )
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Function to continue processing the next command.
+ *
+ * @param cls must be a `char *const*` with the array of
+ * command-line arguments to process next
+ */
+static void
+work (void *cls);
+
+
+/**
+ * Function to schedule job to process the next command.
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+next (char *const *args)
+{
+ GNUNET_assert (NULL == nxt);
+ if (NULL == args[0])
+ {
+ test_shutdown ();
+ return;
+ }
+ nxt = GNUNET_SCHEDULER_add_now (&work,
+ (void *) args);
+}
+
+
+/**
+ * Add an operation to the #out JSON array for processing later.
+ *
+ * @param op_name name of the operation
+ * @param op_value values for the operation (consumed)
+ */
+static void
+output_operation (const char *op_name,
+ json_t *op_value)
+{
+ json_t *action;
+
+ GNUNET_break (NULL != op_value);
+ if (NULL == out)
+ {
+ out = json_array ();
+ GNUNET_assert (NULL != out);
+ }
+ action = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("operation",
+ op_name),
+ GNUNET_JSON_pack_object_steal ("arguments",
+ op_value));
+ GNUNET_assert (0 ==
+ json_array_append_new (out,
+ action));
+}
+
+
+/**
+ * Information about a subroutine for an upload.
+ */
+struct UploadHandler
+{
+ /**
+ * Key to trigger this subroutine.
+ */
+ const char *key;
+
+ /**
+ * Function implementing an upload.
+ *
+ * @param exchange_url URL of the exchange
+ * @param idx index of the operation we are performing
+ * @param value arguments to drive the upload.
+ */
+ void (*cb)(const char *exchange_url,
+ size_t idx,
+ const json_t *value);
+
+};
+
+
+/**
+ * Load the offline key (if not yet done). Triggers shutdown on failure.
+ *
+ * @param do_create #GNUNET_YES if the key may be created
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+load_offline_key (int do_create)
+{
+ static bool done;
+ int ret;
+ char *fn;
+
+ if (done)
+ return GNUNET_OK;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (kcfg,
+ "exchange-offline",
+ "MASTER_PRIV_FILE",
+ &fn))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange-offline",
+ "MASTER_PRIV_FILE");
+ test_shutdown ();
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_YES !=
+ GNUNET_DISK_file_test (fn))
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Exchange master private key `%s' does not exist yet, creating it!\n",
+ fn);
+ ret = GNUNET_CRYPTO_eddsa_key_from_file (fn,
+ do_create,
+ &master_priv.eddsa_priv);
+ if (GNUNET_SYSERR == ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to initialize master key from file `%s': %s\n",
+ fn,
+ "could not create file");
+ GNUNET_free (fn);
+ test_shutdown ();
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (fn);
+ GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv,
+ &master_pub.eddsa_pub);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Using master public key %s\n",
+ TALER_B2S (&master_pub));
+ done = true;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called with information about the post revocation operation result.
+ *
+ * @param cls closure with a `struct DenomRevocationRequest`
+ * @param dr response data
+ */
+static void
+denom_revocation_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_ManagementRevokeDenominationResponse *dr)
+{
+ struct DenomRevocationRequest *drr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &dr->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) drr->idx,
+ hr->http_status,
+ hr->hint,
+ TALER_JSON_get_error_hint (hr->reply));
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (drr_head,
+ drr_tail,
+ drr);
+ GNUNET_free (drr);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload denomination revocation request data.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for denomination revocation
+ */
+static void
+upload_denom_revocation (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ struct TALER_MasterSignatureP master_sig;
+ struct TALER_DenominationHashP h_denom_pub;
+ struct DenomRevocationRequest *drr;
+ const char *err_name;
+ unsigned int err_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
+ &h_denom_pub),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input for denomination revocation: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ drr = GNUNET_new (struct DenomRevocationRequest);
+ drr->idx = idx;
+ drr->h =
+ TALER_EXCHANGE_management_revoke_denomination_key (ctx,
+ exchange_url,
+ &h_denom_pub,
+ &master_sig,
+ &denom_revocation_cb,
+ drr);
+ GNUNET_CONTAINER_DLL_insert (drr_head,
+ drr_tail,
+ drr);
+}
+
+
+/**
+ * Function called with information about the post revocation operation result.
+ *
+ * @param cls closure with a `struct SignkeyRevocationRequest`
+ * @param sr response data
+ */
+static void
+signkey_revocation_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse *sr)
+{
+ struct SignkeyRevocationRequest *srr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &sr->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) srr->idx,
+ hr->http_status,
+ hr->hint,
+ TALER_JSON_get_error_hint (hr->reply));
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (srr_head,
+ srr_tail,
+ srr);
+ GNUNET_free (srr);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload signkey revocation request data.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for denomination revocation
+ */
+static void
+upload_signkey_revocation (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ struct TALER_MasterSignatureP master_sig;
+ struct TALER_ExchangePublicKeyP exchange_pub;
+ struct SignkeyRevocationRequest *srr;
+ const char *err_name;
+ unsigned int err_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("exchange_pub",
+ &exchange_pub),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input for signkey revocation: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ srr = GNUNET_new (struct SignkeyRevocationRequest);
+ srr->idx = idx;
+ srr->h =
+ TALER_EXCHANGE_management_revoke_signing_key (ctx,
+ exchange_url,
+ &exchange_pub,
+ &master_sig,
+ &signkey_revocation_cb,
+ srr);
+ GNUNET_CONTAINER_DLL_insert (srr_head,
+ srr_tail,
+ srr);
+}
+
+
+/**
+ * Function called with information about the post auditor add operation result.
+ *
+ * @param cls closure with a `struct AuditorAddRequest`
+ * @param mer response data
+ */
+static void
+auditor_add_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_ManagementAuditorEnableResponse *mer)
+{
+ struct AuditorAddRequest *aar = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &mer->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) aar->idx,
+ hr->http_status,
+ TALER_ErrorCode_get_hint (hr->ec),
+ hr->hint);
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (aar_head,
+ aar_tail,
+ aar);
+ GNUNET_free (aar);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload auditor add data.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for denomination revocation
+ */
+static void
+upload_auditor_add (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ struct TALER_MasterSignatureP master_sig;
+ const char *auditor_url;
+ const char *auditor_name;
+ struct GNUNET_TIME_Timestamp start_time;
+ struct TALER_AuditorPublicKeyP auditor_pub;
+ struct AuditorAddRequest *aar;
+ const char *err_name;
+ unsigned int err_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_web_url ("auditor_url",
+ &auditor_url),
+ GNUNET_JSON_spec_string ("auditor_name",
+ &auditor_name),
+ GNUNET_JSON_spec_timestamp ("validity_start",
+ &start_time),
+ GNUNET_JSON_spec_fixed_auto ("auditor_pub",
+ &auditor_pub),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input for adding auditor: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ aar = GNUNET_new (struct AuditorAddRequest);
+ aar->idx = idx;
+ aar->h =
+ TALER_EXCHANGE_management_enable_auditor (ctx,
+ exchange_url,
+ &auditor_pub,
+ auditor_url,
+ auditor_name,
+ start_time,
+ &master_sig,
+ &auditor_add_cb,
+ aar);
+ GNUNET_CONTAINER_DLL_insert (aar_head,
+ aar_tail,
+ aar);
+}
+
+
+/**
+ * Function called with information about the post auditor del operation result.
+ *
+ * @param cls closure with a `struct AuditorDelRequest`
+ * @param mdr response data
+ */
+static void
+auditor_del_cb (void *cls,
+ const struct
+ TALER_EXCHANGE_ManagementAuditorDisableResponse *mdr)
+{
+ struct AuditorDelRequest *adr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) adr->idx,
+ hr->http_status,
+ TALER_ErrorCode_get_hint (hr->ec),
+ hr->hint);
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (adr_head,
+ adr_tail,
+ adr);
+ GNUNET_free (adr);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload auditor del data.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for denomination revocation
+ */
+static void
+upload_auditor_del (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ struct TALER_AuditorPublicKeyP auditor_pub;
+ struct TALER_MasterSignatureP master_sig;
+ struct GNUNET_TIME_Timestamp end_time;
+ struct AuditorDelRequest *adr;
+ const char *err_name;
+ unsigned int err_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("auditor_pub",
+ &auditor_pub),
+ GNUNET_JSON_spec_timestamp ("validity_end",
+ &end_time),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to disable auditor: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ adr = GNUNET_new (struct AuditorDelRequest);
+ adr->idx = idx;
+ adr->h =
+ TALER_EXCHANGE_management_disable_auditor (ctx,
+ exchange_url,
+ &auditor_pub,
+ end_time,
+ &master_sig,
+ &auditor_del_cb,
+ adr);
+ GNUNET_CONTAINER_DLL_insert (adr_head,
+ adr_tail,
+ adr);
+}
+
+
+/**
+ * Function called with information about the post wire add operation result.
+ *
+ * @param cls closure with a `struct WireAddRequest`
+ * @param wer response data
+ */
+static void
+wire_add_cb (void *cls,
+ const struct TALER_EXCHANGE_ManagementWireEnableResponse *wer)
+{
+ struct WireAddRequest *war = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &wer->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) war->idx,
+ hr->http_status,
+ TALER_ErrorCode_get_hint (hr->ec),
+ hr->hint);
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (war_head,
+ war_tail,
+ war);
+ GNUNET_free (war);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload wire add data.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for denomination revocation
+ */
+static void
+upload_wire_add (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ struct TALER_MasterSignatureP master_sig_add;
+ struct TALER_MasterSignatureP master_sig_wire;
+ const char *payto_uri;
+ struct GNUNET_TIME_Timestamp start_time;
+ struct WireAddRequest *war;
+ const char *err_name;
+ const char *conversion_url = NULL;
+ const char *bank_label = NULL;
+ int64_t priority = 0;
+ const json_t *debit_restrictions;
+ const json_t *credit_restrictions;
+ unsigned int err_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_payto_uri ("payto_uri",
+ &payto_uri),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_web_url ("conversion_url",
+ &conversion_url),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("bank_label",
+ &bank_label),
+ NULL),
+ GNUNET_JSON_spec_int64 ("priority",
+ &priority),
+ GNUNET_JSON_spec_array_const ("debit_restrictions",
+ &debit_restrictions),
+ GNUNET_JSON_spec_array_const ("credit_restrictions",
+ &credit_restrictions),
+ GNUNET_JSON_spec_timestamp ("validity_start",
+ &start_time),
+ GNUNET_JSON_spec_fixed_auto ("master_sig_add",
+ &master_sig_add),
+ GNUNET_JSON_spec_fixed_auto ("master_sig_wire",
+ &master_sig_wire),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input for adding wire account: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ {
+ char *wire_method;
+
+ wire_method = TALER_payto_get_method (payto_uri);
+ if (NULL == wire_method)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "payto:// URI `%s' is malformed\n",
+ payto_uri);
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ GNUNET_free (wire_method);
+ }
+ {
+ char *msg = TALER_payto_validate (payto_uri);
+
+ if (NULL != msg)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "payto URI is malformed: %s\n",
+ msg);
+ GNUNET_free (msg);
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ }
+ war = GNUNET_new (struct WireAddRequest);
+ war->idx = idx;
+ war->h =
+ TALER_EXCHANGE_management_enable_wire (ctx,
+ exchange_url,
+ payto_uri,
+ conversion_url,
+ debit_restrictions,
+ credit_restrictions,
+ start_time,
+ &master_sig_add,
+ &master_sig_wire,
+ bank_label,
+ priority,
+ &wire_add_cb,
+ war);
+ GNUNET_CONTAINER_DLL_insert (war_head,
+ war_tail,
+ war);
+}
+
+
+/**
+ * Function called with information about the post wire del operation result.
+ *
+ * @param cls closure with a `struct WireDelRequest`
+ * @param wdres response data
+ */
+static void
+wire_del_cb (void *cls,
+ const struct TALER_EXCHANGE_ManagementWireDisableResponse *wdres)
+{
+ struct WireDelRequest *wdr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &wdres->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) wdr->idx,
+ hr->http_status,
+ TALER_ErrorCode_get_hint (hr->ec),
+ hr->hint);
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (wdr_head,
+ wdr_tail,
+ wdr);
+ GNUNET_free (wdr);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload wire del data.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for denomination revocation
+ */
+static void
+upload_wire_del (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ struct TALER_MasterSignatureP master_sig;
+ const char *payto_uri;
+ struct GNUNET_TIME_Timestamp end_time;
+ struct WireDelRequest *wdr;
+ const char *err_name;
+ unsigned int err_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_payto_uri ("payto_uri",
+ &payto_uri),
+ GNUNET_JSON_spec_timestamp ("validity_end",
+ &end_time),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to disable wire account: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ wdr = GNUNET_new (struct WireDelRequest);
+ wdr->idx = idx;
+ wdr->h =
+ TALER_EXCHANGE_management_disable_wire (ctx,
+ exchange_url,
+ payto_uri,
+ end_time,
+ &master_sig,
+ &wire_del_cb,
+ wdr);
+ GNUNET_CONTAINER_DLL_insert (wdr_head,
+ wdr_tail,
+ wdr);
+}
+
+
+/**
+ * Function called with information about the post wire fee operation result.
+ *
+ * @param cls closure with a `struct WireFeeRequest`
+ * @param swr response data
+ */
+static void
+wire_fee_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_ManagementSetWireFeeResponse *swr)
+{
+ struct WireFeeRequest *wfr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &swr->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) wfr->idx,
+ hr->http_status,
+ TALER_ErrorCode_get_hint (hr->ec),
+ hr->hint);
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (wfr_head,
+ wfr_tail,
+ wfr);
+ GNUNET_free (wfr);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload wire fee.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for denomination revocation
+ */
+static void
+upload_wire_fee (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ struct TALER_MasterSignatureP master_sig;
+ const char *wire_method;
+ struct WireFeeRequest *wfr;
+ const char *err_name;
+ unsigned int err_line;
+ struct TALER_WireFeeSet fees;
+ struct GNUNET_TIME_Timestamp start_time;
+ struct GNUNET_TIME_Timestamp end_time;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("wire_method",
+ &wire_method),
+ TALER_JSON_spec_amount ("wire_fee",
+ currency,
+ &fees.wire),
+ TALER_JSON_spec_amount ("closing_fee",
+ currency,
+ &fees.closing),
+ GNUNET_JSON_spec_timestamp ("start_time",
+ &start_time),
+ GNUNET_JSON_spec_timestamp ("end_time",
+ &end_time),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to set wire fee: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ wfr = GNUNET_new (struct WireFeeRequest);
+ wfr->idx = idx;
+ wfr->h =
+ TALER_EXCHANGE_management_set_wire_fees (ctx,
+ exchange_url,
+ wire_method,
+ start_time,
+ end_time,
+ &fees,
+ &master_sig,
+ &wire_fee_cb,
+ wfr);
+ GNUNET_CONTAINER_DLL_insert (wfr_head,
+ wfr_tail,
+ wfr);
+}
+
+
+/**
+ * Function called with information about the post global fee operation result.
+ *
+ * @param cls closure with a `struct WireFeeRequest`
+ * @param gr response data
+ */
+static void
+global_fee_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_ManagementSetGlobalFeeResponse *gr)
+{
+ struct GlobalFeeRequest *gfr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &gr->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) gfr->idx,
+ hr->http_status,
+ TALER_ErrorCode_get_hint (hr->ec),
+ hr->hint);
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (gfr_head,
+ gfr_tail,
+ gfr);
+ GNUNET_free (gfr);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload global fee.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for denomination revocation
+ */
+static void
+upload_global_fee (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ struct TALER_MasterSignatureP master_sig;
+ struct GlobalFeeRequest *gfr;
+ const char *err_name;
+ unsigned int err_line;
+ struct TALER_GlobalFeeSet fees;
+ struct GNUNET_TIME_Timestamp start_time;
+ struct GNUNET_TIME_Timestamp end_time;
+ struct GNUNET_TIME_Relative purse_timeout;
+ struct GNUNET_TIME_Relative history_expiration;
+ uint32_t purse_account_limit;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount ("history_fee",
+ currency,
+ &fees.history),
+ TALER_JSON_spec_amount ("account_fee",
+ currency,
+ &fees.account),
+ TALER_JSON_spec_amount ("purse_fee",
+ currency,
+ &fees.purse),
+ GNUNET_JSON_spec_relative_time ("purse_timeout",
+ &purse_timeout),
+ GNUNET_JSON_spec_relative_time ("history_expiration",
+ &history_expiration),
+ GNUNET_JSON_spec_uint32 ("purse_account_limit",
+ &purse_account_limit),
+ GNUNET_JSON_spec_timestamp ("start_time",
+ &start_time),
+ GNUNET_JSON_spec_timestamp ("end_time",
+ &end_time),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to set wire fee: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ gfr = GNUNET_new (struct GlobalFeeRequest);
+ gfr->idx = idx;
+ gfr->h =
+ TALER_EXCHANGE_management_set_global_fees (ctx,
+ exchange_url,
+ start_time,
+ end_time,
+ &fees,
+ purse_timeout,
+ history_expiration,
+ purse_account_limit,
+ &master_sig,
+ &global_fee_cb,
+ gfr);
+ GNUNET_CONTAINER_DLL_insert (gfr_head,
+ gfr_tail,
+ gfr);
+}
+
+
+/**
+ * Function called with information about the drain profits operation.
+ *
+ * @param cls closure with a `struct DrainProfitsRequest`
+ * @param mdr response data
+ */
+static void
+drain_profits_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_ManagementDrainResponse *mdr)
+{
+ struct DrainProfitsRequest *dpr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) dpr->idx,
+ hr->http_status,
+ TALER_ErrorCode_get_hint (hr->ec),
+ hr->hint);
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (dpr_head,
+ dpr_tail,
+ dpr);
+ GNUNET_free (dpr);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload drain profit action.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for drain profits
+ */
+static void
+upload_drain (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ struct TALER_WireTransferIdentifierRawP wtid;
+ struct TALER_MasterSignatureP master_sig;
+ const char *err_name;
+ unsigned int err_line;
+ struct TALER_Amount amount;
+ struct GNUNET_TIME_Timestamp date;
+ const char *payto_uri;
+ const char *account_section;
+ struct DrainProfitsRequest *dpr;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("wtid",
+ &wtid),
+ TALER_JSON_spec_amount ("amount",
+ currency,
+ &amount),
+ GNUNET_JSON_spec_timestamp ("date",
+ &date),
+ GNUNET_JSON_spec_string ("account_section",
+ &account_section),
+ TALER_JSON_spec_payto_uri ("payto_uri",
+ &payto_uri),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to drain profits: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ dpr = GNUNET_new (struct DrainProfitsRequest);
+ dpr->idx = idx;
+ dpr->h =
+ TALER_EXCHANGE_management_drain_profits (ctx,
+ exchange_url,
+ &wtid,
+ &amount,
+ date,
+ account_section,
+ payto_uri,
+ &master_sig,
+ &drain_profits_cb,
+ dpr);
+ GNUNET_CONTAINER_DLL_insert (dpr_head,
+ dpr_tail,
+ dpr);
+}
+
+
+/**
+ * Function called with information about the post upload keys operation result.
+ *
+ * @param cls closure with a `struct UploadKeysRequest`
+ * @param mr response data
+ */
+static void
+keys_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_ManagementPostKeysResponse *mr)
+{
+ struct UploadKeysRequest *ukr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) ukr->idx,
+ hr->http_status,
+ TALER_ErrorCode_get_hint (hr->ec),
+ hr->hint);
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (ukr_head,
+ ukr_tail,
+ ukr);
+ GNUNET_free (ukr);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload (denomination and signing) key master signatures.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for POSTing keys
+ */
+static void
+upload_keys (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ struct TALER_EXCHANGE_ManagementPostKeysData pkd;
+ struct UploadKeysRequest *ukr;
+ const char *err_name;
+ unsigned int err_line;
+ const json_t *denom_sigs;
+ const json_t *signkey_sigs;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("denom_sigs",
+ &denom_sigs),
+ GNUNET_JSON_spec_array_const ("signkey_sigs",
+ &signkey_sigs),
+ GNUNET_JSON_spec_end ()
+ };
+ bool ok = true;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to 'upload': %s#%u (skipping)\n",
+ err_name,
+ err_line);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ pkd.num_sign_sigs = json_array_size (signkey_sigs);
+ pkd.num_denom_sigs = json_array_size (denom_sigs);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Uploading %u denomination and %u signing key signatures\n",
+ pkd.num_denom_sigs,
+ pkd.num_sign_sigs);
+ pkd.sign_sigs = GNUNET_new_array (
+ pkd.num_sign_sigs,
+ struct TALER_EXCHANGE_SigningKeySignature);
+ pkd.denom_sigs = GNUNET_new_array (
+ pkd.num_denom_sigs,
+ struct TALER_EXCHANGE_DenominationKeySignature);
+ for (unsigned int i = 0; i<pkd.num_sign_sigs; i++)
+ {
+ struct TALER_EXCHANGE_SigningKeySignature *ss = &pkd.sign_sigs[i];
+ json_t *val = json_array_get (signkey_sigs,
+ i);
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("exchange_pub",
+ &ss->exchange_pub),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &ss->master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (val,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input for signkey validity: %s#%u at %u (aborting)\n",
+ err_name,
+ err_line,
+ i);
+ json_dumpf (val,
+ stderr,
+ JSON_INDENT (2));
+ ok = false;
+ }
+ }
+ for (unsigned int i = 0; i<pkd.num_denom_sigs; i++)
+ {
+ struct TALER_EXCHANGE_DenominationKeySignature *ds = &pkd.denom_sigs[i];
+ json_t *val = json_array_get (denom_sigs,
+ i);
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
+ &ds->h_denom_pub),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &ds->master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (val,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input for denomination validity: %s#%u at %u (aborting)\n",
+ err_name,
+ err_line,
+ i);
+ json_dumpf (val,
+ stderr,
+ JSON_INDENT (2));
+ ok = false;
+ }
+ }
+
+ if (ok)
+ {
+ ukr = GNUNET_new (struct UploadKeysRequest);
+ ukr->idx = idx;
+ ukr->h =
+ TALER_EXCHANGE_post_management_keys (ctx,
+ exchange_url,
+ &pkd,
+ &keys_cb,
+ ukr);
+ GNUNET_CONTAINER_DLL_insert (ukr_head,
+ ukr_tail,
+ ukr);
+ }
+ else
+ {
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ }
+ GNUNET_free (pkd.sign_sigs);
+ GNUNET_free (pkd.denom_sigs);
+}
+
+
+/**
+ * Function called with information about the post upload extensions operation result.
+ *
+ * @param cls closure with a `struct UploadExtensionsRequest`
+ * @param er response data
+ */
+static void
+extensions_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_ManagementPostExtensionsResponse *er)
+{
+ struct UploadExtensionsRequest *uer = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &er->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) uer->idx,
+ hr->http_status,
+ TALER_ErrorCode_get_hint (hr->ec),
+ hr->hint);
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (uer_head,
+ uer_tail,
+ uer);
+ GNUNET_free (uer);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload extension configuration
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for POSTing configurations of extensions
+ */
+static void
+upload_extensions (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ const json_t *extensions;
+ struct TALER_MasterSignatureP sig;
+ const char *err_name;
+ unsigned int err_line;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_object_const ("extensions",
+ &extensions),
+ GNUNET_JSON_spec_fixed_auto ("extensions_sig",
+ &sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ /* 1. Parse the signed extensions */
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to set extensions: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+
+ /* 2. Verify the signature */
+ {
+ struct TALER_ExtensionManifestsHashP h_manifests;
+
+ if (GNUNET_OK !=
+ TALER_JSON_extensions_manifests_hash (extensions,
+ &h_manifests))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "couldn't hash extensions' manifests\n");
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+
+ if (GNUNET_OK != TALER_exchange_offline_extension_manifests_hash_verify (
+ &h_manifests,
+ &master_pub,
+ &sig))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "invalid signature for extensions\n");
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ }
+
+ /* 3. Upload the extensions */
+ {
+ struct TALER_EXCHANGE_ManagementPostExtensionsData ped = {
+ .extensions = extensions,
+ .extensions_sig = sig,
+ };
+ struct UploadExtensionsRequest *uer
+ = GNUNET_new (struct UploadExtensionsRequest);
+
+ uer->idx = idx;
+ uer->h = TALER_EXCHANGE_management_post_extensions (
+ ctx,
+ exchange_url,
+ &ped,
+ &extensions_cb,
+ uer);
+ GNUNET_CONTAINER_DLL_insert (uer_head,
+ uer_tail,
+ uer);
+ }
+}
+
+
+/**
+ * Function called with information about the add partner operation.
+ *
+ * @param cls closure with a `struct PartnerAddRequest`
+ * @param apr response data
+ */
+static void
+add_partner_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_ManagementAddPartnerResponse *apr)
+{
+ struct PartnerAddRequest *par = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &apr->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) par->idx,
+ hr->http_status,
+ TALER_ErrorCode_get_hint (hr->ec),
+ hr->hint);
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (par_head,
+ par_tail,
+ par);
+ GNUNET_free (par);
+ test_shutdown ();
+}
+
+
+/**
+ * Add partner action.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for add partner
+ */
+static void
+add_partner (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ struct TALER_MasterPublicKeyP partner_pub;
+ struct GNUNET_TIME_Timestamp start_date;
+ struct GNUNET_TIME_Timestamp end_date;
+ struct GNUNET_TIME_Relative wad_frequency;
+ struct TALER_Amount wad_fee;
+ const char *partner_base_url;
+ struct TALER_MasterSignatureP master_sig;
+ struct PartnerAddRequest *par;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("partner_pub",
+ &partner_pub),
+ TALER_JSON_spec_amount ("wad_fee",
+ currency,
+ &wad_fee),
+ GNUNET_JSON_spec_relative_time ("wad_frequency",
+ &wad_frequency),
+ GNUNET_JSON_spec_timestamp ("start_date",
+ &start_date),
+ GNUNET_JSON_spec_timestamp ("end_date",
+ &end_date),
+ TALER_JSON_spec_web_url ("partner_base_url",
+ &partner_base_url),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+ const char *err_name;
+ unsigned int err_line;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to add partner: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ par = GNUNET_new (struct PartnerAddRequest);
+ par->idx = idx;
+ par->h =
+ TALER_EXCHANGE_management_add_partner (ctx,
+ exchange_url,
+ &partner_pub,
+ start_date,
+ end_date,
+ wad_frequency,
+ &wad_fee,
+ partner_base_url,
+ &master_sig,
+ &add_partner_cb,
+ par);
+ GNUNET_CONTAINER_DLL_insert (par_head,
+ par_tail,
+ par);
+}
+
+
+/**
+ * Function called with information about the AML officer update operation.
+ *
+ * @param cls closure with a `struct AmlStaffRequest`
+ * @param ar response data
+ */
+static void
+update_aml_officer_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse *ar)
+{
+ struct AmlStaffRequest *asr = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr;
+
+ if (MHD_HTTP_NO_CONTENT != hr->http_status)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload failed for command %u with status %u: %s (%s)\n",
+ (unsigned int) asr->idx,
+ hr->http_status,
+ TALER_ErrorCode_get_hint (hr->ec),
+ hr->hint);
+ global_ret = EXIT_FAILURE;
+ }
+ GNUNET_CONTAINER_DLL_remove (asr_head,
+ asr_tail,
+ asr);
+ GNUNET_free (asr);
+ test_shutdown ();
+}
+
+
+/**
+ * Upload AML staff action.
+ *
+ * @param exchange_url base URL of the exchange
+ * @param idx index of the operation we are performing (for logging)
+ * @param value arguments for AML staff change
+ */
+static void
+update_aml_staff (const char *exchange_url,
+ size_t idx,
+ const json_t *value)
+{
+ struct TALER_AmlOfficerPublicKeyP officer_pub;
+ const char *officer_name;
+ struct GNUNET_TIME_Timestamp change_date;
+ bool is_active;
+ bool read_only;
+ struct TALER_MasterSignatureP master_sig;
+ struct AmlStaffRequest *asr;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("officer_pub",
+ &officer_pub),
+ GNUNET_JSON_spec_timestamp ("change_date",
+ &change_date),
+ GNUNET_JSON_spec_bool ("is_active",
+ &is_active),
+ GNUNET_JSON_spec_bool ("read_only",
+ &read_only),
+ GNUNET_JSON_spec_string ("officer_name",
+ &officer_name),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+ const char *err_name;
+ unsigned int err_line;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to AML staff update: %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) idx);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ asr = GNUNET_new (struct AmlStaffRequest);
+ asr->idx = idx;
+ asr->h =
+ TALER_EXCHANGE_management_update_aml_officer (ctx,
+ exchange_url,
+ &officer_pub,
+ officer_name,
+ change_date,
+ is_active,
+ read_only,
+ &master_sig,
+ &update_aml_officer_cb,
+ asr);
+ GNUNET_CONTAINER_DLL_insert (asr_head,
+ asr_tail,
+ asr);
+}
+
+
+/**
+ * Perform uploads based on the JSON in #out.
+ *
+ * @param exchange_url base URL of the exchange to use
+ */
+static void
+trigger_upload (const char *exchange_url)
+{
+ struct UploadHandler uhs[] = {
+ {
+ .key = OP_REVOKE_DENOMINATION,
+ .cb = &upload_denom_revocation
+ },
+ {
+ .key = OP_REVOKE_SIGNKEY,
+ .cb = &upload_signkey_revocation
+ },
+ {
+ .key = OP_ENABLE_AUDITOR,
+ .cb = &upload_auditor_add
+ },
+ {
+ .key = OP_DISABLE_AUDITOR,
+ .cb = &upload_auditor_del
+ },
+ {
+ .key = OP_ENABLE_WIRE,
+ .cb = &upload_wire_add
+ },
+ {
+ .key = OP_DISABLE_WIRE,
+ .cb = &upload_wire_del
+ },
+ {
+ .key = OP_SET_WIRE_FEE,
+ .cb = &upload_wire_fee
+ },
+ {
+ .key = OP_SET_GLOBAL_FEE,
+ .cb = &upload_global_fee
+ },
+ {
+ .key = OP_UPLOAD_SIGS,
+ .cb = &upload_keys
+ },
+ {
+ .key = OP_DRAIN_PROFITS,
+ .cb = &upload_drain
+ },
+ {
+ .key = OP_EXTENSIONS,
+ .cb = &upload_extensions
+ },
+ {
+ .key = OP_UPDATE_AML_STAFF,
+ .cb = &update_aml_staff
+ },
+ {
+ .key = OP_ADD_PARTNER,
+ .cb = &add_partner
+ },
+ /* array termination */
+ {
+ .key = NULL
+ }
+ };
+ size_t index;
+ json_t *obj;
+
+ json_array_foreach (out, index, obj) {
+ bool found = false;
+ const char *key;
+ const json_t *value;
+
+ key = json_string_value (json_object_get (obj, "operation"));
+ value = json_object_get (obj, "arguments");
+ if (NULL == key)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Malformed JSON input\n");
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ /* block of code that uses key and value */
+ for (unsigned int i = 0; NULL != uhs[i].key; i++)
+ {
+ if (0 == strcasecmp (key,
+ uhs[i].key))
+ {
+
+ found = true;
+ uhs[i].cb (exchange_url,
+ index,
+ value);
+ break;
+ }
+ }
+ if (! found)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Upload does not know how to handle `%s'\n",
+ key);
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ }
+}
+
+
+/**
+ * Upload operation result (signatures) to exchange.
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+do_upload (char *const *args)
+{
+ (void) args;
+ if (NULL != in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Downloaded data was not consumed, refusing upload\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if (NULL == out)
+ {
+ json_error_t err;
+
+ out = json_loadf (stdin,
+ JSON_REJECT_DUPLICATES,
+ &err);
+ if (NULL == out)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
+ err.text,
+ err.line,
+ err.source,
+ err.position);
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ }
+ if (! json_is_array (out))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Error: expected JSON array for `upload` command\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if ( (NULL == CFG_exchange_url) &&
+ (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (kcfg,
+ "exchange",
+ "BASE_URL",
+ &CFG_exchange_url)) )
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "BASE_URL");
+ global_ret = EXIT_NOTCONFIGURED;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ trigger_upload (CFG_exchange_url);
+ json_decref (out);
+ out = NULL;
+}
+
+
+/**
+ * Revoke denomination key.
+ *
+ * @param args the array of command-line arguments to process next;
+ * args[0] must be the hash of the denomination key to revoke
+ */
+static void
+do_revoke_denomination_key (char *const *args)
+{
+ struct TALER_DenominationHashP h_denom_pub;
+ struct TALER_MasterSignatureP master_sig;
+
+ if (NULL != in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Downloaded data was not consumed, refusing revocation\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if ( (NULL == args[0]) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (args[0],
+ strlen (args[0]),
+ &h_denom_pub,
+ sizeof (h_denom_pub))) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must specify a denomination key with this subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+ TALER_exchange_offline_denomination_revoke_sign (&h_denom_pub,
+ &master_priv,
+ &master_sig);
+ output_operation (OP_REVOKE_DENOMINATION,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("h_denom_pub",
+ &h_denom_pub),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &master_sig)));
+ next (args + 1);
+}
+
+
+/**
+ * Revoke signkey.
+ *
+ * @param args the array of command-line arguments to process next;
+ * args[0] must be the hash of the denomination key to revoke
+ */
+static void
+do_revoke_signkey (char *const *args)
+{
+ struct TALER_ExchangePublicKeyP exchange_pub;
+ struct TALER_MasterSignatureP master_sig;
+
+ if (NULL != in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Downloaded data was not consumed, refusing revocation\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if ( (NULL == args[0]) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (args[0],
+ strlen (args[0]),
+ &exchange_pub,
+ sizeof (exchange_pub))) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must specify an exchange signing key with this subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+ TALER_exchange_offline_signkey_revoke_sign (&exchange_pub,
+ &master_priv,
+ &master_sig);
+ output_operation (OP_REVOKE_SIGNKEY,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("exchange_pub",
+ &exchange_pub),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &master_sig)));
+ next (args + 1);
+}
+
+
+/**
+ * Add auditor.
+ *
+ * @param args the array of command-line arguments to process next;
+ * args[0] must be the auditor's public key, args[1] the auditor's
+ * API base URL, and args[2] the auditor's name.
+ */
+static void
+do_add_auditor (char *const *args)
+{
+ struct TALER_MasterSignatureP master_sig;
+ struct TALER_AuditorPublicKeyP auditor_pub;
+ struct GNUNET_TIME_Timestamp now;
+
+ if (NULL != in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Downloaded data was not consumed, not adding auditor\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if ( (NULL == args[0]) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (args[0],
+ strlen (args[0]),
+ &auditor_pub,
+ sizeof (auditor_pub))) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must specify an auditor public key as first argument for this subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+
+ if ( (NULL == args[1]) ||
+ (0 != strncmp ("http",
+ args[1],
+ strlen ("http"))) ||
+ (NULL == args[2]) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must specify an auditor URI and auditor name as 2nd and 3rd arguments to this subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+ now = GNUNET_TIME_timestamp_get ();
+ TALER_exchange_offline_auditor_add_sign (&auditor_pub,
+ args[1],
+ now,
+ &master_priv,
+ &master_sig);
+ output_operation (OP_ENABLE_AUDITOR,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("auditor_url",
+ args[1]),
+ GNUNET_JSON_pack_string ("auditor_name",
+ args[2]),
+ GNUNET_JSON_pack_timestamp ("validity_start",
+ now),
+ GNUNET_JSON_pack_data_auto ("auditor_pub",
+ &auditor_pub),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &master_sig)));
+ next (args + 3);
+}
+
+
+/**
+ * Disable auditor account.
+ *
+ * @param args the array of command-line arguments to process next;
+ * args[0] must be the hash of the denomination key to revoke
+ */
+static void
+do_del_auditor (char *const *args)
+{
+ struct TALER_MasterSignatureP master_sig;
+ struct TALER_AuditorPublicKeyP auditor_pub;
+ struct GNUNET_TIME_Timestamp now;
+
+ if (NULL != in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Downloaded data was not consumed, not deleting auditor account\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if ( (NULL == args[0]) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (args[0],
+ strlen (args[0]),
+ &auditor_pub,
+ sizeof (auditor_pub))) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must specify an auditor public key as first argument for this subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+ now = GNUNET_TIME_timestamp_get ();
+ TALER_exchange_offline_auditor_del_sign (&auditor_pub,
+ now,
+ &master_priv,
+ &master_sig);
+ output_operation (OP_DISABLE_AUDITOR,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("auditor_pub",
+ &auditor_pub),
+ GNUNET_JSON_pack_timestamp ("validity_end",
+ now),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &master_sig)));
+ next (args + 1);
+}
+
+
+/**
+ * Parse account restriction.
+ *
+ * @param args the array of command-line arguments to process next
+ * @param[in,out] restrictions JSON array to update
+ * @return -1 on error, otherwise number of arguments from @a args that were used
+ */
+static int
+parse_restriction (char *const *args,
+ json_t *restrictions)
+{
+ if (NULL == args[0])
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Restriction TYPE argument missing\n");
+ return -1;
+ }
+ if (0 == strcmp (args[0],
+ "deny"))
+ {
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ restrictions,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("type",
+ "deny"))));
+ return 1;
+ }
+ if (0 == strcmp (args[0],
+ "regex"))
+ {
+ json_t *i18n;
+ json_error_t err;
+
+ if ( (NULL == args[1]) ||
+ (NULL == args[2]) ||
+ (NULL == args[3]) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Mandatory arguments for restriction of type `regex' missing (REGEX, HINT, HINT-I18 required)\n");
+ return -1;
+ }
+ {
+ regex_t ex;
+
+ if (0 != regcomp (&ex,
+ args[1],
+ REG_NOSUB | REG_EXTENDED))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid regular expression `%s'\n",
+ args[1]);
+ return -1;
+ }
+ regfree (&ex);
+ }
+
+ i18n = json_loads (args[3],
+ JSON_REJECT_DUPLICATES,
+ &err);
+ if (NULL == i18n)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid JSON for restriction of type `regex': `%s` at %d\n",
+ args[3],
+ err.position);
+ return -1;
+ }
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ restrictions,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("type",
+ "regex"),
+ GNUNET_JSON_pack_string ("payto_regex",
+ args[1]),
+ GNUNET_JSON_pack_string ("human_hint",
+ args[2]),
+ GNUNET_JSON_pack_object_steal ("human_hint_i18n",
+ i18n)
+ )));
+ return 4;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Restriction TYPE `%s' unsupported\n",
+ args[0]);
+ return -1;
+}
+
+
+/**
+ * Add wire account.
+ *
+ * @param args the array of command-line arguments to process next;
+ * args[0] must be the hash of the denomination key to revoke
+ */
+static void
+do_add_wire (char *const *args)
+{
+ struct TALER_MasterSignatureP master_sig_add;
+ struct TALER_MasterSignatureP master_sig_wire;
+ struct GNUNET_TIME_Timestamp now;
+ const char *conversion_url = NULL;
+ const char *bank_label = NULL;
+ int64_t priority = 0;
+ json_t *debit_restrictions;
+ json_t *credit_restrictions;
+ unsigned int num_args = 1;
+
+ if (NULL != in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Downloaded data was not consumed, not adding wire account\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if (NULL == args[0])
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must specify a payto://-URI with this subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+ {
+ char *msg = TALER_payto_validate (args[0]);
+
+ if (NULL != msg)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "payto URI is malformed: %s\n",
+ msg);
+ GNUNET_free (msg);
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ }
+ now = GNUNET_TIME_timestamp_get ();
+ {
+ char *wire_method;
+
+ wire_method = TALER_payto_get_method (args[0]);
+ if (NULL == wire_method)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "payto:// URI `%s' is malformed\n",
+ args[0]);
+ global_ret = EXIT_INVALIDARGUMENT;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ GNUNET_free (wire_method);
+ }
+ debit_restrictions = json_array ();
+ GNUNET_assert (NULL != debit_restrictions);
+ credit_restrictions = json_array ();
+ GNUNET_assert (NULL != credit_restrictions);
+ while (NULL != args[num_args])
+ {
+ if (0 == strcmp (args[num_args],
+ "conversion-url"))
+ {
+ num_args++;
+ conversion_url = args[num_args];
+ if (NULL == conversion_url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "'conversion-url' requires an argument\n");
+ global_ret = EXIT_INVALIDARGUMENT;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (debit_restrictions);
+ json_decref (credit_restrictions);
+ return;
+ }
+ if (! TALER_is_web_url (conversion_url))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "'conversion-url' must refer to HTTP(S) endpoint, `%s' is invalid\n",
+ conversion_url);
+ global_ret = EXIT_INVALIDARGUMENT;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (debit_restrictions);
+ json_decref (credit_restrictions);
+ return;
+ }
+ num_args++;
+ continue;
+ }
+ if (0 == strcmp (args[num_args],
+ "credit-restriction"))
+ {
+ int iret;
+
+ num_args++;
+ iret = parse_restriction (&args[num_args],
+ credit_restrictions);
+ if (iret <= 0)
+ {
+ global_ret = EXIT_INVALIDARGUMENT;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (debit_restrictions);
+ json_decref (credit_restrictions);
+ return;
+ }
+ num_args += iret;
+ continue;
+ }
+ if (0 == strcmp (args[num_args],
+ "debit-restriction"))
+ {
+ int iret;
+
+ num_args++;
+ iret = parse_restriction (&args[num_args],
+ debit_restrictions);
+ if (iret <= 0)
+ {
+ global_ret = EXIT_INVALIDARGUMENT;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (debit_restrictions);
+ json_decref (credit_restrictions);
+ return;
+ }
+ num_args += iret;
+ continue;
+ }
+ if (0 == strcmp (args[num_args],
+ "display-hint"))
+ {
+ long long p;
+ char dummy;
+
+ num_args++;
+ if ( (NULL == args[num_args]) ||
+ (NULL == args[num_args + 1]) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "'display-hint' requires at least two arguments (priority and label)\n");
+ global_ret = EXIT_INVALIDARGUMENT;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (debit_restrictions);
+ json_decref (credit_restrictions);
+ return;
+ }
+ if (1 != sscanf (args[num_args],
+ "%lld%c",
+ &p,
+ &dummy))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Priority argument `%s' is not a number\n",
+ args[num_args]);
+ global_ret = EXIT_INVALIDARGUMENT;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (debit_restrictions);
+ json_decref (credit_restrictions);
+ return;
+ }
+ priority = (int64_t) p;
+ num_args++;
+ bank_label = args[num_args];
+ num_args++;
+ continue;
+ }
+ break;
+ }
+ TALER_exchange_offline_wire_add_sign (args[0],
+ conversion_url,
+ debit_restrictions,
+ credit_restrictions,
+ now,
+ &master_priv,
+ &master_sig_add);
+ TALER_exchange_wire_signature_make (args[0],
+ conversion_url,
+ debit_restrictions,
+ credit_restrictions,
+ &master_priv,
+ &master_sig_wire);
+ output_operation (OP_ENABLE_WIRE,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("payto_uri",
+ args[0]),
+ GNUNET_JSON_pack_array_steal ("debit_restrictions",
+ debit_restrictions),
+ GNUNET_JSON_pack_array_steal ("credit_restrictions",
+ credit_restrictions),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("conversion_url",
+ conversion_url)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("bank_label",
+ bank_label)),
+ GNUNET_JSON_pack_int64 ("priority",
+ priority),
+ GNUNET_JSON_pack_timestamp ("validity_start",
+ now),
+ GNUNET_JSON_pack_data_auto ("master_sig_add",
+ &master_sig_add),
+ GNUNET_JSON_pack_data_auto ("master_sig_wire",
+ &master_sig_wire)));
+ next (args + num_args);
+}
+
+
+/**
+ * Disable wire account.
+ *
+ * @param args the array of command-line arguments to process next;
+ * args[0] must be the hash of the denomination key to revoke
+ */
+static void
+do_del_wire (char *const *args)
+{
+ struct TALER_MasterSignatureP master_sig;
+ struct GNUNET_TIME_Timestamp now;
+
+ if (NULL != in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Downloaded data was not consumed, not deleting wire account\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if (NULL == args[0])
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must specify a payto://-URI with this subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+ now = GNUNET_TIME_timestamp_get ();
+ TALER_exchange_offline_wire_del_sign (args[0],
+ now,
+ &master_priv,
+ &master_sig);
+ output_operation (OP_DISABLE_WIRE,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("payto_uri",
+ args[0]),
+ GNUNET_JSON_pack_timestamp ("validity_end",
+ now),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &master_sig)));
+ next (args + 1);
+}
+
+
+/**
+ * Set wire fees for the given year.
+ *
+ * @param args the array of command-line arguments to process next;
+ * args[0] must be the year, args[1] the wire method, args[2] the wire fee and args[3]
+ * the closing fee.
+ */
+static void
+do_set_wire_fee (char *const *args)
+{
+ struct TALER_MasterSignatureP master_sig;
+ char dummy;
+ unsigned int year;
+ struct TALER_WireFeeSet fees;
+ struct GNUNET_TIME_Timestamp start_time;
+ struct GNUNET_TIME_Timestamp end_time;
+
+ if (NULL != in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Downloaded data was not consumed, not setting wire fee\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if ( (NULL == args[0]) ||
+ (NULL == args[1]) ||
+ (NULL == args[2]) ||
+ (NULL == args[3]) ||
+ ( (1 != sscanf (args[0],
+ "%u%c",
+ &year,
+ &dummy)) &&
+ (0 != strcasecmp ("now",
+ args[0])) ) ||
+ (GNUNET_OK !=
+ TALER_string_to_amount (args[2],
+ &fees.wire)) ||
+ (GNUNET_OK !=
+ TALER_string_to_amount (args[3],
+ &fees.closing)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must use YEAR, METHOD, WIRE-FEE, and CLOSING-FEE as arguments for this subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if (0 == strcasecmp ("now",
+ args[0]))
+ year = GNUNET_TIME_get_current_year ();
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+ start_time = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_year_to_time (year));
+ end_time = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_year_to_time (year + 1));
+
+ TALER_exchange_offline_wire_fee_sign (args[1],
+ start_time,
+ end_time,
+ &fees,
+ &master_priv,
+ &master_sig);
+ output_operation (OP_SET_WIRE_FEE,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("wire_method",
+ args[1]),
+ GNUNET_JSON_pack_timestamp ("start_time",
+ start_time),
+ GNUNET_JSON_pack_timestamp ("end_time",
+ end_time),
+ TALER_JSON_pack_amount ("wire_fee",
+ &fees.wire),
+ TALER_JSON_pack_amount ("closing_fee",
+ &fees.closing),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &master_sig)));
+ next (args + 4);
+}
+
+
+/**
+ * Set global fees for the given year.
+ *
+ * @param args the array of command-line arguments to process next;
+ * args[0] must be the year, args[1] the history fee, args[2]
+ * the account fee and args[3] the purse fee. These are followed by args[4] purse timeout,
+ * args[5] history expiration. Last is args[6] the (free) purse account limit.
+ */
+static void
+do_set_global_fee (char *const *args)
+{
+ struct TALER_MasterSignatureP master_sig;
+ char dummy;
+ unsigned int year;
+ struct TALER_GlobalFeeSet fees;
+ struct GNUNET_TIME_Relative purse_timeout;
+ struct GNUNET_TIME_Relative history_expiration;
+ unsigned int purse_account_limit;
+ struct GNUNET_TIME_Timestamp start_time;
+ struct GNUNET_TIME_Timestamp end_time;
+
+ if (NULL != in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Downloaded data was not consumed, not setting global fee\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if ( (NULL == args[0]) ||
+ (NULL == args[1]) ||
+ (NULL == args[2]) ||
+ (NULL == args[3]) ||
+ (NULL == args[4]) ||
+ (NULL == args[5]) ||
+ (NULL == args[6]) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if ( (1 != sscanf (args[0],
+ "%u%c",
+ &year,
+ &dummy)) &&
+ (0 != strcasecmp ("now",
+ args[0])) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid YEAR given for 'global-fee' subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if ( (GNUNET_OK !=
+ TALER_string_to_amount (args[1],
+ &fees.history)) ||
+ (GNUNET_OK !=
+ TALER_string_to_amount (args[2],
+ &fees.account)) ||
+ (GNUNET_OK !=
+ TALER_string_to_amount (args[3],
+ &fees.purse)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid amount given for 'global-fee' subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if ( (GNUNET_OK !=
+ GNUNET_STRINGS_fancy_time_to_relative (args[4],
+ &purse_timeout)) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_fancy_time_to_relative (args[5],
+ &history_expiration)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid delay given for 'global-fee' subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if (1 != sscanf (args[6],
+ "%u%c",
+ &purse_account_limit,
+ &dummy))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid purse account limit given for 'global-fee' subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if (0 == strcasecmp ("now",
+ args[0]))
+ year = GNUNET_TIME_get_current_year ();
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+ start_time = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_year_to_time (year));
+ end_time = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_year_to_time (year + 1));
+
+ TALER_exchange_offline_global_fee_sign (start_time,
+ end_time,
+ &fees,
+ purse_timeout,
+ history_expiration,
+ (uint32_t) purse_account_limit,
+ &master_priv,
+ &master_sig);
+ output_operation (OP_SET_GLOBAL_FEE,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_timestamp ("start_time",
+ start_time),
+ GNUNET_JSON_pack_timestamp ("end_time",
+ end_time),
+ TALER_JSON_pack_amount ("history_fee",
+ &fees.history),
+ TALER_JSON_pack_amount ("account_fee",
+ &fees.account),
+ TALER_JSON_pack_amount ("purse_fee",
+ &fees.purse),
+ GNUNET_JSON_pack_time_rel ("purse_timeout",
+ purse_timeout),
+ GNUNET_JSON_pack_time_rel ("history_expiration",
+ history_expiration),
+ GNUNET_JSON_pack_uint64 ("purse_account_limit",
+ (uint32_t) purse_account_limit),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &master_sig)));
+ next (args + 7);
+}
+
+
+/**
+ * Drain profits from exchange's escrow account to
+ * regular exchange account.
+ *
+ * @param args the array of command-line arguments to process next;
+ * args[0] must be the amount,
+ * args[1] must be the section of the escrow account to drain
+ * args[2] must be the payto://-URI of the target account
+ */
+static void
+do_drain (char *const *args)
+{
+ struct TALER_WireTransferIdentifierRawP wtid;
+ struct GNUNET_TIME_Timestamp date;
+ struct TALER_Amount amount;
+ const char *account_section;
+ const char *payto_uri;
+ struct TALER_MasterSignatureP master_sig;
+ char *err;
+
+ if (NULL != in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Downloaded data was not consumed, refusing drain\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if ( (NULL == args[0]) ||
+ (NULL == args[1]) ||
+ (NULL == args[2]) ||
+ (GNUNET_OK !=
+ TALER_string_to_amount (args[0],
+ &amount)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Drain requires an amount, section name and target payto://-URI as arguments\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if ( (NULL == args[0]) ||
+ (NULL == args[1]) ||
+ (NULL == args[2]) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Drain requires an amount, section name and target payto://-URI as arguments\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_string_to_amount (args[0],
+ &amount))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid amount `%s' specified for drain\n",
+ args[0]);
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ account_section = args[1];
+ payto_uri = args[2];
+ err = TALER_payto_validate (payto_uri);
+ if (NULL != err)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid payto://-URI `%s' specified for drain: %s\n",
+ payto_uri,
+ err);
+ GNUNET_free (err);
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
+ &wtid,
+ sizeof (wtid));
+ date = GNUNET_TIME_timestamp_get ();
+ TALER_exchange_offline_profit_drain_sign (&wtid,
+ date,
+ &amount,
+ account_section,
+ payto_uri,
+ &master_priv,
+ &master_sig);
+ output_operation (OP_DRAIN_PROFITS,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("wtid",
+ &wtid),
+ GNUNET_JSON_pack_string ("account_section",
+ account_section),
+ GNUNET_JSON_pack_string ("payto_uri",
+ payto_uri),
+ TALER_JSON_pack_amount ("amount",
+ &amount),
+ GNUNET_JSON_pack_timestamp ("date",
+ date),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &master_sig)));
+ next (args + 3);
+}
+
+
+/**
+ * Add partner.
+ *
+ * @param args the array of command-line arguments to process next;
+ * args[0] must be the partner's master public key, args[1] the partner's
+ * API base URL, args[2] the wad fee, args[3] the wad frequency, and
+ * args[4] the year (including possibly 'now')
+ */
+static void
+do_add_partner (char *const *args)
+{
+ struct TALER_MasterPublicKeyP partner_pub;
+ struct GNUNET_TIME_Timestamp start_date;
+ struct GNUNET_TIME_Timestamp end_date;
+ struct GNUNET_TIME_Relative wad_frequency;
+ struct TALER_Amount wad_fee;
+ const char *partner_base_url;
+ struct TALER_MasterSignatureP master_sig;
+ char dummy;
+ unsigned int year;
+
+ if (NULL != in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Downloaded data was not consumed, not adding partner\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if ( (NULL == args[0]) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (args[0],
+ strlen (args[0]),
+ &partner_pub,
+ sizeof (partner_pub))) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must specify the partner master public key as first argument for this subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if ( (NULL == args[1]) ||
+ (0 != strncmp ("http",
+ args[1],
+ strlen ("http"))) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must specify the partner's base URL as the 2nd argument to this subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ partner_base_url = args[1];
+ if ( (NULL == args[2]) ||
+ (GNUNET_OK !=
+ TALER_string_to_amount (args[2],
+ &wad_fee)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid amount `%s' specified for wad fee of partner\n",
+ args[2]);
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if ( (NULL == args[3]) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_fancy_time_to_relative (args[3],
+ &wad_frequency)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid wad frequency `%s' specified for add partner\n",
+ args[3]);
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if ( (NULL == args[4]) ||
+ ( (1 != sscanf (args[4],
+ "%u%c",
+ &year,
+ &dummy)) &&
+ (0 != strcasecmp ("now",
+ args[4])) ) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid year `%s' specified for add partner\n",
+ args[4]);
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if (0 == strcasecmp ("now",
+ args[4]))
+ year = GNUNET_TIME_get_current_year ();
+ start_date = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_year_to_time (year));
+ end_date = GNUNET_TIME_absolute_to_timestamp (
+ GNUNET_TIME_year_to_time (year + 1));
+
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+ TALER_exchange_offline_partner_details_sign (&partner_pub,
+ start_date,
+ end_date,
+ wad_frequency,
+ &wad_fee,
+ partner_base_url,
+ &master_priv,
+ &master_sig);
+ output_operation (OP_ADD_PARTNER,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("partner_base_url",
+ partner_base_url),
+ GNUNET_JSON_pack_time_rel ("wad_frequency",
+ wad_frequency),
+ GNUNET_JSON_pack_timestamp ("start_date",
+ start_date),
+ GNUNET_JSON_pack_timestamp ("end_date",
+ end_date),
+ GNUNET_JSON_pack_data_auto ("partner_pub",
+ &partner_pub),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &master_sig)));
+ next (args + 5);
+}
+
+
+/**
+ * Enable or disable AML staff.
+ *
+ * @param is_active true to enable, false to disable
+ * @param args the array of command-line arguments to process next; args[0] must be the AML staff's public key, args[1] the AML staff's legal name, and if @a is_active then args[2] rw (read write) or ro (read only)
+ */
+static void
+do_set_aml_staff (bool is_active,
+ char *const *args)
+{
+ struct TALER_AmlOfficerPublicKeyP officer_pub;
+ const char *officer_name;
+ bool read_only;
+ struct TALER_MasterSignatureP master_sig;
+ struct GNUNET_TIME_Timestamp now
+ = GNUNET_TIME_timestamp_get ();
+
+ if (NULL != in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Downloaded data was not consumed, not updating AML staff status\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ if ( (NULL == args[0]) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (args[0],
+ strlen (args[0]),
+ &officer_pub,
+ sizeof (officer_pub))) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must specify the AML officer's public key as first argument for this subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ if (NULL == args[1])
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must specify the officer's legal name as the 2nd argument to this subcommand\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ officer_name = args[1];
+ if (is_active)
+ {
+ if ( (NULL == args[2]) ||
+ ( (0 != strcmp (args[2],
+ "ro")) &&
+ (0 != strcmp (args[2],
+ "rw")) ) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must specify 'ro' or 'rw' (and not `%s') for the access level\n",
+ args[2]);
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ read_only = (0 == strcmp (args[2],
+ "ro"));
+ }
+ else
+ {
+ read_only = true;
+ }
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+ TALER_exchange_offline_aml_officer_status_sign (&officer_pub,
+ officer_name,
+ now,
+ is_active,
+ read_only,
+ &master_priv,
+ &master_sig);
+ output_operation (OP_UPDATE_AML_STAFF,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("officer_name",
+ officer_name),
+ GNUNET_JSON_pack_timestamp ("change_date",
+ now),
+ GNUNET_JSON_pack_bool ("is_active",
+ is_active),
+ GNUNET_JSON_pack_bool ("read_only",
+ read_only),
+ GNUNET_JSON_pack_data_auto ("officer_pub",
+ &officer_pub),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &master_sig)));
+ next (args + (is_active ? 3 : 2));
+}
+
+
+/**
+ * Disable AML staff.
+ *
+ * @param args the array of command-line arguments to process next;
+ * args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only)
+ */
+static void
+disable_aml_staff (char *const *args)
+{
+ do_set_aml_staff (false,
+ args);
+}
+
+
+/**
+ * Enable AML staff.
+ *
+ * @param args the array of command-line arguments to process next;
+ * args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only)
+ */
+static void
+enable_aml_staff (char *const *args)
+{
+ do_set_aml_staff (true,
+ args);
+}
+
+
+/**
+ * Function called with information about future keys. Dumps the JSON output
+ * (on success), either into an internal buffer or to stdout (depending on
+ * whether there are subsequent commands).
+ *
+ * @param cls closure with the `char **` remaining args
+ * @param mgr response data
+ */
+static void
+download_cb (void *cls,
+ const struct TALER_EXCHANGE_ManagementGetKeysResponse *mgr)
+{
+ char *const *args = cls;
+ const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr;
+
+ mgkh = NULL;
+ switch (hr->http_status)
+ {
+ case MHD_HTTP_OK:
+ break;
+ default:
+ if (0 != hr->http_status)
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to download keys from `%s': %s (HTTP status: %u/%u)\n",
+ CFG_exchange_url,
+ hr->hint,
+ hr->http_status,
+ (unsigned int) hr->ec);
+ else
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to download keys from `%s' (no HTTP response)\n",
+ CFG_exchange_url);
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_FAILURE;
+ return;
+ }
+ in = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("operation",
+ OP_INPUT_KEYS),
+ GNUNET_JSON_pack_object_incref ("arguments",
+ (json_t *) hr->reply));
+ if (NULL == args[0])
+ {
+ json_dumpf (in,
+ stdout,
+ JSON_INDENT (2));
+ json_decref (in);
+ in = NULL;
+ }
+ next (args);
+}
+
+
+/**
+ * Download future keys.
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+do_download (char *const *args)
+{
+ if ( (NULL == CFG_exchange_url) &&
+ (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (kcfg,
+ "exchange",
+ "BASE_URL",
+ &CFG_exchange_url)) )
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "BASE_URL");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_NOTCONFIGURED;
+ return;
+ }
+ mgkh = TALER_EXCHANGE_get_management_keys (ctx,
+ CFG_exchange_url,
+ &download_cb,
+ (void *) args);
+}
+
+
+/**
+ * Check that the security module keys are the same as before. If we had no
+ * keys in store before, remember them (Trust On First Use).
+ *
+ * @param secmset security module keys
+ * @return #GNUNET_OK if keys match with what we have in store
+ * #GNUNET_NO if we had nothing in store but now do
+ * #GNUNET_SYSERR if keys changed from what we remember or other error
+ */
+static enum GNUNET_GenericReturnValue
+tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset)
+{
+ char *fn;
+ struct TALER_SecurityModulePublicKeySetP oldset;
+ ssize_t ret;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (kcfg,
+ "exchange-offline",
+ "SECM_TOFU_FILE",
+ &fn))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange-offline",
+ "SECM_TOFU_FILE");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK ==
+ GNUNET_DISK_file_test (fn))
+ {
+ ret = GNUNET_DISK_fn_read (fn,
+ &oldset,
+ sizeof (oldset));
+ if (GNUNET_SYSERR != ret)
+ {
+ if (ret != sizeof (oldset))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "File `%s' corrupt\n",
+ fn);
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ /* TOFU check */
+ if (0 != memcmp (&oldset,
+ secmset,
+ sizeof (*secmset)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Fatal: security module keys changed (file `%s')!\n",
+ fn);
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (fn);
+ return GNUNET_OK;
+ }
+ }
+
+ {
+ char *key;
+
+ /* check against SECMOD-keys pinned in configuration */
+ if (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (kcfg,
+ "exchange-offline",
+ "SECM_ESIGN_PUBKEY",
+ &key))
+ {
+ struct TALER_SecurityModulePublicKeyP k;
+
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (key,
+ strlen (key),
+ &k,
+ sizeof (k)))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange-offline",
+ "SECM_ESIGN_PUBKEY",
+ "key malformed");
+ GNUNET_free (key);
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (key);
+ if (0 !=
+ GNUNET_memcmp (&k,
+ &secmset->eddsa))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "ESIGN security module key does not match SECM_ESIGN_PUBKEY in configuration\n");
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ }
+ if (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (kcfg,
+ "exchange-offline",
+ "SECM_DENOM_PUBKEY",
+ &key))
+ {
+ struct TALER_SecurityModulePublicKeyP k;
+
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (key,
+ strlen (key),
+ &k,
+ sizeof (k)))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange-offline",
+ "SECM_DENOM_PUBKEY",
+ "key malformed");
+ GNUNET_free (key);
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (key);
+ if (0 !=
+ GNUNET_memcmp (&k,
+ &secmset->rsa))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "DENOM security module key does not match SECM_DENOM_PUBKEY in configuration\n");
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ }
+ if (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (kcfg,
+ "exchange-offline",
+ "SECM_DENOM_CS_PUBKEY",
+ &key))
+ {
+ struct TALER_SecurityModulePublicKeyP k;
+
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (key,
+ strlen (key),
+ &k,
+ sizeof (k)))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange-offline",
+ "SECM_DENOM_CS_PUBKEY",
+ "key malformed");
+ GNUNET_free (key);
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (key);
+ if (0 !=
+ GNUNET_memcmp (&k,
+ &secmset->cs))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "DENOM security module key does not match SECM_DENOM_CS_PUBKEY in configuration\n");
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ }
+ }
+ if (GNUNET_OK !=
+ GNUNET_DISK_directory_create_for_file (fn))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed create directory to store key material in file `%s'\n",
+ fn);
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ /* persist keys for future runs */
+ if (GNUNET_OK !=
+ GNUNET_DISK_fn_write (fn,
+ secmset,
+ sizeof (oldset),
+ GNUNET_DISK_PERM_USER_READ))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to store key material in file `%s'\n",
+ fn);
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (fn);
+ return GNUNET_NO;
+}
+
+
+/**
+ * Output @a signkeys for human consumption.
+ *
+ * @param secm_pub security module public key used to sign the denominations
+ * @param signkeys keys to output
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+show_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
+ const json_t *signkeys)
+{
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (signkeys, index, value) {
+ const char *err_name;
+ unsigned int err_line;
+ struct TALER_ExchangePublicKeyP exchange_pub;
+ struct TALER_SecurityModuleSignatureP secm_sig;
+ struct GNUNET_TIME_Timestamp start_time;
+ struct GNUNET_TIME_Timestamp sign_end;
+ struct GNUNET_TIME_Timestamp legal_end;
+ struct GNUNET_TIME_Relative duration;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("stamp_start",
+ &start_time),
+ GNUNET_JSON_spec_timestamp ("stamp_expire",
+ &sign_end),
+ GNUNET_JSON_spec_timestamp ("stamp_end",
+ &legal_end),
+ GNUNET_JSON_spec_fixed_auto ("key",
+ &exchange_pub),
+ GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig",
+ &secm_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input for signing key to 'show': %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) index);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return GNUNET_SYSERR;
+ }
+ duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time,
+ sign_end.abs_time);
+ if (GNUNET_OK !=
+ TALER_exchange_secmod_eddsa_verify (&exchange_pub,
+ start_time,
+ duration,
+ secm_pub,
+ &secm_sig))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid security module signature for signing key %s (aborting)\n",
+ TALER_B2S (&exchange_pub));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return GNUNET_SYSERR;
+ }
+ {
+ char *legal_end_s;
+
+ legal_end_s = GNUNET_strdup (
+ GNUNET_TIME_timestamp2s (legal_end));
+ printf ("EXCHANGE-KEY %s starting at %s (used for: %s, legal end: %s)\n",
+ TALER_B2S (&exchange_pub),
+ GNUNET_TIME_timestamp2s (start_time),
+ GNUNET_TIME_relative2s (duration,
+ false),
+ legal_end_s);
+ GNUNET_free (legal_end_s);
+ }
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Output @a denomkeys for human consumption.
+ *
+ * @param secm_pub_rsa security module public key used to sign the RSA denominations
+ * @param secm_pub_cs security module public key used to sign the CS denominations
+ * @param denomkeys keys to output
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa,
+ const struct TALER_SecurityModulePublicKeyP *secm_pub_cs,
+ const json_t *denomkeys)
+{
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (denomkeys, index, value) {
+ const char *err_name;
+ unsigned int err_line;
+ const char *section_name;
+ struct TALER_DenominationPublicKey denom_pub;
+ struct GNUNET_TIME_Timestamp stamp_start;
+ struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
+ struct GNUNET_TIME_Timestamp stamp_expire_deposit;
+ struct GNUNET_TIME_Timestamp stamp_expire_legal;
+ struct TALER_Amount coin_value;
+ struct TALER_DenomFeeSet fees;
+ struct TALER_SecurityModuleSignatureP secm_sig;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("section_name",
+ &section_name),
+ TALER_JSON_spec_denom_pub ("denom_pub",
+ &denom_pub),
+ TALER_JSON_spec_amount ("value",
+ currency,
+ &coin_value),
+ TALER_JSON_SPEC_DENOM_FEES ("fee",
+ currency,
+ &fees),
+ GNUNET_JSON_spec_timestamp ("stamp_start",
+ &stamp_start),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
+ &stamp_expire_withdraw),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
+ &stamp_expire_deposit),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
+ &stamp_expire_legal),
+ GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig",
+ &secm_sig),
+ GNUNET_JSON_spec_end ()
+ };
+ struct GNUNET_TIME_Relative duration;
+ struct TALER_DenominationHashP h_denom_pub;
+ enum GNUNET_GenericReturnValue ok;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input for denomination key to 'show': %s#%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) index);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ GNUNET_JSON_parse_free (spec);
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return GNUNET_SYSERR;
+ }
+ duration = GNUNET_TIME_absolute_get_difference (
+ stamp_start.abs_time,
+ stamp_expire_withdraw.abs_time);
+ TALER_denom_pub_hash (&denom_pub,
+ &h_denom_pub);
+ switch (denom_pub.bsign_pub_key->cipher)
+ {
+ case GNUNET_CRYPTO_BSA_RSA:
+ {
+ struct TALER_RsaPubHashP h_rsa;
+
+ TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key,
+ &h_rsa);
+ ok = TALER_exchange_secmod_rsa_verify (&h_rsa,
+ section_name,
+ stamp_start,
+ duration,
+ secm_pub_rsa,
+ &secm_sig);
+ }
+ break;
+ case GNUNET_CRYPTO_BSA_CS:
+ {
+ struct TALER_CsPubHashP h_cs;
+
+ TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key,
+ &h_cs);
+ ok = TALER_exchange_secmod_cs_verify (&h_cs,
+ section_name,
+ stamp_start,
+ duration,
+ secm_pub_cs,
+ &secm_sig);
+ }
+ break;
+ default:
+ GNUNET_break (0);
+ ok = GNUNET_SYSERR;
+ break;
+ }
+ if (GNUNET_OK != ok)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid security module signature for denomination key %s (aborting)\n",
+ GNUNET_h2s (&h_denom_pub.hash));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return GNUNET_SYSERR;
+ }
+
+ {
+ char *withdraw_fee_s;
+ char *deposit_fee_s;
+ char *refresh_fee_s;
+ char *refund_fee_s;
+ char *deposit_s;
+ char *legal_s;
+
+ withdraw_fee_s = TALER_amount_to_string (&fees.withdraw);
+ deposit_fee_s = TALER_amount_to_string (&fees.deposit);
+ refresh_fee_s = TALER_amount_to_string (&fees.refresh);
+ refund_fee_s = TALER_amount_to_string (&fees.refund);
+ deposit_s = GNUNET_strdup (
+ GNUNET_TIME_timestamp2s (stamp_expire_deposit));
+ legal_s = GNUNET_strdup (
+ GNUNET_TIME_timestamp2s (stamp_expire_legal));
+
+ printf (
+ "DENOMINATION-KEY(%s) %s of value %s starting at %s "
+ "(used for: %s, deposit until: %s legal end: %s) with fees %s/%s/%s/%s\n",
+ section_name,
+ TALER_B2S (&h_denom_pub),
+ TALER_amount2s (&coin_value),
+ GNUNET_TIME_timestamp2s (stamp_start),
+ GNUNET_TIME_relative2s (duration,
+ false),
+ deposit_s,
+ legal_s,
+ withdraw_fee_s,
+ deposit_fee_s,
+ refresh_fee_s,
+ refund_fee_s);
+ GNUNET_free (withdraw_fee_s);
+ GNUNET_free (deposit_fee_s);
+ GNUNET_free (refresh_fee_s);
+ GNUNET_free (refund_fee_s);
+ GNUNET_free (deposit_s);
+ GNUNET_free (legal_s);
+ }
+
+ GNUNET_JSON_parse_free (spec);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Parse the input of exchange keys for the 'show' and 'sign' commands.
+ *
+ * @param command_name name of the command, for logging
+ * @return NULL on error, otherwise the keys details to be free'd by caller
+ */
+static json_t *
+parse_keys_input (const char *command_name)
+{
+ const char *op_str;
+ json_t *keys;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_json ("arguments",
+ &keys),
+ GNUNET_JSON_spec_string ("operation",
+ &op_str),
+ GNUNET_JSON_spec_end ()
+ };
+ const char *err_name;
+ unsigned int err_line;
+
+ if (NULL == in)
+ {
+ json_error_t err;
+
+ in = json_loadf (stdin,
+ JSON_REJECT_DUPLICATES,
+ &err);
+ if (NULL == in)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to read JSON input: %s at %d:%s (offset: %d)\n",
+ err.text,
+ err.line,
+ err.source,
+ err.position);
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return NULL;
+ }
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (in,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to '%s': %s#%u (skipping)\n",
+ command_name,
+ err_name,
+ err_line);
+ json_dumpf (in,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return NULL;
+ }
+ if (0 != strcmp (op_str,
+ OP_INPUT_KEYS))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to '%s' : operation is `%s', expected `%s'\n",
+ command_name,
+ op_str,
+ OP_INPUT_KEYS);
+ GNUNET_JSON_parse_free (spec);
+ return NULL;
+ }
+ json_decref (in);
+ in = NULL;
+ return keys;
+}
+
+
+/**
+ * Show future keys.
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+do_show (char *const *args)
+{
+ json_t *keys;
+ const char *err_name;
+ unsigned int err_line;
+ const json_t *denomkeys;
+ const json_t *signkeys;
+ struct TALER_MasterPublicKeyP mpub;
+ struct TALER_SecurityModulePublicKeySetP secmset;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("future_denoms",
+ &denomkeys),
+ GNUNET_JSON_spec_array_const ("future_signkeys",
+ &signkeys),
+ GNUNET_JSON_spec_fixed_auto ("master_pub",
+ &mpub),
+ GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
+ &secmset.rsa),
+ GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
+ &secmset.cs),
+ GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
+ &secmset.eddsa),
+ GNUNET_JSON_spec_end ()
+ };
+
+ keys = parse_keys_input ("show");
+ if (NULL == keys)
+ return;
+
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ return;
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (keys,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to 'show': %s #%u (skipping)\n",
+ err_name,
+ err_line);
+ json_dumpf (in,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (keys);
+ return;
+ }
+ if (0 !=
+ GNUNET_memcmp (&master_pub,
+ &mpub))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Fatal: exchange uses different master key!\n");
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (keys);
+ return;
+ }
+ if (GNUNET_SYSERR ==
+ tofu_check (&secmset))
+ {
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (keys);
+ return;
+ }
+ if ( (GNUNET_OK !=
+ show_signkeys (&secmset.eddsa,
+ signkeys)) ||
+ (GNUNET_OK !=
+ show_denomkeys (&secmset.rsa,
+ &secmset.cs,
+ denomkeys)) )
+ {
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (keys);
+ return;
+ }
+ json_decref (keys);
+ next (args);
+}
+
+
+/**
+ * Sign @a signkeys with offline key.
+ *
+ * @param secm_pub security module public key used to sign the denominations
+ * @param signkeys keys to output
+ * @param[in,out] result array where to output the signatures
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub,
+ const json_t *signkeys,
+ json_t *result)
+{
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (signkeys, index, value) {
+ const char *err_name;
+ unsigned int err_line;
+ struct TALER_ExchangePublicKeyP exchange_pub;
+ struct TALER_SecurityModuleSignatureP secm_sig;
+ struct GNUNET_TIME_Timestamp start_time;
+ struct GNUNET_TIME_Timestamp sign_end;
+ struct GNUNET_TIME_Timestamp legal_end;
+ struct GNUNET_TIME_Relative duration;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("stamp_start",
+ &start_time),
+ GNUNET_JSON_spec_timestamp ("stamp_expire",
+ &sign_end),
+ GNUNET_JSON_spec_timestamp ("stamp_end",
+ &legal_end),
+ GNUNET_JSON_spec_fixed_auto ("key",
+ &exchange_pub),
+ GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig",
+ &secm_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input for signing key to 'show': %s #%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) index);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return GNUNET_SYSERR;
+ }
+
+ duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time,
+ sign_end.abs_time);
+ if (GNUNET_OK !=
+ TALER_exchange_secmod_eddsa_verify (&exchange_pub,
+ start_time,
+ duration,
+ secm_pub,
+ &secm_sig))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid security module signature for signing key %s (aborting)\n",
+ TALER_B2S (&exchange_pub));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ {
+ struct TALER_MasterSignatureP master_sig;
+
+ TALER_exchange_offline_signkey_validity_sign (&exchange_pub,
+ start_time,
+ sign_end,
+ legal_end,
+ &master_priv,
+ &master_sig);
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ result,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("exchange_pub",
+ &exchange_pub),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &master_sig))));
+ }
+ GNUNET_JSON_parse_free (spec);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Looks up the AGE_RESTRICTED setting for a denomination in the config and
+ * returns the age restriction (mask) accordingly.
+ *
+ * @param section_name Section in the configuration for the particular
+ * denomination.
+ */
+static struct TALER_AgeMask
+load_age_mask (const char*section_name)
+{
+ static const struct TALER_AgeMask null_mask = {0};
+ enum GNUNET_GenericReturnValue ret;
+
+ if (! ar_enabled)
+ return null_mask;
+
+ if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
+ kcfg,
+ section_name,
+ "AGE_RESTRICTED")))
+ return null_mask;
+
+ ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg,
+ section_name,
+ "AGE_RESTRICTED");
+ if (GNUNET_SYSERR == ret)
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section_name,
+ "AGE_RESTRICTED",
+ "Value must be YES or NO\n");
+ if (GNUNET_YES == ret)
+ return ar_config.mask;
+
+ return null_mask;
+}
+
+
+/**
+ * Sign @a denomkeys with offline key.
+ *
+ * @param secm_pub_rsa security module public key used to sign the RSA denominations
+ * @param secm_pub_cs security module public key used to sign the CS denominations
+ * @param denomkeys keys to output
+ * @param[in,out] result array where to output the signatures
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa,
+ const struct TALER_SecurityModulePublicKeyP *secm_pub_cs,
+ const json_t *denomkeys,
+ json_t *result)
+{
+ size_t index;
+ json_t *value;
+
+ json_array_foreach (denomkeys, index, value) {
+ const char *err_name;
+ unsigned int err_line;
+ const char *section_name;
+ struct TALER_DenominationPublicKey denom_pub;
+ struct GNUNET_TIME_Timestamp stamp_start;
+ struct GNUNET_TIME_Timestamp stamp_expire_withdraw;
+ struct GNUNET_TIME_Timestamp stamp_expire_deposit;
+ struct GNUNET_TIME_Timestamp stamp_expire_legal;
+ struct TALER_Amount coin_value;
+ struct TALER_DenomFeeSet fees;
+ struct TALER_SecurityModuleSignatureP secm_sig;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("section_name",
+ &section_name),
+ TALER_JSON_spec_denom_pub ("denom_pub",
+ &denom_pub),
+ TALER_JSON_spec_amount ("value",
+ currency,
+ &coin_value),
+ TALER_JSON_SPEC_DENOM_FEES ("fee",
+ currency,
+ &fees),
+ GNUNET_JSON_spec_timestamp ("stamp_start",
+ &stamp_start),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw",
+ &stamp_expire_withdraw),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_deposit",
+ &stamp_expire_deposit),
+ GNUNET_JSON_spec_timestamp ("stamp_expire_legal",
+ &stamp_expire_legal),
+ GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig",
+ &secm_sig),
+ GNUNET_JSON_spec_end ()
+ };
+ struct GNUNET_TIME_Relative duration;
+ struct TALER_DenominationHashP h_denom_pub;
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (value,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input for denomination key to 'sign': %s #%u at %u (skipping)\n",
+ err_name,
+ err_line,
+ (unsigned int) index);
+ json_dumpf (value,
+ stderr,
+ JSON_INDENT (2));
+ GNUNET_JSON_parse_free (spec);
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return GNUNET_SYSERR;
+ }
+ duration = GNUNET_TIME_absolute_get_difference (
+ stamp_start.abs_time,
+ stamp_expire_withdraw.abs_time);
+
+ /* Load the age mask, if applicable to this denomination */
+ denom_pub.age_mask = load_age_mask (section_name);
+
+ TALER_denom_pub_hash (&denom_pub,
+ &h_denom_pub);
+
+ switch (denom_pub.bsign_pub_key->cipher)
+ {
+ case GNUNET_CRYPTO_BSA_RSA:
+ {
+ struct TALER_RsaPubHashP h_rsa;
+
+ TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key,
+ &h_rsa);
+ if (GNUNET_OK !=
+ TALER_exchange_secmod_rsa_verify (&h_rsa,
+ section_name,
+ stamp_start,
+ duration,
+ secm_pub_rsa,
+ &secm_sig))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid security module signature for denomination key %s (aborting)\n",
+ GNUNET_h2s (&h_denom_pub.hash));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ }
+ break;
+ case GNUNET_CRYPTO_BSA_CS:
+ {
+ struct TALER_CsPubHashP h_cs;
+
+ TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key,
+ &h_cs);
+ if (GNUNET_OK !=
+ TALER_exchange_secmod_cs_verify (&h_cs,
+ section_name,
+ stamp_start,
+ duration,
+ secm_pub_cs,
+ &secm_sig))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid security module signature for denomination key %s (aborting)\n",
+ GNUNET_h2s (&h_denom_pub.hash));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ }
+ break;
+ default:
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+
+ {
+ struct TALER_MasterSignatureP master_sig;
+
+ TALER_exchange_offline_denom_validity_sign (&h_denom_pub,
+ stamp_start,
+ stamp_expire_withdraw,
+ stamp_expire_deposit,
+ stamp_expire_legal,
+ &coin_value,
+ &fees,
+ &master_priv,
+ &master_sig);
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ result,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("h_denom_pub",
+ &h_denom_pub),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ &master_sig))));
+ }
+ GNUNET_JSON_parse_free (spec);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Sign future keys.
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+do_sign (char *const *args)
+{
+ json_t *keys;
+ const char *err_name;
+ unsigned int err_line;
+ const json_t *denomkeys;
+ const json_t *signkeys;
+ struct TALER_MasterPublicKeyP mpub;
+ struct TALER_SecurityModulePublicKeySetP secmset;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("future_denoms",
+ &denomkeys),
+ GNUNET_JSON_spec_array_const ("future_signkeys",
+ &signkeys),
+ GNUNET_JSON_spec_fixed_auto ("master_pub",
+ &mpub),
+ GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
+ &secmset.rsa),
+ GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
+ &secmset.cs),
+ GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
+ &secmset.eddsa),
+ GNUNET_JSON_spec_end ()
+ };
+
+ keys = parse_keys_input ("sign");
+ if (NULL == keys)
+ return;
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ {
+ json_decref (keys);
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (keys,
+ spec,
+ &err_name,
+ &err_line))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid input to 'sign' : %s #%u (skipping)\n",
+ err_name,
+ err_line);
+ json_dumpf (in,
+ stderr,
+ JSON_INDENT (2));
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (keys);
+ return;
+ }
+ if (0 !=
+ GNUNET_memcmp (&master_pub,
+ &mpub))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Fatal: exchange uses different master key!\n");
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (keys);
+ return;
+ }
+ if (GNUNET_SYSERR ==
+ tofu_check (&secmset))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Fatal: security module keys changed!\n");
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (keys);
+ return;
+ }
+ {
+ json_t *signkey_sig_array = json_array ();
+ json_t *denomkey_sig_array = json_array ();
+
+ GNUNET_assert (NULL != signkey_sig_array);
+ GNUNET_assert (NULL != denomkey_sig_array);
+ if ( (GNUNET_OK !=
+ sign_signkeys (&secmset.eddsa,
+ signkeys,
+ signkey_sig_array)) ||
+ (GNUNET_OK !=
+ sign_denomkeys (&secmset.rsa,
+ &secmset.cs,
+ denomkeys,
+ denomkey_sig_array)) )
+ {
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ json_decref (signkey_sig_array);
+ json_decref (denomkey_sig_array);
+ json_decref (keys);
+ return;
+ }
+
+ output_operation (OP_UPLOAD_SIGS,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("denom_sigs",
+ denomkey_sig_array),
+ GNUNET_JSON_pack_array_steal ("signkey_sigs",
+ signkey_sig_array)));
+ }
+ json_decref (keys);
+ next (args);
+}
+
+
+/**
+ * Setup and output offline signing key.
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+do_setup (char *const *args)
+{
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_YES))
+ {
+ global_ret = EXIT_NOPERMISSION;
+ return;
+ }
+ if (NULL != *args)
+ {
+ output_operation (OP_SETUP,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("exchange_offline_pub",
+ &master_pub)));
+ }
+
+ else
+ {
+ char *pub_s;
+
+ pub_s = GNUNET_STRINGS_data_to_string_alloc (&master_pub,
+ sizeof (master_pub));
+ fprintf (stdout,
+ "%s\n",
+ pub_s);
+ GNUNET_free (pub_s);
+ }
+ if ( (NULL != *args) &&
+ (0 == strcmp (*args,
+ "-")) )
+ args++;
+ next (args);
+}
+
+
+/**
+ * Print the current extensions as configured
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+do_extensions_show (char *const *args)
+{
+ const struct TALER_Extensions *it;
+ json_t *exts = json_object ();
+ json_t *obj;
+
+ GNUNET_assert (NULL != exts);
+ for (it = TALER_extensions_get_head ();
+ NULL != it && NULL != it->extension;
+ it = it->next)
+ {
+ const struct TALER_Extension *extension = it->extension;
+ int ret;
+
+ ret = json_object_set_new (exts,
+ extension->name,
+ extension->manifest (extension));
+ GNUNET_assert (-1 != ret);
+ }
+
+ obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_object_steal ("extensions",
+ exts));
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "%s\n",
+ json_dumps (obj,
+ JSON_INDENT (2)));
+ json_decref (obj);
+ next (args);
+}
+
+
+/**
+ * Sign the configurations of the enabled extensions
+ */
+static void
+do_extensions_sign (char *const *args)
+{
+ json_t *extensions = json_object ();
+ struct TALER_ExtensionManifestsHashP h_manifests;
+ struct TALER_MasterSignatureP sig;
+ const struct TALER_Extensions *it;
+ bool found = false;
+ json_t *obj;
+
+ GNUNET_assert (NULL != extensions);
+ for (it = TALER_extensions_get_head ();
+ NULL != it && NULL != it->extension;
+ it = it->next)
+ {
+ const struct TALER_Extension *ext = it->extension;
+ GNUNET_assert (ext);
+
+ found = true;
+
+ GNUNET_assert (0 ==
+ json_object_set_new (extensions,
+ ext->name,
+ ext->manifest (ext)));
+ }
+
+ if (! found)
+ return;
+
+ if (GNUNET_OK !=
+ TALER_JSON_extensions_manifests_hash (extensions,
+ &h_manifests))
+ {
+ json_decref (extensions);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "error while hashing manifest for extensions\n");
+ return;
+ }
+
+ if (GNUNET_OK !=
+ load_offline_key (GNUNET_NO))
+ {
+ json_decref (extensions);
+ return;
+ }
+
+ TALER_exchange_offline_extension_manifests_hash_sign (&h_manifests,
+ &master_priv,
+ &sig);
+ obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_object_steal ("extensions",
+ extensions),
+ GNUNET_JSON_pack_data_auto (
+ "extensions_sig",
+ &sig));
+
+ output_operation (OP_EXTENSIONS,
+ obj);
+ next (args);
+}
+
+
+/**
+ * Dispatch @a args in the @a cmds array.
+ *
+ * @param args arguments with subcommand to dispatch
+ * @param cmds array of possible subcommands to call
+ */
+static void
+cmd_handler (char *const *args,
+ const struct SubCommand *cmds)
+{
+ nxt = NULL;
+ for (unsigned int i = 0; NULL != cmds[i].name; i++)
+ {
+ if (0 == strcasecmp (cmds[i].name,
+ args[0]))
+ {
+ cmds[i].cb (&args[1]);
+ return;
+ }
+ }
+
+ if (0 != strcasecmp ("help",
+ args[0]))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Unexpected command `%s'\n",
+ args[0]);
+ global_ret = EXIT_INVALIDARGUMENT;
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "Supported subcommands:\n");
+ for (unsigned int i = 0; NULL != cmds[i].name; i++)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "- %s: %s\n",
+ cmds[i].name,
+ cmds[i].help);
+ }
+ json_decref (out);
+ out = NULL;
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+static void
+do_work_extensions (char *const *args)
+{
+ struct SubCommand cmds[] = {
+ {
+ .name = "show",
+ .help =
+ "show the extensions in the Taler-config and their configured parameters",
+ .cb = &do_extensions_show
+ },
+ {
+ .name = "sign",
+ .help =
+ "sign the configuration of the extensions and publish it with the exchange",
+ .cb = &do_extensions_sign
+ },
+ {
+ .name = NULL,
+ }
+ };
+
+ if (NULL == args[0])
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "You must provide a subcommand: `show` or `sign`.\n");
+ GNUNET_SCHEDULER_shutdown ();
+ global_ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+
+ cmd_handler (args, cmds);
+}
+
+
+static void
+work (void *cls)
+{
+ char *const *args = cls;
+ struct SubCommand cmds[] = {
+ {
+ .name = "setup",
+ .help =
+ "initialize offline key signing material and display public offline key",
+ .cb = &do_setup
+ },
+ {
+ .name = "download",
+ .help =
+ "obtain future public keys from exchange (to be performed online!)",
+ .cb = &do_download
+ },
+ {
+ .name = "show",
+ .help =
+ "display future public keys from exchange for human review (pass '-' as argument to disable consuming input)",
+ .cb = &do_show
+ },
+ {
+ .name = "sign",
+ .help = "sign all future public keys from the input",
+ .cb = &do_sign
+ },
+ {
+ .name = "revoke-denomination",
+ .help =
+ "revoke denomination key (hash of public key must be given as argument)",
+ .cb = &do_revoke_denomination_key
+ },
+ {
+ .name = "revoke-signkey",
+ .help =
+ "revoke exchange online signing key (public key must be given as argument)",
+ .cb = &do_revoke_signkey
+ },
+ {
+ .name = "enable-auditor",
+ .help =
+ "enable auditor for the exchange (auditor-public key, auditor-URI and auditor-name must be given as arguments)",
+ .cb = &do_add_auditor
+ },
+ {
+ .name = "disable-auditor",
+ .help =
+ "disable auditor at the exchange (auditor-public key must be given as argument)",
+ .cb = &do_del_auditor
+ },
+ {
+ .name = "enable-account",
+ .help =
+ "enable wire account of the exchange (payto-URI must be given as argument; for optional arguments see man page)",
+ .cb = &do_add_wire
+ },
+ {
+ .name = "disable-account",
+ .help =
+ "disable wire account of the exchange (payto-URI must be given as argument)",
+ .cb = &do_del_wire
+ },
+ {
+ .name = "wire-fee",
+ .help =
+ "sign wire fees for the given year (year, wire method, wire fee, and closing fee must be given as arguments)",
+ .cb = &do_set_wire_fee
+ },
+ {
+ .name = "global-fee",
+ .help =
+ "sign global fees for the given year (year, history fee, account fee, purse fee, purse timeout, history expiration and the maximum number of free purses per account must be given as arguments)",
+ .cb = &do_set_global_fee
+ },
+ {
+ .name = "drain",
+ .help =
+ "drain profits from exchange escrow account to regular exchange operator account (amount, debit account configuration section and credit account payto://-URI must be given as arguments)",
+ .cb = &do_drain
+ },
+ {
+ .name = "add-partner",
+ .help =
+ "add partner exchange for P2P wad transfers (partner master public key, partner base URL, wad fee, wad frequency and validity year must be given as arguments)",
+ .cb = &do_add_partner
+ },
+ {
+ .name = "aml-enable",
+ .help =
+ "enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)",
+ .cb = &enable_aml_staff
+ },
+ {
+ .name = "aml-disable",
+ .help =
+ "disable AML staff member (staff member public key and legal name must be given as arguments)",
+ .cb = &disable_aml_staff
+ },
+ {
+ .name = "upload",
+ .help =
+ "upload operation result to exchange (to be performed online!)",
+ .cb = &do_upload
+ },
+ {
+ .name = "extensions",
+ .help = "subcommands for extension handling",
+ .cb = &do_work_extensions
+ },
+ /* list terminator */
+ {
+ .name = NULL,
+ }
+ };
+ (void) cls;
+
+ cmd_handler (args, cmds);
+}
+
+
+/**
+ * Main function that will be run.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param cfg configuration
+ */
+static void
+run (void *cls,
+ char *const *args,
+ const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ (void) cls;
+ (void) cfgfile;
+ kcfg = cfg;
+
+ /* load extensions */
+ GNUNET_assert (GNUNET_OK ==
+ TALER_extensions_init (kcfg));
+
+ /* setup age restriction, if applicable */
+ {
+ const struct TALER_AgeRestrictionConfig *arc;
+
+ if (NULL !=
+ (arc = TALER_extensions_get_age_restriction_config ()))
+ {
+ ar_config = *arc;
+ ar_enabled = true;
+ }
+ }
+
+
+ if (GNUNET_OK !=
+ TALER_config_get_currency (kcfg,
+ &currency))
+ {
+ global_ret = EXIT_NOTCONFIGURED;
+ return;
+ }
+
+ ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
+ &rc);
+ rc = GNUNET_CURL_gnunet_rc_create (ctx);
+ GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
+ NULL);
+ next (args);
+}
+
+
+/**
+ * The main function of the taler-exchange-offline tool. This tool is used to
+ * create the signing and denomination keys for the exchange. It uses the
+ * long-term offline private key and generates signatures with it. It also
+ * supports online operations with the exchange to download its input data and
+ * to upload its results. Those online operations should be performed on
+ * another machine in production!
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc,
+ char *const *argv)
+{
+ struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_END
+ };
+ enum GNUNET_GenericReturnValue ret;
+
+ /* force linker to link against libtalerutil; if we do
+ not do this, the linker may "optimize" libtalerutil
+ away and skip #TALER_OS_init(), which we do need */
+ (void) TALER_project_data_default ();
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_get_utf8_args (argc, argv,
+ &argc, &argv))
+ return EXIT_INVALIDARGUMENT;
+ TALER_OS_init ();
+ ret = GNUNET_PROGRAM_run (
+ argc, argv,
+ "taler-exchange-offline",
+ gettext_noop ("Operations for offline signing for a Taler exchange"),
+ options,
+ &run, NULL);
+ GNUNET_free_nz ((void *) argv);
+ if (GNUNET_SYSERR == ret)
+ return EXIT_INVALIDARGUMENT;
+ if (GNUNET_NO == ret)
+ return EXIT_SUCCESS;
+ return global_ret;
+}
+
+
+/* end of taler-exchange-offline.c */
diff --git a/src/exchange-tools/taler-exchange-wire.c b/src/exchange-tools/taler-exchange-wire.c
deleted file mode 100644
index 2f6b4ad73..000000000
--- a/src/exchange-tools/taler-exchange-wire.c
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2015-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
- 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, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-wire.c
- * @brief Create signed response for /wire requests.
- * @author Christian Grothoff
- */
-#include <platform.h>
-#include <jansson.h>
-#include <gnunet/gnunet_json_lib.h>
-#include "taler_crypto_lib.h"
-#include "taler_util.h"
-#include "taler_json_lib.h"
-#include "taler_exchangedb_lib.h"
-#include "taler_signatures.h"
-
-
-/**
- * Filename of the master private key.
- */
-static char *masterkeyfile;
-
-/**
- * Private key for signing.
- */
-static struct TALER_MasterPrivateKeyP master_priv;
-
-/**
- * Return value from main().
- */
-static int global_ret;
-
-
-#include "key-helper.c"
-
-
-/**
- * Function called with information about a wire account. Signs
- * the account's wire details and writes out the JSON file to disk.
- *
- * @param cls closure
- * @param ai account information
- */
-static void
-sign_account_data (void *cls,
- const struct TALER_EXCHANGEDB_AccountInfo *ai)
-{
- char *json_out;
- FILE *out;
- int ret;
-
- (void) cls;
- if (GNUNET_NO == ai->credit_enabled)
- return;
- if (NULL == ai->wire_response_filename)
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- ai->section_name,
- "WIRE_RESPONSE");
- global_ret = 1;
- return;
- }
-
- {
- json_t *wire;
-
- wire = TALER_JSON_exchange_wire_signature_make (ai->payto_uri,
- &master_priv);
- if (NULL == wire)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Could not sign wire account `%s'. Is the URI well-formed?\n",
- ai->payto_uri);
- global_ret = 1;
- return;
- }
- GNUNET_assert (NULL != wire);
- json_out = json_dumps (wire,
- JSON_INDENT (2));
- json_decref (wire);
- }
- GNUNET_assert (NULL != json_out);
- if (GNUNET_OK !=
- GNUNET_DISK_directory_create_for_file (ai->wire_response_filename))
- {
- GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
- "mkdir",
- ai->wire_response_filename);
- global_ret = 1;
- free (json_out);
- return;
- }
-
- out = fopen (ai->wire_response_filename,
- "w+"); /* create, if exists, truncate */
- if (NULL == out)
- {
- GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
- "fopen(w+)",
- ai->wire_response_filename);
- global_ret = 1;
- free (json_out);
- return;
- }
- ret = fprintf (out,
- "%s",
- json_out);
- if ( (0 != fclose (out)) ||
- (-1 == ret) )
- {
- fprintf (stderr,
- "Failure creating wire account file `%s': %s\n",
- ai->wire_response_filename,
- strerror (errno));
- /* attempt to remove malformed file */
- if (0 != unlink (ai->wire_response_filename))
- GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
- "unlink",
- ai->wire_response_filename);
- }
- else
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Created wire account file `%s'\n",
- ai->wire_response_filename);
- }
- free (json_out);
-}
-
-
-/**
- * Main function that will be run.
- *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be NULL!)
- * @param cfg configuration
- */
-static void
-run (void *cls,
- char *const *args,
- const char *cfgfile,
- const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- (void) cls;
- (void) args;
- (void) cfgfile;
-
- if (GNUNET_OK !=
- get_and_check_master_key (cfg,
- masterkeyfile,
- &master_priv))
- {
- global_ret = 1;
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Signing /wire responses\n");
- TALER_EXCHANGEDB_find_accounts (cfg,
- &sign_account_data,
- NULL);
-}
-
-
-/**
- * The main function of the taler-exchange-wire tool. This tool is
- * used to sign the bank account details using the master key.
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
-int
-main (int argc,
- char *const *argv)
-{
- const struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_option_timetravel ('T',
- "timetravel"),
- GNUNET_GETOPT_option_filename ('m',
- "master-key",
- "FILENAME",
- "master key file (private key)",
- &masterkeyfile),
- GNUNET_GETOPT_OPTION_END
- };
-
- /* force linker to link against libtalerutil; if we do
- not do this, the linker may "optimize" libtalerutil
- away and skip #TALER_OS_init(), which we do need */
- (void) TALER_project_data_default ();
- GNUNET_assert (GNUNET_OK ==
- GNUNET_log_setup ("taler-exchange-wire",
- "WARNING",
- NULL));
- if (GNUNET_OK !=
- GNUNET_PROGRAM_run (argc, argv,
- "taler-exchange-wire",
- "Setup /wire response",
- options,
- &run, NULL))
- return 1;
- return global_ret;
-}
-
-
-/* end of taler-exchange-wire.c */
diff --git a/src/exchange-tools/test_taler_exchange_httpd.conf b/src/exchange-tools/test_taler_exchange_httpd.conf
deleted file mode 100644
index 27fc067a9..000000000
--- a/src/exchange-tools/test_taler_exchange_httpd.conf
+++ /dev/null
@@ -1,130 +0,0 @@
-[PATHS]
-# Persistent data storage for the testcase
-TALER_TEST_HOME = test_taler_exchange_httpd_home/
-
-[taler]
-# Currency supported by the exchange (can only be one)
-CURRENCY = EUR
-CURRENCY_ROUND_UNIT = EUR:0.01
-
-[exchange]
-
-# Directory with our terms of service.
-TERMS_DIR = ../../contrib/tos
-
-# Etag / filename for the terms of service.
-TERMS_ETAG = 0
-
-
-# Directory with our privacy policy.
-PRIVACY_DIR = ../../contrib/pp
-
-# Etag / filename for the privacy policy.
-PRIVACY_ETAG = 0
-
-# MAX_REQUESTS = 2
-# how long is one signkey valid?
-SIGNKEY_DURATION = 4 weeks
-
-# how long are the signatures with the signkey valid?
-LEGAL_DURATION = 2 years
-
-# how long do we generate denomination and signing keys
-# ahead of time?
-LOOKAHEAD_SIGN = 2 weeks 1 day
-
-# how long do we provide to clients denomination and signing keys
-# ahead of time?
-LOOKAHEAD_PROVIDE = 1 weeks 1 day
-
-# HTTP port the exchange listens to
-PORT = 8081
-
-# Master public key used to sign the exchange's various keys
-MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
-
-# How to access our database
-DB = postgres
-
-
-[exchangedb]
-# After how long do we close idle reserves? The exchange
-# and the auditor must agree on this value. We currently
-# expect it to be globally defined for the whole system,
-# as there is no way for wallets to query this value. Thus,
-# it is only configurable for testing, and should be treated
-# as constant in production.
-IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
-
-
-[exchangedb-postgres]
-CONFIG = "postgres:///talercheck"
-
-[exchange-account-1]
-PAYTO_URI = "payto://x-taler-bank/localhost:8082/3"
-WIRE_RESPONSE = ${TALER_CONFIG_HOME}/account-1.json
-ENABLE_DEBIT = YES
-ENABLE_CREDIT = YES
-TALER_BANK_AUTH_METHOD = NONE
-
-
-# Wire fees are specified by wire method
-[fees-x-taler-bank]
-# Fees for the foreseeable future...
-# If you see this after 2018, update to match the next 10 years...
-WIRE-FEE-2018 = EUR:0.01
-WIRE-FEE-2019 = EUR:0.01
-WIRE-FEE-2020 = EUR:0.01
-WIRE-FEE-2021 = EUR:0.01
-WIRE-FEE-2022 = EUR:0.01
-WIRE-FEE-2023 = EUR:0.01
-WIRE-FEE-2024 = EUR:0.01
-WIRE-FEE-2025 = EUR:0.01
-WIRE-FEE-2026 = EUR:0.01
-WIRE-FEE-2027 = EUR:0.01
-
-CLOSING-FEE-2018 = EUR:0.01
-CLOSING-FEE-2019 = EUR:0.01
-CLOSING-FEE-2020 = EUR:0.01
-CLOSING-FEE-2021 = EUR:0.01
-CLOSING-FEE-2022 = EUR:0.01
-CLOSING-FEE-2023 = EUR:0.01
-CLOSING-FEE-2024 = EUR:0.01
-CLOSING-FEE-2025 = EUR:0.01
-CLOSING-FEE-2026 = EUR:0.01
-CLOSING-FEE-2027 = EUR:0.01
-
-
-# Coins for the tests.
-[coin_eur_ct_1]
-value = EUR:0.01
-duration_withdraw = 7 days
-duration_spend = 2 years
-duration_legal = 3 years
-fee_withdraw = EUR:0.00
-fee_deposit = EUR:0.00
-fee_refresh = EUR:0.01
-fee_refund = EUR:0.01
-rsa_keysize = 1024
-
-[coin_eur_ct_10]
-value = EUR:0.10
-duration_withdraw = 7 days
-duration_spend = 2 years
-duration_legal = 3 years
-fee_withdraw = EUR:0.01
-fee_deposit = EUR:0.01
-fee_refresh = EUR:0.03
-fee_refund = EUR:0.01
-rsa_keysize = 1024
-
-[coin_eur_1]
-value = EUR:1
-duration_withdraw = 7 days
-duration_spend = 2 years
-duration_legal = 3 years
-fee_withdraw = EUR:0.01
-fee_deposit = EUR:0.01
-fee_refresh = EUR:0.03
-fee_refund = EUR:0.01
-rsa_keysize = 1024
diff --git a/src/exchange-tools/test_taler_exchange_httpd_home/.config/taler/test.json b/src/exchange-tools/test_taler_exchange_httpd_home/.config/taler/test.json
deleted file mode 100644
index eca394241..000000000
--- a/src/exchange-tools/test_taler_exchange_httpd_home/.config/taler/test.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "salt": "AZPRFVJ58NM6M7J5CZQPJAH3EW5DYM52AEZ9Y1C1ER3W94QV8D8TQKF6CK8MYQRA9QMSKDQTGZ306ZS9GQ0M6R01CJ20KPP49WFDZK8",
- "name": "The exchange",
- "account_number": 3,
- "bank_url": "http://localhost:8082/",
- "type": "test",
- "sig": "RPQXP9S4P8PQP7HEZQNRSZCT0ATNEP8GW0P5TPM34V5RX86FCD670V44R9NETSYDDKB8SZV7TKY9PAJYTY51D3VDWY9XXQ5BPFRXR28"
-}
diff --git a/src/exchange-tools/test_taler_exchange_httpd_home/.local/share/taler/exchange/offline-keys/master.priv b/src/exchange-tools/test_taler_exchange_httpd_home/.local/share/taler/exchange/offline-keys/master.priv
deleted file mode 100644
index 394926938..000000000
--- a/src/exchange-tools/test_taler_exchange_httpd_home/.local/share/taler/exchange/offline-keys/master.priv
+++ /dev/null
@@ -1 +0,0 @@
-p^-33XX!\0qmU_ \ No newline at end of file
diff --git a/src/exchange-tools/test_taler_exchange_keyup.sh b/src/exchange-tools/test_taler_exchange_keyup.sh
deleted file mode 100755
index 26f7fe131..000000000
--- a/src/exchange-tools/test_taler_exchange_keyup.sh
+++ /dev/null
@@ -1,142 +0,0 @@
-#!/bin/bash
-#
-# This file is part of TALER
-# Copyright (C) 2015-2020 Taler Systems SA
-#
-# TALER is free software; you can redistribute it and/or modify it under the
-# terms of the GNU Affero 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 Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License along with
-# TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
-#
-#
-# This script uses 'curl' to POST various ill-formed requests to the
-# taler-exchange-httpd. Basically, the goal is to make sure that the
-# HTTP server survives (and produces the 'correct' error code).
-#
-#
-# Clear environment from variables that override config.
-unset XDG_DATA_HOME
-unset XDG_CONFIG_HOME
-#
-
-
-# Exit, with status code "skip" (no 'real' failure)
-function exit_skip() {
- echo $1
- exit 77
-}
-
-# test required commands exist
-echo -n "Testing for jq ..."
-jq -h > /dev/null || exit_skip "jq required"
-echo " OK"
-
-CONF="-c test_taler_exchange_httpd.conf"
-
-echo -n "Launching exchange ..."
-PREFIX=
-# Uncomment this line to run with valgrind...
-# PREFIX="valgrind --leak-check=yes --track-fds=yes --error-exitcode=1 --log-file=valgrind.%p"
-
-# Setup database
-taler-exchange-dbinit $CONF &> /dev/null
-# Setup keys.
-taler-exchange-keyup $CONF &> /dev/null || exit 1
-# Setup wire accounts.
-taler-exchange-wire $CONF > /dev/null || exit 1
-# Run Exchange HTTPD (in background)
-$PREFIX taler-exchange-httpd $CONF 2> test-exchange.log &
-
-# Give HTTP time to start
-
-for n in `seq 1 100`
-do
- echo -n "."
- sleep 0.1
- OK=1
- wget http://localhost:8081/ -o /dev/null -O /dev/null >/dev/null && break
- OK=0
-done
-if [ 1 != $OK ]
-then
- echo "Failed to launch exchange"
- kill -TERM $!
- wait $!
- echo Process status: $?
- exit 77
-fi
-echo " DONE"
-
-# Finally run test...
-echo -n "Running tests ... "
-
-# Revoke active denomination key
-REVOKE_DENOM_HASH=`taler-exchange-keycheck $CONF -i EUR:1 | sort | head -n1 | awk '{print $2}'`
-REVOKE_DENOM_TIME=`taler-exchange-keycheck $CONF -i EUR:1 | sort | head -n1 | awk '{print $1}'`
-
-taler-exchange-keyup $CONF -r "$REVOKE_DENOM_HASH" -k 1024
-
-# check revocation file exists
-RDIR=`taler-config $CONF -f -s exchange -o REVOCATION_DIR`
-if [ -f "$RDIR"/$REVOKE_DENOM_HASH.rev ]
-then
- echo -n "REV-OK "
-else
- echo -n "REV-FAIL ($RDIR) "
- RET=1
-fi
-
-# Check we now have two keys for that timestamp
-CNT=`taler-exchange-keycheck $CONF -i EUR:1 | awk '{print $1}' | grep -- "$REVOKE_DENOM_TIME" | wc -l`
-
-if [ x2 != x${CNT} ]
-then
- echo -n "CNT-FAIL (${CNT}) "
- RET=1
-else
- echo -n "CNT-OK "
-fi
-
-# Reload keys (and revocation data) at the exchange
-kill -SIGUSR1 $!
-
-# Give exchange chance to parse and reload keys
-sleep 5
-
-# Download (updated) keys
-wget http://localhost:8081/keys -O keys.json -o /dev/null >/dev/null
-
-RK=`jq -er .recoup[0].h_denom_pub < keys.json`
-if [ x$RK != x$REVOKE_DENOM_HASH ]
-then
- echo -n "KEYS-FAIL ($RK vs $REVOKE_DENOM_HASH)"
- RET=1
-else
- echo -n "KEYS-OK"
-fi
-
-echo " DONE"
-# $! is the last backgrounded process, hence the exchange
-kill -TERM $!
-wait $!
-if [ 0 != $? ]
-then
- RET=4
-fi
-
-echo "Final cleanup"
-# Can't leave revocations around, would mess up next test run
-rm -r "$RDIR"
-# Also cleaning up live keys, as otherwise we have two for the revoked denomination type next time
-KDIR=`taler-config $CONF -f -s exchange -o KEYDIR`
-rm -r "$KDIR"
-# Clean up our temporary file
-rm keys.json
-
-exit $RET