gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated: first draft for POST /management


From: gnunet
Subject: [taler-exchange] branch master updated: first draft for POST /management/keys
Date: Fri, 27 Nov 2020 19:32:48 +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 a6f98bab first draft for POST /management/keys
a6f98bab is described below

commit a6f98bab5a1b9d05851d665782c5e8aad4701a41
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Fri Nov 27 19:32:43 2020 +0100

    first draft for POST /management/keys
---
 .../taler-exchange-httpd_management_auditors.c     | 222 ++++++++++
 ...exchange-httpd_management_auditors_AP_disable.c | 218 ++++++++++
 ...nge-httpd_management_denominations_HDP_revoke.c | 112 +++++
 .../taler-exchange-httpd_management_post_keys.c    | 457 +++++++++++++++++++++
 ...r-exchange-httpd_management_signkey_EP_revoke.c | 111 +++++
 .../taler-exchange-httpd_management_wire.c         | 259 ++++++++++++
 .../taler-exchange-httpd_management_wire_disable.c | 224 ++++++++++
 src/exchangedb/plugin_exchangedb_postgres.c        |   7 +-
 8 files changed, 1609 insertions(+), 1 deletion(-)

diff --git a/src/exchange/taler-exchange-httpd_management_auditors.c 
b/src/exchange/taler-exchange-httpd_management_auditors.c
new file mode 100644
index 00000000..a69e2788
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_management_auditors.c
@@ -0,0 +1,222 @@
+/*
+  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 Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_management_auditors.c
+ * @brief Handle request to add auditor.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler-exchange-httpd_refund.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+/**
+ * Closure for the #add_auditor transaction.
+ */
+struct AddAuditorContext
+{
+  /**
+   * Master signature to store.
+   */
+  struct TALER_MasterSignatureP master_sig;
+
+  /**
+   * Auditor public key this is about.
+   */
+  struct TALER_AuditorPublicKeyP auditor_pub;
+
+  /**
+   * Auditor URL this is about.
+   */
+  const char *auditor_url;
+
+  /**
+   * Timestamp for checking against replay attacks.
+   */
+  struct GNUNET_TIME_Absolute validity_start;
+
+};
+
+
+/**
+ * Function implementing database transaction to add an auditor.  Runs the
+ * transaction logic; IF it returns a non-error code, the transaction logic
+ * MUST NOT queue a MHD response.  IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF it
+ * returns the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls closure with a `struct AddAuditorContext`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ *             if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+add_auditor (void *cls,
+             struct MHD_Connection *connection,
+             struct TALER_EXCHANGEDB_Session *session,
+             MHD_RESULT *mhd_ret)
+{
+  struct AddAuditorContext *aac = cls;
+  struct GNUNET_TIME_Absolute last_date;
+
+  qs = TEH_plugin->lookup_auditor (TEH_plugin->cls,
+                                   session,
+                                   &aac->auditor_pub,
+                                   &last_date);
+  if (qs < 0)
+  {
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      return qs;
+    GNUNET_break (0);
+    *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_LOOKUP_FAILED,
+                                           "lookup auditor");
+    return qs;
+  }
+  if (last_date.abs_value_us > aac->start_date.abs_value_us)
+  {
+    *mhd_ret = TALER_MHD_reply_with_error (
+      connection,
+      MHD_HTTP_CONFLICT,
+      TALER_EC_EXCHANGE_AUDITOR_MORE_RECENT_PRESENT,
+      NULL);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (0 == qs)
+    qs = TEH_plugin->insert_auditor (TEH_plugin->cls,
+                                     session,
+                                     &aac->auditor_pub,
+                                     aac->auditor_url,
+                                     aac->start_date,
+                                     &aac->master_sig);
+  else
+    qs = TEH_plugin->update_auditor (TEH_plugin->cls,
+                                     session,
+                                     &aac->auditor_pub,
+                                     aac->auditor_url,
+                                     aac->start_date,
+                                     &aac->master_sig,
+                                     true);
+  if (qs < 0)
+  {
+    GNUNET_break (0);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      return qs;
+    *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_STORE_FAILED,
+                                           "add auditor");
+    return qs;
+  }
+  return qs;
+}
+
+
+/**
+ * Handle a "/management/auditors" request.
+ *
+ * @param connection the MHD connection to handle
+ * @param h_denom_pub hash of the public key of the denomination to revoke
+ * @param root uploaded JSON data
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_management_auditors (
+  struct MHD_Connection *connection,
+  const struct GNUNET_HashCode *h_denom_pub,
+  const json_t *root)
+{
+  struct AddAuditorContext aac;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("master_sig",
+                                 &aac.master_sig),
+    GNUNET_JSON_spec_fixed_auto ("auditor_pub",
+                                 &aac.auditor_pub),
+    GNUNET_JSON_spec_string ("auditor_url",
+                             &aac.auditor_url),
+    TALER_JSON_spec_absolute_time ("validity_start",
+                                   &aac.validity_start),
+    GNUNET_JSON_spec_end ()
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  {
+    enum GNUNET_GenericReturnValue res;
+
+    res = TALER_MHD_parse_json_data (connection,
+                                     root,
+                                     spec);
+    if (GNUNET_SYSERR == res)
+      return MHD_NO; /* hard failure */
+    if (GNUNET_NO == res)
+      return MHD_YES; /* failure */
+  }
+  {
+    struct TALER_MasterAddAuditorPS aa = {
+      .purpose.purpose = htonl (
+        TALER_SIGNATURE_MASTER_ADD_AUDITOR),
+      .purpose.size = htonl (sizeof (aa)),
+      .start_date = GNUNET_TIME_absolute_hton (validity_start),
+      .auditor_pub = *auditor_pub
+    };
+
+    GNUNET_CRYPTO_hash (auditor_url,
+                        strlen (auditor_url) + 1,
+                        &aa.h_auditor_url);
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_eddsa_verify (
+          TALER_SIGNATURE_MASTER_ADD_AUDITOR,
+          &aa,
+          &master_sig.eddsa_sig,
+          &TEH_master_public_key.eddsa_pub))
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_FORBIDDEN,
+        TALER_EC_EXCHANGE_AUDITOR_ADD_SIGNATURE_INVALID,
+        NULL);
+    }
+  }
+
+  qs = TEH_DB_run_transaction (connection,
+                               "add auditor",
+                               &res,
+                               &add_auditor,
+                               &aac);
+  if (qs < 0)
+    return res;
+  return TALER_MHD_reply_static (
+    connection,
+    MHD_HTTP_NO_CONTENT,
+    NULL,
+    NULL,
+    0);
+}
+
+
+/* end of taler-exchange-httpd_management_auditors.c */
diff --git a/src/exchange/taler-exchange-httpd_management_auditors_AP_disable.c 
b/src/exchange/taler-exchange-httpd_management_auditors_AP_disable.c
new file mode 100644
index 00000000..374a9203
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_management_auditors_AP_disable.c
@@ -0,0 +1,218 @@
+/*
+  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 Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_management_auditors_AP_disable.c
+ * @brief Handle request to disable auditor.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler-exchange-httpd_refund.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+/**
+ * Closure for the #del_auditor transaction.
+ */
+struct DelAuditorContext
+{
+  /**
+   * Master signature to store.
+   */
+  struct TALER_MasterSignatureP master_sig;
+
+  /**
+   * Auditor public key this is about.
+   */
+  struct TALER_AuditorPublicKeyP auditor_pub;
+
+  /**
+   * Auditor URL this is about.
+   */
+  const char *auditor_url;
+
+  /**
+   * Timestamp for checking against replay attacks.
+   */
+  struct GNUNET_TIME_Absolute validity_end;
+
+};
+
+
+/**
+ * Function implementing database transaction to del an auditor.  Runs the
+ * transaction logic; IF it returns a non-error code, the transaction logic
+ * MUST NOT queue a MHD response.  IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF it
+ * returns the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls closure with a `struct DelAuditorContext`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ *             if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+del_auditor (void *cls,
+             struct MHD_Connection *connection,
+             struct TALER_EXCHANGEDB_Session *session,
+             MHD_RESULT *mhd_ret)
+{
+  struct DelAuditorContext *dac = cls;
+  struct GNUNET_TIME_Absolute last_date;
+
+  qs = TEH_plugin->lookup_auditor (TEH_plugin->cls,
+                                   session,
+                                   &dac->auditor_pub,
+                                   &last_date);
+  if (qs < 0)
+  {
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      return qs;
+    GNUNET_break (0);
+    *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_LOOKUP_FAILED,
+                                           "lookup auditor");
+    return qs;
+  }
+  if (last_date.abs_value_us > dac->end_date.abs_value_us)
+  {
+    *mhd_ret = TALER_MHD_reply_with_error (
+      connection,
+      MHD_HTTP_CONFLICT,
+      TALER_EC_EXCHANGE_AUDITOR_MORE_RECENT_PRESENT,
+      NULL);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (0 == qs)
+  {
+    *mhd_ret = TALER_MHD_reply_with_error (
+      connection,
+      MHD_HTTP_NOT_FOUND,
+      TALER_EC_EXCHANGE_AUDITOR_NOT_FOUND,
+      NULL);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  qs = TEH_plugin->update_auditor (TEH_plugin->cls,
+                                   session,
+                                   &dac->auditor_pub,
+                                   "",
+                                   dac->end_date,
+                                   &dac->master_sig,
+                                   false);
+  if (qs < 0)
+  {
+    GNUNET_break (0);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      return qs;
+    *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_STORE_FAILED,
+                                           "del auditor");
+    return qs;
+  }
+  return qs;
+}
+
+
+/**
+ * Handle a "/management/auditors/$AUDITOR_PUB/disable" request.
+ *
+ * @param connection the MHD connection to handle
+ * @param h_denom_pub hash of the public key of the denomination to revoke
+ * @param root uploaded JSON data
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_management_auditors_AP_disable (
+  struct MHD_Connection *connection,
+  const struct GNUNET_HashCode *h_denom_pub,
+  const json_t *root)
+{
+  struct DelAuditorContext dac;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("master_sig",
+                                 &dac.master_sig),
+    GNUNET_JSON_spec_fixed_auto ("auditor_pub",
+                                 &dac.auditor_pub),
+    TALER_JSON_spec_absolute_time ("validity_end",
+                                   &dac.validity_end),
+    GNUNET_JSON_spec_end ()
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  {
+    enum GNUNET_GenericReturnValue res;
+
+    res = TALER_MHD_parse_json_data (connection,
+                                     root,
+                                     spec);
+    if (GNUNET_SYSERR == res)
+      return MHD_NO; /* hard failure */
+    if (GNUNET_NO == res)
+      return MHD_YES; /* failure */
+  }
+  {
+    struct TALER_MasterDelAuditorPS da = {
+      .purpose.purpose = htonl (
+        TALER_SIGNATURE_MASTER_DEL_AUDITOR),
+      .purpose.size = htonl (sizeof (da)),
+      .end_date = GNUNET_TIME_absolute_hton (validity_end),
+      .auditor_pub = *auditor_pub
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_eddsa_verify (
+          TALER_SIGNATURE_MASTER_DEL_AUDITOR,
+          &da,
+          &master_sig.eddsa_sig,
+          &TEH_master_public_key.eddsa_pub))
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_FORBIDDEN,
+        TALER_EC_EXCHANGE_AUDITOR_DEL_SIGNATURE_INVALID,
+        NULL);
+    }
+  }
+
+  qs = TEH_DB_run_transaction (connection,
+                               "del auditor",
+                               &res,
+                               &del_auditor,
+                               &dac);
+  if (qs < 0)
+    return res;
+  return TALER_MHD_reply_static (
+    connection,
+    MHD_HTTP_NO_CONTENT,
+    NULL,
+    NULL,
+    0);
+}
+
+
+/* end of taler-exchange-httpd_management_auditors_AP_disable.c */
diff --git 
a/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c 
b/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c
new file mode 100644
index 00000000..990dd2e3
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c
@@ -0,0 +1,112 @@
+/*
+  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 Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_management_denominations_HDP_revoke.c
+ * @brief Handle denomination revocation requests.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler-exchange-httpd_refund.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * Handle a "/management/denominations/$HDP/revoke" request.
+ *
+ * @param connection the MHD connection to handle
+ * @param h_denom_pub hash of the public key of the denomination to revoke
+ * @param root uploaded JSON data
+ * @return MHD result code
+  */
+MHD_RESULT
+TEH_handler_management_denominations_HDP_revoke (
+  struct MHD_Connection *connection,
+  const struct GNUNET_HashCode *h_denom_pub,
+  const json_t *root)
+{
+  struct TALER_MasterSignatureP master_sig;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("master_sig",
+                                 &master_sig),
+    GNUNET_JSON_spec_end ()
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  {
+    enum GNUNET_GenericReturnValue res;
+
+    res = TALER_MHD_parse_json_data (connection,
+                                     root,
+                                     spec);
+    if (GNUNET_SYSERR == res)
+      return MHD_NO; /* hard failure */
+    if (GNUNET_NO == res)
+      return MHD_YES; /* failure */
+  }
+  {
+    struct TALER_MasterDenominationKeyRevocationPS rm = {
+      .purpose.purpose = htonl (
+        TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED),
+      .purpose.size = htonl (sizeof (rm)),
+      .h_denom_pub = *h_denom_pub
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_eddsa_verify (
+          TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED,
+          &rm,
+          &master_sig.eddsa_sig,
+          &TEH_master_public_key.eddsa_pub))
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_FORBIDDEN,
+        TALER_EC_EXCHANGE_DENOMINATION_REVOKE_SIGNATURE_INVALID,
+        NULL);
+    }
+  }
+  qs = TEH_plugin->insert_denomination_revocation (TEH_plugin->cls,
+                                                   NULL,
+                                                   h_denom_pub,
+                                                   &master_sig);
+  if (qs < 0)
+  {
+    GNUNET_break (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_GENERIC_DB_STORE_FAILED,
+                                       "denomination revocation");
+  }
+  // FIXME: also update our '/keys' replies! (signal all threads!?!?)
+  return TALER_MHD_reply_static (
+    connection,
+    MHD_HTTP_NO_CONTENT,
+    NULL,
+    NULL,
+    0);
+}
+
+
+/* end of taler-exchange-httpd_management_denominations_HDP_revoke.c */
diff --git a/src/exchange/taler-exchange-httpd_management_post_keys.c 
b/src/exchange/taler-exchange-httpd_management_post_keys.c
new file mode 100644
index 00000000..9f7d5633
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_management_post_keys.c
@@ -0,0 +1,457 @@
+/*
+  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 Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_management_post_keys.c
+ * @brief Handle request to POST /management/keys
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler-exchange-httpd_refund.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * Denomination signature provided.
+ */
+struct DenomSig
+{
+  /**
+   * Hash of a denomination public key.
+   */
+  struct GNUNET_HashCode h_denom_pub;
+
+  /**
+   * Master signature for the @e h_denom_pub.
+   */
+  struct TALER_MasterSignatureP master_sig;
+
+};
+
+
+/**
+ * Signkey signature provided.
+ */
+struct SigningSig
+{
+  /**
+   * Online signing key of the exchange.
+   */
+  struct TALER_ExchangePublicKeyP exchange_pub;
+
+  /**
+   * Master signature for the @e exchange_pub.
+   */
+  struct TALER_MasterSignatureP master_sig;
+
+};
+
+
+/**
+ * Closure for the #add_keys transaction.
+ */
+struct AddKeysContext
+{
+
+  /**
+   * Array of @e nd_sigs denomination signatures.
+   */
+  struct DenomSig *d_sigs;
+
+  /**
+   * Array of @e ns_sigs signkey signatures.
+   */
+  struct SigningSig *s_sigs;
+
+  /**
+   * Length of the d_sigs array.
+   */
+  unsigned int nd_sigs;
+
+  /**
+   * Length of the n_sigs array.
+   */
+  unsigned int ns_sigs;
+
+};
+
+
+/**
+ * Function implementing database transaction to add offline signing keys.
+ * Runs the transaction logic; IF it returns a non-error code, the transaction
+ * logic MUST NOT queue a MHD response.  IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF it
+ * returns the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls closure with a `struct AddKeysContext`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ *             if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+add_keys (void *cls,
+          struct MHD_Connection *connection,
+          struct TALER_EXCHANGEDB_Session *session,
+          MHD_RESULT *mhd_ret)
+{
+  struct AddKeysContext *akc = cls;
+
+  /* activate all denomination keys */
+  for (unsigned int i = 0; i<akc->nd_sigs; i++)
+  {
+    enum GNUNET_DB_QueryStatus qs;
+    bool is_active = false;
+
+    qs = TEH_plugin->lookup_future_deomination_key (
+      TEH_plugin->cls,
+      session,
+      &akc->d_sigs[i].h_denom_pub,
+      &META);
+    if (0 == qs)
+    {
+      /* For idempotency, check if the key is already active */
+      qs = TEH_plugin->lookup_deomination_key (
+        TEH_plugin->cls,
+        session,
+        &akc->d_sigs[i].h_denom_pub,
+        &META);
+      is_active = true; /* if we pass, it's active! */
+    }
+    if (qs < 0)
+    {
+      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+        return qs;
+      GNUNET_break (0);
+      *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                             TALER_EC_GENERIC_DB_LOOKUP_FAILED,
+                                             "lookup denomination key");
+      return qs;
+    }
+    if (0 == qs)
+    {
+      *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_NOT_FOUND,
+                                             TALER_EC_GENERIC_DENOM_UNKNOWN,
+                                             GNUNET_h2s (
+                                               &aks->d_sigs[i].h_denom_pub));
+      return qs;
+    }
+
+    /* check signature is valid */
+    {
+      struct TALER_DenominationKeyValidityPS dkv = {
+        .purpose.purpose = htonl (
+          TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY),
+        .purpose.size = htonl (sizeof (dkv)),
+        .master = TEH_master_public_key,
+        .start = META.start,
+        .expire_withdraw = META.expire_withdraw,
+        .expire_deposit = META.expire_deposit,
+        .expire_legal = META.expire_legal,
+        .value = META.value,
+        .fee_withdraw = META.fee_withdraw,
+        .fee_deposit = META.fee_deposit,
+        .fee_refresh = META.fee_refresh,
+        .fee_refund = META.fee_refund,
+        .denom_hash = akc->d_sigs[i].h_denom_pub
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_CRYPTO_eddsa_verify (
+            TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY,
+            &dkv,
+            &akc->d_sigs[i].master_sig.eddsa_sig,
+            &TEH_master_public_key.eddsa_pub))
+      {
+        GNUNET_break_op (0);
+        return TALER_MHD_reply_with_error (
+          connection,
+          MHD_HTTP_FORBIDDEN,
+          TALER_EC_EXCHANGE_KEYS_ADD_SIGNATURE_INVALID,
+          GNUNET_h2s (&aks->d_sigs[i].h_denom_pub));
+      }
+    }
+    if (is_active)
+      continue; /* skip, already known */
+    qs = TEH_plugin->activate_deomination_key (
+      TEH_plugin->cls,
+      session,
+      &akc->d_sigs[i].h_denom_pub,
+      &akc->d_sigs[i].master_sig);
+    if (qs < 0)
+    {
+      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+        return qs;
+      GNUNET_break (0);
+      *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                             TALER_EC_GENERIC_DB_STORE_FAILED,
+                                             "activate denomination key");
+      return qs;
+    }
+    GNUNET_assert (0 != qs);
+  }
+
+
+  for (unsigned int i = 0; i<akc->ns_sigs; i++)
+  {
+    enum GNUNET_DB_QueryStatus qs;
+    bool is_active = false;
+
+    // FIXME: future signing keys are currently not in DB,
+    // may want to get them from in-memory instead.
+    qs = TEH_plugin->lookup_future_signing_key (
+      TEH_plugin->cls,
+      session,
+      &akc->s_sigs[i].exchange_pub,
+      &META);
+    if (0 == qs)
+    {
+      /* For idempotency, check if the key is already active */
+      qs = TEH_plugin->lookup_signing_key (
+        TEH_plugin->cls,
+        session,
+        &akc->s_sigs[i].exchange_pub,
+        &META);
+      is_active = true; /* if we pass, it's active! */
+    }
+    if (qs < 0)
+    {
+      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+        return qs;
+      GNUNET_break (0);
+      *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                             TALER_EC_GENERIC_DB_LOOKUP_FAILED,
+                                             "lookup signing key");
+      return qs;
+    }
+    if (0 == qs)
+    {
+      *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_NOT_FOUND,
+                                             TALER_EC_GENERIC_SIGNKEY_UNKNOWN,
+                                             TALER_B2S (
+                                               &aks->s_sigs[i].exchange_pub));
+      return qs;
+    }
+
+    /* check signature is valid */
+    {
+      struct TALER_ExchangeSigningKeyValidityPS skv = {
+        .purpose.purpose = htonl (
+          TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY),
+        .purpose.size = htonl (sizeof (dkv)),
+        .master_public_key = TEH_master_public_key,
+        .start = x,
+        .expire = y,
+        .end = z,
+        .signkey_pub = akc->d_sigs[i].exchange_pub
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_CRYPTO_eddsa_verify (
+            TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY,
+            &skv,
+            &akc->s_sigs[i].master_sig.eddsa_sig,
+            &TEH_master_public_key.eddsa_pub))
+      {
+        GNUNET_break_op (0);
+        return TALER_MHD_reply_with_error (
+          connection,
+          MHD_HTTP_FORBIDDEN,
+          TALER_EC_EXCHANGE_KEYS_ADD_SIGNATURE_INVALID,
+          GNUNET_h2s (&aks->d_sigs[i].h_denom_pub));
+      }
+    }
+    if (is_active)
+      continue; /* skip, already known */
+    qs = TEH_plugin->activate_signing_key (
+      TEH_plugin->cls,
+      session,
+      &akc->s_sigs[i].exchange_pub,
+      &akc->s_sigs[i].master_sig);
+    if (qs < 0)
+    {
+      if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+        return qs;
+      GNUNET_break (0);
+      *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                             TALER_EC_GENERIC_DB_STORE_FAILED,
+                                             "activate signing key");
+      return qs;
+    }
+    GNUNET_assert (0 != qs);
+  }
+
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, 
matters here */
+}
+
+
+/**
+ * Handle a POST "/management/keys" request.
+ *
+ * @param connection the MHD connection to handle
+ * @param h_denom_pub hash of the public key of the denomination to revoke
+ * @param root uploaded JSON data
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_management_post_keys (
+  struct MHD_Connection *connection,
+  const struct GNUNET_HashCode *h_denom_pub,
+  const json_t *root)
+{
+  struct AddKeysContext akc;
+  json_t *denom_sigs;
+  json_t *signkey_sigs;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_json ("denom_sigs",
+                           &denom_sigs),
+    GNUNET_JSON_spec_json ("signkey_sigs",
+                           &signkey_sigs),
+    GNUNET_JSON_spec_end ()
+  };
+  enum GNUNET_DB_QueryStatus qs;
+  bool ok;
+  MHD_RESULT ret;
+
+  {
+    enum GNUNET_GenericReturnValue res;
+
+    res = TALER_MHD_parse_json_data (connection,
+                                     root,
+                                     spec);
+    if (GNUNET_SYSERR == res)
+      return MHD_NO; /* hard failure */
+    if (GNUNET_NO == res)
+      return MHD_YES; /* failure */
+  }
+  if (! (json_is_array (denom_sigs) &&
+         json_is_array (signkey_sigs)) )
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (
+      connection,
+      MHD_HTTP_BAD_REQUEST,
+      TALER_EC_XXX,
+      "array expected for denom_sigs and signkey_sigs");
+  }
+  akc.nd_sigs = json_array_size (denom_sigs);
+  akc.d_sigs = GNUNET_new_array (akc.nd_sigs,
+                                 struct DenomSig);
+  ok = true;
+  for (unsigned int i = 0; i<akc.nd_sigs; i++)
+  {
+    struct DenomSig *d = &akc.d_sigs[i];
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_fixed_auto ("master_sig",
+                                   &d->master_sig),
+      GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
+                                   &d->h_denom_pub),
+      GNUNET_JSON_spec_end ()
+    };
+    enum GNUNET_GenericReturnValue res;
+
+    res = TALER_MHD_parse_json_data (connection,
+                                     root,
+                                     json_array_get (denom_sigs,
+                                                     i));
+    if (GNUNET_SYSERR == res)
+    {
+      ret = MHD_NO; /* hard failure */
+      ok = false;
+      break;
+    }
+    if (GNUNET_NO == res)
+    {
+      ret = MHD_YES;
+      ok = false;
+      break;
+    }
+  }
+  if (! ok)
+  {
+    GNUNET_free (akc.d_sigs);
+    return ret;
+  }
+  akc.ns_sigs = json_array_size (signkey_sigs);
+  akc.s_sigs = GNUNET_new_array (akc.nd_sigs,
+                                 struct SigningSig);
+  for (unsigned int i = 0; i<akc.nd_sigs; i++)
+  {
+    struct SigningSig *s = &akc.s_sigs[i];
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_fixed_auto ("master_sig",
+                                   &s->master_sig),
+      GNUNET_JSON_spec_fixed_auto ("exchange_pub",
+                                   &s->exchange_pub),
+      GNUNET_JSON_spec_end ()
+    };
+    enum GNUNET_GenericReturnValue res;
+
+    res = TALER_MHD_parse_json_data (connection,
+                                     root,
+                                     json_array_get (signkey_sigs,
+                                                     i));
+    if (GNUNET_SYSERR == res)
+    {
+      ret = MHD_NO; /* hard failure */
+      ok = false;
+      break;
+    }
+    if (GNUNET_NO == res)
+    {
+      ret = MHD_YES;
+      ok = false;
+      break;
+    }
+  }
+  if (! ok)
+  {
+    GNUNET_free (akc.d_sigs);
+    GNUNET_free (akc.s_sigs);
+    return ret;
+  }
+  qs = TEH_DB_run_transaction (connection,
+                               "add keys",
+                               &res,
+                               &add_keys,
+                               &akc);
+  if (qs < 0)
+    return res;
+  return TALER_MHD_reply_static (
+    connection,
+    MHD_HTTP_NO_CONTENT,
+    NULL,
+    NULL,
+    0);
+}
+
+
+/* end of taler-exchange-httpd_management_management_post_keys.c */
diff --git a/src/exchange/taler-exchange-httpd_management_signkey_EP_revoke.c 
b/src/exchange/taler-exchange-httpd_management_signkey_EP_revoke.c
new file mode 100644
index 00000000..4fc190b3
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_management_signkey_EP_revoke.c
@@ -0,0 +1,111 @@
+/*
+  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 Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_management_signkey_EP_revoke.c
+ * @brief Handle exchange online signing key revocation requests.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * Handle a "/management/signkeys/$EP/revoke" request.
+ *
+ * @param connection the MHD connection to handle
+ * @param exchange_pub exchange online signing public key to revoke
+ * @param root uploaded JSON data
+ * @return MHD result code
+  */
+MHD_RESULT
+TEH_handler_management_signkeys_EP_revoke (
+  struct MHD_Connection *connection,
+  const struct TALER_ExchangePublicKeyP *exchange_pub,
+  const json_t *root)
+{
+  struct TALER_MasterSignatureP master_sig;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("master_sig",
+                                 &master_sig),
+    GNUNET_JSON_spec_end ()
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  {
+    enum GNUNET_GenericReturnValue res;
+
+    res = TALER_MHD_parse_json_data (connection,
+                                     root,
+                                     spec);
+    if (GNUNET_SYSERR == res)
+      return MHD_NO; /* hard failure */
+    if (GNUNET_NO == res)
+      return MHD_YES; /* failure */
+  }
+  {
+    struct TALER_MasterDenominationKeyRevocationPS rm = {
+      .purpose.purpose = htonl (
+        TALER_SIGNATURE_MASTER_SIGNING_KEY_REVOKED),
+      .purpose.size = htonl (sizeof (rm)),
+      .exchange_pub = *exchange_pub
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_eddsa_verify (
+          TALER_SIGNATURE_MASTER_SIGNING_KEY_REVOKED,
+          &rm,
+          &master_sig.eddsa_sig,
+          &TEH_master_public_key.eddsa_pub))
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_FORBIDDEN,
+        TALER_EC_EXCHANGE_SIGNKEY_REVOKE_SIGNATURE_INVALID,
+        NULL);
+    }
+  }
+  qs = TEH_plugin->insert_signkey_revocation (TEH_plugin->cls,
+                                              NULL,
+                                              exchange_pub,
+                                              &master_sig);
+  if (qs < 0)
+  {
+    GNUNET_break (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_GENERIC_DB_STORE_FAILED,
+                                       "signkey revocation");
+  }
+  // FIXME: also update our '/keys' replies! (signal all threads!?!?)
+  return TALER_MHD_reply_static (
+    connection,
+    MHD_HTTP_NO_CONTENT,
+    NULL,
+    NULL,
+    0);
+}
+
+
+/* end of taler-exchange-httpd_management_signkey_HDP_revoke.c */
diff --git a/src/exchange/taler-exchange-httpd_management_wire.c 
b/src/exchange/taler-exchange-httpd_management_wire.c
new file mode 100644
index 00000000..8a13d6cf
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_management_wire.c
@@ -0,0 +1,259 @@
+/*
+  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 Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_management_wire.c
+ * @brief Handle request to add wire account.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler-exchange-httpd_refund.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+/**
+ * Closure for the #add_wire transaction.
+ */
+struct AddWireContext
+{
+  /**
+   * Master signature affirming the WIRE ADD operation
+   * (includes timestamp).
+   */
+  struct TALER_MasterSignatureP master_sig_add;
+
+  /**
+   * Master signature to share with clients affirming the
+   * wire details of the bank.
+   */
+  struct TALER_MasterSignatureP master_sig_wire;
+
+  /**
+   * Payto:// URI this is about.
+   */
+  const char *payto_url;
+
+  /**
+   * Timestamp for checking against replay attacks.
+   */
+  struct GNUNET_TIME_Absolute validity_start;
+
+};
+
+
+/**
+ * Function implementing database transaction to add an wire.  Runs the
+ * transaction logic; IF it returns a non-error code, the transaction logic
+ * MUST NOT queue a MHD response.  IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF it
+ * returns the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls closure with a `struct AddWireContext`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ *             if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+add_wire (void *cls,
+          struct MHD_Connection *connection,
+          struct TALER_EXCHANGEDB_Session *session,
+          MHD_RESULT *mhd_ret)
+{
+  struct AddWireContext *awc = cls;
+  struct GNUNET_TIME_Absolute last_date;
+
+  qs = TEH_plugin->lookup_wire (TEH_plugin->cls,
+                                session,
+                                awc->payto_uri,
+                                &last_date);
+  if (qs < 0)
+  {
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      return qs;
+    GNUNET_break (0);
+    *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_LOOKUP_FAILED,
+                                           "lookup wire");
+    return qs;
+  }
+  if (last_date.abs_value_us > awc->start_date.abs_value_us)
+  {
+    *mhd_ret = TALER_MHD_reply_with_error (
+      connection,
+      MHD_HTTP_CONFLICT,
+      TALER_EC_EXCHANGE_WIRE_MORE_RECENT_PRESENT,
+      NULL);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (0 == qs)
+    qs = TEH_plugin->insert_wire (TEH_plugin->cls,
+                                  session,
+                                  &awc->payto_uri,
+                                  awc->start_date,
+                                  &awc->master_sig_add);
+  else
+    qs = TEH_plugin->update_wire (TEH_plugin->cls,
+                                  session,
+                                  &awc->payto_uri,
+                                  awc->start_date,
+                                  &awc->master_sig_add,
+                                  true);
+  if (qs < 0)
+  {
+    GNUNET_break (0);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      return qs;
+    *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_STORE_FAILED,
+                                           "add wire");
+    return qs;
+  }
+  qs = TEH_plugin->insert_wire_details (TEH_plugin->cls,
+                                        session,
+                                        &awc->payto_uri,
+                                        &awc->master_sig_wire);
+  if (qs < 0)
+  {
+    GNUNET_break (0);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      return qs;
+    *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_STORE_FAILED,
+                                           "add wire details");
+    return qs;
+  }
+  return qs;
+}
+
+
+/**
+ * Handle a "/management/wire" request.
+ *
+ * @param connection the MHD connection to handle
+ * @param root uploaded JSON data
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_management_denominations_wire (
+  struct MHD_Connection *connection,
+  const json_t *root)
+{
+  struct AddWireContext awc;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("master_sig_wire",
+                                 &awc.master_sig_wire),
+    GNUNET_JSON_spec_fixed_auto ("master_sig_add",
+                                 &awc.master_sig_add),
+    GNUNET_JSON_spec_string ("payto_uri",
+                             &awc.payto_uri),
+    TALER_JSON_spec_absolute_time ("validity_start",
+                                   &awc.validity_start),
+    GNUNET_JSON_spec_end ()
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  {
+    enum GNUNET_GenericReturnValue res;
+
+    res = TALER_MHD_parse_json_data (connection,
+                                     root,
+                                     spec);
+    if (GNUNET_SYSERR == res)
+      return MHD_NO; /* hard failure */
+    if (GNUNET_NO == res)
+      return MHD_YES; /* failure */
+  }
+  {
+    struct TALER_MasterAddWirePS aw = {
+      .purpose.purpose = htonl (
+        TALER_SIGNATURE_MASTER_ADD_WIRE),
+      .purpose.size = htonl (sizeof (aw)),
+      .start_date = GNUNET_TIME_absolute_hton (validity_start),
+    };
+
+    GNUNET_CRYPTO_hash (awc.payto_uri,
+                        strlen (awc.payto_uri) + 1,
+                        &aw.h_wire);
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_eddsa_verify (
+          TALER_SIGNATURE_MASTER_ADD_WIRE,
+          &aw,
+          &master_sig.eddsa_sig,
+          &TEH_master_public_key.eddsa_pub))
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_FORBIDDEN,
+        TALER_EC_EXCHANGE_WIRE_ADD_SIGNATURE_INVALID,
+        NULL);
+    }
+  }
+  {
+    struct TALER_MasterWireDetailsPS wd = {
+      .purpose.purpose = htonl (
+        TALER_SIGNATURE_MASTER_ADD_WIRE),
+      .purpose.size = htonl (sizeof (wd)),
+    };
+
+    GNUNET_CRYPTO_hash (awc.payto_uri,
+                        strlen (awc.payto_uri) + 1,
+                        &wd.h_wire);
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_eddsa_verify (
+          TALER_SIGNATURE_MASTER_WIRE_DETAILS,
+          &wd,
+          &master_sig.eddsa_sig,
+          &TEH_master_public_key.eddsa_pub))
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_FORBIDDEN,
+        TALER_EC_EXCHANGE_WIRE_DETALS_SIGNATURE_INVALID,
+        NULL);
+    }
+  }
+
+  qs = TEH_DB_run_transaction (connection,
+                               "add wire",
+                               &res,
+                               &add_wire,
+                               &awc);
+  if (qs < 0)
+    return res;
+  return TALER_MHD_reply_static (
+    connection,
+    MHD_HTTP_NO_CONTENT,
+    NULL,
+    NULL,
+    0);
+}
+
+
+/* end of taler-exchange-httpd_management_wire.c */
diff --git a/src/exchange/taler-exchange-httpd_management_wire_disable.c 
b/src/exchange/taler-exchange-httpd_management_wire_disable.c
new file mode 100644
index 00000000..af6b2e56
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_management_wire_disable.c
@@ -0,0 +1,224 @@
+/*
+  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 Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_management_wire_disable.c
+ * @brief Handle request to disable wire account.
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler-exchange-httpd_refund.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+/**
+ * Closure for the #del_wire transaction.
+ */
+struct DelWireContext
+{
+  /**
+   * Master signature affirming the WIRE DEL operation
+   * (includes timestamp).
+   */
+  struct TALER_MasterSignatureP master_sig;
+
+  /**
+   * Payto:// URI this is about.
+   */
+  const char *payto_url;
+
+  /**
+   * Timestamp for checking against replay attacks.
+   */
+  struct GNUNET_TIME_Absolute validity_start;
+
+};
+
+
+/**
+ * Function implementing database transaction to del an wire.  Runs the
+ * transaction logic; IF it returns a non-error code, the transaction logic
+ * MUST NOT queue a MHD response.  IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF it
+ * returns the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls closure with a `struct DelWireContext`
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ *             if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+del_wire (void *cls,
+          struct MHD_Connection *connection,
+          struct TALER_EXCHANGEDB_Session *session,
+          MHD_RESULT *mhd_ret)
+{
+  struct DelWireContext *awc = cls;
+  struct GNUNET_TIME_Absolute last_date;
+
+  qs = TEH_plugin->lookup_wire (TEH_plugin->cls,
+                                session,
+                                awc->payto_uri,
+                                &last_date);
+  if (qs < 0)
+  {
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      return qs;
+    GNUNET_break (0);
+    *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_LOOKUP_FAILED,
+                                           "lookup wire");
+    return qs;
+  }
+  if (last_date.abs_value_us > awc->start_date.abs_value_us)
+  {
+    *mhd_ret = TALER_MHD_reply_with_error (
+      connection,
+      MHD_HTTP_CONFLICT,
+      TALER_EC_EXCHANGE_WIRE_MORE_RECENT_PRESENT,
+      NULL);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (0 == qs)
+    qs = TEH_plugin->insert_wire (TEH_plugin->cls,
+                                  session,
+                                  &awc->payto_uri,
+                                  awc->end_date,
+                                  &awc->master_sig_del);
+  else
+    qs = TEH_plugin->update_wire (TEH_plugin->cls,
+                                  session,
+                                  &awc->payto_uri,
+                                  awc->end_date,
+                                  &awc->master_sig_del,
+                                  false);
+  if (qs < 0)
+  {
+    GNUNET_break (0);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      return qs;
+    *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_STORE_FAILED,
+                                           "del wire");
+    return qs;
+  }
+  qs = TEH_plugin->delete_wire_details (TEH_plugin->cls,
+                                        session,
+                                        &awc->payto_uri);
+  if (qs < 0)
+  {
+    GNUNET_break (0);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      return qs;
+    *mhd_ret = TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_STORE_FAILED,
+                                           "del wire details");
+    return qs;
+  }
+  return qs;
+}
+
+
+/**
+ * Handle a "/management/wire" request.
+ *
+ * @param connection the MHD connection to handle
+ * @param root uploaded JSON data
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_management_denominations_wire_disable (
+  struct MHD_Connection *connection,
+  const json_t *root)
+{
+  struct DelWireContext awc;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("master_sig",
+                                 &awc.master_sig),
+    GNUNET_JSON_spec_string ("payto_uri",
+                             &awc.payto_uri),
+    TALER_JSON_spec_absolute_time ("validity_end",
+                                   &awc.validity_end),
+    GNUNET_JSON_spec_end ()
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  {
+    enum GNUNET_GenericReturnValue res;
+
+    res = TALER_MHD_parse_json_data (connection,
+                                     root,
+                                     spec);
+    if (GNUNET_SYSERR == res)
+      return MHD_NO; /* hard failure */
+    if (GNUNET_NO == res)
+      return MHD_YES; /* failure */
+  }
+  {
+    struct TALER_MasterDelWirePS aw = {
+      .purpose.purpose = htonl (
+        TALER_SIGNATURE_MASTER_DEL_WIRE),
+      .purpose.size = htonl (sizeof (aw)),
+      .end_date = GNUNET_TIME_absolute_hton (validity_end),
+    };
+
+    GNUNET_CRYPTO_hash (awc.payto_uri,
+                        strlen (awc.payto_uri) + 1,
+                        &aw.h_wire);
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_eddsa_verify (
+          TALER_SIGNATURE_MASTER_DEL_WIRE,
+          &aw,
+          &master_sig.eddsa_sig,
+          &TEH_master_public_key.eddsa_pub))
+    {
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_FORBIDDEN,
+        TALER_EC_EXCHANGE_WIRE_DEL_SIGNATURE_INVALID,
+        NULL);
+    }
+  }
+  qs = TEH_DB_run_transaction (connection,
+                               "del wire",
+                               &res,
+                               &del_wire,
+                               &awc);
+  if (qs < 0)
+    return res;
+  return TALER_MHD_reply_static (
+    connection,
+    MHD_HTTP_NO_CONTENT,
+    NULL,
+    NULL,
+    0);
+}
+
+
+/* end of taler-exchange-httpd_management_wire_disable.c */
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index f94dd739..74d4f92a 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -7109,13 +7109,18 @@ postgres_insert_denomination_revocation (
   const struct GNUNET_HashCode *denom_pub_hash,
   const struct TALER_MasterSignatureP *master_sig)
 {
+  struct PostgresClosure *pc = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
     GNUNET_PQ_query_param_auto_from_type (master_sig),
     GNUNET_PQ_query_param_end
   };
 
-  (void) cls;
+  if (NULL == session)
+    session = postgres_get_session (pc);
+  if (NULL == session)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+
   return GNUNET_PQ_eval_prepared_non_select (session->conn,
                                              "denomination_revocation_insert",
                                              params);

-- 
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]