gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-exchange] branch master updated: implement new taler-auditor-offl


From: gnunet
Subject: [taler-exchange] branch master updated: implement new taler-auditor-offline tool
Date: Sun, 06 Dec 2020 00:05:51 +0100

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new 70b08396 implement new taler-auditor-offline tool
70b08396 is described below

commit 70b08396445c7d9f709ad020a608acbf812433bd
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sun Dec 6 00:05:45 2020 +0100

    implement new taler-auditor-offline tool
---
 src/exchange-tools/.gitignore                      |    1 +
 src/exchange-tools/Makefile.am                     |   15 +
 src/exchange-tools/taler-auditor-offline.c         | 1267 ++++++++++++++++++++
 src/exchange-tools/taler-exchange-offline.c        |    3 +
 src/exchange/taler-exchange-httpd_auditors.c       |   65 +-
 src/include/taler_crypto_lib.h                     |   73 ++
 src/include/taler_exchange_service.h               |    2 +-
 src/include/taler_signatures.h                     |    2 +-
 src/lib/exchange_api_handle.c                      |   42 +-
 .../testing_api_cmd_auditor_add_denom_sig.c        |   45 +-
 src/util/Makefile.am                               |    1 +
 src/util/auditor_signatures.c                      |  122 ++
 12 files changed, 1541 insertions(+), 97 deletions(-)

diff --git a/src/exchange-tools/.gitignore b/src/exchange-tools/.gitignore
index af97f4b0..4d26c9c2 100644
--- a/src/exchange-tools/.gitignore
+++ b/src/exchange-tools/.gitignore
@@ -2,3 +2,4 @@ 
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
diff --git a/src/exchange-tools/Makefile.am b/src/exchange-tools/Makefile.am
index 947e6b2b..e6096303 100644
--- a/src/exchange-tools/Makefile.am
+++ b/src/exchange-tools/Makefile.am
@@ -13,6 +13,7 @@ if USE_COVERAGE
 endif
 
 bin_PROGRAMS = \
+  taler-auditor-offline \
   taler-exchange-keyup \
   taler-exchange-keycheck \
   taler-exchange-offline \
@@ -47,6 +48,20 @@ taler_exchange_offline_LDADD = \
   $(XLIB)
 
 
+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 \
+  -lgnunetjson \
+  -lgnunetcurl \
+  -ljansson \
+  -lgnunetutil \
+  $(XLIB)
+
+
 taler_exchange_wire_SOURCES = \
   taler-exchange-wire.c
 taler_exchange_wire_LDADD = \
diff --git a/src/exchange-tools/taler-auditor-offline.c 
b/src/exchange-tools/taler-auditor-offline.c
new file mode 100644
index 00000000..18d7360c
--- /dev/null
+++ b/src/exchange-tools/taler-auditor-offline.c
@@ -0,0 +1,1267 @@
+/*
+  This file is part of TALER
+  Copyright (C) 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-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"
+
+
+/**
+ * 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;
+
+
+/**
+ * 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_Handle *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_disconnect (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;
+
+  if (NULL == out)
+    out = json_array ();
+  action = json_pack ("{ s:s, s:o }",
+                      "operation",
+                      op_name,
+                      "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.
+ *
+ * @return #GNUNET_OK on success
+ */
+static int
+load_offline_key (void)
+{
+  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,
+                                           GNUNET_YES,
+                                           &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 hr HTTP response data
+ */
+static void
+denomination_add_cb (
+  void *cls,
+  const struct TALER_EXCHANGE_HttpResponse *hr)
+{
+  struct DenominationAddRequest *dar = cls;
+
+  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),
+             hr->hint);
+  }
+  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 argumets 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 GNUNET_HashCode 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 wire account: %s#%u at %u (skipping)\n",
+             err_name,
+             err_line,
+             (unsigned int) idx);
+    global_ret = 7;
+    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 #io.
+ *
+ * @param exchange_url base URL of the exchange to use
+ */
+static void
+trigger_upload (const char *exchange_url)
+{
+  struct UploadHandler uhs[] = {
+    {
+      .key = "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 = 3;
+      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 = 3;
+      test_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)
+{
+  char *exchange_url;
+
+  if (NULL != in)
+  {
+    fprintf (stderr,
+             "Downloaded data was not consumed, refusing upload\n");
+    test_shutdown ();
+    global_ret = 4;
+    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 = 2;
+      return;
+    }
+  }
+  if (! json_is_array (out))
+  {
+    fprintf (stderr,
+             "Error: expected JSON array for `upload` command\n");
+    test_shutdown ();
+    global_ret = 2;
+    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 = 1;
+    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 hr HTTP response data
+ * @param keys information about the various keys used
+ *        by the exchange, NULL if /keys failed
+ * @param compat protocol compatibility information
+ */
+static void
+keys_cb (
+  void *cls,
+  const struct TALER_EXCHANGE_HttpResponse *hr,
+  const struct TALER_EXCHANGE_Keys *keys,
+  enum TALER_EXCHANGE_VersionCompatibility compat)
+{
+  char *const *args = cls;
+
+  switch (hr->http_status)
+  {
+  case MHD_HTTP_OK:
+    break;
+  default:
+    fprintf (stderr,
+             "Failed to download keys: %s (HTTP status: %u/%u)\n",
+             hr->hint,
+             hr->http_status,
+             (unsigned int) hr->ec);
+    TALER_EXCHANGE_disconnect (exchange);
+    exchange = NULL;
+    test_shutdown ();
+    global_ret = 4;
+    return;
+  }
+  if (NULL == args[0])
+  {
+    json_dumpf (hr->reply,
+                stdout,
+                JSON_INDENT (2));
+  }
+  else
+  {
+    in = json_incref ((json_t*) hr->reply);
+  }
+  TALER_EXCHANGE_disconnect (exchange);
+  exchange = NULL;
+  next (args);
+}
+
+
+/**
+ * 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 = 1;
+    return;
+  }
+  exchange = TALER_EXCHANGE_connect (ctx,
+                                     exchange_url,
+                                     &keys_cb,
+                                     (void *) args,
+                                     TALER_EXCHANGE_OPTION_END);
+  GNUNET_free (exchange_url);
+}
+
+
+/**
+ * Output @a denomkeys for human consumption.
+ *
+ * @param denomkeys keys to output
+ * @return #GNUNET_OK on success
+ */
+static int
+show_denomkeys (const json_t *denomkeys)
+{
+  size_t index;
+  json_t *value;
+
+  json_array_foreach (denomkeys, index, value) {
+    const char *err_name;
+    unsigned int err_line;
+    struct TALER_DenominationPublicKey denom_pub;
+    struct GNUNET_TIME_Absolute stamp_start;
+    struct GNUNET_TIME_Absolute stamp_expire_withdraw;
+    struct GNUNET_TIME_Absolute stamp_expire_deposit;
+    struct GNUNET_TIME_Absolute stamp_expire_legal;
+    struct TALER_Amount coin_value;
+    struct TALER_Amount fee_withdraw;
+    struct TALER_Amount fee_deposit;
+    struct TALER_Amount fee_refresh;
+    struct TALER_Amount fee_refund;
+    struct TALER_MasterSignatureP master_sig;
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_rsa_public_key ("denom_pub",
+                                       &denom_pub.rsa_public_key),
+      TALER_JSON_spec_amount ("value",
+                              &coin_value),
+      TALER_JSON_spec_amount ("fee_withdraw",
+                              &fee_withdraw),
+      TALER_JSON_spec_amount ("fee_deposit",
+                              &fee_deposit),
+      TALER_JSON_spec_amount ("fee_refresh",
+                              &fee_refresh),
+      TALER_JSON_spec_amount ("fee_refund",
+                              &fee_refund),
+      GNUNET_JSON_spec_absolute_time ("stamp_start",
+                                      &stamp_start),
+      GNUNET_JSON_spec_absolute_time ("stamp_expire_withdraw",
+                                      &stamp_expire_withdraw),
+      GNUNET_JSON_spec_absolute_time ("stamp_expire_deposit",
+                                      &stamp_expire_deposit),
+      GNUNET_JSON_spec_absolute_time ("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 GNUNET_HashCode h_denom_pub;
+
+    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 = 7;
+      test_shutdown ();
+      return GNUNET_SYSERR;
+    }
+    duration = GNUNET_TIME_absolute_get_difference (stamp_start,
+                                                    stamp_expire_withdraw);
+    GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.rsa_public_key,
+                                       &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,
+          &coin_value,
+          &fee_withdraw,
+          &fee_deposit,
+          &fee_refresh,
+          &fee_refund,
+          &master_pub,
+          &master_sig))
+    {
+      fprintf (stderr,
+               "Invalid master signature for key %s (aborting)\n",
+               TALER_B2S (&h_denom_pub));
+      global_ret = 9;
+      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 (&fee_withdraw);
+      deposit_fee_s = TALER_amount_to_string (&fee_deposit);
+      refresh_fee_s = TALER_amount_to_string (&fee_refresh);
+      refund_fee_s = TALER_amount_to_string (&fee_refund);
+      deposit_s = GNUNET_strdup (
+        GNUNET_STRINGS_absolute_time_to_string (stamp_expire_deposit));
+      legal_s = GNUNET_strdup (
+        GNUNET_STRINGS_absolute_time_to_string (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 (&coin_value),
+        GNUNET_STRINGS_absolute_time_to_string (stamp_start),
+        GNUNET_STRINGS_relative_time_to_string (duration,
+                                                GNUNET_NO),
+        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;
+}
+
+
+/**
+ * Show exchange denomination keys.
+ *
+ * @param args the array of command-line arguments to process next
+ */
+static void
+do_show (char *const *args)
+{
+  if (NULL == in)
+  {
+    json_error_t err;
+
+    out = 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 = 2;
+      test_shutdown ();
+      return;
+    }
+  }
+
+  {
+    const char *err_name;
+    unsigned int err_line;
+    json_t *denomkeys;
+    struct TALER_MasterPublicKeyP mpub;
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_json ("denoms",
+                             &denomkeys),
+      GNUNET_JSON_spec_fixed_auto ("master_public_key",
+                                   &mpub),
+      GNUNET_JSON_spec_end ()
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (in,
+                           spec,
+                           &err_name,
+                           &err_line))
+    {
+      fprintf (stderr,
+               "Invalid input to 'show': %s#%u (skipping)\n",
+               err_name,
+               err_line);
+      global_ret = 7;
+      test_shutdown ();
+      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 = 7;
+      test_shutdown ();
+      return;
+    }
+    if (GNUNET_OK !=
+        show_denomkeys (denomkeys))
+    {
+      global_ret = 8;
+      test_shutdown ();
+      GNUNET_JSON_parse_free (spec);
+      return;
+    }
+    GNUNET_JSON_parse_free (spec);
+  }
+  /* do NOT consume input if next argument is '-' */
+  if ( (NULL != args[0]) &&
+       (0 == strcmp ("-",
+                     args[0])) )
+  {
+    next (args + 1);
+    return;
+  }
+  json_decref (in);
+  in = NULL;
+  next (args);
+}
+
+
+/**
+ * Sign @a denomkeys with offline key.
+ *
+ * @param denomkeys keys to output
+ * @return #GNUNET_OK on success
+ */
+static int
+sign_denomkeys (const json_t *denomkeys)
+{
+  size_t index;
+  json_t *value;
+
+  json_array_foreach (denomkeys, index, value) {
+    const char *err_name;
+    unsigned int err_line;
+    struct TALER_DenominationPublicKey denom_pub;
+    struct GNUNET_TIME_Absolute stamp_start;
+    struct GNUNET_TIME_Absolute stamp_expire_withdraw;
+    struct GNUNET_TIME_Absolute stamp_expire_deposit;
+    struct GNUNET_TIME_Absolute stamp_expire_legal;
+    struct TALER_Amount coin_value;
+    struct TALER_Amount fee_withdraw;
+    struct TALER_Amount fee_deposit;
+    struct TALER_Amount fee_refresh;
+    struct TALER_Amount fee_refund;
+    struct TALER_MasterSignatureP master_sig;
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_rsa_public_key ("denom_pub",
+                                       &denom_pub.rsa_public_key),
+      TALER_JSON_spec_amount ("value",
+                              &coin_value),
+      TALER_JSON_spec_amount ("fee_withdraw",
+                              &fee_withdraw),
+      TALER_JSON_spec_amount ("fee_deposit",
+                              &fee_deposit),
+      TALER_JSON_spec_amount ("fee_refresh",
+                              &fee_refresh),
+      TALER_JSON_spec_amount ("fee_refund",
+                              &fee_refund),
+      GNUNET_JSON_spec_absolute_time ("stamp_start",
+                                      &stamp_start),
+      GNUNET_JSON_spec_absolute_time ("stamp_expire_withdraw",
+                                      &stamp_expire_withdraw),
+      GNUNET_JSON_spec_absolute_time ("stamp_expire_deposit",
+                                      &stamp_expire_deposit),
+      GNUNET_JSON_spec_absolute_time ("stamp_expire_legal",
+                                      &stamp_expire_legal),
+      GNUNET_JSON_spec_fixed_auto ("master_sig",
+                                   &master_sig),
+      GNUNET_JSON_spec_end ()
+    };
+    struct GNUNET_HashCode h_denom_pub;
+
+    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) index);
+      GNUNET_JSON_parse_free (spec);
+      global_ret = 7;
+      test_shutdown ();
+      return GNUNET_SYSERR;
+    }
+    GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.rsa_public_key,
+                                       &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,
+          &coin_value,
+          &fee_withdraw,
+          &fee_deposit,
+          &fee_refresh,
+          &fee_refund,
+          &master_pub,
+          &master_sig))
+    {
+      fprintf (stderr,
+               "Invalid master signature for key %s (aborting)\n",
+               TALER_B2S (&h_denom_pub));
+      global_ret = 9;
+      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,
+                                         &coin_value,
+                                         &fee_withdraw,
+                                         &fee_deposit,
+                                         &fee_refresh,
+                                         &fee_refund,
+                                         &auditor_priv,
+                                         &auditor_sig);
+      output_operation ("sign-denomination",
+                        json_pack ("{s:o,s:o}",
+                                   "h_denomn_pub",
+                                   GNUNET_JSON_from_data_auto (&h_denom_pub),
+                                   "auditor_sig",
+                                   GNUNET_JSON_from_data_auto (&auditor_sig)));
+    }
+    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)
+{
+  if (NULL == in)
+  {
+    json_error_t err;
+
+    out = 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 = 2;
+      test_shutdown ();
+      return;
+    }
+  }
+  if (GNUNET_OK !=
+      load_offline_key ())
+    return;
+
+  {
+    const char *err_name;
+    unsigned int err_line;
+    struct TALER_MasterPublicKeyP mpub;
+    json_t *denomkeys;
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_json ("denoms",
+                             &denomkeys),
+      GNUNET_JSON_spec_fixed_auto ("master_public_key",
+                                   &mpub),
+      GNUNET_JSON_spec_end ()
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (in,
+                           spec,
+                           &err_name,
+                           &err_line))
+    {
+      fprintf (stderr,
+               "Invalid input to 'sign': %s#%u (skipping)\n",
+               err_name,
+               err_line);
+      global_ret = 7;
+      test_shutdown ();
+      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 = 7;
+      test_shutdown ();
+      return;
+    }
+    if (GNUNET_OK !=
+        sign_denomkeys (denomkeys))
+    {
+      global_ret = 8;
+      test_shutdown ();
+      GNUNET_JSON_parse_free (spec);
+      return;
+    }
+    GNUNET_JSON_parse_free (spec);
+  }
+  json_decref (in);
+  in = NULL;
+  next (args);
+}
+
+
+static void
+work (void *cls)
+{
+  char *const *args = cls;
+  struct SubCommand cmds[] = {
+    {
+      .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 = 3;
+  }
+  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)
+{
+  kcfg = cfg;
+  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 = 1;
+    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 = 1;
+      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 = 1;
+      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
+  };
+
+  /* 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-auditor-offline",
+                                   "WARNING",
+                                   NULL));
+  if (GNUNET_OK !=
+      GNUNET_PROGRAM_run (argc, argv,
+                          "taler-auditor-offline",
+                          "Operations for offline signing for a Taler 
exchange",
+                          options,
+                          &run, NULL))
+    return 1;
+  return global_ret;
+}
+
+
+/* end of taler-auditor-offline.c */
diff --git a/src/exchange-tools/taler-exchange-offline.c 
b/src/exchange-tools/taler-exchange-offline.c
index d6b16514..5c9e6398 100644
--- a/src/exchange-tools/taler-exchange-offline.c
+++ b/src/exchange-tools/taler-exchange-offline.c
@@ -2391,6 +2391,9 @@ do_sign (char *const *args)
       return;
     }
   }
+  if (GNUNET_OK !=
+      load_offline_key ())
+    return;
 
   {
     const char *err_name;
diff --git a/src/exchange/taler-exchange-httpd_auditors.c 
b/src/exchange/taler-exchange-httpd_auditors.c
index f0fbb7eb..62bfc304 100644
--- a/src/exchange/taler-exchange-httpd_auditors.c
+++ b/src/exchange/taler-exchange-httpd_auditors.c
@@ -144,49 +144,34 @@ add_auditor_denom_sig (void *cls,
       TALER_B2S (awc->auditor_pub));
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
+  if (GNUNET_OK !=
+      TALER_auditor_denom_validity_verify (
+        auditor_url,
+        awc->h_denom_pub,
+        &TEH_master_public_key,
+        meta.start,
+        meta.expire_withdraw,
+        meta.expire_deposit,
+        meta.expire_legal,
+        &meta.value,
+        &meta.fee_withdraw,
+        &meta.fee_deposit,
+        &meta.fee_refresh,
+        &meta.fee_refund,
+        awc->auditor_pub,
+        &awc->auditor_sig))
   {
-    struct TALER_ExchangeKeyValidityPS kv = {
-      .purpose.purpose = htonl (TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS),
-      .purpose.size = htonl (sizeof (kv)),
-      .master = TEH_master_public_key,
-      .start = GNUNET_TIME_absolute_hton (meta.start),
-      .expire_withdraw = GNUNET_TIME_absolute_hton (meta.expire_withdraw),
-      .expire_deposit = GNUNET_TIME_absolute_hton (meta.expire_deposit),
-      .expire_legal = GNUNET_TIME_absolute_hton (meta.expire_legal),
-      .denom_hash = *awc->h_denom_pub
-    };
-
-    TALER_amount_hton (&kv.value,
-                       &meta.value);
-    TALER_amount_hton (&kv.fee_withdraw,
-                       &meta.fee_withdraw);
-    TALER_amount_hton (&kv.fee_deposit,
-                       &meta.fee_deposit);
-    TALER_amount_hton (&kv.fee_refresh,
-                       &meta.fee_refresh);
-    TALER_amount_hton (&kv.fee_refund,
-                       &meta.fee_refund);
-    GNUNET_CRYPTO_hash (auditor_url,
-                        strlen (auditor_url) + 1,
-                        &kv.auditor_url_hash);
     GNUNET_free (auditor_url);
-    if (GNUNET_OK !=
-        GNUNET_CRYPTO_eddsa_verify (
-          TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS,
-          &kv,
-          &awc->auditor_sig.eddsa_sig,
-          &TEH_master_public_key.eddsa_pub))
-    {
-      /* signature invalid */
-      GNUNET_break_op (0);
-      *mhd_ret = TALER_MHD_reply_with_error (
-        connection,
-        MHD_HTTP_FORBIDDEN,
-        TALER_EC_EXCHANGE_AUDITORS_AUDITOR_SIGNATURE_INVALID,
-        NULL);
-      return GNUNET_DB_STATUS_HARD_ERROR;
-    }
+    /* signature invalid */
+    GNUNET_break_op (0);
+    *mhd_ret = TALER_MHD_reply_with_error (
+      connection,
+      MHD_HTTP_FORBIDDEN,
+      TALER_EC_EXCHANGE_AUDITORS_AUDITOR_SIGNATURE_INVALID,
+      NULL);
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
+  GNUNET_free (auditor_url);
 
   qs = TEH_plugin->insert_auditor_denom_sig (TEH_plugin->cls,
                                              session,
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index 757dee67..1c77bfe6 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -1294,6 +1294,79 @@ TALER_exchange_secmod_rsa_verify (
   const struct TALER_SecurityModuleSignatureP *secm_sig);
 
 
+/**
+ * Create denomination key validity signature by the auditor.
+ *
+ * @param auditor_url BASE URL of the auditor's API
+ * @param h_denom_pub hash of the denomination's public key
+ * @param master_pub master public key of the exchange
+ * @param stamp_start when does the exchange begin signing with this key
+ * @param stamp_expire_withdraw when does the exchange end signing with this 
key
+ * @param stamp_expire_deposit how long does the exchange accept the deposit 
of coins with this key
+ * @param stamp_expire_legal how long does the exchange preserve information 
for legal disputes with this key
+ * @param coin_value what is the value of coins signed with this key
+ * @param fee_withdraw what withdraw fee does the exchange charge for this 
denomination
+ * @param fee_deposit what deposit fee does the exchange charge for this 
denomination
+ * @param fee_refresh what refresh fee does the exchange charge for this 
denomination
+ * @param fee_refund what refund fee does the exchange charge for this 
denomination
+ * @param auditor_priv private key to sign with
+ * @param[out] auditor_sig where to write the signature
+ */
+void
+TALER_auditor_denom_validity_sign (
+  const char *auditor_url,
+  const struct GNUNET_HashCode *h_denom_pub,
+  const struct TALER_MasterPublicKeyP *master_pub,
+  struct GNUNET_TIME_Absolute stamp_start,
+  struct GNUNET_TIME_Absolute stamp_expire_withdraw,
+  struct GNUNET_TIME_Absolute stamp_expire_deposit,
+  struct GNUNET_TIME_Absolute stamp_expire_legal,
+  const struct TALER_Amount *coin_value,
+  const struct TALER_Amount *fee_withdraw,
+  const struct TALER_Amount *fee_deposit,
+  const struct TALER_Amount *fee_refresh,
+  const struct TALER_Amount *fee_refund,
+  const struct TALER_AuditorPrivateKeyP *auditor_priv,
+  struct TALER_AuditorSignatureP *auditor_sig);
+
+
+/**
+ * Verify denomination key validity signature from auditor.
+ *
+ * @param auditor_url BASE URL of the auditor's API
+ * @param h_denom_pub hash of the denomination's public key
+ * @param master_pub master public key of the exchange
+ * @param stamp_start when does the exchange begin signing with this key
+ * @param stamp_expire_withdraw when does the exchange end signing with this 
key
+ * @param stamp_expire_deposit how long does the exchange accept the deposit 
of coins with this key
+ * @param stamp_expire_legal how long does the exchange preserve information 
for legal disputes with this key
+ * @param coin_value what is the value of coins signed with this key
+ * @param fee_withdraw what withdraw fee does the exchange charge for this 
denomination
+ * @param fee_deposit what deposit fee does the exchange charge for this 
denomination
+ * @param fee_refresh what refresh fee does the exchange charge for this 
denomination
+ * @param fee_refund what refund fee does the exchange charge for this 
denomination
+ * @param auditor_pub public key to verify against
+ * @param auditor_sig the signature the signature
+ * @return #GNUNET_OK if the signature is valid
+ */
+int
+TALER_auditor_denom_validity_verify (
+  const char *auditor_url,
+  const struct GNUNET_HashCode *h_denom_pub,
+  const struct TALER_MasterPublicKeyP *master_pub,
+  struct GNUNET_TIME_Absolute stamp_start,
+  struct GNUNET_TIME_Absolute stamp_expire_withdraw,
+  struct GNUNET_TIME_Absolute stamp_expire_deposit,
+  struct GNUNET_TIME_Absolute stamp_expire_legal,
+  const struct TALER_Amount *coin_value,
+  const struct TALER_Amount *fee_withdraw,
+  const struct TALER_Amount *fee_deposit,
+  const struct TALER_Amount *fee_refresh,
+  const struct TALER_Amount *fee_refund,
+  const struct TALER_AuditorPublicKeyP *auditor_pub,
+  const struct TALER_AuditorSignatureP *auditor_sig);
+
+
 /* **************** /wire account offline signing **************** */
 
 
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 512dfb35..5691e58d 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -393,7 +393,7 @@ struct TALER_EXCHANGE_HttpResponse
 
 /**
  * Function called with information about who is auditing
- * a particular exchange and what key the exchange is using.
+ * a particular exchange and what keys the exchange is using.
  *
  * @param cls closure
  * @param hr HTTP response data
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
index c30f21d6..5f380812 100644
--- a/src/include/taler_signatures.h
+++ b/src/include/taler_signatures.h
@@ -1041,7 +1041,7 @@ struct TALER_ExchangeKeyValidityPS
 
   /**
    * The long-term offline master key of the exchange, affirmed by the
-   * auditor.  Hashed string, including 0-terminator.
+   * auditor.
    */
   struct TALER_MasterPublicKeyP master;
 
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index e630adf9..9d4ebf56 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -519,7 +519,6 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
   unsigned int off;
   unsigned int i;
   const char *auditor_url;
-  struct TALER_ExchangeKeyValidityPS kv;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("auditor_pub",
                                  &auditor->auditor_pub),
@@ -539,12 +538,6 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
     return GNUNET_SYSERR;
   }
   auditor->auditor_url = GNUNET_strdup (auditor_url);
-  kv.purpose.purpose = htonl (TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS);
-  kv.purpose.size = htonl (sizeof (struct TALER_ExchangeKeyValidityPS));
-  GNUNET_CRYPTO_hash (auditor_url,
-                      strlen (auditor_url) + 1,
-                      &kv.auditor_url_hash);
-  kv.master = key_data->master_pub;
   len = json_array_size (keys);
   auditor->denom_keys = GNUNET_new_array (len,
                                           struct
@@ -590,27 +583,22 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
     }
     if (check_sigs)
     {
-      kv.start = GNUNET_TIME_absolute_hton (dk->valid_from);
-      kv.expire_withdraw = GNUNET_TIME_absolute_hton 
(dk->withdraw_valid_until);
-      kv.expire_deposit = GNUNET_TIME_absolute_hton (dk->expire_deposit);
-      kv.expire_legal = GNUNET_TIME_absolute_hton (dk->expire_legal);
-      TALER_amount_hton (&kv.value,
-                         &dk->value);
-      TALER_amount_hton (&kv.fee_withdraw,
-                         &dk->fee_withdraw);
-      TALER_amount_hton (&kv.fee_deposit,
-                         &dk->fee_deposit);
-      TALER_amount_hton (&kv.fee_refresh,
-                         &dk->fee_refresh);
-      TALER_amount_hton (&kv.fee_refund,
-                         &dk->fee_refund);
-      kv.denom_hash = dk->h_key;
-
       if (GNUNET_OK !=
-          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS,
-                                      &kv,
-                                      &auditor_sig.eddsa_sig,
-                                      &auditor->auditor_pub.eddsa_pub))
+          TALER_auditor_denom_validity_verify (
+            auditor_url,
+            &dk->h_key,
+            &key_data->master_pub,
+            dk->valid_from,
+            dk->withdraw_valid_until,
+            dk->expire_deposit,
+            dk->expire_legal,
+            &dk->value,
+            &dk->fee_withdraw,
+            &dk->fee_deposit,
+            &dk->fee_refresh,
+            &dk->fee_refund,
+            &auditor->auditor_pub,
+            &auditor_sig))
       {
         GNUNET_break_op (0);
         GNUNET_JSON_parse_free (spec);
diff --git a/src/testing/testing_api_cmd_auditor_add_denom_sig.c 
b/src/testing/testing_api_cmd_auditor_add_denom_sig.c
index 8d5aace4..efa4a9d7 100644
--- a/src/testing/testing_api_cmd_auditor_add_denom_sig.c
+++ b/src/testing/testing_api_cmd_auditor_add_denom_sig.c
@@ -139,36 +139,25 @@ auditor_add_run (void *cls,
   }
   else
   {
-    struct TALER_ExchangeKeyValidityPS kv = {
-      .purpose.purpose = htonl (TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS),
-      .purpose.size = htonl (sizeof (struct TALER_ExchangeKeyValidityPS)),
-      .start = GNUNET_TIME_absolute_hton (dk->valid_from),
-      .expire_withdraw = GNUNET_TIME_absolute_hton (
-        dk->withdraw_valid_until),
-      .expire_deposit = GNUNET_TIME_absolute_hton (dk->expire_deposit),
-      .expire_legal = GNUNET_TIME_absolute_hton (dk->expire_legal),
-      .denom_hash = dk->h_key
-    };
+    struct TALER_MasterPublicKeyP master_pub;
 
-    TALER_amount_hton (&kv.value,
-                       &dk->value);
-    TALER_amount_hton (&kv.fee_withdraw,
-                       &dk->fee_withdraw);
-    TALER_amount_hton (&kv.fee_deposit,
-                       &dk->fee_deposit);
-    TALER_amount_hton (&kv.fee_refresh,
-                       &dk->fee_refresh);
-    TALER_amount_hton (&kv.fee_refund,
-                       &dk->fee_refund);
     GNUNET_CRYPTO_eddsa_key_get_public (&is->master_priv.eddsa_priv,
-                                        &kv.master.eddsa_pub);
-    GNUNET_CRYPTO_hash (is->auditor_url,
-                        strlen (is->auditor_url) + 1,
-                        &kv.auditor_url_hash);
-    /* Finally sign ... */
-    GNUNET_CRYPTO_eddsa_sign (&is->auditor_priv.eddsa_priv,
-                              &kv,
-                              &auditor_sig.eddsa_sig);
+                                        &master_pub.eddsa_pub);
+    TALER_auditor_denom_validity_sign (
+      is->auditor_url,
+      &dk->h_key,
+      &master_pub,
+      dk->valid_from,
+      dk->withdraw_valid_until,
+      dk->expire_deposit,
+      dk->expire_legal,
+      &dk->value,
+      &dk->fee_withdraw,
+      &dk->fee_deposit,
+      &dk->fee_refresh,
+      &dk->fee_refund,
+      &is->auditor_priv,
+      &auditor_sig);
   }
   ds->dh = TALER_EXCHANGE_add_auditor_denomination (
     is->ctx,
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index bf460a57..9dcc4035 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -60,6 +60,7 @@ lib_LTLIBRARIES = \
 
 libtalerutil_la_SOURCES = \
   amount.c \
+  auditor_signatures.c \
   config.c \
   crypto.c \
   crypto_helper_denom.c \
diff --git a/src/util/auditor_signatures.c b/src/util/auditor_signatures.c
new file mode 100644
index 00000000..837b31c6
--- /dev/null
+++ b/src/util/auditor_signatures.c
@@ -0,0 +1,122 @@
+/*
+  This file is part of TALER
+  Copyright (C) 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 auditor_signatures.c
+ * @brief Utility functions for Taler auditor signatures
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include "taler_signatures.h"
+
+
+void
+TALER_auditor_denom_validity_sign (
+  const char *auditor_url,
+  const struct GNUNET_HashCode *h_denom_pub,
+  const struct TALER_MasterPublicKeyP *master_pub,
+  struct GNUNET_TIME_Absolute stamp_start,
+  struct GNUNET_TIME_Absolute stamp_expire_withdraw,
+  struct GNUNET_TIME_Absolute stamp_expire_deposit,
+  struct GNUNET_TIME_Absolute stamp_expire_legal,
+  const struct TALER_Amount *coin_value,
+  const struct TALER_Amount *fee_withdraw,
+  const struct TALER_Amount *fee_deposit,
+  const struct TALER_Amount *fee_refresh,
+  const struct TALER_Amount *fee_refund,
+  const struct TALER_AuditorPrivateKeyP *auditor_priv,
+  struct TALER_AuditorSignatureP *auditor_sig)
+{
+  struct TALER_ExchangeKeyValidityPS kv = {
+    .purpose.purpose = htonl (TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS),
+    .purpose.size = htonl (sizeof (kv)),
+    .start = GNUNET_TIME_absolute_hton (stamp_start),
+    .expire_withdraw = GNUNET_TIME_absolute_hton (stamp_expire_withdraw),
+    .expire_deposit = GNUNET_TIME_absolute_hton (stamp_expire_deposit),
+    .expire_legal = GNUNET_TIME_absolute_hton (stamp_expire_legal),
+    .denom_hash = *h_denom_pub,
+    .master = *master_pub,
+  };
+
+  TALER_amount_hton (&kv.value,
+                     coin_value);
+  TALER_amount_hton (&kv.fee_withdraw,
+                     fee_withdraw);
+  TALER_amount_hton (&kv.fee_deposit,
+                     fee_deposit);
+  TALER_amount_hton (&kv.fee_refresh,
+                     fee_refresh);
+  TALER_amount_hton (&kv.fee_refund,
+                     fee_refund);
+  GNUNET_CRYPTO_hash (auditor_url,
+                      strlen (auditor_url) + 1,
+                      &kv.auditor_url_hash);
+  GNUNET_CRYPTO_eddsa_sign (&auditor_priv->eddsa_priv,
+                            &kv,
+                            &auditor_sig->eddsa_sig);
+}
+
+
+int
+TALER_auditor_denom_validity_verify (
+  const char *auditor_url,
+  const struct GNUNET_HashCode *h_denom_pub,
+  const struct TALER_MasterPublicKeyP *master_pub,
+  struct GNUNET_TIME_Absolute stamp_start,
+  struct GNUNET_TIME_Absolute stamp_expire_withdraw,
+  struct GNUNET_TIME_Absolute stamp_expire_deposit,
+  struct GNUNET_TIME_Absolute stamp_expire_legal,
+  const struct TALER_Amount *coin_value,
+  const struct TALER_Amount *fee_withdraw,
+  const struct TALER_Amount *fee_deposit,
+  const struct TALER_Amount *fee_refresh,
+  const struct TALER_Amount *fee_refund,
+  const struct TALER_AuditorPublicKeyP *auditor_pub,
+  const struct TALER_AuditorSignatureP *auditor_sig)
+{
+  struct TALER_ExchangeKeyValidityPS kv = {
+    .purpose.purpose = htonl (TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS),
+    .purpose.size = htonl (sizeof (kv)),
+    .start = GNUNET_TIME_absolute_hton (stamp_start),
+    .expire_withdraw = GNUNET_TIME_absolute_hton (stamp_expire_withdraw),
+    .expire_deposit = GNUNET_TIME_absolute_hton (stamp_expire_deposit),
+    .expire_legal = GNUNET_TIME_absolute_hton (stamp_expire_legal),
+    .denom_hash = *h_denom_pub,
+    .master = *master_pub,
+  };
+
+  TALER_amount_hton (&kv.value,
+                     coin_value);
+  TALER_amount_hton (&kv.fee_withdraw,
+                     fee_withdraw);
+  TALER_amount_hton (&kv.fee_deposit,
+                     fee_deposit);
+  TALER_amount_hton (&kv.fee_refresh,
+                     fee_refresh);
+  TALER_amount_hton (&kv.fee_refund,
+                     fee_refund);
+  GNUNET_CRYPTO_hash (auditor_url,
+                      strlen (auditor_url) + 1,
+                      &kv.auditor_url_hash);
+  return
+    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS,
+                                &kv,
+                                &auditor_sig->eddsa_sig,
+                                &auditor_pub->eddsa_pub);
+}
+
+
+/* end of auditor_signatures.c */

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]