aboutsummaryrefslogtreecommitdiff
path: root/src/exchange-tools
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-03-01 15:35:04 +0100
committerChristian Grothoff <christian@grothoff.org>2016-03-01 15:35:04 +0100
commitb5cba3251053c22bf1df46282f1dd0a4c46f6a38 (patch)
treeb7495c3e47c40c57ff81045a4e43aa07a3b6c7b1 /src/exchange-tools
parente406833eab7ca0835f9779abebada94592a85a7e (diff)
renaming mint->exchange
Diffstat (limited to 'src/exchange-tools')
-rw-r--r--src/exchange-tools/Makefile.am81
-rw-r--r--src/exchange-tools/taler-auditor-sign.c321
-rw-r--r--src/exchange-tools/taler-exchange-dbinit.c105
-rw-r--r--src/exchange-tools/taler-exchange-keycheck.c247
-rw-r--r--src/exchange-tools/taler-exchange-keyup.c1039
-rw-r--r--src/exchange-tools/taler-exchange-reservemod.c206
-rw-r--r--src/exchange-tools/taler-exchange-sepa.c163
7 files changed, 2162 insertions, 0 deletions
diff --git a/src/exchange-tools/Makefile.am b/src/exchange-tools/Makefile.am
new file mode 100644
index 000000000..4ffabd15d
--- /dev/null
+++ b/src/exchange-tools/Makefile.am
@@ -0,0 +1,81 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+ XLIB = -lgcov
+endif
+
+bin_PROGRAMS = \
+ taler-auditor-sign \
+ taler-exchange-keyup \
+ taler-exchange-keycheck \
+ taler-exchange-reservemod \
+ taler-exchange-sepa \
+ 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/exchangedb/libtalerexchangedb.la \
+ -lgnunetutil $(XLIB)
+taler_exchange_keyup_LDFLAGS = $(POSTGRESQL_LDFLAGS)
+
+taler_auditor_sign_SOURCES = \
+ taler-auditor-sign.c
+taler_auditor_sign_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
+ -lgnunetutil $(XLIB)
+
+
+taler_exchange_sepa_SOURCES = \
+ taler-exchange-sepa.c
+taler_exchange_sepa_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetutil -ljansson $(XLIB)
+taler_exchange_sepa_LDFLAGS = $(POSTGRESQL_LDFLAGS)
+
+taler_exchange_keycheck_SOURCES = \
+ taler-exchange-keycheck.c
+taler_exchange_keycheck_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
+ -lgnunetutil $(XLIB)
+taler_exchange_keycheck_LDFLAGS = $(POSTGRESQL_LDFLAGS)
+
+taler_exchange_reservemod_SOURCES = \
+ taler-exchange-reservemod.c
+taler_exchange_reservemod_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(top_builddir)/src/util/libtalerutil.la \
+ $(top_builddir)/src/pq/libtalerpq.la \
+ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
+ -lgnunetutil -ljansson $(XLIB)
+taler_exchange_reservemod_LDFLAGS = \
+ $(POSTGRESQL_LDFLAGS)
+taler_exchange_reservemod_CPPFLAGS = \
+ -I$(top_srcdir)/src/include \
+ -I$(top_srcdir)/src/pq/ \
+ $(POSTGRESQL_CPPFLAGS)
+
+taler_exchange_dbinit_SOURCES = \
+ taler-exchange-dbinit.c
+taler_exchange_dbinit_LDADD = \
+ $(LIBGCRYPT_LIBS) \
+ $(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)
+taler_exchange_dbinit_CPPFLAGS = \
+ -I$(top_srcdir)/src/include \
+ -I$(top_srcdir)/src/pq/ \
+ $(POSTGRESQL_CPPFLAGS)
diff --git a/src/exchange-tools/taler-auditor-sign.c b/src/exchange-tools/taler-auditor-sign.c
new file mode 100644
index 000000000..e4821f411
--- /dev/null
+++ b/src/exchange-tools/taler-auditor-sign.c
@@ -0,0 +1,321 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-auditor-sign.c
+ * @brief Tool used by the auditor to sign the exchange's master key and the
+ * denomination key(s).
+ * @author Christian Grothoff
+ */
+#include <platform.h>
+#include "taler_exchangedb_lib.h"
+
+
+/**
+ * Are we running in verbose mode?
+ */
+static int verbose;
+
+/**
+ * Filename of the auditor's private key.
+ */
+static char *auditor_key_file;
+
+/**
+ * Exchange's public key (in Crockford base32 encoding).
+ */
+static char *exchange_public_key;
+
+/**
+ * File with the Exchange's denomination keys to sign, itself
+ * signed by the Exchange's public key.
+ */
+static char *exchange_request_file;
+
+/**
+ * Where should we write the auditor's signature?
+ */
+static char *output_file;
+
+/**
+ * Master public key of the exchange.
+ */
+static struct TALER_MasterPublicKeyP master_public_key;
+
+
+/**
+ * Print denomination key details for diagnostics.
+ *
+ * @param dk denomination key to print
+ */
+static void
+print_dk (const struct TALER_DenominationKeyValidityPS *dk)
+{
+ struct TALER_Amount a;
+ char *s;
+
+ fprintf (stdout,
+ "Denomination key hash: %s\n",
+ GNUNET_h2s_full (&dk->denom_hash));
+ TALER_amount_ntoh (&a,
+ &dk->value);
+ fprintf (stdout,
+ "Value: %s\n",
+ s = TALER_amount_to_string (&a));
+ GNUNET_free (s);
+ TALER_amount_ntoh (&a,
+ &dk->fee_withdraw);
+ fprintf (stdout,
+ "Withdraw fee: %s\n",
+ s = TALER_amount_to_string (&a));
+ GNUNET_free (s);
+ TALER_amount_ntoh (&a,
+ &dk->fee_deposit);
+ fprintf (stdout,
+ "Deposit fee: %s\n",
+ s = TALER_amount_to_string (&a));
+ GNUNET_free (s);
+ TALER_amount_ntoh (&a,
+ &dk->fee_refresh);
+ fprintf (stdout,
+ "Refresh fee: %s\n",
+ s = TALER_amount_to_string (&a));
+ GNUNET_free (s);
+
+ fprintf (stdout,
+ "Validity start time: %s\n",
+ GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (dk->start)));
+ fprintf (stdout,
+ "Withdraw end time: %s\n",
+ GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (dk->expire_withdraw)));
+ fprintf (stdout,
+ "Deposit end time: %s\n",
+ GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (dk->expire_spend)));
+ fprintf (stdout,
+ "Legal dispute end time: %s\n",
+ GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (dk->expire_legal)));
+
+ fprintf (stdout,
+ "\n");
+}
+
+
+/**
+ * The main function of the taler-auditor-sign tool. This tool is used
+ * to sign a exchange's master and denomination keys, affirming that the
+ * auditor is aware of them and will validate the exchange's database with
+ * respect to these keys.
+ *
+ * @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)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ {'a', "auditor-key", "FILE",
+ "file containing the private key of the auditor", 1,
+ &GNUNET_GETOPT_set_filename, &auditor_key_file},
+ TALER_GETOPT_OPTION_HELP ("Private key of the auditor to use for signing"),
+ {'m', "exchange-key", "KEY",
+ "public key of the exchange (Crockford base32 encoded)", 1,
+ &GNUNET_GETOPT_set_filename, &exchange_public_key},
+ {'r', "exchange-request", "FILE",
+ "set of keys the exchange requested the auditor to sign", 1,
+ &GNUNET_GETOPT_set_string, &exchange_request_file},
+ {'o', "output", "FILE",
+ "where to write our signature", 1,
+ &GNUNET_GETOPT_set_string, &output_file},
+ GNUNET_GETOPT_OPTION_VERSION (VERSION "-" VCS_VERSION),
+ GNUNET_GETOPT_OPTION_VERBOSE (&verbose),
+ GNUNET_GETOPT_OPTION_END
+ };
+ struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv;
+ struct TALER_AuditorSignatureP *sigs;
+ struct TALER_AuditorPublicKeyP apub;
+ struct GNUNET_DISK_FileHandle *fh;
+ struct TALER_DenominationKeyValidityPS *dks;
+ unsigned int dks_len;
+ struct TALER_ExchangeKeyValidityPS kv;
+ off_t in_size;
+ unsigned int i;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-exchange-keyup",
+ "WARNING",
+ NULL));
+ if (GNUNET_GETOPT_run ("taler-exchange-keyup",
+ options,
+ argc, argv) < 0)
+ return 1;
+ if (NULL == auditor_key_file)
+ {
+ fprintf (stderr,
+ "Auditor key file not given\n");
+ return 1;
+ }
+ eddsa_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (auditor_key_file);
+ if (NULL == eddsa_priv)
+ {
+ fprintf (stderr,
+ "Failed to initialize auditor key from file `%s'\n",
+ auditor_key_file);
+ return 1;
+ }
+ GNUNET_CRYPTO_eddsa_key_get_public (eddsa_priv,
+ &apub.eddsa_pub);
+ if (NULL == exchange_public_key)
+ {
+ fprintf (stderr,
+ "Exchange public key not given\n");
+ GNUNET_free (eddsa_priv);
+ return 1;
+ }
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (exchange_public_key,
+ strlen (exchange_public_key),
+ &master_public_key,
+ sizeof (master_public_key)))
+ {
+ fprintf (stderr,
+ "Public key `%s' malformed\n",
+ exchange_public_key);
+ GNUNET_free (eddsa_priv);
+ return 1;
+ }
+ if (NULL == exchange_request_file)
+ {
+ fprintf (stderr,
+ "Exchange signing request not given\n");
+ GNUNET_free (eddsa_priv);
+ return 1;
+ }
+ fh = GNUNET_DISK_file_open (exchange_request_file,
+ GNUNET_DISK_OPEN_READ,
+ GNUNET_DISK_PERM_NONE);
+ if (NULL == fh)
+ {
+ fprintf (stderr,
+ "Failed to open file `%s': %s\n",
+ exchange_request_file,
+ STRERROR (errno));
+ GNUNET_free (eddsa_priv);
+ return 1;
+ }
+ if (GNUNET_OK !=
+ GNUNET_DISK_file_handle_size (fh,
+ &in_size))
+ {
+ fprintf (stderr,
+ "Failed to obtain input file size `%s': %s\n",
+ exchange_request_file,
+ STRERROR (errno));
+ GNUNET_DISK_file_close (fh);
+ GNUNET_free (eddsa_priv);
+ return 1;
+ }
+ if (0 != (in_size % sizeof (struct TALER_DenominationKeyValidityPS)))
+ {
+ fprintf (stderr,
+ "Input file size of file `%s' is invalid\n",
+ exchange_request_file);
+ GNUNET_DISK_file_close (fh);
+ GNUNET_free (eddsa_priv);
+ return 1;
+ }
+ dks_len = in_size / sizeof (struct TALER_DenominationKeyValidityPS);
+ kv.purpose.purpose = htonl (TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS);
+ kv.purpose.size = htonl (sizeof (struct TALER_ExchangeKeyValidityPS));
+ kv.master = master_public_key;
+ dks = GNUNET_new_array (dks_len,
+ struct TALER_DenominationKeyValidityPS);
+ sigs = GNUNET_new_array (dks_len,
+ struct TALER_AuditorSignatureP);
+ if (in_size !=
+ GNUNET_DISK_file_read (fh,
+ dks,
+ in_size))
+ {
+ fprintf (stderr,
+ "Failed to read input file `%s': %s\n",
+ exchange_request_file,
+ STRERROR (errno));
+ GNUNET_DISK_file_close (fh);
+ GNUNET_free (sigs);
+ GNUNET_free (dks);
+ GNUNET_free (eddsa_priv);
+ return 1;
+ }
+ GNUNET_DISK_file_close (fh);
+ for (i=0;i<dks_len;i++)
+ {
+ struct TALER_DenominationKeyValidityPS *dk = &dks[i];
+
+ if (verbose)
+ print_dk (dk);
+ kv.start = dk->start;
+ kv.expire_withdraw = dk->expire_withdraw;
+ kv.expire_spend = dk->expire_spend;
+ kv.expire_legal = dk->expire_legal;
+ kv.value = dk->value;
+ kv.fee_withdraw = dk->fee_withdraw;
+ kv.fee_deposit = dk->fee_deposit;
+ kv.fee_refresh = dk->fee_refresh;
+ kv.denom_hash = dk->denom_hash;
+
+ /* Finally sign ... */
+ GNUNET_CRYPTO_eddsa_sign (eddsa_priv,
+ &kv.purpose,
+ &sigs[i].eddsa_sig);
+
+
+ }
+
+ if (NULL == output_file)
+ {
+ fprintf (stderr,
+ "Output file not given\n");
+ GNUNET_free (dks);
+ GNUNET_free (sigs);
+ GNUNET_free (eddsa_priv);
+ return 1;
+ }
+
+ /* write result to disk */
+ if (GNUNET_OK !=
+ TALER_EXCHANGEDB_auditor_write (output_file,
+ &apub,
+ sigs,
+ &master_public_key,
+ dks_len,
+ dks))
+ {
+ fprintf (stderr,
+ "Failed to write to file `%s': %s\n",
+ output_file,
+ STRERROR (errno));
+ GNUNET_free (sigs);
+ GNUNET_free (dks);
+ return 1;
+ }
+ GNUNET_free (sigs);
+ GNUNET_free (dks);
+ GNUNET_free (eddsa_priv);
+ return 0;
+}
+
+/* end of taler-auditor-sign.c */
diff --git a/src/exchange-tools/taler-exchange-dbinit.c b/src/exchange-tools/taler-exchange-dbinit.c
new file mode 100644
index 000000000..43a070228
--- /dev/null
+++ b/src/exchange-tools/taler-exchange-dbinit.c
@@ -0,0 +1,105 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file exchange-tools/taler-exchange-dbinit.c
+ * @brief Create tables for the exchange database.
+ * @author Florian Dold
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <libpq-fe.h>
+#include "taler_exchangedb_plugin.h"
+
+/**
+ * Exchange directory with the keys.
+ */
+static char *exchange_base_dir;
+
+/**
+ * Our configuration.
+ */
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Our DB plugin.
+ */
+static struct TALER_EXCHANGEDB_Plugin *plugin;
+
+
+/**
+ * The main function of the database initialization tool.
+ * Used to initialize the Taler Exchange's database.
+ *
+ * @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)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ {'d', "exchange-dir", "DIR",
+ "exchange directory", 1,
+ &GNUNET_GETOPT_set_filename, &exchange_base_dir},
+ GNUNET_GETOPT_OPTION_HELP ("Initialize Taler Exchange database"),
+ GNUNET_GETOPT_OPTION_VERSION (VERSION "-" VCS_VERSION),
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ if (GNUNET_GETOPT_run ("taler-exchange-dbinit",
+ options,
+ argc, argv) < 0)
+ return 1;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-exchange-dbinit",
+ "INFO",
+ NULL));
+ if (NULL == exchange_base_dir)
+ {
+ fprintf (stderr,
+ "Exchange base directory not given.\n");
+ return 1;
+ }
+ cfg = TALER_config_load (exchange_base_dir);
+ if (NULL == cfg)
+ {
+ fprintf (stderr,
+ "Failed to load exchange configuration.\n");
+ return 1;
+ }
+ if (NULL ==
+ (plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
+ {
+ fprintf (stderr,
+ "Failed to initialize database plugin.\n");
+ return 1;
+ }
+ if (GNUNET_OK !=
+ plugin->create_tables (plugin->cls,
+ GNUNET_NO))
+ {
+ fprintf (stderr,
+ "Failed to initialize database.\n");
+ TALER_EXCHANGEDB_plugin_unload (plugin);
+ return 1;
+ }
+ TALER_EXCHANGEDB_plugin_unload (plugin);
+ return 0;
+}
+
+/* end of taler-exchange-dbinit.c */
diff --git a/src/exchange-tools/taler-exchange-keycheck.c b/src/exchange-tools/taler-exchange-keycheck.c
new file mode 100644
index 000000000..d6566cd03
--- /dev/null
+++ b/src/exchange-tools/taler-exchange-keycheck.c
@@ -0,0 +1,247 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file 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 struct GNUNET_CONFIGURATION_Handle *kcfg;
+
+
+/**
+ * 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)
+{
+ 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) -
+ offsetof (struct TALER_ExchangeSigningKeyValidityPS,
+ purpose)))
+ {
+ 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.purpose,
+ &ski->issue.signature.eddsa_signature,
+ &ski->issue.master_public_key.eddsa_pub))
+ {
+ fprintf (stderr,
+ "Signing key `%s' has invalid signature\n",
+ filename);
+ return GNUNET_SYSERR;
+ }
+ printf ("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_DenominationKeyIssueInformation *dki)
+{
+ struct GNUNET_HashCode hc;
+
+ 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_spend).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.purpose,
+ &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 != memcmp (&hc,
+ &dki->issue.properties.denom_hash,
+ sizeof (struct GNUNET_HashCode)))
+ {
+ fprintf (stderr,
+ "Public key for `%s' does not match signature\n",
+ alias);
+ return GNUNET_SYSERR;
+ }
+ printf ("Denomination key `%s' is valid\n",
+ alias);
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Check denomination keys.
+ *
+ * @return #GNUNET_OK if the keys are OK
+ * #GNUNET_NO if not
+ */
+static int
+exchange_denomkeys_check ()
+{
+ if (0 > TALER_EXCHANGEDB_denomination_keys_iterate (exchange_directory,
+ &denomkeys_iter,
+ NULL))
+ return GNUNET_NO;
+ return GNUNET_OK;
+}
+
+
+/**
+ * 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)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_HELP ("gnunet-exchange-keycheck OPTIONS"),
+ {'d', "directory", "DIRECTORY",
+ "exchange directory with keys to check", 1,
+ &GNUNET_GETOPT_set_filename, &exchange_directory},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-exchange-keycheck",
+ "WARNING",
+ NULL));
+
+ if (GNUNET_GETOPT_run ("taler-exchange-keycheck",
+ options,
+ argc, argv) < 0)
+ return 1;
+ if (NULL == exchange_directory)
+ {
+ fprintf (stderr,
+ "Exchange directory not given\n");
+ return 1;
+ }
+
+ kcfg = TALER_config_load (exchange_directory);
+ if (NULL == kcfg)
+ {
+ fprintf (stderr,
+ "Failed to load exchange configuration\n");
+ return 1;
+ }
+ if ( (GNUNET_OK != exchange_signkeys_check ()) ||
+ (GNUNET_OK != exchange_denomkeys_check ()) )
+ {
+ GNUNET_CONFIGURATION_destroy (kcfg);
+ return 1;
+ }
+ GNUNET_CONFIGURATION_destroy (kcfg);
+ return 0;
+}
+
+/* end of taler-exchange-keycheck.c */
diff --git a/src/exchange-tools/taler-exchange-keyup.c b/src/exchange-tools/taler-exchange-keyup.c
new file mode 100644
index 000000000..779e3a3d3
--- /dev/null
+++ b/src/exchange-tools/taler-exchange-keyup.c
@@ -0,0 +1,1039 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-keyup.c
+ * @brief Update the exchange's keys for coins and signatures,
+ * 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 we hash, at 100 bits the chance of
+ * collision (by accident over tiny set -- birthday paradox does not
+ * apply here!) is negligible.
+ */
+#define HASH_CUTOFF 20
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Struct with all of the key information for a kind of coin. Hashed
+ * to generate a unique directory name per coin type.
+ */
+struct CoinTypeNBOP
+{
+ /**
+ * How long are the signatures legally valid?
+ */
+ struct GNUNET_TIME_RelativeNBO duration_legal;
+
+ /**
+ * How long can the coin be spend?
+ */
+ struct GNUNET_TIME_RelativeNBO duration_spend;
+
+ /**
+ * How long can the coin be withdrawn (generated)?
+ */
+ struct GNUNET_TIME_RelativeNBO duration_withdraw;
+
+ /**
+ * What is the value of the coin?
+ */
+ struct TALER_AmountNBO value;
+
+ /**
+ * What is the fee charged for withdrawl?
+ */
+ 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;
+
+ /**
+ * Key size in NBO.
+ */
+ uint32_t rsa_keysize;
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+/**
+ * Set of all of the parameters that chracterize a coin.
+ */
+struct CoinTypeParams
+{
+
+ /**
+ * 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 coin be spend? Should be significantly
+ * larger than @e duration_withdraw (i.e. years).
+ */
+ struct GNUNET_TIME_Relative duration_spend;
+
+ /**
+ * How long can the coin 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;
+
+ /**
+ * How much should coin creation (@e duration_withdraw) duration
+ * overlap with the next coin? Basically, the starting time of two
+ * coins is always @e duration_withdraw - @e duration_overlap apart.
+ */
+ struct GNUNET_TIME_Relative duration_overlap;
+
+ /**
+ * What is the value of the coin?
+ */
+ struct TALER_Amount value;
+
+ /**
+ * What is the fee charged for withdrawl?
+ */
+ 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;
+
+ /**
+ * 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;
+};
+
+
+/**
+ * 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;
+
+/**
+ * Time to pretend when the key update is executed.
+ */
+static char *pretend_time_str;
+
+/**
+ * Handle to the exchange's configuration
+ */
+static 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;
+
+/**
+ * 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;
+
+
+/**
+ * Obtain the name of the directory we use to store signing
+ * keys created at time @a start.
+ *
+ * @param start time at which we create the signing key
+ * @return name of the directory we should use, basically "$EXCHANGEDIR/$TIME/";
+ * (valid until next call to this function)
+ */
+static const char *
+get_signkey_file (struct GNUNET_TIME_Absolute start)
+{
+ static char dir[4096];
+
+ GNUNET_snprintf (dir,
+ sizeof (dir),
+ "%s" DIR_SEPARATOR_STR TALER_EXCHANGEDB_DIR_SIGNING_KEYS DIR_SEPARATOR_STR "%llu",
+ exchange_directory,
+ (unsigned long long) start.abs_value_us);
+ return dir;
+}
+
+
+/**
+ * Hash the data defining the coin type. Exclude information that may
+ * not be the same for all instances of the coin type (i.e. the
+ * anchor, overlap).
+ *
+ * @param p coin parameters to convert to a hash
+ * @param[out] hash set to the hash matching @a p
+ */
+static void
+hash_coin_type (const struct CoinTypeParams *p,
+ struct GNUNET_HashCode *hash)
+{
+ struct CoinTypeNBOP p_nbo;
+
+ memset (&p_nbo,
+ 0,
+ sizeof (struct CoinTypeNBOP));
+ 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);
+ p_nbo.rsa_keysize = htonl (p->rsa_keysize);
+ GNUNET_CRYPTO_hash (&p_nbo,
+ sizeof (struct CoinTypeNBOP),
+ hash);
+}
+
+
+/**
+ * Obtain the name of the directory we should use to store coins of
+ * the given type. The directory name has the format
+ * "$EXCHANGEDIR/$VALUE/$HASH/" where "$VALUE" represents the value of the
+ * coin and "$HASH" encodes all of the coin's parameters, generating a
+ * unique string for each type of coin. Note that the "$HASH"
+ * includes neither the absolute creation time nor the key of the
+ * coin, thus the files in the subdirectory really just refer to the
+ * same type of coins, not the same coin.
+ *
+ * @param p coin parameters to convert to a directory name
+ * @return directory name (valid until next call to this function)
+ */
+static const char *
+get_cointype_dir (const struct CoinTypeParams *p)
+{
+ static char dir[4096];
+ struct GNUNET_HashCode hash;
+ char *hash_str;
+ char *val_str;
+ size_t i;
+
+ hash_coin_type (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);
+ for (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 coin of the given type @a p and validity
+ * start time @a start
+ *
+ * @param p parameters for the coin
+ * @param start when would the coin begin to be issued
+ * @return name of the file to use for this coin
+ * (valid until next call to this function)
+ */
+static const char *
+get_cointype_file (const struct CoinTypeParams *p,
+ struct GNUNET_TIME_Absolute start)
+{
+ static char filename[4096];
+ const char *dir;
+
+ dir = get_cointype_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 coin 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;
+
+ base = GNUNET_STRINGS_get_short_name (filename);
+ stamp.abs_value_us = strtol (base,
+ &end,
+ 10);
+ if ((NULL == end) || (0 != *end))
+ {
+ fprintf(stderr,
+ "Ignoring unexpected file `%s'.\n",
+ filename);
+ return GNUNET_OK;
+ }
+ *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)
+ {
+ /* Real starting time is the last start time + duration - overlap */
+ *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 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 GNUNET_CRYPTO_EddsaPrivateKey *priv;
+ struct TALER_ExchangeSigningKeyValidityPS *issue = &pi->issue;
+
+ priv = GNUNET_CRYPTO_eddsa_key_create ();
+ pi->signkey_priv.eddsa_priv = *priv;
+ GNUNET_free (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) -
+ offsetof (struct TALER_ExchangeSigningKeyValidityPS,
+ purpose));
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv,
+ &issue->purpose,
+ &issue->signature.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 ()
+{
+ 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_keys",
+ "signkey_duration",
+ &signkey_duration))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange_keys",
+ "signkey_duration");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (kcfg,
+ "exchange_keys",
+ "legal_duration",
+ &legal_duration))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange_keys",
+ "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_keys",
+ "legal_duration",
+ "must be longer than signkey_duration");
+ return GNUNET_SYSERR;
+ }
+ TALER_round_rel_time (&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");
+ return GNUNET_SYSERR;
+ }
+
+ get_anchor (signkey_dir,
+ signkey_duration,
+ GNUNET_TIME_UNIT_ZERO /* no overlap for signing keys */,
+ &anchor);
+
+ while (anchor.abs_value_us < lookahead_sign_stamp.abs_value_us)
+ {
+ const char *skf;
+ struct TALER_EXCHANGEDB_PrivateSigningKeyInformationP signkey_issue;
+ ssize_t nwrite;
+ struct GNUNET_TIME_Absolute end;
+
+ skf = get_signkey_file (anchor);
+ end = GNUNET_TIME_absolute_add (anchor,
+ legal_duration);
+ GNUNET_break (GNUNET_YES !=
+ GNUNET_DISK_file_test (skf));
+ 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);
+ nwrite = GNUNET_DISK_fn_write (skf,
+ &signkey_issue,
+ sizeof (struct TALER_EXCHANGEDB_PrivateSigningKeyInformationP),
+ GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_USER_READ);
+ if (sizeof (struct TALER_EXCHANGEDB_PrivateSigningKeyInformationP) != nwrite)
+ {
+ fprintf (stderr,
+ "Failed to write to file `%s': %s\n",
+ skf,
+ STRERROR (errno));
+ return GNUNET_SYSERR;
+ }
+ anchor = GNUNET_TIME_absolute_add (anchor,
+ signkey_duration);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Parse configuration for coin type parameters. Also determines
+ * our anchor by looking at the existing coins of the same type.
+ *
+ * @param ct section in the configuration file giving the coin type parameters
+ * @param[out] params set to the coin parameters from the configuration
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is invalid
+ */
+static int
+get_cointype_params (const char *ct,
+ struct CoinTypeParams *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;
+ }
+ TALER_round_rel_time (&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;
+ }
+ TALER_round_rel_time (&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;
+ }
+ TALER_round_rel_time (&params->duration_legal);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (kcfg,
+ ct,
+ "duration_overlap",
+ &params->duration_overlap))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ ct,
+ "exchange_denom_duration_overlap");
+ return GNUNET_SYSERR;
+ }
+ TALER_round_rel_time (&params->duration_overlap);
+ 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_denom (kcfg,
+ ct,
+ "value",
+ &params->value))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ ct,
+ "value");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ TALER_config_get_denom (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_denom (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_denom (kcfg,
+ ct,
+ "fee_refresh",
+ &params->fee_refresh))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ ct,
+ "fee_refresh");
+ return GNUNET_SYSERR;
+ }
+
+ dir = get_cointype_dir (params);
+ get_anchor (dir,
+ params->duration_withdraw,
+ params->duration_overlap,
+ &params->anchor);
+ 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 coin'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 CoinTypeParams *params,
+ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *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_spend =
+ 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);
+ dki->issue.properties.purpose.purpose
+ = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
+ dki->issue.properties.purpose.size
+ = htonl (sizeof (struct TALER_DenominationKeyValidityPS));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_eddsa_sign (&master_priv.eddsa_priv,
+ &dki->issue.properties.purpose,
+ &dki->issue.signature.eddsa_signature));
+}
+
+
+/**
+ * Generate new coin signing keys for the coin type of the given @a
+ * coin_alias.
+ *
+ * @param cls a `int *`, to be set to #GNUNET_SYSERR on failure
+ * @param coin_alias name of the coin's section in the configuration
+ */
+static void
+exchange_keys_update_cointype (void *cls,
+ const char *coin_alias)
+{
+ int *ret = cls;
+ struct CoinTypeParams p;
+ const char *dkf;
+ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation denomkey_issue;
+
+ if (0 != strncasecmp (coin_alias,
+ "coin_",
+ strlen ("coin_")))
+ return; /* not a coin definition */
+ if (GNUNET_OK !=
+ get_cointype_params (coin_alias,
+ &p))
+ {
+ *ret = GNUNET_SYSERR;
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_DISK_directory_create (get_cointype_dir (&p)))
+ {
+ *ret = GNUNET_SYSERR;
+ return;
+ }
+
+ while (p.anchor.abs_value_us < lookahead_sign_stamp.abs_value_us)
+ {
+ dkf = get_cointype_file (&p,
+ p.anchor);
+ GNUNET_break (GNUNET_YES != GNUNET_DISK_file_test (dkf));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Generating denomination key for type `%s', start %s at %s\n",
+ coin_alias,
+ GNUNET_STRINGS_absolute_time_to_string (p.anchor),
+ dkf);
+ create_denomkey_issue (&p,
+ &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);
+ *ret = GNUNET_SYSERR;
+ GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key);
+ return;
+ }
+ if ( (NULL != auditor_output_file) &&
+ (sizeof (denomkey_issue.issue.properties) !=
+ 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));
+ *ret = GNUNET_SYSERR;
+ return;
+ }
+ GNUNET_CRYPTO_rsa_private_key_free (denomkey_issue.denom_priv.rsa_private_key);
+ p.anchor = GNUNET_TIME_absolute_add (p.anchor,
+ p.duration_spend);
+ p.anchor = GNUNET_TIME_absolute_subtract (p.anchor,
+ p.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 ()
+{
+ int ok;
+
+ ok = GNUNET_OK;
+ GNUNET_CONFIGURATION_iterate_sections (kcfg,
+ &exchange_keys_update_cointype,
+ &ok);
+ return ok;
+}
+
+
+/**
+ * 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 so as 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)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ {'d', "exchange-dir", "DIR",
+ "exchange directory with keys to update", 1,
+ &GNUNET_GETOPT_set_filename, &exchange_directory},
+ TALER_GETOPT_OPTION_HELP ("Setup signing and denomination keys for a Taler exchange"),
+ {'m', "master-key", "FILE",
+ "master key file (private key)", 1,
+ &GNUNET_GETOPT_set_filename, &masterkeyfile},
+ {'o', "output", "FILE",
+ "auditor denomination key signing request file to create", 1,
+ &GNUNET_GETOPT_set_filename, &auditorrequestfile},
+ {'t', "time", "TIMESTAMP",
+ "pretend it is a different time for the update", 0,
+ &GNUNET_GETOPT_set_string, &pretend_time_str},
+ GNUNET_GETOPT_OPTION_VERSION (VERSION "-" VCS_VERSION),
+ GNUNET_GETOPT_OPTION_END
+ };
+ struct GNUNET_TIME_Relative lookahead_sign;
+ struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-exchange-keyup",
+ "WARNING",
+ NULL));
+
+ if (GNUNET_GETOPT_run ("taler-exchange-keyup",
+ options,
+ argc, argv) < 0)
+ return 1;
+ if (NULL == exchange_directory)
+ {
+ fprintf (stderr,
+ "Exchange directory not given\n");
+ return 1;
+ }
+ if (NULL != pretend_time_str)
+ {
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_fancy_time_to_absolute (pretend_time_str,
+ &now))
+ {
+ fprintf (stderr,
+ "timestamp `%s' invalid\n",
+ pretend_time_str);
+ return 1;
+ }
+ }
+ else
+ {
+ now = GNUNET_TIME_absolute_get ();
+ }
+ TALER_round_abs_time (&now);
+
+ kcfg = TALER_config_load (exchange_directory);
+ if (NULL == kcfg)
+ {
+ fprintf (stderr,
+ "Failed to load exchange configuration\n");
+ return 1;
+ }
+ if (NULL == masterkeyfile)
+ {
+ fprintf (stderr,
+ "Master key file not given\n");
+ return 1;
+ }
+ eddsa_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile);
+ if (NULL == eddsa_priv)
+ {
+ fprintf (stderr,
+ "Failed to initialize master key from file `%s'\n",
+ masterkeyfile);
+ return 1;
+ }
+ master_priv.eddsa_priv = *eddsa_priv;
+ GNUNET_free (eddsa_priv);
+ 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)
+ {
+ fprintf (stderr,
+ "Failed to open `%s' for writing: %s\n",
+ auditorrequestfile,
+ STRERROR (errno));
+ return 1;
+ }
+ }
+
+ /* check if key from file matches the one from the configuration */
+ {
+ struct GNUNET_CRYPTO_EddsaPublicKey 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 1;
+ }
+ if (0 !=
+ memcmp (&master_public_key,
+ &master_public_key_from_cfg,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange",
+ "master_public_key",
+ _("does not match with private key"));
+ return 1;
+ }
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (kcfg,
+ "exchange_keys",
+ "lookahead_sign",
+ &lookahead_sign))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ "exchange_keys",
+ "lookahead_sign");
+ return GNUNET_SYSERR;
+ }
+ if (0 == lookahead_sign.rel_value_us)
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ "exchange_keys",
+ "lookahead_sign",
+ _("must not be zero"));
+ return GNUNET_SYSERR;
+ }
+ TALER_round_rel_time (&lookahead_sign);
+ lookahead_sign_stamp = GNUNET_TIME_absolute_add (now,
+ lookahead_sign);
+
+
+ /* finally, do actual work */
+ if (GNUNET_OK != exchange_keys_update_signkeys ())
+ return 1;
+
+ if (GNUNET_OK != exchange_keys_update_denomkeys ())
+ return 1;
+ if (NULL != auditor_output_file)
+ {
+ FCLOSE (auditor_output_file);
+ auditor_output_file = NULL;
+ }
+ return 0;
+}
+
+/* end of taler-exchange-keyup.c */
diff --git a/src/exchange-tools/taler-exchange-reservemod.c b/src/exchange-tools/taler-exchange-reservemod.c
new file mode 100644
index 000000000..984b7f8ee
--- /dev/null
+++ b/src/exchange-tools/taler-exchange-reservemod.c
@@ -0,0 +1,206 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-reservemod.c
+ * @brief Modify reserves. Allows manipulation of reserve balances.
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <libpq-fe.h>
+#include <jansson.h>
+#include "taler_exchangedb_plugin.h"
+
+/**
+ * Director of the exchange, containing the keys.
+ */
+static char *exchange_directory;
+
+/**
+ * Handle to the exchange's configuration
+ */
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Our DB plugin.
+ */
+static struct TALER_EXCHANGEDB_Plugin *plugin;
+
+
+/**
+ * The main function of the reservemod 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)
+{
+ char *reserve_pub_str = NULL;
+ char *add_str = NULL;
+ struct TALER_Amount add_value;
+ char *details = NULL;
+ json_t *jdetails;
+ json_error_t error;
+ struct TALER_ReservePublicKeyP reserve_pub;
+ struct TALER_EXCHANGEDB_Session *session;
+ const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ {'a', "add", "DENOM",
+ "value to add", 1,
+ &GNUNET_GETOPT_set_string, &add_str},
+ {'d', "exchange-dir", "DIR",
+ "exchange directory with keys to update", 1,
+ &GNUNET_GETOPT_set_filename, &exchange_directory},
+ {'D', "details", "JSON",
+ "details about the bank transaction which justify why we add this amount", 1,
+ &GNUNET_GETOPT_set_string, &details},
+ TALER_GETOPT_OPTION_HELP ("Deposit funds into a Taler reserve"),
+ {'R', "reserve", "KEY",
+ "reserve (public key) to modify", 1,
+ &GNUNET_GETOPT_set_string, &reserve_pub_str},
+ GNUNET_GETOPT_OPTION_VERSION (VERSION "-" VCS_VERSION),
+ GNUNET_GETOPT_OPTION_END
+ };
+ int ret;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-exchange-reservemod",
+ "WARNING",
+ NULL));
+
+ if (GNUNET_GETOPT_run ("taler-exchange-reservemod",
+ options,
+ argc, argv) < 0)
+ return 1;
+ if (NULL == exchange_directory)
+ {
+ fprintf (stderr,
+ "Exchange directory not given\n");
+ GNUNET_free_non_null (add_str);
+ GNUNET_free_non_null (details);
+ GNUNET_free_non_null (reserve_pub_str);
+ return 1;
+ }
+ if ((NULL == reserve_pub_str) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (reserve_pub_str,
+ strlen (reserve_pub_str),
+ &reserve_pub,
+ sizeof (struct TALER_ReservePublicKeyP))))
+ {
+ fprintf (stderr,
+ "Parsing reserve key invalid\n");
+ GNUNET_free_non_null (add_str);
+ GNUNET_free_non_null (details);
+ GNUNET_free_non_null (reserve_pub_str);
+ return 1;
+ }
+ if ( (NULL == add_str) ||
+ (GNUNET_OK !=
+ TALER_string_to_amount (add_str,
+ &add_value)) )
+ {
+ fprintf (stderr,
+ "Failed to parse currency amount `%s'\n",
+ add_str);
+ GNUNET_free_non_null (add_str);
+ GNUNET_free_non_null (details);
+ GNUNET_free_non_null (reserve_pub_str);
+ return 1;
+ }
+
+ if (NULL == details)
+ {
+ fprintf (stderr,
+ "No wiring details given (justification required)\n");
+ GNUNET_free_non_null (add_str);
+ GNUNET_free_non_null (reserve_pub_str);
+ return 1;
+ }
+
+ cfg = TALER_config_load (exchange_directory);
+ if (NULL == cfg)
+ {
+ fprintf (stderr,
+ "Failed to load exchange configuration\n");
+ GNUNET_free_non_null (add_str);
+ GNUNET_free_non_null (details);
+ GNUNET_free_non_null (reserve_pub_str);
+ return 1;
+ }
+ ret = 1;
+ if (NULL ==
+ (plugin = TALER_EXCHANGEDB_plugin_load (cfg)))
+ {
+ fprintf (stderr,
+ "Failed to initialize database plugin.\n");
+ goto cleanup;
+ }
+
+ session = plugin->get_session (plugin->cls,
+ GNUNET_NO);
+ if (NULL == session)
+ {
+ fprintf (stderr,
+ "Failed to initialize DB session\n");
+ goto cleanup;
+ }
+ jdetails = json_loads (details,
+ JSON_REJECT_DUPLICATES,
+ &error);
+ if (NULL == jdetails)
+ {
+ fprintf (stderr,
+ "Failed to parse JSON transaction details `%s': %s (%s)\n",
+ details,
+ error.text,
+ error.source);
+ goto cleanup;
+ }
+ /* FIXME: maybe allow passing timestamp via command-line? */
+ ret = plugin->reserves_in_insert (plugin->cls,
+ session,
+ &reserve_pub,
+ &add_value,
+ GNUNET_TIME_absolute_get (),
+ jdetails);
+ json_decref (jdetails);
+ if (GNUNET_SYSERR == ret)
+ {
+ fprintf (stderr,
+ "Failed to update reserve.\n");
+ goto cleanup;
+ }
+ if (GNUNET_NO == ret)
+ {
+ fprintf (stderr,
+ "Record exists, reserve not updated.\n");
+ }
+ ret = 0;
+ cleanup:
+ if (NULL != plugin)
+ TALER_EXCHANGEDB_plugin_unload (plugin);
+ if (NULL != cfg)
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_free_non_null (add_str);
+ GNUNET_free_non_null (details);
+ GNUNET_free_non_null (reserve_pub_str);
+ return ret;
+}
+
+/* end taler-exchange-reservemod.c */
diff --git a/src/exchange-tools/taler-exchange-sepa.c b/src/exchange-tools/taler-exchange-sepa.c
new file mode 100644
index 000000000..a3ac95436
--- /dev/null
+++ b/src/exchange-tools/taler-exchange-sepa.c
@@ -0,0 +1,163 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2015 GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-sepa.c
+ * @brief Create signed response for /wire/sepa requests.
+ * @author Christian Grothoff
+ */
+#include <platform.h>
+#include <jansson.h>
+#include "taler_crypto_lib.h"
+#include "taler_signatures.h"
+
+
+/**
+ * Filename of the master private key.
+ */
+static char *masterkeyfile;
+
+/**
+ * Account holder name.
+ */
+static char *sepa_name;
+
+/**
+ * IBAN number.
+ */
+static char *iban;
+
+/**
+ * BIC number.
+ */
+static char *bic;
+
+/**
+ * Where to write the result.
+ */
+static char *output_filename;
+
+
+/**
+ * The main function of the taler-exchange-sepa tool. This tool is used
+ * to sign the SEPA 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)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ {'b', "bic", "BICCODE",
+ "bank BIC code", 1,
+ &GNUNET_GETOPT_set_string, &bic},
+ {'i', "iban", "IBAN",
+ "IBAN number of the account", 1,
+ &GNUNET_GETOPT_set_string, &iban},
+ {'m', "master-key", "FILE",
+ "master key file (private key)", 1,
+ &GNUNET_GETOPT_set_filename, &masterkeyfile},
+ {'n', "name", "NAME",
+ "name of the account holder", 1,
+ &GNUNET_GETOPT_set_string, &sepa_name},
+ {'o', "output", "FILE",
+ "where to write the result", 1,
+ &GNUNET_GETOPT_set_filename, &output_filename},
+ TALER_GETOPT_OPTION_HELP ("Setup /wire/sepa response"),
+ GNUNET_GETOPT_OPTION_VERSION (VERSION "-" VCS_VERSION),
+ GNUNET_GETOPT_OPTION_END
+ };
+ struct GNUNET_CRYPTO_EddsaPrivateKey *eddsa_priv;
+ struct TALER_MasterWireSepaDetailsPS wsd;
+ struct TALER_MasterSignatureP sig;
+ struct GNUNET_HashContext *hc;
+ json_t *reply;
+ char *json_str;
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_log_setup ("taler-exchange-sepa",
+ "WARNING",
+ NULL));
+
+ if (GNUNET_GETOPT_run ("taler-exchange-sepa",
+ options,
+ argc, argv) < 0)
+ return 1;
+ if (NULL == masterkeyfile)
+ {
+ fprintf (stderr,
+ "Master key file not given\n");
+ return 1;
+ }
+ eddsa_priv = GNUNET_CRYPTO_eddsa_key_create_from_file (masterkeyfile);
+ if (NULL == eddsa_priv)
+ {
+ fprintf (stderr,
+ "Failed to initialize master key from file `%s'\n",
+ masterkeyfile);
+ return 1;
+ }
+
+ /* Compute message to sign */
+ hc = GNUNET_CRYPTO_hash_context_start ();
+ GNUNET_CRYPTO_hash_context_read (hc,
+ sepa_name,
+ strlen (sepa_name) + 1);
+ GNUNET_CRYPTO_hash_context_read (hc,
+ iban,
+ strlen (iban) + 1);
+ GNUNET_CRYPTO_hash_context_read (hc,
+ bic,
+ strlen (bic) + 1);
+ wsd.purpose.size = htonl (sizeof (wsd));
+ wsd.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SEPA_DETAILS);
+ GNUNET_CRYPTO_hash_context_finish (hc,
+ &wsd.h_sepa_details);
+ GNUNET_CRYPTO_eddsa_sign (eddsa_priv,
+ &wsd.purpose,
+ &sig.eddsa_signature);
+ GNUNET_free (eddsa_priv);
+
+ /* build JSON message */
+ reply = json_pack ("{s:s, s:s, s:s, s:o}",
+ "receiver_name", sepa_name,
+ "iban", iban,
+ "bic", bic,
+ "sig", TALER_json_from_data (&sig,
+ sizeof (sig)));
+ GNUNET_assert (NULL != reply);
+
+ /* dump result to stdout */
+ json_str = json_dumps (reply, JSON_INDENT(2));
+ GNUNET_assert (NULL != json_str);
+
+ if (NULL != output_filename)
+ {
+ fclose (stdout);
+ stdout = fopen (output_filename,
+ "w+");
+ }
+ fprintf (stdout,
+ "%s",
+ json_str);
+ fflush (stdout);
+ free (json_str);
+ return 0;
+}
+
+/* end of taler-exchange-sepa.c */