gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated: more work on crypto RSA helper


From: gnunet
Subject: [taler-exchange] branch master updated: more work on crypto RSA helper
Date: Sat, 14 Nov 2020 22:27:53 +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 4f0e38ab more work on crypto RSA helper
4f0e38ab is described below

commit 4f0e38ab12340cea7297fc661c98ddba2d2ef681
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sat Nov 14 22:27:50 2020 +0100

    more work on crypto RSA helper
---
 ChangeLog                                   |   7 +
 src/exchange/taler-exchange-transfer.c      |  23 +-
 src/exchangedb/Makefile.am                  |   4 +-
 src/exchangedb/drop0002.sql                 |  26 +
 src/exchangedb/exchange-0002.sql            |  48 ++
 src/exchangedb/plugin_exchangedb_postgres.c |  48 +-
 src/include/taler_exchangedb_plugin.h       |  14 +
 src/util/taler-helper-crypto-rsa.c          | 999 ++++++++++++++++++++++------
 src/util/taler-helper-crypto-rsa.h          |  90 +++
 9 files changed, 1043 insertions(+), 216 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 7c64dae5..d1836d95 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+Sat 14 Nov 2020 05:47:30 PM CET
+    Modify taler-exchange-transfer to continue even after a
+    wire transfer failed due to the bank refusing it because
+    the target account does not exist. Changed the database
+    to track such failures in the respective table.
+    Opens new issue #6647.  -CG
+
 Tue 10 Nov 2020 01:03:22 PM CET
     Updates to error codes and HTTP status codes for improved
     consistency. Fixed spelling issues. Ensure main() returns
diff --git a/src/exchange/taler-exchange-transfer.c 
b/src/exchange/taler-exchange-transfer.c
index d21f9ccf..9c350220 100644
--- a/src/exchange/taler-exchange-transfer.c
+++ b/src/exchange/taler-exchange-transfer.c
@@ -264,8 +264,26 @@ wire_confirm_cb (void *cls,
   (void) row_id;
   (void) wire_timestamp;
   wpd->eh = NULL;
-  if (MHD_HTTP_OK != http_status_code)
+  switch (http_status_code)
   {
+  case MHD_HTTP_OK:
+    qs = db_plugin->wire_prepare_data_mark_finished (db_plugin->cls,
+                                                     session,
+                                                     wpd->row_id);
+    /* continued below */
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Wire transaction %llu failed: %u/%d\n",
+                (unsigned long long) wpd->row_id,
+                http_status_code,
+                ec);
+    qs = db_plugin->wire_prepare_data_mark_failed (db_plugin->cls,
+                                                   session,
+                                                   wpd->row_id);
+    /* continued below */
+    break;
+  default:
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Wire transaction failed: %u/%d\n",
                 http_status_code,
@@ -278,9 +296,6 @@ wire_confirm_cb (void *cls,
     wpd = NULL;
     return;
   }
-  qs = db_plugin->wire_prepare_data_mark_finished (db_plugin->cls,
-                                                   session,
-                                                   wpd->row_id);
   if (0 >= qs)
   {
     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index 74512636..6736ce37 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -17,7 +17,9 @@ sqldir = $(prefix)/share/taler/sql/exchange/
 sql_DATA = \
   exchange-0000.sql \
   exchange-0001.sql \
-  drop0001.sql
+  exchange-0002.sql \
+  drop0001.sql \
+  drop0002.sql
 
 EXTRA_DIST = \
   exchangedb.conf \
diff --git a/src/exchangedb/drop0002.sql b/src/exchangedb/drop0002.sql
new file mode 100644
index 00000000..224c7f50
--- /dev/null
+++ b/src/exchangedb/drop0002.sql
@@ -0,0 +1,26 @@
+--
+-- 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/>
+--
+
+-- Everything in one big transaction
+BEGIN;
+
+-- exchange-0002 did not create new tables, so nothing to do here.
+
+-- Unregister patch (0002.sql)
+SELECT _v.unregister_patch('exchange-0002');
+
+-- And we're out of here...
+COMMIT;
diff --git a/src/exchangedb/exchange-0002.sql b/src/exchangedb/exchange-0002.sql
new file mode 100644
index 00000000..9a2793f1
--- /dev/null
+++ b/src/exchangedb/exchange-0002.sql
@@ -0,0 +1,48 @@
+--
+-- 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/>
+--
+
+-- Everything in one big transaction
+BEGIN;
+
+-- Check patch versioning is in place.
+SELECT _v.register_patch('exchange-0002', NULL, NULL);
+
+ALTER TABLE prewire
+  ADD failed BOOLEAN NOT NULL DEFAULT false;
+
+COMMENT ON COLUMN prewire.failed
+  IS 'set to TRUE if the bank responded with a non-transient failure to our 
transfer request';
+COMMENT ON COLUMN prewire.finished
+  IS 'set to TRUE once bank confirmed receiving the wire transfer request';
+COMMENT ON COLUMN prewire.buf
+  IS 'serialized data to send to the bank to execute the wire transfer';
+
+-- change comment, existing index is still useful, but only for gc_prewire.
+COMMENT ON INDEX prepare_iteration_index
+  IS 'for gc_prewire';
+
+-- need a new index for updated wire_prepare_data_get statement:
+CREATE INDEX IF NOT EXISTS prepare_get_index
+  ON prewire
+  (failed,finished);
+COMMENT ON INDEX prepare_get_index
+  IS 'for wire_prepare_data_get';
+
+
+
+
+-- Complete transaction
+COMMIT;
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 47b74139..f94dd739 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -1098,7 +1098,13 @@ postgres_get_session (void *cls)
       /* Used in #postgres_wire_prepare_data_mark_finished() */
       GNUNET_PQ_make_prepare ("wire_prepare_data_mark_done",
                               "UPDATE prewire"
-                              " SET finished=true"
+                              " SET finished=TRUE"
+                              " WHERE prewire_uuid=$1;",
+                              1),
+      /* Used in #postgres_wire_prepare_data_mark_failed() */
+      GNUNET_PQ_make_prepare ("wire_prepare_data_mark_failed",
+                              "UPDATE prewire"
+                              " SET failed=TRUE"
                               " WHERE prewire_uuid=$1;",
                               1),
       /* Used in #postgres_wire_prepare_data_get() */
@@ -1108,11 +1114,11 @@ postgres_get_session (void *cls)
                               ",type"
                               ",buf"
                               " FROM prewire"
-                              " WHERE finished=false"
+                              " WHERE finished=FALSE"
+                              "   AND failed=FALSE"
                               " ORDER BY prewire_uuid ASC"
                               " LIMIT 1;",
                               0),
-
       /* Used in #postgres_select_deposits_missing_wire */
       GNUNET_PQ_make_prepare ("deposits_get_overdue",
                               "SELECT"
@@ -5224,10 +5230,10 @@ postgres_wire_prepare_data_insert (void *cls,
  * @return transaction status code
  */
 static enum GNUNET_DB_QueryStatus
-postgres_wire_prepare_data_mark_finished (void *cls,
-                                          struct TALER_EXCHANGEDB_Session *
-                                          session,
-                                          uint64_t rowid)
+postgres_wire_prepare_data_mark_finished (
+  void *cls,
+  struct TALER_EXCHANGEDB_Session *session,
+  uint64_t rowid)
 {
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&rowid),
@@ -5241,6 +5247,32 @@ postgres_wire_prepare_data_mark_finished (void *cls,
 }
 
 
+/**
+ * Function called to mark wire transfer commit data as failed.
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param rowid which entry to mark as failed
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_wire_prepare_data_mark_failed (
+  void *cls,
+  struct TALER_EXCHANGEDB_Session *session,
+  uint64_t rowid)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_uint64 (&rowid),
+    GNUNET_PQ_query_param_end
+  };
+
+  (void) cls;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                             "wire_prepare_data_mark_failed",
+                                             params);
+}
+
+
 /**
  * Function called to get an unfinished wire transfer
  * preparation data. Fetches at most one item.
@@ -7379,6 +7411,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
   plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert;
   plugin->wire_prepare_data_mark_finished =
     &postgres_wire_prepare_data_mark_finished;
+  plugin->wire_prepare_data_mark_failed =
+    &postgres_wire_prepare_data_mark_failed;
   plugin->wire_prepare_data_get = &postgres_wire_prepare_data_get;
   plugin->start_deferred_wire_out = &postgres_start_deferred_wire_out;
   plugin->store_wire_transfer_out = &postgres_store_wire_transfer_out;
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 4f27daef..e2abb8a6 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -2429,6 +2429,20 @@ struct TALER_EXCHANGEDB_Plugin
                                      uint64_t rowid);
 
 
+  /**
+   * Function called to mark wire transfer as failed.
+   *
+   * @param cls closure
+   * @param session database connection
+   * @param rowid which entry to mark as failed
+   * @return transaction status code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*wire_prepare_data_mark_failed)(void *cls,
+                                   struct TALER_EXCHANGEDB_Session *session,
+                                   uint64_t rowid);
+
+
   /**
    * Function called to get an unfinished wire transfer
    * preparation data. Fetches at most one item.
diff --git a/src/util/taler-helper-crypto-rsa.c 
b/src/util/taler-helper-crypto-rsa.c
index 3aadd056..33d2ee72 100644
--- a/src/util/taler-helper-crypto-rsa.c
+++ b/src/util/taler-helper-crypto-rsa.c
@@ -17,9 +17,33 @@
  * @file util/taler-helper-crypto-rsa.c
  * @brief Standalone process to perform private key RSA operations
  * @author Christian Grothoff
+ *
+ * NOTES:
+ * - Option 'DURATION_OVERLAP' renamed to 'OVERLAP_DURATION' for consistency;
+ *   => need to update in deployment scripts and default configuration!
+ * - option 'KEYDIR' moved from section 'exchange' to 
'taler-helper-crypto-rsa'!
+ *
+ * Key design points:
+ * - EVERY thread of the exchange will have its own pair of connections to the
+ *   crypto helpers.  This way, every threat will also have its own /keys state
+ *   and avoid the need to synchronize on those.
+ * - auditor signatures and master signatures are to be kept in the exchange 
DB,
+ *   and merged with the public keys of the helper by the exchange HTTPD!
+ * - the main loop of the helper is SINGLE-THREADED, but there are
+ *   threads for crypto-workers which (only) do the signing in parallel,
+ *   working of a work-queue.
+ * - thread-safety: signing happens in parallel, thus when REMOVING private 
keys,
+ *   we must ensure that all signers are done before we fully free() the
+ *   private key. This is done by reference counting (as work is always
+ *   assigned and collected by the main thread).
+ *
+ * TODO:
+ * - actual networking
+ * - actual signing
  */
 #include "platform.h"
 #include "taler_util.h"
+#include "taler-helper-crypto-rsa.h"
 #include <gcrypt.h>
 
 
@@ -36,12 +60,12 @@ struct DenominationKey
 {
 
   /**
-   * Kept in a DLL of the respective denomination.
+   * Kept in a DLL of the respective denomination. Sorted by anchor time.
    */
   struct DenominationKey *next;
 
   /**
-   * Kept in a DLL of the respective denomination.
+   * Kept in a DLL of the respective denomination. Sorted by anchor time.
    */
   struct DenominationKey *prev;
 
@@ -51,18 +75,44 @@ struct DenominationKey
   struct Denomination *denom;
 
   /**
-   * Denomination key details.  Note that the "dki.issue.signature"
-   * IS ALWAYS uninitialized (all zeros).  The private key is in
-   * 'dki.denom_priv.rsa_private_key' and must be free'd explicitly
-   * (as it is a pointer to a variable-size data structure).
+   * Name of the file this key is stored under.
+   */
+  char *filename;
+
+  /**
+   * The private key of the denomination.  Will be NULL if the private
+   * key is not available (this is the case after the key has expired
+   * for signing coins, but is still valid for depositing coins).
+   */
+  struct TALER_DenominationPrivateKey denom_priv;
+
+  /**
+   * The public key of the denomination.
    */
-  struct TALER_EXCHANGEDB_DenominationKey dki;
+  struct TALER_DenominationPublicKey denom_pub;
+
+  /**
+   * Hash of this denomination's public key.
+   */
+  struct GNUNET_HashCode h_pub;
 
   /**
    * Time at which this key is supposed to become valid.
    */
   struct GNUNET_TIME_Absolute anchor;
 
+  /**
+   * Reference counter. Counts the number of threads that are
+   * using this key at this time.
+   */
+  unsigned int rc;
+
+  /**
+   * Flag set to true if this key has been purged and the memory
+   * must be freed as soon as @e rc hits zero.
+   */
+  bool purge;
+
 };
 
 
@@ -70,12 +120,12 @@ struct Denomination
 {
 
   /**
-   * Kept in a DLL. Sorted?
+   * Kept in a DLL. Sorted by #denomination_action_time().
    */
   struct Denomination *next;
 
   /**
-   * Kept in a DLL. Sorted?
+   * Kept in a DLL. Sorted by #denomination_action_time().
    */
   struct Denomination *prev;
 
@@ -89,18 +139,6 @@ struct Denomination
    */
   struct DenominationKey *keys_tail;
 
-  /**
-   * How long are the signatures legally valid?  Should be
-   * significantly larger than @e duration_spend (i.e. years).
-   */
-  struct GNUNET_TIME_Relative duration_legal;
-
-  /**
-   * How long can the coins be spend?  Should be significantly
-   * larger than @e duration_withdraw (i.e. years).
-   */
-  struct GNUNET_TIME_Relative duration_spend;
-
   /**
    * How long can coins be withdrawn (generated)?  Should be small
    * enough to limit how many coins will be signed into existence with
@@ -110,34 +148,44 @@ struct Denomination
   struct GNUNET_TIME_Relative duration_withdraw;
 
   /**
-   * What is the value of each coin?
+   * What is the configuration section of this denomination type?  Also used
+   * for the directory name where the denomination keys are stored.
    */
-  struct TALER_Amount value;
+  char *section;
 
   /**
-   * What is the fee charged for withdrawal?
+   * Length of (new) RSA keys (in bits).
    */
-  struct TALER_Amount fee_withdraw;
+  uint32_t rsa_keysize;
+};
+
+
+/**
+ * Information we keep for a client connected to us.
+ */
+struct Client
+{
 
   /**
-   * What is the fee charged for deposits?
+   * Kept in a DLL.
    */
-  struct TALER_Amount fee_deposit;
+  struct Client *next;
 
   /**
-   * What is the fee charged for melting?
+   * Kept in a DLL.
    */
-  struct TALER_Amount fee_refresh;
+  struct Client *prev;
 
   /**
-   * What is the fee charged for refunds?
+   * Client socket.
    */
-  struct TALER_Amount fee_refund;
+  struct GNUNET_NETWORK_Handle *sock;
 
   /**
-   * Length of (new) RSA keys (in bits).
+   * Client task to read from @e sock. NULL if we are working.
    */
-  uint32_t rsa_keysize;
+  struct GNUNET_SCHEDULER_Task *task;
+
 };
 
 
@@ -164,9 +212,9 @@ static struct GNUNET_TIME_Absolute now_tmp;
 static const struct GNUNET_CONFIGURATION_Handle *kcfg;
 
 /**
- * The configured currency.
+ * Where do we store the keys?
  */
-static char *currency;
+static const char *keydir;
 
 /**
  * How much should coin creation (@e duration_withdraw) duration overlap
@@ -175,21 +223,11 @@ static char *currency;
  */
 static struct GNUNET_TIME_Relative overlap_duration;
 
-/**
- * How long should keys be legally valid?
- */
-static struct GNUNET_TIME_Relative legal_duration;
-
 /**
  * How long into the future do we pre-generate keys?
  */
 static struct GNUNET_TIME_Relative lookahead_sign;
 
-/**
- * Largest duration for spending of any key.
- */
-static struct GNUNET_TIME_Relative max_duration_spend;
-
 /**
  * Until what time do we provide keys?
  */
@@ -226,29 +264,708 @@ static struct GNUNET_SCHEDULER_Task *accept_task;
  */
 static struct GNUNET_SCHEDULER_Task *keygen_task;
 
+/**
+ * Head of DLL of clients connected to us.
+ */
+static struct Client *clients_head;
 
 /**
- * Load the various duration values from #kcfg.
+ * Tail of DLL of clients connected to us.
+ */
+static struct Client *clients_tail;
+
+
+/**
+ * Function run to read incoming requests from a client.
+ *
+ * @param cls the `struct Client`
+ */
+static void
+read_job (void *cls)
+{
+  struct Client *client = cls;
+
+  // FIXME: DO WORK!
+  // check for:
+  // - sign requests
+  // - revocation requests!?
+}
+
+
+/**
+ * Notify @a client about @a dk becoming available.
  *
+ * @param client the client to notify
+ * @param dk the key to notify @a client about
  * @return #GNUNET_OK on success
  */
 static int
-load_durations (void)
+notify_client_dk_add (const struct Client *client,
+                      const struct DenominationKey *dk)
 {
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_time (kcfg,
-                                           "exchange",
-                                           "LEGAL_DURATION",
-                                           &legal_duration))
+
+  // FIXME: send msg!
+  return GNUNET_SYSERR;
+}
+
+
+/**
+ * Notify @a client about @a dk being purged.
+ *
+ * @param client the client to notify
+ * @param dk the key to notify @a client about
+ * @return #GNUNET_OK on success
+ */
+static int
+notify_client_dk_del (const struct Client *client,
+                      const struct DenominationKey *dk)
+{
+  struct TALER_CRYPTO_RsaKeyPurgeNotification pn = {
+    .header.type = htons (TALER_HELPER_RSA_MT_PURGE),
+    .header.size = htons (sizeof (pn)),
+    .h_denom_pub = dk->h_pub
+  };
+  ssize_t ret;
+
+  ret = send (GNUNET_NETWORK_get_fd (client->sock),
+              &pn,
+              sizeof (pn),
+              0);
+  if (sizeof (pn) != ret)
   {
-    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                               "exchange",
-                               "LEGAL_DURATION",
-                               "fails to specify valid timeframe");
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                         "send");
+    GNUNET_NETWORK_socket_close (client->sock);
+    GNUNET_CONTAINER_DLL_remove (client_head,
+                                 client_tail,
+                                 client);
+    GNUNET_free (client);
     return GNUNET_SYSERR;
   }
-  GNUNET_TIME_round_rel (&legal_duration);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function run to accept incoming connections on #sock.
+ *
+ * @param cls NULL
+ */
+static void
+accept_job (void *cls)
+{
+  struct GNUNET_NETWORK_Handle *sock;
+  struct sockaddr_storage addr;
+  socklen_t alen;
+
+  accept_task = NULL;
+  alen = sizeof (addr);
+  sock = GNUNET_NETWORK_socket_accept (lsock,
+                                       (struct sockaddr *) &addr,
+                                       &alen);
+  if (NULL != sock)
+  {
+    struct Client *client;
+
+    client = GNUNET_new (struct Client);
+    client->sock = sock;
+    GNUNET_CONTAINER_DLL_insert (clients_head,
+                                 clients_tail,
+                                 client);
+    client->task = GNUNET_SCHEDULER_add_read (GNUNET_TIME_UNIT_FOREVER_REL,
+                                              sock,
+                                              &read_job,
+                                              client);
+    for (struct Denomination *denom = denom_head;
+         NULL != denom;
+         denom = denom->next)
+    {
+      for (struct DenominationKey *dk = denom->keys_head;
+           NULL != dk;
+           dk = dk->next)
+      {
+        if (GNUNET_OK !=
+            notify_client_dk_add (client,
+                                  dk))
+        {
+          /* client died, skip the rest */
+          client = NULL;
+          break;
+        }
+      }
+      if (NULL == client)
+        break;
+    }
+  }
+  accept_task = GNUNET_SCHEDULER_add_read (GNUNET_TIME_UNIT_FOREVER_REL,
+                                           lsock,
+                                           &accept_job,
+                                           NULL);
+}
+
+
+/**
+ * Create a new denomination key (we do not have enough).
+ *
+ * @param denom denomination key to create
+ * @return #GNUNET_OK on success
+ */
+static int
+create_key (struct Denomination *denom)
+{
+  struct DenominationKey *dk;
+  struct GNUNET_TIME_Absolute anchor;
+  struct GNUNET_CRYPTO_RsaPrivateKey *priv;
+  struct GNUNET_CRYPTO_RsaPublicKey *pub;
+  size_t buf_size;
+  void *buf;
+
+  if (NULL == denom->keys_tail)
+  {
+    anchor = GNUNET_TIME_absolute_get ();
+    (void) GNUNET_TIME_absolute_round (&anchor);
+  }
+  else
+  {
+    anchor = GNUNET_TIME_absolute_add (denom->keys_tail.anchor,
+                                       GNUNET_TIME_relative_subtract (
+                                         denom->duration_withdraw,
+                                         overlap_duration));
+  }
+  priv = GNUNET_CRYPTO_rsa_private_key_create (denom->rsa_keysize);
+  if (NULL == priv)
+  {
+    GNUNET_break (0);
+    GNUNET_SCHEDULER_shutdown ();
+    global_ret = 40;
+    return GNUNET_SYSERR;
+  }
+  pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv);
+  if (NULL == pub)
+  {
+    GNUNET_break (0);
+    GNUNET_CRYPTO_rsa_private_key_free (priv);
+    GNUNET_SCHEDULER_shutdown ();
+    global_ret = 41;
+    return;
+  }
+  buf_size = GNUNET_CRYPTO_rsa_private_key_encode (priv,
+                                                   &buf);
+  dk = GNUNET_new (struct DenominationKey);
+  dk->denom = denom;
+  dk->anchor = anchor;
+  dk->denom_priv.rsa_priv = priv;
+  GNUNET_CRYPTO_rsa_public_key_hash (pub,
+                                     &dk->h_pub);
+  dk->denom_pub.rsa_pub = pub;
+  GNUNET_asprintf (&dk->filename,
+                   "%s/%s/%llu",
+                   keydir,
+                   denom->section,
+                   anchor.abs_value_us / 
GNUNET_TIME_UNIT_SECONDS.rel_value_us);
+  if (buf_size !=
+      GNUNET_DISK_fn_write (dk->filename,
+                            buf,
+                            buf_size,
+                            GNUNET_DISK_PERM_USER_READ))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "write",
+                              dk->filename);
+    GNUNET_free (buf);
+    GNUNET_CRYPTO_rsa_private_key_free (priv);
+    GNUNET_CRYPTO_rsa_public_key_free (pub);
+    GNUNET_free (dk);
+    GNUNET_SCHEDULER_shutdown ();
+    global_ret = 42;
+    return GNUNET_SYSERR;
+  }
+  GNUNET_free (buf);
 
+  if (GNUNET_OK !=
+      GNUNET_CONTAINER_multihashmap_put (
+        keys,
+        &dk->h_pub,
+        dk,
+        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Duplicate private key created! Terminating.\n");
+    GNUNET_CRYPTO_rsa_private_key_free (priv);
+    GNUNET_CRYPTO_rsa_public_key_free (pub);
+    GNUNET_free (dk);
+    GNUNET_SCHEDULER_shutdown ();
+    global_ret = 43;
+    return;
+  }
+  GNUNET_CONTAINER_DLL_insert_tail (denom_keys_head,
+                                    denom_keys_tail,
+                                    dk);
+  {
+    struct Client *nxt;
+
+    for (struct Client *client = clients_head;
+         NULL != client;
+         client = nxt)
+    {
+      nxt = client->next;
+      if (GNUNET_OK !=
+          notify_client_dk_add (client,
+                                dk))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                    "Failed to notify client about new key, client dropped\n");
+      }
+    }
+  }
+}
+
+
+/**
+ * At what time does this denomination require its next action?
+ * Basically, the minimum of the withdraw expiration time of the
+ * oldest denomination key, and the withdraw expiration time of
+ * the newest denomination key minus the #lookahead_sign time.
+ *
+ * @param denon denomination to compute action time for
+ */
+static struct GNUNET_TIME_Absolute
+denomination_action_time (const struct Denomination *denom)
+{
+  return GNUNET_TIME_absolute_min (
+    GNUNET_TIME_absolute_add (denom->keys_head->anchor,
+                              denom->duration_withdraw),
+    GNUNET_TIME_absolute_subtract (
+      GNUNET_TIME_absolute_subtract (
+        GNUNET_TIME_absolute_add (denom->keys_tail->anchor,
+                                  denom->duration_withdraw),
+        lookahead_sign),
+      overlap_duration));
+}
+
+
+/**
+ * The withdraw period of a key @a dk has expired. Purge it.
+ *
+ * @param[in] dk expired denomination key to purge and free
+ */
+static void
+purge_key (struct DenominationKey *dk)
+{
+  struct Denomination *denom = dk->denom;
+  struct Client *nxt;
+
+  for (struct Client *client = clients_head;
+       NULL != client;
+       client = nxt)
+  {
+    nxt = client->next;
+    if (GNUNET_OK !=
+        notify_client_dk_del (client,
+                              dk))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Failed to notify client about purged key, client 
dropped\n");
+    }
+  }
+  GNUNET_CONTAINER_DLL_remove (denom->keys_head,
+                               denom->keys_tail,
+                               dk);
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CONTAINER_multihashmap_remove (keys,
+                                                       &dk->h_pub,
+                                                       dk));
+  if (0 != unlink (dk->filename))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "unlink",
+                              dk->filename);
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Purged expired private key `%s'\n",
+                dk->filename);
+  }
+  GNUNET_free (dk->filename);
+  if (0 != dk->rc)
+  {
+    /* delay until all signing threads are done with this key */
+    dk->purge = true;
+    return;
+  }
+  GNUNET_CRYPTO_rsa_private_key_free (dk->denom_priv.rsa_priv);
+  GNUNET_free (dk);
+}
+
+
+/**
+ * Create new keys and expire ancient keys of the given denomination @a denom.
+ * Removes the @a denom from the #denom_head DLL and re-insert its at the
+ * correct location sorted by next maintenance activity.
+ *
+ * @param[in,out] denom denomination to update material for
+ */
+static void
+update_keys (struct Denomination *denom)
+{
+  /* create new denomination keys */
+  while ( (NULL == denom->denom_tail) ||
+          (0 ==
+           GNUNET_TIME_absolute_get_remaining
+           GNUNET_TIME_absolute_subtract (
+             GNUNET_TIME_absolute_subtract (
+               GNUNET_TIME_absolute_add (denom->keys_tail->anchor,
+                                         denom->duration_withdraw),
+               lookahead_sign),
+             overlap_duration)) )
+    if (GNUNET_OK !=
+        create_key (denom))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Failed to create keys for `%s'\n",
+                  denom->section);
+      return;
+    }
+  /* remove expired denomination keys */
+  while ( (NULL != denom->denom_head) &&
+          (0 ==
+           GNUNET_TIME_absolute_get_remaining
+             (GNUNET_TIME_absolute_add (denom->denom_head.anchor,
+                                        denom->duration_withdraw))) )
+    purge_key (denom->denom_head);
+
+  /* Update position of 'denom' in #denom_head DLL: sort by action time */
+  {
+    struct Denomination *before;
+    struct GNUNET_TIME_Absolute at;
+
+    at = denomination_action_time (denom);
+    before = NULL;
+    GNUNET_CONTAINER_DLL_remove (denom_head,
+                                 denom_tail,
+                                 denom);
+    for (struct Denomination *pos = denom_head;
+         NULL != pos;
+         pos = pos->next)
+    {
+      if (denomination_action_time (pos).abs_value_us > at.abs_value_us)
+        break;
+      before = pos;
+    }
+    GNUNET_CONTAINER_DLL_insert_after (denom_head,
+                                       denom_tail,
+                                       before,
+                                       denom);
+  }
+}
+
+
+/**
+ * Task run periodically to expire keys and/or generate fresh ones.
+ *
+ * @param cls NULL
+ */
+static void
+update_denominations (void *cls)
+{
+  struct Denomination *denom;
+
+  (void) cls;
+  keygen_task = NULL;
+  do {
+    denom = denom_head;
+    update_keys (denom);
+  } while (denom != denom_head);
+  keygen_task = GNUNET_SCHEDULER_add_at (TIME,
+                                         &update_denominations,
+                                         denomination_action_time (denom));
+}
+
+
+/**
+ * Parse private key of denomination @a denom in @a buf.
+ *
+ * @param[out] denom denomination of the key
+ * @param filename name of the file we are parsing, for logging
+ * @param buf key material
+ * @param buf_size number of bytes in @a buf
+ */
+static void
+parse_key (struct Denomination *denom,
+           const char *filename,
+           const void *buf,
+           size_t buf_size)
+{
+  struct GNUNET_CRYPTO_RsaPrivateKey *priv;
+  char *anchor_s;
+  char dummy;
+  unsigned long long anchor_ll;
+  struct GNUNET_TIME_Absolute anchor;
+
+  anchor_s = strrchr (filename,
+                      '/');
+  if (NULL == anchor_s)
+  {
+    /* File in a directory without '/' in the name, this makes no sense. */
+    GNUNET_break (0);
+    return;
+  }
+  anchor_s++;
+  if (1 != sscanf (anchor_s,
+                   "%llu%c",
+                   &anchor_ll,
+                   &dummy))
+  {
+    /* Filenames in KEYDIR must ONLY be the anchor time in seconds! */
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Filename `%s' invalid for key file, skipping\n",
+                filename);
+    return;
+  }
+  anchor.abs_time_us = anchor_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
+  if (anchor_ll != anchor.abs_time_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us)
+  {
+    /* Integer overflow. Bad, invalid filename. */
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Filename `%s' invalid for key file, skipping\n",
+                filename);
+    return;
+  }
+  priv = GNUNET_CRYPTO_rsa_private_key_decode (buf,
+                                               buf_size);
+  if (NULL == priv)
+  {
+    /* Parser failure. */
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "File `%s' is malformed, skipping\n",
+                filename);
+    return;
+  }
+
+  {
+    struct GNUNET_CRYPTO_RsaPublicKey *pub;
+    struct DenominationKey *dk;
+    struct DenominationKey *before;
+
+    pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv);
+    if (NULL == pub)
+    {
+      GNUNET_break (0);
+      GNUNET_CRYPTO_rsa_private_key_free (priv);
+      return;
+    }
+    dk = GNUNET_new (struct DenominationKey);
+    dk->denom_priv.rsa_priv = priv;
+    dk->denomination = denom;
+    dk->anchor = anchor;
+    dk->filename = GNUNET_strdup (filename);
+    GNUNET_CRYPTO_rsa_public_key_hash (pub,
+                                       &dk->h_pub);
+    dk->denom_pub.rsa_pub = pub;
+    if (GNUNET_OK !=
+        GNUNET_CONTAINER_multihashmap_put (
+          keys,
+          &dk->h_pub,
+          dk,
+          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Duplicate private key detected in file `%s'. Skipping.\n",
+                  filename);
+      GNUNET_CRYPTO_rsa_private_key_free (priv);
+      GNUNET_CRYPTO_rsa_public_key_free (pub);
+      GNUNET_free (dk);
+      return;
+    }
+    before = NULL;
+    for (struct DenominationKey *pos = denom->keys_head;
+         NULL != pos;
+         pos = pos->next)
+    {
+      if (pos->anchor.abs_value_us > anchor.abs_value_us)
+        break;
+      before = pos;
+    }
+    GNUNET_CONTAINER_DLL_insert_after (denom->keys_head,
+                                       denom->keys_tail,
+                                       before,
+                                       dk);
+  }
+}
+
+
+/**
+ * Import a private key from @a filename for the denomination
+ * given in @a cls.
+ *
+ * @param[in,out] cls a `struct Denomiantion`
+ * @param filename name of a file in the directory
+ */
+static int
+import_key (void *cls,
+            const char *filename)
+{
+  struct Denomination *denom = cls;
+  struct GNUNET_DISK_FileHandle *fh;
+  struct GNUNET_DISK_MapHandle *map;
+  off_t fsize;
+  void *ptr;
+  int fd;
+  struct stat sbuf;
+
+  {
+    struct stat lsbuf;
+
+    if (0 != lstat (filename,
+                    &lsbuf))
+    {
+      GNUNET_log_strerror_filename (GNUNET_ERROR_TYPE_WARNING,
+                                    "lstat",
+                                    filename);
+      return GNUNET_OK;
+    }
+    if (! S_ISREG (lsbuf.st_mode))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "File `%s' is not a regular file, which is not allowed for 
private keys!\n",
+                  filename);
+      return GNUNET_OK;
+    }
+  }
+
+  fd = open (filename,
+             O_CLOEXEC);
+  if (-1 == fd)
+  {
+    GNUNET_log_strerror_filename (GNUNET_ERROR_TYPE_WARNING,
+                                  "open",
+                                  filename);
+    return GNUNET_OK;
+  }
+  if (0 != fstat (fd,
+                  &sbuf))
+  {
+    GNUNET_log_strerror_filename (GNUNET_ERROR_TYPE_WARNING,
+                                  "stat",
+                                  filename);
+    return GNUNET_OK;
+  }
+  if (! S_ISREG (sbuf.st_mode))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "File `%s' is not a regular file, which is not allowed for 
private keys!\n",
+                filename);
+    return GNUNET_OK;
+  }
+  if (0 != (sbuf.st_mode & (S_IWUSR | S_IRWXG | S_IRWXO)))
+  {
+    /* permission are NOT tight, try to patch them up! */
+    if (0 !=
+        fchmod (fd,
+                S_IRUSR))
+    {
+      GNUNET_log_strerror_filename (GNUNET_ERROR_TYPE_WARNING,
+                                    "fchmod",
+                                    filename);
+      /* refuse to use key if file has wrong permissions */
+      GNUNET_break (0 == close (fd));
+      return GNUNET_OK;
+    }
+  }
+  fh = GNUNET_DISK_get_handle_from_int_fd (fd);
+  if (NULL == fh)
+  {
+    GNUNET_log_strerror_filename (GNUNET_ERROR_TYPE_WARNING,
+                                  "open",
+                                  filename);
+    GNUNET_break (0 == close (fd));
+    return GNUNET_OK;
+  }
+  if (sbuf.st_size > 2048)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "File `%s' to big to be a private key\n",
+                filename);
+    GNUNET_DISK_file_close (fh);
+    return GNUNET_OK;
+  }
+  ptr = GNUNET_DISK_file_map (fh,
+                              &map,
+                              GNUNET_DISK_MAP_TYPE_READ,
+                              (size_t) sbuf.st_size);
+  if (NULL == ptr)
+  {
+    GNUNET_log_strerror_filename (GNUNET_ERROR_TYPE_WARNING,
+                                  "mmap",
+                                  filename);
+    GNUNET_DISK_file_close (fh);
+    return GNUNET_OK;
+  }
+  parse_key (denom,
+             filename,
+             ptr,
+             (size_t) sbuf.st_size);
+  GNUNET_DISK_file_unmap (map);
+  GNUNET_DISK_file_close (fh);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Generate new denomination signing keys for the denomination type of the 
given @a
+ * denomination_alias.
+ *
+ * @param cls a `int *`, to be set to #GNUNET_SYSERR on failure
+ * @param denomination_alias name of the denomination's section in the 
configuration
+ */
+static void
+load_denominations (void *cls,
+                    const char *denomination_alias)
+{
+  int *ret = cls;
+  struct Denomination *denom;
+
+  if (0 != strncasecmp (denomination_alias,
+                        "coin_",
+                        strlen ("coin_")))
+    return; /* not a denomination type definition */
+  denom = GNUNET_new (struct Denomination);
+  if (GNUNET_OK !=
+      parse_denomination_cfg (denomination_alias,
+                              denom))
+  {
+    *ret = GNUNET_SYSERR;
+    GNUNET_free (denom);
+    return;
+  }
+  {
+    char *dname;
+
+    GNUNET_asprintf (&dname,
+                     "%s/%s",
+                     keydir,
+                     denom->section);
+    GNUNET_DISK_directory_scan (dname,
+                                &import_key,
+                                denom);
+    GNUNET_free (dname);
+  }
+  GNUNET_CONTAINER_DLL_insert (denom_head,
+                               denom_tail,
+                               denom);
+  update_keys (denom);
+}
+
+
+/**
+ * Load the various duration values from #kcfg.
+ *
+ * @return #GNUNET_OK on success
+ */
+static int
+load_durations (void)
+{
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_time (kcfg,
                                            "exchangedb",
@@ -306,32 +1023,6 @@ parse_denomination_cfg (const char *ct,
     return GNUNET_SYSERR;
   }
   GNUNET_TIME_round_rel (&denom->duration_withdraw);
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_time (kcfg,
-                                           ct,
-                                           "DURATION_SPEND",
-                                           &denom->duration_spend))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               ct,
-                               "DURATION_SPEND");
-    return GNUNET_SYSERR;
-  }
-  GNUNET_TIME_round_rel (&denom->duration_spend);
-  max_duration_spend = GNUNET_TIME_relative_max (max_duration_spend,
-                                                 denom->duration_spend);
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_time (kcfg,
-                                           ct,
-                                           "DURATION_LEGAL",
-                                           &denom->duration_legal))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               ct,
-                               "DURATION_LEGAL");
-    return GNUNET_SYSERR;
-  }
-  GNUNET_TIME_round_rel (&denom->duration_legal);
   if (duration_overlap.rel_value_us >=
       denom->duration_withdraw.rel_value_us)
   {
@@ -362,112 +1053,11 @@ parse_denomination_cfg (const char *ct,
     return GNUNET_SYSERR;
   }
   denom->rsa_keysize = (unsigned int) rsa_keysize;
-  if (GNUNET_OK !=
-      TALER_config_get_amount (kcfg,
-                               ct,
-                               "VALUE",
-                               &denom->value))
-  {
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK !=
-      TALER_config_get_amount (kcfg,
-                               ct,
-                               "FEE_WITHDRAW",
-                               &denom->fee_withdraw))
-  {
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK !=
-      TALER_config_get_amount (kcfg,
-                               ct,
-                               "FEE_DEPOSIT",
-                               &denom->fee_deposit))
-  {
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK !=
-      TALER_config_get_amount (kcfg,
-                               ct,
-                               "FEE_REFRESH",
-                               &denom->fee_refresh))
-  {
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK !=
-      TALER_config_get_amount (kcfg,
-                               ct,
-                               "fee_refund",
-                               &denom->fee_refund))
-  {
-    return GNUNET_SYSERR;
-  }
+  denom->section = GNUNET_strdup (ct);
   return GNUNET_OK;
 }
 
 
-/**
- * Generate new denomination signing keys for the denomination type of the 
given @a
- * denomination_alias.
- *
- * @param cls a `int *`, to be set to #GNUNET_SYSERR on failure
- * @param denomination_alias name of the denomination's section in the 
configuration
- */
-static void
-load_denominations (void *cls,
-                    const char *denomination_alias)
-{
-  int *ret = cls;
-  struct Denomination *denom;
-
-  if (0 != strncasecmp (denomination_alias,
-                        "coin_",
-                        strlen ("coin_")))
-    return; /* not a denomination type definition */
-  denom = GNUNET_new (struct Denomination);
-  if (GNUNET_OK !=
-      parse_denomination_cfg (denomination_alias,
-                              denom))
-  {
-    *ret = GNUNET_SYSERR;
-    GNUNET_free (denom);
-    return;
-  }
-  GNUNET_CONTAINER_DLL_insert (denom_head,
-                               denom_tail,
-                               denom);
-  // FIXME: load all existing denomination keys for this denom from disk!
-}
-
-
-/**
- * Function run to accept incoming connections on #sock.
- *
- * @param cls NULL
- */
-static void
-accept_job (void *cls)
-{
-  struct GNUNET_NETWORK_Handle *sock;
-  struct sockaddr_storage addr;
-  socklen_t alen;
-
-  accept_task = NULL;
-  alen = sizeof (addr);
-  sock = GNUNET_NETWORK_socket_accept (lsock,
-                                       (struct sockaddr *) &addr,
-                                       &alen);
-  // FIXME: add to list of managed connections;
-  // then send all known keys;
-  // start to listen for incoming requests;
-
-  accept_task = GNUNET_SCHEDULER_add_read (GNUNET_TIME_UNIT_FOREVER_REL,
-                                           lsock,
-                                           &accept_job,
-                                           NULL);
-}
-
-
 /**
  * Function run on shutdown. Stops the various jobs (nicely).
  *
@@ -497,7 +1087,7 @@ do_shutdown (void *cls)
 
 
 /**
- * Main function that will be run.
+ * Main function that will be run under the GNUnet scheduler.
  *
  * @param cls closure
  * @param args remaining command-line arguments
@@ -514,13 +1104,6 @@ run (void *cls,
   (void) args;
   (void) cfgfile;
   kcfg = cfg;
-  if (GNUNET_OK !=
-      TALER_config_get_currency (cfg,
-                                 &currency))
-  {
-    global_ret = 1;
-    return;
-  }
   if (now.abs_value_us != now_tmp.abs_value_us)
   {
     /* The user gave "--now", use it! */
@@ -540,9 +1123,9 @@ run (void *cls,
   }
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_filename (kcfg,
-                                               "exchange",
+                                               "taler-helper-crypto-rsa",
                                                "KEYDIR",
-                                               &exchange_directory))
+                                               &keydir))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "exchange",
@@ -605,15 +1188,10 @@ run (void *cls,
 
   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
                                  NULL);
-  /* FIXME: start job to accept incoming requests on 'sock' */
-  accept_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                               lsock,
-                                               &accept_job,
-                                               NULL);
 
   /* Load denominations */
   keys = GNUNET_CONTAINER_multihashmap_create (65536,
-                                               GNUNET_NO);
+                                               GNUNET_YES);
   {
     int ok;
 
@@ -637,12 +1215,25 @@ run (void *cls,
     return;
   }
 
-  // FIXME: begin job to create additional denomination keys based on
-  // next needs!
-  // FIXME: same job or extra job for private key expiration/purge?
+  /* start job to accept incoming requests on 'sock' */
+  accept_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                               lsock,
+                                               &accept_job,
+                                               NULL);
+
+  /* start job to keep keys up-to-date */
+  keygen_task = GNUNET_SCHEDULER_add_now (&update_denominations,
+                                          NULL);
 }
 
 
+/**
+ * The entry point.
+ *
+ * @param argc number of arguments in @a argv
+ * @param argv command-line arguments
+ * @return 0 on normal termination
+ */
 int
 main (int argc,
       char **argv)
diff --git a/src/util/taler-helper-crypto-rsa.h 
b/src/util/taler-helper-crypto-rsa.h
new file mode 100644
index 00000000..0f03d12f
--- /dev/null
+++ b/src/util/taler-helper-crypto-rsa.h
@@ -0,0 +1,90 @@
+/*
+  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 util/taler-helper-crypto-rsa.h
+ * @brief IPC messages for the RSA crypto helper.
+ * @author Christian Grothoff
+ */
+#ifndef TALER_HELPER_CRYPTO_RSA_H
+#define TALER_HELPER_CRYPTO_RSA_H
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Message sent if a key is available.
+ */
+struct TALER_CRYPTO_RsaKeyAvailableNotification
+{
+  /**
+   * Type is PURGE.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Number of bytes of the public key.
+   */
+  uint16_t pub_size;
+
+  /**
+   * Number of bytes of the section name.
+   */
+  uint16_t section_name;
+
+  /**
+   * When does the key become available?
+   */
+  struct GNUNET_TIME_AbsoluteNBO anchor_time;
+
+  /**
+   * How long is the key available after @e anchor_time?
+   */
+  struct GNUNET_TIME_RelativeNBO duration_withdraw;
+
+  /* followed by @e pub_size bytes of the public key */
+
+  /* followed by @e section_name bytes of the configuration section name
+     of the denomination of this key */
+
+};
+
+
+/**
+ * Message sent if a key was purged.
+ */
+struct TALER_CRYPTO_RsaKeyPurgeNotification
+{
+  /**
+   * Type is PURGE.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * For now, always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * Hash of the public key of the purged RSA key.
+   */
+  struct GNUNET_HashCode h_denom_pub;
+
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+
+#endif

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