gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated (0f5fc95e -> 19302786)


From: gnunet
Subject: [taler-exchange] branch master updated (0f5fc95e -> 19302786)
Date: Wed, 17 Nov 2021 20:52:42 +0100

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

grothoff pushed a change to branch master
in repository exchange.

    from 0f5fc95e towards merging with master
     new 6e86a3c4 -sms wip
     new 3da97b29 -sms wip
     new cc74bf31 sms merge wip
     new f7688837 -fix crypto_helper FTBFS
     new e51e60cf -checkpoint
     new 19302786 get util tests to pass

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/testing/testing_api_cmd_batch.c                |   84 +-
 src/testing/testing_api_loop.c                     |  175 ++-
 src/util/Makefile.am                               |    1 +
 ...on_age_restriction.c => crypto_helper_common.c} |   52 +-
 .../{secmod_common.h => crypto_helper_common.h}    |   22 +-
 src/util/crypto_helper_denom.c                     |  576 +++-----
 src/util/crypto_helper_esign.c                     |  525 +++----
 src/util/secmod_common.c                           |  434 +++++-
 src/util/secmod_common.h                           |  199 +++
 src/util/taler-exchange-secmod-eddsa.c             | 1288 +++++------------
 src/util/taler-exchange-secmod-rsa.c               | 1518 ++++++--------------
 src/util/taler-exchange-secmod-rsa.h               |    3 +
 src/util/test_helper_eddsa.c                       |  119 +-
 src/util/test_helper_rsa.c                         |  141 +-
 14 files changed, 2324 insertions(+), 2813 deletions(-)
 copy src/util/{extension_age_restriction.c => crypto_helper_common.c} (52%)
 copy src/util/{secmod_common.h => crypto_helper_common.h} (66%)

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/Makefile.am b/src/util/Makefile.am
index c9f2b997..eefdcef4 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -78,6 +78,7 @@ libtalerutil_la_SOURCES = \
   auditor_signatures.c \
   config.c \
   crypto.c \
+  crypto_helper_common.c \
   crypto_helper_denom.c \
   crypto_helper_esign.c \
   crypto_wire.c \
diff --git a/src/util/extension_age_restriction.c 
b/src/util/crypto_helper_common.c
similarity index 52%
copy from src/util/extension_age_restriction.c
copy to src/util/crypto_helper_common.c
index dbb4f3e2..2aa6dbac 100644
--- a/src/util/extension_age_restriction.c
+++ b/src/util/crypto_helper_common.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2020 Taler Systems SA
+  Copyright (C) 2020 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
@@ -14,36 +14,38 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file extension_age_restriction.c
- * @brief Utility functions regarding age restriction
- * @author Özgür Kesim
+ * @file util/secmod_common.c
+ * @brief Common functions for the exchange security modules
+ * @author Florian Dold <dold@taler.net>
  */
 #include "platform.h"
 #include "taler_util.h"
+#include "taler_signatures.h"
 
 
-/**
- *
- * @param cfg
- * @param[out] mask for age restriction
- * @return Error if extension for age restriction was set but age groups were
- *         invalid, OK otherwise.
- */
 enum GNUNET_GenericReturnValue
-TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
-                    struct TALER_AgeMask *mask)
+TALER_crypto_helper_send_all (int sock,
+                              const void *buf,
+                              size_t buf_size)
 {
-  /* FIXME-Oec:
-   *
-   * - Detect if age restriction is enabled in config
-   * - if not, return 0 mask
-   * - else, parse age group and serialize into mask
-   * - return Error on
-   *
-   * */
-  mask->mask = 0;
-  return GNUNET_OK;
-}
+  size_t off = 0;
 
+  while (off < buf_size)
+  {
+    ssize_t ret;
 
-/* end of extension_age_restriction.c */
+    ret = send (sock,
+                buf + off,
+                buf_size - off,
+                0);
+    if (ret < 0)
+    {
+      if (EINTR == errno)
+        continue;
+      return GNUNET_SYSERR;
+    }
+    GNUNET_assert (ret > 0);
+    off += ret;
+  }
+  return GNUNET_OK;
+}
diff --git a/src/util/secmod_common.h b/src/util/crypto_helper_common.h
similarity index 66%
copy from src/util/secmod_common.h
copy to src/util/crypto_helper_common.h
index ca1270fa..cf8b2814 100644
--- a/src/util/secmod_common.h
+++ b/src/util/crypto_helper_common.h
@@ -15,25 +15,27 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file util/secmod_common.h
+ * @file util/crypto_helper_common.h
  * @brief Common functions for the exchange security modules
  * @author Florian Dold <dold@taler.net>
  */
-#ifndef SECMOD_COMMON_H
-#define SECMOD_COMMON_H
+#ifndef CRYPTO_HELPER_COMMON_H
+#define CRYPTO_HELPER_COMMON_H
 
 #include <gnunet/gnunet_util_lib.h>
 #include <gnunet/gnunet_network_lib.h>
 
 /**
- * Create the listen socket for a secmod daemon.
+ * Send all @a buf_size bytes from @a buf to @a sock.
  *
- * This function is not thread-safe, as it changes and
- * restores the process umask.
- *
- * @param unixpath socket path
+ * @param sock socket to send on
+ * @param buf data to send
+ * @param buf_size number of bytes in @a buf
+ * @return #GNUNET_OK on success
  */
-struct GNUNET_NETWORK_Handle *
-TES_open_socket (const char *unixpath);
+enum GNUNET_GenericReturnValue
+TALER_crypto_helper_send_all (int sock,
+                              const void *buf,
+                              size_t buf_size);
 
 #endif
diff --git a/src/util/crypto_helper_denom.c b/src/util/crypto_helper_denom.c
index 6490184e..5aa60a24 100644
--- a/src/util/crypto_helper_denom.c
+++ b/src/util/crypto_helper_denom.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2020 Taler Systems SA
+  Copyright (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
@@ -20,10 +20,10 @@
  */
 #include "platform.h"
 #include "taler_util.h"
-#include "taler_extensions.h"
 #include "taler_signatures.h"
 #include "taler-exchange-secmod-rsa.h"
 #include <poll.h>
+#include "crypto_helper_common.h"
 
 
 struct TALER_CRYPTO_DenominationHelper
@@ -44,16 +44,6 @@ struct TALER_CRYPTO_DenominationHelper
    */
   struct sockaddr_un sa;
 
-  /**
-   * Socket address of this process.
-   */
-  struct sockaddr_un my_sa;
-
-  /**
-   * Template for @e my_sa.
-   */
-  char *template;
-
   /**
    * The UNIX domain socket, -1 if we are currently not connected.
    */
@@ -63,11 +53,6 @@ struct TALER_CRYPTO_DenominationHelper
    * Have we ever been sync'ed?
    */
   bool synced;
-
-  /**
-   * Age Mask that applies to this denomination.
-   */
-  struct TALER_AgeMask age_mask;
 };
 
 
@@ -81,10 +66,6 @@ static void
 do_disconnect (struct TALER_CRYPTO_DenominationHelper *dh)
 {
   GNUNET_break (0 == close (dh->sock));
-  if (0 != unlink (dh->my_sa.sun_path))
-    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
-                              "unlink",
-                              dh->my_sa.sun_path);
   dh->sock = -1;
   dh->synced = false;
 }
@@ -95,106 +76,34 @@ do_disconnect (struct TALER_CRYPTO_DenominationHelper *dh)
  * @e sock field in @a dh.
  *
  * @param[in,out] dh handle to establish connection for
+ * @return #GNUNET_OK on success
  */
-static void
+static enum GNUNET_GenericReturnValue
 try_connect (struct TALER_CRYPTO_DenominationHelper *dh)
 {
-  char *tmpdir;
-
   if (-1 != dh->sock)
-    return;
+    return GNUNET_OK;
   dh->sock = socket (AF_UNIX,
-                     SOCK_DGRAM,
+                     SOCK_STREAM,
                      0);
   if (-1 == dh->sock)
   {
     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
                          "socket");
-    return;
-  }
-  tmpdir = GNUNET_DISK_mktemp (dh->template);
-  if (NULL == tmpdir)
-  {
-    do_disconnect (dh);
-    return;
-  }
-  /* we use >= here because we want the sun_path to always
-     be 0-terminated */
-  if (strlen (tmpdir) >= sizeof (dh->sa.sun_path))
-  {
-    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                               "PATHS",
-                               "TALER_RUNTIME_DIR",
-                               "path too long");
-    GNUNET_free (tmpdir);
-    do_disconnect (dh);
-    return;
+    return GNUNET_SYSERR;
   }
-  dh->my_sa.sun_family = AF_UNIX;
-  strncpy (dh->my_sa.sun_path,
-           tmpdir,
-           sizeof (dh->sa.sun_path) - 1);
-  if (0 != unlink (tmpdir))
-    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
-                              "unlink",
-                              tmpdir);
-  if (0 != bind (dh->sock,
-                 (const struct sockaddr *) &dh->my_sa,
-                 sizeof (dh->my_sa)))
+  if (0 !=
+      connect (dh->sock,
+               (const struct sockaddr *) &dh->sa,
+               sizeof (dh->sa)))
   {
     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
-                              "bind",
-                              tmpdir);
+                              "connect",
+                              dh->sa.sun_path);
     do_disconnect (dh);
-    GNUNET_free (tmpdir);
-    return;
-  }
-  /* Fix permissions on client UNIX domain socket,
-     just in case umask() is not set to enable group write */
-  {
-    char path[sizeof (dh->my_sa.sun_path) + 1];
-
-    strncpy (path,
-             dh->my_sa.sun_path,
-             sizeof (path) - 1);
-    path[sizeof (dh->my_sa.sun_path)] = '\0';
-
-    if (0 != chmod (path,
-                    S_IRUSR | S_IWUSR | S_IWGRP))
-    {
-      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
-                                "chmod",
-                                path);
-    }
-  }
-  GNUNET_free (tmpdir);
-  {
-    struct GNUNET_MessageHeader hdr = {
-      .size = htons (sizeof (hdr)),
-      .type = htons (TALER_HELPER_RSA_MT_REQ_INIT)
-    };
-    ssize_t ret;
-
-    ret = sendto (dh->sock,
-                  &hdr,
-                  sizeof (hdr),
-                  0,
-                  (const struct sockaddr *) &dh->sa,
-                  sizeof (dh->sa));
-    if (ret < 0)
-    {
-      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
-                                "sendto",
-                                dh->sa.sun_path);
-      do_disconnect (dh);
-      return;
-    }
-    /* We are using SOCK_DGRAM, partial writes should not be possible */
-    GNUNET_break (((size_t) ret) == sizeof (hdr));
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Successfully sent REQ_INIT\n");
+    return GNUNET_SYSERR;
   }
-
+  return GNUNET_OK;
 }
 
 
@@ -238,63 +147,11 @@ TALER_CRYPTO_helper_denom_connect (
            sizeof (dh->sa.sun_path) - 1);
   GNUNET_free (unixpath);
   dh->sock = -1;
+  if (GNUNET_OK !=
+      try_connect (dh))
   {
-    char *tmpdir;
-    char *template;
-
-    if (GNUNET_OK !=
-        GNUNET_CONFIGURATION_get_value_filename (cfg,
-                                                 "taler-exchange-secmod-rsa",
-                                                 "CLIENT_DIR",
-                                                 &tmpdir))
-    {
-      GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                                 "taler-exchange-secmod-rsa",
-                                 "CLIENT_DIR");
-      return NULL;
-    }
-    GNUNET_asprintf (&template,
-                     "%s/cli",
-                     tmpdir);
-    /* We expect the service to create the client directory */
-    if (GNUNET_OK !=
-        GNUNET_DISK_directory_test (tmpdir,
-                                    GNUNET_YES))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Unable to read secmod client directory (%s)\n",
-                  tmpdir);
-      GNUNET_free (dh);
-      GNUNET_free (template);
-      GNUNET_free (tmpdir);
-      return NULL;
-    }
-    GNUNET_free (tmpdir);
-    dh->template = template;
-    if (strlen (template) >= sizeof (dh->sa.sun_path))
-    {
-      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                                 "PATHS",
-                                 "TALER_RUNTIME_DIR",
-                                 "path too long");
-      TALER_CRYPTO_helper_denom_disconnect (dh);
-      return NULL;
-    }
-
-    /* Extract the age groups from the config, if the extension has been set,
-     * and serialize them into the age mask
-     */
-    if (GNUNET_OK !=
-        TALER_get_age_mask (cfg, &dh->age_mask))
-    {
-      /* FIXME: maybe more specific error? */
-      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                                 "extensions", /* FIXME: right section etc? */
-                                 "age-restriction",
-                                 "invalid age groups");
-      TALER_CRYPTO_helper_denom_disconnect (dh);
-      return NULL;
-    }
+    TALER_CRYPTO_helper_denom_disconnect (dh);
+    return NULL;
   }
   TALER_CRYPTO_helper_denom_poll (dh);
   return dh;
@@ -342,7 +199,6 @@ handle_mt_avail (struct TALER_CRYPTO_DenominationHelper *dh,
     struct TALER_DenominationHash h_denom_pub;
 
     denom_pub.cipher = TALER_DENOMINATION_RSA;
-    denom_pub.age_mask = dh->age_mask;
     denom_pub.details.rsa_public_key
       = GNUNET_CRYPTO_rsa_public_key_decode (buf,
                                              ntohs (kan->pub_size));
@@ -351,8 +207,8 @@ handle_mt_avail (struct TALER_CRYPTO_DenominationHelper *dh,
       GNUNET_break_op (0);
       return GNUNET_SYSERR;
     }
-    TALER_denom_pub_hash (&denom_pub,
-                          &h_denom_pub);
+    GNUNET_CRYPTO_rsa_public_key_hash (denom_pub.details.rsa_public_key,
+                                       &h_denom_pub.hash);
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Received RSA key %s (%s)\n",
                 GNUNET_h2s (&h_denom_pub.hash),
@@ -367,7 +223,7 @@ handle_mt_avail (struct TALER_CRYPTO_DenominationHelper *dh,
           &kan->secm_sig))
     {
       GNUNET_break_op (0);
-      GNUNET_CRYPTO_rsa_public_key_free (denom_pub.details.rsa_public_key);
+      TALER_denom_pub_free (&denom_pub);
       return GNUNET_SYSERR;
     }
     dh->dkc (dh->dkc_cls,
@@ -378,7 +234,7 @@ handle_mt_avail (struct TALER_CRYPTO_DenominationHelper *dh,
              &denom_pub,
              &kan->secm_pub,
              &kan->secm_sig);
-    GNUNET_CRYPTO_rsa_public_key_free (denom_pub.details.rsa_public_key);
+    TALER_denom_pub_free (&denom_pub);
   }
   return GNUNET_OK;
 }
@@ -418,115 +274,62 @@ handle_mt_purge (struct TALER_CRYPTO_DenominationHelper 
*dh,
 }
 
 
-/**
- * Wait until the socket is ready to read.
- *
- * @param dh helper to wait for
- * @return false on timeout (after 1s)
- */
-static bool
-await_read_ready (struct TALER_CRYPTO_DenominationHelper *dh)
-{
-  /* wait for reply with 1s timeout */
-  struct pollfd pfd = {
-    .fd = dh->sock,
-    .events = POLLIN
-  };
-  sigset_t sigmask;
-  struct timespec ts = {
-    .tv_sec = 1
-  };
-  int ret;
-
-  GNUNET_assert (0 == sigemptyset (&sigmask));
-  GNUNET_assert (0 == sigaddset (&sigmask, SIGTERM));
-  GNUNET_assert (0 == sigaddset (&sigmask, SIGHUP));
-  ret = ppoll (&pfd,
-               1,
-               &ts,
-               &sigmask);
-  if ( (-1 == ret) &&
-       (EINTR != errno) )
-    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
-                         "ppoll");
-  return (0 < ret);
-}
-
-
 void
 TALER_CRYPTO_helper_denom_poll (struct TALER_CRYPTO_DenominationHelper *dh)
 {
   char buf[UINT16_MAX];
-  ssize_t ret;
+  size_t off = 0;
   unsigned int retry_limit = 3;
   const struct GNUNET_MessageHeader *hdr
     = (const struct GNUNET_MessageHeader *) buf;
-  int flag = MSG_DONTWAIT;
 
-  try_connect (dh);
-  if (-1 == dh->sock)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Cannot poll denom helper: socket down\n");
+  if (GNUNET_OK !=
+      try_connect (dh))
     return; /* give up */
-  }
   while (1)
   {
+    uint16_t msize;
+    ssize_t ret;
+
     ret = recv (dh->sock,
                 buf,
                 sizeof (buf),
-                flag);
+                (dh->synced && (0 == off))
+                ? MSG_DONTWAIT
+                : 0);
     if (ret < 0)
     {
+      if (EINTR == errno)
+        continue;
       if (EAGAIN == errno)
       {
-        /* EAGAIN should only happen if we did not
-           already go through this loop */
-        GNUNET_assert (0 != flag);
-        if (dh->synced)
-          break;
-        if (! await_read_ready (dh))
-        {
-          /* timeout AND not synced => full reconnect */
-          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                      "Restarting connection to RSA helper, did not come up 
properly\n");
-          do_disconnect (dh);
-          if (0 == retry_limit)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                        "Cannot poll denom helper: retry limit reached\n");
-            return; /* give up */
-          }
-          try_connect (dh);
-          if (-1 == dh->sock)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                        "Cannot poll denom helper: failed to connect\n");
-            return; /* give up */
-          }
-          retry_limit--;
-          flag = MSG_DONTWAIT;
-        }
-        else
-        {
-          flag = 0; /* syscall must be non-blocking this time */
-        }
-        continue; /* try again */
+        GNUNET_assert (dh->synced);
+        GNUNET_assert (0 == off);
+        break;
       }
       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
                            "recv");
       do_disconnect (dh);
-      return;
+      if (0 == retry_limit)
+        return; /* give up */
+      if (GNUNET_OK !=
+          try_connect (dh))
+        return; /* give up */
+      retry_limit--;
+      continue;
     }
-    retry_limit = 10;
-    flag = MSG_DONTWAIT;
-    if ( (ret < sizeof (struct GNUNET_MessageHeader)) ||
-         (ret != ntohs (hdr->size)) )
+    if (0 == ret)
     {
-      GNUNET_break_op (0);
-      do_disconnect (dh);
+      GNUNET_break (0 == off);
       return;
     }
+    off += ret;
+more:
+    if (off < sizeof (struct GNUNET_MessageHeader))
+      continue;
+    msize = ntohs (hdr->size);
+    if (off < msize)
+      continue;
     switch (ntohs (hdr->type))
     {
     case TALER_HELPER_RSA_MT_AVAIL:
@@ -555,10 +358,19 @@ TALER_CRYPTO_helper_denom_poll (struct 
TALER_CRYPTO_DenominationHelper *dh)
       dh->synced = true;
       break;
     default:
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Received unexpected message of type %d (len: %u)\n",
+                  (unsigned int) ntohs (hdr->type),
+                  (unsigned int) msize);
       GNUNET_break_op (0);
       do_disconnect (dh);
       return;
     }
+    memmove (buf,
+             &buf[msize],
+             off - msize);
+    off -= msize;
+    goto more;
   }
 }
 
@@ -572,22 +384,23 @@ TALER_CRYPTO_helper_denom_sign (
   enum TALER_ErrorCode *ec)
 {
   struct TALER_BlindedDenominationSignature ds = {
-    .details.blinded_rsa_signature = NULL
+    .cipher = TALER_DENOMINATION_INVALID
   };
+
+  if (GNUNET_OK !=
+      try_connect (dh))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to connect to helper\n");
+    *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+    return ds;
+  }
+
   {
     char buf[sizeof (struct TALER_CRYPTO_SignRequest) + msg_size];
     struct TALER_CRYPTO_SignRequest *sr
       = (struct TALER_CRYPTO_SignRequest *) buf;
-    ssize_t ret;
 
-    try_connect (dh);
-    if (-1 == dh->sock)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Failed to connect to helper\n");
-      *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
-      return ds;
-    }
     sr->header.size = htons (sizeof (buf));
     sr->header.type = htons (TALER_HELPER_RSA_MT_REQ_SIGN);
     sr->reserved = htonl (0);
@@ -595,131 +408,161 @@ TALER_CRYPTO_helper_denom_sign (
     memcpy (&sr[1],
             msg,
             msg_size);
-    ret = sendto (dh->sock,
-                  buf,
-                  sizeof (buf),
-                  0,
-                  (const struct sockaddr *) &dh->sa,
-                  sizeof (dh->sa));
-    if (ret < 0)
+    if (GNUNET_OK !=
+        TALER_crypto_helper_send_all (dh->sock,
+                                      buf,
+                                      sizeof (buf)))
     {
       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
-                           "sendto");
+                           "send");
       do_disconnect (dh);
       *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
       return ds;
     }
-    /* We are using SOCK_DGRAM, partial writes should not be possible */
-    GNUNET_break (((size_t) ret) == sizeof (buf));
   }
 
-  while (1)
   {
     char buf[UINT16_MAX];
-    ssize_t ret;
+    size_t off = 0;
     const struct GNUNET_MessageHeader *hdr
       = (const struct GNUNET_MessageHeader *) buf;
+    bool finished = false;
 
-    if (! await_read_ready (dh))
-    {
-      do_disconnect (dh);
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Timeout waiting for helper\n");
-      *ec = TALER_EC_GENERIC_TIMEOUT;
-      return ds;
-    }
-    ret = recv (dh->sock,
-                buf,
-                sizeof (buf),
-                0);
-    if (ret < 0)
-    {
-      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
-                           "recv");
-      do_disconnect (dh);
-      *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
-      return ds;
-    }
-    if ( (ret < sizeof (struct GNUNET_MessageHeader)) ||
-         (ret != ntohs (hdr->size)) )
+    *ec = TALER_EC_INVALID;
+    while (1)
     {
-      GNUNET_break_op (0);
-      do_disconnect (dh);
-      *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
-      return ds;
-    }
-    switch (ntohs (hdr->type))
-    {
-    case TALER_HELPER_RSA_MT_RES_SIGNATURE:
-      if (ret < sizeof (struct TALER_CRYPTO_SignResponse))
+      uint16_t msize;
+      ssize_t ret;
+
+      ret = recv (dh->sock,
+                  buf,
+                  sizeof (buf),
+                  (finished && (0 == off))
+                  ? MSG_DONTWAIT
+                  : 0);
+      if (ret < 0)
       {
-        GNUNET_break_op (0);
+        if (EINTR == errno)
+          continue;
+        if (EAGAIN == errno)
+        {
+          GNUNET_assert (finished);
+          GNUNET_assert (0 == off);
+          return ds;
+        }
+        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                             "recv");
         do_disconnect (dh);
-        *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+        *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE;
+        break;
+      }
+      if (0 == ret)
+      {
+        GNUNET_break (0 == off);
+        if (! finished)
+          *ec = TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
         return ds;
       }
+      off += ret;
+more:
+      if (off < sizeof (struct GNUNET_MessageHeader))
+        continue;
+      msize = ntohs (hdr->size);
+      if (off < msize)
+        continue;
+      switch (ntohs (hdr->type))
       {
-        const struct TALER_CRYPTO_SignResponse *sr =
-          (const struct TALER_CRYPTO_SignResponse *) buf;
-        struct GNUNET_CRYPTO_RsaSignature *rsa_signature;
-
-        rsa_signature = GNUNET_CRYPTO_rsa_signature_decode (&sr[1],
-                                                            ret - sizeof 
(*sr));
-        if (NULL == rsa_signature)
+      case TALER_HELPER_RSA_MT_RES_SIGNATURE:
+        if ( (msize < sizeof (struct TALER_CRYPTO_SignResponse)) ||
+             (finished) )
         {
           GNUNET_break_op (0);
           do_disconnect (dh);
           *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
-          return ds;
+          goto end;
         }
-        *ec = TALER_EC_NONE;
-        ds.cipher = TALER_DENOMINATION_RSA;
-        ds.details.blinded_rsa_signature = rsa_signature;
-        return ds;
-      }
-    case TALER_HELPER_RSA_MT_RES_SIGN_FAILURE:
-      if (ret != sizeof (struct TALER_CRYPTO_SignFailure))
-      {
-        GNUNET_break_op (0);
-        do_disconnect (dh);
-        *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
-        return ds;
-      }
-      {
-        const struct TALER_CRYPTO_SignFailure *sf =
-          (const struct TALER_CRYPTO_SignFailure *) buf;
+        {
+          const struct TALER_CRYPTO_SignResponse *sr =
+            (const struct TALER_CRYPTO_SignResponse *) buf;
+          struct GNUNET_CRYPTO_RsaSignature *rsa_signature;
+
+          rsa_signature = GNUNET_CRYPTO_rsa_signature_decode (
+            &sr[1],
+            msize - sizeof (*sr));
+          if (NULL == rsa_signature)
+          {
+            GNUNET_break_op (0);
+            do_disconnect (dh);
+            *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+            goto end;
+          }
+          *ec = TALER_EC_NONE;
+          finished = true;
+          ds.cipher = TALER_DENOMINATION_RSA;
+          ds.details.blinded_rsa_signature = rsa_signature;
+          break;
+        }
+      case TALER_HELPER_RSA_MT_RES_SIGN_FAILURE:
+        if (msize != sizeof (struct TALER_CRYPTO_SignFailure))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          goto end;
+        }
+        {
+          const struct TALER_CRYPTO_SignFailure *sf =
+            (const struct TALER_CRYPTO_SignFailure *) buf;
 
-        *ec = (enum TALER_ErrorCode) ntohl (sf->ec);
-        return ds;
-      }
-    case TALER_HELPER_RSA_MT_AVAIL:
-      if (GNUNET_OK !=
-          handle_mt_avail (dh,
-                           hdr))
-      {
-        GNUNET_break_op (0);
-        do_disconnect (dh);
-        *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
-        return ds;
-      }
-      break; /* while(1) loop ensures we recvfrom() again */
-    case TALER_HELPER_RSA_MT_PURGE:
-      if (GNUNET_OK !=
-          handle_mt_purge (dh,
-                           hdr))
-      {
+          *ec = (enum TALER_ErrorCode) ntohl (sf->ec);
+          return ds;
+        }
+      case TALER_HELPER_RSA_MT_AVAIL:
+        if (GNUNET_OK !=
+            handle_mt_avail (dh,
+                             hdr))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          goto end;
+        }
+        break; /* while(1) loop ensures we recvfrom() again */
+      case TALER_HELPER_RSA_MT_PURGE:
+        if (GNUNET_OK !=
+            handle_mt_purge (dh,
+                             hdr))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (dh);
+          *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
+          goto end;
+        }
+        break; /* while(1) loop ensures we recvfrom() again */
+      case TALER_HELPER_RSA_SYNCED:
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Synchronized add odd time with RSA helper!\n");
+        dh->synced = true;
+        break;
+      default:
         GNUNET_break_op (0);
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Received unexpected message of type %u\n",
+                    ntohs (hdr->type));
         do_disconnect (dh);
         *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
-        return ds;
+        goto end;
       }
-      break; /* while(1) loop ensures we recvfrom() again */
-    default:
-      GNUNET_break_op (0);
-      do_disconnect (dh);
-      *ec = TALER_EC_EXCHANGE_DENOMINATION_HELPER_BUG;
-      return ds;
-    }
+      memmove (buf,
+               &buf[msize],
+               off - msize);
+      off -= msize;
+      goto more;
+    } /* while(1) */
+end:
+    if (finished)
+      TALER_blinded_denom_sig_free (&ds);
+    return ds;
   }
 }
 
@@ -734,26 +577,20 @@ TALER_CRYPTO_helper_denom_revoke (
     .header.type = htons (TALER_HELPER_RSA_MT_REQ_REVOKE),
     .h_denom_pub = *h_denom_pub
   };
-  ssize_t ret;
 
-  try_connect (dh);
-  if (-1 == dh->sock)
+  if (GNUNET_OK !=
+      try_connect (dh))
     return; /* give up */
-  ret = sendto (dh->sock,
-                &rr,
-                sizeof (rr),
-                0,
-                (const struct sockaddr *) &dh->sa,
-                sizeof (dh->sa));
-  if (ret < 0)
+  if (GNUNET_OK !=
+      TALER_crypto_helper_send_all (dh->sock,
+                                    &rr,
+                                    sizeof (rr)))
   {
     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
-                         "sendto");
+                         "send");
     do_disconnect (dh);
     return;
   }
-  /* We are using SOCK_DGRAM, partial writes should not be possible */
-  GNUNET_break (((size_t) ret) == sizeof (rr));
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Requested revocation of denomination key %s\n",
               GNUNET_h2s (&h_denom_pub->hash));
@@ -766,7 +603,6 @@ TALER_CRYPTO_helper_denom_disconnect (
 {
   if (-1 != dh->sock)
     do_disconnect (dh);
-  GNUNET_free (dh->template);
   GNUNET_free (dh);
 }
 
diff --git a/src/util/crypto_helper_esign.c b/src/util/crypto_helper_esign.c
index 6257a1d2..794a916a 100644
--- a/src/util/crypto_helper_esign.c
+++ b/src/util/crypto_helper_esign.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2020 Taler Systems SA
+  Copyright (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
@@ -23,6 +23,7 @@
 #include "taler_signatures.h"
 #include "taler-exchange-secmod-eddsa.h"
 #include <poll.h>
+#include "crypto_helper_common.h"
 
 
 struct TALER_CRYPTO_ExchangeSignHelper
@@ -43,16 +44,6 @@ struct TALER_CRYPTO_ExchangeSignHelper
    */
   struct sockaddr_un sa;
 
-  /**
-   * Socket address of this process.
-   */
-  struct sockaddr_un my_sa;
-
-  /**
-   * Template for @e my_sa.
-   */
-  char *template;
-
   /**
    * The UNIX domain socket, -1 if we are currently not connected.
    */
@@ -76,10 +67,6 @@ static void
 do_disconnect (struct TALER_CRYPTO_ExchangeSignHelper *esh)
 {
   GNUNET_break (0 == close (esh->sock));
-  if (0 != unlink (esh->my_sa.sun_path))
-    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
-                              "unlink",
-                              esh->my_sa.sun_path);
   esh->sock = -1;
   esh->synced = false;
 }
@@ -90,107 +77,34 @@ do_disconnect (struct TALER_CRYPTO_ExchangeSignHelper *esh)
  * @e sock field in @a esh.
  *
  * @param[in,out] esh handle to establish connection for
+ * @return #GNUNET_OK on success
  */
-static void
+static enum GNUNET_GenericReturnValue
 try_connect (struct TALER_CRYPTO_ExchangeSignHelper *esh)
 {
-  char *tmpdir;
-
   if (-1 != esh->sock)
-    return;
+    return GNUNET_OK;
   esh->sock = socket (AF_UNIX,
-                      SOCK_DGRAM,
+                      SOCK_STREAM,
                       0);
   if (-1 == esh->sock)
   {
     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
                          "socket");
-    return;
-  }
-  tmpdir = GNUNET_DISK_mktemp (esh->template);
-  if (NULL == tmpdir)
-  {
-    do_disconnect (esh);
-    return;
-  }
-  /* we use >= here because we want the sun_path to always
-     be 0-terminated */
-  if (strlen (tmpdir) >= sizeof (esh->sa.sun_path))
-  {
-    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                               "PATHS",
-                               "TALER_RUNTIME_DIR",
-                               "path too long");
-    GNUNET_free (tmpdir);
-    do_disconnect (esh);
-    return;
+    return GNUNET_SYSERR;
   }
-  esh->my_sa.sun_family = AF_UNIX;
-  strncpy (esh->my_sa.sun_path,
-           tmpdir,
-           sizeof (esh->sa.sun_path) - 1);
-  if (0 != unlink (tmpdir))
-    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
-                              "unlink",
-                              tmpdir);
-  if (0 != bind (esh->sock,
-                 (const struct sockaddr *) &esh->my_sa,
-                 sizeof (esh->my_sa)))
+  if (0 !=
+      connect (esh->sock,
+               (const struct sockaddr *) &esh->sa,
+               sizeof (esh->sa)))
   {
     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
-                              "bind",
-                              tmpdir);
+                              "connect",
+                              esh->sa.sun_path);
     do_disconnect (esh);
-    GNUNET_free (tmpdir);
-    return;
-  }
-  /* Fix permissions on client UNIX domain socket,
-     just in case umask() is not set to enable group write */
-  {
-    char path[sizeof (esh->my_sa.sun_path) + 1];
-
-    strncpy (path,
-             esh->my_sa.sun_path,
-             sizeof (path) - 1);
-    path[sizeof (esh->my_sa.sun_path)] = '\0';
-
-    if (0 != chmod (path,
-                    S_IRUSR | S_IWUSR | S_IWGRP))
-    {
-      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
-                                "chmod",
-                                path);
-    }
-  }
-
-  GNUNET_free (tmpdir);
-  {
-    struct GNUNET_MessageHeader hdr = {
-      .size = htons (sizeof (hdr)),
-      .type = htons (TALER_HELPER_EDDSA_MT_REQ_INIT)
-    };
-    ssize_t ret;
-
-    ret = sendto (esh->sock,
-                  &hdr,
-                  sizeof (hdr),
-                  0,
-                  (const struct sockaddr *) &esh->sa,
-                  sizeof (esh->sa));
-    if (ret < 0)
-    {
-      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
-                                "sendto",
-                                esh->sa.sun_path);
-      do_disconnect (esh);
-      return;
-    }
-    /* We are using SOCK_DGRAM, partial writes should not be possible */
-    GNUNET_break (((size_t) ret) == sizeof (hdr));
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Successfully sent REQ_INIT\n");
+    return GNUNET_SYSERR;
   }
-
+  return GNUNET_OK;
 }
 
 
@@ -234,50 +148,13 @@ TALER_CRYPTO_helper_esign_connect (
            sizeof (esh->sa.sun_path) - 1);
   GNUNET_free (unixpath);
   esh->sock = -1;
+  if (GNUNET_OK !=
+      try_connect (esh))
   {
-    char *tmpdir;
-    char *template;
-
-    if (GNUNET_OK !=
-        GNUNET_CONFIGURATION_get_value_filename (cfg,
-                                                 "taler-exchange-secmod-eddsa",
-                                                 "CLIENT_DIR",
-                                                 &tmpdir))
-    {
-      GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                                 "taler-exchange-secmod-eddsa",
-                                 "CLIENT_DIR");
-      GNUNET_free (esh);
-      return NULL;
-    }
-    GNUNET_asprintf (&template,
-                     "%s/cli",
-                     tmpdir);
-    /* We expect the service to create the client directory */
-    if (GNUNET_OK !=
-        GNUNET_DISK_directory_test (tmpdir,
-                                    GNUNET_YES))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Unable to read secmod client directory (%s)\n",
-                  tmpdir);
-      GNUNET_free (esh);
-      GNUNET_free (template);
-      GNUNET_free (tmpdir);
-      return NULL;
-    }
-    GNUNET_free (tmpdir);
-    esh->template = template;
-    if (strlen (template) >= sizeof (esh->sa.sun_path))
-    {
-      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                                 "PATHS",
-                                 "TALER_RUNTIME_DIR",
-                                 "path too long");
-      TALER_CRYPTO_helper_esign_disconnect (esh);
-      return NULL;
-    }
+    TALER_CRYPTO_helper_esign_disconnect (esh);
+    return NULL;
   }
+
   TALER_CRYPTO_helper_esign_poll (esh);
   return esh;
 }
@@ -290,7 +167,7 @@ TALER_CRYPTO_helper_esign_connect (
  * @param hdr message that we received
  * @return #GNUNET_OK on success
  */
-static int
+static enum GNUNET_GenericReturnValue
 handle_mt_avail (struct TALER_CRYPTO_ExchangeSignHelper *esh,
                  const struct GNUNET_MessageHeader *hdr)
 {
@@ -330,7 +207,7 @@ handle_mt_avail (struct TALER_CRYPTO_ExchangeSignHelper 
*esh,
  * @param hdr message that we received
  * @return #GNUNET_OK on success
  */
-static int
+static enum GNUNET_GenericReturnValue
 handle_mt_purge (struct TALER_CRYPTO_ExchangeSignHelper *esh,
                  const struct GNUNET_MessageHeader *hdr)
 {
@@ -352,101 +229,62 @@ handle_mt_purge (struct TALER_CRYPTO_ExchangeSignHelper 
*esh,
 }
 
 
-/**
- * Wait until the socket is ready to read.
- *
- * @param esh helper to wait for
- * @return false on timeout (after 1s)
- */
-static bool
-await_read_ready (struct TALER_CRYPTO_ExchangeSignHelper *esh)
-{
-  /* wait for reply with 1s timeout */
-  struct pollfd pfd = {
-    .fd = esh->sock,
-    .events = POLLIN
-  };
-  sigset_t sigmask;
-  struct timespec ts = {
-    .tv_sec = 1
-  };
-  int ret;
-
-  GNUNET_assert (0 == sigemptyset (&sigmask));
-  GNUNET_assert (0 == sigaddset (&sigmask, SIGTERM));
-  GNUNET_assert (0 == sigaddset (&sigmask, SIGHUP));
-  ret = ppoll (&pfd,
-               1,
-               &ts,
-               &sigmask);
-  if ( (-1 == ret) &&
-       (EINTR != errno) )
-    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
-                         "ppoll");
-  return (0 < ret);
-}
-
-
 void
 TALER_CRYPTO_helper_esign_poll (struct TALER_CRYPTO_ExchangeSignHelper *esh)
 {
   char buf[UINT16_MAX];
-  ssize_t ret;
+  size_t off = 0;
   unsigned int retry_limit = 3;
   const struct GNUNET_MessageHeader *hdr
     = (const struct GNUNET_MessageHeader *) buf;
-  int flag = MSG_DONTWAIT;
 
-  try_connect (esh);
-  if (-1 == esh->sock)
+  if (GNUNET_OK !=
+      try_connect (esh))
     return; /* give up */
   while (1)
   {
+    uint16_t msize;
+    ssize_t ret;
+
     ret = recv (esh->sock,
-                buf,
-                sizeof (buf),
-                flag);
+                buf + off,
+                sizeof (buf) - off,
+                (esh->synced && (0 == off))
+                ? MSG_DONTWAIT
+                : 0);
     if (ret < 0)
     {
+      if (EINTR == errno)
+        continue;
       if (EAGAIN == errno)
       {
-        GNUNET_assert (0 != flag);
-        if (esh->synced)
-          break;
-        if (! await_read_ready (esh))
-        {
-          /* timeout AND not synced => full reconnect */
-          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                      "Restarting connection to EdDSA helper, did not come up 
properly\n");
-          do_disconnect (esh);
-          if (0 == retry_limit)
-            return; /* give up */
-          try_connect (esh);
-          if (-1 == esh->sock)
-            return; /* give up */
-          retry_limit--;
-          flag = MSG_DONTWAIT;
-        }
-        else
-        {
-          flag = 0; /* syscall must be non-blocking this time */
-        }
-        continue; /* try again */
+        GNUNET_assert (esh->synced);
+        GNUNET_assert (0 == off);
+        break;
       }
       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
                            "recv");
       do_disconnect (esh);
-      return;
+      if (0 == retry_limit)
+        return; /* give up */
+      if (GNUNET_OK !=
+          try_connect (esh))
+        return; /* give up */
+      retry_limit--;
+      continue;
     }
-
-    flag = MSG_DONTWAIT;
-    if ( (ret < sizeof (struct GNUNET_MessageHeader)) ||
-         (ret != ntohs (hdr->size)) )
+    if (0 == ret)
     {
-      GNUNET_break_op (0);
-      do_disconnect (esh);
+      GNUNET_break (0 == off);
       return;
     }
+    off += ret;
+more:
+    if (off < sizeof (struct GNUNET_MessageHeader))
+      continue;
+    msize = ntohs (hdr->size);
+    if (off < msize)
+      continue;
     switch (ntohs (hdr->type))
     {
     case TALER_HELPER_EDDSA_MT_AVAIL:
@@ -475,10 +313,19 @@ TALER_CRYPTO_helper_esign_poll (struct 
TALER_CRYPTO_ExchangeSignHelper *esh)
       esh->synced = true;
       break;
     default:
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Received unexpected message of type %d (len: %u)\n",
+                  (unsigned int) ntohs (hdr->type),
+                  (unsigned int) msize);
       GNUNET_break_op (0);
       do_disconnect (esh);
       return;
     }
+    memmove (buf,
+             &buf[msize],
+             off - msize);
+    off -= msize;
+    goto more;
   }
 }
 
@@ -490,131 +337,164 @@ TALER_CRYPTO_helper_esign_sign_ (
   struct TALER_ExchangePublicKeyP *exchange_pub,
   struct TALER_ExchangeSignatureP *exchange_sig)
 {
+  if (GNUNET_OK !=
+      try_connect (esh))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Failed to connect to helper\n");
+    return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
+  }
   {
     uint32_t purpose_size = ntohl (purpose->size);
     char buf[sizeof (struct TALER_CRYPTO_EddsaSignRequest) + purpose_size
              - sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose)];
     struct TALER_CRYPTO_EddsaSignRequest *sr
       = (struct TALER_CRYPTO_EddsaSignRequest *) buf;
-    ssize_t ret;
 
-    try_connect (esh);
-    if (-1 == esh->sock)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Failed to connect to helper\n");
-      return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
-    }
     sr->header.size = htons (sizeof (buf));
     sr->header.type = htons (TALER_HELPER_EDDSA_MT_REQ_SIGN);
     sr->reserved = htonl (0);
     memcpy (&sr->purpose,
             purpose,
             purpose_size);
-    ret = sendto (esh->sock,
-                  buf,
-                  sizeof (buf),
-                  0,
-                  (const struct sockaddr *) &esh->sa,
-                  sizeof (esh->sa));
-    if (ret < 0)
+    if (GNUNET_OK !=
+        TALER_crypto_helper_send_all (esh->sock,
+                                      buf,
+                                      sizeof (buf)))
     {
       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
-                                "sendto",
+                                "send",
                                 esh->sa.sun_path);
       do_disconnect (esh);
       return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
     }
-    /* We are using SOCK_DGRAM, partial writes should not be possible */
-    GNUNET_break (((size_t) ret) == sizeof (buf));
   }
 
-  while (1)
   {
     char buf[UINT16_MAX];
-    ssize_t ret;
+    size_t off = 0;
     const struct GNUNET_MessageHeader *hdr
       = (const struct GNUNET_MessageHeader *) buf;
+    bool finished = false;
+    enum TALER_ErrorCode ec = TALER_EC_INVALID;
 
-    if (! await_read_ready (esh))
-    {
-      do_disconnect (esh);
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Timeout waiting for helper\n");
-      return TALER_EC_GENERIC_TIMEOUT;
-    }
-    ret = recv (esh->sock,
-                buf,
-                sizeof (buf),
-                0);
-    if (ret < 0)
-    {
-      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
-                           "recv");
-      do_disconnect (esh);
-      return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
-    }
-    if ( (ret < sizeof (struct GNUNET_MessageHeader)) ||
-         (ret != ntohs (hdr->size)) )
-    {
-      GNUNET_break_op (0);
-      do_disconnect (esh);
-      return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
-    }
-    switch (ntohs (hdr->type))
+    while (1)
     {
-    case TALER_HELPER_EDDSA_MT_RES_SIGNATURE:
-      if (ret != sizeof (struct TALER_CRYPTO_EddsaSignResponse))
+      ssize_t ret;
+      uint16_t msize;
+
+      ret = recv (esh->sock,
+                  buf,
+                  sizeof (buf),
+                  (finished && (0 == off))
+                  ? MSG_DONTWAIT
+                  : 0);
+      if (ret < 0)
       {
-        GNUNET_break_op (0);
+        if (EINTR == errno)
+          continue;
+        if (EAGAIN == errno)
+        {
+          GNUNET_assert (finished);
+          GNUNET_assert (0 == off);
+          break;
+        }
+        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                             "recv");
         do_disconnect (esh);
-        return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
-      }
-      {
-        const struct TALER_CRYPTO_EddsaSignResponse *sr =
-          (const struct TALER_CRYPTO_EddsaSignResponse *) buf;
-        *exchange_sig = sr->exchange_sig;
-        *exchange_pub = sr->exchange_pub;
-        return TALER_EC_NONE;
+        return TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE;
       }
-    case TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE:
-      if (ret != sizeof (struct TALER_CRYPTO_EddsaSignFailure))
+      if (0 == ret)
       {
-        GNUNET_break_op (0);
-        do_disconnect (esh);
+        GNUNET_break (0 == off);
         return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
       }
+      off += ret;
+more:
+      if (off < sizeof (struct GNUNET_MessageHeader))
+        continue;
+      msize = ntohs (hdr->size);
+      if (off < msize)
+        continue;
+      switch (ntohs (hdr->type))
       {
-        const struct TALER_CRYPTO_EddsaSignFailure *sf =
-          (const struct TALER_CRYPTO_EddsaSignFailure *) buf;
+      case TALER_HELPER_EDDSA_MT_RES_SIGNATURE:
+        if (msize != sizeof (struct TALER_CRYPTO_EddsaSignResponse))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (esh);
+          return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
+        }
+        if (finished)
+        {
+          GNUNET_break_op (0);
+          do_disconnect (esh);
+          return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
+        }
+        {
+          const struct TALER_CRYPTO_EddsaSignResponse *sr =
+            (const struct TALER_CRYPTO_EddsaSignResponse *) buf;
+          *exchange_sig = sr->exchange_sig;
+          *exchange_pub = sr->exchange_pub;
+          finished = true;
+          ec = TALER_EC_NONE;
+          break;
+        }
+      case TALER_HELPER_EDDSA_MT_RES_SIGN_FAILURE:
+        if (msize != sizeof (struct TALER_CRYPTO_EddsaSignFailure))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (esh);
+          return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
+        }
+        {
+          const struct TALER_CRYPTO_EddsaSignFailure *sf =
+            (const struct TALER_CRYPTO_EddsaSignFailure *) buf;
 
-        return (enum TALER_ErrorCode) ntohl (sf->ec);
-      }
-    case TALER_HELPER_EDDSA_MT_AVAIL:
-      if (GNUNET_OK !=
-          handle_mt_avail (esh,
-                           hdr))
-      {
-        GNUNET_break_op (0);
-        do_disconnect (esh);
-        return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
-      }
-      break; /* while(1) loop ensures we recvfrom() again */
-    case TALER_HELPER_EDDSA_MT_PURGE:
-      if (GNUNET_OK !=
-          handle_mt_purge (esh,
-                           hdr))
-      {
+          finished = true;
+          ec = (enum TALER_ErrorCode) ntohl (sf->ec);
+          break;
+        }
+      case TALER_HELPER_EDDSA_MT_AVAIL:
+        if (GNUNET_OK !=
+            handle_mt_avail (esh,
+                             hdr))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (esh);
+          return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
+        }
+        break; /* while(1) loop ensures we recv() again */
+      case TALER_HELPER_EDDSA_MT_PURGE:
+        if (GNUNET_OK !=
+            handle_mt_purge (esh,
+                             hdr))
+        {
+          GNUNET_break_op (0);
+          do_disconnect (esh);
+          return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
+        }
+        break; /* while(1) loop ensures we recv() again */
+      case TALER_HELPER_EDDSA_SYNCED:
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Synchronized add odd time with EdDSA helper!\n");
+        esh->synced = true;
+        break;
+      default:
         GNUNET_break_op (0);
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Received unexpected message of type %u\n",
+                    ntohs (hdr->type));
         do_disconnect (esh);
         return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
       }
-      break; /* while(1) loop ensures we recvfrom() again */
-    default:
-      GNUNET_break_op (0);
-      do_disconnect (esh);
-      return TALER_EC_EXCHANGE_SIGNKEY_HELPER_BUG;
-    }
+      memmove (buf,
+               &buf[msize],
+               off - msize);
+      off -= msize;
+      goto more;
+    } /* while(1) */
+    return ec;
   }
 }
 
@@ -624,31 +504,27 @@ TALER_CRYPTO_helper_esign_revoke (
   struct TALER_CRYPTO_ExchangeSignHelper *esh,
   const struct TALER_ExchangePublicKeyP *exchange_pub)
 {
-  struct TALER_CRYPTO_EddsaRevokeRequest rr = {
-    .header.size = htons (sizeof (rr)),
-    .header.type = htons (TALER_HELPER_EDDSA_MT_REQ_REVOKE),
-    .exchange_pub = *exchange_pub
-  };
-  ssize_t ret;
-
-  try_connect (esh);
-  if (-1 == esh->sock)
+  if (GNUNET_OK !=
+      try_connect (esh))
     return; /* give up */
-  ret = sendto (esh->sock,
-                &rr,
-                sizeof (rr),
-                0,
-                (const struct sockaddr *) &esh->sa,
-                sizeof (esh->sa));
-  if (ret < 0)
   {
-    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
-                         "sendto");
-    do_disconnect (esh);
-    return;
+    struct TALER_CRYPTO_EddsaRevokeRequest rr = {
+      .header.size = htons (sizeof (rr)),
+      .header.type = htons (TALER_HELPER_EDDSA_MT_REQ_REVOKE),
+      .exchange_pub = *exchange_pub
+    };
+
+    if (GNUNET_OK !=
+        TALER_crypto_helper_send_all (esh->sock,
+                                      &rr,
+                                      sizeof (rr)))
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                           "send");
+      do_disconnect (esh);
+      return;
+    }
   }
-  /* We are using SOCK_DGRAM, partial writes should not be possible */
-  GNUNET_break (((size_t) ret) == sizeof (rr));
 }
 
 
@@ -658,7 +534,6 @@ TALER_CRYPTO_helper_esign_disconnect (
 {
   if (-1 != esh->sock)
     do_disconnect (esh);
-  GNUNET_free (esh->template);
   GNUNET_free (esh);
 }
 
diff --git a/src/util/secmod_common.c b/src/util/secmod_common.c
index 2e73e44b..229942c9 100644
--- a/src/util/secmod_common.c
+++ b/src/util/secmod_common.c
@@ -21,6 +21,97 @@
 #include "platform.h"
 #include "taler_util.h"
 #include "taler_signatures.h"
+#include "secmod_common.h"
+#include <poll.h>
+#include <sys/eventfd.h>
+
+
+/**
+ * Head of DLL of clients connected to us.
+ */
+struct TES_Client *TES_clients_head;
+
+/**
+ * Tail of DLL of clients connected to us.
+ */
+struct TES_Client *TES_clients_tail;
+
+/**
+ * Lock for the client queue.
+ */
+pthread_mutex_t TES_clients_lock;
+
+/**
+ * Private key of this security module. Used to sign denomination key
+ * announcements.
+ */
+struct TALER_SecurityModulePrivateKeyP TES_smpriv;
+
+/**
+ * Public key of this security module.
+ */
+struct TALER_SecurityModulePublicKeyP TES_smpub;
+
+/**
+ * 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 *listen_task;
+
+/**
+ * Set once we are in shutdown and workers should terminate.
+ */
+static volatile bool in_shutdown;
+
+
+enum GNUNET_GenericReturnValue
+TES_transmit (int sock,
+              const struct GNUNET_MessageHeader *hdr)
+{
+  ssize_t off = 0;
+  const void *pos = hdr;
+  uint16_t end = ntohs (hdr->size);
+
+  while (off < end)
+  {
+    ssize_t ret = send (sock,
+                        pos,
+                        end - off,
+                        0 /* no flags => blocking! */);
+    if ( (-1 == ret) &&
+         ( (EAGAIN == errno) ||
+           (EINTR == errno) ) )
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG,
+                           "send");
+      continue;
+    }
+    if (-1 == ret)
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                           "send");
+      return GNUNET_SYSERR;
+    }
+    if (0 == ret)
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    off += ret;
+    pos += ret;
+  }
+  return GNUNET_OK;
+}
+
 
 struct GNUNET_NETWORK_Handle *
 TES_open_socket (const char *unixpath)
@@ -37,7 +128,7 @@ TES_open_socket (const char *unixpath)
   old_umask = umask (S_IROTH | S_IWOTH | S_IXOTH);
 
   sock = socket (PF_UNIX,
-                 SOCK_DGRAM,
+                 SOCK_STREAM,
                  0);
   if (-1 == sock)
   {
@@ -80,8 +171,349 @@ TES_open_socket (const char *unixpath)
       goto cleanup;
     }
     ret = GNUNET_NETWORK_socket_box_native (sock);
+    if (GNUNET_OK !=
+        GNUNET_NETWORK_socket_listen (ret,
+                                      512))
+    {
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+                                "listen",
+                                unixpath);
+      GNUNET_break (GNUNET_OK ==
+                    GNUNET_NETWORK_socket_close (ret));
+      ret = NULL;
+    }
   }
 cleanup:
   (void) umask (old_umask);
   return ret;
 }
+
+
+/**
+ * Send a signal to all clients to notify them about a key generation change.
+ */
+void
+TES_wake_clients (void)
+{
+  uint64_t num = 1;
+
+  GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
+  for (struct TES_Client *client = TES_clients_head;
+       NULL != client;
+       client = client->next)
+  {
+    GNUNET_assert (sizeof (num) ==
+                   write (client->esock,
+                          &num,
+                          sizeof (num)));
+  }
+  GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
+}
+
+
+/**
+ * Read work request from the client.
+ *
+ * @param cls a `struct TES_Client *`
+ * @param dispatch function to call with work requests received
+ * @return #GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+TES_read_work (void *cls,
+               TES_MessageDispatch dispatch)
+{
+  struct TES_Client *client = cls;
+  char *buf = client->iobuf;
+  ssize_t buf_size;
+  size_t off = 0;
+  uint16_t msize;
+  const struct GNUNET_MessageHeader *hdr;
+
+  do
+  {
+    buf_size = recv (client->csock,
+                     &buf[off],
+                     sizeof (client->iobuf) - off,
+                     0);
+    if (-1 == buf_size)
+    {
+      if ( (0 == off) &&
+           (EAGAIN == errno) )
+        return GNUNET_NO;
+      if ( (EINTR == errno) ||
+           (EAGAIN == errno) )
+      {
+        GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG,
+                             "recv");
+        continue;
+      }
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                           "recv");
+      return GNUNET_SYSERR;
+    }
+    if (0 == buf_size)
+    {
+      /* regular disconnect? */
+      GNUNET_break_op (0 == off);
+      return GNUNET_SYSERR;
+    }
+    off += buf_size;
+    if (off < sizeof (struct GNUNET_MessageHeader))
+      continue;
+    hdr = (const struct GNUNET_MessageHeader *) buf;
+    msize = ntohs (hdr->size);
+  } while (off < msize);
+
+  if (off > msize)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return dispatch (client,
+                   hdr);
+}
+
+
+bool
+TES_await_ready (struct TES_Client *client)
+{
+  /* wait for reply with 1s timeout */
+  struct pollfd pfds[] = {
+    {
+      .fd = client->csock,
+      .events = POLLIN
+    },
+    {
+      .fd = client->esock,
+      .events = POLLIN
+    },
+  };
+  int ret;
+
+  ret = poll (pfds,
+              2,
+              -1);
+  if ( (-1 == ret) &&
+       (EINTR != errno) )
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+                         "poll");
+  for (int i = 0; i<2; i++)
+  {
+    if ( (pfds[i].fd == client->esock) &&
+         (POLLIN == pfds[i].revents) )
+    {
+      uint64_t num;
+
+      GNUNET_assert (sizeof (num) ==
+                     read (client->esock,
+                           &num,
+                           sizeof (num)));
+      return true;
+    }
+  }
+  return false;
+}
+
+
+void
+TES_free_client (struct TES_Client *client)
+{
+  GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
+  GNUNET_CONTAINER_DLL_remove (TES_clients_head,
+                               TES_clients_tail,
+                               client);
+  GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
+  GNUNET_break (0 == close (client->csock));
+  GNUNET_break (0 == close (client->esock));
+  pthread_detach (client->worker);
+  GNUNET_free (client);
+}
+
+
+/**
+ * Main function of a worker thread that signs.
+ *
+ * @param cls the client we are working on
+ * @return NULL
+ */
+static void *
+sign_worker (void *cls)
+{
+  struct TES_Client *client = cls;
+
+  if (GNUNET_OK !=
+      client->cb.init (client))
+  {
+    GNUNET_break (0);
+    TES_free_client (client);
+    return NULL;
+  }
+  while (! in_shutdown)
+  {
+    if (TES_await_ready (client))
+    {
+      if (GNUNET_OK !=
+          client->cb.updater (client))
+        break;
+    }
+    if (GNUNET_SYSERR ==
+        TES_read_work (client,
+                       client->cb.dispatch))
+      break;
+  }
+  TES_free_client (client);
+  return NULL;
+}
+
+
+/**
+ * Task that listens for incoming clients.
+ *
+ * @param cls a `struct TES_Callbacks`
+ */
+static void
+listen_job (void *cls)
+{
+  const struct TES_Callbacks *cb = cls;
+  int s;
+  int e;
+  struct sockaddr_storage sa;
+  socklen_t sa_len = sizeof (sa);
+
+  listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                               unix_sock,
+                                               &listen_job,
+                                               cls);
+  s = accept (GNUNET_NETWORK_get_fd (unix_sock),
+              (struct sockaddr *) &sa,
+              &sa_len);
+  if (-1 == s)
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                         "accept");
+    return;
+  }
+  e = eventfd (0,
+               EFD_CLOEXEC);
+  if (-1 == e)
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                         "eventfd");
+    GNUNET_break (0 == close (s));
+    return;
+  }
+  {
+    struct TES_Client *client;
+
+    client = GNUNET_new (struct TES_Client);
+    client->cb = *cb;
+    client->csock = s;
+    client->esock = e;
+    GNUNET_assert (0 == pthread_mutex_lock (&TES_clients_lock));
+    GNUNET_CONTAINER_DLL_insert (TES_clients_head,
+                                 TES_clients_tail,
+                                 client);
+    GNUNET_assert (0 == pthread_mutex_unlock (&TES_clients_lock));
+    if (0 !=
+        pthread_create (&client->worker,
+                        NULL,
+                        &sign_worker,
+                        client))
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+                           "pthread_create");
+      TES_free_client (client);
+    }
+  }
+}
+
+
+int
+TES_listen_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                  const char *section,
+                  const struct TES_Callbacks *cb)
+{
+  {
+    char *pfn;
+
+    if (GNUNET_OK !=
+        GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                                 section,
+                                                 "SM_PRIV_KEY",
+                                                 &pfn))
+    {
+      GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                                 section,
+                                 "SM_PRIV_KEY");
+      return EXIT_NOTCONFIGURED;
+    }
+    if (GNUNET_SYSERR ==
+        GNUNET_CRYPTO_eddsa_key_from_file (pfn,
+                                           GNUNET_YES,
+                                           &TES_smpriv.eddsa_priv))
+    {
+      GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+                                 section,
+                                 "SM_PRIV_KEY",
+                                 "Could not use file to persist private key");
+      GNUNET_free (pfn);
+      return EXIT_NOPERMISSION;
+    }
+    GNUNET_free (pfn);
+    GNUNET_CRYPTO_eddsa_key_get_public (&TES_smpriv.eddsa_priv,
+                                        &TES_smpub.eddsa_pub);
+  }
+
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                               section,
+                                               "UNIXPATH",
+                                               &unixpath))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               section,
+                               "UNIXPATH");
+    return EXIT_NOTCONFIGURED;
+  }
+  GNUNET_assert (NULL != unixpath);
+  unix_sock = TES_open_socket (unixpath);
+  if (NULL == unix_sock)
+  {
+    GNUNET_free (unixpath);
+    GNUNET_break (0);
+    return EXIT_NOPERMISSION;
+  }
+  /* start job to accept incoming requests on 'sock' */
+  listen_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                               unix_sock,
+                                               &listen_job,
+                                               (void *) cb);
+  return 0;
+}
+
+
+void
+TES_listen_stop (void)
+{
+  if (NULL != listen_task)
+  {
+    GNUNET_SCHEDULER_cancel (listen_task);
+    listen_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);
+  in_shutdown = true;
+  TES_wake_clients ();
+}
diff --git a/src/util/secmod_common.h b/src/util/secmod_common.h
index ca1270fa..a212c9d4 100644
--- a/src/util/secmod_common.h
+++ b/src/util/secmod_common.h
@@ -24,6 +24,8 @@
 
 #include <gnunet/gnunet_util_lib.h>
 #include <gnunet/gnunet_network_lib.h>
+#include <pthread.h>
+
 
 /**
  * Create the listen socket for a secmod daemon.
@@ -36,4 +38,201 @@
 struct GNUNET_NETWORK_Handle *
 TES_open_socket (const char *unixpath);
 
+
+/**
+ * Send a message starting with @a hdr to @a sock.
+ *
+ * @param sock where to send the message
+ * @param hdr beginning of the message, length indicated in size field
+ * @return #GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+TES_transmit (int sock,
+              const struct GNUNET_MessageHeader *hdr);
+
+
+/**
+ * Information we keep for a client connected to us.
+ */
+struct TES_Client;
+
+/**
+ * Function that handles message @a hdr from @a client.
+ *
+ * @param client sender of the message
+ * @param hdr message we received
+ * @return #GNUNET_OK on success
+ */
+typedef enum GNUNET_GenericReturnValue
+(*TES_MessageDispatch)(struct TES_Client *client,
+                       const struct GNUNET_MessageHeader *hdr);
+
+
+/**
+ * Function that updates the keys for @a client.
+ *
+ * @param client sender of the message
+ * @return #GNUNET_OK on success
+ */
+typedef enum GNUNET_GenericReturnValue
+(*TES_KeyUpdater)(struct TES_Client *client);
+
+
+/**
+ * Module-specific functions to be used.
+ */
+struct TES_Callbacks
+{
+  /**
+   * Function to handle inbound messages.
+   */
+  TES_MessageDispatch dispatch;
+
+  /**
+   * Function to update key material initially.
+   */
+  TES_KeyUpdater init;
+
+  /**
+   * Function to update key material.
+   */
+  TES_KeyUpdater updater;
+
+};
+
+
+/**
+ * Information we keep for a client connected to us.
+ */
+struct TES_Client
+{
+
+  /**
+   * Kept in a DLL.
+   */
+  struct TES_Client *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct TES_Client *prev;
+
+  /**
+   * Callbacks to use for work.
+   */
+  struct TES_Callbacks cb;
+
+  /**
+   * Worker thread for this client.
+   */
+  pthread_t worker;
+
+  /**
+   * Key generation this client is on.
+   */
+  uint64_t key_gen;
+
+  /**
+   * IO-buffer used by @a purpose.
+   */
+  char iobuf[65536];
+
+  /**
+   * Client socket.
+   */
+  int csock;
+
+  /**
+   * Event socket.
+   */
+  int esock;
+};
+
+
+/**
+ * Head of DLL of clients connected to us.
+ */
+extern struct TES_Client *TES_clients_head;
+
+/**
+ * Tail of DLL of clients connected to us.
+ */
+extern struct TES_Client *TES_clients_tail;
+
+/**
+ * Lock for the client queue.
+ */
+extern pthread_mutex_t TES_clients_lock;
+
+/**
+ * Private key of this security module. Used to sign denomination key
+ * announcements.
+ */
+extern struct TALER_SecurityModulePrivateKeyP TES_smpriv;
+
+/**
+ * Public key of this security module.
+ */
+extern struct TALER_SecurityModulePublicKeyP TES_smpub;
+
+
+/**
+ * Send a signal to all clients to notify them about a key generation change.
+ */
+void
+TES_wake_clients (void);
+
+
+/**
+ * Read work request from the client.
+ *
+ * @param cls a `struct TES_Client *`
+ * @param dispatch function to call with work requests received
+ * @return #GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+TES_read_work (void *cls,
+               TES_MessageDispatch dispatch);
+
+
+/**
+ * Wait until the socket is ready to read.
+ *
+ * @param client the client to wait for
+ * @return true if we received an event
+ */
+bool
+TES_await_ready (struct TES_Client *client);
+
+
+/**
+ * Free resources occupied by @a client.
+ *
+ * @param[in] client resources to release
+ */
+void
+TES_free_client (struct TES_Client *client);
+
+
+/**
+ * Start listen task.
+ *
+ * @param cfg configuration to use
+ * @param section configuration section to use
+ * @param cb callback functions to use
+ * @return 0 on success, otherwise return value to return from main()
+ */
+int
+TES_listen_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                  const char *section,
+                  const struct TES_Callbacks *cb);
+
+
+/**
+ * Stop listen task.
+ */
+void
+TES_listen_stop (void);
+
+
 #endif
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..7133a661 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>
 
 
 /**
@@ -78,12 +78,12 @@ struct DenominationKey
   /**
    * The private key of the denomination.
    */
-  struct TALER_DenominationPrivateKey denom_priv;
+  struct GNUNET_CRYPTO_RsaPrivateKey *denom_priv;
 
   /**
    * The public key of the denomination.
    */
-  struct TALER_DenominationPublicKey denom_pub;
+  struct GNUNET_CRYPTO_RsaPublicKey *denom_pub;
 
   /**
    * Hash of this denomination's public key.
@@ -95,6 +95,11 @@ struct DenominationKey
    */
   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,415 +209,21 @@ 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.
- */
-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.
+ * Lock for the keys queue.
  */
-static struct WorkItem *done_tail;
+static pthread_mutex_t keys_lock;
 
 /**
- * Lock for the done queue.
+ * Current key generation.
  */
-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.
- *
- * @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
- * @return #GNUNET_OK on success
- */
-static int
-transmit (const struct sockaddr_un *addr,
-          socklen_t addr_size,
-          const struct GNUNET_MessageHeader *hdr)
-{
-  for (unsigned int i = 0; i<100; i++)
-  {
-    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_strerror (GNUNET_ERROR_TYPE_WARNING,
-                       "sendto");
-  return GNUNET_SYSERR;
-}
-
-
-/**
- * Process completed tasks that are in the #done_head queue, sending
- * the result back to the client (and resuming the client).
- *
- * @param cls NULL
- */
-static void
-handle_done (void *cls)
-{
-  uint64_t data;
-  (void) cls;
-
-  /* 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)
-  {
-    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_assert (0 == pthread_mutex_unlock (&done_lock));
-
-}
-
-
-/**
- * Handle @a client request @a sr to create signature. Create the
- * 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 sr the request details
- */
-static void
-handle_sign_request (const struct sockaddr_un *addr,
-                     socklen_t addr_size,
-                     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);
-
-  dk = GNUNET_CONTAINER_multihashmap_get (keys,
-                                          &sr->h_denom_pub.hash);
-  if (NULL == dk)
-  {
-    struct TALER_CRYPTO_SignFailure sf = {
-      .header.size = htons (sizeof (sr)),
-      .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE),
-      .ec = htonl (TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN)
-    };
-
-    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;
-  }
-  if (0 !=
-      GNUNET_TIME_absolute_get_remaining (dk->anchor).rel_value_us)
-  {
-    /* it is too early */
-    struct TALER_CRYPTO_SignFailure sf = {
-      .header.size = htons (sizeof (sr)),
-      .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE),
-      .ec = htonl (TALER_EC_EXCHANGE_DENOMINATION_HELPER_TOO_EARLY)
-    };
-
-    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_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;
-  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));
-}
+static uint64_t key_gen;
 
 
 /**
@@ -728,7 +234,7 @@ handle_sign_request (const struct sockaddr_un *addr,
  * @return #GNUNET_OK on success
  */
 static enum GNUNET_GenericReturnValue
-notify_client_dk_add (struct Client *client,
+notify_client_dk_add (struct TES_Client *client,
                       const struct DenominationKey *dk)
 {
   struct Denomination *denom = dk->denom;
@@ -739,9 +245,8 @@ notify_client_dk_add (struct Client *client,
   void *p;
   size_t tlen;
 
-  buf_len = GNUNET_CRYPTO_rsa_public_key_encode (
-    dk->denom_pub.details.rsa_public_key,
-    &buf);
+  buf_len = GNUNET_CRYPTO_rsa_public_key_encode (dk->denom_pub,
+                                                 &buf);
   GNUNET_assert (buf_len < UINT16_MAX);
   GNUNET_assert (nlen < UINT16_MAX);
   tlen = buf_len + nlen + sizeof (*an);
@@ -757,9 +262,9 @@ notify_client_dk_add (struct Client *client,
                                     denom->section,
                                     dk->anchor,
                                     denom->duration_withdraw,
-                                    &smpriv,
+                                    &TES_smpriv,
                                     &an->secm_sig);
-  an->secm_pub = smpub;
+  an->secm_pub = TES_smpub;
   p = (void *) &an[1];
   memcpy (p,
           buf,
@@ -768,27 +273,22 @@ notify_client_dk_add (struct Client *client,
   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.hash),
+              denom->section);
+  if (GNUNET_OK !=
+      TES_transmit (client->csock,
+                    &an->header))
   {
-    enum GNUNET_GenericReturnValue ret = GNUNET_OK;
-
     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;
-    }
+                "Client %p must have disconnected\n",
+                client);
     GNUNET_free (an);
-    return ret;
+    return GNUNET_SYSERR;
   }
+  GNUNET_free (an);
+  return GNUNET_OK;
 }
 
 
@@ -799,8 +299,8 @@ notify_client_dk_add (struct Client *client,
  * @param dk the key to notify @a client about
  * @return #GNUNET_OK on success
  */
-static int
-notify_client_dk_del (struct Client *client,
+static enum GNUNET_GenericReturnValue
+notify_client_dk_del (struct TES_Client *client,
                       const struct DenominationKey *dk)
 {
   struct TALER_CRYPTO_RsaKeyPurgeNotification pn = {
@@ -813,20 +313,129 @@ notify_client_dk_del (struct Client *client,
               "Sending RSA denomination expiration %s\n",
               GNUNET_h2s (&dk->h_denom_pub.hash));
   if (GNUNET_OK !=
-      transmit (&client->addr,
-                client->addr_size,
-                &pn.header))
+      TES_transmit (client->csock,
+                    &pn.header))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Client %s must have disconnected\n",
-                client->addr.sun_path);
-    free_client (client);
+                "Client %p must have disconnected\n",
+                client);
     return GNUNET_SYSERR;
   }
   return GNUNET_OK;
 }
 
 
+/**
+ * Handle @a client request @a sr to create signature. Create the
+ * signature using the respective key and return the result to
+ * the client.
+ *
+ * @param client the client making the request
+ * @param sr the request details
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+handle_sign_request (struct TES_Client *client,
+                     const struct TALER_CRYPTO_SignRequest *sr)
+{
+  struct DenominationKey *dk;
+  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);
+  if (NULL == dk)
+  {
+    struct TALER_CRYPTO_SignFailure sf = {
+      .header.size = htons (sizeof (sr)),
+      .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE),
+      .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));
+    return TES_transmit (client->csock,
+                         &sf.header);
+  }
+  if (0 !=
+      GNUNET_TIME_absolute_get_remaining (dk->anchor).rel_value_us)
+  {
+    /* it is too early */
+    struct TALER_CRYPTO_SignFailure sf = {
+      .header.size = htons (sizeof (sr)),
+      .header.type = htons (TALER_HELPER_RSA_MT_RES_SIGN_FAILURE),
+      .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));
+    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));
+  GNUNET_assert (dk->rc < UINT_MAX);
+  dk->rc++;
+  GNUNET_assert (0 == pthread_mutex_unlock (&keys_lock));
+  rsa_signature
+    = GNUNET_CRYPTO_rsa_sign_blinded (dk->denom_priv,
+                                      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)
+  {
+    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");
+    return TES_transmit (client->csock,
+                         &sf.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,
+                "Sending RSA signature\n");
+    ret = TES_transmit (client->csock,
+                        &sr->header);
+    GNUNET_free (sr);
+    return ret;
+  }
+}
+
+
 /**
  * Initialize key material for denomination key @a dk (also on disk).
  *
@@ -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.hash);
   GNUNET_asprintf (&dk->filename,
                    "%s/%s/%llu",
                    keydir,
@@ -875,19 +488,20 @@ 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",
+              "Setup fresh private key %s at %s in `%s' (generation #%llu)\n",
               GNUNET_h2s (&dk->h_denom_pub.hash),
               GNUNET_STRINGS_absolute_time_to_string (dk->anchor),
-              dk->filename);
+              dk->filename,
+              (unsigned long long) key_gen);
   dk->denom_priv = priv;
   dk->denom_pub = pub;
-
+  dk->key_gen = key_gen;
   if (GNUNET_OK !=
       GNUNET_CONTAINER_multihashmap_put (
         keys,
@@ -897,72 +511,75 @@ setup_key (struct DenominationKey *dk,
   {
     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);
+    GNUNET_CRYPTO_rsa_public_key_free (dk->denom_pub);
     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);
   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;
+    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.hash,
+                     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);
+    GNUNET_CRYPTO_rsa_public_key_free (key->denom_pub);
+    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,20 +1013,24 @@ 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 = denom;
     dk->anchor = anchor;
     dk->filename = GNUNET_strdup (filename);
-    TALER_denom_pub_hash (&pub,
-                          &dk->h_denom_pub);
+    GNUNET_CRYPTO_rsa_public_key_hash (pub,
+                                       &dk->h_denom_pub.hash);
     dk->denom_pub = pub;
     if (GNUNET_OK !=
         GNUNET_CONTAINER_multihashmap_put (
@@ -1445,8 +1043,8 @@ parse_key (struct Denomination *denom,
                   "Duplicate private key %s detected in file `%s'. 
Skipping.\n",
                   GNUNET_h2s (&dk->h_denom_pub.hash),
                   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;
     }
@@ -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..1723560a 100644
--- a/src/util/taler-exchange-secmod-rsa.h
+++ b/src/util/taler-exchange-secmod-rsa.h
@@ -102,6 +102,7 @@ struct TALER_CRYPTO_RsaKeyPurgeNotification
   /**
    * Hash of the public key of the purged RSA key.
    */
+  // FIXME: wrong type, not hashed with age restriction here!
   struct TALER_DenominationHash h_denom_pub;
 
 };
@@ -125,6 +126,7 @@ struct TALER_CRYPTO_SignRequest
   /**
    * Hash of the public key of the RSA key to use for the signature.
    */
+  // FIXME: wrong type, not hashed with age restriction here!
   struct TALER_DenominationHash h_denom_pub;
 
   /* followed by message to sign */
@@ -149,6 +151,7 @@ struct TALER_CRYPTO_RevokeRequest
   /**
    * Hash of the public key of the revoked RSA key.
    */
+  // FIXME: wrong type, not hashed with age restriction here!
   struct TALER_DenominationHash 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..7d2d3151 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.
@@ -154,7 +166,6 @@ 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);
       num_keys++;
@@ -349,13 +360,15 @@ test_signing (struct TALER_CRYPTO_DenominationHelper *dh)
     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)
+        TALER_blinded_denom_sig_free (&ds);
       GNUNET_break (0);
       return 17;
     }
@@ -374,7 +387,8 @@ 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;
   enum TALER_ErrorCode ec;
@@ -383,9 +397,9 @@ perf_signing (struct TALER_CRYPTO_DenominationHelper *dh)
 
   TALER_planchet_setup_random (&ps);
   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)
@@ -423,7 +437,7 @@ perf_signing (struct TALER_CRYPTO_DenominationHelper *dh)
                                                delay);
           TALER_blinded_denom_sig_free (&ds);
           j++;
-          if (NUM_SIGN_TESTS == j)
+          if (NUM_SIGN_PERFS <= j)
             break;
         }
         GNUNET_free (pd.coin_ev);
@@ -431,8 +445,65 @@ perf_signing (struct TALER_CRYPTO_DenominationHelper *dh)
     } /* 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,54 +532,47 @@ 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)
@@ -517,6 +581,7 @@ run_test (void)
       GNUNET_assert (num_keys > 0);
       num_keys--;
     }
+  GNUNET_CONFIGURATION_destroy (cfg);
   return ret;
 }
 
@@ -535,7 +600,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 +616,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]