gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] 01/06: -sms wip


From: gnunet
Subject: [taler-exchange] 01/06: -sms wip
Date: Wed, 17 Nov 2021 20:52:43 +0100

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

grothoff pushed a commit to branch master
in repository exchange.

commit 6e86a3c43cd6b16115134dfe617b091f8dbcd89d
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Wed Nov 17 13:03:47 2021 +0100

    -sms wip
---
 src/testing/testing_api_cmd_batch.c    |   84 +-
 src/testing/testing_api_loop.c         |  175 +++-
 src/util/taler-exchange-secmod-eddsa.c | 1288 ++++++++------------------
 src/util/taler-exchange-secmod-rsa.c   | 1546 ++++++++++----------------------
 src/util/taler-exchange-secmod-rsa.h   |    6 +-
 src/util/test_helper_eddsa.c           |  119 ++-
 src/util/test_helper_rsa.c             |  259 ++++--
 7 files changed, 1327 insertions(+), 2150 deletions(-)

diff --git a/src/testing/testing_api_cmd_batch.c 
b/src/testing/testing_api_cmd_batch.c
index e8f76ca3..2e880e8d 100644
--- a/src/testing/testing_api_cmd_batch.c
+++ b/src/testing/testing_api_cmd_batch.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2021 Taler Systems SA
+  Copyright (C) 2014-2018 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as
@@ -113,15 +113,22 @@ batch_cleanup (void *cls,
  * @param index index number of the object to offer.
  * @return #GNUNET_OK on success.
  */
-static enum GNUNET_GenericReturnValue
+static int
 batch_traits (void *cls,
               const void **ret,
               const char *trait,
               unsigned int index)
 {
+#define CURRENT_CMD_INDEX 0
+#define BATCH_INDEX 1
+
   struct BatchState *bs = cls;
+
   struct TALER_TESTING_Trait traits[] = {
-    TALER_TESTING_make_trait_batch_cmds (&bs->batch),
+    TALER_TESTING_make_trait_cmd
+      (CURRENT_CMD_INDEX, &bs->batch[bs->batch_ip]),
+    TALER_TESTING_make_trait_cmd
+      (BATCH_INDEX, bs->batch),
     TALER_TESTING_trait_end ()
   };
 
@@ -133,6 +140,18 @@ batch_traits (void *cls,
 }
 
 
+/**
+ * Create a "batch" command.  Such command takes a
+ * end_CMD-terminated array of CMDs and executed them.
+ * Once it hits the end CMD, it passes the control
+ * to the next top-level CMD, regardless of it being
+ * another batch or ordinary CMD.
+ *
+ * @param label the command label.
+ * @param batch array of CMDs to execute.
+ *
+ * @return the command.
+ */
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_batch (const char *label,
                          struct TALER_TESTING_Command *batch)
@@ -166,29 +185,68 @@ TALER_TESTING_cmd_batch (const char *label,
 }
 
 
+/**
+ * Advance internal pointer to next command.
+ *
+ * @param is interpreter state.
+ * @param cmd batch to advance
+ */
 void
-TALER_TESTING_cmd_batch_next (struct TALER_TESTING_Interpreter *is)
+TALER_TESTING_cmd_batch_next (struct TALER_TESTING_Interpreter *is,
+                              struct TALER_TESTING_Command *par,
+                              struct TALER_TESTING_Command *cmd)
 {
-  struct BatchState *bs = is->commands[is->ip].cls;
+  struct BatchState *bs = cmd->cls;
+  struct TALER_TESTING_Command *chld;
 
   if (NULL == bs->batch[bs->batch_ip].label)
   {
-    is->commands[is->ip].finish_time = GNUNET_TIME_absolute_get ();
-    is->ip++;
+    if (NULL == par)
+    {
+      is->commands[is->ip].finish_time = GNUNET_TIME_absolute_get ();
+      is->ip++;
+    }
+    else
+    {
+      struct BatchState *ps = par->cls;
+
+      cmd->finish_time = GNUNET_TIME_absolute_get ();
+      ps->batch_ip++;
+    }
     return;
   }
-  bs->batch[bs->batch_ip].finish_time = GNUNET_TIME_absolute_get ();
-  bs->batch_ip++;
+  chld = &bs->batch[bs->batch_ip];
+  if (TALER_TESTING_cmd_is_batch (chld))
+  {
+    TALER_TESTING_cmd_batch_next (is,
+                                  cmd,
+                                  chld);
+  }
+  else
+  {
+    bs->batch[bs->batch_ip].finish_time = GNUNET_TIME_absolute_get ();
+    bs->batch_ip++;
+  }
 }
 
 
-bool
+/**
+ * Test if this command is a batch command.
+ *
+ * @return false if not, true if it is a batch command
+ */
+int
 TALER_TESTING_cmd_is_batch (const struct TALER_TESTING_Command *cmd)
 {
   return cmd->run == &batch_run;
 }
 
 
+/**
+ * Obtain what command the batch is at.
+ *
+ * @return cmd current batch command
+ */
 struct TALER_TESTING_Command *
 TALER_TESTING_cmd_batch_get_current (const struct TALER_TESTING_Command *cmd)
 {
@@ -199,6 +257,12 @@ TALER_TESTING_cmd_batch_get_current (const struct 
TALER_TESTING_Command *cmd)
 }
 
 
+/**
+ * Set what command the batch should be at.
+ *
+ * @param cmd current batch command
+ * @param new_ip where to move the IP
+ */
 void
 TALER_TESTING_cmd_batch_set_current (const struct TALER_TESTING_Command *cmd,
                                      unsigned int new_ip)
diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c
index f86c7765..868a2d75 100644
--- a/src/testing/testing_api_loop.c
+++ b/src/testing/testing_api_loop.c
@@ -36,6 +36,49 @@
  */
 static struct GNUNET_DISK_PipeHandle *sigpipe;
 
+
+const struct TALER_TESTING_Command *
+lookup_helper (const struct TALER_TESTING_Command *cmd,
+               const char *label)
+{
+#define BATCH_INDEX 1
+  struct TALER_TESTING_Command *batch;
+  struct TALER_TESTING_Command *current;
+  struct TALER_TESTING_Command *icmd;
+  const struct TALER_TESTING_Command *match;
+
+  current = TALER_TESTING_cmd_batch_get_current (cmd);
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_TESTING_get_trait_cmd (cmd,
+                                              BATCH_INDEX,
+                                              &batch));
+  /* We must do the loop forward, but we can find the last match */
+  match = NULL;
+  for (unsigned int j = 0;
+       NULL != (icmd = &batch[j])->label;
+       j++)
+  {
+    if (TALER_TESTING_cmd_is_batch (icmd))
+    {
+      const struct TALER_TESTING_Command *imatch;
+
+      imatch = lookup_helper (icmd,
+                              label);
+      if (NULL != imatch)
+        match = imatch;
+    }
+    if ( (current != icmd) &&
+         (NULL != icmd->label) &&
+         (0 == strcmp (icmd->label,
+                       label)) )
+      match = icmd;
+    if (current == icmd)
+      break;
+  }
+  return match;
+}
+
+
 /**
  * Lookup command by label.
  *
@@ -66,30 +109,12 @@ TALER_TESTING_interpreter_lookup_command (struct 
TALER_TESTING_Interpreter *is,
 
     if (TALER_TESTING_cmd_is_batch (cmd))
     {
-      struct TALER_TESTING_Command **batch;
-      struct TALER_TESTING_Command *current;
-      struct TALER_TESTING_Command *icmd;
-      const struct TALER_TESTING_Command *match;
-
-      current = TALER_TESTING_cmd_batch_get_current (cmd);
-      GNUNET_assert (GNUNET_OK ==
-                     TALER_TESTING_get_trait_batch_cmds (cmd,
-                                                         &batch));
-      /* We must do the loop forward, but we can find the last match */
-      match = NULL;
-      for (unsigned int j = 0;
-           NULL != (icmd = &(*batch)[j])->label;
-           j++)
-      {
-        if (current == icmd)
-          break; /* do not go past current command */
-        if ( (NULL != icmd->label) &&
-             (0 == strcmp (icmd->label,
-                           label)) )
-          match = icmd;
-      }
-      if (NULL != match)
-        return match;
+      const struct TALER_TESTING_Command *ret;
+
+      ret = lookup_helper (cmd,
+                           label);
+      if (NULL != ret)
+        return ret;
     }
   }
   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -118,6 +143,15 @@ TALER_TESTING_interpreter_get_fakebank (struct 
TALER_TESTING_Interpreter *is)
 }
 
 
+/**
+ * Run tests starting the "fakebank" first.  The "fakebank"
+ * is a C minimalist version of the human-oriented Python bank,
+ * which is also part of the Taler project.
+ *
+ * @param is pointer to the interpreter state
+ * @param commands the list of commands to execute
+ * @param bank_url the url the fakebank is supposed to run on
+ */
 void
 TALER_TESTING_run_with_fakebank (struct TALER_TESTING_Interpreter *is,
                                  struct TALER_TESTING_Command *commands,
@@ -155,6 +189,9 @@ static void
 interpreter_run (void *cls);
 
 
+/**
+ * Current command is done, run the next one.
+ */
 void
 TALER_TESTING_interpreter_next (struct TALER_TESTING_Interpreter *is)
 {
@@ -166,7 +203,9 @@ TALER_TESTING_interpreter_next (struct 
TALER_TESTING_Interpreter *is)
     return; /* ignore, we already failed! */
   if (TALER_TESTING_cmd_is_batch (cmd))
   {
-    TALER_TESTING_cmd_batch_next (is);
+    TALER_TESTING_cmd_batch_next (is,
+                                  NULL,
+                                  cmd);
   }
   else
   {
@@ -189,6 +228,11 @@ TALER_TESTING_interpreter_next (struct 
TALER_TESTING_Interpreter *is)
 }
 
 
+/**
+ * Current command failed, clean up and fail the test case.
+ *
+ * @param is interpreter of the test
+ */
 void
 TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is)
 {
@@ -209,6 +253,11 @@ TALER_TESTING_interpreter_fail (struct 
TALER_TESTING_Interpreter *is)
 }
 
 
+/**
+ * Create command array terminator.
+ *
+ * @return a end-command.
+ */
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_end (void)
 {
@@ -219,6 +268,9 @@ TALER_TESTING_cmd_end (void)
 }
 
 
+/**
+ * Obtain current label.
+ */
 const char *
 TALER_TESTING_interpreter_get_current_label (struct
                                              TALER_TESTING_Interpreter *is)
@@ -289,9 +341,8 @@ do_shutdown (void *cls)
   for (unsigned int j = 0;
        NULL != (cmd = &is->commands[j])->label;
        j++)
-    if (NULL != cmd->cleanup)
-      cmd->cleanup (cmd->cls,
-                    cmd);
+    cmd->cleanup (cmd->cls,
+                  cmd);
 
   if (NULL != is->exchange)
   {
@@ -367,8 +418,17 @@ maint_child_death (void *cls)
   enum GNUNET_OS_ProcessStatusType type;
   unsigned long code;
 
-  while (TALER_TESTING_cmd_is_batch (cmd))
-    cmd = TALER_TESTING_cmd_batch_get_current (cmd);
+  if (TALER_TESTING_cmd_is_batch (cmd))
+  {
+    struct TALER_TESTING_Command *batch_cmd;
+
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_TESTING_get_trait_cmd (cmd,
+                                                0,
+                                                &batch_cmd));
+    cmd = batch_cmd;
+  }
+
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Got SIGCHLD for `%s'.\n",
               cmd->label);
@@ -381,6 +441,7 @@ maint_child_death (void *cls)
                                        sizeof (c)));
   if (GNUNET_OK !=
       TALER_TESTING_get_trait_process (cmd,
+                                       0,
                                        &processp))
   {
     GNUNET_break (0);
@@ -433,6 +494,12 @@ maint_child_death (void *cls)
 }
 
 
+/**
+ * Wait until we receive SIGCHLD signal.
+ * Then obtain the process trait of the current
+ * command, wait on the the zombie and continue
+ * with the next command.
+ */
 void
 TALER_TESTING_wait_for_sigchld (struct TALER_TESTING_Interpreter *is)
 {
@@ -449,6 +516,16 @@ TALER_TESTING_wait_for_sigchld (struct 
TALER_TESTING_Interpreter *is)
 }
 
 
+/**
+ * Run the testsuite.  Note, CMDs are copied into
+ * the interpreter state because they are _usually_
+ * defined into the "run" method that returns after
+ * having scheduled the test interpreter.
+ *
+ * @param is the interpreter state
+ * @param commands the list of command to execute
+ * @param timeout how long to wait
+ */
 void
 TALER_TESTING_run2 (struct TALER_TESTING_Interpreter *is,
                     struct TALER_TESTING_Command *commands,
@@ -478,6 +555,15 @@ TALER_TESTING_run2 (struct TALER_TESTING_Interpreter *is,
 }
 
 
+/**
+ * Run the testsuite.  Note, CMDs are copied into
+ * the interpreter state because they are _usually_
+ * defined into the "run" method that returns after
+ * having scheduled the test interpreter.
+ *
+ * @param is the interpreter state
+ * @param commands the list of command to execute
+ */
 void
 TALER_TESTING_run (struct TALER_TESTING_Interpreter *is,
                    struct TALER_TESTING_Command *commands)
@@ -536,6 +622,16 @@ sighandler_child_death (void)
 }
 
 
+/**
+ * "Canonical" cert_cb used when we are connecting to the
+ * Exchange.
+ *
+ * @param cls closure, typically, the "run" method containing
+ *        all the commands to be run, and a closure for it.
+ * @param hr HTTP response details
+ * @param keys the exchange's keys.
+ * @param compat protocol compatibility information.
+ */
 void
 TALER_TESTING_cert_cb (void *cls,
                        const struct TALER_EXCHANGE_HttpResponse *hr,
@@ -804,6 +900,25 @@ load_urls (struct TALER_TESTING_Interpreter *is)
 }
 
 
+/**
+ * Install signal handlers plus schedules the main wrapper
+ * around the "run" method.
+ *
+ * @param main_cb the "run" method which contains all the
+ *        commands.
+ * @param main_cb_cls a closure for "run", typically NULL.
+ * @param cfg configuration to use
+ * @param exchanged exchange process handle: will be put in the
+ *        state as some commands - e.g. revoke - need to send
+ *        signal to it, for example to let it know to reload the
+ *        key state.. if NULL, the interpreter will run without
+ *        trying to connect to the exchange first.
+ * @param exchange_connect #GNUNET_YES if the test should connect
+ *        to the exchange, #GNUNET_NO otherwise
+ * @return #GNUNET_OK if all is okay, != #GNUNET_OK otherwise.
+ *         non-GNUNET_OK codes are #GNUNET_SYSERR most of the
+ *         times.
+ */
 int
 TALER_TESTING_setup (TALER_TESTING_Main main_cb,
                      void *main_cb_cls,
diff --git a/src/util/taler-exchange-secmod-eddsa.c 
b/src/util/taler-exchange-secmod-eddsa.c
index dc2d35d1..3b7ee3de 100644
--- a/src/util/taler-exchange-secmod-eddsa.c
+++ b/src/util/taler-exchange-secmod-eddsa.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2020 Taler Systems SA
+  Copyright (C) 2014-2021 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
@@ -26,7 +26,7 @@
  *   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.
+ *   one per client.
  * - 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
@@ -41,6 +41,7 @@
 #include "taler_error_codes.h"
 #include "taler_signatures.h"
 #include "secmod_common.h"
+#include <poll.h>
 
 
 /**
@@ -79,6 +80,11 @@ struct Key
    */
   struct GNUNET_TIME_Absolute anchor;
 
+  /**
+   * Generation when this key was created or revoked.
+   */
+  uint64_t key_gen;
+
   /**
    * Reference counter. Counts the number of threads that are
    * using this key at this time.
@@ -94,92 +100,6 @@ struct Key
 };
 
 
-/**
- * Information we keep for a client connected to us.
- */
-struct Client
-{
-
-  /**
-   * Kept in a DLL.
-   */
-  struct Client *next;
-
-  /**
-   * Kept in a DLL.
-   */
-  struct Client *prev;
-
-  /**
-   * Client address.
-   */
-  struct sockaddr_un addr;
-
-  /**
-   * Number of bytes used in @e addr.
-   */
-  socklen_t addr_size;
-
-};
-
-
-struct WorkItem
-{
-
-  /**
-   * Kept in a DLL.
-   */
-  struct WorkItem *next;
-
-  /**
-   * Kept in a DLL.
-   */
-  struct WorkItem *prev;
-
-  /**
-   * Key to be used for this operation.
-   */
-  struct Key *key;
-
-  /**
-   * EDDSA signature over @e msg using @e key. Result of doing the work.
-   */
-  struct TALER_ExchangeSignatureP signature;
-
-  /**
-   * Message to sign.
-   */
-  struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
-
-  /**
-   * Client address.
-   */
-  struct sockaddr_un addr;
-
-  /**
-   * Number of bytes used in @e addr.
-   */
-  socklen_t addr_size;
-
-  /**
-   * Operation status code.
-   */
-  enum TALER_ErrorCode ec;
-
-};
-
-
-/**
- * Private key of this security module. Used to sign denomination key
- * announcements.
- */
-static struct TALER_SecurityModulePrivateKeyP smpriv;
-
-/**
- * Public key of this security module.
- */
-static struct TALER_SecurityModulePublicKeyP smpub;
-
 /**
  * Head of DLL of actual keys, sorted by anchor.
  */
@@ -200,13 +120,6 @@ static struct GNUNET_TIME_Relative duration;
  */
 static int global_ret;
 
-/**
- * Number of worker threads to use. Default (0) is to use one per CPU core
- * available.
- * Length of the #workers array.
- */
-static unsigned int num_workers;
-
 /**
  * Time when the key update is executed.
  * Either the actual current time, or a pretended time.
@@ -219,11 +132,6 @@ static struct GNUNET_TIME_Absolute now;
  */
 static struct GNUNET_TIME_Absolute now_tmp;
 
-/**
- * Handle to the exchange's configuration
- */
-static const struct GNUNET_CONFIGURATION_Handle *kcfg;
-
 /**
  * Where do we store the keys?
  */
@@ -241,324 +149,87 @@ static struct GNUNET_TIME_Relative overlap_duration;
  */
 static struct GNUNET_TIME_Relative lookahead_sign;
 
-/**
- * Our listen socket.
- */
-static struct GNUNET_NETWORK_Handle *unix_sock;
-
-/**
- * Path where we are listening.
- */
-static char *unixpath;
-
-/**
- * Task run to accept new inbound connections.
- */
-static struct GNUNET_SCHEDULER_Task *read_task;
-
 /**
  * Task run to generate new keys.
  */
 static struct GNUNET_SCHEDULER_Task *keygen_task;
 
 /**
- * Head of DLL of clients connected to us.
- */
-static struct Client *clients_head;
-
-/**
- * Tail of DLL of clients connected to us.
- */
-static struct Client *clients_tail;
-
-/**
- * Head of DLL with pending signing operations.
- */
-static struct WorkItem *work_head;
-
-/**
- * Tail of DLL with pending signing operations.
- */
-static struct WorkItem *work_tail;
-
-/**
- * Lock for the work queue.
- */
-static pthread_mutex_t work_lock;
-
-/**
- * Condition variable for the semaphore of the work queue.
- */
-static pthread_cond_t work_cond = PTHREAD_COND_INITIALIZER;
-
-/**
- * Number of items in the work queue. Also used as the semaphore counter.
- */
-static unsigned long long work_counter;
-
-/**
- * Head of DLL with completed signing operations.
- */
-static struct WorkItem *done_head;
-
-/**
- * Tail of DLL with completed signing operations.
- */
-static struct WorkItem *done_tail;
-
-/**
- * Lock for the done queue.
- */
-static pthread_mutex_t done_lock;
-
-/**
- * Task waiting for work to be done.
- */
-static struct GNUNET_SCHEDULER_Task *done_task;
-
-/**
- * Signal used by threads to notify the #done_task that they
- * completed work that is now in the done queue.
+ * Lock for the keys queue.
  */
-static struct GNUNET_NETWORK_Handle *done_signal;
+static pthread_mutex_t keys_lock;
 
 /**
- * Set once we are in shutdown and workers should terminate.
+ * Current key generation.
  */
-static volatile bool in_shutdown;
-
-/**
- * Array of #num_workers sign_worker() threads.
- */
-static pthread_t *workers;
-
-
-/**
- * Main function of a worker thread that signs.
- *
- * @param cls NULL
- * @return NULL
- */
-static void *
-sign_worker (void *cls)
-{
-  (void) cls;
-  GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
-  while (! in_shutdown)
-  {
-    struct WorkItem *wi;
-
-    while (NULL != (wi = work_head))
-    {
-      /* take work from queue */
-      GNUNET_CONTAINER_DLL_remove (work_head,
-                                   work_tail,
-                                   wi);
-      work_counter--;
-      GNUNET_assert (0 == pthread_mutex_unlock (&work_lock));
-      {
-        if (GNUNET_OK !=
-            GNUNET_CRYPTO_eddsa_sign_ (&wi->key->exchange_priv.eddsa_priv,
-                                       wi->purpose,
-                                       &wi->signature.eddsa_signature))
-          wi->ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
-        else
-          wi->ec = TALER_EC_NONE;
-      }
-      /* put completed work into done queue */
-      GNUNET_assert (0 == pthread_mutex_lock (&done_lock));
-      GNUNET_CONTAINER_DLL_insert (done_head,
-                                   done_tail,
-                                   wi);
-      GNUNET_assert (0 == pthread_mutex_unlock (&done_lock));
-      {
-        uint64_t val = GNUNET_htonll (1);
-
-        /* raise #done_signal */
-        if (sizeof(val) !=
-            write (GNUNET_NETWORK_get_fd (done_signal),
-                   &val,
-                   sizeof (val)))
-          GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
-                               "write(eventfd)");
-      }
-      GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
-    }
-    if (in_shutdown)
-      break;
-    /* queue is empty, wait for work */
-    GNUNET_assert (0 ==
-                   pthread_cond_wait (&work_cond,
-                                      &work_lock));
-  }
-  GNUNET_assert (0 ==
-                 pthread_mutex_unlock (&work_lock));
-  return NULL;
-}
+static uint64_t key_gen;
 
 
 /**
- * Free @a client, releasing all (remaining) state.
- *
- * @param[in] client data to free
- */
-static void
-free_client (struct Client *client)
-{
-  GNUNET_CONTAINER_DLL_remove (clients_head,
-                               clients_tail,
-                               client);
-  GNUNET_free (client);
-}
-
-
-/**
- * Function run to read incoming requests from a client.
- *
- * @param cls the `struct Client`
- */
-static void
-read_job (void *cls);
-
-
-/**
- * Free @a key. It must already have been removed from the DLL.
- *
- * @param[in] key the key to free
- */
-static void
-free_key (struct Key *key)
-{
-  GNUNET_free (key->filename);
-  GNUNET_free (key);
-}
-
-
-/**
- * Send a message starting with @a hdr to @a client.  We expect that
- * the client is mostly able to handle everything at whatever speed
- * we have (after all, the crypto should be the slow part). However,
- * especially on startup when we send all of our keys, it is possible
- * that the client cannot keep up. In that case, we throttle when
- * sending fails. This does not work with poll() as we cannot specify
- * the sendto() target address with poll(). So we nanosleep() instead.
+ * Notify @a client about @a key becoming available.
  *
- * @param addr address where to send the message
- * @param addr_size number of bytes in @a addr
- * @param hdr beginning of the message, length indicated in size field
+ * @param[in,out] client the client to notify; possible freed if transmission 
fails
+ * @param key the key to notify @a client about
  * @return #GNUNET_OK on success
  */
-static int
-transmit (const struct sockaddr_un *addr,
-          socklen_t addr_size,
-          const struct GNUNET_MessageHeader *hdr)
+static enum GNUNET_GenericReturnValue
+notify_client_key_add (struct TES_Client *client,
+                       const struct Key *key)
 {
-  for (unsigned int i = 0; i<100; i++)
+  struct TALER_CRYPTO_EddsaKeyAvailableNotification an = {
+    .header.size = htons (sizeof (an)),
+    .header.type = htons (TALER_HELPER_EDDSA_MT_AVAIL),
+    .anchor_time = GNUNET_TIME_absolute_hton (key->anchor),
+    .duration = GNUNET_TIME_relative_hton (duration),
+    .exchange_pub = key->exchange_pub,
+    .secm_pub = TES_smpub
+  };
+
+  TALER_exchange_secmod_eddsa_sign (&key->exchange_pub,
+                                    key->anchor,
+                                    duration,
+                                    &TES_smpriv,
+                                    &an.secm_sig);
+  if (GNUNET_OK !=
+      TES_transmit (client->csock,
+                    &an.header))
   {
-    ssize_t ret = sendto (GNUNET_NETWORK_get_fd (unix_sock),
-                          hdr,
-                          ntohs (hdr->size),
-                          0 /* no flags => blocking! */,
-                          (const struct sockaddr *) addr,
-                          addr_size);
-    if ( (-1 == ret) &&
-         (EAGAIN == errno) )
-    {
-      /* _Maybe_ with blocking sendto(), this should no
-         longer be needed; still keeping it just in case. */
-      /* Wait a bit, in case client is just too slow */
-      struct timespec req = {
-        .tv_sec = 0,
-        .tv_nsec = 1000
-      };
-      nanosleep (&req, NULL);
-      continue;
-    }
-    if (ret == ntohs (hdr->size))
-      return GNUNET_OK;
-    if (ret != ntohs (hdr->size))
-      break;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Client %p must have disconnected\n",
+                client);
+    return GNUNET_SYSERR;
   }
-  GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
-                       "sendto");
-  return GNUNET_SYSERR;
+  return GNUNET_OK;
 }
 
 
 /**
- * Process completed tasks that are in the #done_head queue, sending
- * the result back to the client (and resuming the client).
+ * Notify @a client about @a key being purged.
  *
- * @param cls NULL
+ * @param[in,out] client the client to notify; possible freed if transmission 
fails
+ * @param key the key to notify @a client about
+ * @return #GNUNET_OK on success
  */
-static void
-handle_done (void *cls)
+static enum GNUNET_GenericReturnValue
+notify_client_key_del (struct TES_Client *client,
+                       const struct Key *key)
 {
-  uint64_t data;
-  (void) cls;
+  struct TALER_CRYPTO_EddsaKeyPurgeNotification pn = {
+    .header.type = htons (TALER_HELPER_EDDSA_MT_PURGE),
+    .header.size = htons (sizeof (pn)),
+    .exchange_pub = key->exchange_pub
+  };
 
-  /* consume #done_signal */
-  if (sizeof (data) !=
-      read (GNUNET_NETWORK_get_fd (done_signal),
-            &data,
-            sizeof (data)))
-    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
-                         "read(eventfd)");
-  done_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                             done_signal,
-                                             &handle_done,
-                                             NULL);
-  GNUNET_assert (0 == pthread_mutex_lock (&done_lock));
-  while (NULL != done_head)
+  if (GNUNET_OK !=
+      TES_transmit (client->csock,
+                    &pn.header))
   {
-    struct WorkItem *wi = done_head;
-
-    GNUNET_CONTAINER_DLL_remove (done_head,
-                                 done_tail,
-                                 wi);
-    GNUNET_assert (0 == pthread_mutex_unlock (&done_lock));
-    if (TALER_EC_NONE != wi->ec)
-    {
-      struct TALER_CRYPTO_EddsaSignFailure sf = {
-        .header.size = htons (sizeof (sf)),
-        .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE),
-        .ec = htonl (wi->ec)
-      };
-
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Signing request %p failed, worker failed to produce 
signature\n",
-                  wi);
-      (void) transmit (&wi->addr,
-                       wi->addr_size,
-                       &sf.header);
-    }
-    else
-    {
-      struct TALER_CRYPTO_EddsaSignResponse sr = {
-        .header.size = htons (sizeof (sr)),
-        .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGNATURE),
-        .exchange_pub = wi->key->exchange_pub,
-        .exchange_sig = wi->signature
-      };
-
-      (void) transmit (&wi->addr,
-                       wi->addr_size,
-                       &sr.header);
-    }
-    {
-      struct Key *key = wi->key;
-
-      key->rc--;
-      if ( (0 == key->rc) &&
-           (key->purge) )
-        free_key (key);
-    }
-    GNUNET_free (wi->purpose);
-    GNUNET_free (wi);
-    GNUNET_assert (0 == pthread_mutex_lock (&done_lock));
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Client %p must have disconnected\n",
+                client);
+    return GNUNET_SYSERR;
   }
-  GNUNET_assert (0 == pthread_mutex_unlock (&done_lock));
-
+  return GNUNET_OK;
 }
 
 
@@ -567,19 +238,23 @@ handle_done (void *cls)
  * signature using the respective key and return the result to
  * the client.
  *
- * @param addr address of the client making the request
- * @param addr_size number of bytes in @a addr
+ * @param client the client making the request
  * @param sr the request details
+ * @return #GNUNET_OK on success
  */
-static void
-handle_sign_request (const struct sockaddr_un *addr,
-                     socklen_t addr_size,
+static enum GNUNET_GenericReturnValue
+handle_sign_request (struct TES_Client *client,
                      const struct TALER_CRYPTO_EddsaSignRequest *sr)
 {
   const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose = &sr->purpose;
-  struct WorkItem *wi;
   size_t purpose_size = ntohs (sr->header.size) - sizeof (*sr)
                         + sizeof (*purpose);
+  struct Key *key;
+  struct TALER_CRYPTO_EddsaSignResponse sres = {
+    .header.size = htons (sizeof (sres)),
+    .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGNATURE)
+  };
+  enum TALER_ErrorCode ec;
 
   if (purpose_size != htonl (purpose->size))
   {
@@ -591,122 +266,73 @@ handle_sign_request (const struct sockaddr_un *addr,
 
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Signing request failed, request malformed\n");
-    (void) transmit (addr,
-                     addr_size,
-                     &sf.header);
-    return;
+    return TES_transmit (client->csock,
+                         &sf.header);
   }
-  {
-    struct GNUNET_TIME_Absolute now;
 
-    now = GNUNET_TIME_absolute_get ();
-    if ( (now.abs_value_us >= keys_head->anchor.abs_value_us) &&
-         (now.abs_value_us < keys_head->anchor.abs_value_us
-          + duration.rel_value_us) )
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Signing at %llu with key valid from %llu to %llu\n",
-                  (unsigned long long) now.abs_value_us,
-                  (unsigned long long) keys_head->anchor.abs_value_us,
-                  (unsigned long long) keys_head->anchor.abs_value_us
-                  + duration.rel_value_us);
-    else
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Signing at %llu with key valid from %llu to %llu\n",
-                  (unsigned long long) now.abs_value_us,
-                  (unsigned long long) keys_head->anchor.abs_value_us,
-                  (unsigned long long) keys_head->anchor.abs_value_us
-                  + duration.rel_value_us);
+  GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
+  key = keys_head;
+  while ( (NULL != key) &&
+          (GNUNET_TIME_absolute_is_past (
+             GNUNET_TIME_absolute_add (key->anchor,
+                                       duration))) )
+  {
+    struct Key *nxt = key->next;
+
+    if (0 != key->rc)
+      break; /* do later */
+    GNUNET_CONTAINER_DLL_remove (keys_head,
+                                 keys_tail,
+                                 key);
+    if ( (! key->purge) &&
+         (0 != unlink (key->filename)) )
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                                "unlink",
+                                key->filename);
+    GNUNET_free (key->filename);
+    GNUNET_free (key);
+    key = nxt;
   }
-  wi = GNUNET_new (struct WorkItem);
-  wi->addr = *addr;
-  wi->addr_size = addr_size;
-  wi->key = keys_head;
-  keys_head->rc++;
-  wi->purpose = GNUNET_memdup (purpose,
-                               purpose_size);
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Received request to sign over %u bytes, queueing as %p\n",
-              (unsigned int) purpose_size,
-              wi);
-  GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
-  work_counter++;
-  GNUNET_CONTAINER_DLL_insert (work_head,
-                               work_tail,
-                               wi);
-  GNUNET_assert (0 == pthread_cond_signal (&work_cond));
-  GNUNET_assert (0 == pthread_mutex_unlock (&work_lock));
-}
-
-
-/**
- * Notify @a client about @a key becoming available.
- *
- * @param[in,out] client the client to notify; possible freed if transmission 
fails
- * @param key the key to notify @a client about
- * @return #GNUNET_OK on success
- */
-static int
-notify_client_key_add (struct Client *client,
-                       const struct Key *key)
-{
-  struct TALER_CRYPTO_EddsaKeyAvailableNotification an = {
-    .header.size = htons (sizeof (an)),
-    .header.type = htons (TALER_HELPER_EDDSA_MT_AVAIL),
-    .anchor_time = GNUNET_TIME_absolute_hton (key->anchor),
-    .duration = GNUNET_TIME_relative_hton (duration),
-    .exchange_pub = key->exchange_pub,
-    .secm_pub = smpub
-  };
-
-  TALER_exchange_secmod_eddsa_sign (&key->exchange_pub,
-                                    key->anchor,
-                                    duration,
-                                    &smpriv,
-                                    &an.secm_sig);
-  if (GNUNET_OK !=
-      transmit (&client->addr,
-                client->addr_size,
-                &an.header))
+  if (NULL == key)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Client %s must have disconnected\n",
-                client->addr.sun_path);
-    free_client (client);
-    return GNUNET_SYSERR;
+    GNUNET_break (0);
+    ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
   }
-  return GNUNET_OK;
-}
-
+  else
+  {
+    GNUNET_assert (key->rc < UINT_MAX);
+    key->rc++;
+    GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
 
-/**
- * Notify @a client about @a key being purged.
- *
- * @param[in,out] client the client to notify; possible freed if transmission 
fails
- * @param key the key to notify @a client about
- * @return #GNUNET_OK on success
- */
-static int
-notify_client_key_del (struct Client *client,
-                       const struct Key *key)
-{
-  struct TALER_CRYPTO_EddsaKeyPurgeNotification pn = {
-    .header.type = htons (TALER_HELPER_EDDSA_MT_PURGE),
-    .header.size = htons (sizeof (pn)),
-    .exchange_pub = key->exchange_pub
-  };
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_eddsa_sign_ (&key->exchange_priv.eddsa_priv,
+                                   purpose,
+                                   &sres.exchange_sig.eddsa_signature))
+      ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
+    else
+      ec = TALER_EC_NONE;
+    sres.exchange_pub = key->exchange_pub;
+    GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
+    GNUNET_assert (key->rc > 0);
+    key->rc--;
+  }
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+  if (TALER_EC_NONE != ec)
+  {
+    struct TALER_CRYPTO_EddsaSignFailure sf = {
+      .header.size = htons (sizeof (sf)),
+      .header.type = htons (TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE),
+      .ec = htonl ((uint32_t) ec)
+    };
 
-  if (GNUNET_OK !=
-      transmit (&client->addr,
-                client->addr_size,
-                &pn.header))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Client %s must have disconnected\n",
-                client->addr.sun_path);
-    free_client (client);
-    return GNUNET_SYSERR;
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Signing request %p failed, worker failed to produce 
signature\n",
+                client);
+    return TES_transmit (client->csock,
+                         &sf.header);
   }
-  return GNUNET_OK;
+  return TES_transmit (client->csock,
+                       &sres.header);
 }
 
 
@@ -717,7 +343,7 @@ notify_client_key_del (struct Client *client,
  * @param position where in the DLL will the @a key go
  * @return #GNUNET_OK on success
  */
-static int
+static enum GNUNET_GenericReturnValue
 setup_key (struct Key *key,
            struct Key *position)
 {
@@ -746,54 +372,58 @@ setup_key (struct Key *key,
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Setup fresh private key in `%s'\n",
               key->filename);
+  key->key_gen = key_gen;
   key->exchange_priv.eddsa_priv = priv;
   key->exchange_pub.eddsa_pub = pub;
   GNUNET_CONTAINER_DLL_insert_after (keys_head,
                                      keys_tail,
                                      position,
                                      key);
+  return GNUNET_OK;
+}
 
-  /* tell clients about new key */
-  {
-    struct Client *nxt;
 
-    for (struct Client *client = clients_head;
-         NULL != client;
-         client = nxt)
-    {
-      nxt = client->next;
-      if (GNUNET_OK !=
-          notify_client_key_add (client,
-                                 key))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                    "Failed to notify client about new key, client dropped\n");
-      }
-    }
-  }
-  return GNUNET_OK;
+/**
+ * The validity period of a key @a key has expired. Purge it.
+ *
+ * @param[in] key expired or revoked key to purge
+ */
+static void
+purge_key (struct Key *key)
+{
+  if (key->purge)
+    return;
+  if (0 != unlink (key->filename))
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "unlink",
+                              key->filename);
+  key->purge = true;
+  key->key_gen = key_gen;
+  GNUNET_free (key->filename);
 }
 
 
 /**
- * A client informs us that a key has been revoked.
+ * A @a client informs us that a key has been revoked.
  * Check if the key is still in use, and if so replace (!)
  * it with a fresh key.
  *
- * @param addr address of the client making the request
- * @param addr_size number of bytes in @a addr
+ * @param client the client making the request
  * @param rr the revocation request
+ * @return #GNUNET_OK on success
  */
-static void
-handle_revoke_request (const struct sockaddr_un *addr,
-                       socklen_t addr_size,
+static enum GNUNET_GenericReturnValue
+handle_revoke_request (struct TES_Client *client,
                        const struct TALER_CRYPTO_EddsaRevokeRequest *rr)
 {
   struct Key *key;
   struct Key *nkey;
 
   key = NULL;
-  for (struct Key *pos = keys_head; NULL != pos; pos = pos->next)
+  GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
+  for (struct Key *pos = keys_head;
+       NULL != pos;
+       pos = pos->next)
     if (0 == GNUNET_memcmp (&pos->exchange_pub,
                             &rr->exchange_pub))
     {
@@ -802,16 +432,18 @@ handle_revoke_request (const struct sockaddr_un *addr,
     }
   if (NULL == key)
   {
+    GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Revocation request ignored, key unknown\n");
-    return;
+    return GNUNET_OK;
   }
 
-  /* kill existing key, done first to ensure this always happens */
-  if (0 != unlink (key->filename))
-    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
-                              "unlink",
-                              key->filename);
+  key_gen++;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Revoking key %p, bumping generation to %llu\n",
+              key,
+              (unsigned long long) key_gen);
+  purge_key (key);
 
   /* Setup replacement key */
   nkey = GNUNET_new (struct Key);
@@ -820,157 +452,146 @@ handle_revoke_request (const struct sockaddr_un *addr,
       setup_key (nkey,
                  key))
   {
+    GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     GNUNET_break (0);
     GNUNET_SCHEDULER_shutdown ();
-    global_ret = 44;
-    return;
+    global_ret = EXIT_FAILURE;
+    return GNUNET_SYSERR;
   }
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+  TES_wake_clients ();
+  return GNUNET_OK;
+}
 
-  /* get rid of the old key */
-  key->purge = true;
-  GNUNET_CONTAINER_DLL_remove (keys_head,
-                               keys_tail,
-                               key);
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Revocation complete\n");
 
-  /* Tell clients this key is gone */
-  {
-    struct Client *nxt;
+/**
+ * Handle @a hdr message received from @a client.
+ *
+ * @param client the client that received the message
+ * @param hdr message that was received
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+eddsa_work_dispatch (struct TES_Client *client,
+                     const struct GNUNET_MessageHeader *hdr)
+{
+  uint16_t msize = ntohs (hdr->size);
 
-    for (struct Client *client = clients_head;
-         NULL != client;
-         client = nxt)
+  switch (ntohs (hdr->type))
+  {
+  case TALER_HELPER_EDDSA_MT_REQ_SIGN:
+    if (msize < sizeof (struct TALER_CRYPTO_EddsaSignRequest))
     {
-      nxt = client->next;
-      if (GNUNET_OK !=
-          notify_client_key_del (client,
-                                 key))
-        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                    "Failed to notify client about revoked key, client 
dropped\n");
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    return handle_sign_request (
+      client,
+      (const struct TALER_CRYPTO_EddsaSignRequest *) hdr);
+  case TALER_HELPER_EDDSA_MT_REQ_REVOKE:
+    if (msize != sizeof (struct TALER_CRYPTO_EddsaRevokeRequest))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
     }
+    return handle_revoke_request (
+      client,
+      (const struct TALER_CRYPTO_EddsaRevokeRequest *) hdr);
+  default:
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
   }
-  if (0 == key->rc)
-    free_key (key);
 }
 
 
-static void
-read_job (void *cls)
+/**
+ * Send our initial key set to @a client together with the
+ * "sync" terminator.
+ *
+ * @param client the client to inform
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+eddsa_client_init (struct TES_Client *client)
 {
-  struct Client *client = cls;
-  char buf[65536];
-  ssize_t buf_size;
-  const struct GNUNET_MessageHeader *hdr;
-  struct sockaddr_un addr;
-  socklen_t addr_size = sizeof (addr);
-
-  read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                             unix_sock,
-                                             &read_job,
-                                             NULL);
-  buf_size = GNUNET_NETWORK_socket_recvfrom (unix_sock,
-                                             buf,
-                                             sizeof (buf),
-                                             (struct sockaddr *) &addr,
-                                             &addr_size);
-  if (-1 == buf_size)
-  {
-    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
-                         "recv");
-    return;
-  }
-  if (0 == buf_size)
-  {
-    return;
-  }
-  if (buf_size < sizeof (struct GNUNET_MessageHeader))
-  {
-    GNUNET_break_op (0);
-    return;
-  }
-  hdr = (const struct GNUNET_MessageHeader *) buf;
-  if (ntohs (hdr->size) != buf_size)
+  GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
+  for (struct Key *key = keys_head;
+       NULL != key;
+       key = key->next)
   {
-    GNUNET_break_op (0);
-    free_client (client);
-    return;
+    if (GNUNET_OK !=
+        notify_client_key_add (client,
+                               key))
+    {
+      GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
   }
-  switch (ntohs (hdr->type))
+  client->key_gen = key_gen;
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
   {
-  case TALER_HELPER_EDDSA_MT_REQ_INIT:
-    if (ntohs (hdr->size) != sizeof (struct GNUNET_MessageHeader))
+    struct GNUNET_MessageHeader synced = {
+      .type = htons (TALER_HELPER_EDDSA_SYNCED),
+      .size = htons (sizeof (synced))
+    };
+
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Client %p synced\n",
+                client);
+    if (GNUNET_OK !=
+        TES_transmit (client->csock,
+                      &synced))
     {
-      GNUNET_break_op (0);
-      return;
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
     }
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Notify @a client about all changes to the keys since
+ * the last generation known to the @a client.
+ *
+ * @param client the client to notify
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+eddsa_update_client_keys (struct TES_Client *client)
+{
+  GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
+  for (struct Key *key = keys_head;
+       NULL != key;
+       key = key->next)
+  {
+    if (key->key_gen <= client->key_gen)
+      continue;
+    if (key->purge)
     {
-      struct Client *client;
-
-      client = GNUNET_new (struct Client);
-      client->addr = addr;
-      client->addr_size = addr_size;
-      GNUNET_CONTAINER_DLL_insert (clients_head,
-                                   clients_tail,
-                                   client);
-      for (struct Key *key = keys_head;
-           NULL != key;
-           key = key->next)
-      {
-        if (GNUNET_OK !=
-            notify_client_key_add (client,
-                                   key))
-        {
-          /* client died, skip the rest */
-          client = NULL;
-          break;
-        }
-      }
-      if (NULL != client)
+      if (GNUNET_OK !=
+          notify_client_key_del (client,
+                                 key))
       {
-        struct GNUNET_MessageHeader synced = {
-          .type = htons (TALER_HELPER_EDDSA_SYNCED),
-          .size = htons (sizeof (synced))
-        };
-
-        if (GNUNET_OK !=
-            transmit (&client->addr,
-                      client->addr_size,
-                      &synced))
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                      "Client %s must have disconnected\n",
-                      client->addr.sun_path);
-          free_client (client);
-        }
+        GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+        return GNUNET_SYSERR;
       }
     }
-    break;
-  case TALER_HELPER_EDDSA_MT_REQ_SIGN:
-    if (ntohs (hdr->size) < sizeof (struct TALER_CRYPTO_EddsaSignRequest))
-    {
-      GNUNET_break_op (0);
-      return;
-    }
-    handle_sign_request (&addr,
-                         addr_size,
-                         (const struct TALER_CRYPTO_EddsaSignRequest *) buf);
-    break;
-  case TALER_HELPER_EDDSA_MT_REQ_REVOKE:
-    if (ntohs (hdr->size) != sizeof (struct TALER_CRYPTO_EddsaRevokeRequest))
+    else
     {
-      GNUNET_break_op (0);
-      return;
+      if (GNUNET_OK !=
+          notify_client_key_add (client,
+                                 key))
+      {
+        GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+        return GNUNET_SYSERR;
+      }
     }
-    handle_revoke_request (&addr,
-                           addr_size,
-                           (const struct
-                            TALER_CRYPTO_EddsaRevokeRequest *) buf);
-    break;
-  default:
-    GNUNET_break_op (0);
-    return;
   }
+  client->key_gen = key_gen;
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+  return GNUNET_OK;
 }
 
 
@@ -979,7 +600,7 @@ read_job (void *cls)
  *
  * @return #GNUNET_OK on success
  */
-static int
+static enum GNUNET_GenericReturnValue
 create_key (void)
 {
   struct Key *key;
@@ -1010,7 +631,7 @@ create_key (void)
     GNUNET_break (0);
     GNUNET_free (key);
     GNUNET_SCHEDULER_shutdown ();
-    global_ret = 42;
+    global_ret = EXIT_FAILURE;
     return GNUNET_SYSERR;
   }
   return GNUNET_OK;
@@ -1039,55 +660,6 @@ key_action_time (void)
 }
 
 
-/**
- * The validity period of a key @a key has expired. Purge it.
- *
- * @param[in] key expired key to purge and free
- */
-static void
-purge_key (struct Key *key)
-{
-  struct Client *nxt;
-
-  for (struct Client *client = clients_head;
-       NULL != client;
-       client = nxt)
-  {
-    nxt = client->next;
-    if (GNUNET_OK !=
-        notify_client_key_del (client,
-                               key))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Failed to notify client about purged key, client 
dropped\n");
-    }
-  }
-  GNUNET_CONTAINER_DLL_remove (keys_head,
-                               keys_tail,
-                               key);
-  if (0 != unlink (key->filename))
-  {
-    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
-                              "unlink",
-                              key->filename);
-  }
-  else
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Purged expired private key `%s'\n",
-                key->filename);
-  }
-  GNUNET_free (key->filename);
-  if (0 != key->rc)
-  {
-    /* delay until all signing threads are done with this key */
-    key->purge = true;
-    return;
-  }
-  GNUNET_free (key);
-}
-
-
 /**
  * Create new keys and expire ancient keys.
  *
@@ -1096,35 +668,52 @@ purge_key (struct Key *key)
 static void
 update_keys (void *cls)
 {
-  (void) cls;
+  bool wake = false;
 
+  (void) cls;
   keygen_task = NULL;
+  GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
   /* create new keys */
   while ( (NULL == keys_tail) ||
-          (0 ==
-           GNUNET_TIME_absolute_get_remaining (
-             GNUNET_TIME_absolute_subtract (
-               GNUNET_TIME_absolute_subtract (
-                 GNUNET_TIME_absolute_add (keys_tail->anchor,
-                                           duration),
-                 lookahead_sign),
-               overlap_duration)).rel_value_us) )
+          GNUNET_TIME_absolute_is_past (
+            GNUNET_TIME_absolute_subtract (
+              GNUNET_TIME_absolute_subtract (
+                GNUNET_TIME_absolute_add (keys_tail->anchor,
+                                          duration),
+                lookahead_sign),
+              overlap_duration)) )
   {
+    if (! wake)
+    {
+      key_gen++;
+      wake = true;
+    }
     if (GNUNET_OK !=
         create_key ())
     {
+      GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
       GNUNET_break (0);
+      global_ret = EXIT_FAILURE;
       GNUNET_SCHEDULER_shutdown ();
       return;
     }
   }
   /* remove expired keys */
   while ( (NULL != keys_head) &&
-          (0 ==
-           GNUNET_TIME_absolute_get_remaining
-             (GNUNET_TIME_absolute_add (keys_head->anchor,
-                                        duration)).rel_value_us) )
+          GNUNET_TIME_absolute_is_past (
+            GNUNET_TIME_absolute_add (keys_head->anchor,
+                                      duration)))
+  {
+    if (! wake)
+    {
+      key_gen++;
+      wake = true;
+    }
     purge_key (keys_head);
+  }
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+  if (wake)
+    TES_wake_clients ();
   keygen_task = GNUNET_SCHEDULER_add_at (key_action_time (),
                                          &update_keys,
                                          NULL);
@@ -1137,8 +726,9 @@ update_keys (void *cls)
  * @param filename name of the file we are parsing, for logging
  * @param buf key material
  * @param buf_size number of bytes in @a buf
+ * @return #GNUNET_OK on success
  */
-static void
+static enum GNUNET_GenericReturnValue
 parse_key (const char *filename,
            const void *buf,
            size_t buf_size)
@@ -1155,7 +745,7 @@ parse_key (const char *filename,
   {
     /* File in a directory without '/' in the name, this makes no sense. */
     GNUNET_break (0);
-    return;
+    return GNUNET_SYSERR;
   }
   anchor_s++;
   if (1 != sscanf (anchor_s,
@@ -1167,7 +757,7 @@ parse_key (const char *filename,
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Filename `%s' invalid for key file, skipping\n",
                 filename);
-    return;
+    return GNUNET_SYSERR;
   }
   anchor.abs_value_us = anchor_ll * GNUNET_TIME_UNIT_SECONDS.rel_value_us;
   if (anchor_ll != anchor.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us)
@@ -1176,7 +766,7 @@ parse_key (const char *filename,
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Filename `%s' invalid for key file, skipping\n",
                 filename);
-    return;
+    return GNUNET_SYSERR;
   }
   if (buf_size != sizeof (priv))
   {
@@ -1184,7 +774,7 @@ parse_key (const char *filename,
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "File `%s' is malformed, skipping\n",
                 filename);
-    return;
+    return GNUNET_SYSERR;
   }
   memcpy (&priv,
           buf,
@@ -1197,11 +787,13 @@ parse_key (const char *filename,
 
     GNUNET_CRYPTO_eddsa_key_get_public (&priv,
                                         &pub);
+    GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
     key = GNUNET_new (struct Key);
     key->exchange_priv.eddsa_priv = priv;
     key->exchange_pub.eddsa_pub = pub;
     key->anchor = anchor;
     key->filename = GNUNET_strdup (filename);
+    key->key_gen = key_gen;
     before = NULL;
     for (struct Key *pos = keys_head;
          NULL != pos;
@@ -1215,10 +807,12 @@ parse_key (const char *filename,
                                        keys_tail,
                                        before,
                                        key);
+    GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Imported key from `%s'\n",
                 filename);
   }
+  return GNUNET_OK;
 }
 
 
@@ -1228,7 +822,7 @@ parse_key (const char *filename,
  * @param cls NULL
  * @param filename name of a file in the directory
  */
-static int
+static enum GNUNET_GenericReturnValue
 import_key (void *cls,
             const char *filename)
 {
@@ -1326,9 +920,9 @@ import_key (void *cls,
     GNUNET_DISK_file_close (fh);
     return GNUNET_OK;
   }
-  parse_key (filename,
-             ptr,
-             (size_t) sbuf.st_size);
+  (void) parse_key (filename,
+                    ptr,
+                    (size_t) sbuf.st_size);
   GNUNET_DISK_file_unmap (map);
   GNUNET_DISK_file_close (fh);
   return GNUNET_OK;
@@ -1336,15 +930,16 @@ import_key (void *cls,
 
 
 /**
- * Load the various duration values from #kcfg.
+ * Load the various duration values from @a kcfg.
  *
+ * @param cfg configuration to use
  * @return #GNUNET_OK on success
  */
-static int
-load_durations (void)
+static enum GNUNET_GenericReturnValue
+load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_time (kcfg,
+      GNUNET_CONFIGURATION_get_value_time (cfg,
                                            "taler-exchange-secmod-eddsa",
                                            "OVERLAP_DURATION",
                                            &overlap_duration))
@@ -1355,7 +950,7 @@ load_durations (void)
     return GNUNET_SYSERR;
   }
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_time (kcfg,
+      GNUNET_CONFIGURATION_get_value_time (cfg,
                                            "taler-exchange-secmod-eddsa",
                                            "DURATION",
                                            &duration))
@@ -1368,7 +963,7 @@ load_durations (void)
   GNUNET_TIME_round_rel (&overlap_duration);
 
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_time (kcfg,
+      GNUNET_CONFIGURATION_get_value_time (cfg,
                                            "taler-exchange-secmod-eddsa",
                                            "LOOKAHEAD_SIGN",
                                            &lookahead_sign))
@@ -1392,51 +987,12 @@ static void
 do_shutdown (void *cls)
 {
   (void) cls;
-  if (NULL != read_task)
-  {
-    GNUNET_SCHEDULER_cancel (read_task);
-    read_task = NULL;
-  }
-  if (NULL != unix_sock)
-  {
-    GNUNET_break (GNUNET_OK ==
-                  GNUNET_NETWORK_socket_close (unix_sock));
-    unix_sock = NULL;
-  }
-  if (0 != unlink (unixpath))
-  {
-    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
-                              "unlink",
-                              unixpath);
-  }
-  GNUNET_free (unixpath);
+  TES_listen_stop ();
   if (NULL != keygen_task)
   {
     GNUNET_SCHEDULER_cancel (keygen_task);
     keygen_task = NULL;
   }
-  if (NULL != done_task)
-  {
-    GNUNET_SCHEDULER_cancel (done_task);
-    done_task = NULL;
-  }
-  /* shut down worker threads */
-  if (NULL != workers)
-  {
-    GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
-    in_shutdown = true;
-    GNUNET_assert (0 == pthread_cond_broadcast (&work_cond));
-    GNUNET_assert (0 == pthread_mutex_unlock (&work_lock));
-    for (unsigned int i = 0; i<num_workers; i++)
-      GNUNET_assert (0 == pthread_join (workers[i],
-                                        NULL));
-  }
-  if (NULL != done_signal)
-  {
-    GNUNET_break (GNUNET_OK ==
-                  GNUNET_NETWORK_socket_close (done_signal));
-    done_signal = NULL;
-  }
 }
 
 
@@ -1454,10 +1010,15 @@ run (void *cls,
      const char *cfgfile,
      const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
+  static struct TES_Callbacks cb = {
+    .dispatch = eddsa_work_dispatch,
+    .updater = eddsa_update_client_keys,
+    .init = eddsa_client_init
+  };
+
   (void) cls;
   (void) args;
   (void) cfgfile;
-  kcfg = cfg;
   if (now.abs_value_us != now_tmp.abs_value_us)
   {
     /* The user gave "--now", use it! */
@@ -1469,48 +1030,14 @@ run (void *cls,
     now = GNUNET_TIME_absolute_get ();
   }
   GNUNET_TIME_round_abs (&now);
-
-  {
-    char *pfn;
-
-    if (GNUNET_OK !=
-        GNUNET_CONFIGURATION_get_value_filename (kcfg,
-                                                 "taler-exchange-secmod-eddsa",
-                                                 "SM_PRIV_KEY",
-                                                 &pfn))
-    {
-      GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                                 "taler-exchange-secmod-eddsa",
-                                 "SM_PRIV_KEY");
-      global_ret = 1;
-      return;
-    }
-    if (GNUNET_SYSERR ==
-        GNUNET_CRYPTO_eddsa_key_from_file (pfn,
-                                           GNUNET_YES,
-                                           &smpriv.eddsa_priv))
-    {
-      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                                 "taler-exchange-secmod-rsa",
-                                 "SM_PRIV_KEY",
-                                 "Could not use file to persist private key");
-      GNUNET_free (pfn);
-      global_ret = 1;
-      return;
-    }
-    GNUNET_free (pfn);
-    GNUNET_CRYPTO_eddsa_key_get_public (&smpriv.eddsa_priv,
-                                        &smpub.eddsa_pub);
-  }
-
   if (GNUNET_OK !=
-      load_durations ())
+      load_durations (cfg))
   {
-    global_ret = 1;
+    global_ret = EXIT_NOTCONFIGURED;
     return;
   }
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (kcfg,
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
                                                "taler-exchange-secmod-eddsa",
                                                "KEY_DIR",
                                                &keydir))
@@ -1518,131 +1045,29 @@ run (void *cls,
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "taler-exchange-secmod-eddsa",
                                "KEY_DIR");
-    global_ret = 1;
+    global_ret = EXIT_NOTCONFIGURED;
     return;
   }
-
-  /* Create client directory and set permissions. */
-  {
-    char *client_dir;
-
-    if (GNUNET_OK !=
-        GNUNET_CONFIGURATION_get_value_filename (kcfg,
-                                                 "taler-exchange-secmod-eddsa",
-                                                 "CLIENT_DIR",
-                                                 &client_dir))
-    {
-      GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+  global_ret = TES_listen_start (cfg,
                                  "taler-exchange-secmod-eddsa",
-                                 "CLIENT_DIR");
-      global_ret = 3;
-      return;
-    }
-
-    if (GNUNET_OK != GNUNET_DISK_directory_create (client_dir))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Can't create client directory (%s)\n",
-                  client_dir);
-      global_ret = 3;
-      return;
-    }
-    /* Set sticky group bit, so that clients will be writeable by the current 
service. */
-    if (0 != chmod (client_dir,
-                    S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP
-                    | S_ISGID))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Can't set permissions for client directory (%s)\n",
-                  client_dir);
-      global_ret = 3;
-      return;
-    }
-
-    GNUNET_free (client_dir);
-  }
-
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (kcfg,
-                                               "taler-exchange-secmod-eddsa",
-                                               "UNIXPATH",
-                                               &unixpath))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "taler-exchange-secmod-eddsa",
-                               "UNIXPATH");
-    global_ret = 3;
-    return;
-  }
-
-  GNUNET_assert (NULL != unixpath);
-  unix_sock = TES_open_socket (unixpath);
-
-  if (NULL == unix_sock)
-  {
-    GNUNET_free (unixpath);
-    global_ret = 2;
+                                 &cb);
+  if (0 != global_ret)
     return;
-  }
-
   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
                                  NULL);
-
   /* Load keys */
   GNUNET_break (GNUNET_OK ==
                 GNUNET_DISK_directory_create (keydir));
   GNUNET_DISK_directory_scan (keydir,
                               &import_key,
                               NULL);
-  /* start job to accept incoming requests on 'sock' */
-  read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                             unix_sock,
-                                             &read_job,
-                                             NULL);
-  /* start job to keep keys up-to-date; MUST be run before the #read_task,
+
+  /* start job to keep keys up-to-date; MUST be run before the #listen_task,
      hence with priority. */
   keygen_task = GNUNET_SCHEDULER_add_with_priority (
     GNUNET_SCHEDULER_PRIORITY_URGENT,
     &update_keys,
     NULL);
-
-  /* start job to handle completed work */
-  {
-    int fd;
-
-    fd = eventfd (0,
-                  EFD_NONBLOCK | EFD_CLOEXEC);
-    if (-1 == fd)
-    {
-      GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
-                           "eventfd");
-      global_ret = 6;
-      GNUNET_SCHEDULER_shutdown ();
-      return;
-    }
-    done_signal = GNUNET_NETWORK_socket_box_native (fd);
-  }
-  done_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                             done_signal,
-                                             &handle_done,
-                                             NULL);
-
-  /* start crypto workers */
-  if (0 == num_workers)
-    num_workers = sysconf (_SC_NPROCESSORS_CONF);
-  if (0 == num_workers)
-    num_workers = 1;
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Starting %u crypto workers\n",
-              num_workers);
-  workers = GNUNET_new_array (num_workers,
-                              pthread_t);
-  for (unsigned int i = 0; i<num_workers; i++)
-    GNUNET_assert (0 ==
-                   pthread_create (&workers[i],
-                                   NULL,
-                                   &sign_worker,
-                                   NULL));
 }
 
 
@@ -1660,11 +1085,6 @@ main (int argc,
   struct GNUNET_GETOPT_CommandLineOption options[] = {
     GNUNET_GETOPT_option_timetravel ('T',
                                      "timetravel"),
-    GNUNET_GETOPT_option_uint ('p',
-                               "parallelism",
-                               "NUM_WORKERS",
-                               "number of worker threads to use",
-                               &num_workers),
     GNUNET_GETOPT_option_absolute_time ('t',
                                         "time",
                                         "TIMESTAMP",
@@ -1672,7 +1092,7 @@ main (int argc,
                                         &now_tmp),
     GNUNET_GETOPT_OPTION_END
   };
-  int ret;
+  enum GNUNET_GenericReturnValue ret;
 
   /* Restrict permissions for the key files that we create. */
   (void) umask (S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH);
@@ -1689,8 +1109,8 @@ main (int argc,
                             &run,
                             NULL);
   if (GNUNET_NO == ret)
-    return 0;
+    return EXIT_SUCCESS;
   if (GNUNET_SYSERR == ret)
-    return 1;
+    return EXIT_INVALIDARGUMENT;
   return global_ret;
 }
diff --git a/src/util/taler-exchange-secmod-rsa.c 
b/src/util/taler-exchange-secmod-rsa.c
index 6bca58f8..1884ca98 100644
--- a/src/util/taler-exchange-secmod-rsa.c
+++ b/src/util/taler-exchange-secmod-rsa.c
@@ -25,8 +25,7 @@
  * - 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.
+ *   threads for crypto-workers which do the signing in parallel, one per 
client.
  * - 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
@@ -41,6 +40,7 @@
 #include "taler_error_codes.h"
 #include "taler_signatures.h"
 #include "secmod_common.h"
+#include <poll.h>
 
 
 /**
@@ -88,13 +88,18 @@ struct DenominationKey
   /**
    * Hash of this denomination's public key.
    */
-  struct TALER_DenominationHash h_denom_pub;
+  struct GNUNET_HashCode h_denom_pub;
 
   /**
    * Time at which this key is supposed to become valid.
    */
   struct GNUNET_TIME_Absolute anchor;
 
+  /**
+   * Generation when this key was created or revoked.
+   */
+  uint64_t key_gen;
+
   /**
    * Reference counter. Counts the number of threads that are
    * using this key at this time.
@@ -154,111 +159,11 @@ struct Denomination
 };
 
 
-/**
- * Actively worked on client request.
- */
-struct WorkItem;
-
-
-/**
- * Information we keep for a client connected to us.
- */
-struct Client
-{
-
-  /**
-   * Kept in a DLL.
-   */
-  struct Client *next;
-
-  /**
-   * Kept in a DLL.
-   */
-  struct Client *prev;
-
-  /**
-   * Client address.
-   */
-  struct sockaddr_un addr;
-
-  /**
-   * Number of bytes used in @e addr.
-   */
-  socklen_t addr_size;
-
-};
-
-
-struct WorkItem
-{
-
-  /**
-   * Kept in a DLL.
-   */
-  struct WorkItem *next;
-
-  /**
-   * Kept in a DLL.
-   */
-  struct WorkItem *prev;
-
-  /**
-   * Key to be used for this operation.
-   */
-  struct DenominationKey *dk;
-
-  /**
-   * Signature over @e blinded_msg using @e dk. Result of doing the
-   * work. Initially zero.
-   */
-  struct TALER_BlindedDenominationSignature denom_sig;
-
-  /**
-   * Coin_ev value to sign.
-   */
-  void *blinded_msg;
-
-  /**
-   * Number of bytes in #blinded_msg.
-   */
-  size_t blinded_msg_size;
-
-  /**
-   * Client address.
-   */
-  struct sockaddr_un addr;
-
-  /**
-   * Number of bytes used in @e addr.
-   */
-  socklen_t addr_size;
-
-};
-
-
 /**
  * Return value from main().
  */
 static int global_ret;
 
-/**
- * Private key of this security module. Used to sign denomination key
- * announcements.
- */
-static struct TALER_SecurityModulePrivateKeyP smpriv;
-
-/**
- * Public key of this security module.
- */
-static struct TALER_SecurityModulePublicKeyP smpub;
-
-/**
- * Number of worker threads to use. Default (0) is to use one per CPU core
- * available.
- * Length of the #workers array.
- */
-static unsigned int num_workers;
-
 /**
  * Time when the key update is executed.
  * Either the actual current time, or a pretended time.
@@ -271,11 +176,6 @@ static struct GNUNET_TIME_Absolute now;
  */
 static struct GNUNET_TIME_Absolute now_tmp;
 
-/**
- * Handle to the exchange's configuration
- */
-static const struct GNUNET_CONFIGURATION_Handle *kcfg;
-
 /**
  * Where do we store the keys?
  */
@@ -309,336 +209,119 @@ static struct Denomination *denom_tail;
  */
 static struct GNUNET_CONTAINER_MultiHashMap *keys;
 
-/**
- * Our listen socket.
- */
-static struct GNUNET_NETWORK_Handle *unix_sock;
-
-/**
- * Path where we are listening.
- */
-static char *unixpath;
-
-/**
- * Task run to accept new inbound connections.
- */
-static struct GNUNET_SCHEDULER_Task *read_task;
-
 /**
  * Task run to generate new keys.
  */
 static struct GNUNET_SCHEDULER_Task *keygen_task;
 
-/**
- * Head of DLL of clients connected to us.
- */
-static struct Client *clients_head;
 
 /**
- * Tail of DLL of clients connected to us.
+ * Lock for the keys queue.
  */
-static struct Client *clients_tail;
+static pthread_mutex_t keys_lock;
 
 /**
- * Head of DLL with pending signing operations.
+ * Current key generation.
  */
-static struct WorkItem *work_head;
+static uint64_t key_gen;
 
-/**
- * Tail of DLL with pending signing operations.
- */
-static struct WorkItem *work_tail;
 
 /**
- * Lock for the work queue.
- */
-static pthread_mutex_t work_lock;
-
-/**
- * Condition variable for the semaphore of the work queue.
- */
-static pthread_cond_t work_cond = PTHREAD_COND_INITIALIZER;
-
-/**
- * Number of items in the work queue. Also used as the semaphore counter.
- */
-static unsigned long long work_counter;
-
-/**
- * Head of DLL with completed signing operations.
- */
-static struct WorkItem *done_head;
-
-/**
- * Tail of DLL with completed signing operations.
- */
-static struct WorkItem *done_tail;
-
-/**
- * Lock for the done queue.
- */
-static pthread_mutex_t done_lock;
-
-/**
- * Task waiting for work to be done.
- */
-static struct GNUNET_SCHEDULER_Task *done_task;
-
-/**
- * Signal used by threads to notify the #done_task that they
- * completed work that is now in the done queue.
- */
-static struct GNUNET_NETWORK_Handle *done_signal;
-
-/**
- * Set once we are in shutdown and workers should terminate.
- */
-static volatile bool in_shutdown;
-
-/**
- * Array of #num_workers sign_worker() threads.
- */
-static pthread_t *workers;
-
-
-/**
- * Main function of a worker thread that signs.
- *
- * @param cls NULL
- * @return NULL
- */
-static void *
-sign_worker (void *cls)
-{
-  (void) cls;
-  GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
-  while (! in_shutdown)
-  {
-    struct WorkItem *wi;
-
-    while (NULL != (wi = work_head))
-    {
-      /* take work from queue */
-      GNUNET_CONTAINER_DLL_remove (work_head,
-                                   work_tail,
-                                   wi);
-      work_counter--;
-      GNUNET_assert (0 == pthread_mutex_unlock (&work_lock));
-      GNUNET_break (GNUNET_OK ==
-                    TALER_denom_sign_blinded (&wi->denom_sig,
-                                              &wi->dk->denom_priv,
-                                              wi->blinded_msg,
-                                              wi->blinded_msg_size));
-      /* put completed work into done queue */
-      GNUNET_assert (0 == pthread_mutex_lock (&done_lock));
-      GNUNET_CONTAINER_DLL_insert (done_head,
-                                   done_tail,
-                                   wi);
-      GNUNET_assert (0 == pthread_mutex_unlock (&done_lock));
-      {
-        uint64_t val = GNUNET_htonll (1);
-
-        /* raise #done_signal */
-        if (sizeof(val) !=
-            write (GNUNET_NETWORK_get_fd (done_signal),
-                   &val,
-                   sizeof (val)))
-          GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
-                               "write(eventfd)");
-      }
-      GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
-    }
-    if (in_shutdown)
-      break;
-    /* queue is empty, wait for work */
-    GNUNET_assert (0 ==
-                   pthread_cond_wait (&work_cond,
-                                      &work_lock));
-  }
-  GNUNET_assert (0 ==
-                 pthread_mutex_unlock (&work_lock));
-  return NULL;
-}
-
-
-/**
- * Free @a client, releasing all (remaining) state.
- *
- * @param[in] client data to free
- */
-static void
-free_client (struct Client *client)
-{
-  GNUNET_CONTAINER_DLL_remove (clients_head,
-                               clients_tail,
-                               client);
-  GNUNET_free (client);
-}
-
-
-/**
- * Function run to read incoming requests from a client.
- *
- * @param cls the `struct Client`
- */
-static void
-read_job (void *cls);
-
-
-/**
- * Free @a dk. It must already have been removed from #keys and the
- * denomination's DLL.
- *
- * @param[in] dk key to free
- */
-static void
-free_dk (struct DenominationKey *dk)
-{
-  GNUNET_free (dk->filename);
-  TALER_denom_priv_free (&dk->denom_priv);
-  TALER_denom_pub_free (&dk->denom_pub);
-  GNUNET_free (dk);
-}
-
-
-/**
- * Send a message starting with @a hdr to @a client.  We expect that
- * the client is mostly able to handle everything at whatever speed
- * we have (after all, the crypto should be the slow part). However,
- * especially on startup when we send all of our keys, it is possible
- * that the client cannot keep up. In that case, we throttle when
- * sending fails. This does not work with poll() as we cannot specify
- * the sendto() target address with poll(). So we nanosleep() instead.
+ * Notify @a client about @a dk becoming available.
  *
- * @param addr address where to send the message
- * @param addr_size number of bytes in @a addr
- * @param hdr beginning of the message, length indicated in size field
+ * @param[in,out] client the client to notify; possible freed if transmission 
fails
+ * @param dk the key to notify @a client about
  * @return #GNUNET_OK on success
  */
-static int
-transmit (const struct sockaddr_un *addr,
-          socklen_t addr_size,
-          const struct GNUNET_MessageHeader *hdr)
+static enum GNUNET_GenericReturnValue
+notify_client_dk_add (struct TES_Client *client,
+                      const struct DenominationKey *dk)
 {
-  for (unsigned int i = 0; i<100; i++)
+  struct Denomination *denom = dk->denom;
+  size_t nlen = strlen (denom->section) + 1;
+  struct TALER_CRYPTO_RsaKeyAvailableNotification *an;
+  size_t buf_len;
+  void *buf;
+  void *p;
+  size_t tlen;
+
+  buf_len = GNUNET_CRYPTO_rsa_public_key_encode (dk->denom_pub.rsa_public_key,
+                                                 &buf);
+  GNUNET_assert (buf_len < UINT16_MAX);
+  GNUNET_assert (nlen < UINT16_MAX);
+  tlen = buf_len + nlen + sizeof (*an);
+  GNUNET_assert (tlen < UINT16_MAX);
+  an = GNUNET_malloc (tlen);
+  an->header.size = htons ((uint16_t) tlen);
+  an->header.type = htons (TALER_HELPER_RSA_MT_AVAIL);
+  an->pub_size = htons ((uint16_t) buf_len);
+  an->section_name_len = htons ((uint16_t) nlen);
+  an->anchor_time = GNUNET_TIME_absolute_hton (dk->anchor);
+  an->duration_withdraw = GNUNET_TIME_relative_hton (denom->duration_withdraw);
+  TALER_exchange_secmod_rsa_sign (&dk->h_denom_pub,
+                                  denom->section,
+                                  dk->anchor,
+                                  denom->duration_withdraw,
+                                  &TES_smpriv,
+                                  &an->secm_sig);
+  an->secm_pub = TES_smpub;
+  p = (void *) &an[1];
+  memcpy (p,
+          buf,
+          buf_len);
+  GNUNET_free (buf);
+  memcpy (p + buf_len,
+          denom->section,
+          nlen);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Sending RSA denomination key %s (%s)\n",
+              GNUNET_h2s (&dk->h_denom_pub),
+              denom->section);
+  if (GNUNET_OK !=
+      TES_transmit (client->csock,
+                    &an->header))
   {
-    ssize_t ret = sendto (GNUNET_NETWORK_get_fd (unix_sock),
-                          hdr,
-                          ntohs (hdr->size),
-                          0 /* no flags => blocking! */,
-                          (const struct sockaddr *) addr,
-                          addr_size);
-    if ( (-1 == ret) &&
-         (EAGAIN == errno) )
-    {
-      /* _Maybe_ with blocking sendto(), this should no
-         longer be needed; still keeping it just in case. */
-      /* Wait a bit, in case client is just too slow */
-      struct timespec req = {
-        .tv_sec = 0,
-        .tv_nsec = 1000
-      };
-      nanosleep (&req, NULL);
-      continue;
-    }
-    if (ret == ntohs (hdr->size))
-      return GNUNET_OK;
-    if (ret != ntohs (hdr->size))
-      break;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Client %p must have disconnected\n",
+                client);
+    GNUNET_free (an);
+    return GNUNET_SYSERR;
   }
-  GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
-                       "sendto");
-  return GNUNET_SYSERR;
+  GNUNET_free (an);
+  return GNUNET_OK;
 }
 
 
 /**
- * Process completed tasks that are in the #done_head queue, sending
- * the result back to the client (and resuming the client).
+ * Notify @a client about @a dk being purged.
  *
- * @param cls NULL
+ * @param[in,out] client the client to notify; possible freed if transmission 
fails
+ * @param dk the key to notify @a client about
+ * @return #GNUNET_OK on success
  */
-static void
-handle_done (void *cls)
+static enum GNUNET_GenericReturnValue
+notify_client_dk_del (struct TES_Client *client,
+                      const struct DenominationKey *dk)
 {
-  uint64_t data;
-  (void) cls;
+  struct TALER_CRYPTO_RsaKeyPurgeNotification pn = {
+    .header.type = htons (TALER_HELPER_RSA_MT_PURGE),
+    .header.size = htons (sizeof (pn)),
+    .h_denom_pub = dk->h_denom_pub
+  };
 
-  /* consume #done_signal */
-  if (sizeof (data) !=
-      read (GNUNET_NETWORK_get_fd (done_signal),
-            &data,
-            sizeof (data)))
-    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
-                         "read(eventfd)");
-  done_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                             done_signal,
-                                             &handle_done,
-                                             NULL);
-  GNUNET_assert (0 == pthread_mutex_lock (&done_lock));
-  while (NULL != done_head)
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Sending RSA denomination expiration %s\n",
+              GNUNET_h2s (&dk->h_denom_pub));
+  if (GNUNET_OK !=
+      TES_transmit (client->csock,
+                    &pn.header))
   {
-    struct WorkItem *wi = done_head;
-
-    GNUNET_CONTAINER_DLL_remove (done_head,
-                                 done_tail,
-                                 wi);
-    GNUNET_assert (0 == pthread_mutex_unlock (&done_lock));
-    if (TALER_DENOMINATION_INVALID == wi->denom_sig.cipher)
-    {
-      struct TALER_CRYPTO_SignFailure sf = {
-        .header.size = htons (sizeof (sf)),
-        .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE),
-        .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE)
-      };
-
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Signing request failed, worker failed to produce 
signature\n");
-      (void) transmit (&wi->addr,
-                       wi->addr_size,
-                       &sf.header);
-    }
-    else
-    {
-      struct TALER_CRYPTO_SignResponse *sr;
-      void *buf;
-      size_t buf_size;
-      size_t tsize;
-
-      buf_size = GNUNET_CRYPTO_rsa_signature_encode (
-        wi->denom_sig.details.blinded_rsa_signature,
-        &buf);
-      TALER_blinded_denom_sig_free (&wi->denom_sig);
-      tsize = sizeof (*sr) + buf_size;
-      GNUNET_assert (tsize < UINT16_MAX);
-      sr = GNUNET_malloc (tsize);
-      sr->header.size = htons (tsize);
-      sr->header.type = htons (TALER_HELPER_RSA_MT_RES_SIGNATURE);
-      memcpy (&sr[1],
-              buf,
-              buf_size);
-      GNUNET_free (buf);
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Sending RSA signature\n");
-      (void) transmit (&wi->addr,
-                       wi->addr_size,
-                       &sr->header);
-      GNUNET_free (sr);
-    }
-    {
-      struct DenominationKey *dk = wi->dk;
-
-      dk->rc--;
-      if ( (0 == dk->rc) &&
-           (dk->purge) )
-        free_dk (dk);
-    }
-    GNUNET_free (wi->blinded_msg);
-    GNUNET_free (wi);
-    GNUNET_assert (0 == pthread_mutex_lock (&done_lock));
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Client %p must have disconnected\n",
+                client);
+    return GNUNET_SYSERR;
   }
-  GNUNET_assert (0 == pthread_mutex_unlock (&done_lock));
-
+  return GNUNET_OK;
 }
 
 
@@ -647,22 +330,22 @@ handle_done (void *cls)
  * signature using the respective key and return the result to
  * the client.
  *
- * @param addr address of the client making the request
- * @param addr_size number of bytes in @a addr
+ * @param client the client making the request
  * @param sr the request details
+ * @return #GNUNET_OK on success
  */
-static void
-handle_sign_request (const struct sockaddr_un *addr,
-                     socklen_t addr_size,
+static enum GNUNET_GenericReturnValue
+handle_sign_request (struct TES_Client *client,
                      const struct TALER_CRYPTO_SignRequest *sr)
 {
   struct DenominationKey *dk;
-  struct WorkItem *wi;
   const void *blinded_msg = &sr[1];
   size_t blinded_msg_size = ntohs (sr->header.size) - sizeof (*sr);
+  struct GNUNET_CRYPTO_RsaSignature *rsa_signature;
 
+  GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
   dk = GNUNET_CONTAINER_multihashmap_get (keys,
-                                          &sr->h_denom_pub.hash);
+                                          &sr->h_denom_pub);
   if (NULL == dk)
   {
     struct TALER_CRYPTO_SignFailure sf = {
@@ -671,13 +354,12 @@ handle_sign_request (const struct sockaddr_un *addr,
       .ec = htonl (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN)
     };
 
+    GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Signing request failed, denomination key %s unknown\n",
-                GNUNET_h2s (&sr->h_denom_pub.hash));
-    (void) transmit (addr,
-                     addr_size,
-                     &sf.header);
-    return;
+                GNUNET_h2s (&sr->h_denom_pub));
+    return TES_transmit (client->csock,
+                         &sf.header);
   }
   if (0 !=
       GNUNET_TIME_absolute_get_remaining (dk->anchor).rel_value_us)
@@ -689,141 +371,68 @@ handle_sign_request (const struct sockaddr_un *addr,
       .ec = htonl (TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY)
     };
 
+    GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Signing request failed, denomination key %s is not yet 
valid\n",
-                GNUNET_h2s (&sr->h_denom_pub.hash));
-    (void) transmit (addr,
-                     addr_size,
-                     &sf.header);
-    return;
+                GNUNET_h2s (&sr->h_denom_pub));
+    return TES_transmit (client->csock,
+                         &sf.header);
   }
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Received request to sign over %u bytes with key %s\n",
               (unsigned int) blinded_msg_size,
-              GNUNET_h2s (&sr->h_denom_pub.hash));
-  wi = GNUNET_new (struct WorkItem);
-  wi->addr = *addr;
-  wi->addr_size = addr_size;
-  wi->dk = dk;
+              GNUNET_h2s (&sr->h_denom_pub));
+  GNUNET_assert (dk->rc < UINT_MAX);
   dk->rc++;
-  wi->blinded_msg = GNUNET_memdup (blinded_msg,
-                                   blinded_msg_size);
-  wi->blinded_msg_size = blinded_msg_size;
-  GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
-  work_counter++;
-  GNUNET_CONTAINER_DLL_insert (work_head,
-                               work_tail,
-                               wi);
-  GNUNET_assert (0 == pthread_cond_signal (&work_cond));
-  GNUNET_assert (0 == pthread_mutex_unlock (&work_lock));
-}
-
-
-/**
- * Notify @a client about @a dk becoming available.
- *
- * @param[in,out] client the client to notify; possible freed if transmission 
fails
- * @param dk the key to notify @a client about
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-notify_client_dk_add (struct Client *client,
-                      const struct DenominationKey *dk)
-{
-  struct Denomination *denom = dk->denom;
-  size_t nlen = strlen (denom->section) + 1;
-  struct TALER_CRYPTO_RsaKeyAvailableNotification *an;
-  size_t buf_len;
-  void *buf;
-  void *p;
-  size_t tlen;
-
-  buf_len = GNUNET_CRYPTO_rsa_public_key_encode (
-    dk->denom_pub.details.rsa_public_key,
-    &buf);
-  GNUNET_assert (buf_len < UINT16_MAX);
-  GNUNET_assert (nlen < UINT16_MAX);
-  tlen = buf_len + nlen + sizeof (*an);
-  GNUNET_assert (tlen < UINT16_MAX);
-  an = GNUNET_malloc (tlen);
-  an->header.size = htons ((uint16_t) tlen);
-  an->header.type = htons (TALER_HELPER_RSA_MT_AVAIL);
-  an->pub_size = htons ((uint16_t) buf_len);
-  an->section_name_len = htons ((uint16_t) nlen);
-  an->anchor_time = GNUNET_TIME_absolute_hton (dk->anchor);
-  an->duration_withdraw = GNUNET_TIME_relative_hton (denom->duration_withdraw);
-  TALER_exchange_secmod_denom_sign (&dk->h_denom_pub,
-                                    denom->section,
-                                    dk->anchor,
-                                    denom->duration_withdraw,
-                                    &smpriv,
-                                    &an->secm_sig);
-  an->secm_pub = smpub;
-  p = (void *) &an[1];
-  memcpy (p,
-          buf,
-          buf_len);
-  GNUNET_free (buf);
-  memcpy (p + buf_len,
-          denom->section,
-          nlen);
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+  rsa_signature
+    = GNUNET_CRYPTO_rsa_sign_blinded (dk->denom_priv.rsa_private_key,
+                                      blinded_msg,
+                                      blinded_msg_size);
+  GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
+  GNUNET_assert (dk->rc > 0);
+  dk->rc--;
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+  if (NULL == rsa_signature)
   {
-    enum GNUNET_GenericReturnValue ret = GNUNET_OK;
+    struct TALER_CRYPTO_SignFailure sf = {
+      .header.size = htons (sizeof (sf)),
+      .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE),
+      .ec = htonl (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE)
+    };
 
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Sending RSA denomination key %s (%s)\n",
-                GNUNET_h2s (&dk->h_denom_pub.hash),
-                denom->section);
-    if (GNUNET_OK !=
-        transmit (&client->addr,
-                  client->addr_size,
-                  &an->header))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Client %s must have disconnected\n",
-                  client->addr.sun_path);
-      free_client (client);
-      ret = GNUNET_SYSERR;
-    }
-    GNUNET_free (an);
-    return ret;
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Signing request failed, worker failed to produce 
signature\n");
+    return TES_transmit (client->csock,
+                         &sf.header);
   }
-}
-
-
-/**
- * Notify @a client about @a dk being purged.
- *
- * @param[in,out] client the client to notify; possible freed if transmission 
fails
- * @param dk the key to notify @a client about
- * @return #GNUNET_OK on success
- */
-static int
-notify_client_dk_del (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_denom_pub
-  };
-
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Sending RSA denomination expiration %s\n",
-              GNUNET_h2s (&dk->h_denom_pub.hash));
-  if (GNUNET_OK !=
-      transmit (&client->addr,
-                client->addr_size,
-                &pn.header))
   {
+    struct TALER_CRYPTO_SignResponse *sr;
+    void *buf;
+    size_t buf_size;
+    size_t tsize;
+    enum GNUNET_GenericReturnValue ret;
+
+    buf_size = GNUNET_CRYPTO_rsa_signature_encode (rsa_signature,
+                                                   &buf);
+    GNUNET_CRYPTO_rsa_signature_free (rsa_signature);
+    tsize = sizeof (*sr) + buf_size;
+    GNUNET_assert (tsize < UINT16_MAX);
+    sr = GNUNET_malloc (tsize);
+    sr->header.size = htons (tsize);
+    sr->header.type = htons (TALER_HELPER_RSA_MT_RES_SIGNATURE);
+    memcpy (&sr[1],
+            buf,
+            buf_size);
+    GNUNET_free (buf);
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Client %s must have disconnected\n",
-                client->addr.sun_path);
-    free_client (client);
-    return GNUNET_SYSERR;
+                "Sending RSA signature\n");
+    ret = TES_transmit (client->csock,
+                        &sr->header);
+    GNUNET_free (sr);
+    return ret;
   }
-  return GNUNET_OK;
 }
 
 
@@ -834,31 +443,35 @@ notify_client_dk_del (struct Client *client,
  * @param position where in the DLL will the @a dk go
  * @return #GNUNET_OK on success
  */
-static int
+static enum GNUNET_GenericReturnValue
 setup_key (struct DenominationKey *dk,
            struct DenominationKey *position)
 {
   struct Denomination *denom = dk->denom;
-  struct TALER_DenominationPrivateKey priv;
-  struct TALER_DenominationPublicKey pub;
+  struct GNUNET_CRYPTO_RsaPrivateKey *priv;
+  struct GNUNET_CRYPTO_RsaPublicKey *pub;
   size_t buf_size;
   void *buf;
 
-  if (GNUNET_OK !=
-      TALER_denom_priv_create (&priv,
-                               &pub,
-                               TALER_DENOMINATION_RSA,
-                               (unsigned int) denom->rsa_keysize))
+  priv = GNUNET_CRYPTO_rsa_private_key_create (denom->rsa_keysize);
+  if (NULL == priv)
   {
     GNUNET_break (0);
     GNUNET_SCHEDULER_shutdown ();
-    global_ret = 40;
+    global_ret = EXIT_FAILURE;
+    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);
     return GNUNET_SYSERR;
   }
-  buf_size = GNUNET_CRYPTO_rsa_private_key_encode 
(priv.details.rsa_private_key,
+  buf_size = GNUNET_CRYPTO_rsa_private_key_encode (priv,
                                                    &buf);
-  TALER_denom_pub_hash (&pub,
-                        &dk->h_denom_pub);
+  GNUNET_CRYPTO_rsa_public_key_hash (pub,
+                                     &dk->h_denom_pub);
   GNUNET_asprintf (&dk->filename,
                    "%s/%s/%llu",
                    keydir,
@@ -875,94 +488,98 @@ setup_key (struct DenominationKey *dk,
                               "write",
                               dk->filename);
     GNUNET_free (buf);
-    TALER_denom_priv_free (&priv);
-    TALER_denom_pub_free (&pub);
+    GNUNET_CRYPTO_rsa_private_key_free (priv);
+    GNUNET_CRYPTO_rsa_public_key_free (pub);
     return GNUNET_SYSERR;
   }
   GNUNET_free (buf);
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Setup fresh private key %s at %s in `%s'\n",
-              GNUNET_h2s (&dk->h_denom_pub.hash),
+              "Setup fresh private key %s at %s in `%s' (generation #%llu)\n",
+              GNUNET_h2s (&dk->h_denom_pub),
               GNUNET_STRINGS_absolute_time_to_string (dk->anchor),
-              dk->filename);
-  dk->denom_priv = priv;
-  dk->denom_pub = pub;
-
+              dk->filename,
+              (unsigned long long) key_gen);
+  dk->denom_priv.rsa_private_key = priv;
+  dk->denom_pub.rsa_public_key = pub;
+  dk->key_gen = key_gen;
   if (GNUNET_OK !=
       GNUNET_CONTAINER_multihashmap_put (
         keys,
-        &dk->h_denom_pub.hash,
+        &dk->h_denom_pub,
         dk,
         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Duplicate private key created! Terminating.\n");
-    TALER_denom_priv_free (&dk->denom_priv);
-    TALER_denom_pub_free (&dk->denom_pub);
+    GNUNET_CRYPTO_rsa_private_key_free (dk->denom_priv.rsa_private_key);
+    GNUNET_CRYPTO_rsa_public_key_free (dk->denom_pub.rsa_public_key);
     GNUNET_free (dk->filename);
     GNUNET_free (dk);
     return GNUNET_SYSERR;
   }
   GNUNET_CONTAINER_DLL_insert_after (denom->keys_head,
                                      denom->keys_tail,
-                                     position,
-                                     dk);
-
-  /* tell clients about new key */
-  {
-    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");
-      }
-    }
-  }
+                                     position,
+                                     dk);
   return GNUNET_OK;
 }
 
 
 /**
- * A client informs us that a key has been revoked.
+ * The withdraw period of a key @a dk has expired. Purge it.
+ *
+ * @param[in] dk expired denomination key to purge
+ */
+static void
+purge_key (struct DenominationKey *dk)
+{
+  if (dk->purge)
+    return;
+  if (0 != unlink (dk->filename))
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                              "unlink",
+                              dk->filename);
+  GNUNET_free (dk->filename);
+  dk->purge = true;
+  dk->key_gen = key_gen;
+}
+
+
+/**
+ * A @a client informs us that a key has been revoked.
  * Check if the key is still in use, and if so replace (!)
  * it with a fresh key.
  *
- * @param addr address of the client making the request
- * @param addr_size number of bytes in @a addr
+ * @param client the client making the request
  * @param rr the revocation request
  */
-static void
-handle_revoke_request (const struct sockaddr_un *addr,
-                       socklen_t addr_size,
+static enum GNUNET_GenericReturnValue
+handle_revoke_request (struct TES_Client *client,
                        const struct TALER_CRYPTO_RevokeRequest *rr)
 {
   struct DenominationKey *dk;
   struct DenominationKey *ndk;
   struct Denomination *denom;
 
+  GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
   dk = GNUNET_CONTAINER_multihashmap_get (keys,
-                                          &rr->h_denom_pub.hash);
+                                          &rr->h_denom_pub);
   if (NULL == dk)
   {
+    GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Revocation request ignored, denomination key %s unknown\n",
-                GNUNET_h2s (&rr->h_denom_pub.hash));
-    return;
+                GNUNET_h2s (&rr->h_denom_pub));
+    return GNUNET_OK;
   }
 
-  /* kill existing key, done first to ensure this always happens */
-  if (0 != unlink (dk->filename))
-    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
-                              "unlink",
-                              dk->filename);
+  key_gen++;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Revoking key %p, bumping generation to %llu\n",
+              dk,
+              (unsigned long long) key_gen);
+  purge_key (dk);
+
   /* Setup replacement key */
   denom = dk->denom;
   ndk = GNUNET_new (struct DenominationKey);
@@ -972,171 +589,158 @@ handle_revoke_request (const struct sockaddr_un *addr,
       setup_key (ndk,
                  dk))
   {
+    GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     GNUNET_break (0);
     GNUNET_SCHEDULER_shutdown ();
-    global_ret = 44;
-    return;
+    global_ret = EXIT_FAILURE;
+    return GNUNET_SYSERR;
   }
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+  TES_wake_clients ();
+  return GNUNET_OK;
+}
 
-  /* get rid of the old key */
-  dk->purge = true;
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CONTAINER_multihashmap_remove (
-                   keys,
-                   &dk->h_denom_pub.hash,
-                   dk));
-  GNUNET_CONTAINER_DLL_remove (denom->keys_head,
-                               denom->keys_tail,
-                               dk);
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Revocation of denomination key %s complete\n",
-              GNUNET_h2s (&rr->h_denom_pub.hash));
 
-  /* Tell clients this key is gone */
-  {
-    struct Client *nxt;
+/**
+ * Handle @a hdr message received from @a client.
+ *
+ * @param client the client that received the message
+ * @param hdr message that was received
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+rsa_work_dispatch (struct TES_Client *client,
+                   const struct GNUNET_MessageHeader *hdr)
+{
+  uint16_t msize = ntohs (hdr->size);
 
-    for (struct Client *client = clients_head;
-         NULL != client;
-         client = nxt)
+  switch (ntohs (hdr->type))
+  {
+  case TALER_HELPER_RSA_MT_REQ_SIGN:
+    if (msize <= sizeof (struct TALER_CRYPTO_SignRequest))
     {
-      nxt = client->next;
-      if (GNUNET_OK !=
-          notify_client_dk_del (client,
-                                dk))
-        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                    "Failed to notify client about revoked key, client 
dropped\n");
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    return handle_sign_request (
+      client,
+      (const struct TALER_CRYPTO_SignRequest *) hdr);
+  case TALER_HELPER_RSA_MT_REQ_REVOKE:
+    if (msize != sizeof (struct TALER_CRYPTO_RevokeRequest))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
     }
+    return handle_revoke_request (
+      client,
+      (const struct TALER_CRYPTO_RevokeRequest *) hdr);
+  default:
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
   }
-  if (0 == dk->rc)
-    free_dk (dk);
 }
 
 
-static void
-read_job (void *cls)
+/**
+ * Send our initial key set to @a client together with the
+ * "sync" terminator.
+ *
+ * @param client the client to inform
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+rsa_client_init (struct TES_Client *client)
 {
-  struct Client *client = cls;
-  char buf[65536];
-  ssize_t buf_size;
-  const struct GNUNET_MessageHeader *hdr;
-  struct sockaddr_un addr;
-  socklen_t addr_size = sizeof (addr);
-
-  read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                             unix_sock,
-                                             &read_job,
-                                             NULL);
-  buf_size = GNUNET_NETWORK_socket_recvfrom (unix_sock,
-                                             buf,
-                                             sizeof (buf),
-                                             (struct sockaddr *) &addr,
-                                             &addr_size);
-  if (-1 == buf_size)
-  {
-    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
-                         "recv");
-    return;
-  }
-  if (0 == buf_size)
-  {
-    return;
-  }
-  if (buf_size < sizeof (struct GNUNET_MessageHeader))
-  {
-    GNUNET_break_op (0);
-    return;
-  }
-  hdr = (const struct GNUNET_MessageHeader *) buf;
-  if (ntohs (hdr->size) != buf_size)
-  {
-    GNUNET_break_op (0);
-    free_client (client);
-    return;
+  GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
+  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))
+      {
+        GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                    "Client %p must have disconnected\n",
+                    client);
+        return GNUNET_SYSERR;
+      }
+    }
   }
-  switch (ntohs (hdr->type))
+  client->key_gen = key_gen;
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
   {
-  case TALER_HELPER_RSA_MT_REQ_INIT:
-    if (ntohs (hdr->size) != sizeof (struct GNUNET_MessageHeader))
+    struct GNUNET_MessageHeader synced = {
+      .type = htons (TALER_HELPER_RSA_SYNCED),
+      .size = htons (sizeof (synced))
+    };
+
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Sending RSA SYNCED message to %p\n",
+                client);
+    if (GNUNET_OK !=
+        TES_transmit (client->csock,
+                      &synced))
     {
-      GNUNET_break_op (0);
-      return;
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
     }
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Notify @a client about all changes to the keys since
+ * the last generation known to the @a client.
+ *
+ * @param client the client to notify
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+rsa_update_client_keys (struct TES_Client *client)
+{
+  GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
+  for (struct Denomination *denom = denom_head;
+       NULL != denom;
+       denom = denom->next)
+  {
+    for (struct DenominationKey *key = denom->keys_head;
+         NULL != key;
+         key = key->next)
     {
-      struct Client *client;
-
-      client = GNUNET_new (struct Client);
-      client->addr = addr;
-      client->addr_size = addr_size;
-      GNUNET_CONTAINER_DLL_insert (clients_head,
-                                   clients_tail,
-                                   client);
-      for (struct Denomination *denom = denom_head;
-           NULL != denom;
-           denom = denom->next)
+      if (key->key_gen <= client->key_gen)
+        continue;
+      if (key->purge)
       {
-        for (struct DenominationKey *dk = denom->keys_head;
-             NULL != dk;
-             dk = dk->next)
+        if (GNUNET_OK !=
+            notify_client_dk_del (client,
+                                  key))
         {
-          if (GNUNET_OK !=
-              notify_client_dk_add (client,
-                                    dk))
-          {
-            /* client died, skip the rest */
-            client = NULL;
-            break;
-          }
+          GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+          return GNUNET_SYSERR;
         }
-        if (NULL == client)
-          break;
       }
-      if (NULL != client)
+      else
       {
-        struct GNUNET_MessageHeader synced = {
-          .type = htons (TALER_HELPER_RSA_SYNCED),
-          .size = htons (sizeof (synced))
-        };
-
-        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                    "Sending RSA SYNCED message\n");
         if (GNUNET_OK !=
-            transmit (&client->addr,
-                      client->addr_size,
-                      &synced))
+            notify_client_dk_add (client,
+                                  key))
         {
-          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                      "Client %s must have disconnected\n",
-                      client->addr.sun_path);
-          free_client (client);
+          GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+          return GNUNET_SYSERR;
         }
       }
     }
-    break;
-  case TALER_HELPER_RSA_MT_REQ_SIGN:
-    if (ntohs (hdr->size) <= sizeof (struct TALER_CRYPTO_SignRequest))
-    {
-      GNUNET_break_op (0);
-      return;
-    }
-    handle_sign_request (&addr,
-                         addr_size,
-                         (const struct TALER_CRYPTO_SignRequest *) buf);
-    break;
-  case TALER_HELPER_RSA_MT_REQ_REVOKE:
-    if (ntohs (hdr->size) != sizeof (struct TALER_CRYPTO_RevokeRequest))
-    {
-      GNUNET_break_op (0);
-      return;
-    }
-    handle_revoke_request (&addr,
-                           addr_size,
-                           (const struct TALER_CRYPTO_RevokeRequest *) buf);
-    break;
-  default:
-    GNUNET_break_op (0);
-    return;
   }
+  client->key_gen = key_gen;
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+  return GNUNET_OK;
 }
 
 
@@ -1147,7 +751,7 @@ read_job (void *cls)
  * @param now current time to use (to get many keys to use the exact same time)
  * @return #GNUNET_OK on success
  */
-static int
+static enum GNUNET_GenericReturnValue
 create_key (struct Denomination *denom,
             struct GNUNET_TIME_Absolute now)
 {
@@ -1174,12 +778,12 @@ create_key (struct Denomination *denom,
       setup_key (dk,
                  denom->keys_tail))
   {
+    GNUNET_break (0);
     GNUNET_free (dk);
     GNUNET_SCHEDULER_shutdown ();
-    global_ret = 42;
+    global_ret = EXIT_FAILURE;
     return GNUNET_SYSERR;
   }
-
   return GNUNET_OK;
 }
 
@@ -1195,72 +799,24 @@ create_key (struct Denomination *denom,
 static struct GNUNET_TIME_Absolute
 denomination_action_time (const struct Denomination *denom)
 {
-  if (NULL == denom->keys_head)
+  struct DenominationKey *head = denom->keys_head;
+  struct DenominationKey *tail = denom->keys_tail;
+  struct GNUNET_TIME_Absolute tt;
+
+  if (NULL == head)
     return GNUNET_TIME_UNIT_ZERO_ABS;
+  tt = GNUNET_TIME_absolute_subtract (
+    GNUNET_TIME_absolute_subtract (
+      GNUNET_TIME_absolute_add (tail->anchor,
+                                denom->duration_withdraw),
+      lookahead_sign),
+    overlap_duration);
+  if (head->rc > 0)
+    return tt; /* head expiration does not count due to rc > 0 */
   return GNUNET_TIME_absolute_min (
-    GNUNET_TIME_absolute_add (denom->keys_head->anchor,
+    GNUNET_TIME_absolute_add (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_denom_pub.hash,
-                                                       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;
-  }
-  TALER_denom_priv_free (&dk->denom_priv);
-  GNUNET_free (dk);
+    tt);
 }
 
 
@@ -1271,37 +827,69 @@ purge_key (struct DenominationKey *dk)
  *
  * @param[in,out] denom denomination to update material for
  * @param now current time to use (to get many keys to use the exact same time)
+ * @param[in,out] wake set to true if we should wake the clients
+ * @return #GNUNET_OK on success
  */
-static void
+static enum GNUNET_GenericReturnValue
 update_keys (struct Denomination *denom,
-             struct GNUNET_TIME_Absolute now)
+             struct GNUNET_TIME_Absolute now,
+             bool *wake)
 {
   /* create new denomination keys */
   while ( (NULL == denom->keys_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)).rel_value_us) )
+          GNUNET_TIME_absolute_is_past (
+            GNUNET_TIME_absolute_subtract (
+              GNUNET_TIME_absolute_subtract (
+                GNUNET_TIME_absolute_add (denom->keys_tail->anchor,
+                                          denom->duration_withdraw),
+                lookahead_sign),
+              overlap_duration)) )
+  {
+    if (! *wake)
+    {
+      key_gen++;
+      *wake = true;
+    }
     if (GNUNET_OK !=
         create_key (denom,
                     now))
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Failed to create keys for `%s'\n",
-                  denom->section);
-      return;
+      GNUNET_break (0);
+      global_ret = EXIT_FAILURE;
+      GNUNET_SCHEDULER_shutdown ();
+      return GNUNET_SYSERR;
     }
+  }
   /* remove expired denomination keys */
   while ( (NULL != denom->keys_head) &&
-          (0 ==
-           GNUNET_TIME_absolute_get_remaining
-             (GNUNET_TIME_absolute_add (denom->keys_head->anchor,
-                                        
denom->duration_withdraw)).rel_value_us) )
-    purge_key (denom->keys_head);
+          GNUNET_TIME_absolute_is_past
+            (GNUNET_TIME_absolute_add (denom->keys_head->anchor,
+                                       denom->duration_withdraw)) )
+  {
+    struct DenominationKey *key = denom->keys_head;
+    struct DenominationKey *nxt = key->next;
+
+    if (0 != key->rc)
+      break; /* later */
+    GNUNET_CONTAINER_DLL_remove (denom->keys_head,
+                                 denom->keys_tail,
+                                 key);
+    GNUNET_assert (GNUNET_OK ==
+                   GNUNET_CONTAINER_multihashmap_remove (
+                     keys,
+                     &key->h_denom_pub,
+                     key));
+    if ( (! key->purge) &&
+         (0 != unlink (key->filename)) )
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                                "unlink",
+                                key->filename);
+    GNUNET_free (key->filename);
+    GNUNET_CRYPTO_rsa_private_key_free (key->denom_priv.rsa_private_key);
+    GNUNET_CRYPTO_rsa_public_key_free (key->denom_pub.rsa_public_key);
+    GNUNET_free (key);
+    key = nxt;
+  }
 
   /* Update position of 'denom' in #denom_head DLL: sort by action time */
   {
@@ -1321,12 +909,12 @@ update_keys (struct Denomination *denom,
         break;
       before = pos;
     }
-
     GNUNET_CONTAINER_DLL_insert_after (denom_head,
                                        denom_tail,
                                        before,
                                        denom);
   }
+  return GNUNET_OK;
 }
 
 
@@ -1340,16 +928,24 @@ update_denominations (void *cls)
 {
   struct Denomination *denom;
   struct GNUNET_TIME_Absolute now;
+  bool wake = false;
 
   (void) cls;
   keygen_task = NULL;
   now = GNUNET_TIME_absolute_get ();
   (void) GNUNET_TIME_round_abs (&now);
+  GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
   do {
     denom = denom_head;
-    update_keys (denom,
-                 now);
+    if (GNUNET_OK !=
+        update_keys (denom,
+                     now,
+                     &wake))
+      return;
   } while (denom != denom_head);
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+  if (wake)
+    TES_wake_clients ();
   keygen_task = GNUNET_SCHEDULER_add_at (denomination_action_time (denom),
                                          &update_denominations,
                                          NULL);
@@ -1370,7 +966,7 @@ parse_key (struct Denomination *denom,
            const void *buf,
            size_t buf_size)
 {
-  struct TALER_DenominationPrivateKey priv;
+  struct GNUNET_CRYPTO_RsaPrivateKey *priv;
   char *anchor_s;
   char dummy;
   unsigned long long anchor_ll;
@@ -1405,11 +1001,9 @@ parse_key (struct Denomination *denom,
                 filename);
     return;
   }
-  priv.cipher = TALER_DENOMINATION_RSA;
-  priv.details.rsa_private_key
-    = GNUNET_CRYPTO_rsa_private_key_decode (buf,
-                                            buf_size);
-  if (NULL == priv.details.rsa_private_key)
+  priv = GNUNET_CRYPTO_rsa_private_key_decode (buf,
+                                               buf_size);
+  if (NULL == priv)
   {
     /* Parser failure. */
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
@@ -1419,34 +1013,38 @@ parse_key (struct Denomination *denom,
   }
 
   {
-    struct TALER_DenominationPublicKey pub;
+    struct GNUNET_CRYPTO_RsaPublicKey *pub;
     struct DenominationKey *dk;
     struct DenominationKey *before;
 
-    TALER_denom_priv_to_pub (&priv,
-                             (struct TALER_AgeMask) { .mask = 0 }, /* 
FIXME-Oec */
-                             &pub);
+    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 = priv;
+    dk->denom_priv.rsa_private_key = priv;
     dk->denom = denom;
     dk->anchor = anchor;
     dk->filename = GNUNET_strdup (filename);
-    TALER_denom_pub_hash (&pub,
-                          &dk->h_denom_pub);
-    dk->denom_pub = pub;
+    GNUNET_CRYPTO_rsa_public_key_hash (pub,
+                                       &dk->h_denom_pub);
+    dk->denom_pub.rsa_public_key = pub;
     if (GNUNET_OK !=
         GNUNET_CONTAINER_multihashmap_put (
           keys,
-          &dk->h_denom_pub.hash,
+          &dk->h_denom_pub,
           dk,
           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Duplicate private key %s detected in file `%s'. 
Skipping.\n",
-                  GNUNET_h2s (&dk->h_denom_pub.hash),
+                  GNUNET_h2s (&dk->h_denom_pub),
                   filename);
-      TALER_denom_priv_free (&priv);
-      TALER_denom_pub_free (&pub);
+      GNUNET_CRYPTO_rsa_private_key_free (priv);
+      GNUNET_CRYPTO_rsa_public_key_free (pub);
       GNUNET_free (dk);
       return;
     }
@@ -1465,7 +1063,7 @@ parse_key (struct Denomination *denom,
                                        dk);
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Imported key %s from `%s'\n",
-                GNUNET_h2s (&dk->h_denom_pub.hash),
+                GNUNET_h2s (&dk->h_denom_pub),
                 filename);
   }
 }
@@ -1477,8 +1075,9 @@ parse_key (struct Denomination *denom,
  *
  * @param[in,out] cls a `struct Denomiantion`
  * @param filename name of a file in the directory
+ * @return #GNUNET_OK (always, continue to iterate)
  */
-static int
+static enum GNUNET_GenericReturnValue
 import_key (void *cls,
             const char *filename)
 {
@@ -1591,18 +1190,20 @@ import_key (void *cls,
  * Parse configuration for denomination type parameters.  Also determines
  * our anchor by looking at the existing denominations of the same type.
  *
+ * @param cfg configuration to use
  * @param ct section in the configuration file giving the denomination type 
parameters
  * @param[out] denom set to the denomination parameters from the configuration
  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the configuration is 
invalid
  */
-static int
-parse_denomination_cfg (const char *ct,
+static enum GNUNET_GenericReturnValue
+parse_denomination_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                        const char *ct,
                         struct Denomination *denom)
 {
   unsigned long long rsa_keysize;
 
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_time (kcfg,
+      GNUNET_CONFIGURATION_get_value_time (cfg,
                                            ct,
                                            "DURATION_WITHDRAW",
                                            &denom->duration_withdraw))
@@ -1623,7 +1224,7 @@ parse_denomination_cfg (const char *ct,
     return GNUNET_SYSERR;
   }
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_number (kcfg,
+      GNUNET_CONFIGURATION_get_value_number (cfg,
                                              ct,
                                              "RSA_KEYSIZE",
                                              &rsa_keysize))
@@ -1653,6 +1254,12 @@ parse_denomination_cfg (const char *ct,
  */
 struct LoadContext
 {
+
+  /**
+   * Configuration to use.
+   */
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
   /**
    * Current time to use.
    */
@@ -1661,7 +1268,7 @@ struct LoadContext
   /**
    * Status, to be set to #GNUNET_SYSERR on failure
    */
-  int ret;
+  enum GNUNET_GenericReturnValue ret;
 };
 
 
@@ -1678,6 +1285,7 @@ load_denominations (void *cls,
 {
   struct LoadContext *ctx = cls;
   struct Denomination *denom;
+  bool wake;
 
   if ( (0 != strncasecmp (denomination_alias,
                           "coin_",
@@ -1688,7 +1296,8 @@ load_denominations (void *cls,
     return; /* not a denomination type definition */
   denom = GNUNET_new (struct Denomination);
   if (GNUNET_OK !=
-      parse_denomination_cfg (denomination_alias,
+      parse_denomination_cfg (ctx->cfg,
+                              denomination_alias,
                               denom))
   {
     ctx->ret = GNUNET_SYSERR;
@@ -1716,20 +1325,22 @@ load_denominations (void *cls,
                                denom_tail,
                                denom);
   update_keys (denom,
-               ctx->now);
+               ctx->now,
+               &wake);
 }
 
 
 /**
- * Load the various duration values from #kcfg.
+ * Load the various duration values from @a cfg
  *
+ * @param cfg configuration to use
  * @return #GNUNET_OK on success
  */
-static int
-load_durations (void)
+static enum GNUNET_GenericReturnValue
+load_durations (const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_time (kcfg,
+      GNUNET_CONFIGURATION_get_value_time (cfg,
                                            "taler-exchange-secmod-rsa",
                                            "OVERLAP_DURATION",
                                            &overlap_duration))
@@ -1742,7 +1353,7 @@ load_durations (void)
   GNUNET_TIME_round_rel (&overlap_duration);
 
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_time (kcfg,
+      GNUNET_CONFIGURATION_get_value_time (cfg,
                                            "taler-exchange-secmod-rsa",
                                            "LOOKAHEAD_SIGN",
                                            &lookahead_sign))
@@ -1766,51 +1377,12 @@ static void
 do_shutdown (void *cls)
 {
   (void) cls;
-  if (NULL != read_task)
-  {
-    GNUNET_SCHEDULER_cancel (read_task);
-    read_task = NULL;
-  }
-  if (NULL != unix_sock)
-  {
-    GNUNET_break (GNUNET_OK ==
-                  GNUNET_NETWORK_socket_close (unix_sock));
-    unix_sock = NULL;
-  }
-  if (0 != unlink (unixpath))
-  {
-    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
-                              "unlink",
-                              unixpath);
-  }
-  GNUNET_free (unixpath);
+  TES_listen_stop ();
   if (NULL != keygen_task)
   {
     GNUNET_SCHEDULER_cancel (keygen_task);
     keygen_task = NULL;
   }
-  if (NULL != done_task)
-  {
-    GNUNET_SCHEDULER_cancel (done_task);
-    done_task = NULL;
-  }
-  /* shut down worker threads */
-  if (NULL != workers)
-  {
-    GNUNET_assert (0 == pthread_mutex_lock (&work_lock));
-    in_shutdown = true;
-    GNUNET_assert (0 == pthread_cond_broadcast (&work_cond));
-    GNUNET_assert (0 == pthread_mutex_unlock (&work_lock));
-    for (unsigned int i = 0; i<num_workers; i++)
-      GNUNET_assert (0 == pthread_join (workers[i],
-                                        NULL));
-  }
-  if (NULL != done_signal)
-  {
-    GNUNET_break (GNUNET_OK ==
-                  GNUNET_NETWORK_socket_close (done_signal));
-    done_signal = NULL;
-  }
 }
 
 
@@ -1828,10 +1400,14 @@ run (void *cls,
      const char *cfgfile,
      const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
+  static struct TES_Callbacks cb = {
+    .dispatch = rsa_work_dispatch,
+    .updater = rsa_update_client_keys,
+    .init = rsa_client_init
+  };
   (void) cls;
   (void) args;
   (void) cfgfile;
-  kcfg = cfg;
   if (now.abs_value_us != now_tmp.abs_value_us)
   {
     /* The user gave "--now", use it! */
@@ -1843,48 +1419,8 @@ run (void *cls,
     now = GNUNET_TIME_absolute_get ();
   }
   GNUNET_TIME_round_abs (&now);
-
-  {
-    char *pfn;
-
-    if (GNUNET_OK !=
-        GNUNET_CONFIGURATION_get_value_filename (kcfg,
-                                                 "taler-exchange-secmod-rsa",
-                                                 "SM_PRIV_KEY",
-                                                 &pfn))
-    {
-      GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                                 "taler-exchange-secmod-rsa",
-                                 "SM_PRIV_KEY");
-      global_ret = 1;
-      return;
-    }
-    if (GNUNET_SYSERR ==
-        GNUNET_CRYPTO_eddsa_key_from_file (pfn,
-                                           GNUNET_YES,
-                                           &smpriv.eddsa_priv))
-    {
-      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                                 "taler-exchange-secmod-rsa",
-                                 "SM_PRIV_KEY",
-                                 "Could not use file to persist private key");
-      GNUNET_free (pfn);
-      global_ret = 1;
-      return;
-    }
-    GNUNET_free (pfn);
-    GNUNET_CRYPTO_eddsa_key_get_public (&smpriv.eddsa_priv,
-                                        &smpub.eddsa_pub);
-  }
-
-  if (GNUNET_OK !=
-      load_durations ())
-  {
-    global_ret = 1;
-    return;
-  }
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (kcfg,
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
                                                "taler-exchange-secmod-rsa",
                                                "KEY_DIR",
                                                &keydir))
@@ -1892,92 +1428,41 @@ run (void *cls,
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "taler-exchange-secmod-rsa",
                                "KEY_DIR");
-    global_ret = 1;
+    global_ret = EXIT_NOTCONFIGURED;
     return;
   }
-
-  /* Create client directory and set permissions. */
-  {
-    char *client_dir;
-
-    if (GNUNET_OK !=
-        GNUNET_CONFIGURATION_get_value_filename (kcfg,
-                                                 "taler-exchange-secmod-rsa",
-                                                 "CLIENT_DIR",
-                                                 &client_dir))
-    {
-      GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                                 "taler-exchange-secmod-rsa",
-                                 "CLIENT_DIR");
-      global_ret = 3;
-      return;
-    }
-
-    if (GNUNET_OK != GNUNET_DISK_directory_create (client_dir))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Can't create client directory (%s)\n",
-                  client_dir);
-      global_ret = 3;
-      return;
-    }
-    /* Set sticky group bit, so that clients will be writeable by the current 
service. */
-    if (0 != chmod (client_dir,
-                    S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP
-                    | S_ISGID))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Can't set permissions for client directory (%s)\n",
-                  client_dir);
-      global_ret = 3;
-      return;
-    }
-
-    GNUNET_free (client_dir);
-  }
-
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (kcfg,
-                                               "taler-exchange-secmod-rsa",
-                                               "UNIXPATH",
-                                               &unixpath))
+      load_durations (cfg))
   {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "taler-exchange-secmod-rsa",
-                               "UNIXPATH");
-    global_ret = 3;
+    global_ret = EXIT_NOTCONFIGURED;
     return;
   }
-
-  GNUNET_assert (NULL != unixpath);
-  unix_sock = TES_open_socket (unixpath);
-
-  if (NULL == unix_sock)
-  {
-    GNUNET_free (unixpath);
-    global_ret = 2;
+  global_ret = TES_listen_start (cfg,
+                                 "taler-exchange-secmod-rsa",
+                                 &cb);
+  if (0 != global_ret)
     return;
-  }
-
   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
                                  NULL);
-
   /* Load denominations */
   keys = GNUNET_CONTAINER_multihashmap_create (65536,
                                                GNUNET_YES);
   {
     struct LoadContext lc = {
+      .cfg = cfg,
       .ret = GNUNET_OK,
-      .now = GNUNET_TIME_absolute_get ()
+      .now = now
     };
 
     (void) GNUNET_TIME_round_abs (&lc.now);
-    GNUNET_CONFIGURATION_iterate_sections (kcfg,
+    GNUNET_assert (0 == pthread_mutex_lock (&keys_lock));
+    GNUNET_CONFIGURATION_iterate_sections (cfg,
                                            &load_denominations,
                                            &lc);
+    GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
     if (GNUNET_OK != lc.ret)
     {
-      global_ret = 4;
+      global_ret = EXIT_FAILURE;
       GNUNET_SCHEDULER_shutdown ();
       return;
     }
@@ -1986,60 +1471,16 @@ run (void *cls,
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "No denominations configured\n");
-    global_ret = 5;
+    global_ret = EXIT_NOTCONFIGURED;
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
-
-  /* start job to accept incoming requests on 'sock' */
-  read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                             unix_sock,
-                                             &read_job,
-                                             NULL);
-  /* start job to keep keys up-to-date; MUST be run before the #read_task,
+  /* start job to keep keys up-to-date; MUST be run before the #listen_task,
      hence with priority. */
   keygen_task = GNUNET_SCHEDULER_add_with_priority (
     GNUNET_SCHEDULER_PRIORITY_URGENT,
     &update_denominations,
     NULL);
-
-  /* start job to handle completed work */
-  {
-    int fd;
-
-    fd = eventfd (0,
-                  EFD_NONBLOCK | EFD_CLOEXEC);
-    if (-1 == fd)
-    {
-      GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
-                           "eventfd");
-      global_ret = 6;
-      GNUNET_SCHEDULER_shutdown ();
-      return;
-    }
-    done_signal = GNUNET_NETWORK_socket_box_native (fd);
-  }
-  done_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                             done_signal,
-                                             &handle_done,
-                                             NULL);
-
-  /* start crypto workers */
-  if (0 == num_workers)
-    num_workers = sysconf (_SC_NPROCESSORS_CONF);
-  if (0 == num_workers)
-    num_workers = 1;
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Starting %u crypto workers\n",
-              num_workers);
-  workers = GNUNET_new_array (num_workers,
-                              pthread_t);
-  for (unsigned int i = 0; i<num_workers; i++)
-    GNUNET_assert (0 ==
-                   pthread_create (&workers[i],
-                                   NULL,
-                                   &sign_worker,
-                                   NULL));
 }
 
 
@@ -2057,11 +1498,6 @@ main (int argc,
   struct GNUNET_GETOPT_CommandLineOption options[] = {
     GNUNET_GETOPT_option_timetravel ('T',
                                      "timetravel"),
-    GNUNET_GETOPT_option_uint ('p',
-                               "parallelism",
-                               "NUM_WORKERS",
-                               "number of worker threads to use",
-                               &num_workers),
     GNUNET_GETOPT_option_absolute_time ('t',
                                         "time",
                                         "TIMESTAMP",
@@ -2069,7 +1505,7 @@ main (int argc,
                                         &now_tmp),
     GNUNET_GETOPT_OPTION_END
   };
-  int ret;
+  enum GNUNET_GenericReturnValue ret;
 
   /* Restrict permissions for the key files that we create. */
   (void) umask (S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH);
@@ -2086,8 +1522,8 @@ main (int argc,
                             &run,
                             NULL);
   if (GNUNET_NO == ret)
-    return 0;
+    return EXIT_SUCCESS;
   if (GNUNET_SYSERR == ret)
-    return 1;
+    return EXIT_INVALIDARGUMENT;
   return global_ret;
 }
diff --git a/src/util/taler-exchange-secmod-rsa.h 
b/src/util/taler-exchange-secmod-rsa.h
index cf439e26..146b6948 100644
--- a/src/util/taler-exchange-secmod-rsa.h
+++ b/src/util/taler-exchange-secmod-rsa.h
@@ -102,7 +102,7 @@ struct TALER_CRYPTO_RsaKeyPurgeNotification
   /**
    * Hash of the public key of the purged RSA key.
    */
-  struct TALER_DenominationHash h_denom_pub;
+  struct GNUNET_HashCode h_denom_pub;
 
 };
 
@@ -125,7 +125,7 @@ struct TALER_CRYPTO_SignRequest
   /**
    * Hash of the public key of the RSA key to use for the signature.
    */
-  struct TALER_DenominationHash h_denom_pub;
+  struct GNUNET_HashCode h_denom_pub;
 
   /* followed by message to sign */
 };
@@ -149,7 +149,7 @@ struct TALER_CRYPTO_RevokeRequest
   /**
    * Hash of the public key of the revoked RSA key.
    */
-  struct TALER_DenominationHash h_denom_pub;
+  struct GNUNET_HashCode h_denom_pub;
 
 };
 
diff --git a/src/util/test_helper_eddsa.c b/src/util/test_helper_eddsa.c
index 4b44f604..48c14491 100644
--- a/src/util/test_helper_eddsa.c
+++ b/src/util/test_helper_eddsa.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  (C) 2020 Taler Systems SA
+  (C) 2020, 2021 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
@@ -36,7 +36,7 @@
 
 /**
  * How many iterations of the successful signing test should we run
- * during the benchmark phase?
+ * during the test phase?
  */
 #define NUM_SIGN_TESTS 3
 
@@ -46,6 +46,11 @@
  */
 #define NUM_SIGN_PERFS 100
 
+/**
+ * How many parallel clients should we use for the parallel
+ * benchmark? (> 500 may cause problems with the max open FD number limit).
+ */
+#define NUM_CORES 8
 
 /**
  * Number of keys currently in #keys.
@@ -270,7 +275,8 @@ test_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh)
  * @return 0 on success
  */
 static int
-perf_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh)
+perf_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh,
+              const char *type)
 {
   struct GNUNET_CRYPTO_EccSignaturePurpose purpose = {
     .purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TEST),
@@ -303,8 +309,69 @@ perf_signing (struct TALER_CRYPTO_ExchangeSignHelper *esh)
                                          delay);
   } /* for j */
   fprintf (stderr,
-           "%u (sequential) signature operations took %s\n",
-           (unsigned int) NUM_SIGN_TESTS,
+           "%u (%s) signature operations took %s\n",
+           (unsigned int) NUM_SIGN_PERFS,
+           type,
+           GNUNET_STRINGS_relative_time_to_string (duration,
+                                                   GNUNET_YES));
+  return 0;
+}
+
+
+/**
+ * Parallel signing logic.
+ *
+ * @param esh handle to the helper
+ * @return 0 on success
+ */
+static int
+par_signing (struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  struct GNUNET_TIME_Absolute start;
+  struct GNUNET_TIME_Relative duration;
+  pid_t pids[NUM_CORES];
+  struct TALER_CRYPTO_ExchangeSignHelper *esh;
+
+  memset (keys,
+          0,
+          sizeof (keys));
+  num_keys = 0;
+  start = GNUNET_TIME_absolute_get ();
+  for (unsigned int i = 0; i<NUM_CORES; i++)
+  {
+    pids[i] = fork ();
+    GNUNET_assert (-1 != pids[i]);
+    if (0 == pids[i])
+    {
+      int ret;
+
+      esh = TALER_CRYPTO_helper_esign_connect (cfg,
+                                               &key_cb,
+                                               NULL);
+      if (NULL == esh)
+      {
+        GNUNET_break (0);
+        exit (EXIT_FAILURE);
+      }
+      ret = perf_signing (esh,
+                          "parallel");
+      TALER_CRYPTO_helper_esign_disconnect (esh);
+      exit (ret);
+    }
+  }
+  for (unsigned int i = 0; i<NUM_CORES; i++)
+  {
+    int wstatus;
+
+    GNUNET_assert (pids[i] ==
+                   waitpid (pids[i],
+                            &wstatus,
+                            0));
+  }
+  duration = GNUNET_TIME_absolute_get_duration (start);
+  fprintf (stderr,
+           "%u (parallel) signature operations took %s (total real time)\n",
+           (unsigned int) NUM_SIGN_PERFS * NUM_CORES,
            GNUNET_STRINGS_relative_time_to_string (duration,
                                                    GNUNET_YES));
   return 0;
@@ -319,10 +386,10 @@ run_test (void)
 {
   struct GNUNET_CONFIGURATION_Handle *cfg;
   struct TALER_CRYPTO_ExchangeSignHelper *esh;
+  int ret;
   struct timespec req = {
     .tv_nsec = 250000000
   };
-  int ret;
 
   cfg = GNUNET_CONFIGURATION_create ();
   if (GNUNET_OK !=
@@ -334,54 +401,47 @@ run_test (void)
   }
 
   /* wait for helper to start and give us keys */
-  fprintf (stderr, "Waiting for helper client directory to become available ");
-  for (unsigned int i = 0; i<1000; i++)
+  fprintf (stderr, "Waiting for helper to start ... ");
+  for (unsigned int i = 0; i<100; i++)
   {
+    nanosleep (&req,
+               NULL);
     esh = TALER_CRYPTO_helper_esign_connect (cfg,
                                              &key_cb,
                                              NULL);
     if (NULL != esh)
       break;
-    nanosleep (&req, NULL);
     fprintf (stderr, ".");
   }
-  GNUNET_CONFIGURATION_destroy (cfg);
   if (NULL == esh)
   {
-    GNUNET_break (0);
+    fprintf (stderr,
+             "\nFAILED: timeout trying to connect to helper\n");
+    GNUNET_CONFIGURATION_destroy (cfg);
     return 1;
   }
-  fprintf (stderr, " done.\n");
-
-  /* wait for helper to start and give us keys */
-  fprintf (stderr, "Waiting for helper to start ");
-  for (unsigned int i = 0; i<1000; i++)
-  {
-    TALER_CRYPTO_helper_esign_poll (esh);
-    if (0 != num_keys)
-      break;
-    nanosleep (&req, NULL);
-    fprintf (stderr, ".");
-  }
   if (0 == num_keys)
   {
     fprintf (stderr,
-             "\nFAILED: timeout trying to connect to helper\n");
+             "\nFAILED: no keys returend by helper\n");
     TALER_CRYPTO_helper_esign_disconnect (esh);
+    GNUNET_CONFIGURATION_destroy (cfg);
     return 1;
   }
   fprintf (stderr,
-           "\nOK: Helper ready (%u keys)\n",
+           " Done (%u keys)\n",
            num_keys);
-
   ret = 0;
   if (0 == ret)
     ret = test_revocation (esh);
   if (0 == ret)
     ret = test_signing (esh);
   if (0 == ret)
-    ret = perf_signing (esh);
+    ret = perf_signing (esh,
+                        "sequential");
   TALER_CRYPTO_helper_esign_disconnect (esh);
+  if (0 == ret)
+    ret = par_signing (cfg);
   /* clean up our state */
   for (unsigned int i = 0; i<MAX_KEYS; i++)
     if (keys[i].valid)
@@ -390,6 +450,7 @@ run_test (void)
       GNUNET_assert (num_keys > 0);
       num_keys--;
     }
+  GNUNET_CONFIGURATION_destroy (cfg);
   return ret;
 }
 
@@ -408,7 +469,7 @@ main (int argc,
   (void) argc;
   (void) argv;
   GNUNET_log_setup ("test-helper-eddsa",
-                    "INFO",
+                    "WARNING",
                     NULL);
   GNUNET_OS_init (TALER_project_data_default ());
   libexec_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
@@ -424,7 +485,7 @@ main (int argc,
                                     "-c",
                                     "test_helper_eddsa.conf",
                                     "-L",
-                                    "INFO",
+                                    "WARNING",
                                     NULL);
   if (NULL == helper)
   {
diff --git a/src/util/test_helper_rsa.c b/src/util/test_helper_rsa.c
index e4c0bf6f..97844001 100644
--- a/src/util/test_helper_rsa.c
+++ b/src/util/test_helper_rsa.c
@@ -22,11 +22,12 @@
 #include "taler_util.h"
 
 /**
- * Configuration has 1 minute duration and 5 minutes lookahead, so
- * we should never have more than 6 active keys, plus for during
- * key expiration / revocation.
+ * Configuration has 1 minute duration and 5 minutes lookahead, but
+ * we do not get 'revocations' for expired keys. So this must be
+ * large enough to deal with key rotation during the runtime of
+ * the benchmark.
  */
-#define MAX_KEYS 7
+#define MAX_KEYS 1024
 
 /**
  * How many random key revocations should we test?
@@ -38,6 +39,17 @@
  */
 #define NUM_SIGN_TESTS 5
 
+/**
+ * How many iterations of the successful signing test should we run
+ * during the benchmark phase?
+ */
+#define NUM_SIGN_PERFS 100
+
+/**
+ * How many parallel clients should we use for the parallel
+ * benchmark? (> 500 may cause problems with the max open FD number limit).
+ */
+#define NUM_CORES 8
 
 /**
  * Number of keys currently in #keys.
@@ -62,7 +74,7 @@ struct KeyData
   /**
    * Hash of the public key.
    */
-  struct TALER_DenominationHash h_denom_pub;
+  struct GNUNET_HashCode h_denom_pub;
 
   /**
    * Full public key.
@@ -110,7 +122,7 @@ key_cb (void *cls,
         const char *section_name,
         struct GNUNET_TIME_Absolute start_time,
         struct GNUNET_TIME_Relative validity_duration,
-        const struct TALER_DenominationHash *h_denom_pub,
+        const struct GNUNET_HashCode *h_denom_pub,
         const struct TALER_DenominationPublicKey *denom_pub,
         const struct TALER_SecurityModulePublicKeyP *sm_pub,
         const struct TALER_SecurityModuleSignatureP *sm_sig)
@@ -119,7 +131,7 @@ key_cb (void *cls,
   (void) sm_sig;
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Key notification about key %s in `%s'\n",
-              GNUNET_h2s (&h_denom_pub->hash),
+              GNUNET_h2s (h_denom_pub),
               section_name);
   if (0 == validity_duration.rel_value_us)
   {
@@ -133,7 +145,8 @@ key_cb (void *cls,
       {
         keys[i].valid = false;
         keys[i].revoked = false;
-        TALER_denom_pub_free (&keys[i].denom_pub);
+        GNUNET_CRYPTO_rsa_public_key_free (keys[i].denom_pub.rsa_public_key);
+        keys[i].denom_pub.rsa_public_key = NULL;
         GNUNET_assert (num_keys > 0);
         num_keys--;
         found = true;
@@ -154,9 +167,8 @@ key_cb (void *cls,
       keys[i].h_denom_pub = *h_denom_pub;
       keys[i].start_time = start_time;
       keys[i].validity_duration = validity_duration;
-      keys[i].denom_pub = *denom_pub;
-      TALER_denom_pub_deep_copy (&keys[i].denom_pub,
-                                 denom_pub);
+      keys[i].denom_pub.rsa_public_key
+        = GNUNET_CRYPTO_rsa_public_key_dup (denom_pub->rsa_public_key);
       num_keys++;
       return;
     }
@@ -199,7 +211,7 @@ test_revocation (struct TALER_CRYPTO_DenominationHelper *dh)
       keys[j].revoked = true;
       fprintf (stderr,
                "Revoking key %s ...",
-               GNUNET_h2s (&keys[j].h_denom_pub.hash));
+               GNUNET_h2s (&keys[j].h_denom_pub));
       TALER_CRYPTO_helper_denom_revoke (dh,
                                         &keys[j].h_denom_pub);
       for (unsigned int k = 0; k<1000; k++)
@@ -235,35 +247,42 @@ test_revocation (struct TALER_CRYPTO_DenominationHelper 
*dh)
 static int
 test_signing (struct TALER_CRYPTO_DenominationHelper *dh)
 {
-  struct TALER_BlindedDenominationSignature ds;
+  struct TALER_DenominationSignature ds;
   enum TALER_ErrorCode ec;
   bool success = false;
-  struct TALER_PlanchetSecretsP ps;
-  struct TALER_CoinPubHash c_hash;
-
-  TALER_planchet_setup_random (&ps);
+  struct GNUNET_HashCode m_hash;
+  struct GNUNET_CRYPTO_RsaBlindingKeySecret bks;
+
+  GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                              &bks,
+                              sizeof (bks));
+  GNUNET_CRYPTO_hash ("Hello",
+                      strlen ("Hello"),
+                      &m_hash);
   for (unsigned int i = 0; i<MAX_KEYS; i++)
   {
     if (! keys[i].valid)
       continue;
     {
-      struct TALER_PlanchetDetail pd;
+      void *buf;
+      size_t buf_size;
 
       GNUNET_assert (GNUNET_YES ==
-                     TALER_planchet_prepare (&keys[i].denom_pub,
-                                             &ps,
-                                             &c_hash,
-                                             &pd));
+                     TALER_rsa_blind (&m_hash,
+                                      &bks,
+                                      keys[i].denom_pub.rsa_public_key,
+                                      &buf,
+                                      &buf_size));
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Requesting signature over %u bytes with key %s\n",
-                  (unsigned int) pd.coin_ev_size,
-                  GNUNET_h2s (&keys[i].h_denom_pub.hash));
+                  (unsigned int) buf_size,
+                  GNUNET_h2s (&keys[i].h_denom_pub));
       ds = TALER_CRYPTO_helper_denom_sign (dh,
                                            &keys[i].h_denom_pub,
-                                           pd.coin_ev,
-                                           pd.coin_ev_size,
+                                           buf,
+                                           buf_size,
                                            &ec);
-      GNUNET_free (pd.coin_ev);
+      GNUNET_free (buf);
     }
     switch (ec)
     {
@@ -283,33 +302,32 @@ test_signing (struct TALER_CRYPTO_DenominationHelper *dh)
         return 5;
       }
       {
-        struct TALER_DenominationSignature rs;
+        struct GNUNET_CRYPTO_RsaSignature *rs;
 
-        if (GNUNET_OK !=
-            TALER_denom_sig_unblind (&rs,
-                                     &ds,
-                                     &ps.blinding_key,
-                                     &keys[i].denom_pub))
+        rs = TALER_rsa_unblind (ds.rsa_signature,
+                                &bks,
+                                keys[i].denom_pub.rsa_public_key);
+        if (NULL == rs)
         {
           GNUNET_break (0);
           return 6;
         }
-        TALER_blinded_denom_sig_free (&ds);
+        GNUNET_CRYPTO_rsa_signature_free (ds.rsa_signature);
         if (GNUNET_OK !=
-            TALER_denom_pub_verify (&keys[i].denom_pub,
-                                    &rs,
-                                    &c_hash))
+            GNUNET_CRYPTO_rsa_verify (&m_hash,
+                                      rs,
+                                      keys[i].denom_pub.rsa_public_key))
         {
           /* signature invalid */
           GNUNET_break (0);
-          TALER_denom_sig_free (&rs);
+          GNUNET_CRYPTO_rsa_signature_free (rs);
           return 7;
         }
-        TALER_denom_sig_free (&rs);
+        GNUNET_CRYPTO_rsa_signature_free (rs);
       }
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Received valid signature for key %s\n",
-                  GNUNET_h2s (&keys[i].h_denom_pub.hash));
+                  GNUNET_h2s (&keys[i].h_denom_pub));
       success = true;
       break;
     case TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY:
@@ -344,24 +362,27 @@ test_signing (struct TALER_CRYPTO_DenominationHelper *dh)
 
   /* check signing does not work if the key is unknown */
   {
-    struct TALER_DenominationHash rnd;
+    struct GNUNET_HashCode rnd;
+    struct TALER_DenominationSignature ds;
 
     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
                                 &rnd,
                                 sizeof (rnd));
-    (void) TALER_CRYPTO_helper_denom_sign (dh,
-                                           &rnd,
-                                           "Hello",
-                                           strlen ("Hello"),
-                                           &ec);
+    ds = TALER_CRYPTO_helper_denom_sign (dh,
+                                         &rnd,
+                                         "Hello",
+                                         strlen ("Hello"),
+                                         &ec);
     if (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN != ec)
     {
+      if (TALER_EC_NONE == ec)
+        GNUNET_CRYPTO_rsa_signature_free (ds.rsa_signature);
       GNUNET_break (0);
       return 17;
     }
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Signing with invalid key %s failed as desired\n",
-                GNUNET_h2s (&rnd.hash));
+                GNUNET_h2s (&rnd));
   }
   return 0;
 }
@@ -374,18 +395,25 @@ test_signing (struct TALER_CRYPTO_DenominationHelper *dh)
  * @return 0 on success
  */
 static int
-perf_signing (struct TALER_CRYPTO_DenominationHelper *dh)
+perf_signing (struct TALER_CRYPTO_DenominationHelper *dh,
+              const char *type)
 {
-  struct TALER_BlindedDenominationSignature ds;
+  struct TALER_DenominationSignature ds;
   enum TALER_ErrorCode ec;
+  struct GNUNET_HashCode m_hash;
+  struct GNUNET_CRYPTO_RsaBlindingKeySecret bks;
   struct GNUNET_TIME_Relative duration;
-  struct TALER_PlanchetSecretsP ps;
 
-  TALER_planchet_setup_random (&ps);
+  GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                              &bks,
+                              sizeof (bks));
+  GNUNET_CRYPTO_hash ("Hello",
+                      strlen ("Hello"),
+                      &m_hash);
   duration = GNUNET_TIME_UNIT_ZERO;
-  for (unsigned int j = 0; j<NUM_SIGN_TESTS;)
+  TALER_CRYPTO_helper_denom_poll (dh);
+  for (unsigned int j = 0; j<NUM_SIGN_PERFS;)
   {
-    TALER_CRYPTO_helper_denom_poll (dh);
     for (unsigned int i = 0; i<MAX_KEYS; i++)
     {
       if (! keys[i].valid)
@@ -397,14 +425,15 @@ perf_signing (struct TALER_CRYPTO_DenominationHelper *dh)
           keys[i].validity_duration.rel_value_us)
         continue;
       {
-        struct TALER_CoinPubHash c_hash;
-        struct TALER_PlanchetDetail pd;
+        void *buf;
+        size_t buf_size;
 
         GNUNET_assert (GNUNET_YES ==
-                       TALER_planchet_prepare (&keys[i].denom_pub,
-                                               &ps,
-                                               &c_hash,
-                                               &pd));
+                       TALER_rsa_blind (&m_hash,
+                                        &bks,
+                                        keys[i].denom_pub.rsa_public_key,
+                                        &buf,
+                                        &buf_size));
         /* use this key as long as it works */
         while (1)
         {
@@ -413,26 +442,83 @@ perf_signing (struct TALER_CRYPTO_DenominationHelper *dh)
 
           ds = TALER_CRYPTO_helper_denom_sign (dh,
                                                &keys[i].h_denom_pub,
-                                               pd.coin_ev,
-                                               pd.coin_ev_size,
+                                               buf,
+                                               buf_size,
                                                &ec);
           if (TALER_EC_NONE != ec)
             break;
           delay = GNUNET_TIME_absolute_get_duration (start);
           duration = GNUNET_TIME_relative_add (duration,
                                                delay);
-          TALER_blinded_denom_sig_free (&ds);
+          GNUNET_CRYPTO_rsa_signature_free (ds.rsa_signature);
           j++;
-          if (NUM_SIGN_TESTS == j)
+          if (NUM_SIGN_PERFS <= j)
             break;
         }
-        GNUNET_free (pd.coin_ev);
+        GNUNET_free (buf);
       }
     } /* for i */
   } /* for j */
   fprintf (stderr,
-           "%u (sequential) signature operations took %s\n",
-           (unsigned int) NUM_SIGN_TESTS,
+           "%u (%s) signature operations took %s\n",
+           (unsigned int) NUM_SIGN_PERFS,
+           type,
+           GNUNET_STRINGS_relative_time_to_string (duration,
+                                                   GNUNET_YES));
+  return 0;
+}
+
+
+/**
+ * Parallel signing logic.
+ *
+ * @param esh handle to the helper
+ * @return 0 on success
+ */
+static int
+par_signing (struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  struct GNUNET_TIME_Absolute start;
+  struct GNUNET_TIME_Relative duration;
+  pid_t pids[NUM_CORES];
+  struct TALER_CRYPTO_DenominationHelper *dh;
+
+  start = GNUNET_TIME_absolute_get ();
+  for (unsigned int i = 0; i<NUM_CORES; i++)
+  {
+    pids[i] = fork ();
+    memset (keys,
+            0,
+            sizeof (keys));
+    num_keys = 0;
+    GNUNET_assert (-1 != pids[i]);
+    if (0 == pids[i])
+    {
+      int ret;
+
+      dh = TALER_CRYPTO_helper_denom_connect (cfg,
+                                              &key_cb,
+                                              NULL);
+      GNUNET_assert (NULL != dh);
+      ret = perf_signing (dh,
+                          "parallel");
+      TALER_CRYPTO_helper_denom_disconnect (dh);
+      exit (ret);
+    }
+  }
+  for (unsigned int i = 0; i<NUM_CORES; i++)
+  {
+    int wstatus;
+
+    GNUNET_assert (pids[i] ==
+                   waitpid (pids[i],
+                            &wstatus,
+                            0));
+  }
+  duration = GNUNET_TIME_absolute_get_duration (start);
+  fprintf (stderr,
+           "%u (parallel) signature operations took %s (total real time)\n",
+           (unsigned int) NUM_SIGN_PERFS * NUM_CORES,
            GNUNET_STRINGS_relative_time_to_string (duration,
                                                    GNUNET_YES));
   return 0;
@@ -461,62 +547,57 @@ run_test (void)
     return 77;
   }
 
-  fprintf (stderr, "Waiting for helper client directory to become available ");
-  for (unsigned int i = 0; i<1000; i++)
+  fprintf (stderr, "Waiting for helper to start ... ");
+  for (unsigned int i = 0; i<100; i++)
   {
+    nanosleep (&req,
+               NULL);
     dh = TALER_CRYPTO_helper_denom_connect (cfg,
                                             &key_cb,
                                             NULL);
     if (NULL != dh)
       break;
-    nanosleep (&req, NULL);
     fprintf (stderr, ".");
   }
-  GNUNET_CONFIGURATION_destroy (cfg);
   if (NULL == dh)
   {
-    GNUNET_break (0);
+    fprintf (stderr,
+             "\nFAILED: timeout trying to connect to helper\n");
+    GNUNET_CONFIGURATION_destroy (cfg);
     return 1;
   }
-  fprintf (stderr, " done.\n");
-
-  /* wait for helper to start and give us keys */
-  fprintf (stderr, "Waiting for helper to start ");
-  for (unsigned int i = 0; i<1000; i++)
-  {
-    TALER_CRYPTO_helper_denom_poll (dh);
-    if (0 != num_keys)
-      break;
-    nanosleep (&req, NULL);
-    fprintf (stderr, ".");
-  }
   if (0 == num_keys)
   {
     fprintf (stderr,
              "\nFAILED: timeout trying to connect to helper\n");
     TALER_CRYPTO_helper_denom_disconnect (dh);
+    GNUNET_CONFIGURATION_destroy (cfg);
     return 1;
   }
   fprintf (stderr,
-           "\nOK: Helper ready (%u keys)\n",
+           " Done (%u keys)\n",
            num_keys);
-
   ret = 0;
   if (0 == ret)
     ret = test_revocation (dh);
   if (0 == ret)
     ret = test_signing (dh);
   if (0 == ret)
-    ret = perf_signing (dh);
+    ret = perf_signing (dh,
+                        "sequential");
   TALER_CRYPTO_helper_denom_disconnect (dh);
+  if (0 == ret)
+    ret = par_signing (cfg);
   /* clean up our state */
   for (unsigned int i = 0; i<MAX_KEYS; i++)
     if (keys[i].valid)
     {
-      TALER_denom_pub_free (&keys[i].denom_pub);
+      GNUNET_CRYPTO_rsa_public_key_free (keys[i].denom_pub.rsa_public_key);
+      keys[i].denom_pub.rsa_public_key = NULL;
       GNUNET_assert (num_keys > 0);
       num_keys--;
     }
+  GNUNET_CONFIGURATION_destroy (cfg);
   return ret;
 }
 
@@ -535,7 +616,7 @@ main (int argc,
   (void) argc;
   (void) argv;
   GNUNET_log_setup ("test-helper-rsa",
-                    "INFO",
+                    "WARNING",
                     NULL);
   GNUNET_OS_init (TALER_project_data_default ());
   libexec_dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
@@ -551,7 +632,7 @@ main (int argc,
                                     "-c",
                                     "test_helper_rsa.conf",
                                     "-L",
-                                    "INFO",
+                                    "WARNING",
                                     NULL);
   if (NULL == helper)
   {

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